Implementation of chrome://cast WebUI.
Spec: go/cast-setup-chrome-webui

BUG=611758
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/1820023002
Cr-Commit-Position: refs/heads/master@{#396501}
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 297fcc2..6e840445 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -518,6 +518,12 @@
       <if expr="enable_media_router">
         <part file="media_router_resources.grdp" />
       </if>
+      <if expr="enable_media_router and (chromeos or is_win or is_macosx)">
+        <include name="IDR_CAST_HTML" file="resources\cast\cast.html" type="BINDATA" />
+        <include name="IDR_CAST_CSS" file="resources\cast\cast.css" type="BINDATA" />
+        <include name="IDR_CAST_JS" file="resources\cast\cast.js" type="BINDATA" />
+        <include name="IDR_CAST_FAVICON" file="resources\cast\cast_favicon.ico" type="BINDATA" />
+      </if>
       <if expr="not is_android and not is_ios and not chromeos">
         <include name="IDR_IME_WINDOW_CLOSE" file="resources\input_ime\ime_window_close.png" type="BINDATA" />
         <include name="IDR_IME_WINDOW_CLOSE_C" file="resources\input_ime\ime_window_close_click.png" type="BINDATA" />
diff --git a/chrome/browser/resources/cast/OWNERS b/chrome/browser/resources/cast/OWNERS
new file mode 100644
index 0000000..5b34f4712
--- /dev/null
+++ b/chrome/browser/resources/cast/OWNERS
@@ -0,0 +1 @@
+sheretov@chromium.org
diff --git a/chrome/browser/resources/cast/cast.css b/chrome/browser/resources/cast/cast.css
new file mode 100644
index 0000000..5193f40
--- /dev/null
+++ b/chrome/browser/resources/cast/cast.css
@@ -0,0 +1,18 @@
+/* Copyright 2016 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. */
+
+body,
+extensionview,
+html  {
+  border: 0;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+
+extensionview {
+  overflow: hidden;
+  position: absolute;
+}
diff --git a/chrome/browser/resources/cast/cast.html b/chrome/browser/resources/cast/cast.html
new file mode 100644
index 0000000..e7afdd9
--- /dev/null
+++ b/chrome/browser/resources/cast/cast.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html i18n-values="dir:textdirection;lang:language">
+<head>
+  <meta charset="utf-8">
+  <title>Google Cast</title>
+  <link rel="stylesheet" href="cast.css">
+  <link rel="shortcut icon" href="cast_favicon.ico">
+  <script src="chrome://resources/js/load_time_data.js"></script>
+  <script src="strings.js"></script>
+  <script src="cast.js"></script>
+</head>
+<body>
+  <extensionview></extensionview>
+</body>
+</html>
diff --git a/chrome/browser/resources/cast/cast.js b/chrome/browser/resources/cast/cast.js
new file mode 100644
index 0000000..e7bd2059
--- /dev/null
+++ b/chrome/browser/resources/cast/cast.js
@@ -0,0 +1,40 @@
+// Copyright 2016 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.
+
+window.addEventListener('load', function init() {
+  var extensionView = document.querySelector('extensionview');
+
+  /**
+   * @param {string} str
+   * @return {!Array<string>}
+   */
+  var splitUrlOnHash = function(str) {
+    str = str || '';
+    var pos = str.indexOf('#');
+    return (pos !== -1) ? [str.substr(0, pos), str.substr(pos + 1)] : [str, ''];
+  };
+
+  new MutationObserver(function() {
+    var newHash = splitUrlOnHash(extensionView.getAttribute('src'))[1];
+    var oldHash = window.location.hash.substr(1);
+    if (newHash !== oldHash) {
+      window.location.hash = newHash;
+    }
+  }).observe(extensionView, {
+    attributes: true
+  });
+
+  window.addEventListener('hashchange', function() {
+    var newHash = window.location.hash.substr(1);
+    var extensionViewSrcParts = splitUrlOnHash(
+        extensionView.getAttribute('src'));
+    if (newHash !== extensionViewSrcParts[1]) {
+      extensionView.load(extensionViewSrcParts[0] + '#' + newHash);
+    }
+  });
+
+  extensionView.load('chrome-extension://' +
+      loadTimeData.getString('extensionId') + '/cast_setup/devices.html');
+});
+
diff --git a/chrome/browser/resources/cast/cast_favicon.ico b/chrome/browser/resources/cast/cast_favicon.ico
new file mode 100644
index 0000000..7f72553
--- /dev/null
+++ b/chrome/browser/resources/cast/cast_favicon.ico
Binary files differ
diff --git a/chrome/browser/ui/webui/cast/OWNERS b/chrome/browser/ui/webui/cast/OWNERS
new file mode 100644
index 0000000..5b34f4712
--- /dev/null
+++ b/chrome/browser/ui/webui/cast/OWNERS
@@ -0,0 +1 @@
+sheretov@chromium.org
diff --git a/chrome/browser/ui/webui/cast/cast_ui.cc b/chrome/browser/ui/webui/cast/cast_ui.cc
new file mode 100644
index 0000000..3b39ba6
--- /dev/null
+++ b/chrome/browser/ui/webui/cast/cast_ui.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 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 "chrome/browser/ui/webui/cast/cast_ui.h"
+
+#include "chrome/browser/media/router/media_router_factory.h"
+#include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "grit/browser_resources.h"
+#include "grit/generated_resources.h"
+
+CastUI::CastUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  // Retrieve the ID of the component extension.
+  // TODO(crbug.com/597778): remove reference to MediaRouterMojoImpl.
+  auto router = static_cast<media_router::MediaRouterMojoImpl*>(
+      media_router::MediaRouterFactory::GetApiForBrowserContext(
+          web_ui->GetWebContents()->GetBrowserContext()));
+  std::string extension_id = router->media_route_provider_extension_id();
+
+  // Set up the chrome://cast data source and add required resources.
+  content::WebUIDataSource* html_source =
+      content::WebUIDataSource::Create(chrome::kChromeUICastHost);
+
+  html_source->AddResourcePath("cast.css", IDR_CAST_CSS);
+  html_source->AddResourcePath("cast.js", IDR_CAST_JS);
+  html_source->AddResourcePath("cast_favicon.ico", IDR_CAST_FAVICON);
+  html_source->AddString("extensionId", extension_id);
+  html_source->SetJsonPath("strings.js");
+  html_source->SetDefaultResource(IDR_CAST_HTML);
+  html_source->OverrideContentSecurityPolicyObjectSrc("object-src *;");
+
+  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), html_source);
+}
+
+CastUI::~CastUI() {
+}
diff --git a/chrome/browser/ui/webui/cast/cast_ui.h b/chrome/browser/ui/webui/cast/cast_ui.h
new file mode 100644
index 0000000..2cae6dd
--- /dev/null
+++ b/chrome/browser/ui/webui/cast/cast_ui.h
@@ -0,0 +1,21 @@
+// Copyright 2016 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 CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI for chrome://cast
+class CastUI : public content::WebUIController {
+ public:
+  explicit CastUI(content::WebUI* web_ui);
+  ~CastUI() override;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastUI);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CAST_CAST_UI_H_
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 1bbf46d..1085b74 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -91,10 +91,15 @@
 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 #endif
 
-#if defined(ENABLE_MEDIA_ROUTER) && !defined(OS_ANDROID)
+#if defined(ENABLE_MEDIA_ROUTER)
+#if !defined(OS_ANDROID)
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/ui/webui/media_router/media_router_ui.h"
 #endif
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/cast/cast_ui.h"
+#endif
+#endif
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/ui/webui/net_export_ui.h"
@@ -567,12 +572,20 @@
   if (url.host() == chrome::kChromeUIWebRtcLogsHost)
     return &NewWebUI<WebRtcLogsUI>;
 #endif
-#if defined(ENABLE_MEDIA_ROUTER) && !defined(OS_ANDROID)
+#if defined(ENABLE_MEDIA_ROUTER)
+#if !defined(OS_ANDROID)
   if (url.host() == chrome::kChromeUIMediaRouterHost &&
       media_router::MediaRouterEnabled(profile)) {
     return &NewWebUI<media_router::MediaRouterUI>;
   }
 #endif
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+  if (url.host() == chrome::kChromeUICastHost &&
+      media_router::MediaRouterEnabled(profile)) {
+    return &NewWebUI<CastUI>;
+  }
+#endif
+#endif
   if (IsAboutUI(url))
     return &NewWebUI<AboutUI>;
 
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index c18d044..06f8ae9 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -728,6 +728,8 @@
       'browser/ui/views/select_file_dialog_extension.h',
       'browser/ui/views/select_file_dialog_extension_factory.cc',
       'browser/ui/views/select_file_dialog_extension_factory.h',
+      'browser/ui/webui/cast/cast_ui.cc',
+      'browser/ui/webui/cast/cast_ui.h',
     ],
     # ARC-only sources.
     'chrome_browser_ui_chromeos_arc_sources': [
@@ -1518,6 +1520,8 @@
       'browser/ui/passwords/manage_passwords_icon.cc',
       'browser/ui/passwords/manage_passwords_icon.h',
       'browser/ui/web_contents_sizer.mm',
+      'browser/ui/webui/cast/cast_ui.cc',
+      'browser/ui/webui/cast/cast_ui.h',
     ],
     'chrome_browser_ui_media_router_sources': [
       'browser/ui/toolbar/media_router_action.cc',
@@ -2552,6 +2556,8 @@
       'browser/ui/views/network_profile_bubble_view.cc',
       'browser/ui/views/uninstall_view.cc',
       'browser/ui/views/uninstall_view.h',
+      'browser/ui/webui/cast/cast_ui.cc',
+      'browser/ui/webui/cast/cast_ui.h',
       'browser/ui/webui/conflicts_ui.cc',
       'browser/ui/webui/conflicts_ui.h',
       'browser/ui/webui/set_as_default_browser_ui.cc',
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 4464a92..68c305a 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -145,6 +145,9 @@
 
 #if defined(ENABLE_MEDIA_ROUTER)
 const char kChromeUIMediaRouterURL[] = "chrome://media-router/";
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+const char kChromeUICastURL[] = "chrome://cast/";
+#endif
 #endif
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
@@ -329,6 +332,9 @@
 
 #if defined(ENABLE_MEDIA_ROUTER)
 const char kChromeUIMediaRouterHost[] = "media-router";
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+const char kChromeUICastHost[] = "cast";
+#endif
 #endif
 
 // Option sub pages.
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 38192c0..5088e56 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -135,6 +135,9 @@
 
 #if defined(ENABLE_MEDIA_ROUTER)
 extern const char kChromeUIMediaRouterURL[];
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+extern const char kChromeUICastURL[];
+#endif
 #endif
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
@@ -306,6 +309,9 @@
 
 #if defined(ENABLE_MEDIA_ROUTER)
 extern const char kChromeUIMediaRouterHost[];
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+extern const char kChromeUICastHost[];
+#endif
 #endif
 
 // Options sub-pages.
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 45ef8e5..ac12ff9 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -188,7 +188,10 @@
       "internal": true,
       "channel": "stable",
       "contexts": ["webui"],
-      "matches": ["chrome://media-router/*"]
+      "matches": [
+        "chrome://cast/*",
+        "chrome://media-router/*"
+      ]
     }
   ],
   "events": {
@@ -208,6 +211,7 @@
       "channel": "trunk",
       "contexts": ["webui"],
       "matches": [
+        "chrome://cast/*",
         "chrome://extensions-frame/*",
         "chrome://extensions/*",
         "chrome://chrome-signin/*",