| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/renderer/feature_manager.h" |
| |
| #include <iostream> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/containers/contains.h" |
| #include "base/logging.h" |
| #include "base/not_fatal_until.h" |
| #include "base/values.h" |
| #include "chromecast/base/cast_features.h" |
| #include "chromecast/common/feature_constants.h" |
| #include "chromecast/renderer/assistant_bindings.h" |
| #include "chromecast/renderer/cast_demo_bindings.h" |
| #include "chromecast/renderer/cast_window_manager_bindings.h" |
| #include "chromecast/renderer/settings_ui_bindings.h" |
| #include "content/public/renderer/render_frame.h" |
| #include "content/public/renderer/render_frame_media_playback_options.h" |
| #include "services/network/public/cpp/is_potentially_trustworthy.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" |
| #include "third_party/blink/public/platform/web_runtime_features.h" |
| #include "third_party/blink/public/platform/web_security_origin.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_security_policy.h" |
| |
| namespace chromecast { |
| |
| FeatureManager::FeatureManager(content::RenderFrame* render_frame) |
| : content::RenderFrameObserver(render_frame), |
| configured_(false), |
| can_install_bindings_(false), |
| dev_origin_(GURL()), |
| secure_origin_set_(false) { |
| registry_.AddInterface(base::BindRepeating( |
| &FeatureManager::OnFeatureManagerRequest, base::Unretained(this))); |
| } |
| |
| FeatureManager::~FeatureManager() {} |
| |
| void FeatureManager::OnInterfaceRequestForFrame( |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle* interface_pipe) { |
| registry_.TryBindInterface(interface_name, interface_pipe); |
| } |
| |
| void FeatureManager::OnDestruct() { |
| delete this; |
| } |
| |
| void FeatureManager::DidClearWindowObject() { |
| can_install_bindings_ = true; |
| if (!configured_) |
| return; |
| |
| EnableBindings(); |
| } |
| |
| void FeatureManager::ConfigureFeatures( |
| std::vector<chromecast::shell::mojom::FeaturePtr> features) { |
| if (configured_) |
| return; |
| configured_ = true; |
| for (auto& feature : features) { |
| // If we want to add enabled/disabled status to FeaturePtr, we can overlap |
| // previous setting via [] operator |
| features_map_[feature->name] = std::move(feature); |
| } |
| |
| ConfigureFeaturesInternal(); |
| |
| if (!can_install_bindings_) |
| return; |
| EnableBindings(); |
| } |
| |
| void FeatureManager::ConfigureFeaturesInternal() { |
| if (FeatureEnabled(feature::kEnableDevMode)) { |
| const base::Value::Dict& dev_mode_config = |
| (features_map_.find(feature::kEnableDevMode)->second)->config; |
| const std::string* dev_mode_origin = |
| dev_mode_config.FindString(feature::kDevModeOrigin); |
| DCHECK(dev_mode_origin); |
| dev_origin_ = GURL(*dev_mode_origin); |
| DCHECK(dev_origin_.is_valid()); |
| } |
| |
| if (FeatureEnabled(feature::kDisableBackgroundSuspend)) { |
| auto options = render_frame()->GetRenderFrameMediaPlaybackOptions(); |
| options.is_background_suspend_enabled = false; |
| render_frame()->SetRenderFrameMediaPlaybackOptions(options); |
| } |
| |
| // Call feature-specific functions. |
| SetupAdditionalSecureOrigin(); |
| |
| // Disable timer throttling for background tabs before the frame is painted. |
| if (FeatureEnabled(feature::kDisableBackgroundTabTimerThrottle)) { |
| blink::WebRuntimeFeatures::EnableTimerThrottlingForBackgroundTabs(false); |
| } |
| if (FeatureEnabled(feature::kEnableSettingsUiMojo)) { |
| v8_bindings_.insert(new shell::SettingsUiBindings(render_frame())); |
| } |
| |
| // Window manager bindings will install themselves depending on the specific |
| // feature flags enabled, so we pass the feature manager through to let it |
| // decide. |
| v8_bindings_.insert( |
| new shell::CastWindowManagerBindings(render_frame(), this)); |
| if (FeatureEnabled(feature::kEnableDemoStandaloneMode)) { |
| v8_bindings_.insert(new shell::CastDemoBindings(render_frame())); |
| } |
| |
| if (FeatureEnabled(feature::kEnableAssistantMessagePipe)) { |
| auto& feature = GetFeature(feature::kEnableAssistantMessagePipe); |
| v8_bindings_.insert( |
| new shell::AssistantBindings(render_frame(), feature->config)); |
| } |
| } |
| |
| void FeatureManager::EnableBindings() { |
| LOG(INFO) << "Enabling bindings: " << *this; |
| for (auto* binding : v8_bindings_) { |
| binding->TryInstall(); |
| } |
| } |
| |
| void FeatureManager::OnFeatureManagerRequest( |
| mojo::PendingReceiver<shell::mojom::FeatureManager> request) { |
| bindings_.Add(this, std::move(request)); |
| } |
| |
| bool FeatureManager::FeatureEnabled(const std::string& feature) const { |
| return base::Contains(features_map_, feature); |
| } |
| |
| const chromecast::shell::mojom::FeaturePtr& FeatureManager::GetFeature( |
| const std::string& feature) const { |
| auto itor = features_map_.find(feature); |
| CHECK(itor != features_map_.end(), base::NotFatalUntil::M130); |
| return itor->second; |
| } |
| |
| void FeatureManager::SetupAdditionalSecureOrigin() { |
| if (!dev_origin_.is_valid()) { |
| return; |
| } |
| |
| // Secure origin should be only set once, otherwise it will cause CHECK |
| // failure when race between origin safelist changing and thread creation |
| // happens (b/63583734). |
| if (secure_origin_set_) { |
| return; |
| } |
| |
| secure_origin_set_ = true; |
| |
| LOG(INFO) << "Treat origin " << dev_origin_ << " as secure origin"; |
| |
| blink::WebSecurityPolicy::AddSchemeToSecureContextSafelist( |
| blink::WebString::FromASCII(dev_origin_.scheme())); |
| |
| network::SecureOriginAllowlist::GetInstance().SetAuxiliaryAllowlist( |
| dev_origin_.spec(), nullptr); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const FeatureManager& features) { |
| for (auto& feature : features.features_map_) { |
| os << feature.first << " "; |
| } |
| return os; |
| } |
| |
| } // namespace chromecast |