[ios] Implements about:policy.

BUG=1027249

Change-Id: I719bef6dd78c7266ff8eb2f480a34a1effa3f927
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2076448
Commit-Queue: Rohit Rao <[email protected]>
Reviewed-by: Mike Dougherty <[email protected]>
Cr-Commit-Position: refs/heads/master@{#745337}
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index a1e13e0f..8507e22 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -103,9 +103,11 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/omaha",
+    "//ios/chrome/browser/policy:feature_flags",
     "//ios/chrome/browser/signin",
     "//ios/chrome/browser/ui/webui/gcm",
     "//ios/chrome/browser/ui/webui/net_export",
+    "//ios/chrome/browser/ui/webui/policy",
     "//ios/chrome/browser/ui/webui/sync_internals",
     "//ios/chrome/browser/ui/webui/translate_internals",
     "//url",
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
index 102d978..a2f30484 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/policy/policy_features.h"
 #include "ios/chrome/browser/system_flags.h"
 #include "ios/chrome/browser/ui/webui/about_ui.h"
 #include "ios/chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui_ios.h"
@@ -20,6 +21,7 @@
 #include "ios/chrome/browser/ui/webui/net_export/net_export_ui.h"
 #include "ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/omaha_ui.h"
+#include "ios/chrome/browser/ui/webui/policy/policy_ui.h"
 #include "ios/chrome/browser/ui/webui/prefs_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/signin_internals_ui_ios.h"
 #include "ios/chrome/browser/ui/webui/suggestions_ui.h"
@@ -109,6 +111,10 @@
   if (url_host == kChromeUIVersionHost)
     return &NewWebUIIOS<VersionUI>;
 
+  if (IsEnterprisePolicyEnabled() && url_host == kChromeUIPolicyHost) {
+    return &NewWebUIIOS<PolicyUI>;
+  }
+
   return nullptr;
 }
 
diff --git a/ios/chrome/browser/ui/webui/policy/BUILD.gn b/ios/chrome/browser/ui/webui/policy/BUILD.gn
new file mode 100644
index 0000000..cde2d419
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/policy/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2020 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("policy") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  sources = [
+    "policy_ui.h",
+    "policy_ui.mm",
+    "policy_ui_handler.h",
+    "policy_ui_handler.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//components/policy:generated",
+    "//components/policy/core/browser",
+    "//components/policy/core/common",
+    "//components/resources",
+    "//components/strings",
+    "//ios/chrome/browser:chrome_url_constants",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/policy",
+    "//ios/web/public/webui",
+    "//ui/base",
+  ]
+}
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui.h b/ios/chrome/browser/ui/webui/policy/policy_ui.h
new file mode 100644
index 0000000..f4c1cb4
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui.h
@@ -0,0 +1,23 @@
+// Copyright 2020 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_POLICY_POLICY_UI_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_POLICY_POLICY_UI_H_
+
+#include "ios/web/public/webui/web_ui_ios_controller.h"
+
+namespace web {
+class WebUIIOS;
+}
+
+// The Web UI controller for the chrome://policy page.
+class PolicyUI : public web::WebUIIOSController {
+ public:
+  explicit PolicyUI(web::WebUIIOS* web_ui);
+  ~PolicyUI() override;
+  PolicyUI(const PolicyUI&) = delete;
+  PolicyUI& operator=(const PolicyUI&) = delete;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_POLICY_POLICY_UI_H_
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui.mm b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
new file mode 100644
index 0000000..14c9b4a
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui.mm
@@ -0,0 +1,96 @@
+// Copyright 2020 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/policy/policy_ui.h"
+
+#include <memory>
+
+#include "components/grit/dev_ui_components_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/ui/webui/policy/policy_ui_handler.h"
+#include "ios/web/public/webui/web_ui_ios.h"
+#include "ios/web/public/webui/web_ui_ios_data_source.h"
+#include "ios/web/public/webui/web_ui_ios_message_handler.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+web::WebUIIOSDataSource* CreatePolicyUIHtmlSource() {
+  web::WebUIIOSDataSource* source =
+      web::WebUIIOSDataSource::Create(kChromeUIPolicyHost);
+  PolicyUIHandler::AddCommonLocalizedStringsToSource(source);
+
+  static constexpr webui::LocalizedString kStrings[] = {
+      // Localized strings (alphabetical order).
+      {"exportPoliciesJSON", IDS_EXPORT_POLICIES_JSON},
+      {"filterPlaceholder", IDS_POLICY_FILTER_PLACEHOLDER},
+      {"hideExpandedStatus", IDS_POLICY_HIDE_EXPANDED_STATUS},
+      {"isAffiliatedYes", IDS_POLICY_IS_AFFILIATED_YES},
+      {"isAffiliatedNo", IDS_POLICY_IS_AFFILIATED_NO},
+      {"labelAssetId", IDS_POLICY_LABEL_ASSET_ID},
+      {"labelClientId", IDS_POLICY_LABEL_CLIENT_ID},
+      {"labelDirectoryApiId", IDS_POLICY_LABEL_DIRECTORY_API_ID},
+      {"labelEnterpriseDisplayDomain",
+       IDS_POLICY_LABEL_ENTERPRISE_DISPLAY_DOMAIN},
+      {"labelEnterpriseEnrollmentDomain",
+       IDS_POLICY_LABEL_ENTERPRISE_ENROLLMENT_DOMAIN},
+      {"labelGaiaId", IDS_POLICY_LABEL_GAIA_ID},
+      {"labelIsAffiliated", IDS_POLICY_LABEL_IS_AFFILIATED},
+      {"labelLocation", IDS_POLICY_LABEL_LOCATION},
+      {"labelMachineEnrollmentDomain",
+       IDS_POLICY_LABEL_MACHINE_ENROLLMENT_DOMAIN},
+      {"labelMachineEnrollmentMachineName",
+       IDS_POLICY_LABEL_MACHINE_ENROLLMENT_MACHINE_NAME},
+      {"labelMachineEnrollmentToken",
+       IDS_POLICY_LABEL_MACHINE_ENROLLMENT_TOKEN},
+      {"labelMachineEntrollmentDeviceId",
+       IDS_POLICY_LABEL_MACHINE_ENROLLMENT_DEVICE_ID},
+      {"labelIsOffHoursActive", IDS_POLICY_LABEL_IS_OFFHOURS_ACTIVE},
+      {"labelPoliciesPush", IDS_POLICY_LABEL_PUSH_POLICIES},
+      {"labelRefreshInterval", IDS_POLICY_LABEL_REFRESH_INTERVAL},
+      {"labelStatus", IDS_POLICY_LABEL_STATUS},
+      {"labelTimeSinceLastRefresh", IDS_POLICY_LABEL_TIME_SINCE_LAST_REFRESH},
+      {"labelUsername", IDS_POLICY_LABEL_USERNAME},
+      {"noPoliciesSet", IDS_POLICY_NO_POLICIES_SET},
+      {"offHoursActive", IDS_POLICY_OFFHOURS_ACTIVE},
+      {"offHoursNotActive", IDS_POLICY_OFFHOURS_NOT_ACTIVE},
+      {"policiesPushOff", IDS_POLICY_PUSH_POLICIES_OFF},
+      {"policiesPushOn", IDS_POLICY_PUSH_POLICIES_ON},
+      {"policyLearnMore", IDS_POLICY_LEARN_MORE},
+      {"reloadPolicies", IDS_POLICY_RELOAD_POLICIES},
+      {"showExpandedStatus", IDS_POLICY_SHOW_EXPANDED_STATUS},
+      {"showLess", IDS_POLICY_SHOW_LESS},
+      {"showMore", IDS_POLICY_SHOW_MORE},
+      {"showUnset", IDS_POLICY_SHOW_UNSET},
+      {"signinProfile", IDS_POLICY_SIGNIN_PROFILE},
+      {"status", IDS_POLICY_STATUS},
+      {"statusDevice", IDS_POLICY_STATUS_DEVICE},
+      {"statusMachine", IDS_POLICY_STATUS_MACHINE},
+      {"statusUser", IDS_POLICY_STATUS_USER},
+  };
+  source->AddLocalizedStrings(kStrings);
+  source->UseStringsJs();
+
+  source->AddResourcePath("policy.css", IDR_POLICY_CSS);
+  source->AddResourcePath("policy_base.js", IDR_POLICY_BASE_JS);
+  source->AddResourcePath("policy.js", IDR_POLICY_JS);
+  source->SetDefaultResource(IDR_POLICY_HTML);
+  return source;
+}
+
+}  // namespace
+
+PolicyUI::PolicyUI(web::WebUIIOS* web_ui) : web::WebUIIOSController(web_ui) {
+  web_ui->AddMessageHandler(std::make_unique<PolicyUIHandler>());
+  web::WebUIIOSDataSource::Add(ChromeBrowserState::FromWebUIIOS(web_ui),
+                               CreatePolicyUIHtmlSource());
+}
+
+PolicyUI::~PolicyUI() {}
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h
new file mode 100644
index 0000000..2488b0e
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.h
@@ -0,0 +1,74 @@
+// Copyright 2020 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_POLICY_POLICY_UI_HANDLER_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_POLICY_POLICY_UI_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/schema_registry.h"
+#include "ios/web/public/webui/web_ui_ios.h"
+#include "ios/web/public/webui/web_ui_ios_data_source.h"
+#include "ios/web/public/webui/web_ui_ios_message_handler.h"
+
+namespace policy {
+class PolicyMap;
+struct PolicyNamespace;
+}  // namespace policy
+
+// The JavaScript message handler for the chrome://policy page.
+class PolicyUIHandler : public web::WebUIIOSMessageHandler,
+                        public policy::PolicyService::Observer,
+                        public policy::SchemaRegistry::Observer {
+ public:
+  PolicyUIHandler();
+  ~PolicyUIHandler() override;
+  PolicyUIHandler(const PolicyUIHandler&) = delete;
+  PolicyUIHandler& operator=(const PolicyUIHandler&) = delete;
+
+  static void AddCommonLocalizedStringsToSource(
+      web::WebUIIOSDataSource* source);
+
+  // web::WebUIIOSMessageHandler.
+  void RegisterMessages() override;
+
+  // policy::PolicyService::Observer.
+  void OnPolicyUpdated(const policy::PolicyNamespace& ns,
+                       const policy::PolicyMap& previous,
+                       const policy::PolicyMap& current) override;
+
+  // policy::SchemaRegistry::Observer.
+  void OnSchemaRegistryUpdated(bool has_new_schemas) override;
+
+ private:
+  // Returns a dictionary containing the policies supported by Chrome.
+  base::Value GetPolicyNames() const;
+
+  // Returns a dictionary containing the current values of the policies
+  // supported by Chrome.
+  base::Value GetPolicyValues() const;
+
+  // Called to handle the "listenPoliciesUpdates" WebUI message.
+  void HandleListenPoliciesUpdates(const base::ListValue* args);
+
+  // Called to handle the "reloadPolicies" WebUI message.
+  void HandleReloadPolicies(const base::ListValue* args);
+
+  // Send information about the current policy values to the UI. For each policy
+  // whose value has been set, dictionaries containing the value and additional
+  // metadata are sent.
+  void SendPolicies();
+
+  // The callback invoked by PolicyService::RefreshPolicies().
+  void OnRefreshPoliciesDone();
+
+  // Returns the PolicyService associated with this WebUI's BrowserState.
+  policy::PolicyService* GetPolicyService() const;
+
+  // Vends WeakPtrs for this object.
+  base::WeakPtrFactory<PolicyUIHandler> weak_factory_{this};
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_POLICY_POLICY_UI_HANDLER_H_
diff --git a/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm
new file mode 100644
index 0000000..4fba478
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/policy/policy_ui_handler.mm
@@ -0,0 +1,158 @@
+// Copyright 2020 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/policy/policy_ui_handler.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/values.h"
+#include "components/policy/core/browser/policy_conversions.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/core/common/schema.h"
+#include "components/policy/core/common/schema_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/policy/browser_state_policy_connector.h"
+#include "ios/chrome/browser/policy/policy_conversions_client_ios.h"
+#include "ui/base/webui/web_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+PolicyUIHandler::PolicyUIHandler() {}
+
+PolicyUIHandler::~PolicyUIHandler() {
+  GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
+  policy::SchemaRegistry* registry = ChromeBrowserState::FromWebUIIOS(web_ui())
+                                         ->GetPolicyConnector()
+                                         ->GetSchemaRegistry();
+  registry->RemoveObserver(this);
+}
+
+void PolicyUIHandler::AddCommonLocalizedStringsToSource(
+    web::WebUIIOSDataSource* source) {
+  static constexpr webui::LocalizedString kStrings[] = {
+      {"conflict", IDS_POLICY_LABEL_CONFLICT},
+      {"headerLevel", IDS_POLICY_HEADER_LEVEL},
+      {"headerName", IDS_POLICY_HEADER_NAME},
+      {"headerScope", IDS_POLICY_HEADER_SCOPE},
+      {"headerSource", IDS_POLICY_HEADER_SOURCE},
+      {"headerStatus", IDS_POLICY_HEADER_STATUS},
+      {"headerValue", IDS_POLICY_HEADER_VALUE},
+      {"warning", IDS_POLICY_HEADER_WARNING},
+      {"levelMandatory", IDS_POLICY_LEVEL_MANDATORY},
+      {"levelRecommended", IDS_POLICY_LEVEL_RECOMMENDED},
+      {"error", IDS_POLICY_LABEL_ERROR},
+      {"ignored", IDS_POLICY_LABEL_IGNORED},
+      {"notSpecified", IDS_POLICY_NOT_SPECIFIED},
+      {"ok", IDS_POLICY_OK},
+      {"scopeDevice", IDS_POLICY_SCOPE_DEVICE},
+      {"scopeUser", IDS_POLICY_SCOPE_USER},
+      {"title", IDS_POLICY_TITLE},
+      {"unknown", IDS_POLICY_UNKNOWN},
+      {"unset", IDS_POLICY_UNSET},
+      {"value", IDS_POLICY_LABEL_VALUE},
+  };
+  source->AddLocalizedStrings(kStrings);
+  source->AddLocalizedStrings(policy::kPolicySources);
+  source->UseStringsJs();
+}
+
+void PolicyUIHandler::RegisterMessages() {
+  GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
+
+  ChromeBrowserState* browser_state =
+      ChromeBrowserState::FromWebUIIOS(web_ui());
+  browser_state->GetPolicyConnector()->GetSchemaRegistry()->AddObserver(this);
+
+  web_ui()->RegisterMessageCallback(
+      "listenPoliciesUpdates",
+      base::BindRepeating(&PolicyUIHandler::HandleListenPoliciesUpdates,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "reloadPolicies",
+      base::BindRepeating(&PolicyUIHandler::HandleReloadPolicies,
+                          base::Unretained(this)));
+}
+
+void PolicyUIHandler::OnSchemaRegistryUpdated(bool has_new_schemas) {
+  // Update UI when new schema is added.
+  if (has_new_schemas) {
+    SendPolicies();
+  }
+}
+
+void PolicyUIHandler::OnPolicyUpdated(const policy::PolicyNamespace& ns,
+                                      const policy::PolicyMap& previous,
+                                      const policy::PolicyMap& current) {
+  SendPolicies();
+}
+
+base::Value PolicyUIHandler::GetPolicyNames() const {
+  ChromeBrowserState* browser_state =
+      ChromeBrowserState::FromWebUIIOS(web_ui());
+  policy::SchemaRegistry* registry =
+      browser_state->GetPolicyConnector()->GetSchemaRegistry();
+  scoped_refptr<policy::SchemaMap> schema_map = registry->schema_map();
+
+  // Add Chrome policy names.
+  base::Value chrome_policy_names(base::Value::Type::LIST);
+  policy::PolicyNamespace chrome_namespace(policy::POLICY_DOMAIN_CHROME, "");
+  const policy::Schema* chrome_schema = schema_map->GetSchema(chrome_namespace);
+  for (auto it = chrome_schema->GetPropertiesIterator(); !it.IsAtEnd();
+       it.Advance()) {
+    chrome_policy_names.Append(base::Value(it.key()));
+  }
+
+  base::Value chrome_values(base::Value::Type::DICTIONARY);
+  chrome_values.SetStringKey("name", "Chrome Policies");
+  chrome_values.SetKey("policyNames", std::move(chrome_policy_names));
+
+  base::Value names(base::Value::Type::DICTIONARY);
+  names.SetKey("chrome", std::move(chrome_values));
+  return names;
+}
+
+base::Value PolicyUIHandler::GetPolicyValues() const {
+  auto client = std::make_unique<PolicyConversionsClientIOS>(
+      ChromeBrowserState::FromWebUIIOS(web_ui()));
+  return policy::ArrayPolicyConversions(std::move(client))
+      .EnableConvertValues(true)
+      .ToValue();
+}
+
+void PolicyUIHandler::HandleListenPoliciesUpdates(const base::ListValue* args) {
+  OnRefreshPoliciesDone();
+}
+
+void PolicyUIHandler::HandleReloadPolicies(const base::ListValue* args) {
+  GetPolicyService()->RefreshPolicies(base::Bind(
+      &PolicyUIHandler::OnRefreshPoliciesDone, weak_factory_.GetWeakPtr()));
+}
+
+void PolicyUIHandler::SendPolicies() {
+  base::Value names = GetPolicyNames();
+  base::Value values = GetPolicyValues();
+  std::vector<const base::Value*> args;
+  args.push_back(&names);
+  args.push_back(&values);
+  web_ui()->FireWebUIListener("policies-updated", args);
+}
+
+void PolicyUIHandler::OnRefreshPoliciesDone() {
+  SendPolicies();
+}
+
+policy::PolicyService* PolicyUIHandler::GetPolicyService() const {
+  ChromeBrowserState* browser_state =
+      ChromeBrowserState::FromWebUIIOS(web_ui());
+  return browser_state->GetPolicyConnector()->GetPolicyService();
+}