[ChromeDriver] Disable /dev/shm usage when needed

Automatically add --disable-dev-shm-usage switch to ChromeDriver command
line on Linux when /dev/shm is not accessible.

Bug: chromedriver:2782, b/124440075
Change-Id: I7322094d7da4e6b1a02fa3092b50ba4dbbc6fa7b
Reviewed-on: https://chromium-review.googlesource.com/c/1478187
Reviewed-by: Caleb Rouleau <crouleau@chromium.org>
Commit-Queue: John Chen <johnchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#633558}
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index 77cc701..43921dc 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -96,6 +96,27 @@
   return true;
 }
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+// Ensure that there is a writable shared memory directory. We use
+// network::SimpleURLLoader to connect to Chrome, and it calls
+// base::subtle::PlatformSharedMemoryRegion::Create to get a shared memory
+// region. network::SimpleURLLoader would fail if the shared memory directory is
+// not accessible. We work around this issue by adding --disable-dev-shm-usage
+// to command line, to use an alternative directory for shared memory.
+// See https://crbug.com/chromedriver/2782.
+void EnsureSharedMemory(base::CommandLine* cmd_line) {
+  if (!cmd_line->HasSwitch("disable-dev-shm-usage")) {
+    base::FilePath directory;
+    if (GetShmemTempDir(false, &directory) &&
+        access(directory.value().c_str(), W_OK | X_OK) < 0) {
+      VLOG(0) << directory
+              << " not writable, adding --disable-dev-shm-usage switch";
+      cmd_line->AppendSwitch("disable-dev-shm-usage");
+    }
+  }
+}
+#endif
+
 class HttpServer : public net::HttpServer::Delegate {
  public:
   explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func)
@@ -383,31 +404,35 @@
     std::string options;
     const char* const kOptionAndDescriptions[] = {
         "port=PORT",
-        "port to listen on",
+            "port to listen on",
         "adb-port=PORT",
-        "adb server port",
+            "adb server port",
         "log-path=FILE",
-        "write server log to file instead of stderr, "
-        "increases log level to INFO",
+            "write server log to file instead of stderr, "
+            "increases log level to INFO",
         "log-level=LEVEL",
-        "set log level: ALL, DEBUG, INFO, WARNING, "
-        "SEVERE, OFF",
+            "set log level: ALL, DEBUG, INFO, WARNING, SEVERE, OFF",
         "verbose",
-        "log verbosely (equivalent to --log-level=ALL)",
+            "log verbosely (equivalent to --log-level=ALL)",
         "silent",
-        "log nothing (equivalent to --log-level=OFF)",
+            "log nothing (equivalent to --log-level=OFF)",
         "append-log",
-        "append log file instead of rewriting",
+            "append log file instead of rewriting",
         "replayable",
-        "(experimental) log verbosely and don't truncate long "
-        "strings so that the log can be replayed.",
+            "(experimental) log verbosely and don't truncate long "
+            "strings so that the log can be replayed.",
         "version",
-        "print the version number and exit",
+            "print the version number and exit",
         "url-base",
-        "base URL path prefix for commands, e.g. wd/url",
+            "base URL path prefix for commands, e.g. wd/url",
         "whitelisted-ips",
-        "comma-separated whitelist of remote IP addresses "
-        "which are allowed to connect to ChromeDriver",
+            "comma-separated whitelist of remote IP addresses "
+            "which are allowed to connect to ChromeDriver",
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+        "disable-dev-shm-usage",
+            "do not use /dev/shm "
+            "(add this switch if seeing errors related to shared memory)",
+#endif
     };
     for (size_t i = 0; i < base::size(kOptionAndDescriptions) - 1; i += 2) {
       options += base::StringPrintf(
@@ -499,6 +524,10 @@
     return 1;
   }
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  EnsureSharedMemory(cmd_line);
+#endif
+
   mojo::core::Init();
 
   base::TaskScheduler::CreateAndStartWithDefaultParams("ChromeDriver");