| // 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 "components/cast_receiver/browser/runtime_application_base.h" |
| |
| #include <algorithm> |
| |
| #include "base/notreached.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "components/cast_receiver/browser/permissions_manager_impl.h" |
| #include "components/media_control/browser/media_blocker.h" |
| #include "components/url_rewrite/browser/url_request_rewrite_rules_manager.h" |
| #include "content/public/browser/web_contents.h" |
| |
| namespace cast_receiver { |
| |
| RuntimeApplicationBase::RuntimeApplicationBase( |
| std::string cast_session_id, |
| ApplicationConfig app_config, |
| ApplicationClient& application_client) |
| : cast_session_id_(std::move(cast_session_id)), |
| app_config_(std::move(app_config)), |
| task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), |
| application_client_(application_client) { |
| DCHECK(task_runner_); |
| } |
| |
| RuntimeApplicationBase::~RuntimeApplicationBase() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(!is_application_running_); |
| } |
| |
| void RuntimeApplicationBase::SetEmbedderApplication( |
| EmbedderApplication& embedder_application) { |
| DCHECK(!embedder_application_); |
| embedder_application_ = &embedder_application; |
| } |
| |
| const std::string& RuntimeApplicationBase::GetDisplayName() const { |
| return config().display_name; |
| } |
| |
| const std::string& RuntimeApplicationBase::GetAppId() const { |
| return config().app_id; |
| } |
| |
| const std::string& RuntimeApplicationBase::GetCastSessionId() const { |
| return cast_session_id_; |
| } |
| |
| void RuntimeApplicationBase::Load(StatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(embedder_application().GetWebContents()); |
| |
| is_application_running_ = true; |
| if (cached_mojom_rules_) { |
| // Apply cached URL rewrite rules before anything is done with the page. |
| SetUrlRewriteRules(std::move(cached_mojom_rules_)); |
| } |
| |
| DLOG(INFO) << "Loaded application: " << *this; |
| std::move(callback).Run(OkStatus()); |
| } |
| |
| void RuntimeApplicationBase::Stop(StatusCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| StopApplication(EmbedderApplication::ApplicationStopReason::kUserRequest, |
| net::ERR_ABORTED); |
| std::move(callback).Run(OkStatus()); |
| } |
| |
| ApplicationClient::ApplicationControls& |
| RuntimeApplicationBase::GetApplicationControls() { |
| DCHECK(embedder_application().GetWebContents()); |
| |
| return application_client_->GetApplicationControls( |
| *embedder_application().GetWebContents()); |
| } |
| |
| void RuntimeApplicationBase::NavigateToPage(const GURL& url) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto* window_controls = embedder_application().GetContentWindowControls(); |
| DCHECK(window_controls); |
| window_controls->AddVisibilityChangeObserver(*this); |
| |
| embedder_application().NavigateToPage(url); |
| |
| SetWebVisibilityAndPaint(is_visible_); |
| } |
| |
| void RuntimeApplicationBase::SetContentPermissions( |
| content::WebContents& web_contents) { |
| PermissionsManagerImpl* permissions_manager = |
| PermissionsManagerImpl::CreateInstance(web_contents, GetAppId()); |
| if (config().url.has_value()) { |
| auto app_url_origin = url::Origin::Create(config().url.value()); |
| if (!app_url_origin.opaque()) { |
| permissions_manager->AddOrigin(app_url_origin); |
| } |
| } |
| for (blink::PermissionType permission : config().permissions.permissions) { |
| permissions_manager->AddPermission(permission); |
| } |
| for (auto& origin : config().permissions.additional_origins) { |
| DCHECK(!origin.opaque()); |
| permissions_manager->AddOrigin(origin); |
| } |
| } |
| |
| void RuntimeApplicationBase::OnPageNavigationComplete() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DLOG(INFO) << "Page loaded: " << *this; |
| |
| embedder_application().NotifyApplicationStarted(); |
| |
| SetWebVisibilityAndPaint(is_visible_); |
| SetTouchInputEnabled(is_touch_input_enabled_); |
| SetMediaBlocking(is_media_load_blocked_, is_media_start_blocked_); |
| } |
| |
| void RuntimeApplicationBase::SetUrlRewriteRules( |
| url_rewrite::mojom::UrlRequestRewriteRulesPtr mojom_rules) { |
| if (!embedder_application().GetWebContents()) { |
| cached_mojom_rules_ = std::move(mojom_rules); |
| return; |
| } |
| |
| url_rewrite::UrlRequestRewriteRulesManager& |
| url_request_rewrite_rules_manager = |
| GetApplicationControls().GetUrlRequestRewriteRulesManager(); |
| if (!url_request_rewrite_rules_manager.OnRulesUpdated( |
| std::move(mojom_rules))) { |
| LOG(ERROR) << "URL rewrite rules update failed."; |
| StopApplication(EmbedderApplication::ApplicationStopReason::kRuntimeError, |
| net::Error::ERR_UNEXPECTED); |
| } |
| } |
| |
| void RuntimeApplicationBase::SetMediaBlocking(bool load_blocked, |
| bool start_blocked) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| is_media_load_blocked_ = load_blocked; |
| is_media_start_blocked_ = start_blocked; |
| DLOG(INFO) << "Media state updated: is_load_blocked=" << load_blocked |
| << ", is_start_blocked=" << start_blocked << ", " << *this; |
| |
| if (!embedder_application().GetWebContents()) { |
| return; |
| } |
| |
| media_control::MediaBlocker& media_blocker = |
| GetApplicationControls().GetMediaBlocker(); |
| |
| media_blocker.BlockMediaLoading(is_media_load_blocked_); |
| |
| // TODO(crbug.com/1359584): Block media starting. |
| } |
| |
| void RuntimeApplicationBase::SetVisibility(bool is_visible) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| is_visible_ = is_visible; |
| DLOG(INFO) << "Visibility updated: is_visible_=" << is_visible_ << ", " |
| << *this; |
| |
| auto* window_controls = embedder_application().GetContentWindowControls(); |
| if (!window_controls) { |
| return; |
| } |
| |
| if (is_visible_) { |
| window_controls->ShowWindow(); |
| } else { |
| window_controls->HideWindow(); |
| } |
| } |
| |
| void RuntimeApplicationBase::SetTouchInputEnabled(bool enabled) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| is_touch_input_enabled_ = enabled; |
| DLOG(INFO) << "Touch input updated: is_touch_input_enabled_= " |
| << is_touch_input_enabled_ << ", " << *this; |
| |
| auto* window_controls = embedder_application().GetContentWindowControls(); |
| if (!window_controls) { |
| return; |
| } |
| |
| if (is_touch_input_enabled_) { |
| window_controls->EnableTouchInput(); |
| } else { |
| window_controls->DisableTouchInput(); |
| } |
| } |
| |
| bool RuntimeApplicationBase::IsApplicationRunning() const { |
| return is_application_running_; |
| } |
| |
| void RuntimeApplicationBase::StopApplication( |
| EmbedderApplication::ApplicationStopReason stop_reason, |
| net::Error net_error_code) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!is_application_running_) { |
| return; |
| } |
| is_application_running_ = false; |
| |
| auto* web_contents = embedder_application().GetWebContents(); |
| if (web_contents) { |
| web_contents->DispatchBeforeUnload(false /* auto_cancel */); |
| web_contents->ClosePage(); |
| |
| // Check if window is still available as page might have been closed before. |
| auto* window_controls = embedder_application().GetContentWindowControls(); |
| if (window_controls) { |
| window_controls->RemoveVisibilityChangeObserver(*this); |
| } |
| } |
| |
| embedder_application().NotifyApplicationStopped(stop_reason, net_error_code); |
| |
| DLOG(INFO) << "Application is stopped: stop_reason=" << stop_reason << ", " |
| << *this; |
| } |
| |
| void RuntimeApplicationBase::SetWebVisibilityAndPaint(bool is_visible) { |
| auto* web_contents = embedder_application().GetWebContents(); |
| if (!web_contents) { |
| return; |
| } |
| |
| if (is_visible) { |
| web_contents->WasShown(); |
| } else { |
| // NOTE: Calling WasHidden() and later WasShown() does not behave properly |
| // on some platforms (e.g. Linux devices using X11 platform for Ozone). In |
| // such cases, the WasShown() call will execute, and the browser-side code |
| // associated with this call will run, but it will never reach the Renderer |
| // process, so the LayerTreeHost will never draw the surface assocaited with |
| // this WebContents. |
| DLOG(WARNING) |
| << "WebContents hidden. NOTE: Changing from hidden to visible does not " |
| "work in all cases, and such calls may not be respected."; |
| web_contents->WasHidden(); |
| } |
| |
| if (web_contents->GetVisibility() != content::Visibility::VISIBLE) { |
| // Since we are managing the visibility, we need to ensure pages are |
| // unfrozen in the event this occurred while in the background. |
| web_contents->SetPageFrozen(false); |
| } |
| } |
| |
| void RuntimeApplicationBase::OnWindowShown() { |
| SetWebVisibilityAndPaint(true); |
| } |
| |
| void RuntimeApplicationBase::OnWindowHidden() { |
| SetWebVisibilityAndPaint(false); |
| } |
| |
| } // namespace cast_receiver |