Introduce --force-devtools-available for Chrome OS

The --force-devtools-available flag can be used for autotest on Chrome OS
devices, because autotest tends to rely on devtools remote inspection,
so disabled devtools can break autotest.
It is only possible to change command-line flags in developer mode on
Chrome OS devices.

Bug: 838146
Test: browser_tests --gtest_filter=DevToolsAllowedByCommandLineSwitch*
Change-Id: Ic45aaa618dae522adcce41a3e8d20c4c68b54dc7
Reviewed-on: https://chromium-review.googlesource.com/1141742
Commit-Queue: Pavol Marko <pmarko@chromium.org>
Reviewed-by: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: Pavel Feldman <pfeldman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576217}
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index f2a77f7..cc71fb5 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/grit/browser_resources.h"
 #include "components/guest_view/browser/guest_view_base.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -32,6 +31,11 @@
 #include "extensions/common/manifest.h"
 #include "ui/base/resource/resource_bundle.h"
 
+#if defined(OS_CHROMEOS)
+#include "base/command_line.h"
+#include "chromeos/chromeos_switches.h"
+#endif
+
 using content::DevToolsAgentHost;
 
 const char ChromeDevToolsManagerDelegate::kTypeApp[] = "app";
@@ -149,6 +153,12 @@
 bool ChromeDevToolsManagerDelegate::AllowInspection(
     Profile* profile,
     const extensions::Extension* extension) {
+#if defined(OS_CHROMEOS)
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(chromeos::switches::kForceDevToolsAvailable))
+    return true;
+#endif
+
   using Availability = policy::DeveloperToolsPolicyHandler::Availability;
   Availability availability =
       policy::DeveloperToolsPolicyHandler::GetDevToolsAvailability(
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index fda94ff0..a1219d85 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -94,6 +94,10 @@
 #include "ui/gl/gl_switches.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/chromeos_switches.h"
+#endif
+
 using app_modal::JavaScriptAppModalDialog;
 using app_modal::NativeAppModalDialog;
 using content::BrowserThread;
@@ -1921,7 +1925,31 @@
   ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
 }
 
-using DevToolsSanityExtensionTest = extensions::ExtensionBrowserTest;
+class DevToolsSanityExtensionTest : public extensions::ExtensionBrowserTest {
+ public:
+  // Installs an extensions, emulating that it has been force-installed by
+  // policy.
+  // Contains assertions - callers should wrap calls of this method in
+  // |ASSERT_NO_FATAL_FAILURE|. Fills |*out_web_contents| with a |WebContents|
+  // that belongs to the force-installed extension.
+  void ForceInstallExtension(content::WebContents** out_web_contents) {
+    base::FilePath crx_path;
+    base::PathService::Get(chrome::DIR_TEST_DATA, &crx_path);
+    crx_path = crx_path.AppendASCII("devtools")
+                   .AppendASCII("extensions")
+                   .AppendASCII("options.crx");
+    const Extension* extension = InstallExtension(
+        crx_path, 1, extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD);
+    ASSERT_TRUE(extension);
+
+    GURL url("chrome-extension://" + extension->id() + "/options.html");
+    ui_test_utils::NavigateToURL(browser(), url);
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetWebContentsAt(0);
+    *out_web_contents = web_contents;
+  }
+};
+
 IN_PROC_BROWSER_TEST_F(DevToolsSanityExtensionTest,
                        PolicyDisallowedForForceInstalledExtensions) {
   browser()->profile()->GetPrefs()->SetInteger(
@@ -1929,25 +1957,49 @@
       static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
                            kDisallowedForForceInstalledExtensions));
 
-  base::FilePath crx_path;
-  base::PathService::Get(chrome::DIR_TEST_DATA, &crx_path);
-  crx_path = crx_path.AppendASCII("devtools")
-                 .AppendASCII("extensions")
-                 .AppendASCII("options.crx");
-  const Extension* extension = InstallExtension(
-      crx_path, 1, extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD);
-  ASSERT_TRUE(extension);
-
-  GURL url("chrome-extension://" + extension->id() + "/options.html");
-  ui_test_utils::NavigateToURL(browser(), url);
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetWebContentsAt(0);
+  content::WebContents* web_contents = nullptr;
+  ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&web_contents));
 
   DevToolsWindow::OpenDevToolsWindow(web_contents);
   auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
   ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
 }
 
+class DevToolsAllowedByCommandLineSwitch : public DevToolsSanityExtensionTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    extensions::ExtensionBrowserTest::SetUpCommandLine(command_line);
+    // Same as |chromeos::switches::kForceDevToolsAvailable|, but used as a
+    // literal here so it's possible to verify that the switch does not apply on
+    // non-ChromeOS platforms.
+    const std::string kForceDevToolsAvailableBase = "force-devtools-available";
+#if defined(OS_CHROMEOS)
+    ASSERT_EQ(kForceDevToolsAvailableBase,
+              chromeos::switches::kForceDevToolsAvailable);
+#endif
+    command_line->AppendSwitch("--" + kForceDevToolsAvailableBase);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(DevToolsAllowedByCommandLineSwitch,
+                       SwitchOverridesPolicyOnChromeOS) {
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kDevToolsAvailability,
+      static_cast<int>(policy::DeveloperToolsPolicyHandler::Availability::
+                           kDisallowedForForceInstalledExtensions));
+
+  content::WebContents* web_contents = nullptr;
+  ASSERT_NO_FATAL_FAILURE(ForceInstallExtension(&web_contents));
+
+  DevToolsWindow::OpenDevToolsWindow(web_contents);
+  auto agent_host = content::DevToolsAgentHost::GetOrCreateFor(web_contents);
+#if defined(OS_CHROMEOS)
+  ASSERT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+#else
+  ASSERT_FALSE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+#endif
+}
+
 class DevToolsPixelOutputTests : public DevToolsSanityTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index b9f2070..c933b0e 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -431,6 +431,10 @@
 // Not passed on restart after sign out.
 const char kFirstExecAfterBoot[] = "first-exec-after-boot";
 
+// Forces developer tools availability, no matter what values the enterprise
+// policies DeveloperToolsDisabled and DeveloperToolsAvailability are set to.
+const char kForceDevToolsAvailable[] = "force-devtools-available";
+
 // Forces first-run UI to be shown for every login.
 const char kForceFirstRunUI[] = "force-first-run-ui";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index f8ff4c2..33063c4 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -122,6 +122,7 @@
 CHROMEOS_EXPORT extern const char kFirstExecAfterBoot[];
 CHROMEOS_EXPORT extern const char kFakeDriveFsLauncherChrootPath[];
 CHROMEOS_EXPORT extern const char kFakeDriveFsLauncherSocketPath[];
+CHROMEOS_EXPORT extern const char kForceDevToolsAvailable[];
 CHROMEOS_EXPORT extern const char kForceFirstRunUI[];
 CHROMEOS_EXPORT extern const char kForceHappinessTrackingSystem[];
 CHROMEOS_EXPORT extern const char kForceLoginManagerInTests[];