window-global.js (5534B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 "use strict"; 5 6 const { 7 windowGlobalTargetSpec, 8 } = require("resource://devtools/shared/specs/targets/window-global.js"); 9 const { 10 FrontClassWithSpec, 11 registerFront, 12 } = require("resource://devtools/shared/protocol.js"); 13 const { 14 TargetMixin, 15 } = require("resource://devtools/client/fronts/targets/target-mixin.js"); 16 17 class WindowGlobalTargetFront extends TargetMixin( 18 FrontClassWithSpec(windowGlobalTargetSpec) 19 ) { 20 constructor(client, targetFront, parentFront) { 21 super(client, targetFront, parentFront); 22 23 // For targets which support the Watcher and configuration actor, the status 24 // for the `javascriptEnabled` setting will be available on the configuration 25 // front, and the target will only be used to read the initial value from older 26 // servers. 27 // Note: this property is marked as private but is accessed by the 28 // TargetCommand to provide the "isJavascriptEnabled" wrapper. It should NOT be 29 // used anywhere else. 30 this._javascriptEnabled = null; 31 32 // If this target was retrieved via NodeFront connectToFrame, keep a 33 // reference to the parent NodeFront. 34 this._parentNodeFront = null; 35 36 this._onTabNavigated = this._onTabNavigated.bind(this); 37 this._onFrameUpdate = this._onFrameUpdate.bind(this); 38 39 this.on("tabNavigated", this._onTabNavigated); 40 this.on("frameUpdate", this._onFrameUpdate); 41 } 42 43 form(json) { 44 this.actorID = json.actor; 45 this.browsingContextID = json.browsingContextID; 46 this.innerWindowId = json.innerWindowId; 47 this.processID = json.processID; 48 this.isFallbackExtensionDocument = json.isFallbackExtensionDocument; 49 this.addonId = json.addonId; 50 51 // Save the full form for Target class usage. 52 // Do not use `form` name to avoid colliding with protocol.js's `form` method 53 this.targetForm = json; 54 55 this.outerWindowID = json.outerWindowID; 56 this.favicon = json.favicon; 57 58 // Initial value for the page title and url. Since the WindowGlobalTargetActor can 59 // be created very early, those might not represent the actual value we'd want to 60 // display for the user (e.g. the <title> might not have been parsed yet, and the 61 // url could still be about:blank, which is what the platform uses at the very start 62 // of a navigation to a new location). 63 // Those values are set again from the targetCommand when receiving DOCUMENT_EVENT 64 // resource, at which point both values should be in their expected form. 65 this.setTitle(json.title); 66 this.setUrl(json.url); 67 } 68 69 /** 70 * Event listener for `frameUpdate` event. 71 */ 72 _onFrameUpdate(packet) { 73 this.emit("frame-update", packet); 74 } 75 76 /** 77 * Event listener for `tabNavigated` event. 78 */ 79 _onTabNavigated(packet) { 80 const event = Object.create(null); 81 event.url = packet.url; 82 event.title = packet.title; 83 event.isFrameSwitching = packet.isFrameSwitching; 84 85 // Keep the title unmodified when a developer toolbox switches frame 86 // for a tab (Bug 1261687). 87 if (!packet.isFrameSwitching) { 88 this.setTitle(packet.title); 89 this.setUrl(packet.url); 90 } 91 92 // Send any stored event payload (DOMWindow or nsIRequest) for backwards 93 // compatibility with non-remotable tools. 94 if (packet.state == "start") { 95 this.emit("will-navigate", event); 96 } else { 97 this.emit("navigate", event); 98 } 99 } 100 101 getParentNodeFront() { 102 return this._parentNodeFront; 103 } 104 105 setParentNodeFront(nodeFront) { 106 this._parentNodeFront = nodeFront; 107 } 108 109 /** 110 * Set the targetFront url. 111 * 112 * @param {string} url 113 */ 114 setUrl(url) { 115 this._url = url; 116 } 117 118 /** 119 * Set the targetFront title. 120 * 121 * @param {string} title 122 */ 123 setTitle(title) { 124 this._title = title; 125 } 126 127 async detach() { 128 // When calling this.destroy() at the end of this method, 129 // we will end up calling detach again from TargetMixin.destroy. 130 // Avoid invalid loops and do not try to resolve only once the previous call to detach 131 // is done as it would do async infinite loop that never resolves. 132 if (this._isDetaching) { 133 return; 134 } 135 this._isDetaching = true; 136 137 // Remove listeners set in constructor 138 this.off("tabNavigated", this._onTabNavigated); 139 this.off("frameUpdate", this._onFrameUpdate); 140 141 try { 142 await super.detach(); 143 } catch (e) { 144 this.logDetachError(e, "browsing context"); 145 } 146 147 // Detach will destroy the target actor, but the server won't emit any 148 // target-destroyed-form in such manual, client side destruction. 149 // So that we have to manually destroy the associated front on the client 150 // 151 // If detach was called by TargetFrontMixin.destroy, avoid recalling it from it 152 // as it would do an async infinite loop which would never resolve. 153 if (!this.isDestroyedOrBeingDestroyed()) { 154 this.destroy(); 155 } 156 } 157 158 destroy() { 159 const promise = super.destroy(); 160 this._parentNodeFront = null; 161 162 // As detach isn't necessarily called on target's destroy 163 // (it isn't for local tabs), ensure removing listeners set in constructor. 164 this.off("tabNavigated", this._onTabNavigated); 165 this.off("frameUpdate", this._onFrameUpdate); 166 167 return promise; 168 } 169 } 170 171 exports.WindowGlobalTargetFront = WindowGlobalTargetFront; 172 registerFront(exports.WindowGlobalTargetFront);