addon-sdk/source/lib/sdk/tabs/utils.js

changeset 0
6474c204b198
     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;

mercurial