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 /* 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';
6 module.metadata = {
7 'stability': 'unstable'
8 };
11 // NOTE: This file should only deal with xul/native tabs
14 const { Ci } = require('chrome');
15 const { defer } = require("../lang/functional");
16 const { windows, isBrowser } = require('../window/utils');
17 const { isPrivateBrowsingSupported } = require('../self');
18 const { isGlobalPBSupported } = require('../private-browsing/utils');
20 // Bug 834961: ignore private windows when they are not supported
21 function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
23 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
25 // Define predicate functions that can be used to detech weather
26 // we deal with fennec tabs or firefox tabs.
28 // Predicate to detect whether tab is XUL "Tab" node.
29 const isXULTab = tab =>
30 tab instanceof Ci.nsIDOMNode &&
31 tab.nodeName === "tab" &&
32 tab.namespaceURI === XUL_NS;
33 exports.isXULTab = isXULTab;
35 // Predicate to detecet whether given tab is a fettec tab.
36 // Unfortunately we have to guess via duck typinng of:
37 // http://mxr.mozilla.org/mozilla-central/source/mobile/android/chrome/content/browser.js#2583
38 const isFennecTab = tab =>
39 tab &&
40 tab.QueryInterface &&
41 Ci.nsIBrowserTab &&
42 tab.QueryInterface(Ci.nsIBrowserTab) === tab;
43 exports.isFennecTab = isFennecTab;
45 const isTab = x => isXULTab(x) || isFennecTab(x);
46 exports.isTab = isTab;
48 function activateTab(tab, window) {
49 let gBrowser = getTabBrowserForTab(tab);
51 // normal case
52 if (gBrowser) {
53 gBrowser.selectedTab = tab;
54 }
55 // fennec ?
56 else if (window && window.BrowserApp) {
57 window.BrowserApp.selectTab(tab);
58 }
59 return null;
60 }
61 exports.activateTab = activateTab;
63 function getTabBrowser(window) {
64 return window.gBrowser;
65 }
66 exports.getTabBrowser = getTabBrowser;
68 function getTabContainer(window) {
69 return getTabBrowser(window).tabContainer;
70 }
71 exports.getTabContainer = getTabContainer;
73 /**
74 * Returns the tabs for the `window` if given, or the tabs
75 * across all the browser's windows otherwise.
76 *
77 * @param {nsIWindow} [window]
78 * A reference to a window
79 *
80 * @returns {Array} an array of Tab objects
81 */
82 function getTabs(window) {
83 if (arguments.length === 0) {
84 return getWindows().filter(isBrowser).reduce(function(tabs, window) {
85 return tabs.concat(getTabs(window))
86 }, []);
87 }
89 // fennec
90 if (window.BrowserApp)
91 return window.BrowserApp.tabs;
93 // firefox - default
94 return Array.slice(getTabContainer(window).children);
95 }
96 exports.getTabs = getTabs;
98 function getActiveTab(window) {
99 return getSelectedTab(window);
100 }
101 exports.getActiveTab = getActiveTab;
103 function getOwnerWindow(tab) {
104 // normal case
105 if (tab.ownerDocument)
106 return tab.ownerDocument.defaultView;
108 // try fennec case
109 return getWindowHoldingTab(tab);
110 }
111 exports.getOwnerWindow = getOwnerWindow;
113 // fennec
114 function getWindowHoldingTab(rawTab) {
115 for each (let window in getWindows()) {
116 // this function may be called when not using fennec,
117 // but BrowserApp is only defined on Fennec
118 if (!window.BrowserApp)
119 continue;
121 for each (let tab in window.BrowserApp.tabs) {
122 if (tab === rawTab)
123 return window;
124 }
125 }
127 return null;
128 }
130 function openTab(window, url, options) {
131 options = options || {};
133 // fennec?
134 if (window.BrowserApp) {
135 return window.BrowserApp.addTab(url, {
136 selected: options.inBackground ? false : true,
137 pinned: options.isPinned || false,
138 isPrivate: options.isPrivate || false
139 });
140 }
142 // firefox
143 let newTab = window.gBrowser.addTab(url);
144 if (!options.inBackground) {
145 activateTab(newTab);
146 }
147 return newTab;
148 };
149 exports.openTab = openTab;
151 function isTabOpen(tab) {
152 // try normal case then fennec case
153 return !!((tab.linkedBrowser) || getWindowHoldingTab(tab));
154 }
155 exports.isTabOpen = isTabOpen;
157 function closeTab(tab) {
158 let gBrowser = getTabBrowserForTab(tab);
159 // normal case?
160 if (gBrowser) {
161 // Bug 699450: the tab may already have been detached
162 if (!tab.parentNode)
163 return;
164 return gBrowser.removeTab(tab);
165 }
167 let window = getWindowHoldingTab(tab);
168 // fennec?
169 if (window && window.BrowserApp) {
170 // Bug 699450: the tab may already have been detached
171 if (!tab.browser)
172 return;
173 return window.BrowserApp.closeTab(tab);
174 }
175 return null;
176 }
177 exports.closeTab = closeTab;
179 function getURI(tab) {
180 if (tab.browser) // fennec
181 return tab.browser.currentURI.spec;
182 return tab.linkedBrowser.currentURI.spec;
183 }
184 exports.getURI = getURI;
186 function getTabBrowserForTab(tab) {
187 let outerWin = getOwnerWindow(tab);
188 if (outerWin)
189 return getOwnerWindow(tab).gBrowser;
190 return null;
191 }
192 exports.getTabBrowserForTab = getTabBrowserForTab;
194 function getBrowserForTab(tab) {
195 if (tab.browser) // fennec
196 return tab.browser;
198 return tab.linkedBrowser;
199 }
200 exports.getBrowserForTab = getBrowserForTab;
202 function getTabId(tab) {
203 if (tab.browser) // fennec
204 return tab.id
206 return String.split(tab.linkedPanel, 'panel').pop();
207 }
208 exports.getTabId = getTabId;
210 function getTabForId(id) {
211 return getTabs().find(tab => getTabId(tab) === id) || null;
212 }
213 exports.getTabForId = getTabForId;
215 function getTabTitle(tab) {
216 return getBrowserForTab(tab).contentDocument.title || tab.label || "";
217 }
218 exports.getTabTitle = getTabTitle;
220 function setTabTitle(tab, title) {
221 title = String(title);
222 if (tab.browser)
223 tab.browser.contentDocument.title = title;
224 tab.label = String(title);
225 }
226 exports.setTabTitle = setTabTitle;
228 function getTabContentWindow(tab) {
229 return getBrowserForTab(tab).contentWindow;
230 }
231 exports.getTabContentWindow = getTabContentWindow;
233 /**
234 * Returns all tabs' content windows across all the browsers' windows
235 */
236 function getAllTabContentWindows() {
237 return getTabs().map(getTabContentWindow);
238 }
239 exports.getAllTabContentWindows = getAllTabContentWindows;
241 // gets the tab containing the provided window
242 function getTabForContentWindow(window) {
243 // Retrieve the topmost frame container. It can be either <xul:browser>,
244 // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser.
245 let browser;
246 try {
247 browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
248 .getInterface(Ci.nsIWebNavigation)
249 .QueryInterface(Ci.nsIDocShell)
250 .chromeEventHandler;
251 } catch(e) {
252 // Bug 699450: The tab may already have been detached so that `window` is
253 // in a almost destroyed state and can't be queryinterfaced anymore.
254 }
256 // Is null for toplevel documents
257 if (!browser) {
258 return null;
259 }
261 // Retrieve the owner window, should be browser.xul one
262 let chromeWindow = browser.ownerDocument.defaultView;
264 // Ensure that it is top-level browser window.
265 // We need extra checks because of Mac hidden window that has a broken
266 // `gBrowser` global attribute.
267 if ('gBrowser' in chromeWindow && chromeWindow.gBrowser &&
268 'browsers' in chromeWindow.gBrowser) {
269 // Looks like we are on Firefox Desktop
270 // Then search for the position in tabbrowser in order to get the tab object
271 let browsers = chromeWindow.gBrowser.browsers;
272 let i = browsers.indexOf(browser);
273 if (i !== -1)
274 return chromeWindow.gBrowser.tabs[i];
275 return null;
276 }
277 // Fennec
278 else if ('BrowserApp' in chromeWindow) {
279 return getTabForWindow(window);
280 }
282 return null;
283 }
284 exports.getTabForContentWindow = getTabForContentWindow;
286 // used on fennec
287 function getTabForWindow(window) {
288 for each (let { BrowserApp } in getWindows()) {
289 if (!BrowserApp)
290 continue;
292 for each (let tab in BrowserApp.tabs) {
293 if (tab.browser.contentWindow == window.top)
294 return tab;
295 }
296 }
297 return null;
298 }
300 function getTabURL(tab) {
301 if (tab.browser) // fennec
302 return String(tab.browser.currentURI.spec);
303 return String(getBrowserForTab(tab).currentURI.spec);
304 }
305 exports.getTabURL = getTabURL;
307 function setTabURL(tab, url) {
308 url = String(url);
309 if (tab.browser)
310 return tab.browser.loadURI(url);
311 return getBrowserForTab(tab).loadURI(url);
312 }
313 // "TabOpen" event is fired when it's still "about:blank" is loaded in the
314 // changing `location` property of the `contentDocument` has no effect since
315 // seems to be either ignored or overridden by internal listener, there for
316 // location change is enqueued for the next turn of event loop.
317 exports.setTabURL = defer(setTabURL);
319 function getTabContentType(tab) {
320 return getBrowserForTab(tab).contentDocument.contentType;
321 }
322 exports.getTabContentType = getTabContentType;
324 function getSelectedTab(window) {
325 if (window.BrowserApp) // fennec?
326 return window.BrowserApp.selectedTab;
327 if (window.gBrowser)
328 return window.gBrowser.selectedTab;
329 return null;
330 }
331 exports.getSelectedTab = getSelectedTab;
334 function getTabForBrowser(browser) {
335 for each (let window in getWindows()) {
336 // this function may be called when not using fennec
337 if (!window.BrowserApp)
338 continue;
340 for each (let tab in window.BrowserApp.tabs) {
341 if (tab.browser === browser)
342 return tab;
343 }
344 }
345 return null;
346 }
347 exports.getTabForBrowser = getTabForBrowser;
349 function pin(tab) {
350 let gBrowser = getTabBrowserForTab(tab);
351 // TODO: Implement Fennec support
352 if (gBrowser) gBrowser.pinTab(tab);
353 }
354 exports.pin = pin;
356 function unpin(tab) {
357 let gBrowser = getTabBrowserForTab(tab);
358 // TODO: Implement Fennec support
359 if (gBrowser) gBrowser.unpinTab(tab);
360 }
361 exports.unpin = unpin;
363 function isPinned(tab) !!tab.pinned
364 exports.isPinned = isPinned;
366 function reload(tab) {
367 let gBrowser = getTabBrowserForTab(tab);
368 // Firefox
369 if (gBrowser) gBrowser.unpinTab(tab);
370 // Fennec
371 else if (tab.browser) tab.browser.reload();
372 }
373 exports.reload = reload
375 function getIndex(tab) {
376 let gBrowser = getTabBrowserForTab(tab);
377 // Firefox
378 if (gBrowser) {
379 let document = getBrowserForTab(tab).contentDocument;
380 return gBrowser.getBrowserIndexForDocument(document);
381 }
382 // Fennec
383 else {
384 let window = getWindowHoldingTab(tab)
385 let tabs = window.BrowserApp.tabs;
386 for (let i = tabs.length; i >= 0; i--)
387 if (tabs[i] === tab) return i;
388 }
389 }
390 exports.getIndex = getIndex;
392 function move(tab, index) {
393 let gBrowser = getTabBrowserForTab(tab);
394 // Firefox
395 if (gBrowser) gBrowser.moveTabTo(tab, index);
396 // TODO: Implement fennec support
397 }
398 exports.move = move;