| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "fuchsia_web/webengine/browser/context_impl.h" |
| |
| #include <lib/fpromise/result.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/handle.h> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/koid.h" |
| #include "base/fuchsia/mem_buffer_util.h" |
| #include "base/functional/bind.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/typed_macros.h" |
| #include "build/chromecast_buildflags.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "fuchsia_web/webengine/browser/frame_impl.h" |
| #include "fuchsia_web/webengine/browser/trace_event.h" |
| #include "fuchsia_web/webengine/browser/web_engine_devtools_controller.h" |
| #include "third_party/blink/public/common/web_preferences/web_preferences.h" |
| #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h" |
| #include "third_party/perfetto/include/perfetto/tracing/track_event_args.h" |
| |
| #if BUILDFLAG(ENABLE_CAST_RECEIVER) |
| #include "components/cast_streaming/browser/public/network_context_getter.h" // nogncheck |
| #endif |
| |
| ContextImpl::ContextImpl( |
| std::unique_ptr<content::BrowserContext> browser_context, |
| inspect::Node inspect_node, |
| WebEngineDevToolsController* devtools_controller) |
| : browser_context_(std::move(browser_context)), |
| devtools_controller_(devtools_controller), |
| inspect_node_(std::move(inspect_node)), |
| cookie_manager_(base::BindRepeating(&ContextImpl::GetNetworkContext, |
| base::Unretained(this))) { |
| DCHECK(browser_context_); |
| DCHECK(devtools_controller_); |
| |
| TRACE_EVENT(kWebEngineFidlCategory, "fuchsia.web/Context created", |
| perfetto::Flow::FromPointer(this)); |
| } |
| |
| ContextImpl::~ContextImpl() { |
| TRACE_EVENT(kWebEngineFidlCategory, "fuchsia.web/Context destroyed", |
| perfetto::TerminatingFlow::FromPointer(this)); |
| } |
| |
| void ContextImpl::DestroyFrame(FrameImpl* frame) { |
| auto iter = frames_.find(frame); |
| DCHECK(iter != frames_.end()); |
| frames_.erase(iter); |
| } |
| |
| bool ContextImpl::IsJavaScriptInjectionAllowed() { |
| return allow_javascript_injection_; |
| } |
| |
| #if BUILDFLAG(ENABLE_CAST_RECEIVER) |
| void ContextImpl::SetCastStreamingEnabled() { |
| cast_streaming_enabled_ = true; |
| cast_streaming::SetNetworkContextGetter(base::BindRepeating( |
| &ContextImpl::GetNetworkContext, base::Unretained(this))); |
| } |
| #endif |
| |
| void ContextImpl::CreateFrame( |
| fidl::InterfaceRequest<fuchsia::web::Frame> frame_request) { |
| TRACE_EVENT(kWebEngineFidlCategory, "fuchsia.web/Context.CreateFrame", |
| perfetto::Flow::FromPointer(this)); |
| |
| CreateFrameWithParams(fuchsia::web::CreateFrameParams(), |
| std::move(frame_request)); |
| } |
| |
| void ContextImpl::CreateFrameWithParams( |
| fuchsia::web::CreateFrameParams params, |
| fidl::InterfaceRequest<fuchsia::web::Frame> frame_request) { |
| if (!params.IsEmpty()) { |
| TRACE_EVENT(kWebEngineFidlCategory, |
| "fuchsia.web/Context.CreateFrameWithParams", |
| perfetto::Flow::FromPointer(this)); |
| } |
| |
| // Ensure the params can be cloned as required by CreateFrameForWebContents(). |
| fuchsia::web::CreateFrameParams cloned_params; |
| zx_status_t status = params.Clone(&cloned_params); |
| if (status != ZX_OK) { |
| ZX_LOG(ERROR, status) << "CreateFrameParams Clone() failed"; |
| frame_request.Close(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| |
| // Create a WebContents to host the new Frame. |
| content::WebContents::CreateParams create_params(browser_context_.get(), |
| nullptr); |
| create_params.initially_hidden = true; |
| auto web_contents = content::WebContents::Create(create_params); |
| |
| CreateFrameForWebContents(std::move(web_contents), std::move(params), |
| std::move(frame_request)); |
| } |
| |
| FrameImpl* ContextImpl::CreateFrameForWebContents( |
| std::unique_ptr<content::WebContents> web_contents, |
| fuchsia::web::CreateFrameParams params, |
| fidl::InterfaceRequest<fuchsia::web::Frame> frame_request) { |
| DCHECK(frame_request.is_valid()); |
| |
| // Register the new Frame with the DevTools controller. The controller will |
| // reject registration if user-debugging is requested, but it is not enabled |
| // in the controller. |
| const bool user_debugging_requested = |
| params.has_enable_remote_debugging() && params.enable_remote_debugging(); |
| if (!devtools_controller_->OnFrameCreated(web_contents.get(), |
| user_debugging_requested)) { |
| frame_request.Close(ZX_ERR_INVALID_ARGS); |
| return nullptr; |
| } |
| |
| // |params.debug_name| is handled by FrameImpl. |
| |
| // Verify the explicit sites filter error page content. If the parameter is |
| // present, it will be provided to the FrameImpl after it is created below. |
| absl::optional<std::string> explicit_sites_filter_error_page; |
| if (params.has_explicit_sites_filter_error_page()) { |
| explicit_sites_filter_error_page = |
| base::StringFromMemData(params.explicit_sites_filter_error_page()); |
| if (!explicit_sites_filter_error_page) { |
| frame_request.Close(ZX_ERR_INVALID_ARGS); |
| return nullptr; |
| } |
| } |
| |
| // Wrap the WebContents into a FrameImpl owned by |this|. |
| auto inspect_node_name = |
| base::StringPrintf("frame-%lu", *base::GetKoid(frame_request.channel())); |
| auto frame_impl = std::make_unique<FrameImpl>( |
| std::move(web_contents), this, std::move(params), |
| inspect_node_.CreateChild(inspect_node_name), std::move(frame_request)); |
| |
| if (explicit_sites_filter_error_page) { |
| frame_impl->EnableExplicitSitesFilter( |
| std::move(*explicit_sites_filter_error_page)); |
| } |
| |
| FrameImpl* frame_ptr = frame_impl.get(); |
| frames_.insert(std::move(frame_impl)); |
| return frame_ptr; |
| } |
| |
| void ContextImpl::GetCookieManager( |
| fidl::InterfaceRequest<fuchsia::web::CookieManager> request) { |
| TRACE_EVENT(kWebEngineFidlCategory, "fuchsia.web/Context.GetCookieManager", |
| perfetto::Flow::FromPointer(this)); |
| |
| cookie_manager_bindings_.AddBinding(&cookie_manager_, std::move(request)); |
| } |
| |
| void ContextImpl::GetRemoteDebuggingPort( |
| GetRemoteDebuggingPortCallback callback) { |
| TRACE_EVENT(kWebEngineFidlCategory, |
| "fuchsia.web/Context.GetRemoteDebuggingPort", |
| perfetto::Flow::FromPointer(this)); |
| |
| devtools_controller_->GetDevToolsPort(base::BindOnce( |
| [](GetRemoteDebuggingPortCallback callback, uint16_t port) { |
| if (port == 0) { |
| callback(fpromise::error( |
| fuchsia::web::ContextError::REMOTE_DEBUGGING_PORT_NOT_OPENED)); |
| } else { |
| callback(fpromise::ok(port)); |
| } |
| }, |
| std::move(callback))); |
| } |
| |
| FrameImpl* ContextImpl::GetFrameImplForTest( |
| fuchsia::web::FramePtr* frame_ptr) const { |
| DCHECK(frame_ptr); |
| |
| // Find the FrameImpl whose channel is connected to |frame_ptr| by inspecting |
| // the "related" KOIDs of active FrameImpls. |
| zx_koid_t channel_koid = base::GetKoid(frame_ptr->channel()).value(); |
| for (const std::unique_ptr<FrameImpl>& frame : frames_) { |
| zx_koid_t peer_koid = |
| base::GetRelatedKoid(*frame->GetBindingChannelForTest()) // IN-TEST |
| .value(); |
| |
| if (peer_koid == channel_koid) |
| return frame.get(); |
| } |
| |
| return nullptr; |
| } |
| |
| network::mojom::NetworkContext* ContextImpl::GetNetworkContext() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| return browser_context_->GetDefaultStoragePartition()->GetNetworkContext(); |
| } |