Commit a6deb201 authored by Trent Apted's avatar Trent Apted Committed by Commit Bot

Scaffolding for ChromeOS chrome://media-app and chrome://media-app-guest.

Installs these routes as webui with basic structure and no permissions.

Code added as a component in //chromeos/components/media_app_ui so that
any chrome dependencies are clearly documented.

--enable-features=MediaApp is required to enable the app at runtime on
ChromeOS. The app is loaded as a guest (currently in an <iframe>) hosted
in another chrome:// WebUI host that may be granted privileges in
future. Currently the WebUI loaded in the <iframe> uses a chrome://
scheme, but this is not the long-term plan.

A js2gtest is added with a test fixture that enables these flags and
does integration testing.

A build-time flag, enable_cros_media_app determines the bulk of what is
served inside the sandboxed guest. When false, it provides a mock that
facilitates integration testing even when is_chrome_branded is false. An
internal change - https://crrev.com/i/1646974 - provides the content
when is_chrome_branded is true (enable_cros_media_app must be set
manually in args.gn until that lands).

Bug: 996088
Change-Id: Ied7a41795409bdc8c028e63155f37452be84532e
Test: MediaAppUIBrowserTest.WebviewHasAppElement
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1767457
Commit-Queue: Trent Apted <tapted@chromium.org>
Reviewed-by: default avatarDan Beam <dbeam@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#697850}
parent d641e555
...@@ -1912,6 +1912,7 @@ jumbo_split_static_library("ui") { ...@@ -1912,6 +1912,7 @@ jumbo_split_static_library("ui") {
"//chromeos/assistant:buildflags", "//chromeos/assistant:buildflags",
"//chromeos/audio", "//chromeos/audio",
"//chromeos/components/account_manager", "//chromeos/components/account_manager",
"//chromeos/components/media_app_ui",
"//chromeos/components/multidevice", "//chromeos/components/multidevice",
"//chromeos/components/multidevice/debug_webui", "//chromeos/components/multidevice/debug_webui",
"//chromeos/components/multidevice/logging", "//chromeos/components/multidevice/logging",
......
...@@ -183,6 +183,9 @@ ...@@ -183,6 +183,9 @@
#include "chrome/browser/ui/webui/chromeos/terminal/terminal_ui.h" #include "chrome/browser/ui/webui/chromeos/terminal/terminal_ui.h"
#include "chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h"
#include "chrome/browser/ui/webui/signin/inline_login_ui.h" #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
#include "chromeos/components/media_app_ui/media_app_guest_ui.h"
#include "chromeos/components/media_app_ui/media_app_ui.h"
#include "chromeos/components/media_app_ui/url_constants.h"
#include "chromeos/components/multidevice/debug_webui/proximity_auth_ui.h" #include "chromeos/components/multidevice/debug_webui/proximity_auth_ui.h"
#include "chromeos/components/multidevice/debug_webui/url_constants.h" #include "chromeos/components/multidevice/debug_webui/url_constants.h"
#include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_features.h"
...@@ -559,6 +562,12 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, ...@@ -559,6 +562,12 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
return &NewWebUI<chromeos::settings::OSSettingsUI>; return &NewWebUI<chromeos::settings::OSSettingsUI>;
if (url.host_piece() == chrome::kChromeUIPowerHost) if (url.host_piece() == chrome::kChromeUIPowerHost)
return &NewWebUI<chromeos::PowerUI>; return &NewWebUI<chromeos::PowerUI>;
if (base::FeatureList::IsEnabled(chromeos::features::kMediaApp)) {
if (url.host_piece() == chromeos::kChromeUIMediaAppHost)
return &NewWebUI<chromeos::MediaAppUI>;
if (url.host_piece() == chromeos::kChromeUIMediaAppGuestHost)
return &NewWebUI<chromeos::MediaAppGuestUI>;
}
if (url.host_piece() == chromeos::multidevice::kChromeUIProximityAuthHost) if (url.host_piece() == chromeos::multidevice::kChromeUIProximityAuthHost)
return &NewWebUI<chromeos::multidevice::ProximityAuthUI>; return &NewWebUI<chromeos::multidevice::ProximityAuthUI>;
if (url.host_piece() == chrome::kChromeUIInternetConfigDialogHost) if (url.host_piece() == chrome::kChromeUIInternetConfigDialogHost)
......
...@@ -1434,6 +1434,9 @@ if (!is_android) { ...@@ -1434,6 +1434,9 @@ if (!is_android) {
"//chrome/test/data/webui:browser_tests_js_mojo_lite_webui", "//chrome/test/data/webui:browser_tests_js_mojo_lite_webui",
"//chrome/test/data/webui:browser_tests_js_webui", "//chrome/test/data/webui:browser_tests_js_webui",
] ]
if (is_chromeos) {
deps += [ "//chromeos/components/media_app_ui:browser_tests_js" ]
}
} }
if (!is_fuchsia) { if (!is_fuchsia) {
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//chrome/test/base/js2gtest.gni")
assert(is_chromeos, "Media App is Chrome OS only")
static_library("media_app_ui") {
sources = [
"media_app_guest_ui.cc",
"media_app_guest_ui.h",
"media_app_ui.cc",
"media_app_ui.h",
"url_constants.cc",
"url_constants.h",
]
deps = [
"//chromeos/constants",
"//chromeos/resources",
"//content/public/browser",
"//ui/webui",
]
}
js2gtest("browser_tests_js") {
test_type = "mojo_lite_webui"
sources = [
"test/media_app_ui_browsertest.js",
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
deps = [
"//chromeos/constants",
]
}
include_rules = [
# Do not add chrome here (use a delegate instead).
"+chromeos/grit/chromeos_resources.h",
"+content/public/browser",
"+ui/webui",
]
Provides the chrome://media-app WebUI and supporting interfaces that allow the
Media App to integrate with ChromeOS.
The bulk of the UI is provided by an internal CIPD package via DEPS, which is
then served via the resource bundle. See go/media-app for the internal source.
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/media_app_ui/media_app_guest_ui.h"
#include "chromeos/components/media_app_ui/url_constants.h"
#include "chromeos/grit/chromeos_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
namespace chromeos {
// static
content::WebUIDataSource* MediaAppGuestUI::CreateDataSource() {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(kChromeUIMediaAppGuestHost);
source->AddResourcePath("app.html", IDR_MEDIA_APP_APP_HTML);
source->AddResourcePath("js/app_main.js", IDR_MEDIA_APP_APP_JS);
source->AddResourcePath("js/app_image_handler_module.js",
IDR_MEDIA_APP_IMAGE_HANDLER_MODULE_JS);
source->AddResourcePath("js/app_drop_target_module.js",
IDR_MEDIA_APP_DROP_TARGET_MODULE_JS);
return source;
}
MediaAppGuestUI::MediaAppGuestUI(content::WebUI* web_ui)
: MojoWebUIController(web_ui) {
content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
CreateDataSource());
}
MediaAppGuestUI::~MediaAppGuestUI() = default;
} // namespace chromeos
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_GUEST_UI_H_
#define CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_GUEST_UI_H_
#include "base/macros.h"
#include "ui/webui/mojo_web_ui_controller.h"
namespace content {
class WebUIDataSource;
}
namespace chromeos {
// The WebUI controller for chrome://media-app-guest.
class MediaAppGuestUI : public ui::MojoWebUIController {
public:
static content::WebUIDataSource* CreateDataSource();
explicit MediaAppGuestUI(content::WebUI* web_ui);
~MediaAppGuestUI() override;
private:
DISALLOW_COPY_AND_ASSIGN(MediaAppGuestUI);
};
} // namespace chromeos
#endif // CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_GUEST_UI_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/media_app_ui/media_app_ui.h"
#include "chromeos/components/media_app_ui/media_app_guest_ui.h"
#include "chromeos/components/media_app_ui/url_constants.h"
#include "chromeos/grit/chromeos_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
namespace chromeos {
namespace {
content::WebUIDataSource* CreateHostDataSource() {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(kChromeUIMediaAppHost);
source->SetDefaultResource(IDR_MEDIA_APP_INDEX_HTML);
return source;
}
} // namespace
MediaAppUI::MediaAppUI(content::WebUI* web_ui) : MojoWebUIController(web_ui) {
content::BrowserContext* browser_context =
web_ui->GetWebContents()->GetBrowserContext();
content::WebUIDataSource* host_source = CreateHostDataSource();
content::WebUIDataSource::Add(browser_context, host_source);
// Whilst the guest is in an <iframe> rather than a <webview>, we need a CSP
// override to use the guest origin in the host.
// TODO(crbug/996088): Remove these overrides when there's a new sandboxing
// option for the guest.
std::string csp = std::string("frame-src ") + kChromeUIMediaAppGuestURL + ";";
host_source->OverrideContentSecurityPolicyChildSrc(csp);
// We also add the guest data source here (and allow it to be iframed). If
// it's only added in the MediaAppGuestUI constructor, then a user navigation
// to chrome://media-app-guest is required before the <iframe> can see it.
// This is due to logic in RenderFrameHostManager::GetFrameHostForNavigation()
// that currently skips creating webui objects when !IsMainFrame() (which is
// temporary according to https://crbug.com/713313 but, long-term, the guest
// shouldn't need the webui objects in any case - just the data source).
content::WebUIDataSource* guest_source = MediaAppGuestUI::CreateDataSource();
guest_source->DisableDenyXFrameOptions();
content::WebUIDataSource::Add(browser_context, guest_source);
}
MediaAppUI::~MediaAppUI() = default;
} // namespace chromeos
import("//build/config/chrome_build.gni")
declare_args() {
# Whether to enable the "real" ChromeOS Media App. When false, a mock app is
# bundled for testing integration points.
# TODO(crbug/996088): Change this to is_chromeos && is_chrome_branded.
enable_cros_media_app = false
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_UI_H_
#define CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_UI_H_
#include "base/macros.h"
#include "ui/webui/mojo_web_ui_controller.h"
namespace chromeos {
// The WebUI controller for chrome://media-app.
class MediaAppUI : public ui::MojoWebUIController {
public:
explicit MediaAppUI(content::WebUI* web_ui);
~MediaAppUI() override;
private:
DISALLOW_COPY_AND_ASSIGN(MediaAppUI);
};
} // namespace chromeos
#endif // CHROMEOS_COMPONENTS_MEDIA_APP_UI_MEDIA_APP_UI_H_
<!-- Copyright 2019 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<!DOCTYPE html>
<style>
body {
overflow: hidden;
margin: 0;
}
</style>
<backlight-app></backlight-app>
<script src="/js/app_main.js"></script>
<!-- TODO(crbug/996088): Include scripts to run in the <webview> sandbox that
communicate via postMessage to scripts running in index.html -->
<!-- Copyright 2019 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<!DOCTYPE html>
<meta charset="utf-8">
<title>Media App</title>
<style>
body {
height: 100vh;
margin: 0;
overflow: hidden;
}
/*
* This is the <iframe> style set for sandboxed guests that use
* guest_view_iframe_container.js.
* TODO(crbug/996088): Remove the iframe styles if switched to <webview>.
*/
iframe {
border: 0;
height: 100%;
width: 100%;
}
</style>
<script src="chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js"></script>
<iframe src="chrome://media-app-guest/app.html"></iframe>
<!-- TODO(crbug/996088): Include scripts running with privileges necessary to
provide functionality to the sandboxed guest. -->
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* Provides a mock of http://go/media-app/index.ts which is pre-built and
* brought in via DEPS to ../../app/js/app_main.js. Runs in an isolated guest.
*/
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
* An empty script used when mocking that provides no functionality, but avoids
* a 404 error.
*/
include_rules = [
# Tests run in browser_tests, so can access things under chrome/test/base*.
"+chrome/test/base",
]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test suite for chrome://media-app.
*/
GEN('#include "chromeos/constants/chromeos_features.h"');
const HOST_ORIGIN = 'chrome://media-app';
const GUEST_SRC = 'chrome://media-app-guest/app.html';
var MediaAppUIBrowserTest = class extends testing.Test {
/** @override */
get browsePreload() {
return HOST_ORIGIN;
}
/** @override */
get featureList() {
return {enabled: ['chromeos::features::kMediaApp']};
}
/** @override */
get runAccessibilityChecks() {
return false;
}
};
// Tests that chrome://media-app is allowed to frame chrome://media-app-guest.
// The URL is set in the html. If that URL can't load, test this fails like JS
// ERROR: "Refused to frame '...' because it violates the following Content
// Security Policy directive: "frame-src chrome://media-app-guest/".
// This test also fails if the guest renderer is terminated, e.g., due to webui
// performing bad IPC such as network requests (failure detected in
// content/public/test/no_renderer_crashes_assertion.cc).
TEST_F('MediaAppUIBrowserTest', 'GuestCanLoad', () => {
const guest = document.querySelector('iframe');
assertEquals(document.location.origin, HOST_ORIGIN);
assertEquals(guest.src, GUEST_SRC);
});
// TODO(crbug/996088): Add Tests that inspect the guest itself. Until the guest
// listens for postMessage calls, we can't test for anything interesting due to
// CORS (i.e. "Blocked a frame with origin chrome://media-app from accessing a
// cross-origin frame.").
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/media_app_ui/url_constants.h"
namespace chromeos {
const char kChromeUIMediaAppHost[] = "media-app";
const char kChromeUIMediaAppURL[] = "chrome://media-app/";
const char kChromeUIMediaAppGuestHost[] = "media-app-guest";
const char kChromeUIMediaAppGuestURL[] = "chrome://media-app-guest/";
} // namespace chromeos
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_COMPONENTS_MEDIA_APP_UI_URL_CONSTANTS_H_
#define CHROMEOS_COMPONENTS_MEDIA_APP_UI_URL_CONSTANTS_H_
namespace chromeos {
extern const char kChromeUIMediaAppHost[];
extern const char kChromeUIMediaAppURL[];
extern const char kChromeUIMediaAppGuestHost[];
extern const char kChromeUIMediaAppGuestURL[];
} // namespace chromeos
#endif // CHROMEOS_COMPONENTS_MEDIA_APP_UI_URL_CONSTANTS_H_
...@@ -146,6 +146,9 @@ const base::Feature kImeDecoderWithSandbox{"ImeDecoderWithSandbox", ...@@ -146,6 +146,9 @@ const base::Feature kImeDecoderWithSandbox{"ImeDecoderWithSandbox",
const base::Feature kInstantTethering{"InstantTethering", const base::Feature kInstantTethering{"InstantTethering",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
// ChromeOS Media App. https://crbug.com/996088.
const base::Feature kMediaApp{"MediaApp", base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether to enable the Parental Controls section of settings. // Controls whether to enable the Parental Controls section of settings.
const base::Feature kParentalControlsSettings{ const base::Feature kParentalControlsSettings{
"ChromeOSParentalControlsSettings", base::FEATURE_DISABLED_BY_DEFAULT}; "ChromeOSParentalControlsSettings", base::FEATURE_DISABLED_BY_DEFAULT};
......
...@@ -70,6 +70,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) ...@@ -70,6 +70,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kImeDecoderWithSandbox; extern const base::Feature kImeDecoderWithSandbox;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kInstantTethering; extern const base::Feature kInstantTethering;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kMediaApp;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kParentalControlsSettings; extern const base::Feature kParentalControlsSettings;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//chromeos/assistant/assistant.gni") import("//chromeos/assistant/assistant.gni")
import("//chromeos/components/media_app_ui/media_app_ui.gni")
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos") assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
...@@ -20,7 +21,10 @@ grit("resources") { ...@@ -20,7 +21,10 @@ grit("resources") {
] ]
output_dir = "$root_gen_dir/chromeos" output_dir = "$root_gen_dir/chromeos"
defines = [ "enable_cros_libassistant=$enable_cros_libassistant" ] defines = [
"enable_cros_libassistant=$enable_cros_libassistant",
"enable_cros_media_app=$enable_cros_media_app",
]
grit_flags = [ grit_flags = [
"-E", "-E",
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
<if expr="enable_cros_libassistant"> <if expr="enable_cros_libassistant">
<part file="assistant_resources.grdp" /> <part file="assistant_resources.grdp" />
</if> </if>
<part file="media_app_resources.grdp" />
</includes> </includes>
</release> </release>
</grit> </grit>
<?xml version="1.0" encoding="utf-8"?>
<grit-part>
<include name="IDR_MEDIA_APP_INDEX_HTML"
file="../components/media_app_ui/resources/index.html"
type="BINDATA" />
<include name="IDR_MEDIA_APP_APP_HTML"
file="../components/media_app_ui/resources/app.html"
type="BINDATA" />
<if expr="enable_cros_media_app">
<then>
<include name="IDR_MEDIA_APP_APP_JS"
file="../components/media_app_ui/resources/app/js/app_main.js"
type="BINDATA" />
<include name="IDR_MEDIA_APP_IMAGE_HANDLER_MODULE_JS"
file="../components/media_app_ui/resources/app/js/app_image_handler_module.js"
type="BINDATA" />
<include name="IDR_MEDIA_APP_DROP_TARGET_MODULE_JS"
file="../components/media_app_ui/resources/app/js/app_drop_target_module.js"
type="BINDATA" />
</then>
<else>
<include name="IDR_MEDIA_APP_APP_JS"
file="../components/media_app_ui/resources/mock/js/app_main.js"
type="BINDATA" />
<include name="IDR_MEDIA_APP_IMAGE_HANDLER_MODULE_JS"
file="../components/media_app_ui/resources/mock/js/mock_module.js"
type="BINDATA" />
<include name="IDR_MEDIA_APP_DROP_TARGET_MODULE_JS"
file="../components/media_app_ui/resources/mock/js/mock_module.js"
type="BINDATA" />
</else>
</if>
</grit-part>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment