Migrate protocol/ to ESM

- InspectorBackendCommands is now loaded via ES Modules

Bug: 1006759
Change-Id: I2826518a55e1f4fd7704cfb04120885f9aa9b604
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1837772
Reviewed-by: Yang Guo <[email protected]>
Reviewed-by: Benedikt Meurer <[email protected]>
Commit-Queue: Tim Van der Lippe <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#702821}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 01369246df2350c6be2b0118d69313a7f56486bb
diff --git a/BUILD.gn b/BUILD.gn
index 87e9e3c..6adb8df 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -528,8 +528,6 @@
   "front_end/profiler/ProfileTypeRegistry.js",
   "front_end/profiler/ProfileView.js",
   "front_end/profiler/TopDownProfileDataGrid.js",
-  "front_end/protocol/NodeURL.js",
-  "front_end/protocol/InspectorBackend.js",
   "front_end/protocol/module.json",
   "front_end/quick_open/CommandMenu.js",
   "front_end/quick_open/filteredListWidget.css",
@@ -899,7 +897,9 @@
 all_devtools_files += lighthouse_locale_files
 
 all_devtools_modules = [
-  "front_end/root.js",
+  "front_end/protocol/protocol.js",
+  "front_end/protocol/NodeURL.js",
+  "front_end/protocol/InspectorBackend.js",
   "front_end/host/host.js",
   "front_end/host/UserMetrics.js",
   "front_end/host/ResourceLoader.js",
@@ -1191,6 +1191,9 @@
 ]
 
 copied_devtools_modules = [
+  "$resources_out_dir/protocol/protocol.js",
+  "$resources_out_dir/protocol/NodeURL.js",
+  "$resources_out_dir/protocol/InspectorBackend.js",
   "$resources_out_dir/host/host.js",
   "$resources_out_dir/host/UserMetrics.js",
   "$resources_out_dir/host/ResourceLoader.js",
@@ -1492,7 +1495,10 @@
   grd_files =
       copied_devtools_modules + generated_applications +
       generated_non_autostart_non_remote_modules + devtools_embedder_scripts +
-      [ "$resources_out_dir/devtools_extension_api.js" ]
+      [
+        "$resources_out_dir/devtools_extension_api.js",
+        "$resources_out_dir/InspectorBackendCommands.js",
+      ]
 
   # Bundle remote modules in ChromeOS.
   if (is_chromeos) {
diff --git a/front_end/protocol/InspectorBackend.js b/front_end/protocol/InspectorBackend.js
index 81b7a8f..3bdb696 100644
--- a/front_end/protocol/InspectorBackend.js
+++ b/front_end/protocol/InspectorBackend.js
@@ -32,16 +32,21 @@
  * @typedef {string}
  * @suppress {checkTypes}
  */
-Protocol.Error = Symbol('Protocol.Error');
+export const ProtocolError = Symbol('Protocol.Error');
+export const DevToolsStubErrorCode = -32015;
+// TODO(dgozman): we are not reporting generic errors in tests, but we should
+// instead report them and just have some expected errors in test expectations.
+const _GenericError = -32000;
+const _ConnectionClosedErrorCode = -32001;
 
 /**
  * @unrestricted
  */
-Protocol.InspectorBackend = class {
+export default class InspectorBackend {
   constructor() {
-    /** @type {!Map<string, !Protocol.InspectorBackend._AgentPrototype>} */
+    /** @type {!Map<string, !_AgentPrototype>} */
     this._agentPrototypes = new Map();
-    /** @type {!Map<string, !Protocol.InspectorBackend._DispatcherPrototype>} */
+    /** @type {!Map<string, !_DispatcherPrototype>} */
     this._dispatcherPrototypes = new Map();
     this._initialized = false;
   }
@@ -73,31 +78,31 @@
     const methodName = domain.substr(0, upperCaseLength).toLowerCase() + domain.slice(upperCaseLength) + 'Agent';
 
     /**
-     * @this {Protocol.TargetBase}
+     * @this {TargetBase}
      */
     function agentGetter() {
       return this._agents[domain];
     }
 
-    Protocol.TargetBase.prototype[methodName] = agentGetter;
+    TargetBase.prototype[methodName] = agentGetter;
 
     /**
-     * @this {Protocol.TargetBase}
+     * @this {TargetBase}
      */
     function registerDispatcher(dispatcher) {
       this.registerDispatcher(domain, dispatcher);
     }
 
-    Protocol.TargetBase.prototype['register' + domain + 'Dispatcher'] = registerDispatcher;
+    TargetBase.prototype['register' + domain + 'Dispatcher'] = registerDispatcher;
   }
 
   /**
    * @param {string} domain
-   * @return {!Protocol.InspectorBackend._AgentPrototype}
+   * @return {!_AgentPrototype}
    */
   _agentPrototype(domain) {
     if (!this._agentPrototypes.has(domain)) {
-      this._agentPrototypes.set(domain, new Protocol.InspectorBackend._AgentPrototype(domain));
+      this._agentPrototypes.set(domain, new _AgentPrototype(domain));
       this._addAgentGetterMethodToProtocolTargetPrototype(domain);
     }
 
@@ -106,11 +111,11 @@
 
   /**
    * @param {string} domain
-   * @return {!Protocol.InspectorBackend._DispatcherPrototype}
+   * @return {!_DispatcherPrototype}
    */
   _dispatcherPrototype(domain) {
     if (!this._dispatcherPrototypes.has(domain)) {
-      this._dispatcherPrototypes.set(domain, new Protocol.InspectorBackend._DispatcherPrototype());
+      this._dispatcherPrototypes.set(domain, new _DispatcherPrototype());
     }
     return this._dispatcherPrototypes.get(domain);
   }
@@ -180,40 +185,60 @@
     }
     return callbackWrapper;
   }
-};
+}
 
-Protocol.DevToolsStubErrorCode = -32015;
-
-Protocol.inspectorBackend = new Protocol.InspectorBackend();
+/** @type {function():!Connection} */
+let _factory;
 
 /**
  * @interface
  */
-Protocol.Connection = function() {};
+export class Connection {
+  constructor() {
+    /** @type {?function(!Object)} */
+    this._onMessage;
+  }
 
-Protocol.Connection.prototype = {
   /**
    * @param {function((!Object|string))} onMessage
    */
-  setOnMessage(onMessage) {},
+  setOnMessage(onMessage) {
+  }
 
   /**
    * @param {function(string)} onDisconnect
    */
-  setOnDisconnect(onDisconnect) {},
+  setOnDisconnect(onDisconnect) {
+  }
 
   /**
    * @param {string} message
    */
-  sendRawMessage(message) {},
+  sendRawMessage(message) {
+  }
 
   /**
    * @return {!Promise}
    */
-  disconnect() {},
-};
+  disconnect() {
+  }
 
-Protocol.test = {
+  /**
+   * @param {function():!Connection} factory
+   */
+  static setFactory(factory) {
+    _factory = factory;
+  }
+
+  /**
+   * @return {function():!Connection}
+   */
+  static getFactory() {
+    return _factory;
+  }
+}
+
+const test = {
   /**
    * This will get called for every protocol message.
    * Protocol.test.dumpProtocol = console.log
@@ -241,42 +266,20 @@
 
   /**
    * Set to get notified about any messages sent over protocol.
-   * @type {?function({domain: string, method: string, params: !Object, id: number}, ?Protocol.TargetBase)}
+   * @type {?function({domain: string, method: string, params: !Object, id: number}, ?TargetBase)}
    */
   onMessageSent: null,
 
   /**
    * Set to get notified about any messages received over protocol.
-   * @type {?function(!Object, ?Protocol.TargetBase)}
+   * @type {?function(!Object, ?TargetBase)}
    */
   onMessageReceived: null,
 };
 
-/**
- * @param {function():!Protocol.Connection} factory
- */
-Protocol.Connection.setFactory = function(factory) {
-  Protocol.Connection._factory = factory;
-};
-
-
-/** @type {function():!Protocol.Connection} */
-Protocol.Connection._factory;
-
-/**
- * Takes error and result.
- * @typedef {function(?Object, ?Object)}
- */
-Protocol._Callback;
-
-// TODO(dgozman): we are not reporting generic errors in tests, but we should
-// instead report them and just have some expected errors in test expectations.
-Protocol._GenericError = -32000;
-Protocol._ConnectionClosedErrorCode = -32001;
-
-Protocol.SessionRouter = class {
+export class SessionRouter {
   /**
-   * @param {!Protocol.Connection} connection
+   * @param {!Connection} connection
    */
   constructor(connection) {
     this._connection = connection;
@@ -284,14 +287,14 @@
     this._pendingResponsesCount = 0;
     this._domainToLogger = new Map();
 
-    /** @type {!Map<string, {target: !Protocol.TargetBase, callbacks: !Map<number, !Protocol._Callback>, proxyConnection: ?Protocol.Connection}>} */
+    /** @type {!Map<string, {target: !TargetBase, callbacks: !Map<number, !Protocol._Callback>, proxyConnection: ?Connection}>} */
     this._sessions = new Map();
 
     /** @type {!Array<function()>} */
     this._pendingScripts = [];
 
-    Protocol.test.deprecatedRunAfterPendingDispatches = this._deprecatedRunAfterPendingDispatches.bind(this);
-    Protocol.test.sendRawMessage = this._sendRawMessageForTesting.bind(this);
+    test.deprecatedRunAfterPendingDispatches = this._deprecatedRunAfterPendingDispatches.bind(this);
+    test.sendRawMessage = this._sendRawMessageForTesting.bind(this);
 
     this._connection.setOnMessage(this._onMessage.bind(this));
 
@@ -304,9 +307,9 @@
   }
 
   /**
-   * @param {!Protocol.TargetBase} target
+   * @param {!TargetBase} target
    * @param {string} sessionId
-   * @param {?Protocol.Connection} proxyConnection
+   * @param {?Connection} proxyConnection
    */
   registerSession(target, sessionId, proxyConnection) {
     this._sessions.set(sessionId, {target, callbacks: new Map(), proxyConnection});
@@ -318,14 +321,14 @@
   unregisterSession(sessionId) {
     const session = this._sessions.get(sessionId);
     for (const callback of session.callbacks.values()) {
-      Protocol.SessionRouter.dispatchConnectionError(callback);
+      SessionRouter.dispatchConnectionError(callback);
     }
     this._sessions.delete(sessionId);
   }
 
   /**
    * @param {string} sessionId
-   * @return {?Protocol.TargetBase}
+   * @return {?TargetBase}
    */
   _getTargetBySessionId(sessionId) {
     const session = this._sessions.get(sessionId ? sessionId : '');
@@ -343,7 +346,7 @@
   }
 
   /**
-   * @return {!Protocol.Connection}
+   * @return {!Connection}
    */
   connection() {
     return this._connection;
@@ -368,13 +371,13 @@
       messageObject.sessionId = sessionId;
     }
 
-    if (Protocol.test.dumpProtocol) {
-      Protocol.test.dumpProtocol('frontend: ' + JSON.stringify(messageObject));
+    if (test.dumpProtocol) {
+      test.dumpProtocol('frontend: ' + JSON.stringify(messageObject));
     }
 
-    if (Protocol.test.onMessageSent) {
+    if (test.onMessageSent) {
       const paramsObject = JSON.parse(JSON.stringify(params || {}));
-      Protocol.test.onMessageSent(
+      test.onMessageSent(
           {domain, method, params: /** @type {!Object} */ (paramsObject), id: messageId},
           this._getTargetBySessionId(sessionId));
     }
@@ -398,13 +401,13 @@
    * @param {!Object|string} message
    */
   _onMessage(message) {
-    if (Protocol.test.dumpProtocol) {
-      Protocol.test.dumpProtocol('backend: ' + ((typeof message === 'string') ? message : JSON.stringify(message)));
+    if (test.dumpProtocol) {
+      test.dumpProtocol('backend: ' + ((typeof message === 'string') ? message : JSON.stringify(message)));
     }
 
-    if (Protocol.test.onMessageReceived) {
+    if (test.onMessageReceived) {
       const messageObjectCopy = JSON.parse((typeof message === 'string') ? message : JSON.stringify(message));
-      Protocol.test.onMessageReceived(
+      test.onMessageReceived(
           /** @type {!Object} */ (messageObjectCopy), this._getTargetBySessionId(messageObjectCopy.sessionId));
     }
 
@@ -497,22 +500,22 @@
   static dispatchConnectionError(callback) {
     const error = {
       message: 'Connection is closed, can\'t dispatch pending call',
-      code: Protocol._ConnectionClosedErrorCode,
+      code: _ConnectionClosedErrorCode,
       data: null
     };
     setTimeout(() => callback(error, null), 0);
   }
-};
+}
 
 /**
  * @unrestricted
  */
-Protocol.TargetBase = class {
+export class TargetBase {
   /**
    * @param {boolean} needsNodeJSPatching
-   * @param {?Protocol.TargetBase} parentTarget
+   * @param {?TargetBase} parentTarget
    * @param {string} sessionId
-   * @param {?Protocol.Connection} connection
+   * @param {?Connection} connection
    */
   constructor(needsNodeJSPatching, parentTarget, sessionId, connection) {
     this._needsNodeJSPatching = needsNodeJSPatching;
@@ -524,23 +527,22 @@
     if (sessionId) {
       this._router = parentTarget._router;
     } else if (connection) {
-      this._router = new Protocol.SessionRouter(connection);
+      this._router = new SessionRouter(connection);
     } else {
-      this._router = new Protocol.SessionRouter(Protocol.Connection._factory());
+      this._router = new SessionRouter(_factory());
     }
 
     this._router.registerSession(this, this._sessionId);
 
     this._agents = {};
     for (const [domain, agentPrototype] of Protocol.inspectorBackend._agentPrototypes) {
-      this._agents[domain] = Object.create(/** @type {!Protocol.InspectorBackend._AgentPrototype} */ (agentPrototype));
+      this._agents[domain] = Object.create(/** @type {!_AgentPrototype} */ (agentPrototype));
       this._agents[domain]._target = this;
     }
 
     this._dispatchers = {};
     for (const [domain, dispatcherPrototype] of Protocol.inspectorBackend._dispatcherPrototypes) {
-      this._dispatchers[domain] =
-          Object.create(/** @type {!Protocol.InspectorBackend._DispatcherPrototype} */ (dispatcherPrototype));
+      this._dispatchers[domain] = Object.create(/** @type {!_DispatcherPrototype} */ (dispatcherPrototype));
       this._dispatchers[domain]._dispatchers = [];
     }
   }
@@ -576,17 +578,17 @@
   }
 
   /**
-   * @return {!Protocol.SessionRouter}
+   * @return {!SessionRouter}
    */
   router() {
     return this._router;
   }
-};
+}
 
 /**
  * @unrestricted
  */
-Protocol.InspectorBackend._AgentPrototype = class {
+export class _AgentPrototype {
   /**
    * @param {string} domain
    */
@@ -607,13 +609,12 @@
 
     /**
      * @param {...*} vararg
-     * @this {Protocol.InspectorBackend._AgentPrototype}
+     * @this {_AgentPrototype}
      * @return {!Promise.<*>}
      */
     function sendMessagePromise(vararg) {
       const params = Array.prototype.slice.call(arguments);
-      return Protocol.InspectorBackend._AgentPrototype.prototype._sendMessageToBackendPromise.call(
-          this, domainAndMethod, signature, params);
+      return _AgentPrototype.prototype._sendMessageToBackendPromise.call(this, domainAndMethod, signature, params);
     }
 
     this[methodName] = sendMessagePromise;
@@ -621,7 +622,7 @@
     /**
      * @param {!Object} request
      * @return {!Promise}
-     * @this {Protocol.InspectorBackend._AgentPrototype}
+     * @this {_AgentPrototype}
      */
     function invoke(request) {
       return this._invoke(domainAndMethod, request);
@@ -704,8 +705,8 @@
 
     return new Promise(resolve => {
       const callback = (error, result) => {
-        if (error && !Protocol.test.suppressRequestErrors && error.code !== Protocol.DevToolsStubErrorCode &&
-            error.code !== Protocol._GenericError && error.code !== Protocol._ConnectionClosedErrorCode) {
+        if (error && !test.suppressRequestErrors && error.code !== Protocol.DevToolsStubErrorCode &&
+            error.code !== _GenericError && error.code !== _ConnectionClosedErrorCode) {
           console.error('Request ' + method + ' failed. ' + JSON.stringify(error));
         }
 
@@ -719,7 +720,7 @@
       };
 
       if (!this._target._router) {
-        Protocol.SessionRouter.dispatchConnectionError(callback);
+        SessionRouter.dispatchConnectionError(callback);
       } else {
         this._target._router.sendMessage(this._target._sessionId, this._domain, method, params, callback);
       }
@@ -734,8 +735,8 @@
   _invoke(method, request) {
     return new Promise(fulfill => {
       const callback = (error, result) => {
-        if (error && !Protocol.test.suppressRequestErrors && error.code !== Protocol.DevToolsStubErrorCode &&
-            error.code !== Protocol._GenericError && error.code !== Protocol._ConnectionClosedErrorCode) {
+        if (error && !test.suppressRequestErrors && error.code !== Protocol.DevToolsStubErrorCode &&
+            error.code !== _GenericError && error.code !== _ConnectionClosedErrorCode) {
           console.error('Request ' + method + ' failed. ' + JSON.stringify(error));
         }
 
@@ -750,18 +751,18 @@
       };
 
       if (!this._target._router) {
-        Protocol.SessionRouter.dispatchConnectionError(callback);
+        SessionRouter.dispatchConnectionError(callback);
       } else {
         this._target._router.sendMessage(this._target._sessionId, this._domain, method, request, callback);
       }
     });
   }
-};
+}
 
 /**
  * @unrestricted
  */
-Protocol.InspectorBackend._DispatcherPrototype = class {
+export class _DispatcherPrototype {
   constructor() {
     this._eventArgs = {};
   }
@@ -811,4 +812,47 @@
       }
     }
   }
-};
+}
+
+/* Legacy exported object */
+self.Protocol = self.Protocol || {};
+
+/* Legacy exported object */
+Protocol = Protocol || {};
+
+Protocol.DevToolsStubErrorCode = DevToolsStubErrorCode;
+/** @typedef {string} */
+Protocol.Error = ProtocolError;
+
+/** @constructor */
+Protocol.InspectorBackend = InspectorBackend;
+
+/**
+ * @unrestricted
+ */
+Protocol.InspectorBackend._AgentPrototype = _AgentPrototype;
+
+/**
+ * @unrestricted
+ */
+Protocol.InspectorBackend._DispatcherPrototype = _DispatcherPrototype;
+
+/** @interface */
+Protocol.Connection = Connection;
+
+/** @type {!InspectorBackend} */
+Protocol.inspectorBackend = new InspectorBackend();
+
+Protocol.test = test;
+
+/** @constructor */
+Protocol.SessionRouter = SessionRouter;
+
+/** @constructor */
+Protocol.TargetBase = TargetBase;
+
+/**
+ * Takes error and result.
+ * @typedef {function(?Object, ?Object)}
+ */
+Protocol._Callback;
\ No newline at end of file
diff --git a/front_end/protocol/NodeURL.js b/front_end/protocol/NodeURL.js
index 6e00a79..d64cfc4 100644
--- a/front_end/protocol/NodeURL.js
+++ b/front_end/protocol/NodeURL.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-Protocol.NodeURL = class {
+export default class NodeURL {
   /**
    * @param {!Object} object
    */
@@ -14,7 +14,7 @@
      * @param {string} path
      */
     function process(object, path) {
-      if (object.url && Protocol.NodeURL._isPlatformPath(object.url, Host.isWin())) {
+      if (object.url && NodeURL._isPlatformPath(object.url, Host.isWin())) {
         object.url = Common.ParsedURL.platformPathToURL(object.url);
       }
       for (const entry of Object.entries(object)) {
@@ -41,4 +41,13 @@
       return fileSystemPath.length ? fileSystemPath[0] === '/' : false;
     }
   }
-};
+}
+
+/* Legacy exported object */
+self.Protocol = self.Protocol || {};
+
+/* Legacy exported object */
+Protocol = Protocol || {};
+
+/** @constructor */
+Protocol.NodeURL = NodeURL;
diff --git a/front_end/protocol/module.json b/front_end/protocol/module.json
index 9dda4bd..6dc2f8e 100644
--- a/front_end/protocol/module.json
+++ b/front_end/protocol/module.json
@@ -3,7 +3,9 @@
     "common",
     "host"
   ],
-  "scripts": [
+  "scripts": [],
+  "modules": [
+    "protocol.js",
     "NodeURL.js",
     "InspectorBackend.js",
     "../InspectorBackendCommands.js"
diff --git a/front_end/protocol/protocol.js b/front_end/protocol/protocol.js
new file mode 100644
index 0000000..4b09755
--- /dev/null
+++ b/front_end/protocol/protocol.js
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './InspectorBackend.js';
+import './NodeURL.js';
+import '../InspectorBackendCommands.js';
+
+import * as InspectorBackend from './InspectorBackend.js';
+import * as NodeURL from './NodeURL.js';
+
+export {
+  InspectorBackend,
+  NodeURL,
+};
diff --git a/front_end/root.js b/front_end/root.js
index c807e43..31d9fe6 100644
--- a/front_end/root.js
+++ b/front_end/root.js
@@ -7,4 +7,5 @@
 import './dom_extension/DOMExtension.js';
 import './common/common.js';
 import './host/host.js';
+import './protocol/protocol.js';
 import './ui/ui.js';
diff --git a/front_end/sdk_test_runner/PageMockTestRunner.js b/front_end/sdk_test_runner/PageMockTestRunner.js
index febd281..8885079 100644
--- a/front_end/sdk_test_runner/PageMockTestRunner.js
+++ b/front_end/sdk_test_runner/PageMockTestRunner.js
@@ -48,13 +48,13 @@
     this._enabledDomains.clear();
     SDK.targetManager._targets = [];
 
-    const oldFactory = Protocol.Connection._factory;
-    Protocol.Connection._factory = () => {
+    const oldFactory = Protocol.Connection.getFactory();
+    Protocol.Connection.setFactory(() => {
       this._connection = new MockPageConnection(this);
       return this._connection;
-    };
+    });
     const target = SDK.targetManager.createTarget(nextId('mock-target-'), targetName, this._type, null);
-    Protocol.Connection._factory = oldFactory;
+    Protocol.Connection.setFactory(oldFactory);
 
     this._target = target;
     return target;
diff --git a/scripts/compile_frontend.py b/scripts/compile_frontend.py
index befedb9..3319a0f 100755
--- a/scripts/compile_frontend.py
+++ b/scripts/compile_frontend.py
@@ -290,6 +290,12 @@
         args.extend(['--js', file])
         if "InspectorBackend.js" in file:
             args.extend(['--js', protocol_externs_file])
+
+            # Write a dummy file for InspectorBackendCommands. We don't type-check this file, but we
+            # import it from protocol/protocol.js
+            inspector_backends_commands_file = path.join(temp_frontend_path, 'InspectorBackendCommands.js')
+            modular_build.write_file(inspector_backends_commands_file, '')
+            args.extend(['--js', inspector_backends_commands_file])
     command += args
     command = [arg.replace(DEVTOOLS_FRONTEND_PATH, temp_frontend_path) for arg in command]
     compiler_args_file = tempfile.NamedTemporaryFile(mode='wt', delete=False)