Bug 1675375 Part 7: Update expectations in helper_hittest_clippath.html. r=botond
[gecko.git] / browser / modules / BrowserWindowTracker.jsm
blob4e0d41cb5b05b9ca2d50af8ede0b1f2d5a96ffc2
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/. */
5 /*
6  * This module tracks each browser window and informs network module
7  * the current selected tab's content outer window ID.
8  */
10 var EXPORTED_SYMBOLS = ["BrowserWindowTracker"];
12 const { XPCOMUtils } = ChromeUtils.import(
13   "resource://gre/modules/XPCOMUtils.jsm"
15 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
17 // Lazy getters
18 XPCOMUtils.defineLazyModuleGetters(this, {
19   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
20 });
22 // Constants
23 const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
24 const WINDOW_EVENTS = ["activate", "unload"];
25 const DEBUG = false;
27 // Variables
28 var _lastTopBrowsingContextID = 0;
29 var _trackedWindows = [];
31 // Global methods
32 function debug(s) {
33   if (DEBUG) {
34     dump("-*- UpdateTopBrowsingContextIDHelper: " + s + "\n");
35   }
38 function _updateCurrentBrowsingContextID(browser) {
39   if (
40     !browser.browsingContext ||
41     browser.browsingContext.id === _lastTopBrowsingContextID ||
42     browser.ownerGlobal != _trackedWindows[0]
43   ) {
44     return;
45   }
47   debug(
48     "Current window uri=" +
49       (browser.currentURI && browser.currentURI.spec) +
50       " browsing context id=" +
51       browser.browsingContext.id
52   );
54   _lastTopBrowsingContextID = browser.browsingContext.id;
55   let idWrapper = Cc["@mozilla.org/supports-PRUint64;1"].createInstance(
56     Ci.nsISupportsPRUint64
57   );
58   idWrapper.data = _lastTopBrowsingContextID;
59   Services.obs.notifyObservers(
60     idWrapper,
61     "net:current-top-browsing-context-id"
62   );
65 function _handleEvent(event) {
66   switch (event.type) {
67     case "TabBrowserInserted":
68       if (
69         event.target.ownerGlobal.gBrowser.selectedBrowser ===
70         event.target.linkedBrowser
71       ) {
72         _updateCurrentBrowsingContextID(event.target.linkedBrowser);
73       }
74       break;
75     case "TabSelect":
76       _updateCurrentBrowsingContextID(event.target.linkedBrowser);
77       break;
78     case "activate":
79       WindowHelper.onActivate(event.target);
80       break;
81     case "unload":
82       WindowHelper.removeWindow(event.currentTarget);
83       break;
84   }
87 function _trackWindowOrder(window) {
88   if (window.windowState == window.STATE_MINIMIZED) {
89     let firstMinimizedWindow = _trackedWindows.findIndex(
90       w => w.windowState == w.STATE_MINIMIZED
91     );
92     if (firstMinimizedWindow == -1) {
93       firstMinimizedWindow = _trackedWindows.length;
94     }
95     _trackedWindows.splice(firstMinimizedWindow, 0, window);
96   } else {
97     _trackedWindows.unshift(window);
98   }
101 function _untrackWindowOrder(window) {
102   let idx = _trackedWindows.indexOf(window);
103   if (idx >= 0) {
104     _trackedWindows.splice(idx, 1);
105   }
108 // Methods that impact a window. Put into single object for organization.
109 var WindowHelper = {
110   addWindow(window) {
111     // Add event listeners
112     TAB_EVENTS.forEach(function(event) {
113       window.gBrowser.tabContainer.addEventListener(event, _handleEvent);
114     });
115     WINDOW_EVENTS.forEach(function(event) {
116       window.addEventListener(event, _handleEvent);
117     });
119     _trackWindowOrder(window);
121     // Update the selected tab's content outer window ID.
122     _updateCurrentBrowsingContextID(window.gBrowser.selectedBrowser);
123   },
125   removeWindow(window) {
126     _untrackWindowOrder(window);
128     // Remove the event listeners
129     TAB_EVENTS.forEach(function(event) {
130       window.gBrowser.tabContainer.removeEventListener(event, _handleEvent);
131     });
132     WINDOW_EVENTS.forEach(function(event) {
133       window.removeEventListener(event, _handleEvent);
134     });
135   },
137   onActivate(window) {
138     // If this window was the last focused window, we don't need to do anything
139     if (window == _trackedWindows[0]) {
140       return;
141     }
143     _untrackWindowOrder(window);
144     _trackWindowOrder(window);
146     _updateCurrentBrowsingContextID(window.gBrowser.selectedBrowser);
147   },
150 this.BrowserWindowTracker = {
151   /**
152    * Get the most recent browser window.
153    *
154    * @param options an object accepting the arguments for the search.
155    *        * private: true to restrict the search to private windows
156    *            only, false to restrict the search to non-private only.
157    *            Omit the property to search in both groups.
158    *        * allowPopups: true if popup windows are permissable.
159    */
160   getTopWindow(options = {}) {
161     for (let win of _trackedWindows) {
162       if (
163         !win.closed &&
164         (options.allowPopups || win.toolbar.visible) &&
165         (!("private" in options) ||
166           PrivateBrowsingUtils.permanentPrivateBrowsing ||
167           PrivateBrowsingUtils.isWindowPrivate(win) == options.private)
168       ) {
169         return win;
170       }
171     }
172     return null;
173   },
175   windowCreated(browser) {
176     if (browser === browser.ownerGlobal.gBrowser.selectedBrowser) {
177       _updateCurrentBrowsingContextID(browser);
178     }
179   },
181   /**
182    * Number of currently open browser windows.
183    */
184   get windowCount() {
185     return _trackedWindows.length;
186   },
188   /**
189    * Array of browser windows ordered by z-index, in reverse order.
190    * This means that the top-most browser window will be the first item.
191    */
192   get orderedWindows() {
193     // Clone the windows array immediately as it may change during iteration,
194     // we'd rather have an outdated order than skip/revisit windows.
195     return [..._trackedWindows];
196   },
198   getAllVisibleTabs() {
199     let tabs = [];
200     for (let win of BrowserWindowTracker.orderedWindows) {
201       for (let tab of win.gBrowser.visibleTabs) {
202         // Only use tabs which are not discarded / unrestored
203         if (tab.linkedPanel) {
204           let { contentTitle, browserId } = tab.linkedBrowser;
205           tabs.push({ contentTitle, browserId });
206         }
207       }
208     }
209     return tabs;
210   },
212   track(window) {
213     return WindowHelper.addWindow(window);
214   },