| // 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/extensions_renderer_client.h" |
| |
| #include <memory> |
| #include <ostream> |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "components/guest_view/buildflags/buildflags.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "extensions/buildflags/buildflags.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/mojom/context_type.mojom.h" |
| #include "extensions/common/switches.h" |
| #include "extensions/renderer/dispatcher.h" |
| #include "extensions/renderer/extension_frame_helper.h" |
| #include "extensions/renderer/extension_web_view_helper.h" |
| #include "extensions/renderer/extensions_render_frame_observer.h" |
| #include "extensions/renderer/resource_request_policy.h" |
| #include "extensions/renderer/script_context.h" |
| #include "third_party/blink/public/common/security/protocol_handler_security_level.h" |
| #include "third_party/blink/public/platform/web_url.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_plugin_params.h" |
| |
| #if BUILDFLAG(ENABLE_GUEST_VIEW) |
| #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h" |
| #endif |
| |
| namespace extensions { |
| |
| namespace { |
| |
| ExtensionsRendererClient* g_client = nullptr; |
| |
| #if BUILDFLAG(ENABLE_GUEST_VIEW) |
| void IsGuestViewApiAvailableToScriptContext(bool* api_is_available, |
| ScriptContext* context) { |
| if (context->GetAvailability("guestViewInternal").is_available()) { |
| *api_is_available = true; |
| } |
| } |
| #endif |
| |
| } // namespace |
| |
| ExtensionsRendererClient::ExtensionsRendererClient() = default; |
| ExtensionsRendererClient::~ExtensionsRendererClient() = default; |
| |
| ExtensionsRendererClient* ExtensionsRendererClient::Get() { |
| CHECK(g_client); |
| return g_client; |
| } |
| |
| void ExtensionsRendererClient::Set(ExtensionsRendererClient* client) { |
| g_client = client; |
| } |
| |
| void ExtensionsRendererClient::OnExtensionLoaded(const Extension& extension) { |
| resource_request_policy_->OnExtensionLoaded(extension); |
| } |
| |
| void ExtensionsRendererClient::OnExtensionUnloaded( |
| const ExtensionId& extension_id) { |
| resource_request_policy_->OnExtensionUnloaded(extension_id); |
| } |
| |
| void ExtensionsRendererClient::AddAPIProvider( |
| std::unique_ptr<ExtensionsRendererAPIProvider> api_provider) { |
| CHECK(!dispatcher_) |
| << "API providers must be added before the Dispatcher is instantiated."; |
| api_providers_.push_back(std::move(api_provider)); |
| } |
| |
| void ExtensionsRendererClient::RenderThreadStarted() { |
| // Tests may create their own ExtensionDispatcher and inject it using |
| // `SetDispatcherForTesting()`. Don't overwrite it. |
| if (!dispatcher()) { |
| dispatcher_ = std::make_unique<Dispatcher>(std::move(api_providers_)); |
| } |
| content::RenderThread* thread = content::RenderThread::Get(); |
| dispatcher()->OnRenderThreadStarted(thread); |
| thread->AddObserver(dispatcher()); |
| |
| resource_request_policy_ = std::make_unique<ResourceRequestPolicy>( |
| dispatcher(), CreateResourceRequestPolicyDelegate()); |
| |
| FinishInitialization(); |
| } |
| |
| void ExtensionsRendererClient::WebViewCreated( |
| blink::WebView* web_view, |
| const url::Origin* outermost_origin) { |
| new ExtensionWebViewHelper(web_view, outermost_origin); |
| } |
| |
| void ExtensionsRendererClient::RenderFrameCreated( |
| content::RenderFrame* render_frame, |
| service_manager::BinderRegistry* registry) { |
| new ExtensionsRenderFrameObserver(render_frame, registry); |
| new ExtensionFrameHelper(render_frame, dispatcher()); |
| dispatcher_->OnRenderFrameCreated(render_frame); |
| } |
| |
| bool ExtensionsRendererClient::OverrideCreatePlugin( |
| content::RenderFrame* render_frame, |
| const blink::WebPluginParams& params) { |
| #if BUILDFLAG(ENABLE_GUEST_VIEW) |
| if (params.mime_type.Utf8() != content::kBrowserPluginMimeType) { |
| return true; |
| } |
| |
| bool guest_view_api_available = false; |
| dispatcher_->script_context_set_iterator()->ForEach( |
| render_frame, base::BindRepeating(&IsGuestViewApiAvailableToScriptContext, |
| &guest_view_api_available)); |
| return !guest_view_api_available; |
| #else |
| return true; |
| #endif |
| } |
| |
| bool ExtensionsRendererClient::AllowPopup() { |
| ScriptContext* current_context = |
| dispatcher_->script_context_set().GetCurrent(); |
| if (!current_context || !current_context->extension()) { |
| return false; |
| } |
| |
| // See http://crbug.com/117446 for the subtlety of this check. |
| switch (current_context->context_type()) { |
| case mojom::ContextType::kUnspecified: |
| case mojom::ContextType::kWebPage: |
| case mojom::ContextType::kUnprivilegedExtension: |
| case mojom::ContextType::kWebUi: |
| case mojom::ContextType::kUntrustedWebUi: |
| case mojom::ContextType::kOffscreenExtension: |
| case mojom::ContextType::kUserScript: |
| return false; |
| case mojom::ContextType::kPrivilegedExtension: |
| return !current_context->IsForServiceWorker(); |
| case mojom::ContextType::kContentScript: |
| return true; |
| case mojom::ContextType::kPrivilegedWebPage: |
| return current_context->web_frame()->IsOutermostMainFrame(); |
| } |
| } |
| |
| blink::ProtocolHandlerSecurityLevel |
| ExtensionsRendererClient::GetProtocolHandlerSecurityLevel() { |
| // WARNING: This must match the logic of |
| // Browser::GetProtocolHandlerSecurityLevel(). |
| ScriptContext* current_context = |
| dispatcher_->script_context_set().GetCurrent(); |
| if (!current_context || !current_context->extension()) { |
| return blink::ProtocolHandlerSecurityLevel::kStrict; |
| } |
| |
| switch (current_context->context_type()) { |
| case mojom::ContextType::kPrivilegedWebPage: |
| case mojom::ContextType::kContentScript: |
| case mojom::ContextType::kOffscreenExtension: |
| case mojom::ContextType::kUnprivilegedExtension: |
| case mojom::ContextType::kUnspecified: |
| case mojom::ContextType::kUserScript: |
| case mojom::ContextType::kWebUi: |
| case mojom::ContextType::kUntrustedWebUi: |
| case mojom::ContextType::kWebPage: |
| return blink::ProtocolHandlerSecurityLevel::kStrict; |
| case mojom::ContextType::kPrivilegedExtension: |
| return blink::ProtocolHandlerSecurityLevel::kExtensionFeatures; |
| } |
| } |
| |
| v8::Local<v8::Object> ExtensionsRendererClient::GetScriptableObject( |
| const blink::WebElement& plugin_element, |
| v8::Isolate* isolate) { |
| #if BUILDFLAG(ENABLE_GUEST_VIEW) |
| // If there is a MimeHandlerView that can provide the scriptable object then |
| // MaybeCreateMimeHandlerView must have been called before and a container |
| // manager should exist. |
| auto* container_manager = MimeHandlerViewContainerManager::Get( |
| content::RenderFrame::FromWebFrame( |
| plugin_element.GetDocument().GetFrame()), |
| false /* create_if_does_not_exist */); |
| if (container_manager) { |
| return container_manager->GetScriptableObject(plugin_element, isolate); |
| } |
| #endif |
| return v8::Local<v8::Object>(); |
| } |
| |
| // static |
| blink::WebFrame* ExtensionsRendererClient::FindFrame( |
| blink::WebLocalFrame* relative_to_frame, |
| const std::string& name) { |
| content::RenderFrame* result = ExtensionFrameHelper::FindFrame( |
| content::RenderFrame::FromWebFrame(relative_to_frame), name); |
| return result ? result->GetWebFrame() : nullptr; |
| } |
| |
| void ExtensionsRendererClient::RunScriptsAtDocumentStart( |
| content::RenderFrame* render_frame) { |
| dispatcher_->RunScriptsAtDocumentStart(render_frame); |
| } |
| |
| void ExtensionsRendererClient::RunScriptsAtDocumentEnd( |
| content::RenderFrame* render_frame) { |
| dispatcher_->RunScriptsAtDocumentEnd(render_frame); |
| } |
| |
| void ExtensionsRendererClient::RunScriptsAtDocumentIdle( |
| content::RenderFrame* render_frame) { |
| dispatcher_->RunScriptsAtDocumentIdle(render_frame); |
| } |
| |
| void ExtensionsRendererClient::WillSendRequest( |
| blink::WebLocalFrame* frame, |
| ui::PageTransition transition_type, |
| const blink::WebURL& upstream_url, |
| const blink::WebURL& target_url, |
| const net::SiteForCookies& site_for_cookies, |
| const url::Origin* initiator_origin, |
| GURL* new_url) { |
| std::string extension_id; |
| if (initiator_origin && |
| initiator_origin->scheme() == extensions::kExtensionScheme) { |
| extension_id = initiator_origin->host(); |
| } else { |
| if (site_for_cookies.scheme() == extensions::kExtensionScheme) { |
| extension_id = site_for_cookies.registrable_domain(); |
| } |
| } |
| |
| // The rest of this method is only concerned with extensions URLs. |
| if (!target_url.ProtocolIs(extensions::kExtensionScheme)) { |
| return; |
| } |
| |
| if (target_url.ProtocolIs(extensions::kExtensionScheme) && |
| !resource_request_policy_->CanRequestResource( |
| upstream_url, target_url, frame, transition_type, initiator_origin)) { |
| *new_url = GURL(kExtensionInvalidRequestURL); |
| } |
| |
| RecordMetricsForURLRequest(frame, target_url); |
| } |
| |
| void ExtensionsRendererClient::SetDispatcherForTesting( |
| std::unique_ptr<Dispatcher> dispatcher) { |
| dispatcher_ = std::move(dispatcher); |
| } |
| |
| std::unique_ptr<ResourceRequestPolicy::Delegate> |
| ExtensionsRendererClient::CreateResourceRequestPolicyDelegate() { |
| return nullptr; |
| } |
| |
| } // namespace extensions |