addon-sdk/source/lib/sdk/window/utils.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:44785c5e0f2a
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 module.metadata = {
7 'stability': 'unstable'
8 };
9
10 const { Cc, Ci } = require('chrome');
11 const array = require('../util/array');
12 const { defer } = require('sdk/core/promise');
13
14 const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
15 getService(Ci.nsIWindowWatcher);
16 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
17 getService(Ci.nsIAppShellService);
18 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
19 getService(Ci.nsIWindowMediator);
20 const io = Cc['@mozilla.org/network/io-service;1'].
21 getService(Ci.nsIIOService);
22 const FM = Cc["@mozilla.org/focus-manager;1"].
23 getService(Ci.nsIFocusManager);
24
25 const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
26
27 const BROWSER = 'navigator:browser',
28 URI_BROWSER = 'chrome://browser/content/browser.xul',
29 NAME = '_blank',
30 FEATURES = 'chrome,all,dialog=no,non-private';
31
32 function isWindowPrivate(win) {
33 if (!win)
34 return false;
35
36 // if the pbService is undefined, the PrivateBrowsingUtils.jsm is available,
37 // and the app is Firefox, then assume per-window private browsing is
38 // enabled.
39 try {
40 return win.QueryInterface(Ci.nsIInterfaceRequestor)
41 .getInterface(Ci.nsIWebNavigation)
42 .QueryInterface(Ci.nsILoadContext)
43 .usePrivateBrowsing;
44 }
45 catch(e) {}
46
47 // Sometimes the input is not a nsIDOMWindow.. but it is still a winodw.
48 try {
49 return !!win.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing;
50 }
51 catch (e) {}
52
53 return false;
54 }
55 exports.isWindowPrivate = isWindowPrivate;
56
57 function getMostRecentBrowserWindow() {
58 return getMostRecentWindow(BROWSER);
59 }
60 exports.getMostRecentBrowserWindow = getMostRecentBrowserWindow;
61
62 function getHiddenWindow() {
63 return appShellService.hiddenDOMWindow;
64 }
65 exports.getHiddenWindow = getHiddenWindow;
66
67 function getMostRecentWindow(type) {
68 return WM.getMostRecentWindow(type);
69 }
70 exports.getMostRecentWindow = getMostRecentWindow;
71
72 /**
73 * Returns the ID of the window's current inner window.
74 */
75 function getInnerId(window) {
76 return window.QueryInterface(Ci.nsIInterfaceRequestor).
77 getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
78 };
79 exports.getInnerId = getInnerId;
80
81 /**
82 * Returns the ID of the window's outer window.
83 */
84 function getOuterId(window) {
85 return window.QueryInterface(Ci.nsIInterfaceRequestor).
86 getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
87 };
88 exports.getOuterId = getOuterId;
89
90 /**
91 * Returns window by the outer window id.
92 */
93 const getByOuterId = WM.getOuterWindowWithId;
94 exports.getByOuterId = getByOuterId;
95
96 const getByInnerId = WM.getCurrentInnerWindowWithId;
97 exports.getByInnerId = getByInnerId;
98
99 /**
100 * Returns `nsIXULWindow` for the given `nsIDOMWindow`.
101 */
102 function getXULWindow(window) {
103 return window.QueryInterface(Ci.nsIInterfaceRequestor).
104 getInterface(Ci.nsIWebNavigation).
105 QueryInterface(Ci.nsIDocShellTreeItem).
106 treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
107 getInterface(Ci.nsIXULWindow);
108 };
109 exports.getXULWindow = getXULWindow;
110
111 function getDOMWindow(xulWindow) {
112 return xulWindow.QueryInterface(Ci.nsIInterfaceRequestor).
113 getInterface(Ci.nsIDOMWindow);
114 }
115 exports.getDOMWindow = getDOMWindow;
116
117 /**
118 * Returns `nsIBaseWindow` for the given `nsIDOMWindow`.
119 */
120 function getBaseWindow(window) {
121 return window.QueryInterface(Ci.nsIInterfaceRequestor).
122 getInterface(Ci.nsIWebNavigation).
123 QueryInterface(Ci.nsIDocShell).
124 QueryInterface(Ci.nsIDocShellTreeItem).
125 treeOwner.
126 QueryInterface(Ci.nsIBaseWindow);
127 }
128 exports.getBaseWindow = getBaseWindow;
129
130 /**
131 * Returns the `nsIDOMWindow` toplevel window for any child/inner window
132 */
133 function getToplevelWindow(window) {
134 return window.QueryInterface(Ci.nsIInterfaceRequestor)
135 .getInterface(Ci.nsIWebNavigation)
136 .QueryInterface(Ci.nsIDocShellTreeItem)
137 .rootTreeItem
138 .QueryInterface(Ci.nsIInterfaceRequestor)
139 .getInterface(Ci.nsIDOMWindow);
140 }
141 exports.getToplevelWindow = getToplevelWindow;
142
143 function getWindowDocShell(window) window.gBrowser.docShell;
144 exports.getWindowDocShell = getWindowDocShell;
145
146 function getWindowLoadingContext(window) {
147 return getWindowDocShell(window).
148 QueryInterface(Ci.nsILoadContext);
149 }
150 exports.getWindowLoadingContext = getWindowLoadingContext;
151
152 const isTopLevel = window => window && getToplevelWindow(window) === window;
153 exports.isTopLevel = isTopLevel;
154
155 /**
156 * Takes hash of options and serializes it to a features string that
157 * can be used passed to `window.open`. For more details on features string see:
158 * https://developer.mozilla.org/en/DOM/window.open#Position_and_size_features
159 */
160 function serializeFeatures(options) {
161 return Object.keys(options).reduce(function(result, name) {
162 let value = options[name];
163
164 // the chrome and private features are special
165 if ((name == 'private' || name == 'chrome'))
166 return result + ((value === true) ? ',' + name : '');
167
168 return result + ',' + name + '=' +
169 (value === true ? 'yes' : value === false ? 'no' : value);
170 }, '').substr(1);
171 }
172
173 /**
174 * Opens a top level window and returns it's `nsIDOMWindow` representation.
175 * @params {String} uri
176 * URI of the document to be loaded into window.
177 * @params {nsIDOMWindow} options.parent
178 * Used as parent for the created window.
179 * @params {String} options.name
180 * Optional name that is assigned to the window.
181 * @params {Object} options.features
182 * Map of key, values like: `{ width: 10, height: 15, chrome: true, private: true }`.
183 */
184 function open(uri, options) {
185 uri = uri || URI_BROWSER;
186 options = options || {};
187
188 if (['chrome', 'resource', 'data'].indexOf(io.newURI(uri, null, null).scheme) < 0)
189 throw new Error('only chrome, resource and data uris are allowed');
190
191 let newWindow = windowWatcher.
192 openWindow(options.parent || null,
193 uri,
194 options.name || null,
195 options.features ? serializeFeatures(options.features) : null,
196 options.args || null);
197
198 return newWindow;
199 }
200 exports.open = open;
201
202 function onFocus(window) {
203 let { resolve, promise } = defer();
204
205 if (isFocused(window)) {
206 resolve(window);
207 }
208 else {
209 window.addEventListener("focus", function focusListener() {
210 window.removeEventListener("focus", focusListener, true);
211 resolve(window);
212 }, true);
213 }
214
215 return promise;
216 }
217 exports.onFocus = onFocus;
218
219 function isFocused(window) {
220 const FM = Cc["@mozilla.org/focus-manager;1"].
221 getService(Ci.nsIFocusManager);
222
223 let childTargetWindow = {};
224 FM.getFocusedElementForWindow(window, true, childTargetWindow);
225 childTargetWindow = childTargetWindow.value;
226
227 let focusedChildWindow = {};
228 if (FM.activeWindow) {
229 FM.getFocusedElementForWindow(FM.activeWindow, true, focusedChildWindow);
230 focusedChildWindow = focusedChildWindow.value;
231 }
232
233 return (focusedChildWindow === childTargetWindow);
234 }
235 exports.isFocused = isFocused;
236
237 /**
238 * Opens a top level window and returns it's `nsIDOMWindow` representation.
239 * Same as `open` but with more features
240 * @param {Object} options
241 *
242 */
243 function openDialog(options) {
244 options = options || {};
245
246 let features = options.features || FEATURES;
247 let featureAry = features.toLowerCase().split(',');
248
249 if (!!options.private) {
250 // add private flag if private window is desired
251 if (!array.has(featureAry, 'private')) {
252 featureAry.push('private');
253 }
254
255 // remove the non-private flag ig a private window is desired
256 let nonPrivateIndex = featureAry.indexOf('non-private');
257 if (nonPrivateIndex >= 0) {
258 featureAry.splice(nonPrivateIndex, 1);
259 }
260
261 features = featureAry.join(',');
262 }
263
264 let browser = getMostRecentBrowserWindow();
265
266 // if there is no browser then do nothing
267 if (!browser)
268 return undefined;
269
270 let newWindow = browser.openDialog.apply(
271 browser,
272 array.flatten([
273 options.url || URI_BROWSER,
274 options.name || NAME,
275 features,
276 options.args || null
277 ])
278 );
279
280 return newWindow;
281 }
282 exports.openDialog = openDialog;
283
284 /**
285 * Returns an array of all currently opened windows.
286 * Note that these windows may still be loading.
287 */
288 function windows(type, options) {
289 options = options || {};
290 let list = [];
291 let winEnum = WM.getEnumerator(type);
292 while (winEnum.hasMoreElements()) {
293 let window = winEnum.getNext().QueryInterface(Ci.nsIDOMWindow);
294 // Only add non-private windows when pb permission isn't set,
295 // unless an option forces the addition of them.
296 if (!window.closed && (options.includePrivate || !isWindowPrivate(window))) {
297 list.push(window);
298 }
299 }
300 return list;
301 }
302 exports.windows = windows;
303
304 /**
305 * Check if the given window is interactive.
306 * i.e. if its "DOMContentLoaded" event has already been fired.
307 * @params {nsIDOMWindow} window
308 */
309 const isInteractive = window =>
310 window.document.readyState === "interactive" ||
311 isDocumentLoaded(window) ||
312 // XUL documents stays '"uninitialized"' until it's `readyState` becomes
313 // `"complete"`.
314 isXULDocumentWindow(window) && window.document.readyState === "interactive";
315 exports.isInteractive = isInteractive;
316
317 const isXULDocumentWindow = ({document}) =>
318 document.documentElement &&
319 document.documentElement.namespaceURI === XUL_NS;
320
321 /**
322 * Check if the given window is completely loaded.
323 * i.e. if its "load" event has already been fired and all possible DOM content
324 * is done loading (the whole DOM document, images content, ...)
325 * @params {nsIDOMWindow} window
326 */
327 function isDocumentLoaded(window) {
328 return window.document.readyState == "complete";
329 }
330 exports.isDocumentLoaded = isDocumentLoaded;
331
332 function isBrowser(window) {
333 try {
334 return window.document.documentElement.getAttribute("windowtype") === BROWSER;
335 }
336 catch (e) {}
337 return false;
338 };
339 exports.isBrowser = isBrowser;
340
341 function getWindowTitle(window) {
342 return window && window.document ? window.document.title : null;
343 }
344 exports.getWindowTitle = getWindowTitle;
345
346 function isXULBrowser(window) {
347 return !!(isBrowser(window) && window.XULBrowserWindow);
348 }
349 exports.isXULBrowser = isXULBrowser;
350
351 /**
352 * Returns the most recent focused window
353 */
354 function getFocusedWindow() {
355 let window = WM.getMostRecentWindow(BROWSER);
356
357 return window ? window.document.commandDispatcher.focusedWindow : null;
358 }
359 exports.getFocusedWindow = getFocusedWindow;
360
361 /**
362 * Returns the focused browser window if any, or the most recent one.
363 * Opening new window, updates most recent window, but focus window
364 * changes later; so most recent window and focused window are not always
365 * the same.
366 */
367 function getFocusedBrowser() {
368 let window = FM.activeWindow;
369 return isBrowser(window) ? window : getMostRecentBrowserWindow()
370 }
371 exports.getFocusedBrowser = getFocusedBrowser;
372
373 /**
374 * Returns the focused element in the most recent focused window
375 */
376 function getFocusedElement() {
377 let window = WM.getMostRecentWindow(BROWSER);
378
379 return window ? window.document.commandDispatcher.focusedElement : null;
380 }
381 exports.getFocusedElement = getFocusedElement;
382
383 function getFrames(window) {
384 return Array.slice(window.frames).reduce(function(frames, frame) {
385 return frames.concat(frame, getFrames(frame));
386 }, []);
387 }
388 exports.getFrames = getFrames;
389
390 function getScreenPixelsPerCSSPixel(window) {
391 return window.QueryInterface(Ci.nsIInterfaceRequestor).
392 getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
393 }
394 exports.getScreenPixelsPerCSSPixel = getScreenPixelsPerCSSPixel;
395
396 function getOwnerBrowserWindow(node) {
397 /**
398 Takes DOM node and returns browser window that contains it.
399 **/
400 let window = getToplevelWindow(node.ownerDocument.defaultView);
401 // If anchored window is browser then it's target browser window.
402 return isBrowser(window) ? window : null;
403 }
404 exports.getOwnerBrowserWindow = getOwnerBrowserWindow;
405
406 function getParentWindow(window) {
407 try {
408 return window.QueryInterface(Ci.nsIInterfaceRequestor)
409 .getInterface(Ci.nsIWebNavigation)
410 .QueryInterface(Ci.nsIDocShellTreeItem).parent
411 .QueryInterface(Ci.nsIInterfaceRequestor)
412 .getInterface(Ci.nsIDOMWindow);
413 }
414 catch (e) {}
415 return null;
416 }
417 exports.getParentWindow = getParentWindow;
418
419
420 function getParentFrame(window) {
421 try {
422 return window.QueryInterface(Ci.nsIInterfaceRequestor)
423 .getInterface(Ci.nsIWebNavigation)
424 .QueryInterface(Ci.nsIDocShellTreeItem).parent
425 .QueryInterface(Ci.nsIInterfaceRequestor)
426 .getInterface(Ci.nsIDOMWindow);
427 }
428 catch (e) {}
429 return null;
430 }
431 exports.getParentWindow = getParentWindow;
432
433 // The element in which the window is embedded, or `null`
434 // if the window is top-level. Similar to `window.frameElement`
435 // but can cross chrome-content boundries.
436 const getFrameElement = target =>
437 (target instanceof Ci.nsIDOMDocument ? target.defaultView : target).
438 QueryInterface(Ci.nsIInterfaceRequestor).
439 getInterface(Ci.nsIDOMWindowUtils).
440 containerElement;
441 exports.getFrameElement = getFrameElement;

mercurial