| // Copyright 2022 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/annotations/annotations_java_script_feature.h" |
| |
| #import <vector> |
| |
| #import "base/logging.h" |
| #import "base/no_destructor.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "components/shared_highlighting/ios/parsing_utils.h" |
| #import "ios/web/annotations/annotations_text_manager.h" |
| #import "ios/web/public/js_messaging/script_message.h" |
| #import "ios/web/public/js_messaging/web_frame.h" |
| #import "ios/web/public/js_messaging/web_frame_util.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| const char kScriptName[] = "annotations"; |
| const char kScriptHandlerName[] = "annotations"; |
| } // namespace |
| |
| namespace web { |
| |
| AnnotationsJavaScriptFeature::AnnotationsJavaScriptFeature() |
| : JavaScriptFeature( |
| ContentWorld::kAnyContentWorld, |
| {FeatureScript::CreateWithFilename( |
| kScriptName, |
| FeatureScript::InjectionTime::kDocumentStart, |
| FeatureScript::TargetFrames::kMainFrame, |
| FeatureScript::ReinjectionBehavior::kInjectOncePerWindow)}) {} |
| |
| AnnotationsJavaScriptFeature::~AnnotationsJavaScriptFeature() = default; |
| |
| // static |
| AnnotationsJavaScriptFeature* AnnotationsJavaScriptFeature::GetInstance() { |
| static base::NoDestructor<AnnotationsJavaScriptFeature> instance; |
| return instance.get(); |
| } |
| |
| void AnnotationsJavaScriptFeature::ExtractText(WebState* web_state, |
| int maximum_text_length) { |
| DCHECK(web_state); |
| auto* frame = web::GetMainFrame(web_state); |
| if (!frame) { |
| return; |
| } |
| |
| std::vector<base::Value> parameters; |
| parameters.push_back(base::Value(maximum_text_length)); |
| CallJavaScriptFunction(frame, "annotations.extractText", parameters); |
| } |
| |
| void AnnotationsJavaScriptFeature::DecorateAnnotations( |
| WebState* web_state, |
| base::Value& annotations) { |
| DCHECK(web_state); |
| auto* frame = web::GetMainFrame(web_state); |
| if (!frame) { |
| return; |
| } |
| |
| std::vector<base::Value> parameters; |
| parameters.push_back(base::Value(std::move(annotations))); |
| CallJavaScriptFunction(frame, "annotations.decorateAnnotations", parameters); |
| } |
| |
| void AnnotationsJavaScriptFeature::RemoveDecorations(WebState* web_state) { |
| DCHECK(web_state); |
| auto* frame = web::GetMainFrame(web_state); |
| if (!frame) { |
| return; |
| } |
| |
| CallJavaScriptFunction(frame, "annotations.removeDecorations", {}); |
| } |
| |
| void AnnotationsJavaScriptFeature::RemoveHighlight(WebState* web_state) { |
| DCHECK(web_state); |
| auto* frame = web::GetMainFrame(web_state); |
| if (!frame) { |
| return; |
| } |
| |
| CallJavaScriptFunction(frame, "annotations.removeHighlight", {}); |
| } |
| |
| void AnnotationsJavaScriptFeature::ScriptMessageReceived( |
| WebState* web_state, |
| const ScriptMessage& script_message) { |
| if (!script_message.is_main_frame()) { |
| return; |
| } |
| |
| auto* manager = AnnotationsTextManager::FromWebState(web_state); |
| if (!manager) { |
| return; |
| } |
| |
| base::Value* response = script_message.body(); |
| if (!response || !response->is_dict()) { |
| return; |
| } |
| |
| const std::string* command = response->FindStringKey("command"); |
| if (!command) { |
| return; |
| } |
| |
| // Discard messages if we've navigated away. |
| auto sender_url = script_message.request_url(); |
| GURL current_url = web_state->GetLastCommittedURL(); |
| if (!sender_url || !(*sender_url).EqualsIgnoringRef(current_url)) { |
| return; |
| } |
| |
| if (*command == "annotations.log") { |
| const std::string* text = response->FindStringKey("text"); |
| DLOG(WARNING) << *text; |
| } else if (*command == "annotations.extractedText") { |
| const std::string* text = response->FindStringKey("text"); |
| if (!text) { |
| return; |
| } |
| manager->OnTextExtracted(web_state, *text); |
| } else if (*command == "annotations.decoratingComplete") { |
| absl::optional<double> optional_annotations = |
| response->FindDoubleKey("annotations"); |
| absl::optional<double> optional_successes = |
| response->FindDoubleKey("successes"); |
| if (!optional_annotations || !optional_successes) { |
| return; |
| } |
| int annotations = static_cast<int>(optional_annotations.value()); |
| int successes = static_cast<int>(optional_successes.value()); |
| manager->OnDecorated(web_state, successes, annotations); |
| } else if (*command == "annotations.onClick") { |
| const std::string* data = response->FindStringKey("data"); |
| absl::optional<CGRect> rect = |
| shared_highlighting::ParseRect(response->FindDictKey("rect")); |
| const std::string* text = response->FindStringKey("text"); |
| if (!data || !rect || !text) { |
| return; |
| } |
| manager->OnClick( |
| web_state, *text, |
| shared_highlighting::ConvertToBrowserRect(*rect, web_state), *data); |
| } |
| } |
| |
| absl::optional<std::string> |
| AnnotationsJavaScriptFeature::GetScriptMessageHandlerName() const { |
| return kScriptHandlerName; |
| } |
| |
| } // namespace web |