merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book
2017-08-08 13:18:14 +02:00
106 changed files with 1910 additions and 1638 deletions

View File

@@ -235,8 +235,8 @@ if (!isDevtools) {
}
const gInterestingCategories = new Set([
"agent-style-sheets", "addon-provider-module", "webextension-scripts",
"webextension-schemas", "webextension-scripts-addon",
"agent-style-sheets", "addon-provider-module", "webextension-modules",
"webextension-scripts", "webextension-schemas", "webextension-scripts-addon",
"webextension-scripts-content", "webextension-scripts-devtools"
]);
@@ -503,13 +503,13 @@ add_task(async function checkAllTheFiles() {
findChromeUrlsFromArray(uint16, "chrome://");
findChromeUrlsFromArray(uint16, "resource://");
const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".html", ".xhtml"];
const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".json", ".html", ".xhtml"];
let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
// our zipreader APIs are all sync)
let uris = await generateURIsFromDirTree(appDir, [".css", ".manifest", ".json", ".jpg", ".png", ".gif", ".svg", ".dtd", ".properties"].concat(kCodeExtensions));
let uris = await generateURIsFromDirTree(appDir, [".css", ".manifest", ".jpg", ".png", ".gif", ".svg", ".dtd", ".properties"].concat(kCodeExtensions));
// Parse and remove all manifests from the list.
// NOTE that this must be done before filtering out devtools paths

View File

@@ -1446,6 +1446,9 @@ var CustomizableUIInternal = {
name = aWidget.id + "." + aProp;
def = aDef || "";
}
if (aWidget.localized === false) {
return def;
}
try {
if (Array.isArray(aFormatArgs) && aFormatArgs.length) {
return gWidgetsBundle.formatStringFromName(name, aFormatArgs,
@@ -2318,6 +2321,7 @@ var CustomizableUIInternal = {
source: aSource || CustomizableUI.SOURCE_EXTERNAL,
instances: new Map(),
currentArea: null,
localized: true,
removable: true,
overflows: true,
defaultArea: null,
@@ -2353,7 +2357,8 @@ var CustomizableUIInternal = {
}
}
const kOptBoolProps = ["removable", "showInPrivateBrowsing", "overflows", "tabSpecific"];
const kOptBoolProps = ["removable", "showInPrivateBrowsing", "overflows", "tabSpecific",
"localized"];
for (let prop of kOptBoolProps) {
if (typeof aData[prop] == "boolean") {
widget[prop] = aData[prop];
@@ -3296,6 +3301,9 @@ this.CustomizableUI = {
* invoked when a user hides your view.
* - tooltiptext: string to use for the tooltip of the widget
* - label: string to use for the label of the widget
* - localized: If true, or undefined, attempt to retrieve the
* widget's string properties from the customizable
* widgets string bundle.
* - removable: whether the widget is removable (optional, default: true)
* NB: if you specify false here, you must provide a
* defaultArea, too.

View File

@@ -80,12 +80,16 @@ const convertBookmarks = result => {
return node;
};
let observer = {
skipTags: true,
skipDescendantsOnItemRemoval: true,
let observer = new class extends EventEmitter {
constructor() {
super();
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
this.skipTags = true;
this.skipDescendantsOnItemRemoval = true;
}
onBeginUpdateBatch() {}
onEndUpdateBatch() {}
onItemAdded(id, parentId, index, itemType, uri, title, dateAdded, guid, parentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
@@ -107,9 +111,9 @@ let observer = {
}
this.emit("created", bookmark);
},
}
onItemVisited() {},
onItemVisited() {}
onItemMoved(id, oldParentId, oldIndex, newParentId, newIndex, itemType, guid, oldParentGuid, newParentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
@@ -123,7 +127,7 @@ let observer = {
oldIndex,
};
this.emit("moved", {guid, info});
},
}
onItemRemoved(id, parentId, index, itemType, uri, guid, parentGuid, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
@@ -141,7 +145,7 @@ let observer = {
}
this.emit("removed", {guid, info: {parentId: parentGuid, index, node}});
},
}
onItemChanged(id, prop, isAnno, val, lastMod, itemType, parentId, guid, parentGuid, oldVal, source) {
if (itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR) {
@@ -159,9 +163,8 @@ let observer = {
}
this.emit("changed", {guid, info});
},
};
EventEmitter.decorate(observer);
}
}();
const decrementListeners = () => {
listenerCount -= 1;

View File

@@ -1,10 +1,27 @@
/* -*- 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-utils.js */
// This file provides some useful code for the |tabs| and |windows|
// modules. All of the code is installed on |global|, which is a scope
// shared among the different ext-*.js scripts.
XPCOMUtils.defineLazyModuleGetter(global, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
/* global EventEmitter:false, TabContext:false, WindowEventManager:false,
makeWidgetId:false, tabTracker:true, windowTracker:true */
/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
var {
ExtensionError,
defineLazyGetter,
} = ExtensionUtils;
let tabTracker;
let windowTracker;
// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
@@ -76,164 +93,796 @@ global.openOptionsPage = (extension) => {
return window.BrowserOpenAddonsMgr(viewId);
};
extensions.registerModules({
bookmarks: {
url: "chrome://browser/content/ext-bookmarks.js",
schema: "chrome://browser/content/schemas/bookmarks.json",
scopes: ["addon_parent"],
paths: [
["bookmarks"],
],
},
browserAction: {
url: "chrome://browser/content/ext-browserAction.js",
schema: "chrome://browser/content/schemas/browser_action.json",
scopes: ["addon_parent"],
manifest: ["browser_action"],
paths: [
["browserAction"],
],
},
browsingData: {
url: "chrome://browser/content/ext-browsingData.js",
schema: "chrome://browser/content/schemas/browsing_data.json",
scopes: ["addon_parent"],
paths: [
["browsingData"],
],
},
chrome_settings_overrides: {
url: "chrome://browser/content/ext-chrome-settings-overrides.js",
scopes: [],
schema: "chrome://browser/content/schemas/chrome_settings_overrides.json",
manifest: ["chrome_settings_overrides"],
},
commands: {
url: "chrome://browser/content/ext-commands.js",
schema: "chrome://browser/content/schemas/commands.json",
scopes: ["addon_parent"],
manifest: ["commands"],
paths: [
["commands"],
],
},
devtools: {
url: "chrome://browser/content/ext-devtools.js",
schema: "chrome://browser/content/schemas/devtools.json",
scopes: ["devtools_parent"],
manifest: ["devtools_page"],
paths: [
["devtools"],
],
},
devtools_inspectedWindow: {
url: "chrome://browser/content/ext-devtools-inspectedWindow.js",
schema: "chrome://browser/content/schemas/devtools_inspected_window.json",
scopes: ["devtools_parent"],
paths: [
["devtools", "inspectedWindow"],
],
},
devtools_network: {
url: "chrome://browser/content/ext-devtools-network.js",
schema: "chrome://browser/content/schemas/devtools_network.json",
scopes: ["devtools_parent"],
paths: [
["devtools", "network"],
],
},
devtools_panels: {
url: "chrome://browser/content/ext-devtools-panels.js",
schema: "chrome://browser/content/schemas/devtools_panels.json",
scopes: ["devtools_parent"],
paths: [
["devtools", "panels"],
],
},
history: {
url: "chrome://browser/content/ext-history.js",
schema: "chrome://browser/content/schemas/history.json",
scopes: ["addon_parent"],
paths: [
["history"],
],
},
// This module supports the "menus" and "contextMenus" namespaces,
// and because of permissions, the module name must differ from both.
menusInternal: {
url: "chrome://browser/content/ext-menus.js",
schema: "chrome://browser/content/schemas/menus.json",
scopes: ["addon_parent"],
paths: [
["menusInternal"],
],
},
omnibox: {
url: "chrome://browser/content/ext-omnibox.js",
schema: "chrome://browser/content/schemas/omnibox.json",
scopes: ["addon_parent"],
manifest: ["omnibox"],
paths: [
["omnibox"],
],
},
pageAction: {
url: "chrome://browser/content/ext-pageAction.js",
schema: "chrome://browser/content/schemas/page_action.json",
scopes: ["addon_parent"],
manifest: ["page_action"],
paths: [
["pageAction"],
],
},
geckoProfiler: {
url: "chrome://browser/content/ext-geckoProfiler.js",
schema: "chrome://browser/content/schemas/geckoProfiler.json",
scopes: ["addon_parent"],
paths: [
["geckoProfiler"],
],
},
sessions: {
url: "chrome://browser/content/ext-sessions.js",
schema: "chrome://browser/content/schemas/sessions.json",
scopes: ["addon_parent"],
paths: [
["sessions"],
],
},
sidebarAction: {
url: "chrome://browser/content/ext-sidebarAction.js",
schema: "chrome://browser/content/schemas/sidebar_action.json",
scopes: ["addon_parent"],
manifest: ["sidebar_action"],
paths: [
["sidebarAction"],
],
},
tabs: {
url: "chrome://browser/content/ext-tabs.js",
schema: "chrome://browser/content/schemas/tabs.json",
scopes: ["addon_parent"],
paths: [
["tabs"],
],
},
urlOverrides: {
url: "chrome://browser/content/ext-url-overrides.js",
schema: "chrome://browser/content/schemas/url_overrides.json",
scopes: ["addon_parent"],
manifest: ["chrome_url_overrides"],
paths: [
["urlOverrides"],
],
},
windows: {
url: "chrome://browser/content/ext-windows.js",
schema: "chrome://browser/content/schemas/windows.json",
scopes: ["addon_parent"],
paths: [
["windows"],
],
},
global.makeWidgetId = id => {
id = id.toLowerCase();
// FIXME: This allows for collisions.
return id.replace(/[^a-z0-9_-]/g, "_");
};
// Manages tab-specific context data, and dispatching tab select events
// across all windows.
global.TabContext = class extends EventEmitter {
constructor(getDefaults, extension) {
super();
this.extension = extension;
this.getDefaults = getDefaults;
this.tabData = new WeakMap();
this.lastLocation = new WeakMap();
windowTracker.addListener("progress", this);
windowTracker.addListener("TabSelect", this);
}
get(nativeTab) {
if (!this.tabData.has(nativeTab)) {
this.tabData.set(nativeTab, this.getDefaults(nativeTab));
}
return this.tabData.get(nativeTab);
}
clear(nativeTab) {
this.tabData.delete(nativeTab);
}
handleEvent(event) {
if (event.type == "TabSelect") {
let nativeTab = event.target;
this.emit("tab-select", nativeTab);
this.emit("location-change", nativeTab);
}
}
onStateChange(browser, webProgress, request, stateFlags, statusCode) {
let flags = Ci.nsIWebProgressListener;
if (!(~stateFlags & (flags.STATE_IS_WINDOW | flags.STATE_START) ||
this.lastLocation.has(browser))) {
this.lastLocation.set(browser, request.URI);
}
}
onLocationChange(browser, webProgress, request, locationURI, flags) {
let gBrowser = browser.ownerGlobal.gBrowser;
let lastLocation = this.lastLocation.get(browser);
if (browser === gBrowser.selectedBrowser &&
!(lastLocation && lastLocation.equalsExceptRef(browser.currentURI))) {
let nativeTab = gBrowser.getTabForBrowser(browser);
this.emit("location-change", nativeTab, true);
}
this.lastLocation.set(browser, browser.currentURI);
}
shutdown() {
windowTracker.removeListener("progress", this);
windowTracker.removeListener("TabSelect", this);
}
};
class WindowTracker extends WindowTrackerBase {
addProgressListener(window, listener) {
window.gBrowser.addTabsProgressListener(listener);
}
removeProgressListener(window, listener) {
window.gBrowser.removeTabsProgressListener(listener);
}
}
/**
* An event manager API provider which listens for a DOM event in any browser
* window, and calls the given listener function whenever an event is received.
* That listener function receives a `fire` object, which it can use to dispatch
* events to the extension, and a DOM event object.
*
* @param {BaseContext} context
* The extension context which the event manager belongs to.
* @param {string} name
* The API name of the event manager, e.g.,"runtime.onMessage".
* @param {string} event
* The name of the DOM event to listen for.
* @param {function} listener
* The listener function to call when a DOM event is received.
*/
global.WindowEventManager = class extends EventManager {
constructor(context, name, event, listener) {
super(context, name, fire => {
let listener2 = listener.bind(null, fire);
windowTracker.addListener(event, listener2);
return () => {
windowTracker.removeListener(event, listener2);
};
});
}
};
class TabTracker extends TabTrackerBase {
constructor() {
super();
this._tabs = new WeakMap();
this._tabIds = new Map();
this._nextId = 1;
this._handleTabDestroyed = this._handleTabDestroyed.bind(this);
}
init() {
if (this.initialized) {
return;
}
this.initialized = true;
this.adoptedTabs = new WeakMap();
this._handleWindowOpen = this._handleWindowOpen.bind(this);
this._handleWindowClose = this._handleWindowClose.bind(this);
windowTracker.addListener("TabClose", this);
windowTracker.addListener("TabOpen", this);
windowTracker.addListener("TabSelect", this);
windowTracker.addOpenListener(this._handleWindowOpen);
windowTracker.addCloseListener(this._handleWindowClose);
/* eslint-disable mozilla/balanced-listeners */
this.on("tab-detached", this._handleTabDestroyed);
this.on("tab-removed", this._handleTabDestroyed);
/* eslint-enable mozilla/balanced-listeners */
}
getId(nativeTab) {
if (this._tabs.has(nativeTab)) {
return this._tabs.get(nativeTab);
}
this.init();
let id = this._nextId++;
this.setId(nativeTab, id);
return id;
}
setId(nativeTab, id) {
this._tabs.set(nativeTab, id);
this._tabIds.set(id, nativeTab);
}
_handleTabDestroyed(event, {nativeTab}) {
let id = this._tabs.get(nativeTab);
if (id) {
this._tabs.delete(nativeTab);
if (this._tabIds.get(id) === nativeTab) {
this._tabIds.delete(id);
}
}
}
/**
* Returns the XUL <tab> element associated with the given tab ID. If no tab
* with the given ID exists, and no default value is provided, an error is
* raised, belonging to the scope of the given context.
*
* @param {integer} tabId
* The ID of the tab to retrieve.
* @param {*} default_
* The value to return if no tab exists with the given ID.
* @returns {Element<tab>}
* A XUL <tab> element.
*/
getTab(tabId, default_ = undefined) {
let nativeTab = this._tabIds.get(tabId);
if (nativeTab) {
return nativeTab;
}
if (default_ !== undefined) {
return default_;
}
throw new ExtensionError(`Invalid tab ID: ${tabId}`);
}
/**
* @param {Event} event
* The DOM Event to handle.
* @private
*/
handleEvent(event) {
let nativeTab = event.target;
switch (event.type) {
case "TabOpen":
let {adoptedTab} = event.detail;
if (adoptedTab) {
this.adoptedTabs.set(adoptedTab, event.target);
// This tab is being created to adopt a tab from a different window.
// Copy the ID from the old tab to the new.
this.setId(nativeTab, this.getId(adoptedTab));
adoptedTab.linkedBrowser.messageManager.sendAsyncMessage("Extension:SetFrameData", {
windowId: windowTracker.getId(nativeTab.ownerGlobal),
});
}
// Save the current tab, since the newly-created tab will likely be
// active by the time the promise below resolves and the event is
// dispatched.
let currentTab = nativeTab.ownerGlobal.gBrowser.selectedTab;
// We need to delay sending this event until the next tick, since the
// tab does not have its final index when the TabOpen event is dispatched.
Promise.resolve().then(() => {
if (event.detail.adoptedTab) {
this.emitAttached(event.originalTarget);
} else {
this.emitCreated(event.originalTarget, currentTab);
}
});
break;
case "TabClose":
let {adoptedBy} = event.detail;
if (adoptedBy) {
// This tab is being closed because it was adopted by a new window.
// Copy its ID to the new tab, in case it was created as the first tab
// of a new window, and did not have an `adoptedTab` detail when it was
// opened.
this.setId(adoptedBy, this.getId(nativeTab));
this.emitDetached(nativeTab, adoptedBy);
} else {
this.emitRemoved(nativeTab, false);
}
break;
case "TabSelect":
// Because we are delaying calling emitCreated above, we also need to
// delay sending this event because it shouldn't fire before onCreated.
Promise.resolve().then(() => {
this.emitActivated(nativeTab);
});
break;
}
}
/**
* A private method which is called whenever a new browser window is opened,
* and dispatches the necessary events for it.
*
* @param {DOMWindow} window
* The window being opened.
* @private
*/
_handleWindowOpen(window) {
if (window.arguments && window.arguments[0] instanceof window.XULElement) {
// If the first window argument is a XUL element, it means the
// window is about to adopt a tab from another window to replace its
// initial tab.
//
// Note that this event handler depends on running before the
// delayed startup code in browser.js, which is currently triggered
// by the first MozAfterPaint event. That code handles finally
// adopting the tab, and clears it from the arguments list in the
// process, so if we run later than it, we're too late.
let nativeTab = window.arguments[0];
let adoptedBy = window.gBrowser.tabs[0];
this.adoptedTabs.set(nativeTab, adoptedBy);
this.setId(adoptedBy, this.getId(nativeTab));
// We need to be sure to fire this event after the onDetached event
// for the original tab.
let listener = (event, details) => {
if (details.nativeTab === nativeTab) {
this.off("tab-detached", listener);
Promise.resolve().then(() => {
this.emitAttached(details.adoptedBy);
});
}
};
this.on("tab-detached", listener);
} else {
for (let nativeTab of window.gBrowser.tabs) {
this.emitCreated(nativeTab);
}
// emitActivated to trigger tab.onActivated/tab.onHighlighted for a newly opened window.
this.emitActivated(window.gBrowser.tabs[0]);
}
}
/**
* A private method which is called whenever a browser window is closed,
* and dispatches the necessary events for it.
*
* @param {DOMWindow} window
* The window being closed.
* @private
*/
_handleWindowClose(window) {
for (let nativeTab of window.gBrowser.tabs) {
if (this.adoptedTabs.has(nativeTab)) {
this.emitDetached(nativeTab, this.adoptedTabs.get(nativeTab));
} else {
this.emitRemoved(nativeTab, true);
}
}
}
/**
* Emits a "tab-activated" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which has been activated.
* @private
*/
emitActivated(nativeTab) {
this.emit("tab-activated", {
tabId: this.getId(nativeTab),
windowId: windowTracker.getId(nativeTab.ownerGlobal)});
}
/**
* Emits a "tab-attached" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element in the window to which the tab is being attached.
* @private
*/
emitAttached(nativeTab) {
let newWindowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
this.emit("tab-attached", {nativeTab, tabId, newWindowId, newPosition: nativeTab._tPos});
}
/**
* Emits a "tab-detached" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element in the window from which the tab is being detached.
* @param {NativeTab} adoptedBy
* The tab element in the window to which detached tab is being moved,
* and will adopt this tab's contents.
* @private
*/
emitDetached(nativeTab, adoptedBy) {
let oldWindowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
this.emit("tab-detached", {nativeTab, adoptedBy, tabId, oldWindowId, oldPosition: nativeTab._tPos});
}
/**
* Emits a "tab-created" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being created.
* @param {NativeTab} [currentTab]
* The tab element for the currently active tab.
* @private
*/
emitCreated(nativeTab, currentTab) {
this.emit("tab-created", {nativeTab, currentTab});
}
/**
* Emits a "tab-removed" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being removed.
* @param {boolean} isWindowClosing
* True if the tab is being removed because the browser window is
* closing.
* @private
*/
emitRemoved(nativeTab, isWindowClosing) {
let windowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
// When addons run in-process, `window.close()` is synchronous. Most other
// addon-invoked calls are asynchronous since they go through a proxy
// context via the message manager. This includes event registrations such
// as `tabs.onRemoved.addListener`.
//
// So, even if `window.close()` were to be called (in-process) after calling
// `tabs.onRemoved.addListener`, then the tab would be closed before the
// event listener is registered. To make sure that the event listener is
// notified, we dispatch `tabs.onRemoved` asynchronously.
Services.tm.dispatchToMainThread(() => {
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
});
}
getBrowserData(browser) {
if (browser.ownerGlobal.location.href === "about:addons") {
// When we're loaded into a <browser> inside about:addons, we need to go up
// one more level.
browser = browser.ownerGlobal.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.chromeEventHandler;
}
let result = {
tabId: -1,
windowId: -1,
};
let {gBrowser} = browser.ownerGlobal;
// Some non-browser windows have gBrowser but not
// getTabForBrowser!
if (gBrowser && gBrowser.getTabForBrowser) {
result.windowId = windowTracker.getId(browser.ownerGlobal);
let nativeTab = gBrowser.getTabForBrowser(browser);
if (nativeTab) {
result.tabId = this.getId(nativeTab);
}
}
return result;
}
get activeTab() {
let window = windowTracker.topWindow;
if (window && window.gBrowser) {
return window.gBrowser.selectedTab;
}
return null;
}
}
windowTracker = new WindowTracker();
tabTracker = new TabTracker();
Object.assign(global, {tabTracker, windowTracker});
class Tab extends TabBase {
get _favIconUrl() {
return this.window.gBrowser.getIcon(this.nativeTab);
}
get audible() {
return this.nativeTab.soundPlaying;
}
get browser() {
return this.nativeTab.linkedBrowser;
}
get frameLoader() {
// If we don't have a frameLoader yet, just return a dummy with no width and
// height.
return super.frameLoader || {lazyWidth: 0, lazyHeight: 0};
}
get cookieStoreId() {
return getCookieStoreIdForTab(this, this.nativeTab);
}
get height() {
return this.frameLoader.lazyHeight;
}
get index() {
return this.nativeTab._tPos;
}
get mutedInfo() {
let {nativeTab} = this;
let mutedInfo = {muted: nativeTab.muted};
if (nativeTab.muteReason === null) {
mutedInfo.reason = "user";
} else if (nativeTab.muteReason) {
mutedInfo.reason = "extension";
mutedInfo.extensionId = nativeTab.muteReason;
}
return mutedInfo;
}
get lastAccessed() {
return this.nativeTab.lastAccessed;
}
get pinned() {
return this.nativeTab.pinned;
}
get active() {
return this.nativeTab.selected;
}
get selected() {
return this.nativeTab.selected;
}
get status() {
if (this.nativeTab.getAttribute("busy") === "true") {
return "loading";
}
return "complete";
}
get width() {
return this.frameLoader.lazyWidth;
}
get window() {
return this.nativeTab.ownerGlobal;
}
get windowId() {
return windowTracker.getId(this.window);
}
/**
* Converts session store data to an object compatible with the return value
* of the convert() method, representing that data.
*
* @param {Extension} extension
* The extension for which to convert the data.
* @param {Object} tabData
* Session store data for a closed tab, as returned by
* `SessionStore.getClosedTabData()`.
* @param {DOMWindow} [window = null]
* The browser window which the tab belonged to before it was closed.
* May be null if the window the tab belonged to no longer exists.
*
* @returns {Object}
* @static
*/
static convertFromSessionStoreClosedData(extension, tabData, window = null) {
let result = {
sessionId: String(tabData.closedId),
index: tabData.pos ? tabData.pos : 0,
windowId: window && windowTracker.getId(window),
highlighted: false,
active: false,
pinned: false,
incognito: Boolean(tabData.state && tabData.state.isPrivate),
lastAccessed: tabData.state ? tabData.state.lastAccessed : tabData.lastAccessed,
};
if (extension.tabManager.hasTabPermission(tabData)) {
let entries = tabData.state ? tabData.state.entries : tabData.entries;
let entry = entries[entries.length - 1];
result.url = entry.url;
result.title = entry.title;
if (tabData.image) {
result.favIconUrl = tabData.image;
}
}
return result;
}
}
class Window extends WindowBase {
/**
* Update the geometry of the browser window.
*
* @param {Object} options
* An object containing new values for the window's geometry.
* @param {integer} [options.left]
* The new pixel distance of the left side of the browser window from
* the left of the screen.
* @param {integer} [options.top]
* The new pixel distance of the top side of the browser window from
* the top of the screen.
* @param {integer} [options.width]
* The new pixel width of the window.
* @param {integer} [options.height]
* The new pixel height of the window.
*/
updateGeometry(options) {
let {window} = this;
if (options.left !== null || options.top !== null) {
let left = options.left !== null ? options.left : window.screenX;
let top = options.top !== null ? options.top : window.screenY;
window.moveTo(left, top);
}
if (options.width !== null || options.height !== null) {
let width = options.width !== null ? options.width : window.outerWidth;
let height = options.height !== null ? options.height : window.outerHeight;
window.resizeTo(width, height);
}
}
get title() {
return this.window.document.title;
}
setTitlePreface(titlePreface) {
this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
}
get focused() {
return this.window.document.hasFocus();
}
get top() {
return this.window.screenY;
}
get left() {
return this.window.screenX;
}
get width() {
return this.window.outerWidth;
}
get height() {
return this.window.outerHeight;
}
get incognito() {
return PrivateBrowsingUtils.isWindowPrivate(this.window);
}
get alwaysOnTop() {
return this.xulWindow.zLevel >= Ci.nsIXULWindow.raisedZ;
}
get isLastFocused() {
return this.window === windowTracker.topWindow;
}
static getState(window) {
const STATES = {
[window.STATE_MAXIMIZED]: "maximized",
[window.STATE_MINIMIZED]: "minimized",
[window.STATE_NORMAL]: "normal",
};
let state = STATES[window.windowState];
if (window.fullScreen) {
state = "fullscreen";
}
return state;
}
get state() {
return Window.getState(this.window);
}
set state(state) {
let {window} = this;
if (state !== "fullscreen" && window.fullScreen) {
window.fullScreen = false;
}
switch (state) {
case "maximized":
window.maximize();
break;
case "minimized":
case "docked":
window.minimize();
break;
case "normal":
// Restore sometimes returns the window to its previous state, rather
// than to the "normal" state, so it may need to be called anywhere from
// zero to two times.
window.restore();
if (window.windowState !== window.STATE_NORMAL) {
window.restore();
}
if (window.windowState !== window.STATE_NORMAL) {
// And on OS-X, where normal vs. maximized is basically a heuristic,
// we need to cheat.
window.sizeToContent();
}
break;
case "fullscreen":
window.fullScreen = true;
break;
default:
throw new Error(`Unexpected window state: ${state}`);
}
}
* getTabs() {
let {tabManager} = this.extension;
for (let nativeTab of this.window.gBrowser.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
/**
* Converts session store data to an object compatible with the return value
* of the convert() method, representing that data.
*
* @param {Extension} extension
* The extension for which to convert the data.
* @param {Object} windowData
* Session store data for a closed window, as returned by
* `SessionStore.getClosedWindowData()`.
*
* @returns {Object}
* @static
*/
static convertFromSessionStoreClosedData(extension, windowData) {
let result = {
sessionId: String(windowData.closedId),
focused: false,
incognito: false,
type: "normal", // this is always "normal" for a closed window
// Surely this does not actually work?
state: this.getState(windowData),
alwaysOnTop: false,
};
if (windowData.tabs.length) {
result.tabs = windowData.tabs.map(tabData => {
return Tab.convertFromSessionStoreClosedData(extension, tabData);
});
}
return result;
}
}
Object.assign(global, {Tab, Window});
class TabManager extends TabManagerBase {
get(tabId, default_ = undefined) {
let nativeTab = tabTracker.getTab(tabId, default_);
if (nativeTab) {
return this.getWrapper(nativeTab);
}
return default_;
}
addActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.addActiveTabPermission(nativeTab);
}
revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(nativeTab);
}
wrapTab(nativeTab) {
return new Tab(this.extension, nativeTab, tabTracker.getId(nativeTab));
}
}
class WindowManager extends WindowManagerBase {
get(windowId, context) {
let window = windowTracker.getWindow(windowId, context);
return this.getWrapper(window);
}
* getAll() {
for (let window of windowTracker.browserWindows()) {
yield this.getWrapper(window);
}
}
wrapWindow(window) {
return new Window(this.extension, window, windowTracker.getId(window));
}
}
extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
defineLazyGetter(extension, "tabManager",
() => new TabManager(extension));
defineLazyGetter(extension, "windowManager",
() => new WindowManager(extension));
});

View File

@@ -0,0 +1,159 @@
{
"bookmarks": {
"url": "chrome://browser/content/ext-bookmarks.js",
"schema": "chrome://browser/content/schemas/bookmarks.json",
"scopes": ["addon_parent"],
"paths": [
["bookmarks"]
]
},
"browserAction": {
"url": "chrome://browser/content/ext-browserAction.js",
"schema": "chrome://browser/content/schemas/browser_action.json",
"scopes": ["addon_parent"],
"manifest": ["browser_action"],
"paths": [
["browserAction"]
]
},
"browsingData": {
"url": "chrome://browser/content/ext-browsingData.js",
"schema": "chrome://browser/content/schemas/browsing_data.json",
"scopes": ["addon_parent"],
"paths": [
["browsingData"]
]
},
"chrome_settings_overrides": {
"url": "chrome://browser/content/ext-chrome-settings-overrides.js",
"scopes": [],
"schema": "chrome://browser/content/schemas/chrome_settings_overrides.json",
"manifest": ["chrome_settings_overrides"]
},
"commands": {
"url": "chrome://browser/content/ext-commands.js",
"schema": "chrome://browser/content/schemas/commands.json",
"scopes": ["addon_parent"],
"manifest": ["commands"],
"paths": [
["commands"]
]
},
"devtools": {
"url": "chrome://browser/content/ext-devtools.js",
"schema": "chrome://browser/content/schemas/devtools.json",
"scopes": ["devtools_parent"],
"manifest": ["devtools_page"],
"paths": [
["devtools"]
]
},
"devtools_inspectedWindow": {
"url": "chrome://browser/content/ext-devtools-inspectedWindow.js",
"schema": "chrome://browser/content/schemas/devtools_inspected_window.json",
"scopes": ["devtools_parent"],
"paths": [
["devtools", "inspectedWindow"]
]
},
"devtools_network": {
"url": "chrome://browser/content/ext-devtools-network.js",
"schema": "chrome://browser/content/schemas/devtools_network.json",
"scopes": ["devtools_parent"],
"paths": [
["devtools", "network"]
]
},
"devtools_panels": {
"url": "chrome://browser/content/ext-devtools-panels.js",
"schema": "chrome://browser/content/schemas/devtools_panels.json",
"scopes": ["devtools_parent"],
"paths": [
["devtools", "panels"]
]
},
"history": {
"url": "chrome://browser/content/ext-history.js",
"schema": "chrome://browser/content/schemas/history.json",
"scopes": ["addon_parent"],
"paths": [
["history"]
]
},
"menusInternal": {
"url": "chrome://browser/content/ext-menus.js",
"schema": "chrome://browser/content/schemas/menus.json",
"scopes": ["addon_parent"],
"paths": [
["menusInternal"]
]
},
"omnibox": {
"url": "chrome://browser/content/ext-omnibox.js",
"schema": "chrome://browser/content/schemas/omnibox.json",
"scopes": ["addon_parent"],
"manifest": ["omnibox"],
"paths": [
["omnibox"]
]
},
"pageAction": {
"url": "chrome://browser/content/ext-pageAction.js",
"schema": "chrome://browser/content/schemas/page_action.json",
"scopes": ["addon_parent"],
"manifest": ["page_action"],
"paths": [
["pageAction"]
]
},
"geckoProfiler": {
"url": "chrome://browser/content/ext-geckoProfiler.js",
"schema": "chrome://browser/content/schemas/geckoProfiler.json",
"scopes": ["addon_parent"],
"paths": [
["geckoProfiler"]
]
},
"sessions": {
"url": "chrome://browser/content/ext-sessions.js",
"schema": "chrome://browser/content/schemas/sessions.json",
"scopes": ["addon_parent"],
"paths": [
["sessions"]
]
},
"sidebarAction": {
"url": "chrome://browser/content/ext-sidebarAction.js",
"schema": "chrome://browser/content/schemas/sidebar_action.json",
"scopes": ["addon_parent"],
"manifest": ["sidebar_action"],
"paths": [
["sidebarAction"]
]
},
"tabs": {
"url": "chrome://browser/content/ext-tabs.js",
"schema": "chrome://browser/content/schemas/tabs.json",
"scopes": ["addon_parent"],
"paths": [
["tabs"]
]
},
"urlOverrides": {
"url": "chrome://browser/content/ext-url-overrides.js",
"schema": "chrome://browser/content/schemas/url_overrides.json",
"scopes": ["addon_parent"],
"manifest": ["chrome_url_overrides"],
"paths": [
["urlOverrides"]
]
},
"windows": {
"url": "chrome://browser/content/ext-windows.js",
"schema": "chrome://browser/content/schemas/windows.json",
"scopes": ["addon_parent"],
"paths": [
["windows"]
]
}
}

View File

@@ -6,7 +6,7 @@
/* global browserActionFor:false, sidebarActionFor:false, pageActionFor:false */
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
@@ -31,6 +31,7 @@ Cu.import("resource://gre/modules/ExtensionParent.jsm");
var {
IconDetails,
StartupCache,
} = ExtensionParent;
const POPUP_PRELOAD_TIMEOUT_MS = 200;
@@ -65,7 +66,7 @@ this.browserAction = class extends ExtensionAPI {
return browserActionMap.get(extension);
}
onManifestEntry(entryName) {
async onManifestEntry(entryName) {
let {extension} = this;
let options = extension.manifest.browser_action;
@@ -88,10 +89,6 @@ this.browserAction = class extends ExtensionAPI {
title: options.default_title || extension.name,
badgeText: "",
badgeBackgroundColor: null,
icon: IconDetails.normalize({
path: options.default_icon,
themeIcons: options.theme_icons,
}, extension),
popup: options.default_popup || "",
area: browserAreas[options.default_area || "navbar"],
};
@@ -102,13 +99,25 @@ this.browserAction = class extends ExtensionAPI {
"or not in your browser_action options.");
}
browserActionMap.set(extension, this);
this.defaults.icon = await StartupCache.get(
extension, ["browserAction", "default_icon"],
() => IconDetails.normalize({
path: options.default_icon,
themeIcons: options.theme_icons,
}, extension));
this.iconData.set(
this.defaults.icon,
await StartupCache.get(
extension, ["browserAction", "default_icon_data"],
() => this.getIconData(this.defaults.icon)));
this.tabContext = new TabContext(tab => Object.create(this.defaults),
extension);
EventEmitter.decorate(this);
this.build();
browserActionMap.set(extension, this);
}
onShutdown(reason) {
@@ -130,6 +139,10 @@ this.browserAction = class extends ExtensionAPI {
tooltiptext: this.defaults.title || "",
defaultArea: this.defaults.area,
// Don't attempt to load properties from the built-in widget string
// bundle.
localized: false,
onBeforeCreated: document => {
let view = document.createElementNS(XUL_NS, "panelview");
view.id = this.viewId;

View File

@@ -5,8 +5,6 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionChildDevToolsUtils",
"resource://gre/modules/ExtensionChildDevToolsUtils.jsm");
@@ -23,7 +21,7 @@ var {
* @param {string} panelOptions.id
* The id of the addon devtools panel registered in the main process.
*/
class ChildDevToolsPanel extends EventEmitter {
class ChildDevToolsPanel extends ExtensionUtils.EventEmitter {
constructor(context, {id}) {
super();

View File

@@ -4,7 +4,7 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
"resource://gre/modules/ExtensionParent.jsm");
@@ -25,7 +25,6 @@ this.commands = class extends ExtensionAPI {
this.keysetsMap = new WeakMap();
this.register();
EventEmitter.decorate(this);
}
onShutdown(reason) {

View File

@@ -4,7 +4,7 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-devtools.js */
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
Cu.import("resource://gre/modules/ExtensionParent.jsm");

View File

@@ -6,7 +6,7 @@
/* global getTargetTabIdForToolbox, getDevToolsTargetForContext */
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
/**
* This module provides helpers used by the other specialized `ext-devtools-*.js` modules

View File

@@ -92,11 +92,11 @@ var _observer;
const getHistoryObserver = () => {
if (!_observer) {
_observer = {
onDeleteURI: function(uri, guid, reason) {
_observer = new class extends EventEmitter {
onDeleteURI(uri, guid, reason) {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
},
onVisit: function(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed, lastKnownTitle) {
}
onVisit(uri, visitId, time, sessionId, referringId, transitionType, guid, hidden, visitCount, typed, lastKnownTitle) {
let data = {
id: guid,
url: uri.spec,
@@ -106,23 +106,22 @@ const getHistoryObserver = () => {
typedCount: typed,
};
this.emit("visited", data);
},
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onTitleChanged: function(uri, title) {
}
onBeginUpdateBatch() {}
onEndUpdateBatch() {}
onTitleChanged(uri, title) {
this.emit("titleChanged", {url: uri.spec, title: title});
},
onClearHistory: function() {
}
onClearHistory() {
this.emit("visitRemoved", {allHistory: true, urls: []});
},
onPageChanged: function() {},
onFrecencyChanged: function() {},
onManyFrecenciesChanged: function() {},
onDeleteVisits: function(uri, time, guid, reason) {
}
onPageChanged() {}
onFrecencyChanged() {}
onManyFrecenciesChanged() {}
onDeleteVisits(uri, time, guid, reason) {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
},
};
EventEmitter.decorate(_observer);
}
}();
PlacesUtils.history.addObserver(_observer);
}
return _observer;

View File

@@ -3,7 +3,7 @@
"use strict";
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
Cu.import("resource://gre/modules/Services.jsm");

View File

@@ -4,7 +4,7 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-browserAction.js */
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
"resource:///modules/ExtensionPopups.jsm");
@@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/ExtensionParent.jsm");
var {
IconDetails,
StartupCache,
} = ExtensionParent;
const popupOpenTimingHistogram = "WEBEXT_PAGEACTION_POPUP_OPEN_MS";
@@ -32,7 +33,7 @@ this.pageAction = class extends ExtensionAPI {
return pageActionMap.get(extension);
}
onManifestEntry(entryName) {
async onManifestEntry(entryName) {
let {extension} = this;
let options = extension.manifest.page_action;
@@ -45,7 +46,6 @@ this.pageAction = class extends ExtensionAPI {
this.defaults = {
show: false,
title: options.default_title || extension.name,
icon: IconDetails.normalize({path: options.default_icon}, extension),
popup: options.default_popup || "",
};
@@ -63,9 +63,17 @@ this.pageAction = class extends ExtensionAPI {
// WeakMap[ChromeWindow -> <xul:image>]
this.buttons = new WeakMap();
EventEmitter.decorate(this);
pageActionMap.set(extension, this);
this.defaults.icon = await StartupCache.get(
extension, ["pageAction", "default_icon"],
() => IconDetails.normalize({path: options.default_icon}, extension));
this.iconData.set(
this.defaults.icon,
await StartupCache.get(
extension, ["pageAction", "default_icon_data"],
() => this.getIconData(this.defaults.icon)));
}
onShutdown(reason) {

View File

@@ -3,7 +3,7 @@
"use strict";
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
var {
ExtensionError,

View File

@@ -3,7 +3,7 @@
"use strict";
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");

View File

@@ -3,7 +3,7 @@
"use strict";
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
const stringSvc = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);

View File

@@ -1,819 +0,0 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
/* exported WindowEventManager, makeWidgetId */
/* global EventEmitter:false, TabContext:false, WindowEventManager:false,
makeWidgetId:false, tabTracker:true, windowTracker:true */
/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/ExtensionTabs.jsm");
var {
ExtensionError,
defineLazyGetter,
} = ExtensionUtils;
let tabTracker;
let windowTracker;
// This file provides some useful code for the |tabs| and |windows|
// modules. All of the code is installed on |global|, which is a scope
// shared among the different ext-*.js scripts.
global.makeWidgetId = id => {
id = id.toLowerCase();
// FIXME: This allows for collisions.
return id.replace(/[^a-z0-9_-]/g, "_");
};
// Manages tab-specific context data, and dispatching tab select events
// across all windows.
global.TabContext = function TabContext(getDefaults, extension) {
this.extension = extension;
this.getDefaults = getDefaults;
this.tabData = new WeakMap();
this.lastLocation = new WeakMap();
windowTracker.addListener("progress", this);
windowTracker.addListener("TabSelect", this);
EventEmitter.decorate(this);
};
TabContext.prototype = {
get(nativeTab) {
if (!this.tabData.has(nativeTab)) {
this.tabData.set(nativeTab, this.getDefaults(nativeTab));
}
return this.tabData.get(nativeTab);
},
clear(nativeTab) {
this.tabData.delete(nativeTab);
},
handleEvent(event) {
if (event.type == "TabSelect") {
let nativeTab = event.target;
this.emit("tab-select", nativeTab);
this.emit("location-change", nativeTab);
}
},
onStateChange(browser, webProgress, request, stateFlags, statusCode) {
let flags = Ci.nsIWebProgressListener;
if (!(~stateFlags & (flags.STATE_IS_WINDOW | flags.STATE_START) ||
this.lastLocation.has(browser))) {
this.lastLocation.set(browser, request.URI);
}
},
onLocationChange(browser, webProgress, request, locationURI, flags) {
let gBrowser = browser.ownerGlobal.gBrowser;
let lastLocation = this.lastLocation.get(browser);
if (browser === gBrowser.selectedBrowser &&
!(lastLocation && lastLocation.equalsExceptRef(browser.currentURI))) {
let nativeTab = gBrowser.getTabForBrowser(browser);
this.emit("location-change", nativeTab, true);
}
this.lastLocation.set(browser, browser.currentURI);
},
shutdown() {
windowTracker.removeListener("progress", this);
windowTracker.removeListener("TabSelect", this);
},
};
class WindowTracker extends WindowTrackerBase {
addProgressListener(window, listener) {
window.gBrowser.addTabsProgressListener(listener);
}
removeProgressListener(window, listener) {
window.gBrowser.removeTabsProgressListener(listener);
}
}
/**
* An event manager API provider which listens for a DOM event in any browser
* window, and calls the given listener function whenever an event is received.
* That listener function receives a `fire` object, which it can use to dispatch
* events to the extension, and a DOM event object.
*
* @param {BaseContext} context
* The extension context which the event manager belongs to.
* @param {string} name
* The API name of the event manager, e.g.,"runtime.onMessage".
* @param {string} event
* The name of the DOM event to listen for.
* @param {function} listener
* The listener function to call when a DOM event is received.
*/
global.WindowEventManager = class extends EventManager {
constructor(context, name, event, listener) {
super(context, name, fire => {
let listener2 = listener.bind(null, fire);
windowTracker.addListener(event, listener2);
return () => {
windowTracker.removeListener(event, listener2);
};
});
}
};
class TabTracker extends TabTrackerBase {
constructor() {
super();
this._tabs = new WeakMap();
this._tabIds = new Map();
this._nextId = 1;
this._handleTabDestroyed = this._handleTabDestroyed.bind(this);
}
init() {
if (this.initialized) {
return;
}
this.initialized = true;
this.adoptedTabs = new WeakMap();
this._handleWindowOpen = this._handleWindowOpen.bind(this);
this._handleWindowClose = this._handleWindowClose.bind(this);
windowTracker.addListener("TabClose", this);
windowTracker.addListener("TabOpen", this);
windowTracker.addListener("TabSelect", this);
windowTracker.addOpenListener(this._handleWindowOpen);
windowTracker.addCloseListener(this._handleWindowClose);
/* eslint-disable mozilla/balanced-listeners */
this.on("tab-detached", this._handleTabDestroyed);
this.on("tab-removed", this._handleTabDestroyed);
/* eslint-enable mozilla/balanced-listeners */
}
getId(nativeTab) {
if (this._tabs.has(nativeTab)) {
return this._tabs.get(nativeTab);
}
this.init();
let id = this._nextId++;
this.setId(nativeTab, id);
return id;
}
setId(nativeTab, id) {
this._tabs.set(nativeTab, id);
this._tabIds.set(id, nativeTab);
}
_handleTabDestroyed(event, {nativeTab}) {
let id = this._tabs.get(nativeTab);
if (id) {
this._tabs.delete(nativeTab);
if (this._tabIds.get(id) === nativeTab) {
this._tabIds.delete(id);
}
}
}
/**
* Returns the XUL <tab> element associated with the given tab ID. If no tab
* with the given ID exists, and no default value is provided, an error is
* raised, belonging to the scope of the given context.
*
* @param {integer} tabId
* The ID of the tab to retrieve.
* @param {*} default_
* The value to return if no tab exists with the given ID.
* @returns {Element<tab>}
* A XUL <tab> element.
*/
getTab(tabId, default_ = undefined) {
let nativeTab = this._tabIds.get(tabId);
if (nativeTab) {
return nativeTab;
}
if (default_ !== undefined) {
return default_;
}
throw new ExtensionError(`Invalid tab ID: ${tabId}`);
}
/**
* @param {Event} event
* The DOM Event to handle.
* @private
*/
handleEvent(event) {
let nativeTab = event.target;
switch (event.type) {
case "TabOpen":
let {adoptedTab} = event.detail;
if (adoptedTab) {
this.adoptedTabs.set(adoptedTab, event.target);
// This tab is being created to adopt a tab from a different window.
// Copy the ID from the old tab to the new.
this.setId(nativeTab, this.getId(adoptedTab));
adoptedTab.linkedBrowser.messageManager.sendAsyncMessage("Extension:SetFrameData", {
windowId: windowTracker.getId(nativeTab.ownerGlobal),
});
}
// Save the current tab, since the newly-created tab will likely be
// active by the time the promise below resolves and the event is
// dispatched.
let currentTab = nativeTab.ownerGlobal.gBrowser.selectedTab;
// We need to delay sending this event until the next tick, since the
// tab does not have its final index when the TabOpen event is dispatched.
Promise.resolve().then(() => {
if (event.detail.adoptedTab) {
this.emitAttached(event.originalTarget);
} else {
this.emitCreated(event.originalTarget, currentTab);
}
});
break;
case "TabClose":
let {adoptedBy} = event.detail;
if (adoptedBy) {
// This tab is being closed because it was adopted by a new window.
// Copy its ID to the new tab, in case it was created as the first tab
// of a new window, and did not have an `adoptedTab` detail when it was
// opened.
this.setId(adoptedBy, this.getId(nativeTab));
this.emitDetached(nativeTab, adoptedBy);
} else {
this.emitRemoved(nativeTab, false);
}
break;
case "TabSelect":
// Because we are delaying calling emitCreated above, we also need to
// delay sending this event because it shouldn't fire before onCreated.
Promise.resolve().then(() => {
this.emitActivated(nativeTab);
});
break;
}
}
/**
* A private method which is called whenever a new browser window is opened,
* and dispatches the necessary events for it.
*
* @param {DOMWindow} window
* The window being opened.
* @private
*/
_handleWindowOpen(window) {
if (window.arguments && window.arguments[0] instanceof window.XULElement) {
// If the first window argument is a XUL element, it means the
// window is about to adopt a tab from another window to replace its
// initial tab.
//
// Note that this event handler depends on running before the
// delayed startup code in browser.js, which is currently triggered
// by the first MozAfterPaint event. That code handles finally
// adopting the tab, and clears it from the arguments list in the
// process, so if we run later than it, we're too late.
let nativeTab = window.arguments[0];
let adoptedBy = window.gBrowser.tabs[0];
this.adoptedTabs.set(nativeTab, adoptedBy);
this.setId(adoptedBy, this.getId(nativeTab));
// We need to be sure to fire this event after the onDetached event
// for the original tab.
let listener = (event, details) => {
if (details.nativeTab === nativeTab) {
this.off("tab-detached", listener);
Promise.resolve().then(() => {
this.emitAttached(details.adoptedBy);
});
}
};
this.on("tab-detached", listener);
} else {
for (let nativeTab of window.gBrowser.tabs) {
this.emitCreated(nativeTab);
}
// emitActivated to trigger tab.onActivated/tab.onHighlighted for a newly opened window.
this.emitActivated(window.gBrowser.tabs[0]);
}
}
/**
* A private method which is called whenever a browser window is closed,
* and dispatches the necessary events for it.
*
* @param {DOMWindow} window
* The window being closed.
* @private
*/
_handleWindowClose(window) {
for (let nativeTab of window.gBrowser.tabs) {
if (this.adoptedTabs.has(nativeTab)) {
this.emitDetached(nativeTab, this.adoptedTabs.get(nativeTab));
} else {
this.emitRemoved(nativeTab, true);
}
}
}
/**
* Emits a "tab-activated" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which has been activated.
* @private
*/
emitActivated(nativeTab) {
this.emit("tab-activated", {
tabId: this.getId(nativeTab),
windowId: windowTracker.getId(nativeTab.ownerGlobal)});
}
/**
* Emits a "tab-attached" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element in the window to which the tab is being attached.
* @private
*/
emitAttached(nativeTab) {
let newWindowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
this.emit("tab-attached", {nativeTab, tabId, newWindowId, newPosition: nativeTab._tPos});
}
/**
* Emits a "tab-detached" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element in the window from which the tab is being detached.
* @param {NativeTab} adoptedBy
* The tab element in the window to which detached tab is being moved,
* and will adopt this tab's contents.
* @private
*/
emitDetached(nativeTab, adoptedBy) {
let oldWindowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
this.emit("tab-detached", {nativeTab, adoptedBy, tabId, oldWindowId, oldPosition: nativeTab._tPos});
}
/**
* Emits a "tab-created" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being created.
* @param {NativeTab} [currentTab]
* The tab element for the currently active tab.
* @private
*/
emitCreated(nativeTab, currentTab) {
this.emit("tab-created", {nativeTab, currentTab});
}
/**
* Emits a "tab-removed" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being removed.
* @param {boolean} isWindowClosing
* True if the tab is being removed because the browser window is
* closing.
* @private
*/
emitRemoved(nativeTab, isWindowClosing) {
let windowId = windowTracker.getId(nativeTab.ownerGlobal);
let tabId = this.getId(nativeTab);
// When addons run in-process, `window.close()` is synchronous. Most other
// addon-invoked calls are asynchronous since they go through a proxy
// context via the message manager. This includes event registrations such
// as `tabs.onRemoved.addListener`.
//
// So, even if `window.close()` were to be called (in-process) after calling
// `tabs.onRemoved.addListener`, then the tab would be closed before the
// event listener is registered. To make sure that the event listener is
// notified, we dispatch `tabs.onRemoved` asynchronously.
Services.tm.dispatchToMainThread(() => {
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
});
}
getBrowserData(browser) {
if (browser.ownerGlobal.location.href === "about:addons") {
// When we're loaded into a <browser> inside about:addons, we need to go up
// one more level.
browser = browser.ownerGlobal.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.chromeEventHandler;
}
let result = {
tabId: -1,
windowId: -1,
};
let {gBrowser} = browser.ownerGlobal;
// Some non-browser windows have gBrowser but not
// getTabForBrowser!
if (gBrowser && gBrowser.getTabForBrowser) {
result.windowId = windowTracker.getId(browser.ownerGlobal);
let nativeTab = gBrowser.getTabForBrowser(browser);
if (nativeTab) {
result.tabId = this.getId(nativeTab);
}
}
return result;
}
get activeTab() {
let window = windowTracker.topWindow;
if (window && window.gBrowser) {
return window.gBrowser.selectedTab;
}
return null;
}
}
windowTracker = new WindowTracker();
tabTracker = new TabTracker();
Object.assign(global, {tabTracker, windowTracker});
class Tab extends TabBase {
get _favIconUrl() {
return this.window.gBrowser.getIcon(this.nativeTab);
}
get audible() {
return this.nativeTab.soundPlaying;
}
get browser() {
return this.nativeTab.linkedBrowser;
}
get frameLoader() {
// If we don't have a frameLoader yet, just return a dummy with no width and
// height.
return super.frameLoader || {lazyWidth: 0, lazyHeight: 0};
}
get cookieStoreId() {
return getCookieStoreIdForTab(this, this.nativeTab);
}
get height() {
return this.frameLoader.lazyHeight;
}
get index() {
return this.nativeTab._tPos;
}
get mutedInfo() {
let {nativeTab} = this;
let mutedInfo = {muted: nativeTab.muted};
if (nativeTab.muteReason === null) {
mutedInfo.reason = "user";
} else if (nativeTab.muteReason) {
mutedInfo.reason = "extension";
mutedInfo.extensionId = nativeTab.muteReason;
}
return mutedInfo;
}
get lastAccessed() {
return this.nativeTab.lastAccessed;
}
get pinned() {
return this.nativeTab.pinned;
}
get active() {
return this.nativeTab.selected;
}
get selected() {
return this.nativeTab.selected;
}
get status() {
if (this.nativeTab.getAttribute("busy") === "true") {
return "loading";
}
return "complete";
}
get width() {
return this.frameLoader.lazyWidth;
}
get window() {
return this.nativeTab.ownerGlobal;
}
get windowId() {
return windowTracker.getId(this.window);
}
/**
* Converts session store data to an object compatible with the return value
* of the convert() method, representing that data.
*
* @param {Extension} extension
* The extension for which to convert the data.
* @param {Object} tabData
* Session store data for a closed tab, as returned by
* `SessionStore.getClosedTabData()`.
* @param {DOMWindow} [window = null]
* The browser window which the tab belonged to before it was closed.
* May be null if the window the tab belonged to no longer exists.
*
* @returns {Object}
* @static
*/
static convertFromSessionStoreClosedData(extension, tabData, window = null) {
let result = {
sessionId: String(tabData.closedId),
index: tabData.pos ? tabData.pos : 0,
windowId: window && windowTracker.getId(window),
highlighted: false,
active: false,
pinned: false,
incognito: Boolean(tabData.state && tabData.state.isPrivate),
lastAccessed: tabData.state ? tabData.state.lastAccessed : tabData.lastAccessed,
};
if (extension.tabManager.hasTabPermission(tabData)) {
let entries = tabData.state ? tabData.state.entries : tabData.entries;
let entry = entries[entries.length - 1];
result.url = entry.url;
result.title = entry.title;
if (tabData.image) {
result.favIconUrl = tabData.image;
}
}
return result;
}
}
class Window extends WindowBase {
/**
* Update the geometry of the browser window.
*
* @param {Object} options
* An object containing new values for the window's geometry.
* @param {integer} [options.left]
* The new pixel distance of the left side of the browser window from
* the left of the screen.
* @param {integer} [options.top]
* The new pixel distance of the top side of the browser window from
* the top of the screen.
* @param {integer} [options.width]
* The new pixel width of the window.
* @param {integer} [options.height]
* The new pixel height of the window.
*/
updateGeometry(options) {
let {window} = this;
if (options.left !== null || options.top !== null) {
let left = options.left !== null ? options.left : window.screenX;
let top = options.top !== null ? options.top : window.screenY;
window.moveTo(left, top);
}
if (options.width !== null || options.height !== null) {
let width = options.width !== null ? options.width : window.outerWidth;
let height = options.height !== null ? options.height : window.outerHeight;
window.resizeTo(width, height);
}
}
get title() {
return this.window.document.title;
}
setTitlePreface(titlePreface) {
this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
}
get focused() {
return this.window.document.hasFocus();
}
get top() {
return this.window.screenY;
}
get left() {
return this.window.screenX;
}
get width() {
return this.window.outerWidth;
}
get height() {
return this.window.outerHeight;
}
get incognito() {
return PrivateBrowsingUtils.isWindowPrivate(this.window);
}
get alwaysOnTop() {
return this.xulWindow.zLevel >= Ci.nsIXULWindow.raisedZ;
}
get isLastFocused() {
return this.window === windowTracker.topWindow;
}
static getState(window) {
const STATES = {
[window.STATE_MAXIMIZED]: "maximized",
[window.STATE_MINIMIZED]: "minimized",
[window.STATE_NORMAL]: "normal",
};
let state = STATES[window.windowState];
if (window.fullScreen) {
state = "fullscreen";
}
return state;
}
get state() {
return Window.getState(this.window);
}
set state(state) {
let {window} = this;
if (state !== "fullscreen" && window.fullScreen) {
window.fullScreen = false;
}
switch (state) {
case "maximized":
window.maximize();
break;
case "minimized":
case "docked":
window.minimize();
break;
case "normal":
// Restore sometimes returns the window to its previous state, rather
// than to the "normal" state, so it may need to be called anywhere from
// zero to two times.
window.restore();
if (window.windowState !== window.STATE_NORMAL) {
window.restore();
}
if (window.windowState !== window.STATE_NORMAL) {
// And on OS-X, where normal vs. maximized is basically a heuristic,
// we need to cheat.
window.sizeToContent();
}
break;
case "fullscreen":
window.fullScreen = true;
break;
default:
throw new Error(`Unexpected window state: ${state}`);
}
}
* getTabs() {
let {tabManager} = this.extension;
for (let nativeTab of this.window.gBrowser.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
/**
* Converts session store data to an object compatible with the return value
* of the convert() method, representing that data.
*
* @param {Extension} extension
* The extension for which to convert the data.
* @param {Object} windowData
* Session store data for a closed window, as returned by
* `SessionStore.getClosedWindowData()`.
*
* @returns {Object}
* @static
*/
static convertFromSessionStoreClosedData(extension, windowData) {
let result = {
sessionId: String(windowData.closedId),
focused: false,
incognito: false,
type: "normal", // this is always "normal" for a closed window
// Surely this does not actually work?
state: this.getState(windowData),
alwaysOnTop: false,
};
if (windowData.tabs.length) {
result.tabs = windowData.tabs.map(tabData => {
return Tab.convertFromSessionStoreClosedData(extension, tabData);
});
}
return result;
}
}
Object.assign(global, {Tab, Window});
class TabManager extends TabManagerBase {
get(tabId, default_ = undefined) {
let nativeTab = tabTracker.getTab(tabId, default_);
if (nativeTab) {
return this.getWrapper(nativeTab);
}
return default_;
}
addActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.addActiveTabPermission(nativeTab);
}
revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(nativeTab);
}
wrapTab(nativeTab) {
return new Tab(this.extension, nativeTab, tabTracker.getId(nativeTab));
}
}
class WindowManager extends WindowManagerBase {
get(windowId, context) {
let window = windowTracker.getWindow(windowId, context);
return this.getWrapper(window);
}
* getAll() {
for (let window of windowTracker.browserWindows()) {
yield this.getWrapper(window);
}
}
wrapWindow(window) {
return new Window(this.extension, window, windowTracker.getId(window));
}
}
extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
defineLazyGetter(extension, "tabManager",
() => new TabManager(extension));
defineLazyGetter(extension, "windowManager",
() => new WindowManager(extension));
});

View File

@@ -3,7 +3,7 @@
"use strict";
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-utils.js */
/* import-globals-from ext-browser.js */
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",

View File

@@ -1,5 +1,6 @@
category webextension-scripts browser chrome://browser/content/ext-browser.js
category webextension-scripts utils chrome://browser/content/ext-utils.js
category webextension-modules browser chrome://browser/content/ext-browser.json
category webextension-scripts c-browser chrome://browser/content/ext-browser.js
category webextension-scripts-devtools browser chrome://browser/content/ext-c-browser.js
category webextension-scripts-addon browser chrome://browser/content/ext-c-browser.js

View File

@@ -14,6 +14,7 @@ browser.jar:
content/browser/extension.svg
content/browser/ext-bookmarks.js
content/browser/ext-browser.js
content/browser/ext-browser.json
content/browser/ext-browserAction.js
content/browser/ext-browsingData.js
content/browser/ext-chrome-settings-overrides.js
@@ -31,7 +32,6 @@ browser.jar:
content/browser/ext-sidebarAction.js
content/browser/ext-tabs.js
content/browser/ext-url-overrides.js
content/browser/ext-utils.js
content/browser/ext-windows.js
content/browser/ext-c-browser.js
content/browser/ext-c-devtools-inspectedWindow.js

View File

@@ -509,10 +509,18 @@ Section "-Application" APP_IDX
${EndIf}
; Update lastwritetime of the Start Menu shortcut to clear the tile cache.
; Do this for both shell contexts in case the user has shortcuts in multiple
; locations, then restore the previous context at the end.
${If} ${AtLeastWin8}
${AndIf} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
FileOpen $0 "$SMPROGRAMS\${BrandFullName}.lnk" a
FileClose $0
SetShellVarContext all
${TouchStartMenuShortcut}
SetShellVarContext current
${TouchStartMenuShortcut}
${If} $TmpVal == "HKLM"
SetShellVarContext all
${ElseIf} $TmpVal == "HKCU"
SetShellVarContext current
${EndIf}
${EndIf}
${If} $AddDesktopSC == 1

View File

@@ -71,10 +71,18 @@
${MigrateStartMenuShortcut}
; Update lastwritetime of the Start Menu shortcut to clear the tile cache.
; Do this for both shell contexts in case the user has shortcuts in multiple
; locations, then restore the previous context at the end.
${If} ${AtLeastWin8}
${AndIf} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
FileOpen $0 "$SMPROGRAMS\${BrandFullName}.lnk" a
FileClose $0
SetShellVarContext all
${TouchStartMenuShortcut}
SetShellVarContext current
${TouchStartMenuShortcut}
${If} $TmpVal == "HKLM"
SetShellVarContext all
${ElseIf} $TmpVal == "HKCU"
SetShellVarContext current
${EndIf}
${EndIf}
; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
@@ -152,6 +160,22 @@
!macroend
!define PostUpdate "!insertmacro PostUpdate"
; Update the last modified time on the Start Menu shortcut, so that its icon
; gets refreshed. Should be called on Win8+ after MigrateStartMenuShortcut.
!macro TouchStartMenuShortcut
${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
FileOpen $0 "$SMPROGRAMS\${BrandFullName}.lnk" a
${IfNot} ${Errors}
System::Call '*(i, i) p .r1'
System::Call 'kernel32::GetSystemTimeAsFileTime(p r1)'
System::Call 'kernel32::SetFileTime(p r0, i 0, i 0, p r1) i .r2'
System::Free $1
FileClose $0
${EndIf}
${EndIf}
!macroend
!define TouchStartMenuShortcut "!insertmacro TouchStartMenuShortcut"
!macro SetAsDefaultAppGlobal
${RemoveDeprecatedKeys} ; Does not use SHCTX

View File

@@ -20,7 +20,7 @@ gyp_vars.update({
'use_official_google_api_keys': 0,
'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0,
'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0,
'include_alsa_audio': 0,
'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0,
'include_pulse_audio': 1 if CONFIG['MOZ_PULSEAUDIO'] else 0,
# basic stuff for everything
'include_internal_video_render': 0,

View File

@@ -10,7 +10,7 @@
* (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*
* [and in turn extracted from "sdk/lang/functional/concurrent.js"]
* [and in turn extracted from the SDK's "lang/functional/concurrent.js"]
*/
exports.debounce = function (fn, wait) {
let timeout, args, context, timestamp, result;

View File

@@ -47,10 +47,18 @@ enum {
// bit is set, and if so it indicates whether we're only whitespace or
// not.
NS_TEXT_IS_ONLY_WHITESPACE = DATA_NODE_FLAG_BIT(3),
// This bit is set if there is a NewlineProperty attached to the node
// (used by nsTextFrame).
NS_HAS_NEWLINE_PROPERTY = DATA_NODE_FLAG_BIT(4),
// This bit is set if there is a FlowLengthProperty attached to the node
// (used by nsTextFrame).
NS_HAS_FLOWLENGTH_PROPERTY = DATA_NODE_FLAG_BIT(5),
};
// Make sure we have enough space for those bits
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 4);
ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 6);
#undef DATA_NODE_FLAG_BIT

View File

@@ -34,8 +34,7 @@ const int32_t kTimeBetweenChecks = 45; /* seconds */
nsWindowMemoryReporter::nsWindowMemoryReporter()
: mLastCheckForGhostWindows(TimeStamp::NowLoRes()),
mCycleCollectorIsRunning(false),
mCheckTimerWaitingForCCEnd(false),
mGhostWindowCount(0)
mCheckTimerWaitingForCCEnd(false)
{
}
@@ -120,7 +119,8 @@ nsWindowMemoryReporter::Init()
/* weakRef = */ true);
}
RegisterGhostWindowsDistinguishedAmount(GhostWindowsDistinguishedAmount);
RegisterStrongMemoryReporter(new GhostWindowsReporter());
RegisterGhostWindowsDistinguishedAmount(GhostWindowsReporter::DistinguishedAmount);
}
/* static */ nsWindowMemoryReporter*
@@ -506,17 +506,6 @@ nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
aData);
}
MOZ_COLLECT_REPORT(
"ghost-windows", KIND_OTHER, UNITS_COUNT, ghostWindows.Count(),
"The number of ghost windows present (the number of nodes underneath "
"explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost "
"window is not shown in any tab, does not share a domain with any non-detached "
"windows, and has met these criteria for at least "
"memory.ghost_window_timeout_seconds, or has survived a round of "
"about:memory's minimize memory usage button.\n\n"
"Ghost windows can happen legitimately, but they are often indicative of "
"leaks in the browser or add-ons.");
WindowPaths windowPaths;
WindowPaths topWindowPaths;
@@ -761,7 +750,6 @@ nsWindowMemoryReporter::CheckForGhostWindows(
KillCheckTimer();
nsTHashtable<nsCStringHashKey> nonDetachedWindowDomains;
nsDataHashtable<nsISupportsHashKey, nsCString> domainMap;
// Populate nonDetachedWindowDomains.
for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
@@ -776,13 +764,8 @@ nsWindowMemoryReporter::CheckForGhostWindows(
nsCOMPtr<nsIURI> uri = GetWindowURI(window);
nsAutoCString domain;
if (uri) {
domain = domainMap.LookupForAdd(uri).OrInsert([&]() {
nsCString d;
tldService->GetBaseDomain(uri, 0, d);
return d;
});
tldService->GetBaseDomain(uri, 0, domain);
}
nonDetachedWindowDomains.PutEntry(domain);
}
@@ -790,7 +773,6 @@ nsWindowMemoryReporter::CheckForGhostWindows(
// if it's not null.
uint32_t ghostTimeout = GetGhostTimeout();
TimeStamp now = mLastCheckForGhostWindows;
mGhostWindowCount = 0;
for (auto iter = mDetachedWindows.Iter(); !iter.Done(); iter.Next()) {
nsWeakPtr weakKey = do_QueryInterface(iter.Key());
nsCOMPtr<mozIDOMWindow> iwindow = do_QueryReferent(weakKey);
@@ -841,7 +823,6 @@ nsWindowMemoryReporter::CheckForGhostWindows(
} else if ((now - timeStamp).ToSeconds() > ghostTimeout) {
// This definitely is a ghost window, so add it to aOutGhostIDs, if
// that is not null.
mGhostWindowCount++;
if (aOutGhostIDs && window) {
aOutGhostIDs->PutEntry(window->WindowID());
}
@@ -850,10 +831,15 @@ nsWindowMemoryReporter::CheckForGhostWindows(
}
}
NS_IMPL_ISUPPORTS(nsWindowMemoryReporter::GhostWindowsReporter,
nsIMemoryReporter)
/* static */ int64_t
nsWindowMemoryReporter::GhostWindowsDistinguishedAmount()
nsWindowMemoryReporter::GhostWindowsReporter::DistinguishedAmount()
{
return sWindowReporter->mGhostWindowCount;
nsTHashtable<nsUint64HashKey> ghostWindows;
sWindowReporter->CheckForGhostWindows(&ghostWindows);
return ghostWindows.Count();
}
void

View File

@@ -164,11 +164,40 @@ public:
static nsWindowMemoryReporter* Get();
void ObserveDOMWindowDetached(nsGlobalWindow* aWindow);
static int64_t GhostWindowsDistinguishedAmount();
private:
~nsWindowMemoryReporter();
/**
* nsGhostWindowReporter generates the "ghost-windows" report, which counts
* the number of ghost windows present.
*/
class GhostWindowsReporter final : public nsIMemoryReporter
{
~GhostWindowsReporter() {}
public:
NS_DECL_ISUPPORTS
static int64_t DistinguishedAmount();
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override
{
MOZ_COLLECT_REPORT(
"ghost-windows", KIND_OTHER, UNITS_COUNT, DistinguishedAmount(),
"The number of ghost windows present (the number of nodes underneath "
"explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost "
"window is not shown in any tab, does not share a domain with any non-detached "
"windows, and has met these criteria for at least "
"memory.ghost_window_timeout_seconds, or has survived a round of "
"about:memory's minimize memory usage button.\n\n"
"Ghost windows can happen legitimately, but they are often indicative of "
"leaks in the browser or add-ons.");
return NS_OK;
}
};
// Protect ctor, use Init() instead.
nsWindowMemoryReporter();
@@ -231,8 +260,6 @@ private:
bool mCycleCollectorIsRunning;
bool mCheckTimerWaitingForCCEnd;
int64_t mGhostWindowCount;
};
#endif // nsWindowMemoryReporter_h__

View File

@@ -138,7 +138,7 @@ function runTest() {
win.focus();
}, 0);
}, {once: true});
iframe.src = "data:text/html,";
iframe.srcdoc = "foo";
}, SimpleTest.finish);
}

View File

@@ -823,6 +823,9 @@ public:
const RefPtr<UnscaledFont>& GetUnscaledFont() const { return mUnscaledFont; }
virtual cairo_scaled_font_t* GetCairoScaledFont() { return nullptr; }
virtual void SetCairoScaledFont(cairo_scaled_font_t* font) {}
protected:
explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
: mUnscaledFont(aUnscaledFont)

View File

@@ -48,8 +48,8 @@ public:
#ifdef USE_CAIRO_SCALED_FONT
bool PopulateCairoScaledFont();
cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
void SetCairoScaledFont(cairo_scaled_font_t* font);
virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
virtual void SetCairoScaledFont(cairo_scaled_font_t* font);
#endif
protected:

View File

@@ -262,12 +262,6 @@ gfxAndroidPlatform::GetFTLibrary()
return gPlatformFTLibrary;
}
already_AddRefed<ScaledFont>
gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
}
bool
gfxAndroidPlatform::FontHintingEnabled()
{

View File

@@ -33,9 +33,6 @@ public:
virtual gfxImageFormat GetOffscreenFormat() override { return mOffscreenFormat; }
already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
// to support IPC font list (sharing between chrome and content)
void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);

View File

@@ -460,7 +460,7 @@ gfxDWriteFont::GetSpaceGlyph()
bool
gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
{
cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
cairo_scaled_font_t *scaledFont = InitCairoScaledFont();
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to
// the cairo_t, precluding any further drawing.
@@ -497,7 +497,7 @@ gfxDWriteFont::CairoFontFace()
cairo_scaled_font_t *
gfxDWriteFont::GetCairoScaledFont()
gfxDWriteFont::InitCairoScaledFont()
{
if (!mScaledFont) {
cairo_matrix_t sizeMatrix;
@@ -679,31 +679,19 @@ gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
already_AddRefed<ScaledFont>
gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
{
bool wantCairo = aTarget->GetBackendType() == BackendType::CAIRO;
if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) {
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
NativeFont nativeFont;
nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
nativeFont.mFont = GetFontFace();
if (wantCairo) {
mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
GetUnscaledFont(),
GetAdjustedSize(),
GetCairoScaledFont());
} else {
if (!mAzureScaledFont) {
gfxDWriteFontEntry *fe =
static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
bool useEmbeddedBitmap =
fe->IsCJKFont() &&
HasBitmapStrikeForSize(NS_lround(mAdjustedSize));
bool forceGDI = GetForceGDIClassic();
IDWriteRenderingParams* params = gfxWindowsPlatform::GetPlatform()->GetRenderingParams(
mUseClearType ?
(forceGDI ?
gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL) :
gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC :
gfxWindowsPlatform::TEXT_RENDERING_NORMAL) :
gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE);
const gfxFontStyle* fontStyle = GetStyle();
@@ -716,9 +704,20 @@ gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
params,
params->GetGamma(),
params->GetEnhancedContrast());
if (!mAzureScaledFont) {
return nullptr;
}
}
mAzureScaledFontIsCairo = wantCairo;
if (aTarget->GetBackendType() == BackendType::CAIRO) {
if (!mAzureScaledFont->GetCairoScaledFont()) {
cairo_scaled_font_t* cairoScaledFont = InitCairoScaledFont();
if (!cairoScaledFont) {
return nullptr;
}
mAzureScaledFont->SetCairoScaledFont(cairoScaledFont);
}
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();

View File

@@ -71,9 +71,9 @@ public:
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFont(mozilla::gfx::DrawTarget *aTarget) override;
virtual cairo_scaled_font_t *GetCairoScaledFont() override;
protected:
cairo_scaled_font_t *InitCairoScaledFont();
virtual const Metrics& GetHorizontalMetrics() override;
bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);
@@ -105,7 +105,6 @@ protected:
bool mNeedsBold;
bool mUseSubpixelPositions;
bool mAllowManualShowGlyphs;
bool mAzureScaledFontIsCairo;
static bool mUseClearType;
};

View File

@@ -45,7 +45,7 @@ gfxFT2FontBase::GetGlyph(uint32_t aCharCode)
// lightweight cache, which is stored on the cairo_font_face_t.
cairo_font_face_t *face =
cairo_scaled_font_get_font_face(CairoScaledFont());
cairo_scaled_font_get_font_face(GetCairoScaledFont());
if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
return 0;
@@ -113,7 +113,7 @@ gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents)
// so caching only the advance could allow many requests to be cached with
// little memory use. Ideally this cache would be merged with
// gfxGlyphExtents.
cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
cairo_scaled_font_glyph_extents(GetCairoScaledFont(), glyphs, 1, aExtents);
}
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
@@ -486,7 +486,7 @@ gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
// for the target can be different from the scaled_font passed to
// cairo_set_scaled_font. (Unfortunately we have measured only for an
// identity ctm.)
cairo_scaled_font_t *cairoFont = CairoScaledFont();
cairo_scaled_font_t *cairoFont = GetCairoScaledFont();
if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to

View File

@@ -31,7 +31,6 @@ public:
virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget,
uint16_t aGID) override;
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
virtual FontType GetType() const override { return FONT_TYPE_FT2; }

View File

@@ -34,6 +34,9 @@
#include "mozilla/Preferences.h"
#include "mozilla/gfx/2D.h"
using namespace mozilla;
using namespace mozilla::gfx;
/**
* gfxFT2Font
*/
@@ -157,7 +160,7 @@ gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
}
}
gfxFT2Font::gfxFT2Font(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
gfxFT2Font::gfxFT2Font(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
cairo_scaled_font_t *aCairoFont,
FT2FontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
@@ -173,6 +176,23 @@ gfxFT2Font::~gfxFT2Font()
{
}
already_AddRefed<ScaledFont>
gfxFT2Font::GetScaledFont(DrawTarget *aTarget)
{
if (!mAzureScaledFont) {
NativeFont nativeFont;
nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
nativeFont.mFont = GetCairoScaledFont();
mAzureScaledFont =
Factory::CreateScaledFontForNativeFont(nativeFont,
GetUnscaledFont(),
GetAdjustedSize());
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
void
gfxFT2Font::FillGlyphDataForChar(FT_Face face, uint32_t ch, CachedGlyphData *gd)
{
@@ -208,7 +228,7 @@ gfxFT2Font::FillGlyphDataForChar(FT_Face face, uint32_t ch, CachedGlyphData *gd)
}
void
gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
gfxFT2Font::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
@@ -217,7 +237,7 @@ gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
}
void
gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
gfxFT2Font::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const
{
aSizes->mFontInstances += aMallocSizeOf(this);

View File

@@ -28,6 +28,9 @@ public: // new functions
FT2FontEntry *GetFontEntry();
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFont(DrawTarget *aTarget) override;
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const override;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,

View File

@@ -31,12 +31,12 @@ class gfxFT2LockedFace {
public:
explicit gfxFT2LockedFace(gfxFT2FontBase *aFont) :
mGfxFont(aFont),
mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
mFace(cairo_ft_scaled_font_lock_face(aFont->GetCairoScaledFont()))
{ }
~gfxFT2LockedFace()
{
if (mFace) {
cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
cairo_ft_scaled_font_unlock_face(mGfxFont->GetCairoScaledFont());
}
}

View File

@@ -55,6 +55,20 @@ using namespace mozilla::unicode;
gfxPlatform::GetLog(eGfxLog_cmapdata), \
LogLevel::Debug)
template <>
class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
{
public:
static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
};
template <>
class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
{
public:
static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
};
static const FcChar8*
ToFcChar8Ptr(const char* aStr)
{
@@ -1181,8 +1195,9 @@ gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnsc
gfxFloat aAdjustedSize,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
bool aNeedsBold) :
gfxFontconfigFontBase(aUnscaledFont, aScaledFont, aPattern, aFontEntry, aFontStyle)
bool aNeedsBold)
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
, mPattern(aPattern)
{
mAdjustedSize = aAdjustedSize;
}
@@ -1191,6 +1206,21 @@ gfxFontconfigFont::~gfxFontconfigFont()
{
}
already_AddRefed<ScaledFont>
gfxFontconfigFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
{
if (!mAzureScaledFont) {
mAzureScaledFont =
Factory::CreateScaledFontForFontconfigFont(GetCairoScaledFont(),
GetPattern(),
GetUnscaledFont(),
GetAdjustedSize());
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
gfxFcPlatformFontList::gfxFcPlatformFontList()
: mLocalNames(64)
, mGenericMappings(32)

View File

@@ -11,6 +11,7 @@
#include "gfxFT2FontBase.h"
#include "gfxPlatformFontList.h"
#include "mozilla/mozalloc.h"
#include "nsAutoRef.h"
#include "nsClassHashtable.h"
#include <fontconfig/fontconfig.h>
@@ -20,13 +21,12 @@
#include <cairo.h>
#include <cairo-ft.h>
#include "gfxFontconfigUtils.h" // xxx - only for nsAutoRefTraits<FcPattern>, etc.
template <>
class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
{
public:
static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
};
template <>
@@ -209,7 +209,7 @@ protected:
bool mForceScalable;
};
class gfxFontconfigFont : public gfxFontconfigFontBase {
class gfxFontconfigFont : public gfxFT2FontBase {
public:
gfxFontconfigFont(const RefPtr<mozilla::gfx::UnscaledFontFontconfig> &aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
@@ -219,8 +219,16 @@ public:
const gfxFontStyle *aFontStyle,
bool aNeedsBold);
protected:
virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }
virtual FcPattern *GetPattern() const { return mPattern; }
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFont(DrawTarget *aTarget) override;
private:
virtual ~gfxFontconfigFont();
nsCountedRef<FcPattern> mPattern;
};
class gfxFcPlatformFontList : public gfxPlatformFontList {

View File

@@ -1446,7 +1446,7 @@ public:
const nsString& GetName() const { return mFontEntry->Name(); }
const gfxFontStyle *GetStyle() const { return &mStyle; }
virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
virtual mozilla::UniquePtr<gfxFont>
CopyWithAntialiasOption(AntialiasOption anAAOption) {
@@ -1831,10 +1831,7 @@ public:
return mUnscaledFont;
}
virtual already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget)
{
return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this);
}
virtual already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget) = 0;
bool KerningDisabled() {
return mKerningSet && !mKerningEnabled;

View File

@@ -1,56 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
#ifndef GFX_FONTCONFIG_UTILS_H
#define GFX_FONTCONFIG_UTILS_H
#include "gfxPlatform.h"
#include "nsAutoRef.h"
#include "gfxFT2FontBase.h"
#include <fontconfig/fontconfig.h>
template <>
class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
{
public:
static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
};
template <>
class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
{
public:
static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
};
template <>
class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
{
public:
static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
};
class gfxFontconfigFontBase : public gfxFT2FontBase {
public:
gfxFontconfigFontBase(const RefPtr<mozilla::gfx::UnscaledFontFontconfig>& aUnscaledFont,
cairo_scaled_font_t *aScaledFont,
FcPattern *aPattern,
gfxFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle)
: gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
, mPattern(aPattern) { }
virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }
virtual FcPattern *GetPattern() const { return mPattern; }
private:
nsCountedRef<FcPattern> mPattern;
};
#endif /* GFX_FONTCONFIG_UTILS_H */

View File

@@ -22,6 +22,7 @@
#define ROUND(x) floor((x) + 0.5)
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
static inline cairo_antialias_t
@@ -135,6 +136,27 @@ gfxGDIFont::SetupCairoFont(DrawTarget* aDrawTarget)
return true;
}
already_AddRefed<ScaledFont>
gfxGDIFont::GetScaledFont(DrawTarget *aTarget)
{
if (!mAzureScaledFont) {
NativeFont nativeFont;
nativeFont.mType = NativeFontType::GDI_FONT_FACE;
LOGFONT lf;
GetObject(GetHFONT(), sizeof(LOGFONT), &lf);
nativeFont.mFont = &lf;
mAzureScaledFont =
Factory::CreateScaledFontWithCairo(nativeFont,
GetUnscaledFont(),
GetAdjustedSize(),
GetCairoScaledFont());
}
RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
return scaledFont.forget();
}
gfxFont::RunMetrics
gfxGDIFont::Measure(const gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,

View File

@@ -29,13 +29,15 @@ public:
HFONT GetHFONT() { return mFont; }
cairo_font_face_t* CairoFontFace() { return mFontFace; }
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; }
/* overrides for the pure virtual methods in gfxFont */
virtual uint32_t GetSpaceGlyph() override;
virtual bool SetupCairoFont(DrawTarget* aDrawTarget) override;
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFont(DrawTarget *aTarget) override;
/* override Measure to add padding for antialiasing */
virtual RunMetrics Measure(const gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,

View File

@@ -1276,17 +1276,6 @@ gfxPlatform::GetWrappedDataSourceSurface(gfxASurface* aSurface)
return result.forget();
}
already_AddRefed<ScaledFont>
gfxPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
NativeFont nativeFont;
nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
nativeFont.mFont = aFont->GetCairoScaledFont();
return Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize());
}
void
gfxPlatform::ComputeTileSize()
{
@@ -2579,17 +2568,6 @@ gfxPlatform::DisableBufferRotation()
sBufferRotationCheckPref = false;
}
already_AddRefed<ScaledFont>
gfxPlatform::GetScaledFontForFontWithCairoSkia(DrawTarget* aTarget, gfxFont* aFont)
{
NativeFont nativeFont;
nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
nativeFont.mFont = aFont->GetCairoScaledFont();
return Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize());
}
/* static */ bool
gfxPlatform::UsesOffMainThreadCompositing()
{

View File

@@ -239,9 +239,6 @@ public:
static already_AddRefed<DataSourceSurface>
GetWrappedDataSourceSurface(gfxASurface *aSurface);
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont);
already_AddRefed<DrawTarget>
CreateOffscreenContentDrawTarget(const mozilla::gfx::IntSize& aSize, mozilla::gfx::SurfaceFormat aFormat);
@@ -780,9 +777,6 @@ protected:
*/
static mozilla::gfx::BackendType BackendTypeForName(const nsCString& aName);
static already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFontWithCairoSkia(mozilla::gfx::DrawTarget* aTarget, gfxFont* aFont);
virtual bool CanUseHardwareVideoDecoding();
int8_t mAllowDownloadableFonts;

View File

@@ -13,7 +13,6 @@
#include "nsUnicodeProperties.h"
#include "gfx2DGlue.h"
#include "gfxFcPlatformFontList.h"
#include "gfxFontconfigUtils.h"
#include "gfxFontconfigFonts.h"
#include "gfxConfig.h"
#include "gfxContext.h"
@@ -568,20 +567,6 @@ gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target)
}
#endif
already_AddRefed<ScaledFont>
gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
return Factory::CreateScaledFontForFontconfigFont(
fcFont->GetCairoScaledFont(),
fcFont->GetPattern(),
fcFont->GetUnscaledFont(),
fcFont->GetAdjustedSize());
}
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
}
#ifdef GL_PROVIDER_GLX
class GLXVsyncSource final : public VsyncSource

View File

@@ -22,8 +22,6 @@ struct _XDisplay;
typedef struct _XDisplay Display;
#endif // MOZ_X11
class gfxFontconfigUtils;
class gfxPlatformGtk : public gfxPlatform {
public:
gfxPlatformGtk();
@@ -37,9 +35,6 @@ public:
CreateOffscreenSurface(const IntSize& aSize,
gfxImageFormat aFormat) override;
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
virtual nsresult GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts) override;
@@ -138,8 +133,6 @@ public:
#endif // MOZ_X11
protected:
static gfxFontconfigUtils *sFontconfigUtils;
int8_t mMaxGenericSubstitutions;
private:

View File

@@ -133,13 +133,6 @@ gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize,
return newSurface.forget();
}
already_AddRefed<ScaledFont>
gfxPlatformMac::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
gfxMacFont *font = static_cast<gfxMacFont*>(aFont);
return font->GetScaledFont(aTarget);
}
gfxFontGroup *
gfxPlatformMac::CreateFontGroup(const FontFamilyList& aFontFamilyList,
const gfxFontStyle *aStyle,

View File

@@ -30,9 +30,6 @@ public:
CreateOffscreenSurface(const IntSize& aSize,
gfxImageFormat aFormat) override;
already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
gfxFontGroup*
CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
const gfxFontStyle *aStyle,

View File

@@ -554,34 +554,6 @@ gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize,
return surf.forget();
}
already_AddRefed<ScaledFont>
gfxWindowsPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
{
if (aFont->GetType() == gfxFont::FONT_TYPE_DWRITE) {
return aFont->GetScaledFont(aTarget);
}
NS_ASSERTION(aFont->GetType() == gfxFont::FONT_TYPE_GDI,
"Fonts on windows should be GDI or DWrite!");
NativeFont nativeFont;
nativeFont.mType = NativeFontType::GDI_FONT_FACE;
LOGFONT lf;
GetObject(static_cast<gfxGDIFont*>(aFont)->GetHFONT(), sizeof(LOGFONT), &lf);
nativeFont.mFont = &lf;
if (aTarget->GetBackendType() == BackendType::CAIRO) {
return Factory::CreateScaledFontWithCairo(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize(),
aFont->GetCairoScaledFont());
}
return Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetUnscaledFont(),
aFont->GetAdjustedSize());
}
static const char kFontAparajita[] = "Aparajita";
static const char kFontArabicTypesetting[] = "Arabic Typesetting";
static const char kFontArial[] = "Arial";

View File

@@ -121,9 +121,6 @@ public:
CreateOffscreenSurface(const IntSize& aSize,
gfxImageFormat aFormat) override;
virtual already_AddRefed<mozilla::gfx::ScaledFont>
GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) override;
enum RenderMode {
/* Use GDI and windows surfaces */
RENDER_GDI = 0,

View File

@@ -181,8 +181,7 @@ Version(JSContext *cx,
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
args.rval().setInt32(JS_GetVersion(cx));
if (args.get(0).isInt32())
JS_SetVersionForCompartment(js::GetContextCompartment(cx),
JSVersion(args[0].toInt32()));
JS::SetVersionForCurrentRealm(cx, JSVersion(args[0].toInt32()));
return true;
}

View File

@@ -177,6 +177,8 @@ struct GCPolicy<mozilla::Maybe<T>>
}
};
template <> struct GCPolicy<JS::Realm*>; // see Realm.h
} // namespace JS
#endif // GCPolicyAPI_h

View File

@@ -13,13 +13,82 @@
#ifndef js_Realm_h
#define js_Realm_h
#include "jstypes.h"
#include "jspubtd.h"
#include "js/GCPolicyAPI.h"
#include "js/TypeDecls.h" // forward-declaration of JS::Realm
struct JSContext;
class JSObject;
namespace js {
namespace gc {
JS_PUBLIC_API(void) TraceRealm(JSTracer* trc, JS::Realm* realm, const char* name);
JS_PUBLIC_API(bool) RealmNeedsSweep(JS::Realm* realm);
}
}
namespace JS {
// Each Realm holds a strong reference to its GlobalObject, and vice versa.
template <>
struct GCPolicy<Realm*>
{
static Realm* initial() { return nullptr; }
static void trace(JSTracer* trc, Realm** vp, const char* name) {
if (*vp)
::js::gc::TraceRealm(trc, *vp, name);
}
static bool needsSweep(Realm** vp) {
return *vp && ::js::gc::RealmNeedsSweep(*vp);
}
};
// Get the current realm, if any. The ECMAScript spec calls this "the current
// Realm Record".
extern JS_PUBLIC_API(Realm*)
GetCurrentRealmOrNull(JSContext* cx);
// Return the compartment that contains a given realm.
inline JSCompartment*
GetCompartmentForRealm(Realm* realm) {
// Implementation note: For now, realms are a fiction; we treat realms and
// compartments as being one-to-one, but they are actually identical.
return reinterpret_cast<JSCompartment*>(realm);
}
// Return the realm in a given compartment.
inline Realm*
GetRealmForCompartment(JSCompartment* compartment) {
return reinterpret_cast<Realm*>(compartment);
}
// Get the value of the "private data" internal field of the given Realm.
// This field is initially null and is set using SetRealmPrivate.
// It's a pointer to embeddding-specific data that SpiderMonkey never uses.
extern JS_PUBLIC_API(void*)
GetRealmPrivate(Realm* realm);
// Set the "private data" internal field of the given Realm.
extern JS_PUBLIC_API(void)
SetRealmPrivate(Realm* realm, void* data);
typedef void
(* DestroyRealmCallback)(JSFreeOp* fop, Realm* realm);
// Set the callback SpiderMonkey calls just before garbage-collecting a realm.
// Embeddings can use this callback to free private data associated with the
// realm via SetRealmPrivate.
//
// By the time this is called, the global object for the realm has already been
// collected.
extern JS_PUBLIC_API(void)
SetDestroyRealmCallback(JSContext* cx, DestroyRealmCallback callback);
typedef void
(* RealmNameCallback)(JSContext* cx, Handle<Realm*> realm, char* buf, size_t bufsize);
// Set the callback SpiderMonkey calls to get the name of a realm, for
// diagnostic output.
extern JS_PUBLIC_API(void)
SetRealmNameCallback(JSContext* cx, RealmNameCallback callback);
extern JS_PUBLIC_API(JSObject*)
GetRealmObjectPrototype(JSContext* cx);
@@ -35,6 +104,17 @@ GetRealmErrorPrototype(JSContext* cx);
extern JS_PUBLIC_API(JSObject*)
GetRealmIteratorPrototype(JSContext* cx);
/**
* Change the JS language version for the current Realm. This is discouraged,
* but necessary to support the `version()` builtin function in the js and xpc
* shells.
*
* It would be nice to put this in jsfriendapi, but the linkage requirements
* of the shells make that impossible.
*/
JS_PUBLIC_API(void)
SetVersionForCurrentRealm(JSContext* cx, JSVersion version);
} // namespace JS
#endif // js_Realm_h

View File

@@ -133,7 +133,7 @@ JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF)
#undef JS_EXPAND_DEF
// Specify the RootKind for all types. Value and jsid map to special cases;
// pointer types we can derive directly from the TraceKind; everything else
// Cell pointer types we can derive directly from the TraceKind; everything else
// should go in the Traceable list and use GCPolicy<T>::trace for tracing.
template <typename T>
struct MapTypeToRootKind {
@@ -144,6 +144,10 @@ struct MapTypeToRootKind<T*> {
static const JS::RootKind kind =
JS::MapTraceKindToRootKind<JS::MapTypeToTraceKind<T>::kind>::kind;
};
template <> struct MapTypeToRootKind<JS::Realm*> {
// Not a pointer to a GC cell. Use GCPolicy.
static const JS::RootKind kind = JS::RootKind::Traceable;
};
template <typename T>
struct MapTypeToRootKind<mozilla::UniquePtr<T>> {
static const JS::RootKind kind = JS::MapTypeToRootKind<T>::kind;

View File

@@ -28,6 +28,7 @@ class JSObject;
class JSScript;
class JSString;
class JSAddonId;
struct JSFreeOp;
struct jsid;
@@ -37,6 +38,7 @@ typedef unsigned char Latin1Char;
class Symbol;
class Value;
class Realm;
template <typename T> class Handle;
template <typename T> class MutableHandle;
template <typename T> class Rooted;

View File

@@ -90,7 +90,7 @@ class RegExpStack
static const uintptr_t kMemoryTop = static_cast<uintptr_t>(-1);
// Minimal size of allocated stack area, in bytes.
static const size_t kMinimumStackSize = 1 * 1024;
static const size_t kMinimumStackSize = 256;
// Maximal size of allocated stack area, in bytes.
static const size_t kMaximumStackSize = 64 * 1024 * 1024;

View File

@@ -588,12 +588,6 @@ JS_GetVersion(JSContext* cx)
return VersionNumber(cx->findVersion());
}
JS_PUBLIC_API(void)
JS_SetVersionForCompartment(JSCompartment* compartment, JSVersion version)
{
compartment->behaviors().setVersion(version);
}
static const struct v2smap {
JSVersion version;
const char* string;

View File

@@ -1135,17 +1135,6 @@ class MOZ_RAII JSAutoRequest
extern JS_PUBLIC_API(JSVersion)
JS_GetVersion(JSContext* cx);
/**
* Mutate the version on the compartment. This is generally discouraged, but
* necessary to support the version mutation in the js and xpc shell command
* set.
*
* It would be nice to put this in jsfriendapi, but the linkage requirements
* of the shells make that impossible.
*/
JS_PUBLIC_API(void)
JS_SetVersionForCompartment(JSCompartment* compartment, JSVersion version);
extern JS_PUBLIC_API(const char*)
JS_VersionToString(JSVersion version);

View File

@@ -68,6 +68,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
enterCompartmentDepth(0),
performanceMonitoring(runtime_),
data(nullptr),
realmData(nullptr),
allocationMetadataBuilder(nullptr),
lastAnimationTime(0),
regExps(zone),

View File

@@ -669,12 +669,12 @@ struct JSCompartment
return runtime_;
}
/*
* Nb: global_ might be nullptr, if (a) it's the atoms compartment, or
* (b) the compartment's global has been collected. The latter can happen
* if e.g. a string in a compartment is rooted but no object is, and thus
* the global isn't rooted, and thus the global can be finalized while the
* compartment lives on.
/* The global object for this compartment.
*
* This returns nullptr if this is the atoms compartment. (The global_
* field is also null briefly during GC, after the global object is
* collected; but when that happens the JSCompartment is destroyed during
* the same GC.)
*
* In contrast, JSObject::global() is infallible because marking a JSObject
* always marks its global as well.
@@ -685,10 +685,14 @@ struct JSCompartment
/* An unbarriered getter for use while tracing. */
inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
/* True if a global object exists, but it's being collected. */
inline bool globalIsAboutToBeFinalized();
inline void initGlobal(js::GlobalObject& global);
public:
void* data;
void* realmData;
private:
const js::AllocationMetadataBuilder *allocationMetadataBuilder;

View File

@@ -11,6 +11,7 @@
#include "jsiter.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "jscntxtinlines.h"
@@ -35,6 +36,13 @@ JSCompartment::unsafeUnbarrieredMaybeGlobal() const
return *global_.unsafeGet();
}
inline bool
JSCompartment::globalIsAboutToBeFinalized()
{
MOZ_ASSERT(zone_->isGCSweeping());
return global_ && js::gc::IsAboutToBeFinalizedUnbarriered(global_.unsafeGet());
}
template <typename T>
js::AutoCompartment::AutoCompartment(JSContext* cx, const T& target)
: cx_(cx),

View File

@@ -3444,6 +3444,8 @@ void
JSCompartment::destroy(FreeOp* fop)
{
JSRuntime* rt = fop->runtime();
if (auto callback = rt->destroyRealmCallback)
callback(fop, JS::GetRealmForCompartment(this));
if (auto callback = rt->destroyCompartmentCallback)
callback(fop, this);
if (principals())
@@ -3464,10 +3466,9 @@ Zone::destroy(FreeOp* fop)
* It's simpler if we preserve the invariant that every zone has at least one
* compartment. If we know we're deleting the entire zone, then
* SweepCompartments is allowed to delete all compartments. In this case,
* |keepAtleastOne| is false. If some objects remain in the zone so that it
* cannot be deleted, then we set |keepAtleastOne| to true, which prohibits
* SweepCompartments from deleting every compartment. Instead, it preserves an
* arbitrary compartment in the zone.
* |keepAtleastOne| is false. If any cells remain alive in the zone, set
* |keepAtleastOne| true to prohibit sweepCompartments from deleting every
* compartment. Instead, it preserves an arbitrary compartment in the zone.
*/
void
Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)

View File

@@ -132,8 +132,6 @@ enum JSShellExitCode {
EXITCODE_TIMEOUT = 6
};
static const size_t gStackChunkSize = 8192;
/*
* Note: This limit should match the stack limit set by the browser in
* js/xpconnect/src/XPCJSContext.cpp
@@ -994,7 +992,7 @@ Version(JSContext* cx, unsigned argc, Value* vp)
"version");
return false;
}
JS_SetVersionForCompartment(js::GetContextCompartment(cx), JSVersion(v));
SetVersionForCurrentRealm(cx, JSVersion(v));
args.rval().setInt32(origVersion);
}
return true;

View File

@@ -7,12 +7,57 @@
#include "js/Realm.h"
#include "jscntxt.h"
#include "jscompartment.h" // For JSContext::global
#include "jscompartment.h"
#include "vm/GlobalObject.h"
#include "jscompartmentinlines.h"
using namespace js;
JS_PUBLIC_API(void)
gc::TraceRealm(JSTracer* trc, JS::Realm* realm, const char* name)
{
// The way GC works with compartments is basically incomprehensible.
// For Realms, what we want is very simple: each Realm has a strong
// reference to its GlobalObject, and vice versa.
//
// Here we simply trace our side of that edge. During GC,
// GCRuntime::traceRuntimeCommon() marks all other compartment roots, for
// all compartments.
JS::GetCompartmentForRealm(realm)->traceGlobal(trc);
}
JS_PUBLIC_API(bool)
gc::RealmNeedsSweep(JS::Realm* realm)
{
return JS::GetCompartmentForRealm(realm)->globalIsAboutToBeFinalized();
}
JS_PUBLIC_API(void*)
JS::GetRealmPrivate(JS::Realm* realm)
{
return GetCompartmentForRealm(realm)->realmData;
}
JS_PUBLIC_API(void)
JS::SetRealmPrivate(JS::Realm* realm, void* data)
{
GetCompartmentForRealm(realm)->realmData = data;
}
JS_PUBLIC_API(void)
JS::SetDestroyRealmCallback(JSContext* cx, JS::DestroyRealmCallback callback)
{
cx->runtime()->destroyRealmCallback = callback;
}
JS_PUBLIC_API(void)
JS::SetRealmNameCallback(JSContext* cx, JS::RealmNameCallback callback)
{
cx->runtime()->realmNameCallback = callback;
}
JS_PUBLIC_API(JSObject*)
JS::GetRealmObjectPrototype(JSContext* cx)
{
@@ -47,3 +92,10 @@ JS::GetRealmIteratorPrototype(JSContext* cx)
CHECK_REQUEST(cx);
return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
}
JS_PUBLIC_API(void)
JS::SetVersionForCurrentRealm(JSContext* cx, JSVersion version)
{
JSCompartment* compartment = GetContextCompartment(cx);
compartment->behaviors().setVersion(version);
}

View File

@@ -119,6 +119,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
destroyCompartmentCallback(nullptr),
sizeOfIncludingThisCompartmentCallback(nullptr),
compartmentNameCallback(nullptr),
destroyRealmCallback(nullptr),
realmNameCallback(nullptr),
externalStringSizeofCallback(nullptr),
securityCallbacks(&NullSecurityCallbacks),
DOMcallbacks(nullptr),

View File

@@ -501,6 +501,12 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
/* Call this to get the name of a compartment. */
js::ActiveThreadData<JSCompartmentNameCallback> compartmentNameCallback;
/* Realm destroy callback. */
js::ActiveThreadData<JS::DestroyRealmCallback> destroyRealmCallback;
/* Call this to get the name of a realm. */
js::ActiveThreadData<JS::RealmNameCallback> realmNameCallback;
/* Callback for doing memory reporting on external strings. */
js::ActiveThreadData<JSExternalStringSizeofCallback> externalStringSizeofCallback;

View File

@@ -3350,10 +3350,8 @@ nsXPCComponents_Utils::SetAddonCallInterposition(HandleValue target,
RootedObject targetObj(cx, &target.toObject());
targetObj = js::CheckedUnwrap(targetObj);
NS_ENSURE_TRUE(targetObj, NS_ERROR_INVALID_ARG);
XPCWrappedNativeScope* xpcScope = ObjectScope(targetObj);
NS_ENSURE_TRUE(xpcScope, NS_ERROR_INVALID_ARG);
xpcScope->SetAddonCallInterposition();
xpc::CompartmentPrivate::Get(targetObj)->SetAddonCallInterposition();
return NS_OK;
}

View File

@@ -175,6 +175,7 @@ CompartmentPrivate::CompartmentPrivate(JSCompartment* c)
, skipWriteToGlobalPrototype(false)
, isWebExtensionContentScript(false)
, waiveInterposition(false)
, addonCallInterposition(false)
, allowCPOWs(false)
, universalXPConnectEnabled(false)
, forcePermissiveCOWs(false)
@@ -2663,6 +2664,13 @@ CompartmentNameCallback(JSContext* cx, JSCompartment* comp,
memcpy(buf, name.get(), name.Length() + 1);
}
static void
GetRealmName(JSContext* cx, JS::Handle<JS::Realm*> realm, char* buf, size_t bufsize)
{
JSCompartment* comp = JS::GetCompartmentForRealm(realm);
CompartmentNameCallback(cx, comp, buf, bufsize);
}
static bool
PreserveWrapper(JSContext* cx, JSObject* obj)
{
@@ -2834,6 +2842,7 @@ XPCJSRuntime::Initialize(JSContext* cx)
JS_SetDestroyCompartmentCallback(cx, CompartmentDestroyedCallback);
JS_SetSizeOfIncludingThisCompartmentCallback(cx, CompartmentSizeOfIncludingThisCallback);
JS_SetCompartmentNameCallback(cx, CompartmentNameCallback);
JS::SetRealmNameCallback(cx, GetRealmName);
mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
DoCycleCollectionCallback);

View File

@@ -392,8 +392,7 @@ Version(JSContext* cx, unsigned argc, Value* vp)
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setInt32(JS_GetVersion(cx));
if (args.get(0).isInt32())
JS_SetVersionForCompartment(js::GetContextCompartment(cx),
JSVersion(args[0].toInt32()));
SetVersionForCurrentRealm(cx, JSVersion(args[0].toInt32()));
return true;
}
@@ -1086,8 +1085,7 @@ ProcessArgs(AutoJSAPI& jsapi, char** argv, int argc, XPCShellDirProvider* aDirPr
if (++i == argc) {
return printUsageAndSetExitCode();
}
JS_SetVersionForCompartment(js::GetContextCompartment(cx),
JSVersion(atoi(argv[i])));
SetVersionForCurrentRealm(cx, JSVersion(atoi(argv[i])));
break;
case 'W':
reportWarnings = false;

View File

@@ -96,7 +96,6 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx,
mComponents(nullptr),
mNext(nullptr),
mGlobalJSObject(aGlobal),
mHasCallInterpositions(false),
mIsContentXBLScope(false),
mIsAddonScope(false)
{

View File

@@ -1022,9 +1022,6 @@ public:
static bool UpdateInterpositionWhitelist(JSContext* cx,
nsIAddonInterposition* interposition);
void SetAddonCallInterposition() { mHasCallInterpositions = true; }
bool HasCallInterposition() { return mHasCallInterpositions; };
static bool AllowCPOWsInAddon(JSContext* cx, JSAddonId* addonId, bool allow);
protected:
@@ -1072,10 +1069,6 @@ private:
// objects from other scope, for add-on compatibility reasons.
nsCOMPtr<nsIAddonInterposition> mInterposition;
// If this flag is set, we intercept function calls on vanilla JS function objects
// from this scope if the caller scope has mInterposition set.
bool mHasCallInterpositions;
JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
bool mIsContentXBLScope;
@@ -3018,6 +3011,9 @@ bool ReportWrapperDenial(JSContext* cx, JS::HandleId id, WrapperDenialType type,
class CompartmentPrivate
{
CompartmentPrivate() = delete;
CompartmentPrivate(const CompartmentPrivate&) = delete;
public:
enum LocationHint {
LocationHintRegular,
@@ -3063,15 +3059,20 @@ public:
// classes, for example).
bool skipWriteToGlobalPrototype;
// This scope corresponds to a WebExtension content script, and receives
// various bits of special compatibility behavior.
// This compartment corresponds to a WebExtension content script, and
// receives various bits of special compatibility behavior.
bool isWebExtensionContentScript;
// Even if an add-on needs interposition, it does not necessary need it
// for every scope. If this flag is set we waive interposition for this
// scope.
// for every compartment. If this flag is set we waive interposition for
// this compartment.
bool waiveInterposition;
// If this flag is set, we intercept function calls on vanilla JS function
// objects from this compartment if the caller compartment has the
// hasInterposition flag set.
bool addonCallInterposition;
// If CPOWs are disabled for browser code via the
// dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only
// add-ons can use CPOWs. This flag allows a non-addon scope
@@ -3152,6 +3153,8 @@ public:
locationURI = aLocationURI;
}
void SetAddonCallInterposition() { addonCallInterposition = true; }
JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
void UpdateWeakPointersAfterGC();

View File

@@ -114,8 +114,7 @@ InterposeCall(JSContext* cx, JS::HandleObject target, const JS::CallArgs& args,
nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
RootedObject unwrappedTarget(cx, UncheckedUnwrap(target));
XPCWrappedNativeScope* targetScope = ObjectScope(unwrappedTarget);
bool hasInterpostion = targetScope->HasCallInterposition();
bool hasInterpostion = xpc::CompartmentPrivate::Get(unwrappedTarget)->addonCallInterposition;
if (!hasInterpostion)
return true;

View File

@@ -64,7 +64,6 @@
#include "nsContentUtils.h"
#include "nsLineBreaker.h"
#include "nsIWordBreaker.h"
#include "nsGenericDOMDataNode.h"
#include "nsIFrameInlines.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
@@ -736,7 +735,9 @@ int32_t nsTextFrame::GetInFlowContentLength() {
}
FlowLengthProperty* flowLength =
static_cast<FlowLengthProperty*>(mContent->GetProperty(nsGkAtoms::flowlength));
mContent->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)
? static_cast<FlowLengthProperty*>(mContent->GetProperty(nsGkAtoms::flowlength))
: nullptr;
/**
* This frame must start inside the cached flow. If the flow starts at
@@ -764,6 +765,7 @@ int32_t nsTextFrame::GetInFlowContentLength() {
delete flowLength;
flowLength = nullptr;
}
mContent->SetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
if (flowLength) {
flowLength->mStartOffset = mContentOffset;
@@ -4380,9 +4382,13 @@ nsTextFrame::Init(nsIContent* aContent,
// Remove any NewlineOffsetProperty or InFlowContentLengthProperty since they
// might be invalid if the content was modified while there was no frame
if (aContent->HasFlag(NS_HAS_NEWLINE_PROPERTY)) {
aContent->DeleteProperty(nsGkAtoms::newline);
if (PresContext()->BidiEnabled()) {
aContent->UnsetFlags(NS_HAS_NEWLINE_PROPERTY);
}
if (aContent->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
aContent->DeleteProperty(nsGkAtoms::flowlength);
aContent->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
// Since our content has a frame now, this flag is no longer needed.
@@ -4830,9 +4836,13 @@ nsTextFrame::DisconnectTextRuns()
nsresult
nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
{
if (mContent->HasFlag(NS_HAS_NEWLINE_PROPERTY)) {
mContent->DeleteProperty(nsGkAtoms::newline);
if (PresContext()->BidiEnabled()) {
mContent->UnsetFlags(NS_HAS_NEWLINE_PROPERTY);
}
if (mContent->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
mContent->DeleteProperty(nsGkAtoms::flowlength);
mContent->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
// Find the first frame whose text has changed. Frames that are entirely
@@ -9278,7 +9288,9 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
NewlineProperty* cachedNewlineOffset = nullptr;
if (textStyle->NewlineIsSignificant(this)) {
cachedNewlineOffset =
static_cast<NewlineProperty*>(mContent->GetProperty(nsGkAtoms::newline));
mContent->HasFlag(NS_HAS_NEWLINE_PROPERTY)
? static_cast<NewlineProperty*>(mContent->GetProperty(nsGkAtoms::newline))
: nullptr;
if (cachedNewlineOffset && cachedNewlineOffset->mStartOffset <= offset &&
(cachedNewlineOffset->mNewlineOffset == -1 ||
cachedNewlineOffset->mNewlineOffset >= offset)) {
@@ -9763,6 +9775,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
delete cachedNewlineOffset;
cachedNewlineOffset = nullptr;
}
mContent->SetFlags(NS_HAS_NEWLINE_PROPERTY);
}
if (cachedNewlineOffset) {
cachedNewlineOffset->mStartOffset = offset;
@@ -9770,6 +9783,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
}
} else if (cachedNewlineOffset) {
mContent->DeleteProperty(nsGkAtoms::newline);
mContent->UnsetFlags(NS_HAS_NEWLINE_PROPERTY);
}
// Compute space and letter counts for justification, if required
@@ -10252,7 +10266,10 @@ void
nsTextFrame::AdjustOffsetsForBidi(int32_t aStart, int32_t aEnd)
{
AddStateBits(NS_FRAME_IS_BIDI);
if (mContent->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
mContent->DeleteProperty(nsGkAtoms::flowlength);
mContent->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
/*
* After Bidi resolution we may need to reassign text runs.

View File

@@ -12,6 +12,7 @@
#include "mozilla/UniquePtr.h"
#include "nsFrame.h"
#include "nsFrameSelection.h"
#include "nsGenericDOMDataNode.h"
#include "nsSplittableFrame.h"
#include "nsLineBox.h"
#include "gfxSkipChars.h"
@@ -96,7 +97,10 @@ public:
aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
// Setting a non-fluid continuation might affect our flow length (they're
// quite rare so we assume it always does) so we delete our cached value:
if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
GetContent()->DeleteProperty(nsGkAtoms::flowlength);
GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
}
nsIFrame* GetNextInFlowVirtual() const override { return GetNextInFlow(); }
nsTextFrame* GetNextInFlow() const
@@ -119,7 +123,10 @@ public:
!mNextContinuation->HasAnyStateBits(NS_FRAME_IS_FLUID_CONTINUATION)) {
// Changing from non-fluid to fluid continuation might affect our flow
// length, so we delete our cached value:
if (GetContent()->HasFlag(NS_HAS_FLOWLENGTH_PROPERTY)) {
GetContent()->DeleteProperty(nsGkAtoms::flowlength);
GetContent()->UnsetFlags(NS_HAS_FLOWLENGTH_PROPERTY);
}
}
if (aNextInFlow) {
aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);

View File

@@ -87,7 +87,7 @@ random-if(winWidget) == 305643-1.html 305643-1-ref.html # depends on windows ver
== 413542-1.html 413542-1-ref.html
== 413542-2.html 413542-2-ref.html
fails-if(webrender) == 413928-1.html 413928-1-ref.html
== 413928-2.html 413928-2-ref.html
fails-if(webrender) == 413928-2.html 413928-2-ref.html
== 425338-1a.html 425338-1-ref.html
== 425338-1b.html 425338-1-ref.html
== 489517-1.html 489517-1-ref.html

View File

@@ -88,7 +88,7 @@ parser.add_argument('-sfl', '--max-stack-frame-length', type=int,
parser.add_argument('-a', '--ignore-alloc-fns', action='store_true',
help='ignore allocation functions at the start of traces')
parser.add_argument('-f', '--max-frames', type=range_1_24,
parser.add_argument('-f', '--max-frames', type=range_1_24, default=8,
help='maximum number of frames to consider in each trace')
parser.add_argument('-c', '--chain-reports', action='store_true',

View File

@@ -166,7 +166,7 @@ variable is used to find breakpad symbols for stack fixing.
p.add_argument('-o', '--output', type=argparse.FileType('w'),
help='output file; stdout if unspecified')
p.add_argument('-f', '--max-frames', type=range_1_24,
p.add_argument('-f', '--max-frames', type=range_1_24, default=8,
help='maximum number of frames to consider in each trace')
p.add_argument('-s', '--sort-by', choices=sortByChoices.keys(),

View File

@@ -1,5 +1,5 @@
#-----------------------------------------------------------------
# dmd.py --filter-stacks-for-testing -o script-max-frames-8-actual.txt --max-frames=8 script-max-frames.json
# dmd.py --filter-stacks-for-testing -o script-max-frames-8-actual.txt script-max-frames.json
Invocation {
$DMD = '--mode=live --stacks=full'

View File

@@ -187,7 +187,7 @@ function run_test() {
// of the tested values.
jsonFile = FileUtils.getFile("CurWorkD", ["script-max-frames.json"]);
test("script-max-frames-8",
["--max-frames=8", jsonFile.path]);
[jsonFile.path]); // --max-frames=8 is the default
test("script-max-frames-3",
["--max-frames=3", "--no-fix-stacks", jsonFile.path]);
test("script-max-frames-1",

View File

@@ -6,7 +6,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
Cu.import("resource://gre/modules/ExtensionTabs.jsm");
/* globals EventDispatcher */
Cu.import("resource://gre/modules/Messaging.jsm");

View File

@@ -27,9 +27,6 @@ from mozboot.util import (
)
APPLICATION_CHOICE = '''
Please choose the version of Firefox you want to build:
%s
Note on Artifact Mode:
Firefox for Desktop and Android supports a fast build mode called
@@ -56,6 +53,8 @@ But don't worry! You can always switch configurations later.
You can learn more about Artifact mode builds at
https://developer.mozilla.org/en-US/docs/Artifact_builds.
Please choose the version of Firefox you want to build:
%s
Your choice: '''
APPLICATIONS_LIST=[

View File

@@ -174,6 +174,7 @@ HistoryStore.prototype = {
async applyIncomingBatch(records) {
let failed = [];
let blockers = [];
// Convert incoming records to mozIPlaceInfo objects. Some records can be
// ignored or handled directly, so we're rewriting the array in-place.
@@ -184,7 +185,9 @@ HistoryStore.prototype = {
try {
if (record.deleted) {
await this.remove(record);
let promise = this.remove(record);
promise = promise.catch(ex => failed.push(record.id));
blockers.push(promise);
// No further processing needed. Remove it from the list.
shouldApply = false;
@@ -206,9 +209,20 @@ HistoryStore.prototype = {
records.length = k; // truncate array
if (records.length) {
await PlacesUtils.history.insertMany(records)
blockers.push(new Promise(resolve => {
let updatePlacesCallback = {
handleResult: function handleResult() {},
handleError: function handleError(resultCode, placeInfo) {
failed.push(placeInfo.guid);
},
handleCompletion: resolve,
};
this._asyncHistory.updatePlaces(records, updatePlacesCallback);
}));
}
// failed is updated asynchronously, hence the await on blockers.
await Promise.all(blockers);
return failed;
},
@@ -221,8 +235,11 @@ HistoryStore.prototype = {
*/
async _recordToPlaceInfo(record) {
// Sort out invalid URIs and ones Places just simply doesn't want.
record.url = PlacesUtils.normalizeToURLOrGUID(record.histUri);
record.uri = Utils.makeURI(record.histUri);
if (!record.uri) {
this._log.warn("Attempted to process invalid URI, skipping.");
throw new Error("Invalid URI in record");
}
if (!Utils.checkGUID(record.id)) {
this._log.warn("Encountered record with invalid GUID: " + record.id);
@@ -279,8 +296,8 @@ HistoryStore.prototype = {
continue;
}
visit.date = PlacesUtils.toDate(visit.date);
visit.transition = visit.type;
visit.visitDate = visit.date;
visit.transitionType = visit.type;
k += 1;
}
record.visits.length = k; // truncate array

View File

@@ -1,2 +1,3 @@
mar==2.1.2
backports.lzma==0.0.8
redo

View File

@@ -88,7 +88,12 @@ function step2() {
// top window time and iframe window time.
assert_greater_than(topWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
"Time ranges for top and iframe windows are disjoint. Times: " +
[topWindowTimeOnTestStart, topWindowTimeBeforeCreatingIframe, topWindowTimeBeforeNotification, topWindowTimeAfterNotification, iframeWindowTimeBeforeNotification, iframeWindowTimeAfterNotification]);
[topWindowTimeOnTestStart, topWindowTimeBeforeCreatingIframe,
topWindowTimeBeforeNotification, topWindowTimeAfterNotification,
iframeWindowTimeBeforeNotification, iframeWindowTimeAfterNotification,
topWindowEntries[1].time - topWindowTimeBeforeNotification,
iframeWindowEntries[1].time - iframeWindowTimeBeforeNotification
]);
assert_equals(topWindowEntries.length, 2, "Top window observer has two notifications.");
assert_between_inclusive(

View File

@@ -18,10 +18,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const global = this;
class ExtensionAPI {
class ExtensionAPI extends ExtensionUtils.EventEmitter {
constructor(extension) {
super();
this.extension = extension;
extension.once("shutdown", () => {

View File

@@ -15,6 +15,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
this.EXPORTED_SYMBOLS = ["ExtensionCommon"];
Cu.importGlobalProperties(["fetch"]);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -618,6 +620,26 @@ function deepCopy(dest, source) {
}
}
function getChild(map, key) {
let child = map.children.get(key);
if (!child) {
child = {
modules: new Set(),
children: new Map(),
};
map.children.set(key, child);
}
return child;
}
function getPath(map, path) {
for (let key of path) {
map = getChild(map, key);
}
return map;
}
/**
* Manages loading and accessing a set of APIs for a specific extension
* context.
@@ -712,7 +734,7 @@ class CanOfAPIs {
if (!obj) {
return;
}
modules = modules.get(key);
modules = getChild(modules, key);
for (let name of modules.modules) {
if (!this.apis.has(name)) {
@@ -748,7 +770,7 @@ class CanOfAPIs {
if (!obj) {
return;
}
modules = modules.get(key);
modules = getChild(modules, key);
for (let name of modules.modules) {
if (!this.apis.has(name)) {
@@ -768,18 +790,6 @@ class CanOfAPIs {
}
}
class DeepMap extends DefaultMap {
constructor() {
super(() => new DeepMap());
this.modules = new Set();
}
getPath(path) {
return path.reduce((map, key) => map.get(key), this);
}
}
/**
* @class APIModule
* @abstract
@@ -832,10 +842,12 @@ class SchemaAPIManager extends EventEmitter {
this.global = this._createExtGlobal();
this.modules = new Map();
this.modulePaths = new DeepMap();
this.modulePaths = {children: new Map(), modules: new Set()};
this.manifestKeys = new Map();
this.eventModules = new DefaultMap(() => new Set());
this._modulesJSONLoaded = false;
this.schemaURLs = new Set();
this.apis = new DefaultWeakMap(() => new Map());
@@ -843,6 +855,41 @@ class SchemaAPIManager extends EventEmitter {
this._scriptScopes = [];
}
async loadModuleJSON(urls) {
function fetchJSON(url) {
return fetch(url).then(resp => resp.json());
}
for (let json of await Promise.all(urls.map(fetchJSON))) {
this.registerModules(json);
}
this._modulesJSONLoaded = true;
return new StructuredCloneHolder({
modules: this.modules,
modulePaths: this.modulePaths,
manifestKeys: this.manifestKeys,
eventModules: this.eventModules,
schemaURLs: this.schemaURLs,
});
}
initModuleData(moduleData) {
if (!this._modulesJSONLoaded) {
let data = moduleData.deserialize({});
this.modules = data.modules;
this.modulePaths = data.modulePaths;
this.manifestKeys = data.manifestKeys;
this.eventModules = new DefaultMap(() => new Set(),
data.eventModules);
this.schemaURLs = data.schemaURLs;
}
this._modulesJSONLoaded = true;
}
/**
* Registers a set of ExtensionAPI modules to be lazily loaded and
* managed by this manager.
@@ -878,7 +925,7 @@ class SchemaAPIManager extends EventEmitter {
}
for (let path of details.paths || []) {
this.modulePaths.getPath(path).modules.add(name);
getPath(this.modulePaths, path).modules.add(name);
}
}
}

View File

@@ -64,6 +64,7 @@ var {
} = ExtensionUtils;
const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
const CATEGORY_EXTENSION_MODULES = "webextension-modules";
const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas";
const CATEGORY_EXTENSION_SCRIPTS = "webextension-scripts";
@@ -74,6 +75,7 @@ schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
let GlobalManager;
let ParentAPIManager;
let ProxyMessenger;
let StartupCache;
// This object loads the ext-*.js scripts that define the extension API.
let apiManager = new class extends SchemaAPIManager {
@@ -93,18 +95,31 @@ let apiManager = new class extends SchemaAPIManager {
});
}
getModuleJSONURLs() {
return Array.from(XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_MODULES),
([name, url]) => url);
}
// Loads all the ext-*.js scripts currently registered.
lazyInit() {
if (this.initialized) {
return this.initialized;
}
let scripts = [];
let modulesPromise = StartupCache.other.get(
["parentModules"],
() => this.loadModuleJSON(this.getModuleJSONURLs()));
let scriptURLs = [];
for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCRIPTS)) {
scripts.push(value);
scriptURLs.push(value);
}
let promise = Promise.all(scripts.map(url => ChromeUtils.compileScript(url))).then(scripts => {
let promise = (async () => {
let scripts = await Promise.all(scriptURLs.map(url => ChromeUtils.compileScript(url)));
this.initModuleData(await modulesPromise);
for (let script of scripts) {
script.executeInGlobal(this.global);
}
@@ -124,7 +139,7 @@ let apiManager = new class extends SchemaAPIManager {
}
return Promise.all(promises);
});
});
})();
/* eslint-disable mozilla/balanced-listeners */
Services.mm.addMessageListener("Extension:GetTabAndWindowId", this);
@@ -1361,10 +1376,10 @@ let IconDetails = {
},
};
let StartupCache = {
StartupCache = {
DB_NAME: "ExtensionStartupCache",
STORE_NAMES: Object.freeze(["locales", "manifests", "permissions", "schemas"]),
STORE_NAMES: Object.freeze(["general", "locales", "manifests", "other", "permissions", "schemas"]),
get file() {
return FileUtils.getFile("ProfLD", ["startupCache", "webext.sc.lz4"]);
@@ -1413,6 +1428,7 @@ let StartupCache = {
clearAddonData(id) {
return Promise.all([
this.general.delete(id),
this.locales.delete(id),
this.manifests.delete(id),
this.permissions.delete(id),
@@ -1428,6 +1444,15 @@ let StartupCache = {
this._dataPromise = Promise.resolve(this._data);
}
},
get(extension, path, createFunc) {
return this.general.get([extension.id, extension.version, ...path],
createFunc);
},
delete(extension, path) {
return this.general.delete([extension.id, extension.version, ...path]);
},
};
// void StartupCache.dataPromise;

View File

@@ -13,10 +13,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
var {
EventEmitter,
normalizeTime,
} = ExtensionUtils;
@@ -142,19 +141,22 @@ class DownloadItem {
// DownloadMap maps back and forth betwen the numeric identifiers used in
// the downloads WebExtension API and a Download object from the Downloads jsm.
// todo: make id and extension info persistent (bug 1247794)
const DownloadMap = {
currentId: 0,
loadPromise: null,
const DownloadMap = new class extends EventEmitter {
constructor() {
super();
this.currentId = 0;
this.loadPromise = null;
// Maps numeric id -> DownloadItem
byId: new Map(),
this.byId = new Map();
// Maps Download object -> DownloadItem
byDownload: new WeakMap(),
this.byDownload = new WeakMap();
}
lazyInit() {
if (this.loadPromise == null) {
EventEmitter.decorate(this);
this.loadPromise = Downloads.getList(Downloads.ALL).then(list => {
let self = this;
return list.addView({
@@ -192,15 +194,15 @@ const DownloadMap = {
});
}
return this.loadPromise;
},
}
getDownloadList() {
return this.lazyInit();
},
}
getAll() {
return this.lazyInit().then(() => this.byId.values());
},
}
fromId(id) {
const download = this.byId.get(id);
@@ -208,7 +210,7 @@ const DownloadMap = {
throw new Error(`Invalid download id ${id}`);
}
return download;
},
}
newFromDownload(download, extension) {
if (this.byDownload.has(download)) {
@@ -220,7 +222,7 @@ const DownloadMap = {
this.byId.set(id, item);
this.byDownload.set(download, item);
return item;
},
}
erase(item) {
// This will need to get more complicated for bug 1255507 but for now we
@@ -228,8 +230,8 @@ const DownloadMap = {
return this.getDownloadList().then(list => {
list.remove(item.download);
});
},
};
}
}();
// Create a callable function that filters a DownloadItem based on a
// query object of the type passed to search() or erase().

View File

@@ -3,8 +3,6 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-toolkit.js */
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "idleService",
"@mozilla.org/widget/idleservice;1",
"nsIIdleService");
@@ -37,14 +35,13 @@ const getIdleObserver = (extension, context) => {
let observerInfo = getIdleObserverInfo(extension, context);
let {observer, detectionInterval} = observerInfo;
if (!observer) {
observer = {
observe: function(subject, topic, data) {
observer = new class extends ExtensionUtils.EventEmitter {
observe(subject, topic, data) {
if (topic == "idle" || topic == "active") {
this.emit("stateChanged", topic);
}
},
};
EventEmitter.decorate(observer);
}
}();
idleService.addIdleObserver(observer, detectionInterval);
observerInfo.observer = observer;
observerInfo.detectionInterval = detectionInterval;

View File

@@ -14,8 +14,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
XPCOMUtils.defineLazyServiceGetter(this, "promptService",
"@mozilla.org/embedcomp/prompt-service;1",
"nsIPromptService");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => {
const {GlobalManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
@@ -91,10 +89,10 @@ const listenerMap = new WeakMap();
// Some management APIs are intentionally limited.
const allowedTypes = ["theme", "extension"];
class AddonListener {
class AddonListener extends ExtensionUtils.EventEmitter {
constructor() {
super();
AddonManager.addAddonListener(this);
EventEmitter.decorate(this);
}
release() {

View File

@@ -3,7 +3,9 @@
// The ext-* files are imported into the same scopes.
/* import-globals-from ext-toolkit.js */
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
const ToolkitModules = {};
XPCOMUtils.defineLazyModuleGetter(ToolkitModules, "EventEmitter",
"resource://gre/modules/EventEmitter.jsm");
var {
@@ -98,7 +100,7 @@ this.notifications = class extends ExtensionAPI {
let {extension} = context;
let map = new Map();
EventEmitter.decorate(map);
ToolkitModules.EventEmitter.decorate(map);
notificationsMap.set(extension, map);
return {

View File

@@ -5,11 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* exported TabTrackerBase, TabManagerBase, TabBase, WindowTrackerBase, WindowManagerBase, WindowBase */
var EXPORTED_SYMBOLS = ["TabTrackerBase", "TabManagerBase", "TabBase", "WindowTrackerBase", "WindowManagerBase", "WindowBase"];
/* globals EventEmitter */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -18,12 +14,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
var {
DefaultMap,
DefaultWeakMap,
EventEmitter,
ExtensionError,
defineLazyGetter,
getWinUtils,
@@ -1847,3 +1840,5 @@ class WindowManagerBase {
}
/* eslint-enable valid-jsdoc */
}
Object.assign(global, {TabTrackerBase, TabManagerBase, TabBase, WindowTrackerBase, WindowManagerBase, WindowBase});

View File

@@ -17,6 +17,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
Cu.import("resource://gre/modules/ExtensionCommon.jsm");
global.EventEmitter = ExtensionUtils.EventEmitter;
global.EventManager = ExtensionCommon.EventManager;
global.InputEventManager = class extends EventManager {
constructor(...args) {
@@ -78,185 +79,6 @@ global.isValidCookieStoreId = function(storeId) {
isContainerCookieStoreId(storeId);
};
extensions.registerModules({
manifest: {
schema: "chrome://extensions/content/schemas/extension_types.json",
scopes: [],
},
alarms: {
url: "chrome://extensions/content/ext-alarms.js",
schema: "chrome://extensions/content/schemas/alarms.json",
scopes: ["addon_parent"],
paths: [
["alarms"],
],
},
backgroundPage: {
url: "chrome://extensions/content/ext-backgroundPage.js",
scopes: ["addon_parent"],
manifest: ["background"],
},
browserSettings: {
url: "chrome://extensions/content/ext-browserSettings.js",
schema: "chrome://extensions/content/schemas/browser_settings.json",
scopes: ["addon_parent"],
paths: [
["browserSettings"],
],
},
contextualIdentities: {
url: "chrome://extensions/content/ext-contextualIdentities.js",
schema: "chrome://extensions/content/schemas/contextual_identities.json",
scopes: ["addon_parent"],
paths: [
["contextualIdentities"],
],
},
cookies: {
url: "chrome://extensions/content/ext-cookies.js",
schema: "chrome://extensions/content/schemas/cookies.json",
scopes: ["addon_parent"],
paths: [
["cookies"],
],
},
downloads: {
url: "chrome://extensions/content/ext-downloads.js",
schema: "chrome://extensions/content/schemas/downloads.json",
scopes: ["addon_parent"],
paths: [
["downloads"],
],
},
extension: {
url: "chrome://extensions/content/ext-extension.js",
schema: "chrome://extensions/content/schemas/extension.json",
scopes: ["addon_parent"],
paths: [
["extension"],
],
},
geolocation: {
url: "chrome://extensions/content/ext-geolocation.js",
events: ["startup"],
},
i18n: {
url: "chrome://extensions/content/ext-i18n.js",
schema: "chrome://extensions/content/schemas/i18n.json",
scopes: ["addon_parent", "content_child", "devtools_child"],
paths: [
["i18n"],
],
},
idle: {
url: "chrome://extensions/content/ext-idle.js",
schema: "chrome://extensions/content/schemas/idle.json",
scopes: ["addon_parent"],
paths: [
["idle"],
],
},
management: {
url: "chrome://extensions/content/ext-management.js",
schema: "chrome://extensions/content/schemas/management.json",
scopes: ["addon_parent"],
paths: [
["management"],
],
},
notifications: {
url: "chrome://extensions/content/ext-notifications.js",
schema: "chrome://extensions/content/schemas/notifications.json",
scopes: ["addon_parent"],
paths: [
["notifications"],
],
},
permissions: {
url: "chrome://extensions/content/ext-permissions.js",
schema: "chrome://extensions/content/schemas/permissions.json",
scopes: ["addon_parent"],
paths: [
["permissions"],
],
},
privacy: {
url: "chrome://extensions/content/ext-privacy.js",
schema: "chrome://extensions/content/schemas/privacy.json",
scopes: ["addon_parent"],
paths: [
["privacy"],
],
},
protocolHandlers: {
url: "chrome://extensions/content/ext-protocolHandlers.js",
schema: "chrome://extensions/content/schemas/extension_protocol_handlers.json",
scopes: ["addon_parent"],
manifest: ["protocol_handlers"],
},
proxy: {
url: "chrome://extensions/content/ext-proxy.js",
schema: "chrome://extensions/content/schemas/proxy.json",
scopes: ["addon_parent"],
paths: [
["proxy"],
],
},
runtime: {
url: "chrome://extensions/content/ext-runtime.js",
schema: "chrome://extensions/content/schemas/runtime.json",
scopes: ["addon_parent", "content_parent", "devtools_parent"],
paths: [
["runtime"],
],
},
storage: {
url: "chrome://extensions/content/ext-storage.js",
schema: "chrome://extensions/content/schemas/storage.json",
scopes: ["addon_parent", "content_parent", "devtools_parent"],
paths: [
["storage"],
],
},
test: {
schema: "chrome://extensions/content/schemas/test.json",
scopes: [],
},
theme: {
url: "chrome://extensions/content/ext-theme.js",
schema: "chrome://extensions/content/schemas/theme.json",
scopes: ["addon_parent"],
manifest: ["theme"],
paths: [
["theme"],
],
},
topSites: {
url: "chrome://extensions/content/ext-topSites.js",
schema: "chrome://extensions/content/schemas/top_sites.json",
scopes: ["addon_parent"],
paths: [
["topSites"],
],
},
webNavigation: {
url: "chrome://extensions/content/ext-webNavigation.js",
schema: "chrome://extensions/content/schemas/web_navigation.json",
scopes: ["addon_parent"],
paths: [
["webNavigation"],
],
},
webRequest: {
url: "chrome://extensions/content/ext-webRequest.js",
schema: "chrome://extensions/content/schemas/web_request.json",
scopes: ["addon_parent"],
paths: [
["webRequest"],
],
},
});
if (AppConstants.MOZ_BUILD_APP === "browser") {
extensions.registerModules({
identity: {

View File

@@ -0,0 +1,178 @@
{
"manifest": {
"schema": "chrome://extensions/content/schemas/extension_types.json",
"scopes": []
},
"alarms": {
"url": "chrome://extensions/content/ext-alarms.js",
"schema": "chrome://extensions/content/schemas/alarms.json",
"scopes": ["addon_parent"],
"paths": [
["alarms"]
]
},
"backgroundPage": {
"url": "chrome://extensions/content/ext-backgroundPage.js",
"scopes": ["addon_parent"],
"manifest": ["background"]
},
"browserSettings": {
"url": "chrome://extensions/content/ext-browserSettings.js",
"schema": "chrome://extensions/content/schemas/browser_settings.json",
"scopes": ["addon_parent"],
"paths": [
["browserSettings"]
]
},
"contextualIdentities": {
"url": "chrome://extensions/content/ext-contextualIdentities.js",
"schema": "chrome://extensions/content/schemas/contextual_identities.json",
"scopes": ["addon_parent"],
"paths": [
["contextualIdentities"]
]
},
"cookies": {
"url": "chrome://extensions/content/ext-cookies.js",
"schema": "chrome://extensions/content/schemas/cookies.json",
"scopes": ["addon_parent"],
"paths": [
["cookies"]
]
},
"downloads": {
"url": "chrome://extensions/content/ext-downloads.js",
"schema": "chrome://extensions/content/schemas/downloads.json",
"scopes": ["addon_parent"],
"paths": [
["downloads"]
]
},
"extension": {
"url": "chrome://extensions/content/ext-extension.js",
"schema": "chrome://extensions/content/schemas/extension.json",
"scopes": ["addon_parent"],
"paths": [
["extension"]
]
},
"geolocation": {
"url": "chrome://extensions/content/ext-geolocation.js",
"events": ["startup"]
},
"i18n": {
"url": "chrome://extensions/content/ext-i18n.js",
"schema": "chrome://extensions/content/schemas/i18n.json",
"scopes": ["addon_parent", "content_child", "devtools_child"],
"paths": [
["i18n"]
]
},
"idle": {
"url": "chrome://extensions/content/ext-idle.js",
"schema": "chrome://extensions/content/schemas/idle.json",
"scopes": ["addon_parent"],
"paths": [
["idle"]
]
},
"management": {
"url": "chrome://extensions/content/ext-management.js",
"schema": "chrome://extensions/content/schemas/management.json",
"scopes": ["addon_parent"],
"paths": [
["management"]
]
},
"notifications": {
"url": "chrome://extensions/content/ext-notifications.js",
"schema": "chrome://extensions/content/schemas/notifications.json",
"scopes": ["addon_parent"],
"paths": [
["notifications"]
]
},
"permissions": {
"url": "chrome://extensions/content/ext-permissions.js",
"schema": "chrome://extensions/content/schemas/permissions.json",
"scopes": ["addon_parent"],
"paths": [
["permissions"]
]
},
"privacy": {
"url": "chrome://extensions/content/ext-privacy.js",
"schema": "chrome://extensions/content/schemas/privacy.json",
"scopes": ["addon_parent"],
"paths": [
["privacy"]
]
},
"protocolHandlers": {
"url": "chrome://extensions/content/ext-protocolHandlers.js",
"schema": "chrome://extensions/content/schemas/extension_protocol_handlers.json",
"scopes": ["addon_parent"],
"manifest": ["protocol_handlers"]
},
"proxy": {
"url": "chrome://extensions/content/ext-proxy.js",
"schema": "chrome://extensions/content/schemas/proxy.json",
"scopes": ["addon_parent"],
"paths": [
["proxy"]
]
},
"runtime": {
"url": "chrome://extensions/content/ext-runtime.js",
"schema": "chrome://extensions/content/schemas/runtime.json",
"scopes": ["addon_parent", "content_parent", "devtools_parent"],
"paths": [
["runtime"]
]
},
"storage": {
"url": "chrome://extensions/content/ext-storage.js",
"schema": "chrome://extensions/content/schemas/storage.json",
"scopes": ["addon_parent", "content_parent", "devtools_parent"],
"paths": [
["storage"]
]
},
"test": {
"schema": "chrome://extensions/content/schemas/test.json",
"scopes": []
},
"theme": {
"url": "chrome://extensions/content/ext-theme.js",
"schema": "chrome://extensions/content/schemas/theme.json",
"scopes": ["addon_parent"],
"manifest": ["theme"],
"paths": [
["theme"]
]
},
"topSites": {
"url": "chrome://extensions/content/ext-topSites.js",
"schema": "chrome://extensions/content/schemas/top_sites.json",
"scopes": ["addon_parent"],
"paths": [
["topSites"]
]
},
"webNavigation": {
"url": "chrome://extensions/content/ext-webNavigation.js",
"schema": "chrome://extensions/content/schemas/web_navigation.json",
"scopes": ["addon_parent"],
"paths": [
["webNavigation"]
]
},
"webRequest": {
"url": "chrome://extensions/content/ext-webRequest.js",
"schema": "chrome://extensions/content/schemas/web_request.json",
"scopes": ["addon_parent"],
"paths": [
["webRequest"]
]
}
}

View File

@@ -1,5 +1,9 @@
# scripts
category webextension-scripts toolkit chrome://extensions/content/ext-toolkit.js
category webextension-modules toolkit chrome://extensions/content/ext-toolkit.json
category webextension-scripts a-toolkit chrome://extensions/content/ext-toolkit.js
category webextension-scripts b-tabs-base chrome://extensions/content/ext-tabs-base.js
category webextension-scripts-content toolkit chrome://extensions/content/ext-c-toolkit.js
category webextension-scripts-devtools toolkit chrome://extensions/content/ext-c-toolkit.js
category webextension-scripts-addon toolkit chrome://extensions/content/ext-c-toolkit.js

View File

@@ -24,8 +24,10 @@ toolkit.jar:
content/extensions/ext-proxy.js
content/extensions/ext-runtime.js
content/extensions/ext-storage.js
content/extensions/ext-tabs-base.js
content/extensions/ext-theme.js
content/extensions/ext-toolkit.js
content/extensions/ext-toolkit.json
content/extensions/ext-topSites.js
content/extensions/ext-webRequest.js
content/extensions/ext-webNavigation.js

View File

@@ -21,7 +21,6 @@ EXTRA_JS_MODULES += [
'ExtensionSettingsStore.jsm',
'ExtensionStorage.jsm',
'ExtensionStorageSync.jsm',
'ExtensionTabs.jsm',
'ExtensionUtils.jsm',
'LegacyExtensionsUtils.jsm',
'MessageChannel.jsm',

Some files were not shown because too many files have changed in this diff Show More