blob: ba0fb427b03434387d2b783a02690d557d564fa8 [file] [log] [blame]
// 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/bind.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "extensions/common/api/automation.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_handlers/automation.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/renderer/api/automation/automation_api_converters.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/renderer/object_backed_native_handler.h"
#include "extensions/renderer/script_context.h"
#include "ipc/message_filter.h"
#include "third_party/blink/public/strings/grit/blink_accessibility_strings.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"
namespace extensions {
class AutomationMessageFilter : public IPC::MessageFilter {
public:
AutomationMessageFilter(
AutomationInternalCustomBindings* owner,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: owner_(owner), removed_(false), task_runner_(std::move(task_runner)) {
DCHECK(owner);
content::RenderThread::Get()->AddFilter(this);
}
AutomationMessageFilter(const AutomationMessageFilter&) = delete;
AutomationMessageFilter& operator=(const AutomationMessageFilter&) = delete;
void Detach() {
owner_ = nullptr;
Remove();
}
// IPC::MessageFilter
bool OnMessageReceived(const IPC::Message& message) override {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&AutomationMessageFilter::OnMessageReceivedOnRenderThread, this,
message));
// Always return false in case there are multiple
// AutomationInternalCustomBindings instances attached to the same thread.
return false;
}
void OnFilterRemoved() override { removed_ = true; }
private:
void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
if (owner_)
owner_->OnMessageReceived(message);
}
~AutomationMessageFilter() override { Remove(); }
void Remove() {
if (!removed_) {
removed_ = true;
content::RenderThread::Get()->RemoveFilter(this);
}
}
AutomationInternalCustomBindings* owner_;
bool removed_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
};
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() {}
void AutomationInternalCustomBindings::OnMessageReceived(
const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message)
IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEventBundle,
HandleAccessibilityEvents)
IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityLocationChange,
HandleAccessibilityLocationChange)
IPC_END_MESSAGE_MAP()
}
void AutomationInternalCustomBindings::AddRoutes() {
automation_v8_bindings_->AddV8Routes();
}
void AutomationInternalCustomBindings::Invalidate() {
ObjectBackedNativeHandler::Invalidate();
if (message_filter_)
message_filter_->Detach();
AutomationTreeManagerOwner::Invalidate();
}
ui::AutomationV8Bindings*
AutomationInternalCustomBindings::GetAutomationV8Bindings() const {
DCHECK(automation_v8_bindings_);
return automation_v8_bindings_.get();
}
bool AutomationInternalCustomBindings::IsInteractPermitted() const {
const Extension* extension = context()->extension();
CHECK(extension);
const AutomationInfo* automation_info = AutomationInfo::Get(extension);
CHECK(automation_info);
return automation_info->interact;
}
void AutomationInternalCustomBindings::StartCachingAccessibilityTrees() {
if (should_ignore_context_)
return;
if (!message_filter_) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
context()->web_frame()->GetTaskRunner(
blink::TaskType::kInternalDefault);
message_filter_ = base::MakeRefCounted<AutomationMessageFilter>(
this, std::move(task_runner));
}
}
void AutomationInternalCustomBindings::StopCachingAccessibilityTrees() {
message_filter_->Detach();
message_filter_.reset();
}
//
// Handle accessibility events from the browser process.
//
void AutomationInternalCustomBindings::HandleAccessibilityEvents(
const ExtensionMsg_AccessibilityEventBundleParams& event_bundle,
bool is_active_profile) {
OnAccessibilityEvents(event_bundle.tree_id, event_bundle.events,
event_bundle.updates, event_bundle.mouse_location,
is_active_profile);
}
void AutomationInternalCustomBindings::HandleAccessibilityLocationChange(
const ExtensionMsg_AccessibilityLocationChangeParams& params) {
OnAccessibilityLocationChange(params.tree_id, params.id, params.new_location);
}
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,
AutomationV8Router::HandlerFunction handler_function) {
ObjectBackedNativeHandler::RouteHandlerFunction(name, handler_function);
}
void AutomationInternalCustomBindings::RouteHandlerFunction(
const std::string& name,
const std::string& api_name,
AutomationV8Router::HandlerFunction handler_function) {
ObjectBackedNativeHandler::RouteHandlerFunction(name, api_name,
handler_function);
}
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::STATE_TYPE_FOCUSED);
}
std::string AutomationInternalCustomBindings::GetOffscreenStateString() const {
return api::automation::ToString(api::automation::STATE_TYPE_OFFSCREEN);
}
void AutomationInternalCustomBindings::DispatchEvent(
const std::string& event_name,
const base::Value::List& event_args) const {
bindings_system_->DispatchEventInContext(event_name, event_args, nullptr,
context());
if (notify_event_for_testing_.is_null() ||
event_name != "automationInternal.onAccessibilityEvent") {
return;
}
// Find the event type within the event_params for the test.
const base::Value::Dict* dict = event_args[0].GetIfDict();
DCHECK(dict);
const std::string* event_type_string = dict->FindString("eventType");
DCHECK(event_type_string);
api::automation::EventType event_type =
api::automation::ParseEventType(*event_type_string);
notify_event_for_testing_.Run(event_type);
}
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() {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
context()->web_frame()->GetTaskRunner(blink::TaskType::kInternalDefault);
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&AutomationInternalCustomBindings::
MaybeSendOnAllAutomationEventListenersRemoved,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace extensions