chromium/components/cast_receiver/browser/web_runtime_application.cc

// 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/web_runtime_application.h"

#include "base/task/bind_post_task.h"
#include "components/cast_receiver/browser/public/embedder_application.h"
#include "components/cast_receiver/browser/public/message_port_service.h"
#include "components/url_rewrite/browser/url_request_rewrite_rules_manager.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller_factory.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "net/base/net_errors.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"

namespace cast_receiver {

WebRuntimeApplication::WebRuntimeApplication(
    std::string cast_session_id,
    ApplicationConfig config,
    ApplicationClient& application_client)
    : RuntimeApplicationBase(std::move(cast_session_id),
                             std::move(config),
                             application_client) {
  DCHECK(app_url().is_valid());
}

WebRuntimeApplication::~WebRuntimeApplication() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  StopApplication(EmbedderApplication::ApplicationStopReason::kUserRequest,
                  net::ERR_ABORTED);
}

void WebRuntimeApplication::Launch(StatusCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  DVLOG(1) << "Launching application: " << *this;

  SetContentPermissions(*embedder_application().GetWebContents());

  // Register GrpcWebUI for handling Cast apps with URLs in the form
  // chrome*://* that use WebUIs.
  auto web_ui_controller_factory =
      embedder_application().CreateWebUIControllerFactory(
          {"home", "error", "cast_resources"});
  if (web_ui_controller_factory) {
    content::WebUIControllerFactory::RegisterFactory(
        web_ui_controller_factory.release());
  }

  embedder_application().GetAllBindings(base::BindPostTask(
      task_runner(),
      base::BindOnce(&WebRuntimeApplication::OnAllBindingsReceived,
                     weak_factory_.GetWeakPtr())));

  // Signal that application is launching.
  std::move(callback).Run(OkStatus());
}

bool WebRuntimeApplication::IsStreamingApplication() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return false;
}

void WebRuntimeApplication::InnerWebContentsCreated(
    content::WebContents* inner_web_contents) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(inner_web_contents);

  DVLOG(1) << "Inner web contents created";

  auto* outer_web_contents = embedder_application().GetWebContents();
  if (outer_web_contents) {
    url_rewrite::UrlRequestRewriteRulesManager&
        url_request_rewrite_rules_manager =
            GetApplicationControls().GetUrlRequestRewriteRulesManager();
    SetContentPermissions(*inner_web_contents);
    url_request_rewrite_rules_manager.AddWebContents(inner_web_contents);
  }
}

void WebRuntimeApplication::MediaStartedPlaying(
    const MediaPlayerInfo& video_type,
    const content::MediaPlayerId& id) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (IsApplicationRunning()) {
    embedder_application().NotifyMediaPlaybackChanged(true);
  }
}

void WebRuntimeApplication::MediaStoppedPlaying(
    const MediaPlayerInfo& video_type,
    const content::MediaPlayerId& id,
    content::WebContentsObserver::MediaStoppedReason reason) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (IsApplicationRunning()) {
    embedder_application().NotifyMediaPlaybackChanged(false);
  }
}

void WebRuntimeApplication::OnAllBindingsReceived(
    Status status,
    std::vector<std::string> bindings) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!status.ok()) {
    LOG(ERROR) << "Failed to get all bindings: " << status;
    StopApplication(EmbedderApplication::ApplicationStopReason::kRuntimeError,
                    net::ERR_FAILED);
    return;
  }

  content::WebContentsObserver::Observe(
      embedder_application().GetWebContents());
  PageStateObserver::Observe(embedder_application().GetWebContents());
  auto* message_port_sevice = embedder_application().GetMessagePortService();
  DCHECK(message_port_sevice);
  bindings_manager_ =
      std::make_unique<BindingsManager>(*this, *message_port_sevice);
  for (auto& binding : bindings) {
    bindings_manager_->AddBinding(std::move(binding));
  }
  bindings_manager_->ConfigureWebContents(
      embedder_application().GetWebContents());

  // Application is initialized now - we can load the URL.
  NavigateToPage(app_url());
}

void WebRuntimeApplication::OnPageLoadComplete() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  OnPageNavigationComplete();
}

void WebRuntimeApplication::OnError() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  StopApplication(EmbedderApplication::ApplicationStopReason::kRuntimeError,
                  net::ERR_UNEXPECTED);
}

void WebRuntimeApplication::OnPageStopped(StopReason reason,
                                          net::Error error_code) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  switch (reason) {
    case PageStateObserver::StopReason::kUnknown:
      StopApplication(EmbedderApplication::ApplicationStopReason::kRuntimeError,
                      error_code);
      break;
    case PageStateObserver::StopReason::kApplicationRequest:
      StopApplication(
          EmbedderApplication::ApplicationStopReason::kApplicationRequest,
          error_code);
      break;
    case PageStateObserver::StopReason::kHttpError:
      StopApplication(EmbedderApplication::ApplicationStopReason::kHttpError,
                      error_code);
      break;
  }
}

}  // namespace cast_receiver