summaryrefslogtreecommitdiffstats
path: root/devtools/client/fronts/targets/window-global.js
blob: 8bf69383e02473aa4d23bba521875f3fe29c3697 (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
/* 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 {
  windowGlobalTargetSpec,
} = require("resource://devtools/shared/specs/targets/window-global.js");
const {
  FrontClassWithSpec,
  registerFront,
} = require("resource://devtools/shared/protocol.js");
const {
  TargetMixin,
} = require("resource://devtools/client/fronts/targets/target-mixin.js");

class WindowGlobalTargetFront extends TargetMixin(
  FrontClassWithSpec(windowGlobalTargetSpec)
) {
  constructor(client, targetFront, parentFront) {
    super(client, targetFront, parentFront);

    // For targets which support the Watcher and configuration actor, the status
    // for the `javascriptEnabled` setting will be available on the configuration
    // front, and the target will only be used to read the initial value from older
    // servers.
    // Note: this property is marked as private but is accessed by the
    // TargetCommand to provide the "isJavascriptEnabled" wrapper. It should NOT be
    // used anywhere else.
    this._javascriptEnabled = null;

    // If this target was retrieved via NodeFront connectToFrame, keep a
    // reference to the parent NodeFront.
    this._parentNodeFront = null;

    this._onTabNavigated = this._onTabNavigated.bind(this);
    this._onFrameUpdate = this._onFrameUpdate.bind(this);

    this.on("tabNavigated", this._onTabNavigated);
    this.on("frameUpdate", this._onFrameUpdate);
  }

  form(json) {
    this.actorID = json.actor;
    this.browsingContextID = json.browsingContextID;
    this.innerWindowId = json.innerWindowId;
    this.processID = json.processID;

    // Save the full form for Target class usage.
    // Do not use `form` name to avoid colliding with protocol.js's `form` method
    this.targetForm = json;

    this.outerWindowID = json.outerWindowID;
    this.favicon = json.favicon;

    // Initial value for the page title and url. Since the WindowGlobalTargetActor can
    // be created very early, those might not represent the actual value we'd want to
    // display for the user (e.g. the <title> might not have been parsed yet, and the
    // url could still be about:blank, which is what the platform uses at the very start
    // of a navigation to a new location).
    // Those values are set again  from the targetCommand when receiving DOCUMENT_EVENT
    // resource, at which point both values should be in their expected form.
    this.setTitle(json.title);
    this.setUrl(json.url);
  }

  /**
   * Event listener for `frameUpdate` event.
   */
  _onFrameUpdate(packet) {
    this.emit("frame-update", packet);
  }

  /**
   * Event listener for `tabNavigated` event.
   */
  _onTabNavigated(packet) {
    const event = Object.create(null);
    event.url = packet.url;
    event.title = packet.title;
    event.isFrameSwitching = packet.isFrameSwitching;

    // Keep the title unmodified when a developer toolbox switches frame
    // for a tab (Bug 1261687), but always update the title when the target
    // is a WebExtension (where the addon name is always included in the title
    // and the url is supposed to be updated every time the selected frame changes).
    if (!packet.isFrameSwitching || this.isWebExtension) {
      this.setTitle(packet.title);
      this.setUrl(packet.url);
    }

    // Send any stored event payload (DOMWindow or nsIRequest) for backwards
    // compatibility with non-remotable tools.
    if (packet.state == "start") {
      this.emit("will-navigate", event);
    } else {
      this.emit("navigate", event);
    }
  }

  getParentNodeFront() {
    return this._parentNodeFront;
  }

  setParentNodeFront(nodeFront) {
    this._parentNodeFront = nodeFront;
  }

  /**
   * Set the targetFront url.
   *
   * @param {string} url
   */
  setUrl(url) {
    this._url = url;
  }

  /**
   * Set the targetFront title.
   *
   * @param {string} title
   */
  setTitle(title) {
    this._title = title;
  }

  async detach() {
    // When calling this.destroy() at the end of this method,
    // we will end up calling detach again from TargetMixin.destroy.
    // Avoid invalid loops and do not try to resolve only once the previous call to detach
    // is done as it would do async infinite loop that never resolves.
    if (this._isDetaching) {
      return;
    }
    this._isDetaching = true;

    // Remove listeners set in constructor
    this.off("tabNavigated", this._onTabNavigated);
    this.off("frameUpdate", this._onFrameUpdate);

    try {
      await super.detach();
    } catch (e) {
      this.logDetachError(e, "browsing context");
    }

    // Detach will destroy the target actor, but the server won't emit any
    // target-destroyed-form in such manual, client side destruction.
    // So that we have to manually destroy the associated front on the client
    //
    // If detach was called by TargetFrontMixin.destroy, avoid recalling it from it
    // as it would do an async infinite loop which would never resolve.
    if (!this.isDestroyedOrBeingDestroyed()) {
      this.destroy();
    }
  }

  destroy() {
    const promise = super.destroy();
    this._parentNodeFront = null;

    // As detach isn't necessarily called on target's destroy
    // (it isn't for local tabs), ensure removing listeners set in constructor.
    this.off("tabNavigated", this._onTabNavigated);
    this.off("frameUpdate", this._onFrameUpdate);

    return promise;
  }
}

exports.WindowGlobalTargetFront = WindowGlobalTargetFront;
registerFront(exports.WindowGlobalTargetFront);