window-utils.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. module.metadata = {
  6. 'stability': 'deprecated'
  7. };
  8. const { Cc, Ci } = require('chrome');
  9. const { EventEmitter } = require('../deprecated/events');
  10. const { Trait } = require('../deprecated/traits');
  11. const { when } = require('../system/unload');
  12. const events = require('../system/events');
  13. const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser,
  14. getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils');
  15. const errors = require('../deprecated/errors');
  16. const { deprecateFunction } = require('../util/deprecate');
  17. const { ignoreWindow, isGlobalPBSupported } = require('sdk/private-browsing/utils');
  18. const { isPrivateBrowsingSupported } = require('../self');
  19. const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
  20. getService(Ci.nsIWindowWatcher);
  21. const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
  22. getService(Ci.nsIAppShellService);
  23. // Bug 834961: ignore private windows when they are not supported
  24. function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
  25. /**
  26. * An iterator for XUL windows currently in the application.
  27. *
  28. * @return A generator that yields XUL windows exposing the
  29. * nsIDOMWindow interface.
  30. */
  31. function windowIterator() {
  32. // Bug 752631: We only pass already loaded window in order to avoid
  33. // breaking XUL windows DOM. DOM is broken when some JS code try
  34. // to access DOM during "uninitialized" state of the related document.
  35. let list = getWindows().filter(isDocumentLoaded);
  36. for (let i = 0, l = list.length; i < l; i++) {
  37. yield list[i];
  38. }
  39. };
  40. exports.windowIterator = windowIterator;
  41. /**
  42. * An iterator for browser windows currently open in the application.
  43. * @returns {Function}
  44. * A generator that yields browser windows exposing the `nsIDOMWindow`
  45. * interface.
  46. */
  47. function browserWindowIterator() {
  48. for each (let window in windowIterator()) {
  49. if (isBrowser(window))
  50. yield window;
  51. }
  52. }
  53. exports.browserWindowIterator = browserWindowIterator;
  54. function WindowTracker(delegate) {
  55. if (!(this instanceof WindowTracker)) {
  56. return new WindowTracker(delegate);
  57. }
  58. this._delegate = delegate;
  59. this._loadingWindows = [];
  60. for each (let window in getWindows())
  61. this._regWindow(window);
  62. windowWatcher.registerNotification(this);
  63. this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this);
  64. events.on('toplevel-window-ready', this._onToplevelWindowReady);
  65. require('../system/unload').ensure(this);
  66. return this;
  67. };
  68. WindowTracker.prototype = {
  69. _regLoadingWindow: function _regLoadingWindow(window) {
  70. // Bug 834961: ignore private windows when they are not supported
  71. if (ignoreWindow(window))
  72. return;
  73. this._loadingWindows.push(window);
  74. window.addEventListener('load', this, true);
  75. },
  76. _unregLoadingWindow: function _unregLoadingWindow(window) {
  77. var index = this._loadingWindows.indexOf(window);
  78. if (index != -1) {
  79. this._loadingWindows.splice(index, 1);
  80. window.removeEventListener('load', this, true);
  81. }
  82. },
  83. _regWindow: function _regWindow(window) {
  84. // Bug 834961: ignore private windows when they are not supported
  85. if (ignoreWindow(window))
  86. return;
  87. if (window.document.readyState == 'complete') {
  88. this._unregLoadingWindow(window);
  89. this._delegate.onTrack(window);
  90. } else
  91. this._regLoadingWindow(window);
  92. },
  93. _unregWindow: function _unregWindow(window) {
  94. if (window.document.readyState == 'complete') {
  95. if (this._delegate.onUntrack)
  96. this._delegate.onUntrack(window);
  97. } else {
  98. this._unregLoadingWindow(window);
  99. }
  100. },
  101. unload: function unload() {
  102. windowWatcher.unregisterNotification(this);
  103. events.off('toplevel-window-ready', this._onToplevelWindowReady);
  104. for each (let window in getWindows())
  105. this._unregWindow(window);
  106. },
  107. handleEvent: errors.catchAndLog(function handleEvent(event) {
  108. if (event.type == 'load' && event.target) {
  109. var window = event.target.defaultView;
  110. if (window)
  111. this._regWindow(window);
  112. }
  113. }),
  114. _onToplevelWindowReady: function _onToplevelWindowReady({subject}) {
  115. let window = subject;
  116. // ignore private windows if they are not supported
  117. if (ignoreWindow(window))
  118. return;
  119. this._regWindow(window);
  120. },
  121. observe: errors.catchAndLog(function observe(subject, topic, data) {
  122. var window = subject.QueryInterface(Ci.nsIDOMWindow);
  123. // ignore private windows if they are not supported
  124. if (ignoreWindow(window))
  125. return;
  126. if (topic == 'domwindowclosed')
  127. this._unregWindow(window);
  128. })
  129. };
  130. exports.WindowTracker = WindowTracker;
  131. const WindowTrackerTrait = Trait.compose({
  132. _onTrack: Trait.required,
  133. _onUntrack: Trait.required,
  134. constructor: function WindowTrackerTrait() {
  135. WindowTracker({
  136. onTrack: this._onTrack.bind(this),
  137. onUntrack: this._onUntrack.bind(this)
  138. });
  139. }
  140. });
  141. exports.WindowTrackerTrait = WindowTrackerTrait;
  142. var gDocsToClose = [];
  143. function onDocUnload(event) {
  144. var index = gDocsToClose.indexOf(event.target);
  145. if (index == -1)
  146. throw new Error('internal error: unloading document not found');
  147. var document = gDocsToClose.splice(index, 1)[0];
  148. // Just in case, let's remove the event listener too.
  149. document.defaultView.removeEventListener('unload', onDocUnload, false);
  150. }
  151. onDocUnload = require('./errors').catchAndLog(onDocUnload);
  152. exports.closeOnUnload = function closeOnUnload(window) {
  153. window.addEventListener('unload', onDocUnload, false);
  154. gDocsToClose.push(window.document);
  155. };
  156. Object.defineProperties(exports, {
  157. activeWindow: {
  158. enumerable: true,
  159. get: function() {
  160. return getMostRecentWindow(null);
  161. },
  162. set: function(window) {
  163. try {
  164. window.focus();
  165. } catch (e) {}
  166. }
  167. },
  168. activeBrowserWindow: {
  169. enumerable: true,
  170. get: getMostRecentBrowserWindow
  171. }
  172. });
  173. /**
  174. * Returns the ID of the window's current inner window.
  175. */
  176. exports.getInnerId = deprecateFunction(getInnerId,
  177. 'require("window-utils").getInnerId is deprecated, ' +
  178. 'please use require("sdk/window/utils").getInnerId instead'
  179. );
  180. exports.getOuterId = deprecateFunction(getOuterId,
  181. 'require("window-utils").getOuterId is deprecated, ' +
  182. 'please use require("sdk/window/utils").getOuterId instead'
  183. );
  184. exports.isBrowser = deprecateFunction(isBrowser,
  185. 'require("window-utils").isBrowser is deprecated, ' +
  186. 'please use require("sdk/window/utils").isBrowser instead'
  187. );
  188. exports.hiddenWindow = appShellService.hiddenDOMWindow;
  189. when(
  190. function() {
  191. gDocsToClose.slice().forEach(
  192. function(doc) { doc.defaultView.close(); });
  193. });