[DevTools] Do not allow chrome.debugger to attach to web ui pages

If the page navigates to web ui, we force detach the debugger extension.

[email protected]

Bug: 798222
Change-Id: Idb46c2f59e839388397a8dfa6ce2e2a897698df3
Reviewed-on: https://chromium-review.googlesource.com/935961
Commit-Queue: Dmitry Gozman <[email protected]>
Reviewed-by: Devlin <[email protected]>
Reviewed-by: Pavel Feldman <[email protected]>
Reviewed-by: Nasko Oskov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#540916}
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index f005243..849602a 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -104,8 +104,10 @@
 
   ~ExtensionDevToolsClientHost() override;
 
+  bool Attach();
   const std::string& extension_id() { return extension_id_; }
   DevToolsAgentHost* agent_host() { return agent_host_.get(); }
+  void RespondDetachedToPendingRequests();
   void Close();
   void SendMessageToBackend(DebuggerSendCommandFunction* function,
                             const std::string& method,
@@ -138,6 +140,7 @@
   Profile* profile_;
   scoped_refptr<DevToolsAgentHost> agent_host_;
   std::string extension_id_;
+  std::string extension_name_;
   Debuggee debuggee_;
   content::NotificationRegistrar registrar_;
   int last_request_id_;
@@ -161,6 +164,7 @@
     : profile_(profile),
       agent_host_(agent_host),
       extension_id_(extension_id),
+      extension_name_(extension_name),
       last_request_id_(0),
       infobar_(nullptr),
       detach_reason_(api::debugger::DETACH_REASON_TARGET_CLOSED),
@@ -178,27 +182,34 @@
   // Disconnect explicitly to make sure that |this| observer is not leaked.
   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
+}
 
+bool ExtensionDevToolsClientHost::Attach() {
   // Attach to debugger and tell it we are ready.
-  agent_host_->AttachClient(this);
+  if (!agent_host_->AttachRestrictedClient(this))
+    return false;
 
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           ::switches::kSilentDebuggerExtensionAPI)) {
-    return;
+    return true;
   }
 
   // We allow policy-installed extensions to circumvent the normal
   // infobar warning. See crbug.com/693621.
   const Extension* extension =
-      ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
-          extension_id);
+      ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID(
+          extension_id_);
+  // TODO(dgozman): null-checking |extension| below is sketchy.
+  // We probably should not allow debugging in this case. Or maybe
+  // it's never null?
   if (extension && Manifest::IsPolicyLocation(extension->location()))
-    return;
+    return true;
 
   infobar_ = ExtensionDevToolsInfoBar::Create(
-      extension_id, extension_name, this,
+      extension_id_, extension_name_, this,
       base::Bind(&ExtensionDevToolsClientHost::InfoBarDismissed,
                  base::Unretained(this)));
+  return true;
 }
 
 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
@@ -211,6 +222,7 @@
 void ExtensionDevToolsClientHost::AgentHostClosed(
     DevToolsAgentHost* agent_host) {
   DCHECK(agent_host == agent_host_.get());
+  RespondDetachedToPendingRequests();
   SendDetachedEvent();
   delete this;
 }
@@ -241,10 +253,17 @@
 
 void ExtensionDevToolsClientHost::InfoBarDismissed() {
   detach_reason_ = api::debugger::DETACH_REASON_CANCELED_BY_USER;
+  RespondDetachedToPendingRequests();
   SendDetachedEvent();
   Close();
 }
 
+void ExtensionDevToolsClientHost::RespondDetachedToPendingRequests() {
+  for (const auto& it : pending_requests_)
+    it.second->SendDetachedError();
+  pending_requests_.clear();
+}
+
 void ExtensionDevToolsClientHost::SendDetachedEvent() {
   if (!EventRouter::Get(profile_))
     return;
@@ -447,9 +466,16 @@
     return false;
   }
 
-  new ExtensionDevToolsClientHost(GetProfile(), agent_host_.get(),
-                                  extension()->id(), extension()->name(),
-                                  debuggee_);
+  auto host = std::make_unique<ExtensionDevToolsClientHost>(
+      GetProfile(), agent_host_.get(), extension()->id(), extension()->name(),
+      debuggee_);
+
+  if (!host->Attach()) {
+    FormatErrorMessage(keys::kRestrictedError);
+    return false;
+  }
+
+  host.release();  // An attached client host manages its own lifetime.
   SendResponse(true);
   return true;
 }
@@ -471,6 +497,7 @@
   if (!InitClientHost())
     return false;
 
+  client_host_->RespondDetachedToPendingRequests();
   client_host_->Close();
   SendResponse(true);
   return true;
@@ -517,6 +544,10 @@
   SendResponse(true);
 }
 
+void DebuggerSendCommandFunction::SendDetachedError() {
+  error_ = keys::kDetachedWhileHandlingError;
+  SendResponse(false);
+}
 
 // DebuggerGetTargetsFunction -------------------------------------------------