Implement a TestRunner API to set permissions from LayoutTests.

Permissions can be set using setPermission() and they are automatically
reset between each test.

BUG=475141

Review URL: https://codereview.chromium.org/1078673002

Cr-Commit-Position: refs/heads/master@{#326014}
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.cc b/content/shell/browser/layout_test/layout_test_browser_context.cc
index f6fbceb9..259a407 100644
--- a/content/shell/browser/layout_test/layout_test_browser_context.cc
+++ b/content/shell/browser/layout_test/layout_test_browser_context.cc
@@ -81,4 +81,9 @@
   return permission_manager_.get();
 }
 
+LayoutTestPermissionManager*
+LayoutTestBrowserContext::GetLayoutTestPermissionManager() {
+  return static_cast<LayoutTestPermissionManager*>(GetPermissionManager());
+}
+
 }  // namespace content
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.h b/content/shell/browser/layout_test/layout_test_browser_context.h
index c23d960..d9c9832f 100644
--- a/content/shell/browser/layout_test/layout_test_browser_context.h
+++ b/content/shell/browser/layout_test/layout_test_browser_context.h
@@ -15,6 +15,7 @@
 namespace content {
 
 class DownloadManagerDelegate;
+class LayoutTestPermissionManager;
 class LayoutTestPushMessagingService;
 class PermissionManager;
 class PushMessagingService;
@@ -30,6 +31,7 @@
   PermissionManager* GetPermissionManager() override;
 
   LayoutTestPushMessagingService* GetLayoutTestPushMessagingService();
+  LayoutTestPermissionManager* GetLayoutTestPermissionManager();
 
  protected:
   ShellURLRequestContextGetter* CreateURLRequestContextGetter(
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.cc b/content/shell/browser/layout_test/layout_test_message_filter.cc
index aa5ff0b2..fcae408c 100644
--- a/content/shell/browser/layout_test/layout_test_message_filter.cc
+++ b/content/shell/browser/layout_test/layout_test_message_filter.cc
@@ -7,9 +7,11 @@
 #include "base/files/file_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/permission_type.h"
 #include "content/shell/browser/layout_test/layout_test_browser_context.h"
 #include "content/shell/browser/layout_test/layout_test_content_browser_client.h"
 #include "content/shell/browser/layout_test/layout_test_notification_manager.h"
+#include "content/shell/browser/layout_test/layout_test_permission_manager.h"
 #include "content/shell/browser/layout_test/layout_test_push_messaging_service.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_content_browser_client.h"
@@ -46,7 +48,9 @@
     *thread = BrowserThread::FILE;
   if (message.type() == LayoutTestHostMsg_SimulateWebNotificationClick::ID ||
       message.type() == LayoutTestHostMsg_SetPushMessagingPermission::ID ||
-      message.type() == LayoutTestHostMsg_ClearPushMessagingPermissions::ID)
+      message.type() == LayoutTestHostMsg_ClearPushMessagingPermissions::ID ||
+      message.type() == LayoutTestHostMsg_SetPermission::ID ||
+      message.type() == LayoutTestHostMsg_ResetPermissions::ID)
     *thread = BrowserThread::UI;
 }
 
@@ -71,6 +75,8 @@
                         OnClearPushMessagingPermissions)
     IPC_MESSAGE_HANDLER(LayoutTestHostMsg_AcceptAllCookies, OnAcceptAllCookies)
     IPC_MESSAGE_HANDLER(LayoutTestHostMsg_DeleteAllCookies, OnDeleteAllCookies)
+    IPC_MESSAGE_HANDLER(LayoutTestHostMsg_SetPermission, OnSetPermission)
+    IPC_MESSAGE_HANDLER(LayoutTestHostMsg_ResetPermissions, OnResetPermissions)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -164,4 +170,41 @@
       ->DeleteAllAsync(net::CookieMonster::DeleteCallback());
 }
 
+void LayoutTestMessageFilter::OnSetPermission(const std::string& name,
+                                              PermissionStatus status,
+                                              const GURL& origin,
+                                              const GURL& embedding_origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  content::PermissionType type;
+  if (name == "midi-sysex") {
+    type = PermissionType::MIDI_SYSEX;
+  } else if (name == "push-messaging") {
+    type = PermissionType::PUSH_MESSAGING;
+  } else if (name == "notifications") {
+    type = PermissionType::NOTIFICATIONS;
+  } else if (name == "geolocation") {
+    type = PermissionType::GEOLOCATION;
+  } else if (name == "protected-media-identifier") {
+    type = PermissionType::PROTECTED_MEDIA_IDENTIFIER;
+  } else {
+    NOTREACHED();
+    type = PermissionType::NOTIFICATIONS;
+  }
+
+  LayoutTestContentBrowserClient::Get()
+      ->GetLayoutTestBrowserContext()
+      ->GetLayoutTestPermissionManager()
+      ->SetPermission(type, status, origin, embedding_origin);
+}
+
+void LayoutTestMessageFilter::OnResetPermissions() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  LayoutTestContentBrowserClient::Get()
+      ->GetLayoutTestBrowserContext()
+      ->GetLayoutTestPermissionManager()
+      ->ResetPermissions();
+}
+
 }  // namespace content
diff --git a/content/shell/browser/layout_test/layout_test_message_filter.h b/content/shell/browser/layout_test/layout_test_message_filter.h
index af874b1f..a41f72a8 100644
--- a/content/shell/browser/layout_test/layout_test_message_filter.h
+++ b/content/shell/browser/layout_test/layout_test_message_filter.h
@@ -11,6 +11,7 @@
 #include "base/basictypes.h"
 #include "base/files/file_path.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "content/public/common/permission_status.mojom.h"
 
 class GURL;
 
@@ -58,6 +59,11 @@
   void OnClearPushMessagingPermissions();
   void OnAcceptAllCookies(bool accept);
   void OnDeleteAllCookies();
+  void OnSetPermission(const std::string& name,
+                       PermissionStatus status,
+                       const GURL& origin,
+                       const GURL& embedding_origin);
+  void OnResetPermissions();
 
   int render_process_id_;
 
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.cc b/content/shell/browser/layout_test/layout_test_permission_manager.cc
index ed4e7c6..53159fbe 100644
--- a/content/shell/browser/layout_test/layout_test_permission_manager.cc
+++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -8,9 +8,9 @@
 #include "base/callback.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/browser/web_contents.h"
 #include "content/shell/browser/layout_test/layout_test_content_browser_client.h"
 #include "content/shell/browser/layout_test/layout_test_notification_manager.h"
-#include "url/gurl.h"
 
 namespace content {
 
@@ -35,8 +35,35 @@
 
 }  // anonymous namespace
 
+LayoutTestPermissionManager::PermissionDescription::PermissionDescription(
+    PermissionType type,
+    const GURL& origin,
+    const GURL& embedding_origin)
+    : type(type),
+      origin(origin),
+      embedding_origin(embedding_origin) {
+}
+
+bool LayoutTestPermissionManager::PermissionDescription::operator==(
+    const PermissionDescription& other) const {
+  return type == other.type &&
+         origin == other.origin &&
+         embedding_origin == other.embedding_origin;
+}
+
+size_t LayoutTestPermissionManager::PermissionDescription::Hash::operator()(
+    const PermissionDescription& description) const {
+  size_t hash = BASE_HASH_NAMESPACE::hash<int>()(static_cast<int>(
+      description.type));
+  hash += BASE_HASH_NAMESPACE::hash<std::string>()(
+      description.embedding_origin.spec());
+  hash += BASE_HASH_NAMESPACE::hash<std::string>()(
+      description.origin.spec());
+  return hash;
+}
+
 LayoutTestPermissionManager::LayoutTestPermissionManager()
-    : ShellPermissionManager() {
+    : PermissionManager() {
 }
 
 LayoutTestPermissionManager::~LayoutTestPermissionManager() {
@@ -61,12 +88,78 @@
     return;
   }
 
-  ShellPermissionManager::RequestPermission(permission,
-                                            web_contents,
-                                            request_id,
-                                            requesting_origin,
-                                            user_gesture,
-                                            callback);
+  callback.Run(GetPermissionStatus(permission,
+                                   requesting_origin,
+                                   web_contents->GetLastCommittedURL()));
+}
+
+void LayoutTestPermissionManager::CancelPermissionRequest(
+    PermissionType permission,
+    WebContents* web_contents,
+    int request_id,
+    const GURL& requesting_origin) {
+}
+
+void LayoutTestPermissionManager::ResetPermission(
+    PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+  auto it = permissions_.find(
+      PermissionDescription(permission, requesting_origin, embedding_origin));
+  if (it == permissions_.end())
+    return;
+  permissions_.erase(it);;
+}
+
+PermissionStatus LayoutTestPermissionManager::GetPermissionStatus(
+    PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+
+  auto it = permissions_.find(
+      PermissionDescription(permission, requesting_origin, embedding_origin));
+  if (it == permissions_.end())
+    return PERMISSION_STATUS_DENIED;
+  return it->second;
+}
+
+void LayoutTestPermissionManager::RegisterPermissionUsage(
+    PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin) {
+}
+
+int LayoutTestPermissionManager::SubscribePermissionStatusChange(
+    PermissionType permission,
+    const GURL& requesting_origin,
+    const GURL& embedding_origin,
+    const base::Callback<void(PermissionStatus)>& callback) {
+  // TODO(mlamouri): to be implemented, see https://crbug.com/475141
+  return -1;
+}
+
+void LayoutTestPermissionManager::UnsubscribePermissionStatusChange(
+    int subscription_id) {
+  // TODO(mlamouri): to be implemented, see https://crbug.com/475141
+}
+
+void LayoutTestPermissionManager::SetPermission(PermissionType permission,
+                                                PermissionStatus status,
+                                                const GURL& origin,
+                                                const GURL& embedding_origin) {
+  PermissionDescription description(permission, origin, embedding_origin);
+
+  auto it = permissions_.find(description);
+  if (it == permissions_.end()) {
+    permissions_.insert(std::pair<PermissionDescription, PermissionStatus>(
+        description, status));
+  } else {
+    it->second = status;
+  }
+}
+
+void LayoutTestPermissionManager::ResetPermissions() {
+  permissions_.clear();
 }
 
 }  // namespace content
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.h b/content/shell/browser/layout_test/layout_test_permission_manager.h
index 49e8189..e1cf5c1 100644
--- a/content/shell/browser/layout_test/layout_test_permission_manager.h
+++ b/content/shell/browser/layout_test/layout_test_permission_manager.h
@@ -6,17 +6,19 @@
 #define CONTENT_SHELL_BROWSER_LAYOUT_TEST_LAYOUT_TEST_PERMISSION_MANAGER_H_
 
 #include "base/callback_forward.h"
+#include "base/containers/hash_tables.h"
 #include "base/macros.h"
-#include "content/shell/browser/shell_permission_manager.h"
+#include "content/public/browser/permission_manager.h"
+#include "url/gurl.h"
 
 namespace content {
 
-class LayoutTestPermissionManager : public ShellPermissionManager {
+class LayoutTestPermissionManager : public PermissionManager {
  public:
   LayoutTestPermissionManager();
   ~LayoutTestPermissionManager() override;
 
-  // ShellPermissionManager overrides.
+  // PermissionManager overrides.
   void RequestPermission(
       PermissionType permission,
       WebContents* web_contents,
@@ -24,8 +26,58 @@
       const GURL& requesting_origin,
       bool user_gesture,
       const base::Callback<void(PermissionStatus)>& callback) override;
+  void CancelPermissionRequest(PermissionType permission,
+                               WebContents* web_contents,
+                               int request_id,
+                               const GURL& requesting_origin) override;
+  void ResetPermission(PermissionType permission,
+                       const GURL& requesting_origin,
+                       const GURL& embedding_origin) override;
+  PermissionStatus GetPermissionStatus(PermissionType permission,
+                                       const GURL& requesting_origin,
+                                       const GURL& embedding_origin) override;
+  void RegisterPermissionUsage(PermissionType permission,
+                               const GURL& requesting_origin,
+                               const GURL& embedding_origin) override;
+  int SubscribePermissionStatusChange(
+      PermissionType permission,
+      const GURL& requesting_origin,
+      const GURL& embedding_origin,
+      const base::Callback<void(PermissionStatus)>& callback) override;
+  void UnsubscribePermissionStatusChange(int subscription_id) override;
+
+  void SetPermission(PermissionType permission,
+                     PermissionStatus status,
+                     const GURL& origin,
+                     const GURL& embedding_origin);
+  void ResetPermissions();
 
  private:
+  // Representation of a permission for the LayoutTestPermissionManager.
+  struct PermissionDescription {
+    PermissionDescription(PermissionType type,
+                          const GURL& origin,
+                          const GURL& embedding_origin);
+    bool operator==(const PermissionDescription& other) const;
+
+    // Hash operator for hash maps.
+    struct Hash {
+      size_t operator()(const PermissionDescription& description) const;
+    };
+
+    PermissionType type;
+    GURL origin;
+    GURL embedding_origin;
+  };
+
+  using PermissionsMap = base::hash_map<PermissionDescription,
+                                        PermissionStatus,
+                                        PermissionDescription::Hash>;
+
+  // List of permissions currently known by the LayoutTestPermissionManager and
+  // their associated |PermissionStatus|.
+  PermissionsMap permissions_;
+
   DISALLOW_COPY_AND_ASSIGN(LayoutTestPermissionManager);
 };
 
diff --git a/content/shell/common/layout_test/layout_test_messages.h b/content/shell/common/layout_test/layout_test_messages.h
index 9c813e08..ed3a56b 100644
--- a/content/shell/common/layout_test/layout_test_messages.h
+++ b/content/shell/common/layout_test/layout_test_messages.h
@@ -7,12 +7,17 @@
 #include <vector>
 
 #include "content/public/common/common_param_traits.h"
+#include "content/public/common/permission_status.mojom.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_platform_file.h"
 #include "url/gurl.h"
 
 #define IPC_MESSAGE_START LayoutTestMsgStart
 
+IPC_ENUM_TRAITS_MIN_MAX_VALUE(content::PermissionStatus,
+                              content::PERMISSION_STATUS_GRANTED,
+                              content::PERMISSION_STATUS_ASK)
+
 IPC_SYNC_MESSAGE_ROUTED1_1(LayoutTestHostMsg_ReadFileToString,
                            base::FilePath /* local path */,
                            std::string /* contents */)
@@ -35,3 +40,9 @@
 IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_AcceptAllCookies,
                     bool /* accept */)
 IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_DeleteAllCookies)
+IPC_MESSAGE_ROUTED4(LayoutTestHostMsg_SetPermission,
+                    std::string /* name */,
+                    content::PermissionStatus /* status */,
+                    GURL /* origin */,
+                    GURL /* embedding_origin */ )
+IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ResetPermissions)
diff --git a/content/shell/renderer/layout_test/webkit_test_runner.cc b/content/shell/renderer/layout_test/webkit_test_runner.cc
index 7d4ade6..e025926a 100644
--- a/content/shell/renderer/layout_test/webkit_test_runner.cc
+++ b/content/shell/renderer/layout_test/webkit_test_runner.cc
@@ -598,6 +598,30 @@
                              current_entry_indexes_[pos]);
 }
 
+void WebKitTestRunner::SetPermission(const std::string& name,
+                                     const std::string& value,
+                                     const GURL& origin,
+                                     const GURL& embedding_origin) {
+  content::PermissionStatus status;
+  if (value == "granted")
+    status = PERMISSION_STATUS_GRANTED;
+  else if (value == "prompt")
+    status = PERMISSION_STATUS_ASK;
+  else if (value == "denied")
+    status = PERMISSION_STATUS_DENIED;
+  else {
+    NOTREACHED();
+    status = PERMISSION_STATUS_DENIED;
+  }
+
+  Send(new LayoutTestHostMsg_SetPermission(
+      routing_id(), name, status, origin, embedding_origin));
+}
+
+void WebKitTestRunner::ResetPermissions() {
+  Send(new LayoutTestHostMsg_ResetPermissions(routing_id()));
+}
+
 // RenderViewObserver  --------------------------------------------------------
 
 void WebKitTestRunner::DidClearWindowObject(WebLocalFrame* frame) {
diff --git a/content/shell/renderer/layout_test/webkit_test_runner.h b/content/shell/renderer/layout_test/webkit_test_runner.h
index 5ec08812..e63ed68 100644
--- a/content/shell/renderer/layout_test/webkit_test_runner.h
+++ b/content/shell/renderer/layout_test/webkit_test_runner.h
@@ -114,6 +114,11 @@
                        const std::string& frame_name) override;
   bool AllowExternalPages() override;
   std::string DumpHistoryForWindow(WebTestProxyBase* proxy) override;
+  void SetPermission(const std::string& name,
+                     const std::string& value,
+                     const GURL& origin,
+                     const GURL& embedding_origin) override;
+  void ResetPermissions() override;
 
   void Reset();
 
diff --git a/content/shell/renderer/test_runner/test_runner.cc b/content/shell/renderer/test_runner/test_runner.cc
index d7a47f3..7177151 100644
--- a/content/shell/renderer/test_runner/test_runner.cc
+++ b/content/shell/renderer/test_runner/test_runner.cc
@@ -313,6 +313,10 @@
   void SetGeofencingMockProvider(bool service_available);
   void ClearGeofencingMockProvider();
   void SetGeofencingMockPosition(double latitude, double longitude);
+  void SetPermission(const std::string& name,
+                     const std::string& value,
+                     const std::string& origin,
+                     const std::string& embedding_origin);
 
   std::string PlatformName();
   std::string TooltipText();
@@ -572,6 +576,7 @@
                  &TestRunnerBindings::ClearGeofencingMockProvider)
       .SetMethod("setGeofencingMockPosition",
                  &TestRunnerBindings::SetGeofencingMockPosition)
+      .SetMethod("setPermission", &TestRunnerBindings::SetPermission)
 
       // Properties.
       .SetProperty("platformName", &TestRunnerBindings::PlatformName)
@@ -1463,6 +1468,17 @@
     runner_->SetGeofencingMockPosition(latitude, longitude);
 }
 
+void TestRunnerBindings::SetPermission(const std::string& name,
+                                       const std::string& value,
+                                       const std::string& origin,
+                                       const std::string& embedding_origin) {
+  if (!runner_)
+    return;
+
+  return runner_->SetPermission(
+      name, value, GURL(origin), GURL(embedding_origin));
+}
+
 std::string TestRunnerBindings::PlatformName() {
   if (runner_)
     return runner_->platform_name_;
@@ -1654,6 +1670,7 @@
     delegate_->ResetScreenOrientation();
     delegate_->SetBluetoothMockDataSet("");
     delegate_->ClearGeofencingMockProvider();
+    delegate_->ResetPermissions();
     ResetBatteryStatus();
     ResetDeviceLight();
   }
@@ -2823,6 +2840,13 @@
   delegate_->SetGeofencingMockPosition(latitude, longitude);
 }
 
+void TestRunner::SetPermission(const std::string& name,
+                               const std::string& value,
+                               const GURL& origin,
+                               const GURL& embedding_origin) {
+  delegate_->SetPermission(name, value, origin, embedding_origin);
+}
+
 void TestRunner::SetPOSIXLocale(const std::string& locale) {
   delegate_->SetLocale(locale);
 }
diff --git a/content/shell/renderer/test_runner/test_runner.h b/content/shell/renderer/test_runner/test_runner.h
index a76ef6c4..94d83ec 100644
--- a/content/shell/renderer/test_runner/test_runner.h
+++ b/content/shell/renderer/test_runner/test_runner.h
@@ -534,6 +534,13 @@
   // Set the mock geofencing position while running a layout test.
   void SetGeofencingMockPosition(double latitude, double longitude);
 
+  // Sets the permission's |name| to |value| for a given {origin, embedder}
+  // tuple.
+  void SetPermission(const std::string& name,
+                     const std::string& value,
+                     const GURL& origin,
+                     const GURL& embedding_origin);
+
   // Calls setlocale(LC_ALL, ...) for a specified locale.
   // Resets between tests.
   void SetPOSIXLocale(const std::string& locale);
diff --git a/content/shell/renderer/test_runner/web_test_delegate.h b/content/shell/renderer/test_runner/web_test_delegate.h
index cb31881a..304f8c9 100644
--- a/content/shell/renderer/test_runner/web_test_delegate.h
+++ b/content/shell/renderer/test_runner/web_test_delegate.h
@@ -201,6 +201,16 @@
   // Returns a text dump the back/forward history for the WebView associated
   // with the given WebTestProxyBase.
   virtual std::string DumpHistoryForWindow(WebTestProxyBase* proxy) = 0;
+
+  // Sends a message to the LayoutTestPermissionManager in order for it to
+  // update its database.
+  virtual void SetPermission(const std::string& permission_name,
+                             const std::string& permission_value,
+                             const GURL& origin,
+                             const GURL& embedding_origin) = 0;
+
+  // Clear all the permissions set via SetPermission().
+  virtual void ResetPermissions() = 0;
 };
 
 }  // namespace content