Add a Physical Web WebUI

Adds a WebUI page that receives a list of URLs broadcast by
nearby devices along with associated page metadata and displays
them as a list of clickable items.

BUG=630769

Review-Url: https://codereview.chromium.org/2248913007
Cr-Commit-Position: refs/heads/master@{#416393}
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 0610650..2d620709 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -203,6 +203,7 @@
       <part file="pageinfo_strings.grdp" />
       <part file="password_manager_strings.grdp" />
       <part file="pdf_strings.grdp" />
+      <part file="physical_web_ui_strings.grdp" />
       <part file="policy_strings.grdp" />
       <part file="security_interstitials_strings.grdp" />
       <part file="ssl_errors_strings.grdp" />
diff --git a/components/physical_web.gypi b/components/physical_web.gypi
new file mode 100644
index 0000000..eb9e971
--- /dev/null
+++ b/components/physical_web.gypi
@@ -0,0 +1,30 @@
+# 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.
+
+{
+  'targets': [
+    {
+      # GN version: //components/physical_web/data_source
+      'target_name': 'physical_web_data_source',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+      ],
+      'sources': [
+        # Note: sources list duplicated in GN build.
+        'physical_web/data_source/physical_web_data_source.h',
+      ],
+    },
+    {
+      # GN version: //components/physical_web/webui
+      'target_name': 'physical_web_ui',
+      'type': 'static_library',
+      'sources': [
+        # Note: sources list duplicated in GN build.
+        'physical_web/webui/physical_web_ui_constants.cc',
+        'physical_web/webui/physical_web_ui_constants.h',
+      ],
+    },
+  ],
+}
diff --git a/components/physical_web/webui/BUILD.gn b/components/physical_web/webui/BUILD.gn
new file mode 100644
index 0000000..f3d6432
--- /dev/null
+++ b/components/physical_web/webui/BUILD.gn
@@ -0,0 +1,14 @@
+# 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.
+
+source_set("webui") {
+  sources = [
+    "physical_web_ui_constants.cc",
+    "physical_web_ui_constants.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
+}
diff --git a/components/physical_web/webui/physical_web_ui_constants.cc b/components/physical_web/webui/physical_web_ui_constants.cc
new file mode 100644
index 0000000..b16c4a9b
--- /dev/null
+++ b/components/physical_web/webui/physical_web_ui_constants.cc
@@ -0,0 +1,27 @@
+// 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 "components/physical_web/webui/physical_web_ui_constants.h"
+
+namespace physical_web_ui {
+
+// Resource paths.
+const char kPhysicalWebJS[] = "physical_web.js";
+const char kPhysicalWebCSS[] = "physical_web.css";
+
+// Message handlers.
+const char kRequestNearbyUrls[] = "requestNearbyURLs";
+
+// Strings.
+const char kTitle[] = "title";
+const char kPageInfo[] = "pageInfo";
+const char kPageInfoDescription[] = "pageInfoDescription";
+const char kPageInfoIcon[] = "pageInfoIcon";
+const char kPageInfoTitle[] = "pageInfoTitle";
+const char kResolvedUrl[] = "resolvedUrl";
+const char kScannedUrl[] = "scannedUrl";
+const char kReturnNearbyUrls[] = "returnNearbyURLs";
+const char kMetadata[] = "metadata";
+
+}  // namespace physical_web_ui
diff --git a/components/physical_web/webui/physical_web_ui_constants.h b/components/physical_web/webui/physical_web_ui_constants.h
new file mode 100644
index 0000000..8319801
--- /dev/null
+++ b/components/physical_web/webui/physical_web_ui_constants.h
@@ -0,0 +1,33 @@
+// 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 COMPONENTS_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_UI_CONSTANTS_H_
+#define COMPONENTS_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_UI_CONSTANTS_H_
+
+namespace physical_web_ui {
+
+// Resource paths.
+// Must match the resource file names.
+extern const char kPhysicalWebJS[];
+extern const char kPhysicalWebCSS[];
+
+// Message handlers.
+// Must match the constants used in the resource files.
+extern const char kRequestNearbyUrls[];
+
+// Strings.
+// Must match the constants used in the resource files.
+extern const char kTitle[];
+extern const char kPageInfo[];
+extern const char kPageInfoDescription[];
+extern const char kPageInfoIcon[];
+extern const char kPageInfoTitle[];
+extern const char kResolvedUrl[];
+extern const char kScannedUrl[];
+extern const char kReturnNearbyUrls[];
+extern const char kMetadata[];
+
+}  // namespace physical_web_ui
+
+#endif  // COMPONENTS_PHYSICAL_WEB_WEBUI_PHYSICAL_WEB_UI_CONSTANTS_H_
diff --git a/components/physical_web/webui/resources/physical_web.css b/components/physical_web/webui/resources/physical_web.css
new file mode 100644
index 0000000..9c5e888
--- /dev/null
+++ b/components/physical_web/webui/resources/physical_web.css
@@ -0,0 +1,50 @@
+/* Copyright (c) 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 {
+  font-family: Sans-Serif;
+  font-size: 12px;
+}
+.physicalWebTemplate {
+  display: flex;
+  width: 290px;
+  margin-bottom: 15px;
+  text-decoration: none;
+}
+
+.physicalWebIcon {
+  width: 32px;
+}
+.physicalWebText {
+  flex: 1;
+}
+.physicalWebIcon img {
+  padding-top: 1px;
+  width: 75%;
+}
+.physicalWebTemplate .title {
+  display: block;
+  color: #222;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-bottom: 3px;
+}
+.physicalWebTemplate .resolvedUrl {
+  display: block;
+  font-size: 80%;
+  color: #888;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-bottom: 4px;
+}
+.physicalWebTemplate .description {
+  display: block;
+  height: 2.5em;
+  font-size: 90%;
+  color: #444;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
diff --git a/components/physical_web/webui/resources/physical_web.html b/components/physical_web/webui/resources/physical_web.html
new file mode 100644
index 0000000..61e8488
--- /dev/null
+++ b/components/physical_web/webui/resources/physical_web.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html i18n-values="dir:textdirection;lang:language">
+<head>
+<meta charset="utf-8">
+<title i18n-content="title"></title>
+<if expr="is_android or is_ios">
+<meta name="viewport" content="width=device-width, user-scalable=no">
+</if>
+<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+<link rel="stylesheet" href="chrome://physical-web/physical_web.css">
+
+<if expr="is_ios">
+<!-- TODO(crbug.com/487000): Remove this once injected by web. -->
+<script src="chrome://resources/js/ios/web_ui.js"></script>
+</if>
+
+<script src="chrome://resources/js/load_time_data.js"></script>
+<script src="chrome://resources/js/util.js"></script>
+<script src="chrome://physical-web/physical_web.js"></script>
+<script src="chrome://physical-web/strings.js"></script>
+</head>
+<body>
+<div id="body-container" style="visibility:visible">
+
+  <h1 i18n-content="title"></h1>
+
+  <a class="physicalWebTemplate" id="physicalWebTemplate" jsvalues="href:resolvedUrl" jsselect="metadata">
+    <div class="physicalWebIcon">
+      <img jsvalues="src:icon" />
+    </div>
+    <div class="physicalWebText">
+      <div class="title" jscontent="title"></div>
+      <div class="resolvedUrl" jscontent="resolvedUrl"></div>
+      <div class="description" jscontent="description"></div>
+    </div>
+  </a>
+
+</div>
+
+<script src="chrome://resources/js/i18n_template.js"></script>
+<script src="chrome://resources/js/jstemplate_compiled.js"></script>
+</body>
+</html>
diff --git a/components/physical_web/webui/resources/physical_web.js b/components/physical_web/webui/resources/physical_web.js
new file mode 100644
index 0000000..459fb18
--- /dev/null
+++ b/components/physical_web/webui/resources/physical_web.js
@@ -0,0 +1,31 @@
+// Copyright (c) 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.
+
+/**
+ * Takes the |nearbyUrlsData| input argument which holds metadata for web pages
+ * broadcast by nearby devices.
+ * @param {Object} nearbyUrlsData Information about web pages broadcast by
+ *      nearby devices
+ */
+function renderTemplate(nearbyUrlsData) {
+  // This is the javascript code that processes the template:
+  jstProcess(new JsEvalContext(nearbyUrlsData), $('physicalWebTemplate'));
+}
+
+function requestNearbyURLs() {
+  chrome.send('requestNearbyURLs');
+}
+
+function handleClickPhysicalWebItem(node) {
+  // do something
+}
+
+function returnNearbyURLs(nearbyUrlsData) {
+  var bodyContainer = $('body-container');
+  renderTemplate(nearbyUrlsData);
+
+  bodyContainer.style.visibility = 'visible';
+}
+
+document.addEventListener('DOMContentLoaded', requestNearbyURLs);
diff --git a/components/physical_web_ui_strings.grdp b/components/physical_web_ui_strings.grdp
new file mode 100644
index 0000000..cc34d51
--- /dev/null
+++ b/components/physical_web_ui_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_PHYSICAL_WEB_UI_TITLE" desc="Title of a built-in page that displays a list of Physical Web URLs (URLs broadcast by nearby devices).">
+    Physical Web
+  </message>
+</grit-part>
diff --git a/components/resources/components_resources.grd b/components/resources/components_resources.grd
index 3291db2..4a7e4df 100644
--- a/components/resources/components_resources.grd
+++ b/components/resources/components_resources.grd
@@ -15,6 +15,7 @@
       <part file="gcm_driver_resources.grdp" />
       <part file="net_log_resources.grdp" />
       <part file="neterror_resources.grdp" />
+      <part file="physical_web_ui_resources.grdp" />
       <part file="printing_resources.grdp" />
       <part file="proximity_auth_resources.grdp" />
       <part file="security_interstitials_resources.grdp" />
diff --git a/components/resources/physical_web_ui_resources.grdp b/components/resources/physical_web_ui_resources.grdp
new file mode 100644
index 0000000..0da8416
--- /dev/null
+++ b/components/resources/physical_web_ui_resources.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <include name="IDR_PHYSICAL_WEB_UI_HTML" file="../physical_web/webui/resources/physical_web.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+  <include name="IDR_PHYSICAL_WEB_UI_JS" file="../physical_web/webui/resources/physical_web.js" type="BINDATA" />
+  <include name="IDR_PHYSICAL_WEB_UI_CSS" file="../physical_web/webui/resources/physical_web.css" type="BINDATA" />
+</grit-part>
\ No newline at end of file
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index d031d0b..35c4faf 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -564,6 +564,8 @@
     "ui/webui/mojo_web_ui_ios_controller.h",
     "ui/webui/net_export/net_export_ui.cc",
     "ui/webui/net_export/net_export_ui.h",
+    "ui/webui/physical_web_ui.cc",
+    "ui/webui/physical_web_ui.h",
     "ui/webui/sync_internals/sync_internals_message_handler.cc",
     "ui/webui/sync_internals/sync_internals_message_handler.h",
     "ui/webui/sync_internals/sync_internals_ui.cc",
@@ -653,6 +655,7 @@
     "//components/password_manager/core/common",
     "//components/password_manager/sync/browser",
     "//components/physical_web/data_source",
+    "//components/physical_web/webui",
     "//components/policy/core/common",
     "//components/pref_registry",
     "//components/prefs",
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index 471301ae..926634a 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -45,6 +45,7 @@
   "+components/password_manager/core/common",
   "+components/password_manager/sync/browser",
   "+components/physical_web/data_source",
+  "+components/physical_web/webui",
   "+components/pref_registry",
   "+components/prefs",
   "+components/profile_metrics",
diff --git a/ios/chrome/browser/chrome_url_constants.cc b/ios/chrome/browser/chrome_url_constants.cc
index 9767f871..1dc2079 100644
--- a/ios/chrome/browser/chrome_url_constants.cc
+++ b/ios/chrome/browser/chrome_url_constants.cc
@@ -17,6 +17,7 @@
 const char kChromeUIFlagsURL[] = "chrome://flags/";
 const char kChromeUIHistoryURL[] = "chrome://history/";
 const char kChromeUINewTabURL[] = "chrome://newtab/";
+const char kChromeUIPhysicalWebURL[] = "chrome://physical-web/";
 const char kChromeUISettingsURL[] = "chrome://settings/";
 const char kChromeUITermsURL[] = "chrome://terms/";
 const char kChromeUIVersionURL[] = "chrome://version/";
@@ -36,6 +37,7 @@
 const char kChromeUINetExportHost[] = "net-export";
 const char kChromeUINewTabHost[] = "newtab";
 const char kChromeUIOmahaHost[] = "omaha";
+const char kChromeUIPhysicalWebHost[] = "physical-web";
 const char kChromeUIPolicyHost[] = "policy";
 const char kChromeUISignInInternalsHost[] = "signin-internals";
 const char kChromeUISyncInternalsHost[] = "sync-internals";
@@ -46,9 +48,11 @@
 // These hosts will also be suggested by BuiltinProvider.
 // 'histograms' is chrome WebUI on iOS, content WebUI on other platforms.
 const char* const kChromeHostURLs[] = {
-    kChromeUIBookmarksHost, kChromeUIChromeURLsHost,    kChromeUICreditsHost,
-    kChromeUIFlagsHost,     kChromeUIHistogramHost,     kChromeUINetExportHost,
-    kChromeUINewTabHost,    kChromeUISyncInternalsHost, kChromeUITermsHost,
+    kChromeUIBookmarksHost,   kChromeUIChromeURLsHost,
+    kChromeUICreditsHost,     kChromeUIFlagsHost,
+    kChromeUIHistogramHost,   kChromeUINetExportHost,
+    kChromeUINewTabHost,      kChromeUISyncInternalsHost,
+    kChromeUIPhysicalWebHost, kChromeUITermsHost,
     kChromeUIVersionHost,
 };
 const size_t kNumberOfChromeHostURLs = arraysize(kChromeHostURLs);
diff --git a/ios/chrome/browser/chrome_url_constants.h b/ios/chrome/browser/chrome_url_constants.h
index bfb289e..3960571 100644
--- a/ios/chrome/browser/chrome_url_constants.h
+++ b/ios/chrome/browser/chrome_url_constants.h
@@ -27,6 +27,7 @@
 extern const char kChromeUIFlagsURL[];
 extern const char kChromeUIHistoryURL[];
 extern const char kChromeUINewTabURL[];
+extern const char kChromeUIPhysicalWebURL[];
 extern const char kChromeUISettingsURL[];
 extern const char kChromeUITermsURL[];
 extern const char kChromeUIVersionURL[];
@@ -47,6 +48,7 @@
 extern const char kChromeUINetExportHost[];
 extern const char kChromeUINewTabHost[];
 extern const char kChromeUIOmahaHost[];
+extern const char kChromeUIPhysicalWebHost[];
 extern const char kChromeUIPolicyHost[];
 extern const char kChromeUISignInInternalsHost[];
 extern const char kChromeUISyncInternalsHost[];
diff --git a/ios/chrome/browser/ui/webui/physical_web_ui.cc b/ios/chrome/browser/ui/webui/physical_web_ui.cc
new file mode 100644
index 0000000..3c807ee
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/physical_web_ui.cc
@@ -0,0 +1,99 @@
+// 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 "ios/chrome/browser/ui/webui/physical_web_ui.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/values.h"
+#include "components/grit/components_resources.h"
+#include "components/physical_web/data_source/physical_web_data_source.h"
+#include "components/physical_web/webui/physical_web_ui_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/web/public/web_ui_ios_data_source.h"
+#include "ios/web/public/webui/web_ui_ios.h"
+#include "ios/web/public/webui/web_ui_ios_message_handler.h"
+
+namespace {
+
+web::WebUIIOSDataSource* CreatePhysicalWebUIDataSource() {
+  web::WebUIIOSDataSource* html_source =
+      web::WebUIIOSDataSource::Create(kChromeUIPhysicalWebHost);
+
+  // Localized and data strings.
+  html_source->AddLocalizedString(physical_web_ui::kTitle,
+                                  IDS_PHYSICAL_WEB_UI_TITLE);
+
+  html_source->SetJsonPath("strings.js");
+  html_source->AddResourcePath(physical_web_ui::kPhysicalWebJS,
+                               IDR_PHYSICAL_WEB_UI_JS);
+  html_source->AddResourcePath(physical_web_ui::kPhysicalWebCSS,
+                               IDR_PHYSICAL_WEB_UI_CSS);
+  html_source->SetDefaultResource(IDR_PHYSICAL_WEB_UI_HTML);
+  return html_source;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// PhysicalWebDOMHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// The handler for Javascript messages for the chrome:physical-web page.
+class PhysicalWebDOMHandler : public web::WebUIIOSMessageHandler {
+ public:
+  PhysicalWebDOMHandler() {}
+  ~PhysicalWebDOMHandler() override {}
+
+  void RegisterMessages() override;
+
+  void HandleRequestNearbyURLs(const base::ListValue* args);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PhysicalWebDOMHandler);
+};
+
+void PhysicalWebDOMHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      physical_web_ui::kRequestNearbyUrls,
+      base::Bind(&PhysicalWebDOMHandler::HandleRequestNearbyURLs,
+                 base::Unretained(this)));
+}
+
+void PhysicalWebDOMHandler::HandleRequestNearbyURLs(
+    const base::ListValue* args) {
+  base::DictionaryValue results;
+
+  std::unique_ptr<base::ListValue> metadata =
+      GetApplicationContext()->GetPhysicalWebDataSource()->GetMetadata();
+
+  results.Set(physical_web_ui::kMetadata, metadata.release());
+
+  web_ui()->CallJavascriptFunction(physical_web_ui::kReturnNearbyUrls, results);
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// PhysicalWebUI
+//
+////////////////////////////////////////////////////////////////////////////////
+
+PhysicalWebUI::PhysicalWebUI(web::WebUIIOS* web_ui)
+    : web::WebUIIOSController(web_ui) {
+  PhysicalWebDOMHandler* handler = new PhysicalWebDOMHandler();
+  web_ui->AddMessageHandler(handler);
+
+  web::WebUIIOSDataSource::Add(ios::ChromeBrowserState::FromWebUIIOS(web_ui),
+                               CreatePhysicalWebUIDataSource());
+
+  base::RecordAction(base::UserMetricsAction("PhysicalWeb.WebUI.Open"));
+}
+
+PhysicalWebUI::~PhysicalWebUI() {}
diff --git a/ios/chrome/browser/ui/webui/physical_web_ui.h b/ios/chrome/browser/ui/webui/physical_web_ui.h
new file mode 100644
index 0000000..8fe669e
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/physical_web_ui.h
@@ -0,0 +1,23 @@
+// 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 IOS_CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_UI_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_UI_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "ios/web/public/webui/web_ui_ios_controller.h"
+
+// The WebUI handler for chrome://physical-web.
+class PhysicalWebUI : public web::WebUIIOSController {
+ public:
+  explicit PhysicalWebUI(web::WebUIIOS* web_ui);
+  ~PhysicalWebUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PhysicalWebUI);
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_PHYSICAL_WEB_UI_H_
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index cac5400..c21c6176 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -90,6 +90,7 @@
         '../../components/components.gyp:password_manager_core_browser',
         '../../components/components.gyp:password_manager_sync_browser',
         '../../components/components.gyp:physical_web_data_source',
+        '../../components/components.gyp:physical_web_ui',
         '../../components/components.gyp:pref_registry',
         '../../components/components.gyp:profile_metrics',
         '../../components/components.gyp:proxy_config',
@@ -692,6 +693,8 @@
         'browser/ui/webui/mojo_web_ui_ios_controller.h',
         'browser/ui/webui/net_export/net_export_ui.cc',
         'browser/ui/webui/net_export/net_export_ui.h',
+        'browser/ui/webui/physical_web_ui.cc',
+        'browser/ui/webui/physical_web_ui.h',
         'browser/ui/webui/sync_internals/sync_internals_message_handler.cc',
         'browser/ui/webui/sync_internals/sync_internals_message_handler.h',
         'browser/ui/webui/sync_internals/sync_internals_ui.cc',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 7818331..9507377 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -12837,6 +12837,13 @@
   <description>A Physical Web URL was selected.</description>
 </action>
 
+<action name="PhysicalWeb.WebUI.Open">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <description>The Physical Web WebUI was opened or refreshed.</description>
+</action>
+
 <action name="PlatformVerificationAccepted">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>