Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 "use strict";
9 let devtools_ = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
10 let { createExtraActors, appendExtraActors } = devtools_.require("devtools/server/actors/common");
12 /* Root actor for the remote debugging protocol. */
14 /**
15 * Create a remote debugging protocol root actor.
16 *
17 * @param aConnection
18 * The DebuggerServerConnection whose root actor we are constructing.
19 *
20 * @param aParameters
21 * The properties of |aParameters| provide backing objects for the root
22 * actor's requests; if a given property is omitted from |aParameters|, the
23 * root actor won't implement the corresponding requests or notifications.
24 * Supported properties:
25 *
26 * - tabList: a live list (see below) of tab actors. If present, the
27 * new root actor supports the 'listTabs' request, providing the live
28 * list's elements as its tab actors, and sending 'tabListChanged'
29 * notifications when the live list's contents change. One actor in
30 * this list must have a true '.selected' property.
31 *
32 * - addonList: a live list (see below) of addon actors. If present, the
33 * new root actor supports the 'listAddons' request, providing the live
34 * list's elements as its addon actors, and sending 'addonListchanged'
35 * notifications when the live list's contents change.
36 *
37 * - globalActorFactories: an object |A| describing further actors to
38 * attach to the 'listTabs' reply. This is the type accumulated by
39 * DebuggerServer.addGlobalActor. For each own property |P| of |A|,
40 * the root actor adds a property named |P| to the 'listTabs'
41 * reply whose value is the name of an actor constructed by
42 * |A[P]|.
43 *
44 * - onShutdown: a function to call when the root actor is disconnected.
45 *
46 * Instance properties:
47 *
48 * - applicationType: the string the root actor will include as the
49 * "applicationType" property in the greeting packet. By default, this
50 * is "browser".
51 *
52 * Live lists:
53 *
54 * A "live list", as used for the |tabList|, is an object that presents a
55 * list of actors, and also notifies its clients of changes to the list. A
56 * live list's interface is two properties:
57 *
58 * - getList: a method that returns a promise to the contents of the list.
59 *
60 * - onListChanged: a handler called, with no arguments, when the set of
61 * values the iterator would produce has changed since the last
62 * time 'iterator' was called. This may only be set to null or a
63 * callable value (one for which the typeof operator returns
64 * 'function'). (Note that the live list will not call the
65 * onListChanged handler until the list has been iterated over
66 * once; if nobody's seen the list in the first place, nobody
67 * should care if its contents have changed!)
68 *
69 * When the list changes, the list implementation should ensure that any
70 * actors yielded in previous iterations whose referents (tabs) still exist
71 * get yielded again in subsequent iterations. If the underlying referent
72 * is the same, the same actor should be presented for it.
73 *
74 * The root actor registers an 'onListChanged' handler on the appropriate
75 * list when it may need to send the client 'tabListChanged' notifications,
76 * and is careful to remove the handler whenever it does not need to send
77 * such notifications (including when it is disconnected). This means that
78 * live list implementations can use the state of the handler property (set
79 * or null) to install and remove observers and event listeners.
80 *
81 * Note that, as the only way for the root actor to see the members of the
82 * live list is to begin an iteration over the list, the live list need not
83 * actually produce any actors until they are reached in the course of
84 * iteration: alliterative lazy live lists.
85 */
86 function RootActor(aConnection, aParameters) {
87 this.conn = aConnection;
88 this._parameters = aParameters;
89 this._onTabListChanged = this.onTabListChanged.bind(this);
90 this._onAddonListChanged = this.onAddonListChanged.bind(this);
91 this._extraActors = {};
92 }
94 RootActor.prototype = {
95 constructor: RootActor,
96 applicationType: "browser",
98 /**
99 * Return a 'hello' packet as specified by the Remote Debugging Protocol.
100 */
101 sayHello: function() {
102 return {
103 from: this.actorID,
104 applicationType: this.applicationType,
105 /* This is not in the spec, but it's used by tests. */
106 testConnectionPrefix: this.conn.prefix,
107 traits: {
108 sources: true,
109 editOuterHTML: true,
110 // Wether the server-side highlighter actor exists and can be used to
111 // remotely highlight nodes (see server/actors/highlighter.js)
112 highlightable: true,
113 // Wether the inspector actor implements the getImageDataFromURL
114 // method that returns data-uris for image URLs. This is used for image
115 // tooltips for instance
116 urlToImageDataResolver: true,
117 networkMonitor: true,
118 // Wether the storage inspector actor to inspect cookies, etc.
119 storageInspector: true,
120 // Wether storage inspector is read only
121 storageInspectorReadOnly: true,
122 // Wether conditional breakpoints are supported
123 conditionalBreakpoints: true
124 }
125 };
126 },
128 /**
129 * This is true for the root actor only, used by some child actors
130 */
131 get isRootActor() true,
133 /**
134 * The (chrome) window, for use by child actors
135 */
136 get window() Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType),
138 /**
139 * URL of the chrome window.
140 */
141 get url() { return this.window ? this.window.document.location.href : null; },
143 /**
144 * Getter for the best nsIWebProgress for to watching this window.
145 */
146 get webProgress() {
147 return this.window
148 .QueryInterface(Ci.nsIInterfaceRequestor)
149 .getInterface(Ci.nsIDocShell)
150 .QueryInterface(Ci.nsIInterfaceRequestor)
151 .getInterface(Ci.nsIWebProgress);
152 },
154 /**
155 * Disconnects the actor from the browser window.
156 */
157 disconnect: function() {
158 /* Tell the live lists we aren't watching any more. */
159 if (this._parameters.tabList) {
160 this._parameters.tabList.onListChanged = null;
161 }
162 if (this._parameters.addonList) {
163 this._parameters.addonList.onListChanged = null;
164 }
165 if (typeof this._parameters.onShutdown === 'function') {
166 this._parameters.onShutdown();
167 }
168 this._extraActors = null;
169 },
171 /* The 'listTabs' request and the 'tabListChanged' notification. */
173 /**
174 * Handles the listTabs request. The actors will survive until at least
175 * the next listTabs request.
176 */
177 onListTabs: function() {
178 let tabList = this._parameters.tabList;
179 if (!tabList) {
180 return { from: this.actorID, error: "noTabs",
181 message: "This root actor has no browser tabs." };
182 }
184 /*
185 * Walk the tab list, accumulating the array of tab actors for the
186 * reply, and moving all the actors to a new ActorPool. We'll
187 * replace the old tab actor pool with the one we build here, thus
188 * retiring any actors that didn't get listed again, and preparing any
189 * new actors to receive packets.
190 */
191 let newActorPool = new ActorPool(this.conn);
192 let tabActorList = [];
193 let selected;
194 return tabList.getList().then((tabActors) => {
195 for (let tabActor of tabActors) {
196 if (tabActor.selected) {
197 selected = tabActorList.length;
198 }
199 tabActor.parentID = this.actorID;
200 newActorPool.addActor(tabActor);
201 tabActorList.push(tabActor);
202 }
204 /* DebuggerServer.addGlobalActor support: create actors. */
205 if (!this._globalActorPool) {
206 this._globalActorPool = new ActorPool(this.conn);
207 this._createExtraActors(this._parameters.globalActorFactories, this._globalActorPool);
208 this.conn.addActorPool(this._globalActorPool);
209 }
211 /*
212 * Drop the old actorID -> actor map. Actors that still mattered were
213 * added to the new map; others will go away.
214 */
215 if (this._tabActorPool) {
216 this.conn.removeActorPool(this._tabActorPool);
217 }
218 this._tabActorPool = newActorPool;
219 this.conn.addActorPool(this._tabActorPool);
221 let reply = {
222 "from": this.actorID,
223 "selected": selected || 0,
224 "tabs": [actor.form() for (actor of tabActorList)],
225 };
227 /* If a root window is accessible, include its URL. */
228 if (this.url) {
229 reply.url = this.url;
230 }
232 /* DebuggerServer.addGlobalActor support: name actors in 'listTabs' reply. */
233 this._appendExtraActors(reply);
235 /*
236 * Now that we're actually going to report the contents of tabList to
237 * the client, we're responsible for letting the client know if it
238 * changes.
239 */
240 tabList.onListChanged = this._onTabListChanged;
242 return reply;
243 });
244 },
246 onTabListChanged: function () {
247 this.conn.send({ from: this.actorID, type:"tabListChanged" });
248 /* It's a one-shot notification; no need to watch any more. */
249 this._parameters.tabList.onListChanged = null;
250 },
252 onListAddons: function () {
253 let addonList = this._parameters.addonList;
254 if (!addonList) {
255 return { from: this.actorID, error: "noAddons",
256 message: "This root actor has no browser addons." };
257 }
259 return addonList.getList().then((addonActors) => {
260 let addonActorPool = new ActorPool(this.conn);
261 for (let addonActor of addonActors) {
262 addonActorPool.addActor(addonActor);
263 }
265 if (this._addonActorPool) {
266 this.conn.removeActorPool(this._addonActorPool);
267 }
268 this._addonActorPool = addonActorPool;
269 this.conn.addActorPool(this._addonActorPool);
271 addonList.onListChanged = this._onAddonListChanged;
273 return {
274 "from": this.actorID,
275 "addons": [addonActor.form() for (addonActor of addonActors)]
276 };
277 });
278 },
280 onAddonListChanged: function () {
281 this.conn.send({ from: this.actorID, type: "addonListChanged" });
282 this._parameters.addonList.onListChanged = null;
283 },
285 /* This is not in the spec, but it's used by tests. */
286 onEcho: function (aRequest) {
287 /*
288 * Request packets are frozen. Copy aRequest, so that
289 * DebuggerServerConnection.onPacket can attach a 'from' property.
290 */
291 return JSON.parse(JSON.stringify(aRequest));
292 },
294 onProtocolDescription: function (aRequest) {
295 return protocol.dumpProtocolSpec()
296 },
298 /* Support for DebuggerServer.addGlobalActor. */
299 _createExtraActors: createExtraActors,
300 _appendExtraActors: appendExtraActors,
302 /* ThreadActor hooks. */
304 /**
305 * Prepare to enter a nested event loop by disabling debuggee events.
306 */
307 preNest: function() {
308 // Disable events in all open windows.
309 let e = Services.wm.getEnumerator(null);
310 while (e.hasMoreElements()) {
311 let win = e.getNext();
312 let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
313 .getInterface(Ci.nsIDOMWindowUtils);
314 windowUtils.suppressEventHandling(true);
315 windowUtils.suspendTimeouts();
316 }
317 },
319 /**
320 * Prepare to exit a nested event loop by enabling debuggee events.
321 */
322 postNest: function(aNestData) {
323 // Enable events in all open windows.
324 let e = Services.wm.getEnumerator(null);
325 while (e.hasMoreElements()) {
326 let win = e.getNext();
327 let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
328 .getInterface(Ci.nsIDOMWindowUtils);
329 windowUtils.resumeTimeouts();
330 windowUtils.suppressEventHandling(false);
331 }
332 }
333 };
335 RootActor.prototype.requestTypes = {
336 "listTabs": RootActor.prototype.onListTabs,
337 "listAddons": RootActor.prototype.onListAddons,
338 "echo": RootActor.prototype.onEcho,
339 "protocolDescription": RootActor.prototype.onProtocolDescription
340 };