forked from mirrors/gecko-dev
Backed out changeset 990a8eb972cd (bug 1439875) Backed out changeset 187aef60922c (bug 1439875) Backed out changeset 9c764daa1234 (bug 1439875) Backed out changeset f8e091577d2a (bug 1439875) Backed out changeset 1ee5a2d21901 (bug 1439875) Backed out changeset 610ba5dfaae1 (bug 1439875)
252 lines
9.4 KiB
JavaScript
252 lines
9.4 KiB
JavaScript
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set sts=2 sw=2 et tw=80: */
|
|
"use strict";
|
|
|
|
// The ext-* files are imported into the same scopes.
|
|
/* import-globals-from ext-browser.js */
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
|
"@mozilla.org/browser/aboutnewtab-service;1",
|
|
"nsIAboutNewTabService");
|
|
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
|
|
var {
|
|
promiseObserved,
|
|
} = ExtensionUtils;
|
|
|
|
const onXULFrameLoaderCreated = ({target}) => {
|
|
target.messageManager.sendAsyncMessage("AllowScriptsToClose", {});
|
|
};
|
|
|
|
this.windows = class extends ExtensionAPI {
|
|
getAPI(context) {
|
|
let {extension} = context;
|
|
|
|
const {windowManager} = extension;
|
|
|
|
return {
|
|
windows: {
|
|
onCreated:
|
|
new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
|
|
fire.async(windowManager.convert(window));
|
|
}).api(),
|
|
|
|
onRemoved:
|
|
new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
|
|
fire.async(windowTracker.getId(window));
|
|
}).api(),
|
|
|
|
onFocusChanged: new EventManager(context, "windows.onFocusChanged", fire => {
|
|
// Keep track of the last windowId used to fire an onFocusChanged event
|
|
let lastOnFocusChangedWindowId;
|
|
|
|
let listener = event => {
|
|
// Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
|
|
// event when switching focus between two Firefox windows.
|
|
Promise.resolve().then(() => {
|
|
let window = Services.focus.activeWindow;
|
|
let windowId = window ? windowTracker.getId(window) : Window.WINDOW_ID_NONE;
|
|
if (windowId !== lastOnFocusChangedWindowId) {
|
|
fire.async(windowId);
|
|
lastOnFocusChangedWindowId = windowId;
|
|
}
|
|
});
|
|
};
|
|
windowTracker.addListener("focus", listener);
|
|
windowTracker.addListener("blur", listener);
|
|
return () => {
|
|
windowTracker.removeListener("focus", listener);
|
|
windowTracker.removeListener("blur", listener);
|
|
};
|
|
}).api(),
|
|
|
|
get: function(windowId, getInfo) {
|
|
let window = windowTracker.getWindow(windowId, context);
|
|
if (!window) {
|
|
return Promise.reject({message: `Invalid window ID: ${windowId}`});
|
|
}
|
|
return Promise.resolve(windowManager.convert(window, getInfo));
|
|
},
|
|
|
|
getCurrent: function(getInfo) {
|
|
let window = context.currentWindow || windowTracker.topWindow;
|
|
return Promise.resolve(windowManager.convert(window, getInfo));
|
|
},
|
|
|
|
getLastFocused: function(getInfo) {
|
|
let window = windowTracker.topWindow;
|
|
return Promise.resolve(windowManager.convert(window, getInfo));
|
|
},
|
|
|
|
getAll: function(getInfo) {
|
|
let doNotCheckTypes = getInfo === null || getInfo.windowTypes === null;
|
|
|
|
function typeFilter(win) {
|
|
return doNotCheckTypes || getInfo.windowTypes.includes(win.type);
|
|
}
|
|
|
|
let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo))
|
|
.filter(typeFilter);
|
|
|
|
return Promise.resolve(windows);
|
|
},
|
|
|
|
create: function(createData) {
|
|
let needResize = (createData.left !== null || createData.top !== null ||
|
|
createData.width !== null || createData.height !== null);
|
|
|
|
if (needResize) {
|
|
if (createData.state !== null && createData.state != "normal") {
|
|
return Promise.reject({message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"`});
|
|
}
|
|
createData.state = "normal";
|
|
}
|
|
|
|
function mkstr(s) {
|
|
let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
|
|
result.data = s;
|
|
return result;
|
|
}
|
|
|
|
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
|
|
|
if (createData.tabId !== null) {
|
|
if (createData.url !== null) {
|
|
return Promise.reject({message: "`tabId` may not be used in conjunction with `url`"});
|
|
}
|
|
|
|
if (createData.allowScriptsToClose) {
|
|
return Promise.reject({message: "`tabId` may not be used in conjunction with `allowScriptsToClose`"});
|
|
}
|
|
|
|
let tab = tabTracker.getTab(createData.tabId);
|
|
|
|
// Private browsing tabs can only be moved to private browsing
|
|
// windows.
|
|
let incognito = PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser);
|
|
if (createData.incognito !== null && createData.incognito != incognito) {
|
|
return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
|
|
}
|
|
if (createData.incognito && !PrivateBrowsingUtils.enabled) {
|
|
return Promise.reject({message: "`incognito` cannot be used if incognito mode is disabled"});
|
|
}
|
|
createData.incognito = incognito;
|
|
|
|
args.appendElement(tab);
|
|
} else if (createData.url !== null) {
|
|
if (Array.isArray(createData.url)) {
|
|
let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
|
for (let url of createData.url) {
|
|
array.appendElement(mkstr(url));
|
|
}
|
|
args.appendElement(array);
|
|
} else {
|
|
args.appendElement(mkstr(createData.url));
|
|
}
|
|
} else {
|
|
args.appendElement(mkstr(aboutNewTabService.newTabURL));
|
|
}
|
|
|
|
let features = ["chrome"];
|
|
|
|
if (createData.type === null || createData.type == "normal") {
|
|
features.push("dialog=no", "all");
|
|
} else {
|
|
// All other types create "popup"-type windows by default.
|
|
features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
|
|
}
|
|
|
|
if (createData.incognito !== null) {
|
|
if (createData.incognito) {
|
|
features.push("private");
|
|
} else {
|
|
features.push("non-private");
|
|
}
|
|
}
|
|
|
|
let {allowScriptsToClose, url} = createData;
|
|
if (allowScriptsToClose === null) {
|
|
allowScriptsToClose = typeof url === "string" && url.startsWith("moz-extension://");
|
|
}
|
|
|
|
let window = Services.ww.openWindow(null, "chrome://browser/content/browser.xul", "_blank",
|
|
features.join(","), args);
|
|
|
|
let win = windowManager.getWrapper(window);
|
|
win.updateGeometry(createData);
|
|
|
|
// TODO: focused, type
|
|
|
|
return new Promise(resolve => {
|
|
window.addEventListener("load", function() {
|
|
resolve(promiseObserved("browser-delayed-startup-finished", win => win == window));
|
|
}, {once: true});
|
|
}).then(() => {
|
|
if (["minimized", "fullscreen", "docked", "normal", "maximized"].includes(createData.state)) {
|
|
win.state = createData.state;
|
|
}
|
|
if (allowScriptsToClose) {
|
|
for (let {linkedBrowser} of window.gBrowser.tabs) {
|
|
onXULFrameLoaderCreated({target: linkedBrowser});
|
|
// eslint-disable-next-line mozilla/balanced-listeners
|
|
linkedBrowser.addEventListener("XULFrameLoaderCreated", onXULFrameLoaderCreated);
|
|
}
|
|
}
|
|
if (createData.titlePreface !== null) {
|
|
win.setTitlePreface(createData.titlePreface);
|
|
}
|
|
return win.convert({populate: true});
|
|
});
|
|
},
|
|
|
|
update: function(windowId, updateInfo) {
|
|
if (updateInfo.state !== null && updateInfo.state != "normal") {
|
|
if (updateInfo.left !== null || updateInfo.top !== null ||
|
|
updateInfo.width !== null || updateInfo.height !== null) {
|
|
return Promise.reject({message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"`});
|
|
}
|
|
}
|
|
|
|
let win = windowManager.get(windowId, context);
|
|
if (updateInfo.focused) {
|
|
Services.focus.activeWindow = win.window;
|
|
}
|
|
|
|
if (updateInfo.state !== null) {
|
|
win.state = updateInfo.state;
|
|
}
|
|
|
|
if (updateInfo.drawAttention) {
|
|
// Bug 1257497 - Firefox can't cancel attention actions.
|
|
win.window.getAttention();
|
|
}
|
|
|
|
win.updateGeometry(updateInfo);
|
|
|
|
if (updateInfo.titlePreface !== null) {
|
|
win.setTitlePreface(updateInfo.titlePreface);
|
|
win.window.gBrowser.updateTitlebar();
|
|
}
|
|
|
|
// TODO: All the other properties, focused=false...
|
|
|
|
return Promise.resolve(win.convert());
|
|
},
|
|
|
|
remove: function(windowId) {
|
|
let window = windowTracker.getWindow(windowId, context);
|
|
window.close();
|
|
|
|
return new Promise(resolve => {
|
|
let listener = () => {
|
|
windowTracker.removeListener("domwindowclosed", listener);
|
|
resolve();
|
|
};
|
|
windowTracker.addListener("domwindowclosed", listener);
|
|
});
|
|
},
|
|
},
|
|
};
|
|
}
|
|
};
|