|
| 1 | +/* Any copyright is dedicated to the Public Domain. |
| 2 | + * http://creativecommons.org/publicdomain/zero/1.0/ */ |
| 3 | + |
| 4 | +/** |
| 5 | + * Document the basics of RDP packets via a test. |
| 6 | + */ |
| 7 | + |
| 8 | +"use strict"; |
| 9 | + |
| 10 | +const TEST_URL = "data:text/html,new-tab"; |
| 11 | + |
| 12 | +add_task(async () => { |
| 13 | + // Allow logging all RDP packets |
| 14 | + await pushPref("devtools.debugger.log", true); |
| 15 | + // Really all of them |
| 16 | + await pushPref("devtools.debugger.log.verbose", true); |
| 17 | + |
| 18 | + // Instantiate a DevTools server |
| 19 | + DevToolsServer.init(); |
| 20 | + DevToolsServer.registerAllActors(); |
| 21 | + |
| 22 | + // Instantiate a client connected to this server |
| 23 | + const transport = DevToolsServer.connectPipe(); |
| 24 | + const client = new DevToolsClient(transport); |
| 25 | + |
| 26 | + // This will trigger some handshake with the server |
| 27 | + await client.connect(); |
| 28 | + |
| 29 | + // Ignore this gross hack, this is to be able to emit raw RDP packet via client.request |
| 30 | + // (a Front is instantiated by DevToolsClient which would be confused with us sending |
| 31 | + // RDP packets for the Root actor) |
| 32 | + client.mainRoot.destroy(); |
| 33 | + |
| 34 | + // You need to call listTabs once to retrieve the existing list of Tab Descriptor actors... |
| 35 | + const { tabs } = await client.request({ to: "root", type: "listTabs" }); |
| 36 | + |
| 37 | + // ... which will let you receive the 'tabListChanged' event. |
| 38 | + // This is an empty RDP packet, you need to re-call listTabs to get the full new updated list of actors. |
| 39 | + const onTabListUpdated = client.once("tabListChanged"); |
| 40 | + |
| 41 | + // Open a new tab. |
| 42 | + await BrowserTestUtils.openNewForegroundTab({ |
| 43 | + gBrowser, |
| 44 | + url: TEST_URL, |
| 45 | + }); |
| 46 | + |
| 47 | + await onTabListUpdated; |
| 48 | + |
| 49 | + // The new list of Tab descriptors should contain the newly opened tab |
| 50 | + const { tabs: newTabs } = await client.request({ |
| 51 | + to: "root", |
| 52 | + type: "listTabs", |
| 53 | + }); |
| 54 | + is(newTabs.length, tabs.length + 1); |
| 55 | + |
| 56 | + const tabDescriptorActor = newTabs.pop(); |
| 57 | + is(tabDescriptorActor.url, TEST_URL); |
| 58 | + |
| 59 | + // Query the Tab Descriptor actor to retrieve its related Watcher actor. |
| 60 | + // Each Descriptor actor has a dedicated watcher which will be scoped to the context of the descriptor. |
| 61 | + // Here the watcher will focus on the related tab. |
| 62 | + // |
| 63 | + // You want to pass isServerTargetSwitchingEnabled set to true in order to be notified about the top level document, |
| 64 | + // as well as navigations to subsequent documents. |
| 65 | + const watcherActor = await client.request({ |
| 66 | + to: tabDescriptorActor.actor, |
| 67 | + type: "getWatcher", |
| 68 | + isServerTargetSwitchingEnabled: true, |
| 69 | + }); |
| 70 | + |
| 71 | + // The call to Watcher Actor's watchTargets will emit target-available-form RDP events. |
| 72 | + // One per available target. It will emit one for each immediatly available target, |
| 73 | + // but also for any available later. That, until you call unwatchTarget method. |
| 74 | + const onTopTargetAvailable = client.once("target-available-form"); |
| 75 | + |
| 76 | + // watchTargets accepts "frame", "process" and "worker" |
| 77 | + // When debugging a web page you want to listen to frame and worker targets. |
| 78 | + // "frame" would better be named "WindowGlobal" as it will notify you about all the WindowGlobal of the page. |
| 79 | + // Each top level documents and any iframe documents will have a related WindowGlobal, |
| 80 | + // if any of these documents navigate, a new WindowGlobal will be instantiated. |
| 81 | + // If you care about workers, listen to worker targets as well. |
| 82 | + await client.request({ |
| 83 | + to: watcherActor.actor, |
| 84 | + type: "watchTargets", |
| 85 | + targetType: "frame", |
| 86 | + }); |
| 87 | + |
| 88 | + // This is a trivial example so we have a unique WindowGlobal target for the top level document |
| 89 | + const { target: topTarget } = await onTopTargetAvailable; |
| 90 | + is(topTarget.url, TEST_URL); |
| 91 | + |
| 92 | + // Similarly to watchTarget, the next call to watchResources will emit new resources right away as well as later. |
| 93 | + const onConsoleMessages = client.once("resource-available-form"); |
| 94 | + |
| 95 | + // If you want to observe anything, you have to use Watcher Actor's watchrResources API. |
| 96 | + // The list of all available resources is here: |
| 97 | + // https://searchfox.org/mozilla-central/source/devtools/server/actors/resources/index.js#9 |
| 98 | + // And you might have a look at each ResourceWatcher subclass to learn more about the fields exposed by each resource type: |
| 99 | + // https://searchfox.org/mozilla-central/source/devtools/server/actors/resources |
| 100 | + await client.request({ |
| 101 | + to: watcherActor.actor, |
| 102 | + type: "watchResources", |
| 103 | + resourceTypes: ["console-message"], |
| 104 | + }); |
| 105 | + |
| 106 | + // You may use many useful actors on each target actor, like console, thread, ... |
| 107 | + // You can get the full list of available actors in: |
| 108 | + // https://searchfox.org/mozilla-central/source/devtools/server/actors/utils/actor-registry.js#176 |
| 109 | + // And then look into the mentioned path for implementation. |
| 110 | + // |
| 111 | + // The "target form" contains the list of all these actor IDs |
| 112 | + const webConsoleActorID = topTarget.consoleActor; |
| 113 | + |
| 114 | + // Call the Console API in order to force emitting a console-message resource |
| 115 | + await client.request({ |
| 116 | + to: webConsoleActorID, |
| 117 | + type: "evaluateJSAsync", |
| 118 | + text: "console.log('42')", |
| 119 | + }); |
| 120 | + |
| 121 | + // Wait for the related console-message resource |
| 122 | + const { resources } = await onConsoleMessages; |
| 123 | + |
| 124 | + // Note that resource-available-form comes with a "resources" attribute which is an array of resources |
| 125 | + // which may contain various resource types. |
| 126 | + is(resources[0].message.arguments[0], "42"); |
| 127 | + |
| 128 | + await client.close(); |
| 129 | +}); |
0 commit comments