merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
159
browser/components/extensions/ext-browser.json
Normal file
159
browser/components/extensions/ext-browser.json
Normal 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"]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -138,7 +138,7 @@ function runTest() {
|
||||
win.focus();
|
||||
}, 0);
|
||||
}, {once: true});
|
||||
iframe.src = "data:text/html,";
|
||||
iframe.srcdoc = "foo";
|
||||
}, SimpleTest.finish);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -262,12 +262,6 @@ gfxAndroidPlatform::GetFTLibrary()
|
||||
return gPlatformFTLibrary;
|
||||
}
|
||||
|
||||
already_AddRefed<ScaledFont>
|
||||
gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
|
||||
{
|
||||
return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxAndroidPlatform::FontHintingEnabled()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,8 @@ struct GCPolicy<mozilla::Maybe<T>>
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct GCPolicy<JS::Realm*>; // see Realm.h
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // GCPolicyAPI_h
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -119,6 +119,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
||||
destroyCompartmentCallback(nullptr),
|
||||
sizeOfIncludingThisCompartmentCallback(nullptr),
|
||||
compartmentNameCallback(nullptr),
|
||||
destroyRealmCallback(nullptr),
|
||||
realmNameCallback(nullptr),
|
||||
externalStringSizeofCallback(nullptr),
|
||||
securityCallbacks(&NullSecurityCallbacks),
|
||||
DOMcallbacks(nullptr),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -96,7 +96,6 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx,
|
||||
mComponents(nullptr),
|
||||
mNext(nullptr),
|
||||
mGlobalJSObject(aGlobal),
|
||||
mHasCallInterpositions(false),
|
||||
mIsContentXBLScope(false),
|
||||
mIsAddonScope(false)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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=[
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
mar==2.1.2
|
||||
backports.lzma==0.0.8
|
||||
redo
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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});
|
||||
@@ -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: {
|
||||
|
||||
178
toolkit/components/extensions/ext-toolkit.json
Normal file
178
toolkit/components/extensions/ext-toolkit.json
Normal 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"]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user