summaryrefslogtreecommitdiffstats
path: root/devtools/server/tests/xpcshell/test_addon_debugging_connect.js
blob: 221e73d256b45bc85079a1c9e107ad5f00bc9842 (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { ExtensionTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
);

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
});

const { createAppInfo, promiseStartupManager } = AddonTestUtils;

AddonTestUtils.init(this);
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");

ExtensionTestUtils.init(this);

function watchFrameUpdates(front) {
  const collected = [];

  const listener = data => {
    collected.push(data);
  };

  front.on("frameUpdate", listener);
  let unsubscribe = () => {
    unsubscribe = null;
    front.off("frameUpdate", listener);
    return collected;
  };

  return unsubscribe;
}

function promiseFrameUpdate(front, matcher = () => true) {
  return new Promise(resolve => {
    const listener = data => {
      if (matcher(data)) {
        resolve();
        front.off("frameUpdate", listener);
      }
    };

    front.on("frameUpdate", listener);
  });
}

// Bug 1302702 - Test connect to a webextension addon
add_task(
  {
    // This test needs to run only when the extension are running in a separate
    // child process, otherwise attachThread would pause the main process and this
    // test would get stuck.
    skip_if: () => !WebExtensionPolicy.useRemoteWebExtensions,
  },
  async function test_webextension_addon_debugging_connect() {
    await promiseStartupManager();

    // Install and start a test webextension.
    const extension = ExtensionTestUtils.loadExtension({
      useAddonManager: "temporary",
      background() {
        const { browser } = this;
        browser.test.log("background script executed");
        // window is available in background scripts
        // eslint-disable-next-line no-undef
        browser.test.sendMessage("background page ready", window.location.href);
      },
    });
    await extension.startup();
    const bgPageURL = await extension.awaitMessage("background page ready");

    const commands = await CommandsFactory.forAddon(extension.id);

    // Connect to the target addon actor and wait for the updated list of frames.
    const addonTarget = await commands.descriptorFront.getTarget();
    ok(addonTarget, "Got an RDP target");

    const { frames } = await addonTarget.listFrames();
    const backgroundPageFrame = frames
      .filter(frame => {
        return (
          frame.url && frame.url.endsWith("/_generated_background_page.html")
        );
      })
      .pop();
    ok(backgroundPageFrame, "Found the frame for the background page");

    const threadFront = await addonTarget.attachThread();

    ok(threadFront, "Got a threadFront for the target addon");
    equal(threadFront.paused, false, "The addon threadActor isn't paused");

    equal(
      lazy.ExtensionParent.DebugUtils.debugBrowserPromises.size,
      1,
      "The expected number of debug browser has been created by the addon actor"
    );

    const unwatchFrameUpdates = watchFrameUpdates(addonTarget);

    const promiseBgPageFrameUpdate = promiseFrameUpdate(addonTarget, data => {
      return data.frames?.some(frame => frame.url === bgPageURL);
    });

    // Reload the addon through the RDP protocol.
    await addonTarget.reload();
    info("Wait background page to be fully reloaded");
    await extension.awaitMessage("background page ready");
    info("Wait background page frameUpdate event");
    await promiseBgPageFrameUpdate;

    equal(
      lazy.ExtensionParent.DebugUtils.debugBrowserPromises.size,
      1,
      "The number of debug browser has not been changed after an addon reload"
    );

    const frameUpdates = unwatchFrameUpdates();
    const [frameUpdate] = frameUpdates;

    equal(
      frameUpdates.length,
      1,
      "Expect 1 frameUpdate events to have been received"
    );
    equal(
      frameUpdate.frames?.length,
      1,
      "Expect 1 frame in the frameUpdate event "
    );
    Assert.deepEqual(
      {
        url: frameUpdate.frames[0].url,
      },
      {
        url: bgPageURL,
      },
      "Got the expected frame update when the addon background page was loaded back"
    );

    await commands.destroy();

    // Check that if we close the debugging client without uninstalling the addon,
    // the webextension debugging actor should release the debug browser.
    equal(
      lazy.ExtensionParent.DebugUtils.debugBrowserPromises.size,
      0,
      "The debug browser has been released when the RDP connection has been closed"
    );

    await extension.unload();
  }
);