Bug 1513118 Part 3 - Devtools server/client support for virtual console logs, r=lsmyth.
☠☠ backed out by 9e3564442734 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 29 Dec 2018 08:24:31 -1000
changeset 453663 4abb81088a9b49d700cfea840848a9dac6a0010d
parent 453662 5ce216ffcb1da139187cf8c62473d150df35dc5e
child 453664 0ed38259d2d8e55dd6e00c5ba53cf21a6a390d78
push id35365
push userdvarga@mozilla.com
push dateSun, 13 Jan 2019 10:05:55 +0000
treeherdermozilla-central@1218e374fbc7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsmyth
bugs1513118
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1513118 Part 3 - Devtools server/client support for virtual console logs, r=lsmyth.
devtools/client/framework/toolbox.js
devtools/client/webconsole/webconsole-connection-proxy.js
devtools/server/actors/source.js
devtools/shared/client/constants.js
devtools/shared/client/source-client.js
devtools/shared/specs/source.js
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -464,16 +464,22 @@ Toolbox.prototype = {
           "NetworkActivity",
         ]);
       }
 
       // Attach the thread
       this._threadClient = await attachThread(this);
       await domReady;
 
+      // The web console is immediately loaded when replaying, so that the
+      // timeline will always be populated with generated messages.
+      if (this.target.isReplayEnabled()) {
+        await this.loadTool("webconsole");
+      }
+
       this.isReady = true;
 
       const framesPromise = this._listFrames();
 
       Services.prefs.addObserver("devtools.cache.disabled", this._applyCacheSettings);
       Services.prefs.addObserver("devtools.serviceWorkers.testing.enabled",
                                  this._applyServiceWorkersTestingSettings);
 
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -25,16 +25,17 @@ const PREF_CONNECTION_TIMEOUT = "devtool
 function WebConsoleConnectionProxy(webConsoleFrame, target) {
   this.webConsoleFrame = webConsoleFrame;
   this.target = target;
   this.webConsoleClient = target.activeConsole;
 
   this._onPageError = this._onPageError.bind(this);
   this._onLogMessage = this._onLogMessage.bind(this);
   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
+  this._onVirtualConsoleLog = this._onVirtualConsoleLog.bind(this);
   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   this._onTabNavigated = this._onTabNavigated.bind(this);
   this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
   this._onAttachConsole = this._onAttachConsole.bind(this);
   this._onCachedMessages = this._onCachedMessages.bind(this);
   this._connectionTimeout = this._connectionTimeout.bind(this);
   this._onLastPrivateContextExited =
@@ -116,16 +117,18 @@ WebConsoleConnectionProxy.prototype = {
 
     const client = this.client = this.target.client;
 
     client.addListener("logMessage", this._onLogMessage);
     client.addListener("pageError", this._onPageError);
     client.addListener("consoleAPICall", this._onConsoleAPICall);
     client.addListener("lastPrivateContextExited",
                        this._onLastPrivateContextExited);
+    client.addListener("virtualConsoleLog",
+                       this._onVirtualConsoleLog);
 
     this.target.on("will-navigate", this._onTabWillNavigate);
     this.target.on("navigate", this._onTabNavigated);
 
     if (this.target.isBrowsingContext) {
       this.webConsoleFrame.onLocationChange(this.target.url, this.target.title);
     }
     this._attachConsole();
@@ -297,16 +300,30 @@ WebConsoleConnectionProxy.prototype = {
    *        The message received from the server.
    */
   _onConsoleAPICall: function(type, packet) {
     if (!this.webConsoleFrame || packet.from != this.webConsoleClient.actor) {
       return;
     }
     this.dispatchMessageAdd(packet);
   },
+
+  _onVirtualConsoleLog: function(type, packet) {
+    if (!this.webConsoleFrame) {
+      return;
+    }
+    this.dispatchMessageAdd({
+      type: "consoleAPICall",
+      message: {
+        executionPoint: packet.executionPoint,
+        "arguments": [packet.url + ":" + packet.line, packet.message],
+      },
+    });
+  },
+
   /**
    * The "networkEvent" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
    * @param object networkInfo
    *        The network request information.
    */
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -731,46 +731,30 @@ const SourceActor = ActorClassWithSpec(s
         generatedLocation
       )) {
         success = true;
       }
     }
     return success;
   },
 
-  /*
-   * Ensure the given BreakpointActor is set as breakpoint handler on all
-   * scripts that match the given location in the generated source.
-   *
-   * @param BreakpointActor actor
-   *        The BreakpointActor to be set as a breakpoint handler.
-   * @param GeneratedLocation generatedLocation
-   *        A GeneratedLocation representing the location in the generated
-   *        source for which the given BreakpointActor is to be set as a
-   *        breakpoint handler.
-   *
-   * @returns A Boolean that is true if the BreakpointActor was set as a
-   *          breakpoint handler on at least one script, and false otherwise.
-   */
-  _setBreakpointAtGeneratedLocation: function(actor, generatedLocation) {
+  _findEntryPointsForLocation: function(generatedLocation) {
     const {
       generatedSourceActor,
       generatedLine,
       generatedColumn,
       generatedLastColumn,
     } = generatedLocation;
 
     // Find all scripts that match the given source actor and line
     // number.
-    let scripts = generatedSourceActor._findDebuggeeScripts(
+    const scripts = generatedSourceActor._findDebuggeeScripts(
       { line: generatedLine }
     );
 
-    scripts = scripts.filter((script) => !actor.hasScript(script));
-
     // Find all entry points that correspond to the given location.
     const entryPoints = [];
     if (generatedColumn === undefined) {
       // This is a line breakpoint, so we are interested in all offsets
       // that correspond to the given line number.
       for (const script of scripts) {
         const offsets = script.getLineOffsets(generatedLine);
         if (offsets.length > 0) {
@@ -826,18 +810,61 @@ const SourceActor = ActorClassWithSpec(s
             if (generatedColumn > lastColumnOffset.columnNumber) {
               entryPoints.push({ script, offsets: [lastColumnOffset.offset] });
             }
           }
         }
       }
     }
 
+    return entryPoints;
+  },
+
+  /*
+   * Ensure the given BreakpointActor is set as breakpoint handler on all
+   * scripts that match the given location in the generated source.
+   *
+   * @param BreakpointActor actor
+   *        The BreakpointActor to be set as a breakpoint handler.
+   * @param GeneratedLocation generatedLocation
+   *        A GeneratedLocation representing the location in the generated
+   *        source for which the given BreakpointActor is to be set as a
+   *        breakpoint handler.
+   *
+   * @returns A Boolean that is true if the BreakpointActor was set as a
+   *          breakpoint handler on at least one script, and false otherwise.
+   */
+  _setBreakpointAtGeneratedLocation: function(actor, generatedLocation) {
+    let entryPoints = this._findEntryPointsForLocation(generatedLocation);
+    entryPoints = entryPoints.filter(({script}) => !actor.hasScript(script));
+
     if (entryPoints.length === 0) {
       return false;
     }
 
     setBreakpointAtEntryPoints(actor, entryPoints);
     return true;
   },
+
+  setVirtualLog(line, column, text) {
+    const location = new GeneratedLocation(this, line, column);
+    const entryPoints = this._findEntryPointsForLocation(location);
+
+    for (const { script, offsets } of entryPoints) {
+      for (const offset of offsets) {
+        script.replayVirtualConsoleLog(offset, text, (point, rv) => {
+          const packet = {
+            from: this.actorID,
+            type: "virtualConsoleLog",
+            url: this.url,
+            line,
+            column,
+            executionPoint: point,
+            message: "return" in rv ? "" + rv.return : "" + rv.throw,
+          };
+          this.conn.send(packet);
+        });
+      }
+    }
+  },
 });
 
 exports.SourceActor = SourceActor;
--- a/devtools/shared/client/constants.js
+++ b/devtools/shared/client/constants.js
@@ -32,16 +32,17 @@ const UnsolicitedNotifications = {
   "reflowActivity": "reflowActivity",
   "addonListChanged": "addonListChanged",
   "workerListChanged": "workerListChanged",
   "serviceWorkerRegistrationListChanged": "serviceWorkerRegistrationList",
   "pageError": "pageError",
   "evaluationResult": "evaluationResult",
   "updatedSource": "updatedSource",
   "inspectObject": "inspectObject",
+  "virtualConsoleLog": "virtualConsoleLog",
 
   // newSource is still emitted on the ThreadActor, in addition to the
   // BrowsingContextActor we have to keep it here until ThreadClient is converted to
   // ThreadFront and/or we stop emitting this duplicated events.
   // See ThreadActor.onNewSourceEvent.
   "newSource": "newSource",
 };
 
--- a/devtools/shared/client/source-client.js
+++ b/devtools/shared/client/source-client.js
@@ -243,16 +243,22 @@ SourceClient.prototype = {
       const cleanUp = type == "paused" && why.type == "interrupted"
             ? () => this._activeThread.resume()
             : noop;
 
       return doSetBreakpoint(cleanUp);
     });
   },
 
+  setVirtualLog: DebuggerClient.requester({
+    type: "setVirtualLog",
+    location: arg(0),
+    text: arg(1),
+  }),
+
   setPausePoints: function(pausePoints) {
     const packet = {
       to: this._form.actor,
       type: "setPausePoints",
       pausePoints,
     };
     return this._client.request(packet);
   },
--- a/devtools/shared/specs/source.js
+++ b/devtools/shared/specs/source.js
@@ -61,12 +61,21 @@ const sourceSpec = generateActorSpec({
           line: Arg(0, "number"),
           column: Arg(1, "nullable:number"),
         },
         condition: Arg(2, "nullable:string"),
         noSliding: Arg(3, "nullable:boolean"),
       },
       response: RetVal("json"),
     },
+    setVirtualLog: {
+      request: {
+        location: {
+          line: Arg(0, "number"),
+          column: Arg(1, "nullable:number"),
+        },
+        text: Arg(2, "string"),
+      },
+    },
   },
 });
 
 exports.sourceSpec = sourceSpec;