Files
tubestation/addon-sdk/source/lib/sdk/windows/firefox.js

223 lines
6.0 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 { Class } = require('../core/heritage');
const { observer } = require('./observer');
const { isBrowser, getMostRecentBrowserWindow, windows, open, getInnerId,
getWindowTitle, isFocused, isWindowPrivate } = require('../window/utils');
const { List, addListItem, removeListItem } = require('../util/list');
const { viewFor } = require('../view/core');
const { modelFor } = require('../model/core');
const { emit, emitOnObject, setListeners } = require('../event/core');
const { once } = require('../dom/events');
const { EventTarget } = require('../event/target');
const { getSelectedTab } = require('../tabs/utils');
const { Cc, Ci } = require('chrome');
const { Options } = require('../tabs/common');
const system = require('../system/events');
const { ignoreWindow, isPrivate, isWindowPBSupported } = require('../private-browsing/utils');
const { data, isPrivateBrowsingSupported } = require('../self');
const { setImmediate } = require('../timers');
const supportPrivateWindows = isPrivateBrowsingSupported && isWindowPBSupported;
const modelsFor = new WeakMap();
const viewsFor = new WeakMap();
const Window = Class({
implements: [EventTarget],
initialize: function(domWindow) {
modelsFor.set(domWindow, this);
viewsFor.set(this, domWindow);
},
get title() {
return getWindowTitle(viewsFor.get(this));
},
activate: function() {
viewsFor.get(this).focus();
},
close: function(callback) {
let domWindow = viewsFor.get(this);
if (callback) {
// We want to catch the close event immediately after the close events are
// emitted everywhere but without letting the event loop spin. Registering
// for the same events as windowEventListener but afterwards does this
let listener = (event, closedWin) => {
if (event != "close" || closedWin != domWindow)
return;
observer.off("*", listener);
callback();
}
observer.on("*", listener);
}
domWindow.close();
}
});
const windowTabs = new WeakMap();
const BrowserWindow = Class({
extends: Window,
get tabs() {
let tabs = windowTabs.get(this);
if (tabs)
return tabs;
return new WindowTabs(this);
}
});
const WindowTabs = Class({
implements: [EventTarget],
extends: List,
initialize: function(window) {
List.prototype.initialize.call(this);
windowTabs.set(window, this);
viewsFor.set(this, viewsFor.get(window));
// Make sure the tabs module has loaded and found all existing tabs
const tabs = require('../tabs');
for (let tab of tabs) {
if (tab.window == window)
addListItem(this, tab);
}
},
get activeTab() {
return modelFor(getSelectedTab(viewsFor.get(this)));
},
open: function(options) {
options = Options(options);
let domWindow = viewsFor.get(this);
let { Tab } = require('../tabs/tab-firefox');
// The capturing listener will see the TabOpen event before
// sdk/tabs/observer giving us time to set up the tab and listeners before
// the real open event is fired
let listener = event => {
new Tab(event.target, options);
};
once(domWindow, "TabOpen", listener, true);
domWindow.gBrowser.addTab(options.url);
}
});
const BrowserWindows = Class({
implements: [EventTarget],
extends: List,
initialize: function() {
List.prototype.initialize.call(this);
},
get activeWindow() {
let domWindow = getMostRecentBrowserWindow();
if (ignoreWindow(domWindow))
return null;
return modelsFor.get(domWindow);
},
open: function(options) {
if (typeof options == "string")
options = { url: options };
let { url, isPrivate } = options;
if (url)
url = data.url(url);
let args = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
args.data = url;
let features = {
chrome: true,
all: true,
dialog: false
};
features.private = supportPrivateWindows && isPrivate;
let domWindow = open(null, {
parent: null,
name: "_blank",
features,
args
})
let window = makeNewWindow(domWindow, true);
setListeners(window, options);
return window;
}
});
const browserWindows = new BrowserWindows();
exports.browserWindows = browserWindows;
function windowEmit(window, event, ...args) {
if (window instanceof BrowserWindow && (event == "open" || event == "close"))
emitOnObject(window, event, browserWindows, window, ...args);
else
emit(window, event, window, ...args);
if (window instanceof BrowserWindow)
emit(browserWindows, event, window, ...args);
}
function makeNewWindow(domWindow, browserHint = false) {
if (browserHint || isBrowser(domWindow))
return new BrowserWindow(domWindow);
else
return new Window(domWindow);
}
for (let domWindow of windows()) {
let window = makeNewWindow(domWindow);
if (window instanceof BrowserWindow)
addListItem(browserWindows, window);
}
var windowEventListener = (event, domWindow, ...args) => {
if (ignoreWindow(domWindow))
return;
let window = modelsFor.get(domWindow);
if (!window)
window = makeNewWindow(domWindow);
if (isBrowser(domWindow)) {
if (event == "open")
addListItem(browserWindows, window);
else if (event == "close")
removeListItem(browserWindows, window);
}
windowEmit(window, event, ...args);
// The window object shouldn't be reachable after closed
if (event == "close") {
viewsFor.delete(window);
modelsFor.delete(domWindow);
}
};
observer.on("*", windowEventListener);
viewFor.define(BrowserWindow, window => {
return viewsFor.get(window);
})
const isBrowserWindow = (x) => x instanceof BrowserWindow;
isPrivate.when(isBrowserWindow, (w) => isWindowPrivate(viewsFor.get(w)));
isFocused.when(isBrowserWindow, (w) => isFocused(viewsFor.get(w)));