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