summaryrefslogtreecommitdiffstats
path: root/devtools/client/fronts/watcher.js
blob: f78e0639929a4f4f1d657a4f386dd4d5b75bd7de (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* 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 { watcherSpec } = require("resource://devtools/shared/specs/watcher.js");
const {
  FrontClassWithSpec,
  registerFront,
} = require("resource://devtools/shared/protocol.js");

loader.lazyRequireGetter(
  this,
  "WindowGlobalTargetFront",
  "resource://devtools/client/fronts/targets/window-global.js",
  true
);
loader.lazyRequireGetter(
  this,
  "ContentProcessTargetFront",
  "resource://devtools/client/fronts/targets/content-process.js",
  true
);
loader.lazyRequireGetter(
  this,
  "WorkerTargetFront",
  "resource://devtools/client/fronts/targets/worker.js",
  true
);

class WatcherFront extends FrontClassWithSpec(watcherSpec) {
  constructor(client, targetFront, parentFront) {
    super(client, targetFront, parentFront);

    this._onTargetAvailable = this._onTargetAvailable.bind(this);
    this._onTargetDestroyed = this._onTargetDestroyed.bind(this);

    // Convert form, which is just JSON object to Fronts for these two events
    this.on("target-available-form", this._onTargetAvailable);
    this.on("target-destroyed-form", this._onTargetDestroyed);
  }

  form(json) {
    this.actorID = json.actor;
    this.traits = json.traits;
  }

  _onTargetAvailable(form) {
    let front;
    if (form.actor.includes("/contentProcessTarget")) {
      front = new ContentProcessTargetFront(this.conn, null, this);
    } else if (form.actor.includes("/workerTarget")) {
      front = new WorkerTargetFront(this.conn, null, this);
    } else {
      front = new WindowGlobalTargetFront(this.conn, null, this);
    }
    front.actorID = form.actor;
    front.form(form);
    this.manage(front);
    this.emit("target-available", front);
  }

  _onTargetDestroyed(form, options = {}) {
    const front = this._getTargetFront(form);

    // When server side target switching is off,
    // the watcher may notify us about the top level target destruction a bit late.
    // The descriptor (`this.parentFront`) already switched to the new target.
    // Missing `target-destroyed` isn't critical when target switching is off
    // as `TargetCommand.switchToTarget` will end calling `TargetCommand.onTargetDestroyed` for all
    // existing targets.
    // https://searchfox.org/mozilla-central/rev/af8e5d37fd56be90ccddae2203e7b875d3f3ae87/devtools/shared/commands/target/target-command.js#166-173
    if (front) {
      this.emit("target-destroyed", front, options);
    }
  }

  _getTargetFront(form) {
    let front = this.getActorByID(form.actor);
    // For top level target, the target will be a child of the descriptor front,
    // which happens to be the parent front of the watcher.
    if (!front) {
      front = this.parentFront.getActorByID(form.actor);
    }
    return front;
  }

  /**
   * Retrieve the already existing WindowGlobalTargetFront for the parent
   * BrowsingContext of the given BrowsingContext ID.
   */
  async getParentWindowGlobalTarget(browsingContextID) {
    const id = await this.getParentBrowsingContextID(browsingContextID);
    if (!id) {
      return null;
    }
    return this.getWindowGlobalTarget(id);
  }

  /**
   * Memoized getter for the "blackboxing" actor
   */
  async getBlackboxingActor() {
    if (!this._blackboxingActor) {
      this._blackboxingActor = await super.getBlackboxingActor();
    }
    return this._blackboxingActor;
  }
  /**
   * Memoized getter for the "breakpoint-list" actor
   */
  async getBreakpointListActor() {
    if (!this._breakpointListActor) {
      this._breakpointListActor = await super.getBreakpointListActor();
    }
    return this._breakpointListActor;
  }

  /**
   * Memoized getter for the "target-configuration" actor
   */
  async getTargetConfigurationActor() {
    if (!this._targetConfigurationActor) {
      this._targetConfigurationActor =
        await super.getTargetConfigurationActor();
    }
    return this._targetConfigurationActor;
  }

  /**
   * Memoized getter for the "thread-configuration" actor
   */
  async getThreadConfigurationActor() {
    if (!this._threadConfigurationActor) {
      this._threadConfigurationActor =
        await super.getThreadConfigurationActor();
    }
    return this._threadConfigurationActor;
  }

  /**
   * For a given BrowsingContext ID, return the already existing WindowGlobalTargetFront
   */
  async getWindowGlobalTarget(id) {
    // First scan the watcher children as the watcher manages all the targets
    for (const front of this.poolChildren()) {
      if (front.browsingContextID == id) {
        return front;
      }
    }
    // But the top level target will be created by the Descriptor.getTarget() method
    // and so be hosted in the Descriptor's pool.
    // The parent front of the WatcherActor happens to be the Descriptor Actor.
    // This code could go away or be simplified if the Descriptor starts fetch all
    // the targets, including the top level one via the Watcher. i.e. drop Descriptor.getTarget().
    const topLevelTarget = await this.parentFront.getTarget();
    if (topLevelTarget?.browsingContextID == id) {
      return topLevelTarget;
    }

    // If we could not find a window global target for the provided id, the
    // window global might not be the topmost one of a given process (isProcessRoot == true).
    // For now we only create targets for the top window global of each process,
    // so we recursively check the parent browsing context ids
    // until we find a valid target.
    const parentBrowsingContextID = await this.getParentBrowsingContextID(id);
    if (parentBrowsingContextID && parentBrowsingContextID !== id) {
      return this.getWindowGlobalTarget(parentBrowsingContextID);
    }

    return null;
  }

  getWindowGlobalTargetByInnerWindowId(innerWindowId) {
    for (const front of this.poolChildren()) {
      if (front.innerWindowId == innerWindowId) {
        return front;
      }
    }
    // Use getCachedTarget in order to have a fully synchronous method
    // as the callsite in ResourceCommand benefit from being synchronous.
    // Here we care only about already existing resource and do not need to
    // wait for the next target to come.
    const topLevelTarget = this.parentFront.getCachedTarget();
    if (topLevelTarget?.innerWindowId == innerWindowId) {
      return topLevelTarget;
    }
    console.error("Unable to find target with innerWindowId:" + innerWindowId);
    return null;
  }

  /**
   * Memoized getter for the "networkParent" actor
   */
  async getNetworkParentActor() {
    if (!this._networkParentActor) {
      this._networkParentActor = await super.getNetworkParentActor();
    }
    return this._networkParentActor;
  }
}
registerFront(WatcherFront);