1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/tabs/utils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,398 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +'use strict'; 1.8 + 1.9 +module.metadata = { 1.10 + 'stability': 'unstable' 1.11 +}; 1.12 + 1.13 + 1.14 +// NOTE: This file should only deal with xul/native tabs 1.15 + 1.16 + 1.17 +const { Ci } = require('chrome'); 1.18 +const { defer } = require("../lang/functional"); 1.19 +const { windows, isBrowser } = require('../window/utils'); 1.20 +const { isPrivateBrowsingSupported } = require('../self'); 1.21 +const { isGlobalPBSupported } = require('../private-browsing/utils'); 1.22 + 1.23 +// Bug 834961: ignore private windows when they are not supported 1.24 +function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported }); 1.25 + 1.26 +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.27 + 1.28 +// Define predicate functions that can be used to detech weather 1.29 +// we deal with fennec tabs or firefox tabs. 1.30 + 1.31 +// Predicate to detect whether tab is XUL "Tab" node. 1.32 +const isXULTab = tab => 1.33 + tab instanceof Ci.nsIDOMNode && 1.34 + tab.nodeName === "tab" && 1.35 + tab.namespaceURI === XUL_NS; 1.36 +exports.isXULTab = isXULTab; 1.37 + 1.38 +// Predicate to detecet whether given tab is a fettec tab. 1.39 +// Unfortunately we have to guess via duck typinng of: 1.40 +// http://mxr.mozilla.org/mozilla-central/source/mobile/android/chrome/content/browser.js#2583 1.41 +const isFennecTab = tab => 1.42 + tab && 1.43 + tab.QueryInterface && 1.44 + Ci.nsIBrowserTab && 1.45 + tab.QueryInterface(Ci.nsIBrowserTab) === tab; 1.46 +exports.isFennecTab = isFennecTab; 1.47 + 1.48 +const isTab = x => isXULTab(x) || isFennecTab(x); 1.49 +exports.isTab = isTab; 1.50 + 1.51 +function activateTab(tab, window) { 1.52 + let gBrowser = getTabBrowserForTab(tab); 1.53 + 1.54 + // normal case 1.55 + if (gBrowser) { 1.56 + gBrowser.selectedTab = tab; 1.57 + } 1.58 + // fennec ? 1.59 + else if (window && window.BrowserApp) { 1.60 + window.BrowserApp.selectTab(tab); 1.61 + } 1.62 + return null; 1.63 +} 1.64 +exports.activateTab = activateTab; 1.65 + 1.66 +function getTabBrowser(window) { 1.67 + return window.gBrowser; 1.68 +} 1.69 +exports.getTabBrowser = getTabBrowser; 1.70 + 1.71 +function getTabContainer(window) { 1.72 + return getTabBrowser(window).tabContainer; 1.73 +} 1.74 +exports.getTabContainer = getTabContainer; 1.75 + 1.76 +/** 1.77 + * Returns the tabs for the `window` if given, or the tabs 1.78 + * across all the browser's windows otherwise. 1.79 + * 1.80 + * @param {nsIWindow} [window] 1.81 + * A reference to a window 1.82 + * 1.83 + * @returns {Array} an array of Tab objects 1.84 + */ 1.85 +function getTabs(window) { 1.86 + if (arguments.length === 0) { 1.87 + return getWindows().filter(isBrowser).reduce(function(tabs, window) { 1.88 + return tabs.concat(getTabs(window)) 1.89 + }, []); 1.90 + } 1.91 + 1.92 + // fennec 1.93 + if (window.BrowserApp) 1.94 + return window.BrowserApp.tabs; 1.95 + 1.96 + // firefox - default 1.97 + return Array.slice(getTabContainer(window).children); 1.98 +} 1.99 +exports.getTabs = getTabs; 1.100 + 1.101 +function getActiveTab(window) { 1.102 + return getSelectedTab(window); 1.103 +} 1.104 +exports.getActiveTab = getActiveTab; 1.105 + 1.106 +function getOwnerWindow(tab) { 1.107 + // normal case 1.108 + if (tab.ownerDocument) 1.109 + return tab.ownerDocument.defaultView; 1.110 + 1.111 + // try fennec case 1.112 + return getWindowHoldingTab(tab); 1.113 +} 1.114 +exports.getOwnerWindow = getOwnerWindow; 1.115 + 1.116 +// fennec 1.117 +function getWindowHoldingTab(rawTab) { 1.118 + for each (let window in getWindows()) { 1.119 + // this function may be called when not using fennec, 1.120 + // but BrowserApp is only defined on Fennec 1.121 + if (!window.BrowserApp) 1.122 + continue; 1.123 + 1.124 + for each (let tab in window.BrowserApp.tabs) { 1.125 + if (tab === rawTab) 1.126 + return window; 1.127 + } 1.128 + } 1.129 + 1.130 + return null; 1.131 +} 1.132 + 1.133 +function openTab(window, url, options) { 1.134 + options = options || {}; 1.135 + 1.136 + // fennec? 1.137 + if (window.BrowserApp) { 1.138 + return window.BrowserApp.addTab(url, { 1.139 + selected: options.inBackground ? false : true, 1.140 + pinned: options.isPinned || false, 1.141 + isPrivate: options.isPrivate || false 1.142 + }); 1.143 + } 1.144 + 1.145 + // firefox 1.146 + let newTab = window.gBrowser.addTab(url); 1.147 + if (!options.inBackground) { 1.148 + activateTab(newTab); 1.149 + } 1.150 + return newTab; 1.151 +}; 1.152 +exports.openTab = openTab; 1.153 + 1.154 +function isTabOpen(tab) { 1.155 + // try normal case then fennec case 1.156 + return !!((tab.linkedBrowser) || getWindowHoldingTab(tab)); 1.157 +} 1.158 +exports.isTabOpen = isTabOpen; 1.159 + 1.160 +function closeTab(tab) { 1.161 + let gBrowser = getTabBrowserForTab(tab); 1.162 + // normal case? 1.163 + if (gBrowser) { 1.164 + // Bug 699450: the tab may already have been detached 1.165 + if (!tab.parentNode) 1.166 + return; 1.167 + return gBrowser.removeTab(tab); 1.168 + } 1.169 + 1.170 + let window = getWindowHoldingTab(tab); 1.171 + // fennec? 1.172 + if (window && window.BrowserApp) { 1.173 + // Bug 699450: the tab may already have been detached 1.174 + if (!tab.browser) 1.175 + return; 1.176 + return window.BrowserApp.closeTab(tab); 1.177 + } 1.178 + return null; 1.179 +} 1.180 +exports.closeTab = closeTab; 1.181 + 1.182 +function getURI(tab) { 1.183 + if (tab.browser) // fennec 1.184 + return tab.browser.currentURI.spec; 1.185 + return tab.linkedBrowser.currentURI.spec; 1.186 +} 1.187 +exports.getURI = getURI; 1.188 + 1.189 +function getTabBrowserForTab(tab) { 1.190 + let outerWin = getOwnerWindow(tab); 1.191 + if (outerWin) 1.192 + return getOwnerWindow(tab).gBrowser; 1.193 + return null; 1.194 +} 1.195 +exports.getTabBrowserForTab = getTabBrowserForTab; 1.196 + 1.197 +function getBrowserForTab(tab) { 1.198 + if (tab.browser) // fennec 1.199 + return tab.browser; 1.200 + 1.201 + return tab.linkedBrowser; 1.202 +} 1.203 +exports.getBrowserForTab = getBrowserForTab; 1.204 + 1.205 +function getTabId(tab) { 1.206 + if (tab.browser) // fennec 1.207 + return tab.id 1.208 + 1.209 + return String.split(tab.linkedPanel, 'panel').pop(); 1.210 +} 1.211 +exports.getTabId = getTabId; 1.212 + 1.213 +function getTabForId(id) { 1.214 + return getTabs().find(tab => getTabId(tab) === id) || null; 1.215 +} 1.216 +exports.getTabForId = getTabForId; 1.217 + 1.218 +function getTabTitle(tab) { 1.219 + return getBrowserForTab(tab).contentDocument.title || tab.label || ""; 1.220 +} 1.221 +exports.getTabTitle = getTabTitle; 1.222 + 1.223 +function setTabTitle(tab, title) { 1.224 + title = String(title); 1.225 + if (tab.browser) 1.226 + tab.browser.contentDocument.title = title; 1.227 + tab.label = String(title); 1.228 +} 1.229 +exports.setTabTitle = setTabTitle; 1.230 + 1.231 +function getTabContentWindow(tab) { 1.232 + return getBrowserForTab(tab).contentWindow; 1.233 +} 1.234 +exports.getTabContentWindow = getTabContentWindow; 1.235 + 1.236 +/** 1.237 + * Returns all tabs' content windows across all the browsers' windows 1.238 + */ 1.239 +function getAllTabContentWindows() { 1.240 + return getTabs().map(getTabContentWindow); 1.241 +} 1.242 +exports.getAllTabContentWindows = getAllTabContentWindows; 1.243 + 1.244 +// gets the tab containing the provided window 1.245 +function getTabForContentWindow(window) { 1.246 + // Retrieve the topmost frame container. It can be either <xul:browser>, 1.247 + // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser. 1.248 + let browser; 1.249 + try { 1.250 + browser = window.QueryInterface(Ci.nsIInterfaceRequestor) 1.251 + .getInterface(Ci.nsIWebNavigation) 1.252 + .QueryInterface(Ci.nsIDocShell) 1.253 + .chromeEventHandler; 1.254 + } catch(e) { 1.255 + // Bug 699450: The tab may already have been detached so that `window` is 1.256 + // in a almost destroyed state and can't be queryinterfaced anymore. 1.257 + } 1.258 + 1.259 + // Is null for toplevel documents 1.260 + if (!browser) { 1.261 + return null; 1.262 + } 1.263 + 1.264 + // Retrieve the owner window, should be browser.xul one 1.265 + let chromeWindow = browser.ownerDocument.defaultView; 1.266 + 1.267 + // Ensure that it is top-level browser window. 1.268 + // We need extra checks because of Mac hidden window that has a broken 1.269 + // `gBrowser` global attribute. 1.270 + if ('gBrowser' in chromeWindow && chromeWindow.gBrowser && 1.271 + 'browsers' in chromeWindow.gBrowser) { 1.272 + // Looks like we are on Firefox Desktop 1.273 + // Then search for the position in tabbrowser in order to get the tab object 1.274 + let browsers = chromeWindow.gBrowser.browsers; 1.275 + let i = browsers.indexOf(browser); 1.276 + if (i !== -1) 1.277 + return chromeWindow.gBrowser.tabs[i]; 1.278 + return null; 1.279 + } 1.280 + // Fennec 1.281 + else if ('BrowserApp' in chromeWindow) { 1.282 + return getTabForWindow(window); 1.283 + } 1.284 + 1.285 + return null; 1.286 +} 1.287 +exports.getTabForContentWindow = getTabForContentWindow; 1.288 + 1.289 +// used on fennec 1.290 +function getTabForWindow(window) { 1.291 + for each (let { BrowserApp } in getWindows()) { 1.292 + if (!BrowserApp) 1.293 + continue; 1.294 + 1.295 + for each (let tab in BrowserApp.tabs) { 1.296 + if (tab.browser.contentWindow == window.top) 1.297 + return tab; 1.298 + } 1.299 + } 1.300 + return null; 1.301 +} 1.302 + 1.303 +function getTabURL(tab) { 1.304 + if (tab.browser) // fennec 1.305 + return String(tab.browser.currentURI.spec); 1.306 + return String(getBrowserForTab(tab).currentURI.spec); 1.307 +} 1.308 +exports.getTabURL = getTabURL; 1.309 + 1.310 +function setTabURL(tab, url) { 1.311 + url = String(url); 1.312 + if (tab.browser) 1.313 + return tab.browser.loadURI(url); 1.314 + return getBrowserForTab(tab).loadURI(url); 1.315 +} 1.316 +// "TabOpen" event is fired when it's still "about:blank" is loaded in the 1.317 +// changing `location` property of the `contentDocument` has no effect since 1.318 +// seems to be either ignored or overridden by internal listener, there for 1.319 +// location change is enqueued for the next turn of event loop. 1.320 +exports.setTabURL = defer(setTabURL); 1.321 + 1.322 +function getTabContentType(tab) { 1.323 + return getBrowserForTab(tab).contentDocument.contentType; 1.324 +} 1.325 +exports.getTabContentType = getTabContentType; 1.326 + 1.327 +function getSelectedTab(window) { 1.328 + if (window.BrowserApp) // fennec? 1.329 + return window.BrowserApp.selectedTab; 1.330 + if (window.gBrowser) 1.331 + return window.gBrowser.selectedTab; 1.332 + return null; 1.333 +} 1.334 +exports.getSelectedTab = getSelectedTab; 1.335 + 1.336 + 1.337 +function getTabForBrowser(browser) { 1.338 + for each (let window in getWindows()) { 1.339 + // this function may be called when not using fennec 1.340 + if (!window.BrowserApp) 1.341 + continue; 1.342 + 1.343 + for each (let tab in window.BrowserApp.tabs) { 1.344 + if (tab.browser === browser) 1.345 + return tab; 1.346 + } 1.347 + } 1.348 + return null; 1.349 +} 1.350 +exports.getTabForBrowser = getTabForBrowser; 1.351 + 1.352 +function pin(tab) { 1.353 + let gBrowser = getTabBrowserForTab(tab); 1.354 + // TODO: Implement Fennec support 1.355 + if (gBrowser) gBrowser.pinTab(tab); 1.356 +} 1.357 +exports.pin = pin; 1.358 + 1.359 +function unpin(tab) { 1.360 + let gBrowser = getTabBrowserForTab(tab); 1.361 + // TODO: Implement Fennec support 1.362 + if (gBrowser) gBrowser.unpinTab(tab); 1.363 +} 1.364 +exports.unpin = unpin; 1.365 + 1.366 +function isPinned(tab) !!tab.pinned 1.367 +exports.isPinned = isPinned; 1.368 + 1.369 +function reload(tab) { 1.370 + let gBrowser = getTabBrowserForTab(tab); 1.371 + // Firefox 1.372 + if (gBrowser) gBrowser.unpinTab(tab); 1.373 + // Fennec 1.374 + else if (tab.browser) tab.browser.reload(); 1.375 +} 1.376 +exports.reload = reload 1.377 + 1.378 +function getIndex(tab) { 1.379 + let gBrowser = getTabBrowserForTab(tab); 1.380 + // Firefox 1.381 + if (gBrowser) { 1.382 + let document = getBrowserForTab(tab).contentDocument; 1.383 + return gBrowser.getBrowserIndexForDocument(document); 1.384 + } 1.385 + // Fennec 1.386 + else { 1.387 + let window = getWindowHoldingTab(tab) 1.388 + let tabs = window.BrowserApp.tabs; 1.389 + for (let i = tabs.length; i >= 0; i--) 1.390 + if (tabs[i] === tab) return i; 1.391 + } 1.392 +} 1.393 +exports.getIndex = getIndex; 1.394 + 1.395 +function move(tab, index) { 1.396 + let gBrowser = getTabBrowserForTab(tab); 1.397 + // Firefox 1.398 + if (gBrowser) gBrowser.moveTabTo(tab, index); 1.399 + // TODO: Implement fennec support 1.400 +} 1.401 +exports.move = move;