// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/mojo_binder_policy_map_impl.h" #include <string_view> #include "base/feature_list.h" #include "base/no_destructor.h" #include "base/not_fatal_until.h" #include "content/common/dom_automation_controller.mojom.h" #include "content/common/frame.mojom.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/mojo_binder_policy_map.h" #include "content/public/common/content_client.h" #include "device/gamepad/public/mojom/gamepad.mojom.h" #include "media/mojo/mojom/media_player.mojom.h" #include "media/mojo/mojom/webrtc_video_perf.mojom.h" #include "services/network/public/mojom/restricted_cookie_manager.mojom.h" #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h" #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h" #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h" #include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h" #include "third_party/blink/public/mojom/file/file_utilities.mojom.h" #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom.h" #include "third_party/blink/public/mojom/frame/frame.mojom.h" #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h" #include "third_party/blink/public/mojom/loader/fetch_later.mojom.h" #if BUILDFLAG(IS_MAC) #include "third_party/blink/public/mojom/input/text_input_host.mojom.h" #endif #include "third_party/blink/public/mojom/loader/code_cache.mojom.h" #include "third_party/blink/public/mojom/manifest/manifest_observer.mojom.h" #include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom.h" #include "third_party/blink/public/mojom/notifications/notification_service.mojom.h" #include "third_party/blink/public/mojom/page/display_cutout.mojom.h" namespace content { #if BUILDFLAG(IS_MAC) // Put crbug.com/115920 fix under flag, so we can measure its CWV impact. BASE_FEATURE(kTextInputHostMojoCapabilityControlWorkaround, "TextInputHostMojoCapabilityControlWorkaround", base::FEATURE_ENABLED_BY_DEFAULT); #endif namespace { enum class PolicyClass { kSameOriginPrerendering, kPreview, }; // Register feature specific policies for interfaces registered in // `internal::PopulateBinderMap` and `internal::PopulateBinderMapWithContext`. void RegisterNonAssociatedPolicies(MojoBinderPolicyMap& map, PolicyClass policy) { // For Prerendering, kCancel is usually used for those interfaces that cannot // be granted because they can cause undesirable side-effects (e.g., playing // audio, showing notification) and are non-deferrable. // Please update `PrerenderCancelledInterface` and // `GetCancelledInterfaceType()` in // content/browser/preloading/prerender/prerender_metrics.h once you add a new // kCancel interface. map.SetNonAssociatedPolicy<device::mojom::GamepadHapticsManager>( MojoBinderNonAssociatedPolicy::kCancel); map.SetNonAssociatedPolicy<device::mojom::GamepadMonitor>( MojoBinderNonAssociatedPolicy::kCancel); if (policy == PolicyClass::kSameOriginPrerendering) { // ClipboardHost has sync messages, so it cannot be kDefer. However, the // renderer is not expected to request the interface; prerendering documents // do not have system focus nor user activation, which is required before // sending the request. map.SetNonAssociatedPolicy<blink::mojom::ClipboardHost>( MojoBinderNonAssociatedPolicy::kUnexpected); } // FileUtilitiesHost is only used by APIs that require user activations, being // impossible for a prerendered document. For the reason, this is marked as // kUnexpected. map.SetNonAssociatedPolicy<blink::mojom::FileUtilitiesHost>( MojoBinderNonAssociatedPolicy::kUnexpected); map.SetNonAssociatedPolicy<blink::mojom::CacheStorage>( MojoBinderNonAssociatedPolicy::kGrant); map.SetNonAssociatedPolicy<blink::mojom::IDBFactory>( MojoBinderNonAssociatedPolicy::kGrant); // Grant this interface because some sync web APIs rely on it; deferring it // leads to deadlock. However, granting this interface does not mean that // prerenders are allowed to create output streams. // RenderFrameAudioOutputStreamFactory understands which pages are // prerendering and does not fulfill their requests for audio streams. map.SetNonAssociatedPolicy<blink::mojom::RendererAudioOutputStreamFactory>( MojoBinderNonAssociatedPolicy::kGrant); map.SetNonAssociatedPolicy<network::mojom::RestrictedCookieManager>( MojoBinderNonAssociatedPolicy::kGrant); // Set policy to Grant for CodeCacheHost. Without this loads won't progress // since we wait for a response from code cache when loading resources. map.SetNonAssociatedPolicy<blink::mojom::CodeCacheHost>( MojoBinderNonAssociatedPolicy::kGrant); // Grant this for Media Capabilities APIs. This should be safe as the APIs // just query encoding / decoding information. map.SetNonAssociatedPolicy<media::mojom::WebrtcVideoPerfHistory>( content::MojoBinderNonAssociatedPolicy::kGrant); #if BUILDFLAG(IS_MAC) // Set policy to Grant for TextInputHost. // This is used to return macOS IME sync call results to the browser process, // and will hang entire Chrome if paused. // This is a prospective fix added for crbug.com/1480850 if (base::FeatureList::IsEnabled( kTextInputHostMojoCapabilityControlWorkaround)) { map.SetNonAssociatedPolicy<blink::mojom::TextInputHost>( MojoBinderNonAssociatedPolicy::kGrant); } #endif } // Register same-origin prerendering policies for channel-associated interfaces // registered in `RenderFrameHostImpl::SetUpMojoIfNeeded()`. void RegisterChannelAssociatedPoliciesForSameOriginPrerendering( MojoBinderPolicyMap& map) { // Basic skeleton. All of them are critical to load a page so their policies // have to be kGrant. // TODO(crbug.com/40201285): Message-level control should be performed. map.SetAssociatedPolicy<mojom::FrameHost>(MojoBinderAssociatedPolicy::kGrant); map.SetAssociatedPolicy<blink::mojom::LocalFrameHost>( MojoBinderAssociatedPolicy::kGrant); map.SetAssociatedPolicy<blink::mojom::LocalMainFrameHost>( MojoBinderAssociatedPolicy::kGrant); // These interfaces do not leak sensitive information. map.SetAssociatedPolicy<blink::mojom::BackForwardCacheControllerHost>( MojoBinderAssociatedPolicy::kGrant); map.SetAssociatedPolicy<blink::mojom::ManifestUrlChangeObserver>( MojoBinderAssociatedPolicy::kGrant); map.SetAssociatedPolicy<mojom::DomAutomationControllerHost>( MojoBinderAssociatedPolicy::kGrant); // BroadcastChannel is granted for prerendering, as this API is restricted to // same-origin. map.SetAssociatedPolicy<blink::mojom::BroadcastChannelProvider>( MojoBinderAssociatedPolicy::kGrant); // Granting this interface does not mean prerendering pages are allowed to // play media. Feature-specific capability control is implemented to delay // playing media. See `RenderFrameImpl::DeferMediaLoad` for more information. map.SetAssociatedPolicy<media::mojom::MediaPlayerHost>( MojoBinderAssociatedPolicy::kGrant); // DisplayCutout supports the CSS viewport-fit property. It tracks // the current viewport-fit on a per-document basis, but only calls // the WebContents::NotifyViewportFitChanged and informs WebContents's // observers when the document is fullscreened. Prerendered documents cannot // enter fullscreen because they do not have transient activation, nor are // they active documents (see RenderFrameHostImpl::EnterFullscreen), so it is // safe to allow a prerendered document to use it. map.SetAssociatedPolicy<blink::mojom::DisplayCutoutHost>( MojoBinderAssociatedPolicy::kGrant); // Prerendering pages are allowed to create urls for blobs. map.SetAssociatedPolicy<blink::mojom::BlobURLStore>( MojoBinderAssociatedPolicy::kGrant); // Pages with FetchLater API calls should be allowed to prerender. // TODO(crbug.com/40276121): Update according to feedback from // https://github.com/WICG/pending-beacon/issues/82 map.SetAssociatedPolicy<blink::mojom::FetchLaterLoaderFactory>( MojoBinderAssociatedPolicy::kGrant); } // Register mojo binder policies for same-origin prerendering for content/ // interfaces. void RegisterContentBinderPoliciesForSameOriginPrerendering( MojoBinderPolicyMap& map) { RegisterNonAssociatedPolicies(map, PolicyClass::kSameOriginPrerendering); RegisterChannelAssociatedPoliciesForSameOriginPrerendering(map); } // Register mojo binder policies for preview mode for content/ interfaces. void RegisterContentBinderPoliciesForPreview(MojoBinderPolicyMap& map) { RegisterNonAssociatedPolicies(map, PolicyClass::kPreview); // Inherits the policies for same-origin prerendering. // TODO(b:299240273): Adjust policies for preview. RegisterChannelAssociatedPoliciesForSameOriginPrerendering(map); } // A singleton class that stores the `MojoBinderPolicyMap` of interfaces which // are obtained via `BrowserInterfaceBrowser` for frames. // content/ initializes the policy map with predefined policies, then allows // embedders to update the map. class BrowserInterfaceBrokerMojoBinderPolicyMapHolder { public: BrowserInterfaceBrokerMojoBinderPolicyMapHolder() { RegisterContentBinderPoliciesForSameOriginPrerendering(same_origin_map_); GetContentClient() ->browser() ->RegisterMojoBinderPoliciesForSameOriginPrerendering(same_origin_map_); RegisterContentBinderPoliciesForPreview(preview_map_); GetContentClient()->browser()->RegisterMojoBinderPoliciesForPreview( preview_map_); } ~BrowserInterfaceBrokerMojoBinderPolicyMapHolder() = default; // Remove copy and move operations. BrowserInterfaceBrokerMojoBinderPolicyMapHolder( const BrowserInterfaceBrokerMojoBinderPolicyMapHolder& other) = delete; BrowserInterfaceBrokerMojoBinderPolicyMapHolder& operator=( const BrowserInterfaceBrokerMojoBinderPolicyMapHolder& other) = delete; BrowserInterfaceBrokerMojoBinderPolicyMapHolder( BrowserInterfaceBrokerMojoBinderPolicyMapHolder&&) = delete; BrowserInterfaceBrokerMojoBinderPolicyMapHolder& operator=( BrowserInterfaceBrokerMojoBinderPolicyMapHolder&&) = delete; const MojoBinderPolicyMapImpl* GetSameOriginPolicyMap() const { return &same_origin_map_; } const MojoBinderPolicyMapImpl* GetPreviewPolicyMap() const { return &preview_map_; } private: // TODO(crbug.com/40156088): Set default policy map for content/. // Changes to `same_origin_map_` require security review. MojoBinderPolicyMapImpl same_origin_map_; MojoBinderPolicyMapImpl preview_map_; }; } // namespace MojoBinderPolicyMapImpl::MojoBinderPolicyMapImpl() = default; MojoBinderPolicyMapImpl::MojoBinderPolicyMapImpl( const base::flat_map<std::string, MojoBinderNonAssociatedPolicy>& init_map) : non_associated_policy_map_(init_map) {} MojoBinderPolicyMapImpl::~MojoBinderPolicyMapImpl() = default; const MojoBinderPolicyMapImpl* MojoBinderPolicyMapImpl::GetInstanceForSameOriginPrerendering() { static const base::NoDestructor< BrowserInterfaceBrokerMojoBinderPolicyMapHolder> map; return map->GetSameOriginPolicyMap(); } const MojoBinderPolicyMapImpl* MojoBinderPolicyMapImpl::GetInstanceForPreview() { static const base::NoDestructor< BrowserInterfaceBrokerMojoBinderPolicyMapHolder> map; return map->GetPreviewPolicyMap(); } MojoBinderNonAssociatedPolicy MojoBinderPolicyMapImpl::GetNonAssociatedMojoBinderPolicy( const std::string& interface_name, const MojoBinderNonAssociatedPolicy default_policy) const { const auto& found = non_associated_policy_map_.find(interface_name); if (found != non_associated_policy_map_.end()) return found->second; return default_policy; } MojoBinderAssociatedPolicy MojoBinderPolicyMapImpl::GetAssociatedMojoBinderPolicy( const std::string& interface_name, const MojoBinderAssociatedPolicy default_policy) const { const auto& found = associated_policy_map_.find(interface_name); if (found != associated_policy_map_.end()) return found->second; return default_policy; } MojoBinderNonAssociatedPolicy MojoBinderPolicyMapImpl::GetNonAssociatedMojoBinderPolicyOrDieForTesting( const std::string& interface_name) const { const auto& found = non_associated_policy_map_.find(interface_name); CHECK(found != non_associated_policy_map_.end(), base::NotFatalUntil::M130); return found->second; } MojoBinderAssociatedPolicy MojoBinderPolicyMapImpl::GetAssociatedMojoBinderPolicyOrDieForTesting( const std::string& interface_name) const { const auto& found = associated_policy_map_.find(interface_name); CHECK(found != associated_policy_map_.end(), base::NotFatalUntil::M130); return found->second; } void MojoBinderPolicyMapImpl::SetPolicyByName( const std::string_view& name, MojoBinderNonAssociatedPolicy policy) { non_associated_policy_map_.emplace(name, policy); } void MojoBinderPolicyMapImpl::SetPolicyByName( const std::string_view& name, MojoBinderAssociatedPolicy policy) { associated_policy_map_.emplace(name, policy); } } // namespace content