| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/renderer/api/automation/automation_internal_custom_bindings.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/values.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "content/public/renderer/worker_thread.h" |
| #include "extensions/common/api/automation.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_handlers/automation.h" |
| #include "extensions/common/manifest_handlers/background_info.h" |
| #include "extensions/common/mojom/event_dispatcher.mojom.h" |
| #include "extensions/renderer/api/automation/automation_api_converters.h" |
| #include "extensions/renderer/ipc_message_sender.h" |
| #include "extensions/renderer/native_extension_bindings_system.h" |
| #include "extensions/renderer/object_backed_native_handler.h" |
| #include "extensions/renderer/script_context.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "ui/accessibility/ax_event.h" |
| #include "ui/accessibility/ax_event_generator.h" |
| #include "ui/accessibility/platform/automation/automation_api_util.h" |
| #include "ui/accessibility/platform/automation/automation_ax_tree_wrapper.h" |
| #include "ui/accessibility/platform/automation/automation_tree_manager_owner.h" |
| #include "ui/accessibility/platform/automation/automation_v8_bindings.h" |
| #include "ui/accessibility/platform/automation/automation_v8_router.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/strings/grit/auto_image_annotation_strings.h" |
| |
| namespace extensions { |
| |
| AutomationInternalCustomBindings::AutomationInternalCustomBindings( |
| ScriptContext* context, |
| NativeExtensionBindingsSystem* bindings_system) |
| : ObjectBackedNativeHandler(context), |
| bindings_system_(bindings_system), |
| should_ignore_context_(false), |
| automation_v8_bindings_( |
| std::make_unique<ui::AutomationV8Bindings>(this, this)) { |
| // We will ignore this instance if the extension has a background page and |
| // this context is not that background page. In all other cases, we will have |
| // multiple instances floating around in the same process. |
| if (context && context->extension()) { |
| const GURL background_page_url = |
| extensions::BackgroundInfo::GetBackgroundURL(context->extension()); |
| should_ignore_context_ = |
| background_page_url != "" && background_page_url != context->url(); |
| } |
| } |
| |
| AutomationInternalCustomBindings::~AutomationInternalCustomBindings() = default; |
| |
| void AutomationInternalCustomBindings::AddRoutes() { |
| automation_v8_bindings_->AddV8Routes(); |
| |
| // Extensions specific routes. |
| ObjectBackedNativeHandler::RouteHandlerFunction( |
| "IsInteractPermitted", "automation", |
| base::BindRepeating( |
| &AutomationInternalCustomBindings::IsInteractPermitted, |
| base::Unretained(this))); |
| } |
| |
| void AutomationInternalCustomBindings::Invalidate() { |
| ObjectBackedNativeHandler::Invalidate(); |
| receiver_.reset(); |
| AutomationTreeManagerOwner::Invalidate(); |
| } |
| |
| ui::AutomationV8Bindings* |
| AutomationInternalCustomBindings::GetAutomationV8Bindings() const { |
| DCHECK(automation_v8_bindings_); |
| return automation_v8_bindings_.get(); |
| } |
| |
| void AutomationInternalCustomBindings::IsInteractPermitted( |
| const v8::FunctionCallbackInfo<v8::Value>& args) const { |
| const Extension* extension = context()->extension(); |
| CHECK(extension); |
| const AutomationInfo* automation_info = AutomationInfo::Get(extension); |
| CHECK(automation_info); |
| args.GetReturnValue().Set(automation_info->desktop); |
| } |
| |
| void AutomationInternalCustomBindings::StartCachingAccessibilityTrees() { |
| if (should_ignore_context_) |
| return; |
| |
| if (!receiver_.is_bound()) { |
| bindings_system_->GetIPCMessageSender()->SendBindAutomationIPC( |
| context(), receiver_.BindNewEndpointAndPassRemote()); |
| } |
| } |
| |
| void AutomationInternalCustomBindings::StopCachingAccessibilityTrees() { |
| receiver_.reset(); |
| } |
| |
| // |
| // Handle accessibility events from the browser process. |
| // |
| |
| void AutomationInternalCustomBindings::ThrowInvalidArgumentsException( |
| bool is_fatal) const { |
| GetIsolate()->ThrowException(v8::String::NewFromUtf8Literal( |
| GetIsolate(), |
| "Invalid arguments to AutomationInternalCustomBindings function")); |
| |
| if (!is_fatal) |
| return; |
| |
| LOG(FATAL) << "Invalid arguments to AutomationInternalCustomBindings function" |
| << context()->GetStackTraceAsString(); |
| } |
| |
| v8::Isolate* AutomationInternalCustomBindings::GetIsolate() const { |
| return ObjectBackedNativeHandler::GetIsolate(); |
| } |
| |
| v8::Local<v8::Context> AutomationInternalCustomBindings::GetContext() const { |
| return context()->v8_context(); |
| } |
| |
| void AutomationInternalCustomBindings::RouteHandlerFunction( |
| const std::string& name, |
| scoped_refptr<ui::V8HandlerFunctionWrapper> handler_function_wrapper) { |
| ObjectBackedNativeHandler::RouteHandlerFunction( |
| name, "automation", |
| base::BindRepeating(&ui::V8HandlerFunctionWrapper::RunV8, |
| handler_function_wrapper)); |
| } |
| |
| ui::TreeChangeObserverFilter |
| AutomationInternalCustomBindings::ParseTreeChangeObserverFilter( |
| const std::string& filter) const { |
| return ConvertAutomationTreeChangeObserverFilter( |
| api::automation::ParseTreeChangeObserverFilter(filter)); |
| } |
| |
| std::string AutomationInternalCustomBindings::GetMarkerTypeString( |
| ax::mojom::MarkerType type) const { |
| return api::automation::ToString(ConvertMarkerTypeFromAXToAutomation(type)); |
| } |
| |
| std::string AutomationInternalCustomBindings::GetFocusedStateString() const { |
| return api::automation::ToString(api::automation::StateType::kFocused); |
| } |
| |
| std::string AutomationInternalCustomBindings::GetOffscreenStateString() const { |
| return api::automation::ToString(api::automation::StateType::kOffscreen); |
| } |
| |
| void AutomationInternalCustomBindings::DispatchEvent( |
| const std::string& event_name, |
| const base::Value::List& event_args) const { |
| bindings_system_->DispatchEventInContext(event_name, event_args, nullptr, |
| context()); |
| } |
| |
| std::string |
| AutomationInternalCustomBindings::GetLocalizedStringForImageAnnotationStatus( |
| ax::mojom::ImageAnnotationStatus status) const { |
| int message_id = 0; |
| switch (status) { |
| case ax::mojom::ImageAnnotationStatus::kEligibleForAnnotation: |
| message_id = IDS_AX_IMAGE_ELIGIBLE_FOR_ANNOTATION; |
| break; |
| case ax::mojom::ImageAnnotationStatus::kAnnotationPending: |
| message_id = IDS_AX_IMAGE_ANNOTATION_PENDING; |
| break; |
| case ax::mojom::ImageAnnotationStatus::kAnnotationAdult: |
| message_id = IDS_AX_IMAGE_ANNOTATION_ADULT; |
| break; |
| case ax::mojom::ImageAnnotationStatus::kAnnotationEmpty: |
| case ax::mojom::ImageAnnotationStatus::kAnnotationProcessFailed: |
| message_id = IDS_AX_IMAGE_ANNOTATION_NO_DESCRIPTION; |
| break; |
| case ax::mojom::ImageAnnotationStatus::kNone: |
| case ax::mojom::ImageAnnotationStatus::kWillNotAnnotateDueToScheme: |
| case ax::mojom::ImageAnnotationStatus::kIneligibleForAnnotation: |
| case ax::mojom::ImageAnnotationStatus::kSilentlyEligibleForAnnotation: |
| case ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded: |
| return std::string(); |
| } |
| |
| DCHECK(message_id); |
| |
| return l10n_util::GetStringUTF8(message_id); |
| } |
| |
| std::string AutomationInternalCustomBindings::GetTreeChangeTypeString( |
| ax::mojom::Mutation change_type) const { |
| return ToString(ConvertToAutomationTreeChangeType(change_type)); |
| } |
| |
| std::string AutomationInternalCustomBindings::GetEventTypeString( |
| const std::tuple<ax::mojom::Event, ui::AXEventGenerator::Event>& event_type) |
| const { |
| ui::AXEventGenerator::Event generated_event = std::get<1>(event_type); |
| // Resolve the proper event based on generated or non-generated event sources. |
| api::automation::EventType automation_event_type = |
| generated_event != ui::AXEventGenerator::Event::NONE |
| ? AXGeneratedEventToAutomationEventType(generated_event) |
| : AXEventToAutomationEventType(std::get<0>(event_type)); |
| return api::automation::ToString(automation_event_type); |
| } |
| |
| void AutomationInternalCustomBindings::NotifyTreeEventListenersChanged() { |
| // This task is posted because we need to wait for any pending mutations |
| // to be processed before sending the event. |
| auto callback = |
| base::BindOnce(&AutomationInternalCustomBindings:: |
| MaybeSendOnAllAutomationEventListenersRemoved, |
| weak_ptr_factory_.GetWeakPtr()); |
| |
| if (context()->IsForServiceWorker()) { |
| content::WorkerThread::PostTask(content::WorkerThread::GetCurrentId(), |
| std::move(callback)); |
| } else { |
| context() |
| ->web_frame() |
| ->GetTaskRunner(blink::TaskType::kInternalDefault) |
| ->PostTask(FROM_HERE, std::move(callback)); |
| } |
| } |
| |
| } // namespace extensions |