diff options
Diffstat (limited to 'chromium/chrome/renderer/extensions/extension_hooks_delegate.cc')
-rw-r--r-- | chromium/chrome/renderer/extensions/extension_hooks_delegate.cc | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/chromium/chrome/renderer/extensions/extension_hooks_delegate.cc b/chromium/chrome/renderer/extensions/extension_hooks_delegate.cc new file mode 100644 index 00000000000..b4e8bef3065 --- /dev/null +++ b/chromium/chrome/renderer/extensions/extension_hooks_delegate.cc @@ -0,0 +1,332 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/extensions/extension_hooks_delegate.h" + +#include "content/public/renderer/v8_value_converter.h" +#include "extensions/common/api/messaging/message.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest.h" +#include "extensions/common/view_type.h" +#include "extensions/renderer/bindings/api_signature.h" +#include "extensions/renderer/extension_frame_helper.h" +#include "extensions/renderer/extensions_renderer_client.h" +#include "extensions/renderer/get_script_context.h" +#include "extensions/renderer/message_target.h" +#include "extensions/renderer/messaging_util.h" +#include "extensions/renderer/native_renderer_messaging_service.h" +#include "extensions/renderer/runtime_hooks_delegate.h" +#include "extensions/renderer/script_context.h" +#include "gin/converter.h" +#include "gin/dictionary.h" + +namespace extensions { + +namespace { + +using RequestResult = APIBindingHooks::RequestResult; + +constexpr char kSendExtensionRequest[] = "extension.sendRequest"; +constexpr char kGetURL[] = "extension.getURL"; +constexpr char kGetBackgroundPage[] = "extension.getBackgroundPage"; +constexpr char kGetViews[] = "extension.getViews"; +constexpr char kGetExtensionTabs[] = "extension.getExtensionTabs"; + +// We alias a bunch of chrome.extension APIs to their chrome.runtime +// counterparts. +// NOTE(devlin): This is a very simple alias, in which we just return the +// runtime version from the chrome.runtime object. This is important to note +// for a few reasons: +// - Modifications to the chrome.runtime object will affect the return result +// here. i.e., if script does chrome.runtime.sendMessage = 'some string', +// then chrome.extension.sendMessage will also be 'some string'. +// - Events will share listeners. i.e., a listener added to +// chrome.extension.onMessage will fire from a runtime.onMessage event, and +// vice versa. +// All of these APIs have been deprecated, and are no longer even documented, +// but still have usage. This is the cheap workaround that JS bindings have been +// using, and, while not robust, it should be secure, so use it native bindings, +// too. +void GetAliasedFeature(v8::Local<v8::Name> property_name, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Context> context = info.Holder()->CreationContext(); + + v8::TryCatch try_catch(isolate); + v8::Local<v8::Value> chrome; + if (!context->Global() + ->Get(context, gin::StringToSymbol(isolate, "chrome")) + .ToLocal(&chrome) || + !chrome->IsObject()) { + return; + } + + v8::Local<v8::Value> runtime; + if (!chrome.As<v8::Object>() + ->Get(context, gin::StringToSymbol(isolate, "runtime")) + .ToLocal(&runtime) || + !runtime->IsObject()) { + return; + } + + v8::Local<v8::Object> runtime_obj = runtime.As<v8::Object>(); + v8::Maybe<bool> has_property = + runtime_obj->HasRealNamedProperty(context, property_name); + if (!has_property.IsJust() || !has_property.FromJust()) + return; + + v8::Local<v8::Value> property_value; + // Try and grab the chrome.runtime version. It's possible this has been + // tampered with, so early-out if an exception is thrown. + if (!runtime_obj->Get(context, property_name).ToLocal(&property_value)) + return; + + info.GetReturnValue().Set(property_value); +} + +// A helper method to throw a deprecation error on access. +void ThrowDeprecatedAccessError( + v8::Local<v8::Name> name, + const v8::PropertyCallbackInfo<v8::Value>& info) { + static constexpr char kError[] = + "extension.sendRequest, extension.onRequest, and " + "extension.onRequestExternal are deprecated. Please use " + "runtime.sendMessage, runtime.onMessage, and runtime.onMessageExternal " + "instead."; + v8::Isolate* isolate = info.GetIsolate(); + isolate->ThrowException( + v8::Exception::Error(gin::StringToV8(isolate, kError))); +} + +} // namespace + +ExtensionHooksDelegate::ExtensionHooksDelegate( + NativeRendererMessagingService* messaging_service) + : messaging_service_(messaging_service) {} +ExtensionHooksDelegate::~ExtensionHooksDelegate() {} + +RequestResult ExtensionHooksDelegate::HandleRequest( + const std::string& method_name, + const APISignature* signature, + v8::Local<v8::Context> context, + std::vector<v8::Local<v8::Value>>* arguments, + const APITypeReferenceMap& refs) { + // TODO(devlin): This logic is the same in the RuntimeCustomHooksDelegate - + // would it make sense to share it? + using Handler = RequestResult (ExtensionHooksDelegate::*)( + ScriptContext*, const std::vector<v8::Local<v8::Value>>&); + static struct { + Handler handler; + base::StringPiece method; + } kHandlers[] = { + {&ExtensionHooksDelegate::HandleSendRequest, kSendExtensionRequest}, + {&ExtensionHooksDelegate::HandleGetURL, kGetURL}, + {&ExtensionHooksDelegate::HandleGetBackgroundPage, kGetBackgroundPage}, + {&ExtensionHooksDelegate::HandleGetExtensionTabs, kGetExtensionTabs}, + {&ExtensionHooksDelegate::HandleGetViews, kGetViews}, + }; + + ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context); + + Handler handler = nullptr; + for (const auto& handler_entry : kHandlers) { + if (handler_entry.method == method_name) { + handler = handler_entry.handler; + break; + } + } + + if (!handler) + return RequestResult(RequestResult::NOT_HANDLED); + + if (method_name == kSendExtensionRequest) { + messaging_util::MassageSendMessageArguments(context->GetIsolate(), false, + arguments); + } + + APISignature::V8ParseResult parse_result = + signature->ParseArgumentsToV8(context, *arguments, refs); + if (!parse_result.succeeded()) { + RequestResult result(RequestResult::INVALID_INVOCATION); + result.error = std::move(*parse_result.error); + return result; + } + + return (this->*handler)(script_context, *parse_result.arguments); +} + +void ExtensionHooksDelegate::InitializeTemplate( + v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> object_template, + const APITypeReferenceMap& type_refs) { + static constexpr const char* kAliases[] = { + "connect", "connectNative", "sendMessage", "sendNativeMessage", + "onConnect", "onConnectExternal", "onMessage", "onMessageExternal", + }; + + for (const auto* alias : kAliases) { + object_template->SetAccessor(gin::StringToSymbol(isolate, alias), + &GetAliasedFeature); + } + + bool is_incognito = ExtensionsRendererClient::Get()->IsIncognitoProcess(); + object_template->Set(isolate, "inIncognitoContext", + v8::Boolean::New(isolate, is_incognito)); +} + +void ExtensionHooksDelegate::InitializeInstance( + v8::Local<v8::Context> context, + v8::Local<v8::Object> instance) { + // Throw access errors for deprecated sendRequest-related properties. This + // isn't terribly efficient, but is only done for certain unpacked extensions + // and only if they access the chrome.extension module. + if (messaging_util::IsSendRequestDisabled( + GetScriptContextFromV8ContextChecked(context))) { + static constexpr const char* kDeprecatedSendRequestProperties[] = { + "sendRequest", "onRequest", "onRequestExternal"}; + for (const char* property : kDeprecatedSendRequestProperties) { + v8::Maybe<bool> success = instance->SetAccessor( + context, gin::StringToV8(context->GetIsolate(), property), + &ThrowDeprecatedAccessError); + DCHECK(success.IsJust()); + DCHECK(success.FromJust()); + } + } +} + +RequestResult ExtensionHooksDelegate::HandleSendRequest( + ScriptContext* script_context, + const std::vector<v8::Local<v8::Value>>& arguments) { + DCHECK_EQ(3u, arguments.size()); + // This DCHECK() is correct because no context with sendRequest-related + // APIs disabled should have scriptable access to a context with them + // enabled. + DCHECK(!messaging_util::IsSendRequestDisabled(script_context)); + + std::string target_id; + std::string error; + if (!messaging_util::GetTargetExtensionId(script_context, arguments[0], + "extension.sendRequest", &target_id, + &error)) { + RequestResult result(RequestResult::INVALID_INVOCATION); + result.error = std::move(error); + return result; + } + + v8::Local<v8::Value> v8_message = arguments[1]; + std::unique_ptr<Message> message = messaging_util::MessageFromV8( + script_context->v8_context(), v8_message, &error); + if (!message) { + RequestResult result(RequestResult::INVALID_INVOCATION); + result.error = std::move(error); + return result; + } + + v8::Local<v8::Function> response_callback; + if (!arguments[2]->IsNull()) + response_callback = arguments[2].As<v8::Function>(); + + messaging_service_->SendOneTimeMessage( + script_context, MessageTarget::ForExtension(target_id), + messaging_util::kSendRequestChannel, false, *message, response_callback); + + return RequestResult(RequestResult::HANDLED); +} + +RequestResult ExtensionHooksDelegate::HandleGetURL( + ScriptContext* script_context, + const std::vector<v8::Local<v8::Value>>& arguments) { + // We call a static implementation here rather using an alias due to not being + // able to remove the extension.json GetURL entry, as it is used for generated + // documentation and api feature lists some other methods refer to. + return RuntimeHooksDelegate::GetURL(script_context, arguments); +} + +APIBindingHooks::RequestResult ExtensionHooksDelegate::HandleGetViews( + ScriptContext* script_context, + const std::vector<v8::Local<v8::Value>>& arguments) { + const Extension* extension = script_context->extension(); + DCHECK(extension); + + ViewType view_type = VIEW_TYPE_INVALID; + int window_id = extension_misc::kUnknownWindowId; + int tab_id = extension_misc::kUnknownTabId; + + if (!arguments[0]->IsNull()) { + gin::Dictionary options_dict(script_context->isolate(), + arguments[0].As<v8::Object>()); + v8::Local<v8::Value> v8_window_id; + v8::Local<v8::Value> v8_tab_id; + v8::Local<v8::Value> v8_view_type; + if (!options_dict.Get("windowId", &v8_window_id) || + !options_dict.Get("tabId", &v8_tab_id) || + !options_dict.Get("type", &v8_view_type)) { + NOTREACHED() + << "Unexpected exception: argument parsing produces plain objects"; + return RequestResult(RequestResult::THROWN); + } + + if (!v8_window_id->IsUndefined()) { + DCHECK(v8_window_id->IsInt32()); + window_id = v8_window_id.As<v8::Int32>()->Value(); + } + + if (!v8_tab_id->IsUndefined()) { + DCHECK(v8_tab_id->IsInt32()); + tab_id = v8_tab_id.As<v8::Int32>()->Value(); + } + + if (!v8_view_type->IsUndefined()) { + DCHECK(v8_view_type->IsString()); + std::string view_type_string = base::ToUpperASCII( + gin::V8ToString(script_context->isolate(), v8_view_type)); + if (view_type_string != "ALL") { + bool success = GetViewTypeFromString(view_type_string, &view_type); + DCHECK(success); + } + } + } + + RequestResult result(RequestResult::HANDLED); + result.return_value = ExtensionFrameHelper::GetV8MainFrames( + script_context->v8_context(), extension->id(), window_id, tab_id, + view_type); + return result; +} + +RequestResult ExtensionHooksDelegate::HandleGetExtensionTabs( + ScriptContext* script_context, + const std::vector<v8::Local<v8::Value>>& arguments) { + const Extension* extension = script_context->extension(); + DCHECK(extension); + + ViewType view_type = VIEW_TYPE_TAB_CONTENTS; + int window_id = extension_misc::kUnknownWindowId; + int tab_id = extension_misc::kUnknownTabId; + + if (!arguments[0]->IsNull()) + window_id = arguments[0].As<v8::Int32>()->Value(); + + RequestResult result(RequestResult::HANDLED); + result.return_value = ExtensionFrameHelper::GetV8MainFrames( + script_context->v8_context(), extension->id(), window_id, tab_id, + view_type); + return result; +} + +RequestResult ExtensionHooksDelegate::HandleGetBackgroundPage( + ScriptContext* script_context, + const std::vector<v8::Local<v8::Value>>& arguments) { + const Extension* extension = script_context->extension(); + DCHECK(extension); + + RequestResult result(RequestResult::HANDLED); + result.return_value = ExtensionFrameHelper::GetV8BackgroundPageMainFrame( + script_context->isolate(), extension->id()); + return result; +} + +} // namespace extensions |