242 lines
6.6 KiB
JavaScript
242 lines
6.6 KiB
JavaScript
/* 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() 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() 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;
|
|
});
|