summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/resource/legacy-listeners/thread-states.js
blob: b24bdfa8ccff099e32156ebe69d928619f14e9f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");

module.exports = async function ({ targetCommand, targetFront, onAvailable }) {
  const isBrowserToolbox =
    targetCommand.descriptorFront.isBrowserProcessDescriptor;
  const isNonTopLevelFrameTarget =
    !targetFront.isTopLevel &&
    targetFront.targetType === targetCommand.TYPES.FRAME;

  if (isBrowserToolbox && isNonTopLevelFrameTarget) {
    // In the BrowserToolbox, non-top-level frame targets are already
    // debugged via content-process targets.
    return;
  }

  // Wait for the thread actor to be attached, otherwise getFront(thread) will throw for worker targets
  // This is because worker target are still kind of descriptors and are only resolved into real target
  // after being attached. And the thread actor ID is only retrieved and available after being attached.
  await targetFront.onThreadAttached;

  if (targetFront.isDestroyed()) {
    return;
  }
  const threadFront = await targetFront.getFront("thread");

  let isInterrupted = false;
  const onPausedPacket = packet => {
    // If paused by an explicit interrupt, which are generated by the
    // slow script dialog and internal events such as setting
    // breakpoints, ignore the event.
    const { why } = packet;
    if (why.type === "interrupted" && !why.onNext) {
      isInterrupted = true;
      return;
    }

    // Ignore attached events because they are not useful to the user.
    if (why.type == "alreadyPaused" || why.type == "attached") {
      return;
    }

    onAvailable([
      {
        resourceType: ResourceCommand.TYPES.THREAD_STATE,
        state: "paused",
        why,
        frame: packet.frame,
      },
    ]);
  };
  threadFront.on("paused", onPausedPacket);

  threadFront.on("resumed", () => {
    // NOTE: the client suppresses resumed events while interrupted
    // to prevent unintentional behavior.
    // see [client docs](devtools/client/debugger/src/client/README.md#interrupted) for more information.
    if (isInterrupted) {
      isInterrupted = false;
      return;
    }

    onAvailable([
      {
        resourceType: ResourceCommand.TYPES.THREAD_STATE,
        state: "resumed",
      },
    ]);
  });

  // Notify about already paused thread
  const pausedPacket = threadFront.getLastPausePacket();
  if (pausedPacket) {
    onPausedPacket(pausedPacket);
  }
};