Adds a /json/protocol endpoint to serve the DevTools protocol

This should be useful for node.js developers who curently have
to download the protocol from a website.

This patch increaces the binary size by around 41kb.

BUG=538300

Review-Url: https://codereview.chromium.org/2867073006
Cr-Commit-Position: refs/heads/master@{#470921}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index b3ef290..8b8f8b33 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -64,8 +64,8 @@
     "//content/app/strings",
     "//content/browser/background_sync:background_sync_proto",
     "//content/browser/cache_storage:cache_storage_proto",
+    "//content/browser/devtools:devtools_resources",
     "//content/browser/devtools:protocol_sources",
-    "//content/browser/devtools:resources",
     "//content/browser/dom_storage:local_storage_proto",
     "//content/browser/notifications:notification_proto",
     "//content/browser/payments:payment_app_proto",
@@ -138,6 +138,7 @@
     "//third_party/WebKit/public:mojo_bindings",
     "//third_party/WebKit/public:resources",
     "//third_party/angle:angle_common",
+    "//third_party/brotli:dec",
     "//third_party/icu",
     "//third_party/libyuv",
     "//third_party/re2",
diff --git a/content/browser/devtools/BUILD.gn b/content/browser/devtools/BUILD.gn
index 109d100..1f3a739e 100644
--- a/content/browser/devtools/BUILD.gn
+++ b/content/browser/devtools/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//tools/grit/grit_rule.gni")
+import("//third_party/brotli/brotli.gni")
 import("//third_party/inspector_protocol/inspector_protocol.gni")
 
 group("resources") {
@@ -13,6 +14,17 @@
   }
 }
 
+compressed_protocol_file =
+    "$root_gen_dir/blink/core/inspector/protocol.json.bro"
+
+compress_file_brotli("compressed_protocol_json") {
+  input_file = "$root_gen_dir/blink/core/inspector/protocol.json"
+  output_file = compressed_protocol_file
+  deps = [
+    "//third_party/WebKit/Source/core/inspector:protocol_version",
+  ]
+}
+
 grit("devtools_resources") {
   source = "$root_gen_dir/devtools/devtools_resources.grd"
   source_is_generated = true
@@ -24,10 +36,18 @@
     "grit/devtools_resources_map.h",
   ]
 
+  grit_flags = [
+    "-E",
+    "compressed_protocol_file=" +
+        rebase_path(compressed_protocol_file, root_build_dir),
+  ]
+
   defines =
       [ "SHARED_INTERMEDIATE_DIR=" + rebase_path(root_gen_dir, root_build_dir) ]
 
   deps = [
+    ":compressed_protocol_json",
+
     # This is the action that generates out .grd input file.
     "//third_party/WebKit/public:blink_generate_devtools_grd",
   ]
diff --git a/content/browser/devtools/DEPS b/content/browser/devtools/DEPS
index 4029bd9..28ece8ad 100644
--- a/content/browser/devtools/DEPS
+++ b/content/browser/devtools/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+third_party/brotli",  # For compressed protocol.json.
   # V8 version info
   "+v8/include/v8-version-string.h",
 ]
diff --git a/content/browser/devtools/devtools_http_handler.cc b/content/browser/devtools/devtools_http_handler.cc
index 7f06067..81eda4c 100644
--- a/content/browser/devtools/devtools_http_handler.cc
+++ b/content/browser/devtools/devtools_http_handler.cc
@@ -14,6 +14,7 @@
 #include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -23,10 +24,12 @@
 #include "build/build_config.h"
 #include "content/browser/devtools/devtools_http_handler.h"
 #include "content/browser/devtools/devtools_manager.h"
+#include "content/browser/devtools/grit/devtools_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
 #include "content/public/browser/devtools_manager_delegate.h"
 #include "content/public/browser/devtools_socket_factory.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/user_agent.h"
 #include "net/base/escape.h"
@@ -37,6 +40,7 @@
 #include "net/server/http_server_request_info.h"
 #include "net/server/http_server_response_info.h"
 #include "net/socket/server_socket.h"
+#include "third_party/brotli/include/brotli/decode.h"
 #include "v8/include/v8-version-string.h"
 
 #if defined(OS_ANDROID)
@@ -534,6 +538,11 @@
     return;
   }
 
+  if (command == "protocol") {
+    DecompressAndSendJsonProtocol(connection_id);
+    return;
+  }
+
   if (command == "list") {
     DevToolsManager* manager = DevToolsManager::GetInstance();
     DevToolsAgentHost::List list =
@@ -607,6 +616,47 @@
   return;
 }
 
+void DevToolsHttpHandler::DecompressAndSendJsonProtocol(int connection_id) {
+  scoped_refptr<base::RefCountedMemory> raw_bytes =
+      GetContentClient()->GetDataResourceBytes(COMPRESSED_PROTOCOL_JSON);
+  const uint8_t* next_encoded_byte = raw_bytes->front();
+  size_t input_size_remaining = raw_bytes->size();
+  BrotliDecoderState* decoder = BrotliDecoderCreateInstance(
+      nullptr /* no custom allocator */, nullptr /* no custom deallocator */,
+      nullptr /* no custom memory handle */);
+  CHECK(!!decoder);
+  std::vector<std::string> decoded_parts;
+  size_t decompressed_size = 0;
+  while (!BrotliDecoderIsFinished(decoder)) {
+    size_t output_size_remaining = 0;
+    CHECK(BrotliDecoderDecompressStream(
+              decoder, &input_size_remaining, &next_encoded_byte,
+              &output_size_remaining, nullptr,
+              nullptr) != BROTLI_DECODER_RESULT_ERROR);
+    const uint8_t* output_buffer =
+        BrotliDecoderTakeOutput(decoder, &output_size_remaining);
+    decoded_parts.emplace_back(reinterpret_cast<const char*>(output_buffer),
+                               output_size_remaining);
+    decompressed_size += output_size_remaining;
+  }
+  BrotliDecoderDestroyInstance(decoder);
+
+  // Ideally we'd use a StringBuilder here but there isn't one in base/.
+  std::string json_protocol;
+  json_protocol.reserve(decompressed_size);
+  for (const std::string& part : decoded_parts) {
+    json_protocol.append(part);
+  }
+
+  net::HttpServerResponseInfo response(net::HTTP_OK);
+  response.SetBody(json_protocol, "application/json; charset=UTF-8");
+
+  thread_->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&ServerWrapper::SendResponse,
+                 base::Unretained(server_wrapper_), connection_id, response));
+}
+
 void DevToolsHttpHandler::RespondToJsonList(
     int connection_id,
     const std::string& host,
diff --git a/content/browser/devtools/devtools_http_handler.h b/content/browser/devtools/devtools_http_handler.h
index 5989da0..72836212 100644
--- a/content/browser/devtools/devtools_http_handler.h
+++ b/content/browser/devtools/devtools_http_handler.h
@@ -98,6 +98,8 @@
   void AcceptWebSocket(int connection_id,
                        const net::HttpServerRequestInfo& request);
 
+  void DecompressAndSendJsonProtocol(int connection_id);
+
   // Returns the front end url without the host at the beginning.
   std::string GetFrontendURLInternal(const std::string& target_id,
                                      const std::string& host);