172 lines
5.4 KiB
JavaScript
172 lines
5.4 KiB
JavaScript
/* 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;
|
|
this.isFallbackExtensionDocument = json.isFallbackExtensionDocument;
|
|
this.addonId = json.addonId;
|
|
|
|
// 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).
|
|
if (!packet.isFrameSwitching) {
|
|
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);
|