// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/find_in_page/find_in_page_java_script_feature.h"
#import "base/no_destructor.h"
#import "ios/web/find_in_page/find_in_page_constants.h"
#import "ios/web/public/js_messaging/java_script_feature_util.h"
#import "ios/web/public/js_messaging/web_frame.h"
namespace {
const char kScriptName[] = "find_in_page_native_api";
const char kEventListenersScriptName[] = "find_in_page_event_listeners";
// Timeout for the find within JavaScript in milliseconds.
const double kFindInPageFindTimeout = 100.0;
// The timeout for JavaScript function calls in milliseconds. Important that
// this is longer than `kFindInPageFindTimeout` to allow for incomplete find to
// restart again. If this timeout hits, then something went wrong with the find
// and find in page should not continue.
const double kJavaScriptFunctionCallTimeout = 200.0;
} // namespace
namespace web {
namespace find_in_page {
const int kFindInPagePending = -1;
} // namespace find_in_page
// static
FindInPageJavaScriptFeature* FindInPageJavaScriptFeature::GetInstance() {
static base::NoDestructor<FindInPageJavaScriptFeature> instance;
return instance.get();
}
FindInPageJavaScriptFeature::FindInPageJavaScriptFeature()
: JavaScriptFeature(
ContentWorld::kIsolatedWorld,
{FeatureScript::CreateWithFilename(
kScriptName,
FeatureScript::InjectionTime::kDocumentStart,
FeatureScript::TargetFrames::kAllFrames,
FeatureScript::ReinjectionBehavior::kInjectOncePerWindow),
FeatureScript::CreateWithFilename(
kEventListenersScriptName,
FeatureScript::InjectionTime::kDocumentStart,
FeatureScript::TargetFrames::kAllFrames,
FeatureScript::ReinjectionBehavior::
kReinjectOnDocumentRecreation)},
{web::java_script_features::GetBaseJavaScriptFeature()}) {}
FindInPageJavaScriptFeature::~FindInPageJavaScriptFeature() = default;
bool FindInPageJavaScriptFeature::Search(
WebFrame* frame,
const std::string& query,
base::OnceCallback<void(std::optional<int>)> callback) {
base::Value::List params;
params.Append(query);
params.Append(kFindInPageFindTimeout);
return CallJavaScriptFunction(
frame, kFindInPageSearch, params,
base::BindOnce(&FindInPageJavaScriptFeature::ProcessSearchResult,
base::Unretained(GetInstance()), std::move(callback)),
base::Milliseconds(kJavaScriptFunctionCallTimeout));
}
void FindInPageJavaScriptFeature::Pump(
WebFrame* frame,
base::OnceCallback<void(std::optional<int>)> callback) {
base::Value::List params;
params.Append(kFindInPageFindTimeout);
CallJavaScriptFunction(
frame, kFindInPagePump, params,
base::BindOnce(&FindInPageJavaScriptFeature::ProcessSearchResult,
base::Unretained(GetInstance()), std::move(callback)),
base::Milliseconds(kJavaScriptFunctionCallTimeout));
}
void FindInPageJavaScriptFeature::SelectMatch(
WebFrame* frame,
int index,
base::OnceCallback<void(const base::Value*)> callback) {
base::Value::List params;
params.Append(index);
CallJavaScriptFunction(frame, kFindInPageSelectAndScrollToMatch, params,
std::move(callback),
base::Milliseconds(kJavaScriptFunctionCallTimeout));
}
void FindInPageJavaScriptFeature::Stop(WebFrame* frame) {
CallJavaScriptFunction(frame, kFindInPageStop, base::Value::List());
}
void FindInPageJavaScriptFeature::ProcessSearchResult(
base::OnceCallback<void(const std::optional<int>)> callback,
const base::Value* result) {
std::optional<int> match_count;
if (result && result->is_double()) {
// Valid match number returned. If not, match count will be 0 in order to
// zero-out count from previous find.
match_count = static_cast<int>(result->GetDouble());
}
std::move(callback).Run(match_count);
}
} // namespace web