michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: const { Cc, Ci } = require('chrome'); michael@0: const { Class } = require('../core/heritage'); michael@0: const { tabNS, rawTabNS } = require('./namespace'); michael@0: const { EventTarget } = require('../event/target'); michael@0: const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow, michael@0: getTabForBrowser, michael@0: setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils'); michael@0: const { emit } = require('../event/core'); michael@0: const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils'); michael@0: const { when: unload } = require('../system/unload'); michael@0: const { viewFor } = require('../view/core'); michael@0: const { EVENTS } = require('./events'); michael@0: michael@0: const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec'; michael@0: michael@0: const Tab = Class({ michael@0: extends: EventTarget, michael@0: initialize: function initialize(options) { michael@0: options = options.tab ? options : { tab: options }; michael@0: let tab = options.tab; michael@0: michael@0: EventTarget.prototype.initialize.call(this, options); michael@0: let tabInternals = tabNS(this); michael@0: rawTabNS(tab).tab = this; michael@0: michael@0: let window = tabInternals.window = options.window || getOwnerWindow(tab); michael@0: tabInternals.tab = tab; michael@0: michael@0: // TabReady michael@0: let onReady = tabInternals.onReady = onTabReady.bind(this); michael@0: tab.browser.addEventListener(EVENTS.ready.dom, onReady, false); michael@0: michael@0: // TabPageShow michael@0: let onPageShow = tabInternals.onPageShow = onTabPageShow.bind(this); michael@0: tab.browser.addEventListener(EVENTS.pageshow.dom, onPageShow, false); michael@0: michael@0: // TabLoad michael@0: let onLoad = tabInternals.onLoad = onTabLoad.bind(this); michael@0: tab.browser.addEventListener(EVENTS.load.dom, onLoad, true); michael@0: michael@0: // TabClose michael@0: let onClose = tabInternals.onClose = onTabClose.bind(this); michael@0: window.BrowserApp.deck.addEventListener(EVENTS.close.dom, onClose, false); michael@0: michael@0: unload(cleanupTab.bind(null, this)); michael@0: }, michael@0: michael@0: /** michael@0: * The title of the page currently loaded in the tab. michael@0: * Changing this property changes an actual title. michael@0: * @type {String} michael@0: */ michael@0: get title() getTabTitle(tabNS(this).tab), michael@0: set title(title) setTabTitle(tabNS(this).tab, title), michael@0: michael@0: /** michael@0: * Location of the page currently loaded in this tab. michael@0: * Changing this property will loads page under under the specified location. michael@0: * @type {String} michael@0: */ michael@0: get url() { michael@0: return tabNS(this).closed ? undefined : getTabURL(tabNS(this).tab); michael@0: }, michael@0: set url(url) setTabURL(tabNS(this).tab, url), michael@0: michael@0: /** michael@0: * URI of the favicon for the page currently loaded in this tab. michael@0: * @type {String} michael@0: */ michael@0: get favicon() { michael@0: /* michael@0: * Synchronous favicon services were never supported on Fennec, michael@0: * and as of FF22, are now deprecated. When/if favicon services michael@0: * are supported for Fennec, this getter should reference michael@0: * `require('sdk/places/favicon').getFavicon` michael@0: */ michael@0: console.error( michael@0: 'tab.favicon is deprecated, and currently ' + michael@0: 'favicon helpers are not yet supported by Fennec' michael@0: ); michael@0: michael@0: // return 16x16 blank default michael@0: return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAEklEQVQ4jWNgGAWjYBSMAggAAAQQAAF/TXiOAAAAAElFTkSuQmCC'; michael@0: }, michael@0: michael@0: getThumbnail: function() { michael@0: // TODO: implement! michael@0: console.error(ERR_FENNEC_MSG); michael@0: michael@0: // return 80x45 blank default michael@0: return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC'; michael@0: }, michael@0: michael@0: get id() { michael@0: return getTabId(tabNS(this).tab); michael@0: }, michael@0: michael@0: /** michael@0: * The index of the tab relative to other tabs in the application window. michael@0: * Changing this property will change order of the actual position of the tab. michael@0: * @type {Number} michael@0: */ michael@0: get index() { michael@0: if (tabNS(this).closed) return undefined; michael@0: michael@0: let tabs = tabNS(this).window.BrowserApp.tabs; michael@0: let tab = tabNS(this).tab; michael@0: for (var i = tabs.length; i >= 0; i--) { michael@0: if (tabs[i] === tab) michael@0: return i; michael@0: } michael@0: return null; michael@0: }, michael@0: set index(value) { michael@0: console.error(ERR_FENNEC_MSG); // TODO michael@0: }, michael@0: michael@0: /** michael@0: * Whether or not tab is pinned (Is an app-tab). michael@0: * @type {Boolean} michael@0: */ michael@0: get isPinned() { michael@0: console.error(ERR_FENNEC_MSG); // TODO michael@0: return false; // TODO michael@0: }, michael@0: pin: function pin() { michael@0: console.error(ERR_FENNEC_MSG); // TODO michael@0: }, michael@0: unpin: function unpin() { michael@0: console.error(ERR_FENNEC_MSG); // TODO michael@0: }, michael@0: michael@0: /** michael@0: * Returns the MIME type that the document loaded in the tab is being michael@0: * rendered as. michael@0: * @type {String} michael@0: */ michael@0: get contentType() getTabContentType(tabNS(this).tab), michael@0: michael@0: /** michael@0: * Create a worker for this tab, first argument is options given to Worker. michael@0: * @type {Worker} michael@0: */ michael@0: attach: function attach(options) { michael@0: // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946 michael@0: // TODO: fix this circular dependency michael@0: let { Worker } = require('./worker'); michael@0: return Worker(options, getTabContentWindow(tabNS(this).tab)); michael@0: }, michael@0: michael@0: /** michael@0: * Make this tab active. michael@0: */ michael@0: activate: function activate() { michael@0: activateTab(tabNS(this).tab, tabNS(this).window); michael@0: }, michael@0: michael@0: /** michael@0: * Close the tab michael@0: */ michael@0: close: function close(callback) { michael@0: let tab = this; michael@0: this.once(EVENTS.close.name, function () { michael@0: tabNS(tab).closed = true; michael@0: if (callback) callback(); michael@0: }); michael@0: michael@0: closeTab(tabNS(this).tab); michael@0: }, michael@0: michael@0: /** michael@0: * Reload the tab michael@0: */ michael@0: reload: function reload() { michael@0: tabNS(this).tab.browser.reload(); michael@0: } michael@0: }); michael@0: exports.Tab = Tab; michael@0: michael@0: // Implement `viewFor` polymorphic function for the Tab michael@0: // instances. michael@0: viewFor.define(Tab, x => tabNS(x).tab); michael@0: michael@0: function cleanupTab(tab) { michael@0: let tabInternals = tabNS(tab); michael@0: if (!tabInternals.tab) michael@0: return; michael@0: michael@0: if (tabInternals.tab.browser) { michael@0: tabInternals.tab.browser.removeEventListener(EVENTS.ready.dom, tabInternals.onReady, false); michael@0: tabInternals.tab.browser.removeEventListener(EVENTS.pageshow.dom, tabInternals.onPageShow, false); michael@0: tabInternals.tab.browser.removeEventListener(EVENTS.load.dom, tabInternals.onLoad, true); michael@0: } michael@0: tabInternals.onReady = null; michael@0: tabInternals.onPageShow = null; michael@0: tabInternals.onLoad = null; michael@0: tabInternals.window.BrowserApp.deck.removeEventListener(EVENTS.close.dom, tabInternals.onClose, false); michael@0: tabInternals.onClose = null; michael@0: rawTabNS(tabInternals.tab).tab = null; michael@0: tabInternals.tab = null; michael@0: tabInternals.window = null; michael@0: } michael@0: michael@0: function onTabReady(event) { michael@0: let win = event.target.defaultView; michael@0: michael@0: // ignore frames michael@0: if (win === win.top) { michael@0: emit(this, 'ready', this); michael@0: } michael@0: } michael@0: michael@0: function onTabLoad (event) { michael@0: let win = event.target.defaultView; michael@0: michael@0: // ignore frames michael@0: if (win === win.top) { michael@0: emit(this, 'load', this); michael@0: } michael@0: } michael@0: michael@0: function onTabPageShow(event) { michael@0: let win = event.target.defaultView; michael@0: if (win === win.top) michael@0: emit(this, 'pageshow', this, event.persisted); michael@0: } michael@0: michael@0: // TabClose michael@0: function onTabClose(event) { michael@0: let rawTab = getTabForBrowser(event.target); michael@0: if (tabNS(this).tab !== rawTab) michael@0: return; michael@0: michael@0: emit(this, EVENTS.close.name, this); michael@0: cleanupTab(this); michael@0: }; michael@0: michael@0: getPBOwnerWindow.define(Tab, function(tab) { michael@0: return getTabContentWindow(tabNS(tab).tab); michael@0: });