Commit 7b90734d authored by Alexandre Poirot's avatar Alexandre Poirot
Browse files

Bug 1578408 - Attach content process thread actor from the toolbox. r=jlast,jdescottes,yulia

We need to attach the thread actor as soon as the toolbox start.
To:
* know when the thread is paused, even if the debugger isn't opened
* report paused state by changing the debugger icon
* switch to the debugger on pause
This will also fix some other code, like in the console, which
expect the Thread actor to be attached and already inspecting
the debugged target's globals.

Differential Revision: https://phabricator.services.mozilla.com/D44638

--HG--
extra : moz-landing-system : lando
parent 35abe098
Loading
Loading
Loading
Loading
+11 −17
Original line number Diff line number Diff line
@@ -55,12 +55,6 @@ export async function onConnect(connection: any, actions: Object) {
  // they are active once attached.
  actions.addEventListenerBreakpoints([]).catch(e => console.error(e));

  // In Firefox, we need to initially request all of the sources. This
  // usually fires off individual `newSource` notifications as the
  // debugger finds them, but there may be existing sources already in
  // the debugger (if it's paused already, or if loading the page from
  // bfcache) so explicity fire `newSource` events for all returned
  // sources.
  const traits = tabTarget.traits;
  await actions.connect(
    tabTarget.url,
@@ -69,18 +63,18 @@ export async function onConnect(connection: any, actions: Object) {
    tabTarget.isWebExtension
  );

  const fetched = clientCommands
    .fetchSources()
    .then(sources => actions.newGeneratedSources(sources));

  // If the threadFront is already paused, make sure to show a
  // paused state.
  const pausedPacket = threadFront.getLastPausePacket();
  if (pausedPacket) {
    clientEvents.paused(threadFront, pausedPacket);
  }
  // Fetch the sources for all the targets
  //
  // In Firefox, we need to initially request all of the sources. This
  // usually fires off individual `newSource` notifications as the
  // debugger finds them, but there may be existing sources already in
  // the debugger (if it's paused already, or if loading the page from
  // bfcache) so explicity fire `newSource` events for all returned
  // sources.
  const sources = await clientCommands.fetchSources();
  await actions.newGeneratedSources(sources);

  return fetched;
  await clientCommands.checkIfAlreadyPaused();
}

export { createObjectClient, clientCommands, clientEvents };
+30 −1
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

import { prepareSourcePayload, createThread } from "./create";
import { updateTargets } from "./targets";
import { clientEvents } from "./events";

import Reps from "devtools-reps";
import type { Node } from "devtools-reps";
@@ -425,8 +426,35 @@ async function toggleEventLogging(logEventBreakpoints: boolean) {
  );
}

function getAllThreadFronts() {
  const fronts = [currentThreadFront];
  for (const targetsForType of Object.values(targets)) {
    for (const { threadFront } of Object.values(targetsForType)) {
      fronts.push(threadFront);
    }
  }
  return fronts;
}

// Fetch the sources for all the targets
async function fetchSources(): Promise<Array<GeneratedSourceData>> {
  return getSources(currentThreadFront);
  let sources = [];
  for (const threadFront of getAllThreadFronts()) {
    sources = sources.concat(await getSources(threadFront));
  }
  return sources;
}

// Check if any of the targets were paused before we opened
// the debugger. If one is paused. Fake a `pause` RDP event
// by directly calling the client event listener.
async function checkIfAlreadyPaused() {
  for (const threadFront of getAllThreadFronts()) {
    const pausedPacket = threadFront.getLastPausePacket();
    if (pausedPacket) {
      clientEvents.paused(threadFront, pausedPacket);
    }
  }
}

function getSourceForActor(actor: ActorId) {
@@ -552,6 +580,7 @@ const clientCommands = {
  pauseOnExceptions,
  toggleEventLogging,
  fetchSources,
  checkIfAlreadyPaused,
  registerSourceActor,
  fetchThreads,
  getMainThread,
+13 −5
Original line number Diff line number Diff line
@@ -27,14 +27,22 @@ async function attachTargets(type, targetLists, args) {
      if (targets[threadActorID]) {
        newTargets[threadActorID] = targets[threadActorID];
      } else {
        const [, threadFront] = await targetFront.attachThread(args.options);
        // Content process targets have already been attached by the toolbox.
        // And the thread front has been initialized from there.
        // So we only need to retrieve it here.
        let threadFront = targetFront.threadFront;

        // But workers targets are still only managed by the debugger codebase
        // and so we have to attach their thread actor
        if (!threadFront) {
          [, threadFront] = await targetFront.attachThread(args.options);
          // NOTE: resume is not necessary for ProcessDescriptors and can be removed
          // once we switch to WorkerDescriptors
          threadFront.resume();
        }

        addThreadEventListeners(threadFront);

        await targetFront.attachConsole();
        newTargets[threadFront.actor] = targetFront;
      }
    } catch (e) {
+63 −16
Original line number Diff line number Diff line
@@ -571,9 +571,66 @@ Toolbox.prototype = {
    this.unhighlightTool("jsdebugger");
  },

  _startThreadFrontListeners: function() {
    this.threadFront.on("paused", this._onPausedState);
    this.threadFront.on("resumed", this._onResumedState);
  /**
   * Attach to a new top-level target.
   * This method will attach to the top-level target, as well as any potential
   * additional targets we may care about.
   */
  async _attachTargets(target) {
    this._threadFront = await this._attachTarget(target);

    const fissionSupport = Services.prefs.getBoolPref(
      "devtools.browsertoolbox.fission"
    );

    if (fissionSupport && target.isParentProcess && !target.isAddon) {
      const { mainRoot } = target.client;
      const { processes } = await mainRoot.listProcesses();

      for (const processDescriptor of processes) {
        const targetFront = await processDescriptor.getTarget();

        // Ignore the parent process target, which is the current target
        if (targetFront === target) {
          continue;
        }

        if (!targetFront) {
          console.warn(
            "Can't retrieve the target front for process",
            processDescriptor
          );
          continue;
        }
        await this._attachTarget(targetFront);
      }
    }
  },

  /**
   * This method focuses on attaching to one particular target.
   * It ensure that the target actor is fully initialized and is watching for
   * resources. We do that by calling its `attach` method.
   * And we listen for thread actor events in order to update toolbox UI when
   * we hit a breakpoint.
   */
  async _attachTarget(target) {
    await target.attach();

    // Start tracking network activity on toolbox open for targets such as tabs.
    // (Workers and potentially others don't manage the console client in the target.)
    if (target.activeConsole) {
      await target.activeConsole.startListeners(["NetworkActivity"]);
    }

    const threadFront = await this._attachAndResumeThread(target);
    this._startThreadFrontListeners(threadFront);
    return threadFront;
  },

  _startThreadFrontListeners: function(threadFront) {
    threadFront.on("paused", this._onPausedState);
    threadFront.on("resumed", this._onResumedState);
  },

  _stopThreadFrontListeners: function() {
@@ -581,8 +638,8 @@ Toolbox.prototype = {
    this.threadFront.off("resumed", this._onResumedState);
  },

  _attachAndResumeThread: async function() {
    const [, threadFront] = await this._target.attachThread({
  _attachAndResumeThread: async function(target) {
    const [, threadFront] = await target.attachThread({
      autoBlackBox: false,
      ignoreFrameEnvironment: true,
      pauseOnExceptions: Services.prefs.getBoolPref(
@@ -650,17 +707,7 @@ Toolbox.prototype = {
      // Optimization: fire up a few other things before waiting on
      // the iframe being ready (makes startup faster)

      // Load the toolbox-level actor fronts and utilities now
      await this._target.attach();

      // Start tracking network activity on toolbox open for targets such as tabs.
      // (Workers and potentially others don't manage the console client in the target.)
      if (this._target.activeConsole) {
        await this._target.activeConsole.startListeners(["NetworkActivity"]);
      }

      this._threadFront = await this._attachAndResumeThread();
      this._startThreadFrontListeners();
      await this._attachTargets(this.target);

      await domReady;