diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-10 02:51:36 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-10 02:51:36 -0500 |
commit | 37d5300335d81cecbecc99812747a657588c63eb (patch) | |
tree | 765efa3b6a56bb715d9813a8697473e120436278 /toolkit/jetpack/sdk/window/utils.js | |
parent | b2bdac20c02b12f2057b9ef70b0a946113a00e00 (diff) | |
parent | 4fb11cd5966461bccc3ed1599b808237be6b0de9 (diff) | |
download | UXP-37d5300335d81cecbecc99812747a657588c63eb.tar UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.gz UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.lz UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.xz UXP-37d5300335d81cecbecc99812747a657588c63eb.zip |
Merge branch 'ext-work'
Diffstat (limited to 'toolkit/jetpack/sdk/window/utils.js')
-rw-r--r-- | toolkit/jetpack/sdk/window/utils.js | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/window/utils.js b/toolkit/jetpack/sdk/window/utils.js new file mode 100644 index 000000000..db91a0fed --- /dev/null +++ b/toolkit/jetpack/sdk/window/utils.js @@ -0,0 +1,460 @@ +/* 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'; + +module.metadata = { + 'stability': 'unstable' +}; + +const { Cc, Ci } = require('chrome'); +const array = require('../util/array'); +const { defer } = require('sdk/core/promise'); +const { dispatcher } = require("../util/dispatcher"); + +const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1']. + getService(Ci.nsIWindowWatcher); +const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. + getService(Ci.nsIAppShellService); +const WM = Cc['@mozilla.org/appshell/window-mediator;1']. + getService(Ci.nsIWindowMediator); +const io = Cc['@mozilla.org/network/io-service;1']. + getService(Ci.nsIIOService); +const FM = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + +const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; + +const prefs = require("../preferences/service"); +const BROWSER = 'navigator:browser', + URI_BROWSER = prefs.get('browser.chromeURL', null), + NAME = '_blank', + FEATURES = 'chrome,all,dialog=no,non-private'; + +function isWindowPrivate(win) { + if (!win) + return false; + + // if the pbService is undefined, the PrivateBrowsingUtils.jsm is available, + // and the app is Firefox, then assume per-window private browsing is + // enabled. + try { + return win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext) + .usePrivateBrowsing; + } + catch(e) {} + + // Sometimes the input is not a nsIDOMWindow.. but it is still a winodw. + try { + return !!win.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing; + } + catch (e) {} + + return false; +} +exports.isWindowPrivate = isWindowPrivate; + +function getMostRecentBrowserWindow() { + return getMostRecentWindow(BROWSER); +} +exports.getMostRecentBrowserWindow = getMostRecentBrowserWindow; + +function getHiddenWindow() { + return appShellService.hiddenDOMWindow; +} +exports.getHiddenWindow = getHiddenWindow; + +function getMostRecentWindow(type) { + return WM.getMostRecentWindow(type); +} +exports.getMostRecentWindow = getMostRecentWindow; + +/** + * Returns the ID of the window's current inner window. + */ +function getInnerId(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; +}; +exports.getInnerId = getInnerId; + +/** + * Returns the ID of the window's outer window. + */ +function getOuterId(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils).outerWindowID; +}; +exports.getOuterId = getOuterId; + +/** + * Returns window by the outer window id. + */ +const getByOuterId = WM.getOuterWindowWithId; +exports.getByOuterId = getByOuterId; + +const getByInnerId = WM.getCurrentInnerWindowWithId; +exports.getByInnerId = getByInnerId; + +/** + * Returns `nsIXULWindow` for the given `nsIDOMWindow`. + */ +function getXULWindow(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebNavigation). + QueryInterface(Ci.nsIDocShellTreeItem). + treeOwner.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIXULWindow); +}; +exports.getXULWindow = getXULWindow; + +function getDOMWindow(xulWindow) { + return xulWindow.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindow); +} +exports.getDOMWindow = getDOMWindow; + +/** + * Returns `nsIBaseWindow` for the given `nsIDOMWindow`. + */ +function getBaseWindow(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIWebNavigation). + QueryInterface(Ci.nsIDocShell). + QueryInterface(Ci.nsIDocShellTreeItem). + treeOwner. + QueryInterface(Ci.nsIBaseWindow); +} +exports.getBaseWindow = getBaseWindow; + +/** + * Returns the `nsIDOMWindow` toplevel window for any child/inner window + */ +function getToplevelWindow(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); +} +exports.getToplevelWindow = getToplevelWindow; + +function getWindowDocShell(window) { + return window.gBrowser.docShell; +} +exports.getWindowDocShell = getWindowDocShell; + +function getWindowLoadingContext(window) { + return getWindowDocShell(window). + QueryInterface(Ci.nsILoadContext); +} +exports.getWindowLoadingContext = getWindowLoadingContext; + +const isTopLevel = window => window && getToplevelWindow(window) === window; +exports.isTopLevel = isTopLevel; + +/** + * Takes hash of options and serializes it to a features string that + * can be used passed to `window.open`. For more details on features string see: + * https://developer.mozilla.org/en/DOM/window.open#Position_and_size_features + */ +function serializeFeatures(options) { + return Object.keys(options).reduce(function(result, name) { + let value = options[name]; + + // the chrome and private features are special + if ((name == 'private' || name == 'chrome' || name == 'all')) + return result + ((value === true) ? ',' + name : ''); + + return result + ',' + name + '=' + + (value === true ? 'yes' : value === false ? 'no' : value); + }, '').substr(1); +} + +/** + * Opens a top level window and returns it's `nsIDOMWindow` representation. + * @params {String} uri + * URI of the document to be loaded into window. + * @params {nsIDOMWindow} options.parent + * Used as parent for the created window. + * @params {String} options.name + * Optional name that is assigned to the window. + * @params {Object} options.features + * Map of key, values like: `{ width: 10, height: 15, chrome: true, private: true }`. + */ +function open(uri, options) { + uri = uri || URI_BROWSER; + options = options || {}; + + if (!uri) + throw new Error('browser.chromeURL is undefined, please provide an explicit uri'); + + if (['chrome', 'resource', 'data'].indexOf(io.newURI(uri, null, null).scheme) < 0) + throw new Error('only chrome, resource and data uris are allowed'); + + let newWindow = windowWatcher. + openWindow(options.parent || null, + uri, + options.name || null, + options.features ? serializeFeatures(options.features) : null, + options.args || null); + + return newWindow; +} +exports.open = open; + +function onFocus(window) { + let { resolve, promise } = defer(); + + if (isFocused(window)) { + resolve(window); + } + else { + window.addEventListener("focus", function focusListener() { + window.removeEventListener("focus", focusListener, true); + resolve(window); + }, true); + } + + return promise; +} +exports.onFocus = onFocus; + +var isFocused = dispatcher("window-isFocused"); +isFocused.when(x => x instanceof Ci.nsIDOMWindow, (window) => { + const FM = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + + let childTargetWindow = {}; + FM.getFocusedElementForWindow(window, true, childTargetWindow); + childTargetWindow = childTargetWindow.value; + + let focusedChildWindow = {}; + if (FM.activeWindow) { + FM.getFocusedElementForWindow(FM.activeWindow, true, focusedChildWindow); + focusedChildWindow = focusedChildWindow.value; + } + + return (focusedChildWindow === childTargetWindow); +}); +exports.isFocused = isFocused; + +/** + * Opens a top level window and returns it's `nsIDOMWindow` representation. + * Same as `open` but with more features + * @param {Object} options + * + */ +function openDialog(options) { + options = options || {}; + + let features = options.features || FEATURES; + let featureAry = features.toLowerCase().split(','); + + if (!!options.private) { + // add private flag if private window is desired + if (!array.has(featureAry, 'private')) { + featureAry.push('private'); + } + + // remove the non-private flag ig a private window is desired + let nonPrivateIndex = featureAry.indexOf('non-private'); + if (nonPrivateIndex >= 0) { + featureAry.splice(nonPrivateIndex, 1); + } + + features = featureAry.join(','); + } + + let browser = getMostRecentBrowserWindow(); + + // if there is no browser then do nothing + if (!browser) + return undefined; + + let newWindow = browser.openDialog.apply( + browser, + array.flatten([ + options.url || URI_BROWSER, + options.name || NAME, + features, + options.args || null + ]) + ); + + return newWindow; +} +exports.openDialog = openDialog; + +/** + * Returns an array of all currently opened windows. + * Note that these windows may still be loading. + */ +function windows(type, options) { + options = options || {}; + let list = []; + let winEnum = WM.getEnumerator(type); + while (winEnum.hasMoreElements()) { + let window = winEnum.getNext().QueryInterface(Ci.nsIDOMWindow); + // Only add non-private windows when pb permission isn't set, + // unless an option forces the addition of them. + if (!window.closed && (options.includePrivate || !isWindowPrivate(window))) { + list.push(window); + } + } + return list; +} +exports.windows = windows; + +/** + * Check if the given window is interactive. + * i.e. if its "DOMContentLoaded" event has already been fired. + * @params {nsIDOMWindow} window + */ +const isInteractive = window => + window.document.readyState === "interactive" || + isDocumentLoaded(window) || + // XUL documents stays '"uninitialized"' until it's `readyState` becomes + // `"complete"`. + isXULDocumentWindow(window) && window.document.readyState === "interactive"; +exports.isInteractive = isInteractive; + +/** + * Check if the given browser window has finished the startup. + * @params {nsIDOMWindow} window + */ +const isStartupFinished = (window) => + isBrowser(window) && + window.gBrowserInit && + window.gBrowserInit.delayedStartupFinished; + +exports.isStartupFinished = isStartupFinished; + +const isXULDocumentWindow = ({document}) => + document.documentElement && + document.documentElement.namespaceURI === XUL_NS; + +/** + * Check if the given window is completely loaded. + * i.e. if its "load" event has already been fired and all possible DOM content + * is done loading (the whole DOM document, images content, ...) + * @params {nsIDOMWindow} window + */ +function isDocumentLoaded(window) { + return window.document.readyState == "complete"; +} +exports.isDocumentLoaded = isDocumentLoaded; + +function isBrowser(window) { + try { + return window.document.documentElement.getAttribute("windowtype") === BROWSER; + } + catch (e) {} + return false; +}; +exports.isBrowser = isBrowser; + +function getWindowTitle(window) { + return window && window.document ? window.document.title : null; +} +exports.getWindowTitle = getWindowTitle; + +function isXULBrowser(window) { + return !!(isBrowser(window) && window.XULBrowserWindow); +} +exports.isXULBrowser = isXULBrowser; + +/** + * Returns the most recent focused window + */ +function getFocusedWindow() { + let window = WM.getMostRecentWindow(BROWSER); + + return window ? window.document.commandDispatcher.focusedWindow : null; +} +exports.getFocusedWindow = getFocusedWindow; + +/** + * Returns the focused browser window if any, or the most recent one. + * Opening new window, updates most recent window, but focus window + * changes later; so most recent window and focused window are not always + * the same. + */ +function getFocusedBrowser() { + let window = FM.activeWindow; + return isBrowser(window) ? window : getMostRecentBrowserWindow() +} +exports.getFocusedBrowser = getFocusedBrowser; + +/** + * Returns the focused element in the most recent focused window + */ +function getFocusedElement() { + let window = WM.getMostRecentWindow(BROWSER); + + return window ? window.document.commandDispatcher.focusedElement : null; +} +exports.getFocusedElement = getFocusedElement; + +function getFrames(window) { + return Array.slice(window.frames).reduce(function(frames, frame) { + return frames.concat(frame, getFrames(frame)); + }, []); +} +exports.getFrames = getFrames; + +function getScreenPixelsPerCSSPixel(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel; +} +exports.getScreenPixelsPerCSSPixel = getScreenPixelsPerCSSPixel; + +function getOwnerBrowserWindow(node) { + /** + Takes DOM node and returns browser window that contains it. + **/ + let window = getToplevelWindow(node.ownerDocument.defaultView); + // If anchored window is browser then it's target browser window. + return isBrowser(window) ? window : null; +} +exports.getOwnerBrowserWindow = getOwnerBrowserWindow; + +function getParentWindow(window) { + try { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem).parent + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + } + catch (e) {} + return null; +} +exports.getParentWindow = getParentWindow; + + +function getParentFrame(window) { + try { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem).parent + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + } + catch (e) {} + return null; +} +exports.getParentWindow = getParentWindow; + +// The element in which the window is embedded, or `null` +// if the window is top-level. Similar to `window.frameElement` +// but can cross chrome-content boundries. +const getFrameElement = target => + (target instanceof Ci.nsIDOMDocument ? target.defaultView : target). + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils). + containerElement; +exports.getFrameElement = getFrameElement; |