merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2017-02-07 14:08:46 +01:00
commit 092e5dc5f1
80 changed files with 4044 additions and 1018 deletions

View File

@ -461,11 +461,15 @@ DocAccessibleParent::Destroy()
return;
}
NS_ASSERTION(mChildDocs.IsEmpty(),
"why weren't the child docs destroyed already?");
mShutdown = true;
uint32_t childDocCount = mChildDocs.Length();
for (uint32_t i = 0; i < childDocCount; i++) {
for (uint32_t j = i + 1; j < childDocCount; j++) {
MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
}
}
for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
mChildDocs[i]->Destroy();

View File

@ -221,7 +221,7 @@ BrowserAction.prototype = {
// If we have a pending pre-loaded popup, cancel it after we've waited
// long enough that we can be relatively certain it won't be opening.
if (this.pendingPopup) {
let {node} = this.widget.forWindow(window);
let node = window.gBrowser && this.widget.forWindow(window).node;
if (isAncestorOrSelf(node, event.originalTarget)) {
this.pendingPopupTimeout = setTimeout(() => this.clearPopup(),
POPUP_PRELOAD_TIMEOUT_MS);

View File

@ -58,9 +58,9 @@ extensions.on("page-shutdown", (type, context) => {
}
let {gBrowser} = context.xulBrowser.ownerGlobal;
if (gBrowser) {
let tab = gBrowser.getTabForBrowser(context.xulBrowser);
if (tab) {
gBrowser.removeTab(tab);
let nativeTab = gBrowser.getTabForBrowser(context.xulBrowser);
if (nativeTab) {
gBrowser.removeTab(nativeTab);
}
}
}
@ -83,16 +83,16 @@ let tabListener = {
onLocationChange(browser, webProgress, request, locationURI, flags) {
if (webProgress.isTopLevel) {
let {gBrowser} = browser.ownerGlobal;
let tab = gBrowser.getTabForBrowser(browser);
let nativeTab = gBrowser.getTabForBrowser(browser);
// Now we are certain that the first page in the tab was loaded.
this.initializingTabs.delete(tab);
this.initializingTabs.delete(nativeTab);
// browser.innerWindowID is now set, resolve the promises if any.
let deferred = this.tabReadyPromises.get(tab);
let deferred = this.tabReadyPromises.get(nativeTab);
if (deferred) {
deferred.resolve(tab);
this.tabReadyPromises.delete(tab);
deferred.resolve(nativeTab);
this.tabReadyPromises.delete(nativeTab);
}
}
},
@ -103,19 +103,20 @@ let tabListener = {
* changes to the requested URL. Other tabs are assumed to be ready once their
* inner window ID is known.
*
* @param {XULElement} tab The <tab> element.
* @param {XULElement} nativeTab The <tab> element.
* @returns {Promise} Resolves with the given tab once ready.
*/
awaitTabReady(tab) {
let deferred = this.tabReadyPromises.get(tab);
awaitTabReady(nativeTab) {
let deferred = this.tabReadyPromises.get(nativeTab);
if (!deferred) {
deferred = PromiseUtils.defer();
if (!this.initializingTabs.has(tab) && (tab.linkedBrowser.innerWindowID ||
tab.linkedBrowser.currentURI.spec === "about:blank")) {
deferred.resolve(tab);
if (!this.initializingTabs.has(nativeTab) &&
(nativeTab.linkedBrowser.innerWindowID ||
nativeTab.linkedBrowser.currentURI.spec === "about:blank")) {
deferred.resolve(nativeTab);
} else {
this.initTabReady();
this.tabReadyPromises.set(tab, deferred);
this.tabReadyPromises.set(nativeTab, deferred);
}
}
return deferred.promise;
@ -142,7 +143,7 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
tab = tabManager.getWrapper(tabTracker.activeTab);
}
await tabListener.awaitTabReady(tab.tab);
await tabListener.awaitTabReady(tab.nativeTab);
return tab;
}
@ -150,15 +151,15 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let self = {
tabs: {
onActivated: new WindowEventManager(context, "tabs.onActivated", "TabSelect", (fire, event) => {
let tab = event.originalTarget;
let tabId = tabTracker.getId(tab);
let windowId = windowTracker.getId(tab.ownerGlobal);
let nativeTab = event.originalTarget;
let tabId = tabTracker.getId(nativeTab);
let windowId = windowTracker.getId(nativeTab.ownerGlobal);
fire.async({tabId, windowId});
}).api(),
onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
let listener = (eventName, event) => {
fire.async(tabManager.convert(event.tab));
fire.async(tabManager.convert(event.nativeTab));
};
tabTracker.on("tab-created", listener);
@ -174,9 +175,9 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onHighlighted
*/
onHighlighted: new WindowEventManager(context, "tabs.onHighlighted", "TabSelect", (fire, event) => {
let tab = event.originalTarget;
let tabIds = [tabTracker.getId(tab)];
let windowId = windowTracker.getId(tab.ownerGlobal);
let nativeTab = event.originalTarget;
let tabIds = [tabTracker.getId(nativeTab)];
let windowId = windowTracker.getId(nativeTab.ownerGlobal);
fire.async({tabIds, windowId});
}).api(),
@ -236,17 +237,17 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
};
let moveListener = event => {
let tab = event.originalTarget;
let nativeTab = event.originalTarget;
if (ignoreNextMove.has(tab)) {
ignoreNextMove.delete(tab);
if (ignoreNextMove.has(nativeTab)) {
ignoreNextMove.delete(nativeTab);
return;
}
fire.async(tabTracker.getId(tab), {
windowId: windowTracker.getId(tab.ownerGlobal),
fire.async(tabTracker.getId(nativeTab), {
windowId: windowTracker.getId(nativeTab.ownerGlobal),
fromIndex: event.detail,
toIndex: tab._tPos,
toIndex: nativeTab._tPos,
});
};
@ -400,22 +401,22 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
options.disallowInheritPrincipal = true;
tabListener.initTabReady();
let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
let active = true;
if (createProperties.active !== null) {
active = createProperties.active;
}
if (active) {
window.gBrowser.selectedTab = tab;
window.gBrowser.selectedTab = nativeTab;
}
if (createProperties.index !== null) {
window.gBrowser.moveTabTo(tab, createProperties.index);
window.gBrowser.moveTabTo(nativeTab, createProperties.index);
}
if (createProperties.pinned) {
window.gBrowser.pinTab(tab);
window.gBrowser.pinTab(nativeTab);
}
if (active && !url) {
@ -431,10 +432,10 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
// `executeScript` wait until the requested URL is loaded in
// the tab before dispatching messages to the inner window
// that contains the URL we're attempting to load.
tabListener.initializingTabs.add(tab);
tabListener.initializingTabs.add(nativeTab);
}
return tabManager.convert(tab);
return tabManager.convert(nativeTab);
});
},
@ -444,15 +445,15 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
}
for (let tabId of tabs) {
let tab = tabTracker.getTab(tabId);
tab.ownerGlobal.gBrowser.removeTab(tab);
let nativeTab = tabTracker.getTab(tabId);
nativeTab.ownerGlobal.gBrowser.removeTab(nativeTab);
}
},
async update(tabId, updateProperties) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let tabbrowser = tab.ownerGlobal.gBrowser;
let tabbrowser = nativeTab.ownerGlobal.gBrowser;
if (updateProperties.url !== null) {
let url = context.uri.resolve(updateProperties.url);
@ -461,55 +462,53 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
return Promise.reject({message: `Illegal URL: ${url}`});
}
tab.linkedBrowser.loadURI(url);
nativeTab.linkedBrowser.loadURI(url);
}
if (updateProperties.active !== null) {
if (updateProperties.active) {
tabbrowser.selectedTab = tab;
tabbrowser.selectedTab = nativeTab;
} else {
// Not sure what to do here? Which tab should we select?
}
}
if (updateProperties.muted !== null) {
if (tab.muted != updateProperties.muted) {
tab.toggleMuteAudio(extension.uuid);
if (nativeTab.muted != updateProperties.muted) {
nativeTab.toggleMuteAudio(extension.uuid);
}
}
if (updateProperties.pinned !== null) {
if (updateProperties.pinned) {
tabbrowser.pinTab(tab);
tabbrowser.pinTab(nativeTab);
} else {
tabbrowser.unpinTab(tab);
tabbrowser.unpinTab(nativeTab);
}
}
// FIXME: highlighted/selected, openerTabId
return tabManager.convert(tab);
return tabManager.convert(nativeTab);
},
async reload(tabId, reloadProperties) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
if (reloadProperties && reloadProperties.bypassCache) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
}
tab.linkedBrowser.reloadWithFlags(flags);
nativeTab.linkedBrowser.reloadWithFlags(flags);
},
async get(tabId) {
let tab = tabTracker.getTab(tabId);
return tabManager.convert(tab);
return tabManager.get(tabId).convert();
},
getCurrent() {
let tab;
let tabData;
if (context.tabId) {
tab = tabManager.get(context.tabId).convert();
tabData = tabManager.get(context.tabId).convert();
}
return Promise.resolve(tab);
return Promise.resolve(tabData);
},
async query(queryInfo) {
@ -526,53 +525,21 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
tab => tab.convert());
},
captureVisibleTab(windowId, options) {
if (!extension.hasPermission("<all_urls>")) {
return Promise.reject({message: "The <all_urls> permission is required to use the captureVisibleTab API"});
}
async captureVisibleTab(windowId, options) {
let window = windowId == null ?
windowTracker.topWindow :
windowTracker.getWindow(windowId, context);
let tab = window.gBrowser.selectedTab;
return tabListener.awaitTabReady(tab).then(() => {
let browser = tab.linkedBrowser;
let recipient = {
innerWindowID: browser.innerWindowID,
};
let tab = tabManager.wrapTab(window.gBrowser.selectedTab);
await tabListener.awaitTabReady(tab.nativeTab);
if (!options) {
options = {};
}
if (options.format == null) {
options.format = "png";
}
if (options.quality == null) {
options.quality = 92;
}
let message = {
options,
width: browser.clientWidth,
height: browser.clientHeight,
};
return context.sendMessage(browser.messageManager, "Extension:Capture",
message, {recipient});
});
return tab.capture(context, options);
},
async detectLanguage(tabId) {
let tab = getTabOrActive(tabId);
let tab = await promiseTabWhenReady(tabId);
return tabListener.awaitTabReady(tab).then(() => {
let browser = tab.linkedBrowser;
let recipient = {innerWindowID: browser.innerWindowID};
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
{}, {recipient});
});
return tab.sendMessage(context, "Extension:DetectLanguage");
},
async executeScript(tabId, details) {
@ -619,9 +586,9 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let indexMap = new Map();
let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
for (let tab of tabs) {
for (let nativeTab of tabs) {
// If the window is not specified, use the window from the tab.
let window = destinationWindow || tab.ownerGlobal;
let window = destinationWindow || nativeTab.ownerGlobal;
let gBrowser = window.gBrowser;
let insertionPoint = indexMap.get(window) || index;
@ -635,32 +602,32 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
// be moved to a position after the current set of pinned tabs.
// Attempts to move a tab to an illegal position are ignored.
let numPinned = gBrowser._numPinnedTabs;
let ok = tab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
let ok = nativeTab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
if (!ok) {
continue;
}
indexMap.set(window, insertionPoint + 1);
if (tab.ownerGlobal != window) {
if (nativeTab.ownerGlobal != window) {
// If the window we are moving the tab in is different, then move the tab
// to the new window.
tab = gBrowser.adoptTab(tab, insertionPoint, false);
nativeTab = gBrowser.adoptTab(nativeTab, insertionPoint, false);
} else {
// If the window we are moving is the same, just move the tab.
gBrowser.moveTabTo(tab, insertionPoint);
gBrowser.moveTabTo(nativeTab, insertionPoint);
}
tabsMoved.push(tab);
tabsMoved.push(nativeTab);
}
return tabsMoved.map(tab => tabManager.convert(tab));
return tabsMoved.map(nativeTab => tabManager.convert(nativeTab));
},
duplicate(tabId) {
let tab = tabTracker.getTab(tabId);
let nativeTab = tabTracker.getTab(tabId);
let gBrowser = tab.ownerGlobal.gBrowser;
let newTab = gBrowser.duplicateTab(tab);
let gBrowser = nativeTab.ownerGlobal.gBrowser;
let newTab = gBrowser.duplicateTab(nativeTab);
return new Promise(resolve => {
// We need to use SSTabRestoring because any attributes set before
@ -671,10 +638,10 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
// Pinned tabs that are duplicated are inserted
// after the existing pinned tab and pinned.
if (tab.pinned) {
if (nativeTab.pinned) {
gBrowser.pinTab(newTab);
}
gBrowser.moveTabTo(newTab, tab._tPos + 1);
gBrowser.moveTabTo(newTab, nativeTab._tPos + 1);
}, {once: true});
newTab.addEventListener("SSTabRestored", function() {
@ -687,24 +654,24 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
},
getZoom(tabId) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let {ZoomManager} = tab.ownerGlobal;
let zoom = ZoomManager.getZoomForBrowser(tab.linkedBrowser);
let {ZoomManager} = nativeTab.ownerGlobal;
let zoom = ZoomManager.getZoomForBrowser(nativeTab.linkedBrowser);
return Promise.resolve(zoom);
},
setZoom(tabId, zoom) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let {FullZoom, ZoomManager} = tab.ownerGlobal;
let {FullZoom, ZoomManager} = nativeTab.ownerGlobal;
if (zoom === 0) {
// A value of zero means use the default zoom factor.
return FullZoom.reset(tab.linkedBrowser);
return FullZoom.reset(nativeTab.linkedBrowser);
} else if (zoom >= ZoomManager.MIN && zoom <= ZoomManager.MAX) {
FullZoom.setZoom(zoom, tab.linkedBrowser);
FullZoom.setZoom(zoom, nativeTab.linkedBrowser);
} else {
return Promise.reject({
message: `Zoom value ${zoom} out of range (must be between ${ZoomManager.MIN} and ${ZoomManager.MAX})`,
@ -715,9 +682,9 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
},
_getZoomSettings(tabId) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let {FullZoom} = tab.ownerGlobal;
let {FullZoom} = nativeTab.ownerGlobal;
return {
mode: "automatic",
@ -731,9 +698,9 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
},
setZoomSettings(tabId, settings) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let currentSettings = this._getZoomSettings(tab.id);
let currentSettings = this._getZoomSettings(tabTracker.getId(nativeTab));
if (!Object.keys(settings).every(key => settings[key] === currentSettings[key])) {
return Promise.reject(`Unsupported zoom settings: ${JSON.stringify(settings)}`);
@ -754,14 +721,14 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
// Store the zoom level for all existing tabs.
for (let window of windowTracker.browserWindows()) {
for (let tab of window.gBrowser.tabs) {
let browser = tab.linkedBrowser;
for (let nativeTab of window.gBrowser.tabs) {
let browser = nativeTab.linkedBrowser;
zoomLevels.set(browser, getZoomLevel(browser));
}
}
let tabCreated = (eventName, event) => {
let browser = event.tab.linkedBrowser;
let browser = event.nativeTab.linkedBrowser;
zoomLevels.set(browser, getZoomLevel(browser));
};
@ -778,8 +745,8 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
}
let {gBrowser} = browser.ownerGlobal;
let tab = gBrowser.getTabForBrowser(browser);
if (!tab) {
let nativeTab = gBrowser.getTabForBrowser(browser);
if (!nativeTab) {
// We only care about zoom events in the top-level browser of a tab.
return;
}
@ -790,7 +757,7 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
if (oldZoomFactor != newZoomFactor) {
zoomLevels.set(browser, newZoomFactor);
let tabId = tabTracker.getId(tab);
let tabId = tabTracker.getId(nativeTab);
fire.async({
tabId,
oldZoomFactor,

View File

@ -49,23 +49,23 @@ global.TabContext = function TabContext(getDefaults, extension) {
};
TabContext.prototype = {
get(tab) {
if (!this.tabData.has(tab)) {
this.tabData.set(tab, this.getDefaults(tab));
get(nativeTab) {
if (!this.tabData.has(nativeTab)) {
this.tabData.set(nativeTab, this.getDefaults(nativeTab));
}
return this.tabData.get(tab);
return this.tabData.get(nativeTab);
},
clear(tab) {
this.tabData.delete(tab);
clear(nativeTab) {
this.tabData.delete(nativeTab);
},
handleEvent(event) {
if (event.type == "TabSelect") {
let tab = event.target;
this.emit("tab-select", tab);
this.emit("location-change", tab);
let nativeTab = event.target;
this.emit("tab-select", nativeTab);
this.emit("location-change", nativeTab);
}
},
@ -83,8 +83,8 @@ TabContext.prototype = {
let lastLocation = this.lastLocation.get(browser);
if (browser === gBrowser.selectedBrowser &&
!(lastLocation && lastLocation.equalsExceptRef(browser.currentURI))) {
let tab = gBrowser.getTabForBrowser(browser);
this.emit("location-change", tab, true);
let nativeTab = gBrowser.getTabForBrowser(browser);
this.emit("location-change", nativeTab, true);
}
this.lastLocation.set(browser, browser.currentURI);
},
@ -106,6 +106,21 @@ class WindowTracker extends WindowTrackerBase {
}
}
/**
* 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 SingletonEventManager {
constructor(context, name, event, listener) {
super(context, name, fire => {
@ -152,28 +167,28 @@ class TabTracker extends TabTrackerBase {
/* eslint-enable mozilla/balanced-listeners */
}
getId(tab) {
if (this._tabs.has(tab)) {
return this._tabs.get(tab);
getId(nativeTab) {
if (this._tabs.has(nativeTab)) {
return this._tabs.get(nativeTab);
}
this.init();
let id = this._nextId++;
this.setId(tab, id);
this.setId(nativeTab, id);
return id;
}
setId(tab, id) {
this._tabs.set(tab, id);
this._tabIds.set(id, tab);
setId(nativeTab, id) {
this._tabs.set(nativeTab, id);
this._tabIds.set(id, nativeTab);
}
_handleTabDestroyed(event, {tab}) {
let id = this._tabs.get(tab);
_handleTabDestroyed(event, {nativeTab}) {
let id = this._tabs.get(nativeTab);
if (id) {
this._tabs.delete(tab);
if (this._tabIds.get(id) === tab) {
this._tabs.delete(nativeTab);
if (this._tabIds.get(id) === nativeTab) {
this._tabIds.delete(id);
}
}
@ -192,9 +207,9 @@ class TabTracker extends TabTrackerBase {
* A XUL <tab> element.
*/
getTab(tabId, default_ = undefined) {
let tab = this._tabIds.get(tabId);
if (tab) {
return tab;
let nativeTab = this._tabIds.get(tabId);
if (nativeTab) {
return nativeTab;
}
if (default_ !== undefined) {
return default_;
@ -202,8 +217,13 @@ class TabTracker extends TabTrackerBase {
throw new ExtensionError(`Invalid tab ID: ${tabId}`);
}
/**
* @param {Event} event
* The DOM Event to handle.
* @private
*/
handleEvent(event) {
let tab = event.target;
let nativeTab = event.target;
switch (event.type) {
case "TabOpen":
@ -213,10 +233,10 @@ class TabTracker extends TabTrackerBase {
// 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(tab, this.getId(adoptedTab));
this.setId(nativeTab, this.getId(adoptedTab));
adoptedTab.linkedBrowser.messageManager.sendAsyncMessage("Extension:SetTabAndWindowId", {
windowId: windowTracker.getId(tab.ownerGlobal),
windowId: windowTracker.getId(nativeTab.ownerGlobal),
});
}
@ -238,16 +258,24 @@ class TabTracker extends TabTrackerBase {
// 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(tab));
this.setId(adoptedBy, this.getId(nativeTab));
this.emitDetached(tab, adoptedBy);
this.emitDetached(nativeTab, adoptedBy);
} else {
this.emitRemoved(tab, false);
this.emitRemoved(nativeTab, false);
}
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
@ -259,16 +287,16 @@ class TabTracker extends TabTrackerBase {
// 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 tab = window.arguments[0];
let nativeTab = window.arguments[0];
let adoptedBy = window.gBrowser.tabs[0];
this.adoptedTabs.set(tab, adoptedBy);
this.setId(adoptedBy, this.getId(tab));
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.tab === tab) {
if (details.nativeTab === nativeTab) {
this.off("tab-detached", listener);
Promise.resolve().then(() => {
@ -279,43 +307,85 @@ class TabTracker extends TabTrackerBase {
this.on("tab-detached", listener);
} else {
for (let tab of window.gBrowser.tabs) {
this.emitCreated(tab);
for (let nativeTab of window.gBrowser.tabs) {
this.emitCreated(nativeTab);
}
}
}
/**
* 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 tab of window.gBrowser.tabs) {
if (this.adoptedTabs.has(tab)) {
this.emitDetached(tab, this.adoptedTabs.get(tab));
for (let nativeTab of window.gBrowser.tabs) {
if (this.adoptedTabs.has(nativeTab)) {
this.emitDetached(nativeTab, this.adoptedTabs.get(nativeTab));
} else {
this.emitRemoved(tab, true);
this.emitRemoved(nativeTab, true);
}
}
}
emitAttached(tab) {
let newWindowId = windowTracker.getId(tab.ownerGlobal);
let tabId = this.getId(tab);
/**
* 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", {tab, tabId, newWindowId, newPosition: tab._tPos});
this.emit("tab-attached", {nativeTab, tabId, newWindowId, newPosition: nativeTab._tPos});
}
emitDetached(tab, adoptedBy) {
let oldWindowId = windowTracker.getId(tab.ownerGlobal);
let tabId = this.getId(tab);
/**
* 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", {tab, adoptedBy, tabId, oldWindowId, oldPosition: tab._tPos});
this.emit("tab-detached", {nativeTab, adoptedBy, tabId, oldWindowId, oldPosition: nativeTab._tPos});
}
emitCreated(tab) {
this.emit("tab-created", {tab});
/**
* Emits a "tab-created" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being created.
* @private
*/
emitCreated(nativeTab) {
this.emit("tab-created", {nativeTab});
}
emitRemoved(tab, isWindowClosing) {
let windowId = windowTracker.getId(tab.ownerGlobal);
let tabId = this.getId(tab);
/**
* 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
@ -327,7 +397,7 @@ class TabTracker extends TabTrackerBase {
// event listener is registered. To make sure that the event listener is
// notified, we dispatch `tabs.onRemoved` asynchronously.
Services.tm.mainThread.dispatch(() => {
this.emit("tab-removed", {tab, tabId, windowId, isWindowClosing});
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
}, Ci.nsIThread.DISPATCH_NORMAL);
}
@ -351,9 +421,9 @@ class TabTracker extends TabTrackerBase {
if (gBrowser && gBrowser.getTabForBrowser) {
result.windowId = windowTracker.getId(browser.ownerGlobal);
let tab = gBrowser.getTabForBrowser(browser);
if (tab) {
result.tabId = this.getId(tab);
let nativeTab = gBrowser.getTabForBrowser(browser);
if (nativeTab) {
result.tabId = this.getId(nativeTab);
}
}
@ -376,19 +446,19 @@ Object.assign(global, {tabTracker, windowTracker});
class Tab extends TabBase {
get _favIconUrl() {
return this.window.gBrowser.getIcon(this.tab);
return this.window.gBrowser.getIcon(this.nativeTab);
}
get audible() {
return this.tab.soundPlaying;
return this.nativeTab.soundPlaying;
}
get browser() {
return this.tab.linkedBrowser;
return this.nativeTab.linkedBrowser;
}
get cookieStoreId() {
return getCookieStoreIdForTab(this, this.tab);
return getCookieStoreIdForTab(this, this.nativeTab);
}
get height() {
@ -396,41 +466,37 @@ class Tab extends TabBase {
}
get index() {
return this.tab._tPos;
}
get innerWindowID() {
return this.browser.innerWindowID;
return this.nativeTab._tPos;
}
get mutedInfo() {
let tab = this.tab;
let {nativeTab} = this;
let mutedInfo = {muted: tab.muted};
if (tab.muteReason === null) {
let mutedInfo = {muted: nativeTab.muted};
if (nativeTab.muteReason === null) {
mutedInfo.reason = "user";
} else if (tab.muteReason) {
} else if (nativeTab.muteReason) {
mutedInfo.reason = "extension";
mutedInfo.extensionId = tab.muteReason;
mutedInfo.extensionId = nativeTab.muteReason;
}
return mutedInfo;
}
get pinned() {
return this.tab.pinned;
return this.nativeTab.pinned;
}
get active() {
return this.tab.selected;
return this.nativeTab.selected;
}
get selected() {
return this.tab.selected;
return this.nativeTab.selected;
}
get status() {
if (this.tab.getAttribute("busy") === "true") {
if (this.nativeTab.getAttribute("busy") === "true") {
return "loading";
}
return "complete";
@ -441,31 +507,47 @@ class Tab extends TabBase {
}
get window() {
return this.tab.ownerGlobal;
return this.nativeTab.ownerGlobal;
}
get windowId() {
return windowTracker.getId(this.window);
}
static convertFromSessionStoreClosedData(extension, tab, window = null) {
/**
* 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(tab.closedId),
index: tab.pos ? tab.pos : 0,
sessionId: String(tabData.closedId),
index: tabData.pos ? tabData.pos : 0,
windowId: window && windowTracker.getId(window),
selected: false,
highlighted: false,
active: false,
pinned: false,
incognito: Boolean(tab.state && tab.state.isPrivate),
incognito: Boolean(tabData.state && tabData.state.isPrivate),
};
if (extension.tabManager.hasTabPermission(tab)) {
let entries = tab.state ? tab.state.entries : tab.entries;
if (extension.tabManager.hasTabPermission(tabData)) {
let entries = tabData.state ? tabData.state.entries : tabData.entries;
result.url = entries[0].url;
result.title = entries[0].title;
if (tab.image) {
result.favIconUrl = tab.image;
if (tabData.image) {
result.favIconUrl = tabData.image;
}
}
@ -474,6 +556,22 @@ class Tab extends TabBase {
}
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;
@ -582,25 +680,38 @@ class Window extends WindowBase {
* getTabs() {
let {tabManager} = this.extension;
for (let tab of this.window.gBrowser.tabs) {
yield tabManager.getWrapper(tab);
for (let nativeTab of this.window.gBrowser.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
static convertFromSessionStoreClosedData(extension, 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} windowData
* Session store data for a closed window, as returned by
* `SessionStore.getClosedWindowData()`.
*
* @returns {Object}
* @static
*/
static convertFromSessionStoreClosedData(extension, windowData) {
let result = {
sessionId: String(window.closedId),
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(window),
state: this.getState(windowData),
alwaysOnTop: false,
};
if (window.tabs.length) {
result.tabs = window.tabs.map(tab => {
return Tab.convertFromSessionStoreClosedData(extension, tab);
if (windowData.tabs.length) {
result.tabs = windowData.tabs.map(tabData => {
return Tab.convertFromSessionStoreClosedData(extension, tabData);
});
}
@ -612,24 +723,24 @@ Object.assign(global, {Tab, Window});
class TabManager extends TabManagerBase {
get(tabId, default_ = undefined) {
let tab = tabTracker.getTab(tabId, default_);
let nativeTab = tabTracker.getTab(tabId, default_);
if (tab) {
return this.getWrapper(tab);
if (nativeTab) {
return this.getWrapper(nativeTab);
}
return default_;
}
addActiveTabPermission(tab = tabTracker.activeTab) {
return super.addActiveTabPermission(tab);
addActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.addActiveTabPermission(nativeTab);
}
revokeActiveTabPermission(tab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(tab);
revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(nativeTab);
}
wrapTab(tab) {
return new Tab(this.extension, tab, tabTracker.getId(tab));
wrapTab(nativeTab) {
return new Tab(this.extension, nativeTab, tabTracker.getId(nativeTab));
}
}

View File

@ -5,6 +5,8 @@
let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head><body>${url}</body></html>`;
add_task(function* testBrowserActionClickCanceled() {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {
@ -27,7 +29,6 @@ add_task(function* testBrowserActionClickCanceled() {
let browserAction = browserActionFor(ext);
let widget = getBrowserActionWidget(extension).forWindow(window);
let tab = window.gBrowser.selectedTab;
// Test canceled click.
EventUtils.synthesizeMouseAtCenter(widget.node, {type: "mousedown", button: 0}, window);
@ -76,6 +77,8 @@ add_task(function* testBrowserActionClickCanceled() {
yield closeBrowserAction(extension);
yield extension.unload();
yield BrowserTestUtils.removeTab(tab);
});
add_task(function* testBrowserActionDisabled() {

View File

@ -109,6 +109,7 @@ def rust_triple_alias(host_or_target):
# NetBSD
('x86_64', 'NetBSD'): 'x86_64-unknown-netbsd',
# OpenBSD
('x86', 'OpenBSD'): 'i686-unknown-openbsd',
('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd',
# Linux
('x86', 'Linux'): 'i686-unknown-linux-gnu',

View File

@ -139,7 +139,7 @@ CopyURIs(const InfallibleTArray<URIParams>& aDomains, nsIDomainSet* aSet)
}
void
DomainPolicy::ApplyClone(DomainPolicyClone* aClone)
DomainPolicy::ApplyClone(const DomainPolicyClone* aClone)
{
CopyURIs(aClone->blacklist(), mBlacklist);
CopyURIs(aClone->whitelist(), mWhitelist);

View File

@ -17,6 +17,7 @@ class DomainPolicyClone;
%}
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
[ptr] native DomainPolicyCloneConstPtr(const mozilla::dom::DomainPolicyClone);
/*
* When a domain policy is instantiated by invoking activateDomainPolicy() on
@ -41,7 +42,7 @@ interface nsIDomainPolicy : nsISupports
void deactivate();
[noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone);
[noscript, notxpcom] void applyClone(in DomainPolicyClonePtr aClone);
[noscript, notxpcom] void applyClone(in DomainPolicyCloneConstPtr aClone);
};
[scriptable, builtinclass, uuid(665c981b-0a0f-4229-ac06-a826e02d4f69)]

View File

@ -59,9 +59,9 @@ exports.viewSourceInDebugger = Task.async(function* (toolbox, sourceURL, sourceL
// New debugger frontend
if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
yield toolbox.selectTool("jsdebugger");
const source = dbg._selectors().getSourceByURL(dbg._getState(), sourceURL);
if (source) {
yield toolbox.selectTool("jsdebugger");
dbg._actions().selectSourceURL(sourceURL, { line: sourceLine });
return true;
}

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
EXPORTS.mozilla.dom.battery += [
'Constants.h',
'Types.h',

View File

@ -15446,6 +15446,7 @@ class CGCallback(CGClass):
setupCall = fill(
"""
MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
if (!aExecutionReason) {
aExecutionReason = "${executionReason}";
}

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
TEST_DIRS += ['test']
XPIDL_SOURCES += [

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
EXPORTS.mozilla.dom += [
'BroadcastChannel.h',
]

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
EXPORTS.mozilla += [
'BrowserElementParent.h',
]

4
dom/cache/moz.build vendored
View File

@ -4,6 +4,10 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
EXPORTS.mozilla.dom.cache += [
'Action.h',
'ActorChild.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "Embedding: APIs")
XPIDL_SOURCES += [
'nsICommandManager.idl',
'nsICommandParams.idl',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
XPIDL_SOURCES += [
'nsIConsoleAPIStorage.idl',
]

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Security")
EXPORTS.mozilla.dom += [
'CryptoBuffer.h',
'CryptoKey.h',

View File

@ -4,6 +4,10 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
EXPORTS += [
'DeviceStorage.h',
'DeviceStorageFileDescriptor.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Flyweb")
EXPORTS.mozilla.dom += [
'FlyWebDiscoveryManager.h',
'FlyWebPublishedServer.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
IPDL_SOURCES += [
'ipc/GamepadEventTypes.ipdlh',
'ipc/PGamepadEventChannel.ipdl',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "Geolocation")
EXPORTS += [
'nsGeoPosition.h',
'nsGeoPositionIPCSerialiser.h',

View File

@ -502,10 +502,24 @@ NS_INTERFACE_MAP_BEGIN(ContentChild)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
NS_INTERFACE_MAP_END
mozilla::ipc::IPCResult
ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
{
mLookAndFeelCache = aLookAndFeelIntCache;
InitXPCOM(aXPCOMInit, aInitialData);
InitGraphicsDeviceData();
return IPC_OK();
}
bool
ContentChild::Init(MessageLoop* aIOLoop,
base::ProcessId aParentPid,
IPC::Channel* aChannel)
IPC::Channel* aChannel,
uint64_t aChildID,
bool aIsForBrowser)
{
#ifdef MOZ_WIDGET_GTK
// We need to pass a display down to gtk_init because it's not going to
@ -571,7 +585,8 @@ ContentChild::Init(MessageLoop* aIOLoop,
XRE_GetProcessType());
#endif
SendGetProcessAttributes(&mID, &mIsForBrowser);
mID = aChildID;
mIsForBrowser = aIsForBrowser;
#ifdef NS_PRINTING
// Force the creation of the nsPrintingProxy so that it's IPC counterpart,
@ -933,8 +948,14 @@ ContentChild::InitGraphicsDeviceData()
}
void
ContentChild::InitXPCOM()
ContentChild::InitXPCOM(const XPCOMInitData& aXPCOMInit,
const mozilla::dom::ipc::StructuredCloneData& aInitialData)
{
SET_PREF_PHASE(pref_initPhase::BEGIN_ALL_PREFS);
for (unsigned int i = 0; i < aXPCOMInit.prefs().Length(); i++) {
Preferences::SetPreference(aXPCOMInit.prefs().ElementAt(i));
}
SET_PREF_PHASE(pref_initPhase::END_ALL_PREFS);
// Do this as early as possible to get the parent process to initialize the
// background thread since we'll likely need database information very soon.
BackgroundChild::Startup();
@ -957,42 +978,29 @@ ContentChild::InitXPCOM()
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
NS_WARNING("Couldn't register console listener for child process");
bool isOffline, isLangRTL, haveBidiKeyboards;
bool isConnected;
int32_t captivePortalState;
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
StructuredCloneData initialData;
OptionalURIParams userContentSheetURL;
mAvailableDictionaries = aXPCOMInit.dictionaries();
SendGetXPCOMProcessAttributes(&isOffline, &isConnected, &captivePortalState,
&isLangRTL, &haveBidiKeyboards,
&mAvailableDictionaries,
&clipboardCaps, &domainPolicy, &initialData,
&mFontFamilies, &userContentSheetURL,
&mLookAndFeelCache);
RecvSetOffline(isOffline);
RecvSetConnectivity(isConnected);
RecvSetCaptivePortalState(captivePortalState);
RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
RecvSetOffline(aXPCOMInit.isOffline());
RecvSetConnectivity(aXPCOMInit.isConnected());
RecvSetCaptivePortalState(aXPCOMInit.captivePortalState());
RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(), aXPCOMInit.haveBidiKeyboards());
// Create the CPOW manager as soon as possible.
SendPJavaScriptConstructor();
if (domainPolicy.active()) {
if (aXPCOMInit.domainPolicy().active()) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
if (!mPolicy) {
MOZ_CRASH("Failed to activate domain policy.");
}
mPolicy->ApplyClone(&domainPolicy);
mPolicy->ApplyClone(&aXPCOMInit.domainPolicy());
}
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
clipboardProxy->SetCapabilities(clipboardCaps);
clipboardProxy->SetCapabilities(aXPCOMInit.clipboardCaps());
}
{
@ -1002,7 +1010,9 @@ ContentChild::InitXPCOM()
}
ErrorResult rv;
JS::RootedValue data(jsapi.cx());
initialData.Read(jsapi.cx(), &data, rv);
mozilla::dom::ipc::StructuredCloneData id;
id.Copy(aInitialData);
id.Read(jsapi.cx(), &data, rv);
if (NS_WARN_IF(rv.Failed())) {
MOZ_CRASH();
}
@ -1011,7 +1021,7 @@ ContentChild::InitXPCOM()
}
// The stylesheet cache is not ready yet. Store this URL for future use.
nsCOMPtr<nsIURI> ucsURL = DeserializeURI(userContentSheetURL);
nsCOMPtr<nsIURI> ucsURL = DeserializeURI(aXPCOMInit.userContentSheetURL());
nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
// This will register cross-process observer.

View File

@ -96,9 +96,12 @@ public:
bool Init(MessageLoop* aIOLoop,
base::ProcessId aParentPid,
IPC::Channel* aChannel);
IPC::Channel* aChannel,
uint64_t aChildID,
bool aIsForBrowser);
void InitXPCOM();
void InitXPCOM(const XPCOMInitData& aXPCOMInit,
const mozilla::dom::ipc::StructuredCloneData& aInitialData);
void InitGraphicsDeviceData();
@ -573,6 +576,11 @@ public:
const bool& minimizeMemoryUsage,
const MaybeFileDesc& DMDFile) override;
virtual mozilla::ipc::IPCResult
RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
const StructuredCloneData& aInitialData,
nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
#if defined(XP_WIN) && defined(ACCESSIBILITY)
bool
SendGetA11yContentId();

View File

@ -186,6 +186,8 @@
#include "nsLayoutStylesheetCache.h"
#include "ContentPrefs.h"
#ifdef MOZ_WEBRTC
#include "signaling/src/peerconnection/WebrtcGlobalParent.h"
#endif
@ -1824,6 +1826,48 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
std::vector<std::string> extraArgs;
extraArgs.push_back("-childID");
char idStr[21];
SprintfLiteral(idStr, "%" PRId64, static_cast<uint64_t>(mChildID));
extraArgs.push_back(idStr);
extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser");
std::stringstream boolPrefs;
std::stringstream intPrefs;
std::stringstream stringPrefs;
size_t prefsLen;
ContentPrefs::GetContentPrefs(&prefsLen);
for (unsigned int i = 0; i < prefsLen; i++) {
MOZ_ASSERT(i == 0 || strcmp(ContentPrefs::GetContentPref(i), ContentPrefs::GetContentPref(i - 1)) > 0);
switch (Preferences::GetType(ContentPrefs::GetContentPref(i))) {
case nsIPrefBranch::PREF_INT:
intPrefs << i << ':' << Preferences::GetInt(ContentPrefs::GetContentPref(i)) << '|';
break;
case nsIPrefBranch::PREF_BOOL:
boolPrefs << i << ':' << Preferences::GetBool(ContentPrefs::GetContentPref(i)) << '|';
break;
case nsIPrefBranch::PREF_STRING: {
std::string value(Preferences::GetCString(ContentPrefs::GetContentPref(i)).get());
stringPrefs << i << ':' << value.length() << ':' << value << '|';
}
break;
case nsIPrefBranch::PREF_INVALID:
break;
default:
printf("preference type: %x\n", Preferences::GetType(ContentPrefs::GetContentPref(i)));
MOZ_CRASH();
}
}
extraArgs.push_back("-intPrefs");
extraArgs.push_back(intPrefs.str());
extraArgs.push_back("-boolPrefs");
extraArgs.push_back(boolPrefs.str());
extraArgs.push_back("-stringPrefs");
extraArgs.push_back(stringPrefs.str());
if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
MarkAsDead();
return false;
@ -1843,6 +1887,17 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
// Set a reply timeout for CPOWs.
SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_TIME_MS,
static_cast<uint32_t>((TimeStamp::Now() - mLaunchTS)
.ToMilliseconds()));
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-initializing", cpId.get());
}
return true;
}
@ -1900,6 +1955,90 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
bool aSetupOffMainThreadCompositing,
bool aSendRegisteredChrome)
{
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_TIME_MS,
static_cast<uint32_t>((TimeStamp::Now() - mLaunchTS)
.ToMilliseconds()));
XPCOMInitData xpcomInit;
Preferences::GetPreferences(&xpcomInit.prefs());
nsCOMPtr<nsIIOService> io(do_GetIOService());
MOZ_ASSERT(io, "No IO service?");
DebugOnly<nsresult> rv = io->GetOffline(&xpcomInit.isOffline());
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
rv = io->GetConnectivity(&xpcomInit.isConnected());
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
xpcomInit.captivePortalState() = nsICaptivePortalService::UNKNOWN;
nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
if (cps) {
cps->GetState(&xpcomInit.captivePortalState());
}
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
xpcomInit.isLangRTL() = false;
xpcomInit.haveBidiKeyboards() = false;
if (bidi) {
bidi->IsLangRTL(&xpcomInit.isLangRTL());
bidi->GetHaveBidiKeyboards(&xpcomInit.haveBidiKeyboards());
}
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
MOZ_ASSERT(spellChecker, "No spell checker?");
spellChecker->GetDictionaryList(&xpcomInit.dictionaries());
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
MOZ_ASSERT(clipboard, "No clipboard?");
rv = clipboard->SupportsSelectionClipboard(&xpcomInit.clipboardCaps().supportsSelectionClipboard());
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = clipboard->SupportsFindClipboard(&xpcomInit.clipboardCaps().supportsFindClipboard());
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Let's copy the domain policy from the parent to the child (if it's active).
StructuredCloneData initialData;
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
if (ssm) {
ssm->CloneDomainPolicy(&xpcomInit.domainPolicy());
if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
MOZ_CRASH();
}
JS::RootedValue init(jsapi.cx());
nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init);
if (NS_FAILED(result)) {
MOZ_CRASH();
}
ErrorResult rv;
initialData.Write(jsapi.cx(), init, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
MOZ_CRASH();
}
}
}
// This is only implemented (returns a non-empty list) by MacOSX at present.
gfxPlatform::GetPlatform()->GetSystemFontFamilyList(&xpcomInit.fontFamilies());
nsTArray<LookAndFeelInt> lnfCache = LookAndFeel::GetIntCache();
// Content processes have no permission to access profile directory, so we
// send the file URL instead.
StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
if (ucs) {
SerializeURI(ucs->GetSheetURI(), xpcomInit.userContentSheetURL());
} else {
SerializeURI(nullptr, xpcomInit.userContentSheetURL());
}
Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache);
if (aSendRegisteredChrome) {
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryChrome* chromeRegistry =
@ -2071,13 +2210,6 @@ ContentParent::Pid() const
return base::GetProcId(mSubprocess->GetChildProcessHandle());
}
mozilla::ipc::IPCResult
ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs)
{
Preferences::GetPreferences(aPrefs);
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars)
{
@ -2548,118 +2680,6 @@ ContentParent::RecvInitBackground(Endpoint<PBackgroundParent>&& aEndpoint)
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId,
bool* aIsForBrowser)
{
*aCpId = mChildID;
*aIsForBrowser = mIsForBrowser;
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
bool* aIsConnected,
int32_t* aCaptivePortalState,
bool* aIsLangRTL,
bool* aHaveBidiKeyboards,
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy,
StructuredCloneData* aInitialData,
InfallibleTArray<FontFamilyListEntry>* fontFamilies,
OptionalURIParams* aUserContentCSSURL,
nsTArray<LookAndFeelInt>* aLookAndFeelIntCache)
{
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_TIME_MS,
static_cast<uint32_t>((TimeStamp::Now() - mLaunchTS)
.ToMilliseconds()));
nsCOMPtr<nsIIOService> io(do_GetIOService());
MOZ_ASSERT(io, "No IO service?");
DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
rv = io->GetConnectivity(aIsConnected);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
*aCaptivePortalState = nsICaptivePortalService::UNKNOWN;
nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
if (cps) {
cps->GetState(aCaptivePortalState);
}
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
*aIsLangRTL = false;
*aHaveBidiKeyboards = false;
if (bidi) {
bidi->IsLangRTL(aIsLangRTL);
bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards);
}
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
MOZ_ASSERT(spellChecker, "No spell checker?");
spellChecker->GetDictionaryList(dictionaries);
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
MOZ_ASSERT(clipboard, "No clipboard?");
rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard());
MOZ_ASSERT(NS_SUCCEEDED(rv));
rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Let's copy the domain policy from the parent to the child (if it's active).
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(ssm, IPC_OK());
ssm->CloneDomainPolicy(domainPolicy);
if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
return IPC_FAIL_NO_REASON(this);
}
JS::RootedValue init(jsapi.cx());
nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init);
if (NS_FAILED(result)) {
return IPC_FAIL_NO_REASON(this);
}
ErrorResult rv;
aInitialData->Write(jsapi.cx(), init, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return IPC_FAIL_NO_REASON(this);
}
}
// This is only implemented (returns a non-empty list) by MacOSX at present.
gfxPlatform::GetPlatform()->GetSystemFontFamilyList(fontFamilies);
*aLookAndFeelIntCache = LookAndFeel::GetIntCache();
// Content processes have no permission to access profile directory, so we
// send the file URL instead.
StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
if (ucs) {
SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL);
} else {
SerializeURI(nullptr, *aUserContentCSSURL);
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
nsAutoString cpId;
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-initializing", cpId.get());
}
return IPC_OK();
}
mozilla::jsipc::PJavaScriptParent *
ContentParent::AllocPJavaScriptParent()
{

View File

@ -746,24 +746,6 @@ private:
virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint) override;
virtual mozilla::ipc::IPCResult RecvGetProcessAttributes(ContentParentId* aCpId,
bool* aIsForBrowser) override;
virtual mozilla::ipc::IPCResult
RecvGetXPCOMProcessAttributes(bool* aIsOffline,
bool* aIsConnected,
int32_t* aCaptivePortalState,
bool* aIsLangRTL,
bool* aHaveBidiKeyboards,
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy,
StructuredCloneData* initialData,
InfallibleTArray<FontFamilyListEntry>* fontFamilies,
OptionalURIParams* aUserContentSheetURL,
nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override;
mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
@ -898,7 +880,6 @@ private:
virtual bool
DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
virtual mozilla::ipc::IPCResult RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) override;
virtual mozilla::ipc::IPCResult RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) override;
virtual mozilla::ipc::IPCResult RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) override;

276
dom/ipc/ContentPrefs.cpp Normal file
View File

@ -0,0 +1,276 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "ContentPrefs.h"
const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
"accessibility.monoaudio.enable",
"accessibility.mouse_focuses_formcontrol",
"accessibility.tabfocus_applies_to_xul",
"app.update.channel",
"browser.dom.window.dump.enabled",
"browser.sessionhistory.max_entries",
"browser.sessionhistory.max_total_viewers",
"content.cors.disable",
"content.cors.no_private_data",
"content.notify.backoffcount",
"content.notify.interval",
"content.notify.ontimer",
"content.sink.enable_perf_mode",
"content.sink.event_probe_rate",
"content.sink.initial_perf_time",
"content.sink.interactive_deflect_count",
"content.sink.interactive_parse_time",
"content.sink.interactive_time",
"content.sink.pending_event_mode",
"content.sink.perf_deflect_count",
"content.sink.perf_parse_time",
"device.storage.prompt.testing",
"device.storage.writable.name",
"dom.allow_XUL_XBL_for_file",
"dom.allow_cut_copy",
"dom.enable_frame_timing",
"dom.enable_performance",
"dom.enable_resource_timing",
"dom.event.handling-user-input-time-limit",
"dom.event.touch.coalescing.enabled",
"dom.forms.autocomplete.experimental",
"dom.ipc.processPriorityManager.backgroundGracePeriodMS",
"dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS",
"dom.max_chrome_script_run_time",
"dom.max_script_run_time",
"dom.performance.enable_notify_performance_timing",
"dom.performance.enable_user_timing_logging",
"dom.storage.testing",
"dom.url.encode_decode_hash",
"dom.url.getters_decode_hash",
"dom.use_watchdog",
"dom.vibrator.enabled",
"dom.vibrator.max_vibrate_list_len",
"dom.vibrator.max_vibrate_ms",
"focusmanager.testmode",
"font.size.inflation.disabledInMasterProcess",
"font.size.inflation.emPerLine",
"font.size.inflation.forceEnabled",
"font.size.inflation.lineThreshold",
"font.size.inflation.mappingIntercept",
"font.size.inflation.maxRatio",
"font.size.inflation.minTwips",
"full-screen-api.allow-trusted-requests-only",
"full-screen-api.enabled",
"full-screen-api.unprefix.enabled",
"gfx.font_rendering.opentype_svg.enabled",
"hangmonitor.timeout",
"html5.flushtimer.initialdelay",
"html5.flushtimer.subsequentdelay",
"html5.offmainthread",
"intl.charset.fallback.tld",
"intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
"javascript.enabled",
"javascript.options.asmjs",
"javascript.options.asyncstack",
"javascript.options.baselinejit",
"javascript.options.baselinejit.threshold",
"javascript.options.baselinejit.unsafe_eager_compilation",
"javascript.options.discardSystemSource",
"javascript.options.dump_stack_on_debuggee_would_run",
"javascript.options.gczeal",
"javascript.options.gczeal.frequency",
"javascript.options.ion",
"javascript.options.ion.offthread_compilation",
"javascript.options.ion.threshold",
"javascript.options.ion.unsafe_eager_compilation",
"javascript.options.jit.full_debug_checks",
"javascript.options.native_regexp",
"javascript.options.parallel_parsing",
"javascript.options.shared_memory",
"javascript.options.strict",
"javascript.options.strict.debug",
"javascript.options.throw_on_asmjs_validation_failure",
"javascript.options.throw_on_debuggee_would_run",
"javascript.options.wasm",
"javascript.options.wasm_baselinejit",
"javascript.options.werror",
"javascript.use_us_english_locale",
"jsloader.reuseGlobal",
"layout.css.all-shorthand.enabled",
"layout.css.background-blend-mode.enabled",
"layout.css.background-clip-text.enabled",
"layout.css.box-decoration-break.enabled",
"layout.css.color-adjust.enabled",
"layout.css.contain.enabled",
"layout.css.control-characters.visible",
"layout.css.display-flow-root.enabled",
"layout.css.expensive-style-struct-assertions.enabled",
"layout.css.float-logical-values.enabled",
"layout.css.font-variations.enabled",
"layout.css.grid.enabled",
"layout.css.image-orientation.enabled",
"layout.css.initial-letter.enabled",
"layout.css.isolation.enabled",
"layout.css.mix-blend-mode.enabled",
"layout.css.object-fit-and-position.enabled",
"layout.css.osx-font-smoothing.enabled",
"layout.css.overflow-clip-box.enabled",
"layout.css.prefixes.animations",
"layout.css.prefixes.border-image",
"layout.css.prefixes.box-sizing",
"layout.css.prefixes.device-pixel-ratio-webkit",
"layout.css.prefixes.font-features",
"layout.css.prefixes.gradients",
"layout.css.prefixes.transforms",
"layout.css.prefixes.transitions",
"layout.css.prefixes.webkit",
"layout.css.scope-pseudo.enabled",
"layout.css.scroll-behavior.property-enabled",
"layout.css.scroll-snap.enabled",
"layout.css.shape-outside.enabled",
"layout.css.text-align-unsafe-value.enabled",
"layout.css.text-combine-upright-digits.enabled",
"layout.css.text-combine-upright.enabled",
"layout.css.touch_action.enabled",
"layout.css.unprefixing-service.enabled",
"layout.css.unprefixing-service.globally-whitelisted",
"layout.css.unprefixing-service.include-test-domains",
"layout.css.variables.enabled",
"layout.css.visited_links_enabled",
"layout.idle_period.required_quiescent_frames",
"layout.idle_period.time_limit",
"layout.interruptible-reflow.enabled",
"mathml.disabled",
"media.apple.forcevda",
"media.clearkey.persistent-license.enabled",
"media.cubeb_latency_msg_frames",
"media.cubeb_latency_playback_ms",
"media.decoder-doctor.wmf-disabled-is-failure",
"media.decoder.fuzzing.dont-delay-inputexhausted",
"media.decoder.fuzzing.enabled",
"media.decoder.fuzzing.video-output-minimum-interval-ms",
"media.decoder.limit",
"media.decoder.recycle.enabled",
"media.dormant-on-pause-timeout-ms",
"media.eme.audio.blank",
"media.eme.enabled",
"media.eme.video.blank",
"media.ffmpeg.enabled",
"media.ffvpx.enabled",
"media.flac.enabled",
"media.forcestereo.enabled",
"media.gmp.async-shutdown-timeout",
"media.gmp.decoder.aac",
"media.gmp.decoder.enabled",
"media.gmp.decoder.h264",
"media.gmp.insecure.allow",
"media.gpu-process-decoder",
"media.libavcodec.allow-obsolete",
"media.num-decode-threads",
"media.ogg.enabled",
"media.ogg.flac.enabled",
"media.resampling.enabled",
"media.resampling.rate",
"media.ruin-av-sync.enabled",
"media.rust.test_mode",
"media.suspend-bkgnd-video.delay-ms",
"media.suspend-bkgnd-video.enabled",
"media.use-blank-decoder",
"media.video_stats.enabled",
"media.volume_scale",
"media.webspeech.recognition.enable",
"media.webspeech.recognition.force_enable",
"media.webspeech.synth.force_global_queue",
"media.webspeech.test.enable",
"media.webspeech.test.fake_fsm_events",
"media.webspeech.test.fake_recognition_service",
"media.wmf.allow-unsupported-resolutions",
"media.wmf.decoder.thread-count",
"media.wmf.enabled",
"media.wmf.skip-blacklist",
"media.wmf.vp9.enabled",
"memory.free_dirty_pages",
"memory.low_commit_space_threshold_mb",
"memory.low_memory_notification_interval_ms",
"memory.low_physical_memory_threshold_mb",
"memory.low_virtual_mem_threshold_mb",
"network.IDN.blacklist_chars",
"network.IDN.restriction_profile",
"network.IDN.use_whitelist",
"network.IDN_show_punycode",
"network.buffer.cache.count",
"network.buffer.cache.size",
"network.captive-portal-service.enabled",
"network.cookie.cookieBehavior",
"network.cookie.lifetimePolicy",
"network.dns.disablePrefetch",
"network.dns.disablePrefetchFromHTTPS",
"network.jar.block-remote-files",
"network.loadinfo.skip_type_assertion",
"network.notify.changed",
"network.offline-mirrors-connectivity",
"network.protocol-handler.external.jar",
"network.proxy.type",
"network.security.ports.banned",
"network.security.ports.banned.override",
"network.standard-url.enable-rust",
"network.standard-url.max-length",
"network.sts.max_time_for_events_between_two_polls",
"network.sts.max_time_for_pr_close_during_shutdown",
"network.tcp.keepalive.enabled",
"network.tcp.keepalive.idle_time",
"network.tcp.keepalive.probe_count",
"network.tcp.keepalive.retry_interval",
"network.tcp.sendbuffer",
"nglayout.debug.invalidation",
"privacy.donottrackheader.enabled",
"privacy.firstparty.isolate",
"privacy.firstparty.isolate.restrict_opener_access",
"privacy.resistFingerprinting",
"security.data_uri.inherit_security_context",
"security.fileuri.strict_origin_policy",
"security.sandbox.content.level",
"security.sandbox.content.tempDirSuffix",
"security.sandbox.logging.enabled",
"security.sandbox.mac.track.violations",
"security.sandbox.windows.log",
"security.sandbox.windows.log.stackTraceDepth",
"shutdown.watchdog.timeoutSecs",
"signed.applets.codebase_principal_support",
"svg.disabled",
"svg.display-lists.hit-testing.enabled",
"svg.display-lists.painting.enabled",
"svg.new-getBBox.enabled",
"svg.paint-order.enabled",
"svg.path-caching.enabled",
"svg.transform-box.enabled",
"toolkit.asyncshutdown.crash_timeout",
"toolkit.asyncshutdown.log",
"toolkit.osfile.log",
"toolkit.osfile.log.redirect",
"toolkit.telemetry.enabled",
"toolkit.telemetry.idleTimeout",
"toolkit.telemetry.initDelay",
"toolkit.telemetry.log.dump",
"toolkit.telemetry.log.level",
"toolkit.telemetry.minSubsessionLength",
"toolkit.telemetry.scheduler.idleTickInterval",
"toolkit.telemetry.scheduler.tickInterval",
"toolkit.telemetry.unified",
"ui.key.menuAccessKeyFocuses",
"ui.popup.disable_autohide",
"ui.use_activity_cursor",
"view_source.editor.external"};
const char** mozilla::dom::ContentPrefs::GetContentPrefs(size_t* aCount)
{
*aCount = ArrayLength(ContentPrefs::gInitPrefs);
return gInitPrefs;
}
const char* mozilla::dom::ContentPrefs::GetContentPref(size_t aIndex)
{
MOZ_ASSERT(aIndex < ArrayLength(ContentPrefs::gInitPrefs));
return gInitPrefs[aIndex];
}

25
dom/ipc/ContentPrefs.h Normal file
View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_ContentPrefs_h
#define mozilla_dom_ContentPrefs_h
namespace mozilla {
namespace dom {
class ContentPrefs {
public:
static const char** GetContentPrefs(size_t* aCount);
static const char* GetContentPref(size_t aIndex);
private:
static const char* gInitPrefs[];
};
}
}
#endif

View File

@ -7,6 +7,7 @@
#include "mozilla/ipc/IOThreadChild.h"
#include "ContentProcess.h"
#include "ContentPrefs.h"
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include <stdlib.h>
@ -103,46 +104,143 @@ SetUpSandboxEnvironment()
}
#endif
void
ContentProcess::SetAppDir(const nsACString& aPath)
bool
ContentProcess::Init(int aArgc, char* aArgv[])
{
mXREEmbed.SetAppDir(aPath);
}
// If passed in grab the application path for xpcom init
bool foundAppdir = false;
bool foundChildID = false;
bool foundIsForBrowser = false;
bool foundIntPrefs = false;
bool foundBoolPrefs = false;
bool foundStringPrefs = false;
uint64_t childID;
bool isForBrowser;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
void
ContentProcess::SetProfile(const nsACString& aProfile)
{
bool flag;
nsresult rv =
XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
if (NS_FAILED(rv) ||
NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
NS_WARNING("Invalid profile directory passed to content process.");
mProfileDir = nullptr;
}
}
// If passed in grab the profile path for sandboxing
bool foundProfile = false;
nsCOMPtr<nsIFile> profileDir;
#endif
bool
ContentProcess::Init()
{
mContent.Init(IOThreadChild::message_loop(),
ParentPid(),
IOThreadChild::channel());
mXREEmbed.Start();
mContent.InitXPCOM();
mContent.InitGraphicsDeviceData();
InfallibleTArray<PrefSetting> prefsArray;
for (int idx = aArgc; idx > 0; idx--) {
if (!aArgv[idx]) {
continue;
}
if (!strcmp(aArgv[idx], "-appdir")) {
MOZ_ASSERT(!foundAppdir);
if (foundAppdir) {
continue;
}
nsCString appDir;
appDir.Assign(nsDependentCString(aArgv[idx+1]));
mXREEmbed.SetAppDir(appDir);
foundAppdir = true;
} else if (!strcmp(aArgv[idx], "-childID")) {
MOZ_ASSERT(!foundChildID);
if (foundChildID) {
continue;
}
if (idx + 1 < aArgc) {
childID = strtoull(aArgv[idx + 1], nullptr, 10);
foundChildID = true;
}
} else if (!strcmp(aArgv[idx], "-isForBrowser") || !strcmp(aArgv[idx], "-notForBrowser")) {
MOZ_ASSERT(!foundIsForBrowser);
if (foundIsForBrowser) {
continue;
}
isForBrowser = strcmp(aArgv[idx], "-notForBrowser");
foundIsForBrowser = true;
} else if (!strcmp(aArgv[idx], "-intPrefs")) {
SET_PREF_PHASE(BEGIN_INIT_PREFS);
char* str = aArgv[idx + 1];
while (*str) {
int32_t index = strtol(str, &str, 10);
str++;
MaybePrefValue value(PrefValue(static_cast<int32_t>(strtol(str, &str, 10))));
str++;
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
}
SET_PREF_PHASE(END_INIT_PREFS);
foundIntPrefs = true;
} else if (!strcmp(aArgv[idx], "-boolPrefs")) {
SET_PREF_PHASE(BEGIN_INIT_PREFS);
char* str = aArgv[idx + 1];
while (*str) {
int32_t index = strtol(str, &str, 10);
str++;
MaybePrefValue value(PrefValue(!!strtol(str, &str, 10)));
str++;
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
}
SET_PREF_PHASE(END_INIT_PREFS);
foundBoolPrefs = true;
} else if (!strcmp(aArgv[idx], "-stringPrefs")) {
SET_PREF_PHASE(BEGIN_INIT_PREFS);
char* str = aArgv[idx + 1];
while (*str) {
int32_t index = strtol(str, &str, 10);
str++;
int32_t length = strtol(str, &str, 10);
str++;
MaybePrefValue value(PrefValue(nsCString(str, length)));
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
str += length + 1;
}
SET_PREF_PHASE(END_INIT_PREFS);
foundStringPrefs = true;
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
else if (!strcmp(aArgv[idx], "-profile")) {
MOZ_ASSERT(!foundProfile);
if (foundProfile) {
continue;
}
bool flag;
nsresult rv = XRE_GetFileFromPath(aArgv[idx+1], getter_AddRefs(profileDir));
if (NS_FAILED(rv) ||
NS_FAILED(profileDir->Exists(&flag)) || !flag) {
NS_WARNING("Invalid profile directory passed to content process.");
profileDir = nullptr;
}
foundProfile = true;
}
#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
bool allFound = foundAppdir && foundChildID && foundIsForBrowser && foundIntPrefs && foundBoolPrefs && foundStringPrefs;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
allFound &= foundProfile;
#endif
if (allFound) {
break;
}
}
Preferences::SetInitPreferences(&prefsArray);
mContent.Init(IOThreadChild::message_loop(),
ParentPid(),
IOThreadChild::channel(),
childID,
isForBrowser);
mXREEmbed.Start();
#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
mContent.SetProfileDir(mProfileDir);
mContent.SetProfileDir(profileDir);
#endif
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
SetUpSandboxEnvironment();
SetUpSandboxEnvironment();
#endif
return true;
return true;
}
// Note: CleanUp() never gets called in non-debug builds because we exit early
@ -150,7 +248,7 @@ ContentProcess::Init()
void
ContentProcess::CleanUp()
{
mXREEmbed.Stop();
mXREEmbed.Stop();
}
} // namespace dom

View File

@ -34,23 +34,13 @@ public:
~ContentProcess()
{ }
virtual bool Init() override;
virtual bool Init(int aArgc, char* aArgv[]) override;
virtual void CleanUp() override;
void SetAppDir(const nsACString& aPath);
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
void SetProfile(const nsACString& aProfile);
#endif
private:
ContentChild mContent;
mozilla::ipc::ScopedXREEmbed mXREEmbed;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
nsCOMPtr<nsIFile> mProfileDir;
#endif
#if defined(XP_WIN)
// This object initializes and configures COM.
mozilla::mscom::MainThreadRuntime mCOMRuntime;

View File

@ -334,6 +334,22 @@ struct GMPCapabilityData
GMPAPITags[] capabilities;
};
struct XPCOMInitData
{
bool isOffline;
bool isConnected;
int32_t captivePortalState;
bool isLangRTL;
bool haveBidiKeyboards;
nsString[] dictionaries;
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
/* used on MacOSX only */
FontFamilyListEntry[] fontFamilies;
OptionalURIParams userContentSheetURL;
PrefSetting[] prefs;
};
struct GfxInfoFeatureStatus
{
int32_t feature;
@ -527,6 +543,8 @@ child:
*/
async InitBlobURLs(BlobURLRegistrationData[] registrations);
async SetXPCOMProcessAttributes(XPCOMInitData xpcomInit, StructuredCloneData initialData, LookAndFeelInt[] lookAndFeelIntCache);
// Notify child that last-pb-context-exited notification was observed
async LastPrivateDocShellDestroyed();
@ -650,30 +668,6 @@ child:
parent:
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
/**
* Tell the content process some attributes of itself. This is
* among the first information queried by content processes after
* startup. (The message is sync to allow the content process to
* control when it receives the information.)
*
* |id| is a unique ID among all subprocesses. When
* |isForBrowser|, we're loading <browser> or <xul:browser remote>.
*
* Keep the return values in sync with PBrowser()!
*/
sync GetProcessAttributes()
returns (ContentParentId cpId, bool isForBrowser);
sync GetXPCOMProcessAttributes()
returns (bool isOffline, bool isConnected, int32_t captivePortalState,
bool isLangRTL,
bool haveBidiKeyboards, nsString[] dictionaries,
ClipboardCapabilities clipboardCaps,
DomainPolicyClone domainPolicy,
StructuredCloneData initialData,
FontFamilyListEntry[] fontFamilies /* used on MacOSX only */,
OptionalURIParams userContentSheetURL,
LookAndFeelInt[] lookAndFeelIntCache);
sync CreateChildProcess(IPCTabContext context,
ProcessPriority priority,
TabId openerTabId)
@ -817,7 +811,6 @@ parent:
async ExtProtocolChannelConnectParent(uint32_t registrarId);
// PrefService message
sync ReadPrefsArray() returns (PrefSetting[] prefs) verify;
sync GetGfxVars() returns (GfxVarUpdate[] vars);
sync ReadFontList() returns (FontListEntry[] retValue);

View File

@ -119,6 +119,8 @@
#include "GroupedSHistory.h"
#include "nsIHttpChannel.h"
#include "mozilla/dom/DocGroup.h"
#include "nsISupportsPrimitives.h"
#include "mozilla/Telemetry.h"
#ifdef NS_PRINTING
#include "nsIPrintSession.h"

View File

@ -20,6 +20,7 @@ EXPORTS.mozilla.dom += [
'ContentBridgeParent.h',
'ContentChild.h',
'ContentParent.h',
'ContentPrefs.h',
'ContentProcess.h',
'ContentProcessManager.h',
'CPOWManagerGetter.h',
@ -50,6 +51,7 @@ UNIFIED_SOURCES += [
'ContentBridgeChild.cpp',
'ContentBridgeParent.cpp',
'ContentParent.cpp',
'ContentPrefs.cpp',
'ContentProcess.cpp',
'ContentProcessManager.cpp',
'CrashReporterParent.cpp',

View File

@ -25,7 +25,7 @@ GMPProcessChild::~GMPProcessChild()
}
bool
GMPProcessChild::Init()
GMPProcessChild::Init(int aArgc, char* aArgv[])
{
nsAutoString pluginFilename;

View File

@ -22,7 +22,7 @@ public:
explicit GMPProcessChild(ProcessId aParentPid);
~GMPProcessChild();
bool Init() override;
bool Init(int aArgc, char* aArgv[]) override;
void CleanUp() override;
private:

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")
EXTRA_COMPONENTS += [
'NotificationStorage.js',
'NotificationStorage.manifest',

View File

@ -40,7 +40,7 @@ namespace plugins {
bool
PluginProcessChild::Init()
PluginProcessChild::Init(int aArgc, char* aArgv[])
{
nsDebugImpl::SetMultiprocessMode("NPAPI");

View File

@ -30,7 +30,7 @@ public:
virtual ~PluginProcessChild()
{ }
virtual bool Init() override;
virtual bool Init(int aArgc, char* aArgv[]) override;
virtual void CleanUp() override;
protected:

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
EXPORTS.mozilla.dom += [
'Storage.h',
'StorageIPC.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
EXPORTS.mozilla.dom += [
'U2F.h',
'U2FAuthenticator.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
EXPORTS.mozilla.dom += [
'VRDisplay.h',
'VRDisplayEvent.h',

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
EXPORTS.mozilla.dom += [
'NSSU2FTokenRemote.h',
'ScopedCredential.h',

View File

@ -4,6 +4,369 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
with Files("APZTestData.webidl"):
BUG_COMPONENT = ("Core", "Panning and Zooming")
with Files("AccessibleNode.webidl"):
BUG_COMPONENT = ("Core", "Disability Access APIs")
with Files("Addon*"):
BUG_COMPONENT = ("Toolkit", "Add-ons Manager")
with Files("AnalyserNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Animat*"):
BUG_COMPONENT = ("Core", "DOM: Animation")
with Files("*Audio*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Autocomplete*"):
BUG_COMPONENT = ("Toolkit", "Autocomplete")
with Files("BaseKeyframeTypes.webidl"):
BUG_COMPONENT = ("Core", "DOM: Animation")
with Files("BatteryManager.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("BiquadFilterNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("BrowserElement*"):
BUG_COMPONENT = ("Core", "DOM")
with Files("CSP*"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("CSS*"):
BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
with Files("Canvas*"):
BUG_COMPONENT = ("Core", "Canvas: 2D")
with Files("Caret*"):
BUG_COMPONENT = ("Core", "Editor")
with Files("Channel*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Client*"):
BUG_COMPONENT = ("Core", "DOM: Service Workers")
with Files("ClipboardEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("ConstantSourceNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("ContainerBoxObject.webidl"):
BUG_COMPONENT = ("Core", "DOM")
with Files("ConvolverNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Coordinates.webidl"):
BUG_COMPONENT = ("Core", "Geolocation")
with Files("Crypto.webidl"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("Device*"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("Directory.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("DataTransfer*"):
BUG_COMPONENT = ("Core", "Drag and Drop")
with Files("DragEvent.webidl"):
BUG_COMPONENT = ("Core", "Drag and Drop")
with Files("DecoderDoctorNotification.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video: Playback")
with Files("DelayNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("DynamicsCompressorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("DesktopNotification.webidl"):
BUG_COMPONENT = ("Toolkit", "Notification and Alerts")
with Files("FakePluginTagInit.webidl"):
BUG_COMPONENT = ("Core", "Plug-ins")
with Files("FlyWeb*"):
BUG_COMPONENT = ("Core", "DOM: Flyweb")
with Files("FocusEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("Font*"):
BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
with Files("FormData.webidl"):
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
with Files("Geolocation.webidl"):
BUG_COMPONENT = ("Core", "Geolocation")
with Files("GainNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Gamepad*"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("GeometryUtils.webidl"):
BUG_COMPONENT = ("Core", "Layout")
with Files("GetUserMediaRequest.webidl"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("Grid.webidl"):
BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
with Files("GroupedHistoryEvent.webidl"):
BUG_COMPONENT = ("Core", "Document Navigation")
with Files("HTML*"):
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
with Files("HashChangeEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("HeapSnapshot.webidl"):
BUG_COMPONENT = ("Firefox", "Developer Tools: Memory")
with Files("HiddenPluginEvent.webidl"):
BUG_COMPONENT = ("Core", "Plug-ins")
with Files("IDB*"):
BUG_COMPONENT = ("Core", "DOM: IndexedDB")
with Files("IIRFilterNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Image*"):
BUG_COMPONENT = ("Core", "DOM")
with Files("ImageCapture*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("InputEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("InstallTrigger.webidl"):
BUG_COMPONENT = ("Toolkit", "Add-ons Manager")
with Files("InspectorUtils.webidl"):
BUG_COMPONENT = ("Firefox", "Developer Tools: Inspector")
with Files("KeyAlgorithm.webidl"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("Key*Event*"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("KeyIdsInitData.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video: Playback")
with Files("Keyframe*"):
BUG_COMPONENT = ("Core", "DOM: Animation")
with Files("LocalMediaStream.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("MediaDevice*"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("Media*Source*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("MediaStream*"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("MediaStreamAudio*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("MediaEncryptedEvent.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("MediaKey*"):
BUG_COMPONENT = ("Core", "Audio/Video: Playback")
with Files("Media*List*"):
BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
with Files("MediaStreamList.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("*Record*"):
BUG_COMPONENT = ("Core", "Audio/Video: Recording")
with Files("Media*Track*"):
BUG_COMPONENT = ("Core", "WebRTC: Audio/Video")
with Files("Mouse*"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("MozSelfSupport.webidl"):
BUG_COMPONENT = ("Firefox Health Report", "Client: Desktop")
with Files("MozTimeManager.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("MutationEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("NativeOSFileInternals.webidl"):
BUG_COMPONENT = ("Toolkit", "OS.File")
with Files("Net*"):
BUG_COMPONENT = ("Core", "Networking")
with Files("OfflineAudio*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("OffscreenCanvas.webidl"):
BUG_COMPONENT = ("Core", "Canvas 2D")
with Files("OscillatorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("PannerNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Peer*"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("PeriodicWave.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("PointerEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("PopStateEvent.webidl*"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("Position*"):
BUG_COMPONENT = ("Core", "Geolocation")
with Files("ProfileTimelineMarker.webidl"):
BUG_COMPONENT = ("Firefox", "Developer Tools: Performance Tools (profiler/timeline)")
with Files("ProgressEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("Push*"):
BUG_COMPONENT = ("Core", "DOM: Push Notifications")
with Files("RTC*"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("SVG*"):
BUG_COMPONENT = ("Core", "SVG")
with Files("ScriptProcessorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
# TODO: SecureElement*, SettingChangeNotification
# are FirefoxOS::*, leaving as Core::DOM
with Files("Selection.webidl"):
BUG_COMPONENT = ("Core", "Selection")
with Files("ServiceWorker*"):
BUG_COMPONENT = ("Core", "DOM: Service Workers")
with Files("SimpleGestureEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("SocketCommon.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("SourceBuffer*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("StereoPannerNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Style*"):
BUG_COMPONENT = ("Core", "DOM: CSS Object Model")
with Files("SubtleCrypto.webidl"):
BUG_COMPONENT = ("Core", "DOM: Security")
with Files("TCP*"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("TextTrack*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("ThreadSafeChromeUtils.webidl"):
BUG_COMPONENT = ("Firefox", "Developer Tools: Memory")
with Files("TrackEvent.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("U2F.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("UDP*"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("UIEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("URL.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("UserProximityEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("VTT*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("VRDisplay.webidl"):
BUG_COMPONENT = ("Core", "Graphics")
with Files("Video*"):
BUG_COMPONENT = ("Core", "Audio/Video")
with Files("WaveShaperNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("WebAuthentication.webidl"):
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
with Files("WebGL*"):
BUG_COMPONENT = ("Core", "Canvas: WebGL")
with Files("WebKitCSSMatrix.webidl"):
BUG_COMPONENT = ("Core", "DOM: CSS Object Model")
with Files("Webrtc*"):
BUG_COMPONENT = ("Core", "WebRTC")
with Files("WheelEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("WidevineCDMManifest.webidl"):
BUG_COMPONENT = ("Core", "Audio/Video: Playback")
with Files("WindowOrWorkerGlobalScope.webidl"):
BUG_COMPONENT = ("Core", "DOM: Workers")
with Files("Worker*"):
BUG_COMPONENT = ("Core", "DOM: Workers")
GENERATED_WEBIDL_FILES = [
'CSS2Properties.webidl',
]

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "XBL")
DIRS += ['builtin']
EXPORTS += [

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM")
XPIDL_SOURCES += [
'nsIXMLHttpRequest.idl',
]

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "XML")
DIRS += ['resources']
MOCHITEST_MANIFESTS += ['test/mochitest.ini']

View File

@ -4,6 +4,9 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "XSLT")
XPIDL_SOURCES += [
'nsIXSLTProcessor.idl',
'nsIXSLTProcessorPrivate.idl',

View File

@ -818,18 +818,6 @@ nsPermissionManager::Init()
return NS_OK;
}
NS_IMETHODIMP
nsPermissionManager::RefreshPermission() {
NS_ENSURE_TRUE(IsChildProcess(), NS_ERROR_FAILURE);
nsresult rv = RemoveAllFromMemory();
NS_ENSURE_SUCCESS(rv, rv);
rv = FetchPermissions();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsPermissionManager::OpenDatabase(nsIFile* aPermissionsFile)
{

View File

@ -692,6 +692,7 @@ gl::ErrorOrResult<Buffer11::BufferStorage *> Buffer11::getBufferStorage(BufferUs
ANGLE_TRY(newStorage->resize(mSize, true));
}
mIdleness[usage] = 0;
ANGLE_TRY(updateBufferStorage(newStorage, 0, mSize));
ANGLE_TRY(markBufferUsage(usage));

View File

@ -22,7 +22,7 @@ GPUProcessImpl::~GPUProcessImpl()
}
bool
GPUProcessImpl::Init()
GPUProcessImpl::Init(int aArgc, char* aArgv[])
{
return mGPU.Init(ParentPid(),
IOThreadChild::message_loop(),

View File

@ -24,7 +24,7 @@ public:
explicit GPUProcessImpl(ProcessId aParentPid);
~GPUProcessImpl();
bool Init() override;
bool Init(int aArgc, char* aArgv[]) override;
void CleanUp() override;
private:

View File

@ -59,38 +59,6 @@ using namespace mozilla::media;
typedef std::vector<CompositableOperation> OpVector;
typedef nsTArray<OpDestroy> OpDestroyVector;
namespace {
class ImageBridgeThread : public Thread {
public:
ImageBridgeThread() : Thread("ImageBridgeChild") {
}
protected:
MOZ_IS_CLASS_INIT
void Init() {
#ifdef MOZ_GECKO_PROFILER
mPseudoStackHack = profiler_get_pseudo_stack();
#endif
}
void CleanUp() {
#ifdef MOZ_GECKO_PROFILER
mPseudoStackHack = nullptr;
#endif
}
private:
#ifdef MOZ_GECKO_PROFILER
// This is needed to avoid a spurious leak report. There's no other
// use for it. See bug 1239504 and bug 1215265.
MOZ_INIT_OUTSIDE_CTOR PseudoStack* mPseudoStackHack;
#endif
};
}
struct CompositableTransaction
{
CompositableTransaction()
@ -620,7 +588,7 @@ ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
gfxPlatform::GetPlatform();
if (!sImageBridgeChildThread) {
sImageBridgeChildThread = new ImageBridgeThread();
sImageBridgeChildThread = new Thread("ImageBridgeChild");
if (!sImageBridgeChildThread->Start()) {
return false;
}
@ -747,7 +715,7 @@ ImageBridgeChild::InitSameProcess()
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
sImageBridgeChildThread = new ImageBridgeThread();
sImageBridgeChildThread = new Thread("ImageBridgeChild");
if (!sImageBridgeChildThread->IsRunning()) {
sImageBridgeChildThread->Start();
}
@ -775,7 +743,7 @@ ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint)
MOZ_ASSERT(!sImageBridgeChildSingleton);
MOZ_ASSERT(!sImageBridgeChildThread);
sImageBridgeChildThread = new ImageBridgeThread();
sImageBridgeChildThread = new Thread("ImageBridgeChild");
if (!sImageBridgeChildThread->IsRunning()) {
sImageBridgeChildThread->Start();
}

View File

@ -10,7 +10,7 @@
#include <windows.h>
class nsWin32Locale {
class nsWin32Locale final {
public:
static nsresult GetPlatformLocale(const nsAString& locale, LCID* winLCID);
static void GetXPLocale(LCID winLCID, nsAString& locale);
@ -18,15 +18,6 @@ public:
private:
// Static class - Don't allow instantiation.
nsWin32Locale(void) {}
typedef LCID (WINAPI*LocaleNameToLCIDPtr)(LPCWSTR lpName, DWORD dwFlags);
typedef int (WINAPI*LCIDToLocaleNamePtr)(LCID Locale, LPWSTR lpName,
int cchName, DWORD dwFlags);
static LocaleNameToLCIDPtr localeNameToLCID;
static LCIDToLocaleNamePtr lcidToLocaleName;
static void initFunctionPointers ();
};
#endif

View File

@ -10,9 +10,7 @@
#include "nsXPCOMStrings.h"
#include "nsReadableUtils.h"
#include "nsWin32Locale.h"
#include "prprf.h"
#include <windows.h>
#include "nsCRT.h"
using namespace mozilla;
@ -29,103 +27,6 @@ struct iso_map
iso_pair sublang_list[20];
};
nsWin32Locale::LocaleNameToLCIDPtr nsWin32Locale::localeNameToLCID = nullptr;
nsWin32Locale::LCIDToLocaleNamePtr nsWin32Locale::lcidToLocaleName = nullptr;
// Older versions of VC++ and Win32 SDK and mingw don't have
// macros for languages and sublanguages recently added to Win32.
// see http://www.tug.org/ftp/tex/texinfo/intl/localename.c
#ifndef LANG_URDU
#define LANG_URDU 0x20
#endif
#ifndef LANG_ARMENIAN
#define LANG_ARMENIAN 0x2b
#endif
#ifndef LANG_AZERI
#define LANG_AZERI 0x2c
#endif
#ifndef LANG_MACEDONIAN
#define LANG_MACEDONIAN 0x2f
#endif
#ifndef LANG_GEORGIAN
#define LANG_GEORGIAN 0x37
#endif
#ifndef LANG_HINDI
#define LANG_HINDI 0x39
#endif
#ifndef LANG_MALAY
#define LANG_MALAY 0x3e
#endif
#ifndef LANG_KAZAK
#define LANG_KAZAK 0x3f
#endif
#ifndef LANG_KYRGYZ
#define LANG_KYRGYZ 0x40
#endif
#ifndef LANG_SWAHILI
#define LANG_SWAHILI 0x41
#endif
#ifndef LANG_UZBEK
#define LANG_UZBEK 0x43
#endif
#ifndef LANG_TATAR
#define LANG_TATAR 0x44
#endif
#ifndef LANG_PUNJABI
#define LANG_PUNJABI 0x46
#endif
#ifndef LANG_GUJARAT
#define LANG_GUJARAT 0x47
#endif
#ifndef LANG_TAMIL
#define LANG_TAMIL 0x49
#endif
#ifndef LANG_TELUGU
#define LANG_TELUGU 0x4a
#endif
#ifndef LANG_KANNADA
#define LANG_KANNADA 0x4b
#endif
#ifndef LANG_MARATHI
#define LANG_MARATHI 0x4e
#endif
#ifndef LANG_SANSKRIT
#define LANG_SANSKRIT 0x4f
#endif
#ifndef LANG_MONGOLIAN
#define LANG_MONGOLIAN 0x50
#endif
#ifndef LANG_GALICIAN
#define LANG_GALICIAN 0x56
#endif
#ifndef LANG_KONKANI
#define LANG_KONKANI 0x57
#endif
#ifndef LANG_DIVEHI
#define LANG_DIVEHI 0x65
#endif
#ifndef SUBLANG_MALAY_MALAYSIA
#define SUBLANG_MALAY_MALAYSIA 0x01
#endif
#ifndef SUBLANG_MALAY_BRUNEI_DARUSSALAM
#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02
#endif
#ifndef SUBLANG_CHINESE_MACAU
#define SUBLANG_CHINESE_MACAU 0x05
#endif
#ifndef SUBLANG_FRENCH_MONACO
#define SUBLANG_FRENCH_MONACO 0x06
#endif
#ifndef SUBLANG_ENGLISH_ZIMBABWE
#define SUBLANG_ENGLISH_ZIMBABWE 0x0c
#endif
#ifndef SUBLANG_ENGLISH_PHILIPPINES
#define SUBLANG_ENGLISH_PHILIPPINES 0x0d
#endif
//
// This list is used to map between ISO language
// References :
@ -574,21 +475,6 @@ iso_pair dbg_list[] =
#define CROATIAN_ISO_CODE "hr"
#define SERBIAN_ISO_CODE "sr"
void
nsWin32Locale::initFunctionPointers(void)
{
static bool sInitialized = false;
// We use the Vista and above functions if we have them
if (!sInitialized) {
HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
if (kernelDLL) {
localeNameToLCID = (LocaleNameToLCIDPtr) GetProcAddress(kernelDLL, "LocaleNameToLCID");
lcidToLocaleName = (LCIDToLocaleNamePtr) GetProcAddress(kernelDLL, "LCIDToLocaleName");
}
sInitialized = true;
}
}
//
// the mapping routines are a first approximation to get us going on
// the tier-1 languages. we are making an assumption that we can map
@ -597,18 +483,12 @@ nsWin32Locale::initFunctionPointers(void)
nsresult
nsWin32Locale::GetPlatformLocale(const nsAString& locale, LCID* winLCID)
{
initFunctionPointers ();
if (localeNameToLCID) {
nsAutoString locale_autostr(locale);
LCID lcid = localeNameToLCID(locale_autostr.get(), 0);
// The function returning 0 means that the locale name couldn't be matched,
// so we fallback to the old function
if (lcid != 0)
{
*winLCID = lcid;
return NS_OK;
}
LCID lcid = LocaleNameToLCID(PromiseFlatString(locale).get(), 0);
// The function returning 0 means that the locale name couldn't be matched,
// so we fallback to the old function
if (lcid != 0) {
*winLCID = lcid;
return NS_OK;
}
char locale_string[9] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0'};
@ -644,26 +524,19 @@ nsWin32Locale::GetPlatformLocale(const nsAString& locale, LCID* winLCID)
return NS_ERROR_FAILURE;
}
#ifndef LOCALE_NAME_MAX_LENGTH
#define LOCALE_NAME_MAX_LENGTH 85
#endif
void
nsWin32Locale::GetXPLocale(LCID winLCID, nsAString& locale)
{
initFunctionPointers ();
if (lcidToLocaleName)
{
WCHAR ret_locale[LOCALE_NAME_MAX_LENGTH];
int rv = lcidToLocaleName(winLCID, ret_locale, LOCALE_NAME_MAX_LENGTH, 0);
// rv 0 means that the function failed to match up the LCID, so we fallback
// to the old function
if (rv != 0)
{
locale.Assign(ret_locale);
return;
}
locale.SetCapacity(LOCALE_NAME_MAX_LENGTH);
int length = LCIDToLocaleName(winLCID,
reinterpret_cast<LPWSTR>(locale.BeginWriting()),
LOCALE_NAME_MAX_LENGTH, 0);
// 0 length means that the function failed to match up the LCID,
// so we fallback to the old function
if (length) {
// length contains null terminate.
locale.SetLength(length - 1);
return;
}
DWORD lang_id, sublang_id;

View File

@ -109,8 +109,11 @@ public:
int32_t processType = mProcessType;
int32_t crashType = mCrashType;
nsString childDumpID(mChildDumpID);
nsCOMPtr<nsIAsyncShutdownBlocker> self(this);
NS_DispatchToMainThread(NS_NewRunnableFunction([=] () -> void {
NS_DispatchToMainThread(NS_NewRunnableFunction([
self, processType, crashType, childDumpID
] {
nsCOMPtr<nsICrashService> crashService =
do_GetService("@mozilla.org/crashservice;1");
if (crashService) {
@ -120,7 +123,7 @@ public:
nsCOMPtr<nsIAsyncShutdownClient> barrier = GetShutdownBarrier();
if (barrier) {
barrier->RemoveBlocker(this);
barrier->RemoveBlocker(self);
}
}));

View File

@ -27,7 +27,7 @@ public:
explicit ProcessChild(ProcessId aParentPid);
virtual ~ProcessChild();
virtual bool Init() = 0;
virtual bool Init(int aArgc, char* aArgv[]) = 0;
virtual void CleanUp()
{ }

View File

@ -259,14 +259,23 @@ DecommitPages(void* addr, size_t bytes)
static void*
ComputeRandomAllocationAddress()
{
// Return a random, page-aligned address. x64 CPUs have a 48-bit address
// space and on some platforms the OS will give us access to 47 bits, so
// to be safe we right shift by 18 to leave 46 bits.
uint64_t rand = js::GenerateRandomSeed();
# ifdef HAVE_64BIT_BUILD
// x64 CPUs have a 48-bit address space and on some platforms the OS will
// give us access to 47 bits, so to be safe we right shift by 18 to leave
// 46 bits.
rand >>= 18;
# else
// On 32-bit, right shift by 34 to leave 30 bits, range [0, 1GiB). Then add
// 512MiB to get range [512MiB, 1.5GiB), or [0x20000000, 0x60000000). This
// is based on V8 comments in platform-posix.cc saying this range is
// relatively unpopulated across a variety of kernels.
rand >>= 34;
rand += 512 * 1024 * 1024;
# endif
// Ensure page alignment.
uintptr_t mask = ~uintptr_t(gc::SystemPageSize() - 1);
return (void*) uintptr_t(rand & mask);
}

View File

@ -5,6 +5,6 @@
== box-sizing-content-box-001.xht box-sizing-content-box-001-ref.xht
== box-sizing-content-box-002.xht box-sizing-content-box-002-ref.xht
== box-sizing-content-box-003.xht box-sizing-content-box-003-ref.xht
random-if(Android) fuzzy-if(skiaContent,15,50) fuzzy-if(OSX,255,1575) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht # bug 982547, Bug 1295466
skip-if(Android||gtkWidget) fuzzy-if(skiaContent,15,50) fuzzy-if(OSX,255,1575) == box-sizing-replaced-001.xht box-sizing-replaced-001-ref.xht # bug 982547, Bug 1295466, Bug 1321707
fuzzy-if(Android,27,874) fuzzy-if(gtkWidget,14,29) == box-sizing-replaced-002.xht box-sizing-replaced-002-ref.xht # Bug 1128229, Bug 1313772
fuzzy-if(Android,27,925) fuzzy-if(gtkWidget,14,43) == box-sizing-replaced-003.xht box-sizing-replaced-003-ref.xht # Bug 1128229

View File

@ -728,7 +728,8 @@ nsXULTooltipListener::GetSourceTreeBoxObject(nsITreeBoxObject** aBoxObject)
nsCOMPtr<nsIContent> sourceNode = do_QueryReferent(mSourceNode);
if (mIsSourceTree && sourceNode) {
RefPtr<nsXULElement> xulEl = nsXULElement::FromContent(sourceNode);
RefPtr<nsXULElement> xulEl =
nsXULElement::FromContentOrNull(sourceNode->GetParent());
if (xulEl) {
IgnoredErrorResult ignored;
nsCOMPtr<nsIBoxObject> bx = xulEl->GetBoxObject(ignored);

View File

@ -58,9 +58,9 @@ extensions.on("page-shutdown", (type, context) => {
}
let {BrowserApp} = context.xulBrowser.ownerGlobal;
if (BrowserApp) {
let tab = BrowserApp.getTabForBrowser(context.xulBrowser);
if (tab) {
BrowserApp.closeTab(tab);
let nativeTab = BrowserApp.getTabForBrowser(context.xulBrowser);
if (nativeTab) {
BrowserApp.closeTab(nativeTab);
}
}
}
@ -89,16 +89,16 @@ let tabListener = {
onLocationChange(browser, webProgress, request, locationURI, flags) {
if (webProgress.isTopLevel) {
let {BrowserApp} = browser.ownerGlobal;
let tab = BrowserApp.getTabForBrowser(browser);
let nativeTab = BrowserApp.getTabForBrowser(browser);
// Now we are certain that the first page in the tab was loaded.
this.initializingTabs.delete(tab);
this.initializingTabs.delete(nativeTab);
// browser.innerWindowID is now set, resolve the promises if any.
let deferred = this.tabReadyPromises.get(tab);
let deferred = this.tabReadyPromises.get(nativeTab);
if (deferred) {
deferred.resolve(tab);
this.tabReadyPromises.delete(tab);
deferred.resolve(nativeTab);
this.tabReadyPromises.delete(nativeTab);
}
}
},
@ -109,19 +109,20 @@ let tabListener = {
* changes to the requested URL. Other tabs are assumed to be ready once their
* inner window ID is known.
*
* @param {XULElement} tab The <tab> element.
* @param {NativeTab} nativeTab The native tab object.
* @returns {Promise} Resolves with the given tab once ready.
*/
awaitTabReady(tab) {
let deferred = this.tabReadyPromises.get(tab);
awaitTabReady(nativeTab) {
let deferred = this.tabReadyPromises.get(nativeTab);
if (!deferred) {
deferred = PromiseUtils.defer();
if (!this.initializingTabs.has(tab) && (tab.browser.innerWindowID ||
tab.browser.currentURI.spec === "about:blank")) {
deferred.resolve(tab);
if (!this.initializingTabs.has(nativeTab) &&
(nativeTab.browser.innerWindowID ||
nativeTab.browser.currentURI.spec === "about:blank")) {
deferred.resolve(nativeTab);
} else {
this.initTabReady();
this.tabReadyPromises.set(tab, deferred);
this.tabReadyPromises.set(nativeTab, deferred);
}
}
return deferred.promise;
@ -148,7 +149,7 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
tab = tabManager.getWrapper(tabTracker.activeTab);
}
await tabListener.awaitTabReady(tab.tab);
await tabListener.awaitTabReady(tab.nativeTab);
return tab;
}
@ -163,7 +164,7 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
onCreated: new SingletonEventManager(context, "tabs.onCreated", fire => {
let listener = (eventName, event) => {
fire.async(tabManager.convert(event.tab));
fire.async(tabManager.convert(event.nativeTab));
};
tabTracker.on("tab-created", listener);
@ -233,12 +234,12 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let listener = event => {
let needed = [];
let tab;
let nativeTab;
switch (event.type) {
case "DOMTitleChanged": {
let {BrowserApp} = getBrowserWindow(event.target.ownerGlobal);
tab = BrowserApp.getTabForWindow(event.target.ownerGlobal);
nativeTab = BrowserApp.getTabForWindow(event.target.ownerGlobal);
needed.push("title");
break;
}
@ -246,17 +247,17 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
case "DOMAudioPlaybackStarted":
case "DOMAudioPlaybackStopped": {
let {BrowserApp} = event.target.ownerGlobal;
tab = BrowserApp.getTabForBrowser(event.originalTarget);
nativeTab = BrowserApp.getTabForBrowser(event.originalTarget);
needed.push("audible");
break;
}
}
if (!tab) {
if (!nativeTab) {
return;
}
tab = tabManager.getWrapper(tab);
let tab = tabManager.getWrapper(nativeTab);
let changeInfo = {};
for (let prop of needed) {
changeInfo[prop] = tab[prop];
@ -267,14 +268,14 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let statusListener = ({browser, status, url}) => {
let {BrowserApp} = browser.ownerGlobal;
let tab = BrowserApp.getTabForBrowser(browser);
if (tab) {
let nativeTab = BrowserApp.getTabForBrowser(browser);
if (nativeTab) {
let changed = {status};
if (url) {
changed.url = url;
}
fireForTab(tabManager.wrapTab(tab), changed);
fireForTab(tabManager.wrapTab(nativeTab), changed);
}
};
@ -319,13 +320,13 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
options.disallowInheritPrincipal = true;
tabListener.initTabReady();
let tab = BrowserApp.addTab(url, options);
let nativeTab = BrowserApp.addTab(url, options);
if (createProperties.url) {
tabListener.initializingTabs.add(tab);
tabListener.initializingTabs.add(nativeTab);
}
return tabManager.convert(tab);
return tabManager.convert(nativeTab);
},
async remove(tabs) {
@ -334,15 +335,15 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
}
for (let tabId of tabs) {
let tab = tabTracker.getTab(tabId);
tab.browser.ownerGlobal.BrowserApp.closeTab(tab);
let nativeTab = tabTracker.getTab(tabId);
nativeTab.browser.ownerGlobal.BrowserApp.closeTab(nativeTab);
}
},
async update(tabId, updateProperties) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let {BrowserApp} = tab.browser.ownerGlobal;
let {BrowserApp} = nativeTab.browser.ownerGlobal;
if (updateProperties.url !== null) {
let url = context.uri.resolve(updateProperties.url);
@ -351,29 +352,29 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
return Promise.reject({message: `Illegal URL: ${url}`});
}
tab.browser.loadURI(url);
nativeTab.browser.loadURI(url);
}
if (updateProperties.active !== null) {
if (updateProperties.active) {
BrowserApp.selectTab(tab);
BrowserApp.selectTab(nativeTab);
} else {
// Not sure what to do here? Which tab should we select?
}
}
// FIXME: highlighted/selected, muted, pinned, openerTabId
return tabManager.convert(tab);
return tabManager.convert(nativeTab);
},
async reload(tabId, reloadProperties) {
let tab = getTabOrActive(tabId);
let nativeTab = getTabOrActive(tabId);
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
if (reloadProperties && reloadProperties.bypassCache) {
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
}
tab.browser.reloadWithFlags(flags);
nativeTab.browser.reloadWithFlags(flags);
},
async get(tabId) {
@ -400,41 +401,15 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
tab => tab.convert());
},
captureVisibleTab(windowId, options) {
if (!extension.hasPermission("<all_urls>")) {
return Promise.reject({message: "The <all_urls> permission is required to use the captureVisibleTab API"});
}
async captureVisibleTab(windowId, options) {
let window = windowId == null ?
windowTracker.topWindow :
windowTracker.getWindow(windowId, context);
let tab = window.BrowserApp.selectedTab;
return tabListener.awaitTabReady(tab).then(() => {
let {browser} = tab;
let recipient = {
innerWindowID: browser.innerWindowID,
};
let tab = tabManager.wrapTab(window.BrowserApp.selectedTab);
await tabListener.awaitTabReady(tab.nativeTab);
if (!options) {
options = {};
}
if (options.format == null) {
options.format = "png";
}
if (options.quality == null) {
options.quality = 92;
}
let message = {
options,
width: browser.clientWidth,
height: browser.clientHeight,
};
return context.sendMessage(browser.messageManager, "Extension:Capture",
message, {recipient});
});
return tab.capture(context, options);
},
async executeScript(tabId, details) {

View File

@ -28,17 +28,46 @@ const BrowserStatusFilter = Components.Constructor(
let tabTracker;
let windowTracker;
/**
* A nsIWebProgressListener for a specific XUL browser, which delegates the
* events that it receives to a tab progress listener, and prepends the browser
* to their arguments list.
*
* @param {XULElement} browser
* A XUL browser element.
* @param {object} listener
* A tab progress listener object.
* @param {integer} flags
* The web progress notification flags with which to filter events.
*/
class BrowserProgressListener {
constructor(browser, listener, flags) {
this.listener = listener;
this.browser = browser;
this.filter = new BrowserStatusFilter(this, flags);
this.browser.addProgressListener(this.filter, flags);
}
/**
* Destroy the listener, and perform any necessary cleanup.
*/
destroy() {
this.browser.removeProgressListener(this.filter);
this.filter.removeProgressListener(this);
}
/**
* Calls the appropriate listener in the wrapped tab progress listener, with
* the wrapped XUL browser object as its first argument, and the additional
* arguments in `args`.
*
* @param {string} method
* The name of the nsIWebProgressListener method which is being
* delegated.
* @param {*} args
* The arguments to pass to the delegated listener.
* @private
*/
delegate(method, ...args) {
if (this.listener[method]) {
this.listener[method](this.browser, ...args);
@ -57,6 +86,16 @@ class BrowserProgressListener {
onSecurityChange(webProgress, request, state) {}
}
/**
* Handles wrapping a tab progress listener in browser-specific
* BrowserProgressListener instances, an attaching them to each tab in a given
* browser window.
*
* @param {DOMWindow} window
* The browser window to which to attach the listeners.
* @param {object} listener
* The tab progress listener to wrap.
*/
class ProgressListenerWrapper {
constructor(window, listener) {
this.window = window;
@ -66,39 +105,61 @@ class ProgressListenerWrapper {
this.flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
Ci.nsIWebProgress.NOTIFY_LOCATION;
for (let tab of this.window.BrowserApp.tabs) {
this.addBrowserProgressListener(tab.browser);
for (let nativeTab of this.window.BrowserApp.tabs) {
this.addBrowserProgressListener(nativeTab.browser);
}
this.window.BrowserApp.deck.addEventListener("TabOpen", this);
}
/**
* Destroy the wrapper, removing any remaining listeners it has added.
*/
destroy() {
this.window.BrowserApp.deck.removeEventListener("TabOpen", this);
for (let tab of this.window.BrowserApp.tabs) {
this.removeProgressListener(tab.browser);
for (let nativeTab of this.window.BrowserApp.tabs) {
this.removeProgressListener(nativeTab.browser);
}
}
/**
* Adds a progress listener to the given XUL browser element.
*
* @param {XULElement} browser
* The XUL browser to add the listener to.
* @private
*/
addBrowserProgressListener(browser) {
this.removeProgressListener(browser);
let listener = new BrowserProgressListener(browser, this.listener, this.flags);
this.listeners.set(browser, listener);
browser.addProgressListener(listener.filter, this.flags);
}
/**
* Removes a progress listener from the given XUL browser element.
*
* @param {XULElement} browser
* The XUL browser to remove the listener from.
* @private
*/
removeProgressListener(browser) {
let listener = this.listeners.get(browser);
if (listener) {
browser.removeProgressListener(listener.filter);
listener.destroy();
this.listeners.delete(browser);
}
}
/**
* Handles tab open events, and adds the necessary progress listeners to the
* new tabs.
*
* @param {Event} event
* The DOM event to handle.
* @private
*/
handleEvent(event) {
if (event.type === "TabOpen") {
this.addBrowserProgressListener(event.originalTarget);
@ -107,7 +168,6 @@ class ProgressListenerWrapper {
}
class WindowTracker extends WindowTrackerBase {
constructor(...args) {
super(...args);
@ -132,6 +192,24 @@ class WindowTracker extends WindowTrackerBase {
}
}
}
/**
* An event manager API provider which listens for an event in the Android
* global EventDispatcher, 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 an object detailing the
* EventDispatcher event that was received.
*
* @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 EventDispatcher event to listen for.
* @param {function} listener
* The listener function to call when an EventDispatcher event is
* recieved.
*/
global.GlobalEventManager = class extends SingletonEventManager {
constructor(context, name, event, listener) {
super(context, name, fire => {
@ -149,6 +227,21 @@ global.GlobalEventManager = class extends SingletonEventManager {
}
};
/**
* 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 SingletonEventManager {
constructor(context, name, event, listener) {
super(context, name, fire => {
@ -173,16 +266,16 @@ class TabTracker extends TabTrackerBase {
windowTracker.addListener("TabOpen", this);
}
getId(tab) {
return tab.id;
getId(nativeTab) {
return nativeTab.id;
}
getTab(id, default_ = undefined) {
let win = windowTracker.topWindow;
if (win) {
let tab = win.BrowserApp.getTabForId(id);
if (tab) {
return tab;
let nativeTab = win.BrowserApp.getTabForId(id);
if (nativeTab) {
return nativeTab;
}
}
if (default_ !== undefined) {
@ -191,31 +284,56 @@ class TabTracker extends TabTrackerBase {
throw new ExtensionError(`Invalid tab ID: ${id}`);
}
/**
* Handles tab open and close events, and emits the appropriate internal
* events for them.
*
* @param {Event} event
* A DOM event to handle.
* @private
*/
handleEvent(event) {
const {BrowserApp} = event.target.ownerGlobal;
let tab = BrowserApp.getTabForBrowser(event.target);
let nativeTab = BrowserApp.getTabForBrowser(event.target);
switch (event.type) {
case "TabOpen":
this.emitCreated(tab);
this.emitCreated(nativeTab);
break;
case "TabClose":
this.emitRemoved(tab, false);
this.emitRemoved(nativeTab, false);
break;
}
}
emitCreated(tab) {
this.emit("tab-created", {tab});
/**
* Emits a "tab-created" event for the given tab element.
*
* @param {NativeTab} nativeTab
* The tab element which is being created.
* @private
*/
emitCreated(nativeTab) {
this.emit("tab-created", {nativeTab});
}
emitRemoved(tab, isWindowClosing) {
let windowId = windowTracker.getId(tab.browser.ownerGlobal);
let tabId = this.getId(tab);
/**
* 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.browser.ownerGlobal);
let tabId = this.getId(nativeTab);
Services.tm.mainThread.dispatch(() => {
this.emit("tab-removed", {tab, tabId, windowId, isWindowClosing});
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
}, Ci.nsIThread.DISPATCH_NORMAL);
}
@ -229,9 +347,9 @@ class TabTracker extends TabTrackerBase {
if (BrowserApp) {
result.windowId = windowTracker.getId(browser.ownerGlobal);
let tab = BrowserApp.getTabForBrowser(browser);
if (tab) {
result.tabId = this.getId(tab);
let nativeTab = BrowserApp.getTabForBrowser(browser);
if (nativeTab) {
result.tabId = this.getId(nativeTab);
}
}
@ -258,15 +376,15 @@ class Tab extends TabBase {
}
get audible() {
return this.tab.playingAudio;
return this.nativeTab.playingAudio;
}
get browser() {
return this.tab.browser;
return this.nativeTab.browser;
}
get cookieStoreId() {
return getCookieStoreIdForTab(this, this.tab);
return getCookieStoreIdForTab(this, this.nativeTab);
}
get height() {
@ -278,7 +396,7 @@ class Tab extends TabBase {
}
get index() {
return this.window.BrowserApp.tabs.indexOf(this.tab);
return this.window.BrowserApp.tabs.indexOf(this.nativeTab);
}
get mutedInfo() {
@ -290,11 +408,11 @@ class Tab extends TabBase {
}
get active() {
return this.tab.getActive();
return this.nativeTab.getActive();
}
get selected() {
return this.tab.getActive();
return this.nativeTab.getActive();
}
get status() {
@ -357,8 +475,8 @@ class Window extends WindowBase {
* getTabs() {
let {tabManager} = this.extension;
for (let tab of this.window.BrowserApp.tabs) {
yield tabManager.getWrapper(tab);
for (let nativeTab of this.window.BrowserApp.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
}
@ -367,24 +485,24 @@ Object.assign(global, {Tab, Window});
class TabManager extends TabManagerBase {
get(tabId, default_ = undefined) {
let tab = tabTracker.getTab(tabId, default_);
let nativeTab = tabTracker.getTab(tabId, default_);
if (tab) {
return this.getWrapper(tab);
if (nativeTab) {
return this.getWrapper(nativeTab);
}
return default_;
}
addActiveTabPermission(tab = tabTracker.activeTab) {
return super.addActiveTabPermission(tab);
addActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.addActiveTabPermission(nativeTab);
}
revokeActiveTabPermission(tab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(tab);
revokeActiveTabPermission(nativeTab = tabTracker.activeTab) {
return super.revokeActiveTabPermission(nativeTab);
}
wrapTab(tab) {
return new Tab(this.extension, tab, tab.id);
wrapTab(nativeTab) {
return new Tab(this.extension, nativeTab, nativeTab.id);
}
}

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
@ -546,6 +546,14 @@ NS_INTERFACE_MAP_END
* nsIPrefService Implementation
*/
InfallibleTArray<Preferences::PrefSetting>* gInitPrefs;
/*static*/
void
Preferences::SetInitPreferences(nsTArray<PrefSetting>* aPrefs) {
gInitPrefs = new InfallibleTArray<PrefSetting>(mozilla::Move(*aPrefs));
}
nsresult
Preferences::Init()
{
@ -557,15 +565,13 @@ Preferences::Init()
rv = pref_InitInitialObjects();
NS_ENSURE_SUCCESS(rv, rv);
using mozilla::dom::ContentChild;
if (XRE_IsContentProcess()) {
InfallibleTArray<PrefSetting> prefs;
ContentChild::GetSingleton()->SendReadPrefsArray(&prefs);
// Store the array
for (uint32_t i = 0; i < prefs.Length(); ++i) {
pref_SetPref(prefs[i]);
MOZ_ASSERT(gInitPrefs);
for (unsigned int i = 0; i < gInitPrefs->Length(); i++) {
Preferences::SetPreference(gInitPrefs->ElementAt(i));
}
delete gInitPrefs;
gInitPrefs = nullptr;
return NS_OK;
}
@ -780,6 +786,14 @@ Preferences::GetPreferences(InfallibleTArray<PrefSetting>* aPrefs)
}
}
#ifdef DEBUG
void
Preferences::SetInitPhase(pref_initPhase phase)
{
pref_SetInitPhase(phase);
}
#endif
NS_IMETHODIMP
Preferences::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
{

View File

@ -28,6 +28,20 @@ typedef void (*PrefChangedFunc)(const char *, void *);
#define have_PrefChangedFunc_typedef
#endif
#ifdef DEBUG
enum pref_initPhase {
START,
BEGIN_INIT_PREFS,
END_INIT_PREFS,
BEGIN_ALL_PREFS,
END_ALL_PREFS
};
#define SET_PREF_PHASE(p) Preferences::SetInitPhase(p)
#else
#define SET_PREF_PHASE(p) do { } while (0)
#endif
namespace mozilla {
namespace dom {
@ -366,6 +380,12 @@ public:
static void GetPreference(PrefSetting* aPref);
static void SetPreference(const PrefSetting& aPref);
static void SetInitPreferences(nsTArray<PrefSetting>* aPrefs);
#ifdef DEBUG
static void SetInitPhase(pref_initPhase phase);
#endif
static int64_t SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf);
static void DirtyCallback();

View File

@ -4533,13 +4533,7 @@ pref("webgl.max-acceptable-fb-status-invals", 0);
pref("webgl.enable-webgl2", true);
#ifdef RELEASE_OR_BETA
// Keep this disabled on Release and Beta for now. (see bug 1171228)
pref("webgl.enable-debug-renderer-info", false);
#else
pref("webgl.enable-debug-renderer-info", true);
#endif
pref("webgl.renderer-string-override", "");
pref("webgl.vendor-string-override", "");

View File

@ -29,6 +29,7 @@
#include "prprf.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/ContentPrefs.h"
#include "nsQuickSort.h"
#include "nsString.h"
#include "nsPrintfCString.h"
@ -736,13 +737,48 @@ static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags
}
return flags;
}
#ifdef DEBUG
static pref_initPhase gPhase = START;
void
pref_SetInitPhase(pref_initPhase phase)
{
gPhase = phase;
}
struct StringComparator
{
const char* mKey;
explicit StringComparator(const char* aKey) : mKey(aKey) {}
int operator()(const char* string) const {
return strcmp(mKey, string);
}
};
bool
inInitArray(const char* key)
{
size_t prefsLen;
size_t found;
const char** list = mozilla::dom::ContentPrefs::GetContentPrefs(&prefsLen);
return BinarySearchIf(list, 0, prefsLen,
StringComparator(key), &found);
}
#endif
PrefHashEntry* pref_HashTableLookup(const char *key)
{
#ifndef MOZ_B2G
MOZ_ASSERT(NS_IsMainThread());
#endif
MOZ_ASSERT((!XRE_IsContentProcess() || gPhase != START),
"pref access before commandline prefs set");
/* If you're hitting this assertion, you've added a pref access to start up.
* Consider moving it later or add it to the whitelist in ContentPrefs.cpp
* and get review from a DOM peer
*/
MOZ_ASSERT((!XRE_IsContentProcess() || gPhase > END_INIT_PREFS || inInitArray(key)),
"accessing non-init pref before the rest of the prefs are sent");
return static_cast<PrefHashEntry*>(gHashTable->Search(key));
}

View File

@ -10,6 +10,7 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include "Preferences.h"
extern PLDHashTable* gHashTable;
@ -25,6 +26,11 @@ pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount);
nsresult
pref_SetPref(const mozilla::dom::PrefSetting& aPref);
#ifdef DEBUG
void
pref_SetInitPhase(pref_initPhase phase);
#endif
int pref_CompareStrings(const void *v1, const void *v2, void* unused);
PrefHashEntry* pref_HashTableLookup(const char *key);

View File

@ -40,13 +40,8 @@ namespace net {
/**
* Class that provides an nsILoadInfo implementation.
*
* Note that there is no reason why this class should be MOZ_EXPORT, but
* Thunderbird relies on some insane hacks which require this, so we'll leave it
* as is for now, but hopefully we'll be able to remove the MOZ_EXPORT keyword
* from this class at some point. See bug 1149127 for the discussion.
*/
class MOZ_EXPORT LoadInfo final : public nsILoadInfo
class LoadInfo final : public nsILoadInfo
{
public:
NS_DECL_ISUPPORTS

View File

@ -273,12 +273,6 @@ interface nsIPermissionManager : nsISupports
in boolean exactHost,
in uint64_t sessionExpireTime,
in uint64_t persistentExpireTime);
/**
* Remove all current permission settings and get permission settings from
* chrome process.
*/
void refreshPermission();
};
%{ C++

View File

@ -0,0 +1 @@
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1332564

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/../macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
iconv -f UTF-8 -t UTF-16 > $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings
$(NSINSTALL) -D $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS/updater-xpcshell
$(NSINSTALL) updater-xpcshell $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS
$(NSINSTALL) $(FINAL_TARGET)/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater-xpcshell.app/Contents/MacOS
rm -Rf $(XPCSHELLTESTROOT)/data/updater.app
mv $(XPCSHELLTESTROOT)/data/updater-xpcshell.app $(XPCSHELLTESTROOT)/data/updater.app
mv $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/updater-xpcshell $(XPCSHELLTESTROOT)/data/updater.app/Contents/MacOS/org.mozilla.updater
@ -35,7 +35,7 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
# Copy for mochitest chrome tests
rsync -a -C $(XPCSHELLTESTROOT)/data/updater.app $(MOCHITESTROOT)/data/
else
cp $(PROGRAM) $(XPCSHELLTESTROOT)/data/updater$(BIN_SUFFIX)
cp $(PROGRAM) $(MOCHITESTROOT)/data/updater$(BIN_SUFFIX)
cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(XPCSHELLTESTROOT)/data/updater$(BIN_SUFFIX)
cp $(FINAL_TARGET)/updater-xpcshell$(BIN_SUFFIX) $(MOCHITESTROOT)/data/updater$(BIN_SUFFIX)
endif
endif # COMPILE_ENVIRONMENT

View File

@ -4,10 +4,11 @@
# 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/.
FINAL_TARGET = '_tests/xpcshell/toolkit/mozapps/update/tests'
Program('updater-xpcshell')
updater_rel_path = '../'
DIST_INSTALL = False
DEFINES['TEST_UPDATER'] = True
include('../updater-common.build')

View File

@ -3501,10 +3501,25 @@ int NS_main(int argc, NS_tchar **argv)
*d = NS_T('\0');
++d;
const size_t callbackBackupPathBufSize =
sizeof(gCallbackBackupPath)/sizeof(gCallbackBackupPath[0]);
const int callbackBackupPathLen =
NS_tsnprintf(gCallbackBackupPath, callbackBackupPathBufSize,
NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
if (callbackBackupPathLen < 0 ||
callbackBackupPathLen >= static_cast<int>(callbackBackupPathBufSize)) {
LOG(("NS_main: callback backup path truncated"));
LogFinish();
WriteStatusFile(USAGE_ERROR);
// Don't attempt to launch the callback when the callback path is
// longer than expected.
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
return 1;
}
// Make a copy of the callback executable so it can be read when patching.
NS_tsnprintf(gCallbackBackupPath,
sizeof(gCallbackBackupPath)/sizeof(gCallbackBackupPath[0]),
NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
NS_tremove(gCallbackBackupPath);
if(!CopyFileW(argv[callbackIndex], gCallbackBackupPath, true)) {
DWORD copyFileError = GetLastError();

View File

@ -625,49 +625,8 @@ XRE_InitChildProcess(int aArgc,
process = new PluginProcessChild(parentPID);
break;
case GeckoProcessType_Content: {
process = new ContentProcess(parentPID);
// If passed in grab the application path for xpcom init
bool foundAppdir = false;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
// If passed in grab the profile path for sandboxing
bool foundProfile = false;
#endif
for (int idx = aArgc; idx > 0; idx--) {
if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) {
MOZ_ASSERT(!foundAppdir);
if (foundAppdir) {
continue;
}
nsCString appDir;
appDir.Assign(nsDependentCString(aArgv[idx+1]));
static_cast<ContentProcess*>(process.get())->SetAppDir(appDir);
foundAppdir = true;
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
if (aArgv[idx] && !strcmp(aArgv[idx], "-profile")) {
MOZ_ASSERT(!foundProfile);
if (foundProfile) {
continue;
}
nsCString profile;
profile.Assign(nsDependentCString(aArgv[idx+1]));
static_cast<ContentProcess*>(process.get())->SetProfile(profile);
foundProfile = true;
}
if (foundProfile && foundAppdir) {
break;
}
#else
if (foundAppdir) {
break;
}
#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
}
}
case GeckoProcessType_Content:
process = new ContentProcess(parentPID);
break;
case GeckoProcessType_IPDLUnitTest:
@ -690,7 +649,7 @@ XRE_InitChildProcess(int aArgc,
MOZ_CRASH("Unknown main thread class");
}
if (!process->Init()) {
if (!process->Init(aArgc, aArgv)) {
return NS_ERROR_FAILURE;
}

View File

@ -0,0 +1,195 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/IncrementalTokenizer.h"
#include "mozilla/AutoRestore.h"
#include "nsIInputStream.h"
#include "IncrementalTokenizer.h"
#include <algorithm>
namespace mozilla {
IncrementalTokenizer::IncrementalTokenizer(Consumer aConsumer,
const char * aWhitespaces,
const char * aAdditionalWordChars,
uint32_t aRawMinBuffered)
: TokenizerBase(aWhitespaces, aAdditionalWordChars)
#ifdef DEBUG
, mConsuming(false)
#endif
, mNeedMoreInput(false)
, mRollback(false)
, mInputCursor(0)
, mConsumer(aConsumer)
{
mInputFinished = false;
mMinRawDelivery = aRawMinBuffered;
}
nsresult IncrementalTokenizer::FeedInput(const nsACString & aInput)
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
mInput.Append(aInput);
return Process();
}
nsresult IncrementalTokenizer::FeedInput(nsIInputStream * aInput, uint32_t aCount)
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
MOZ_ASSERT(!mConsuming);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
nsresult rv = NS_OK;
while (NS_SUCCEEDED(rv) && aCount) {
nsCString::index_type remainder = mInput.Length();
nsCString::index_type load =
std::min<nsCString::index_type>(aCount, PR_UINT32_MAX - remainder);
if (!load) {
// To keep the API simple, we fail if the input data buffer if filled.
// It's highly unlikely there will ever be such amout of data cumulated
// unless a logic fault in the consumer code.
NS_ERROR("IncrementalTokenizer consumer not reading data?");
return NS_ERROR_OUT_OF_MEMORY;
}
if (!mInput.SetLength(remainder + load, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCString::char_iterator buffer = mInput.BeginWriting() + remainder;
uint32_t read;
rv = aInput->Read(buffer, load, &read);
if (NS_SUCCEEDED(rv)) {
// remainder + load fits the uint32_t size, so must remainder + read.
mInput.SetLength(remainder + read);
aCount -= read;
rv = Process();
}
}
return rv;
}
nsresult IncrementalTokenizer::FinishInput()
{
NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
MOZ_ASSERT(!mInputFinished);
MOZ_ASSERT(!mConsuming);
mInput.Cut(0, mInputCursor);
mInputCursor = 0;
mInputFinished = true;
nsresult rv = Process();
mConsumer = nullptr;
return rv;
}
bool IncrementalTokenizer::Next(Token & aToken)
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
if (mPastEof) {
return false;
}
nsACString::const_char_iterator next = Parse(aToken);
mPastEof = aToken.Type() == TOKEN_EOF;
if (next == mCursor && !mPastEof) {
// Not enough input to make a deterministic decision.
return false;
}
AssignFragment(aToken, mCursor, next);
mCursor = next;
return true;
}
void IncrementalTokenizer::NeedMoreInput()
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
// When the input has been finished, we can't set the flag to prevent
// indefinite wait for more input (that will never come)
mNeedMoreInput = !mInputFinished;
}
void IncrementalTokenizer::Rollback()
{
// Assert we are called only from the consumer callback
MOZ_ASSERT(mConsuming);
mRollback = true;
}
nsresult IncrementalTokenizer::Process()
{
#ifdef DEBUG
// Assert we are not re-entered
MOZ_ASSERT(!mConsuming);
AutoRestore<bool> consuming(mConsuming);
mConsuming = true;
#endif
MOZ_ASSERT(!mPastEof);
nsresult rv = NS_OK;
mInput.BeginReading(mCursor);
mCursor += mInputCursor;
mInput.EndReading(mEnd);
while (NS_SUCCEEDED(rv) && !mPastEof) {
Token token;
nsACString::const_char_iterator next = Parse(token);
mPastEof = token.Type() == TOKEN_EOF;
if (next == mCursor && !mPastEof) {
// Not enough input to make a deterministic decision.
break;
}
AssignFragment(token, mCursor, next);
nsACString::const_char_iterator rollback = mCursor;
mCursor = next;
mNeedMoreInput = mRollback = false;
rv = mConsumer(token, *this);
if (NS_FAILED(rv)) {
break;
}
if (mNeedMoreInput || mRollback) {
mCursor = rollback;
mPastEof = false;
if (mNeedMoreInput) {
break;
}
}
}
mInputCursor = mCursor - mInput.BeginReading();
return rv;
}
} // mozilla

View File

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 INCREMENTAL_TOKENIZER_H__
#define INCREMENTAL_TOKENIZER_H__
#include "mozilla/Tokenizer.h"
#include "nsError.h"
#include <functional>
class nsIInputStream;
namespace mozilla {
class IncrementalTokenizer : public TokenizerBase
{
public:
/**
* The consumer callback. The function is called for every single token
* as found in the input. Failure result returned by this callback stops
* the tokenization immediately and bubbles to result of Feed/FinishInput.
*
* Fragment()s of consumed tokens are ensured to remain valid until next call to
* Feed/FinishInput and are pointing to a single linear buffer. Hence, those can
* be safely used to accumulate the data for processing after Feed/FinishInput
* returned.
*/
typedef std::function<nsresult(Token const&, IncrementalTokenizer& i)> Consumer;
/**
* For aWhitespaces and aAdditionalWordChars arguments see TokenizerBase.
*
* @param aConsumer
* A mandatory non-null argument, a function that consumes the tokens as they
* come when the tokenizer is fed.
* @param aRawMinBuffered
* When we have buffered at least aRawMinBuffered data, but there was no custom
* token found so far because of too small incremental feed chunks, deliver
* the raw data to preserve streaming and to save memory. This only has effect
* in OnlyCustomTokenizing mode.
*/
explicit IncrementalTokenizer(Consumer aConsumer,
const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr,
uint32_t aRawMinBuffered = 1024);
/**
* Pushes the input to be tokenized. These directly call the Consumer callback
* on every found token. Result of the Consumer callback is returned here.
*
* The tokenizer must be initialized with a valid consumer prior call to these
* methods. It's not allowed to call Feed/FinishInput from inside the Consumer
* callback.
*/
nsresult FeedInput(const nsACString& aInput);
nsresult FeedInput(nsIInputStream* aInput, uint32_t aCount);
nsresult FinishInput();
/**
* Can only be called from inside the consumer callback.
*
* When there is still anything to read from the input, tokenize it, store
* the token type and value to aToken result and shift the cursor past this
* just parsed token. Each call to Next() reads another token from
* the input and shifts the cursor.
*
* Returns false if there is not enough data to deterministically recognize
* tokens or when the last returned token was EOF.
*/
MOZ_MUST_USE
bool Next(Token& aToken);
/**
* Can only be called from inside the consumer callback.
*
* Tells the tokenizer to revert the cursor and stop the async parsing until
* next feed of the input. This is useful when more than one token is needed
* to decide on the syntax but there is not enough input to get a next token
* (Next() returned false.)
*/
void NeedMoreInput();
/**
* Can only be called from inside the consumer callback.
*
* This makes the consumer callback be called again while parsing
* the input at the previous cursor position again. This is useful when
* the tokenizer state (custom tokens, tokenization mode) has changed and
* we want to re-parse the input again.
*/
void Rollback();
private:
// Loops over the input with TokenizerBase::Parse and calls the Consumer callback.
nsresult Process();
#ifdef DEBUG
// True when inside the consumer callback, used only for assertions.
bool mConsuming;
#endif // DEBUG
// Modifyable only from the Consumer callback, tells the parser to break, rollback
// and wait for more input.
bool mNeedMoreInput;
// Modifyable only from the Consumer callback, tells the parser to rollback and
// parse the input again, with (if modified) new settings of the tokenizer.
bool mRollback;
// The input buffer. Updated with each call to Feed/FinishInput.
nsCString mInput;
// Numerical index pointing at the current cursor position. We don't keep direct
// reference to the string buffer since the buffer gets often reallocated.
nsCString::index_type mInputCursor;
// Refernce to the consumer function.
Consumer mConsumer;
};
} // mozilla
#endif

View File

@ -7,6 +7,7 @@
#include "Tokenizer.h"
#include "nsUnicharUtils.h"
#include <algorithm>
namespace mozilla {
@ -15,11 +16,9 @@ static const char sWhitespaces[] = " \t";
Tokenizer::Tokenizer(const nsACString& aSource,
const char* aWhitespaces,
const char* aAdditionalWordChars)
: mPastEof(false)
, mHasFailed(false)
, mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
, mAdditionalWordChars(aAdditionalWordChars)
: TokenizerBase(aWhitespaces, aAdditionalWordChars)
{
mInputFinished = true;
aSource.BeginReading(mCursor);
mRecord = mRollback = mCursor;
aSource.EndReading(mEnd);
@ -43,7 +42,7 @@ Tokenizer::Next(Token& aToken)
mRollback = mCursor;
mCursor = Parse(aToken);
aToken.AssignFragment(mRollback, mCursor);
AssignFragment(aToken, mRollback, mCursor);
mPastEof = aToken.Type() == TOKEN_EOF;
mHasFailed = false;
@ -67,7 +66,7 @@ Tokenizer::Check(const TokenType aTokenType, Token& aResult)
mRollback = mCursor;
mCursor = next;
aResult.AssignFragment(mRollback, mCursor);
AssignFragment(aResult, mRollback, mCursor);
mPastEof = aResult.Type() == TOKEN_EOF;
mHasFailed = false;
@ -96,12 +95,6 @@ Tokenizer::Check(const Token& aToken)
return true;
}
bool
Tokenizer::HasFailed() const
{
return mHasFailed;
}
void
Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines)
{
@ -275,24 +268,156 @@ Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion)
aResult.Rebind(mRecord, close - mRecord);
}
// protected
// TokenizerBase
TokenizerBase::TokenizerBase(const char* aWhitespaces,
const char* aAdditionalWordChars)
: mPastEof(false)
, mHasFailed(false)
, mInputFinished(true)
, mMode(Mode::FULL)
, mMinRawDelivery(1024)
, mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
, mAdditionalWordChars(aAdditionalWordChars)
, mCursor(nullptr)
, mEnd(nullptr)
, mNextCustomTokenID(TOKEN_CUSTOM0)
{
}
TokenizerBase::Token
TokenizerBase::AddCustomToken(const nsACString & aValue,
ECaseSensitivity aCaseInsensitivity, bool aEnabled)
{
MOZ_ASSERT(!aValue.IsEmpty());
UniquePtr<Token>& t = *mCustomTokens.AppendElement();
t = MakeUnique<Token>();
t->mType = static_cast<TokenType>(++mNextCustomTokenID);
t->mCustomCaseInsensitivity = aCaseInsensitivity;
t->mCustomEnabled = aEnabled;
t->mCustom.Assign(aValue);
return *t;
}
void
TokenizerBase::RemoveCustomToken(Token& aToken)
{
if (aToken.mType == TOKEN_UNKNOWN) {
// Already removed
return;
}
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (custom->mType == aToken.mType) {
mCustomTokens.RemoveElement(custom);
aToken.mType = TOKEN_UNKNOWN;
return;
}
}
MOZ_ASSERT(false, "Token to remove not found");
}
void
TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled)
{
if (aToken.mType == TOKEN_UNKNOWN) {
// Already removed
return;
}
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (custom->Type() == aToken.Type()) {
// This effectively destroys the token instance.
custom->mCustomEnabled = aEnabled;
return;
}
}
MOZ_ASSERT(false, "Token to change not found");
}
void
TokenizerBase::SetTokenizingMode(Mode aMode)
{
mMode = aMode;
}
bool
Tokenizer::HasInput() const
TokenizerBase::HasFailed() const
{
return mHasFailed;
}
bool
TokenizerBase::HasInput() const
{
return !mPastEof;
}
nsACString::const_char_iterator
Tokenizer::Parse(Token& aToken) const
TokenizerBase::Parse(Token& aToken) const
{
if (mCursor == mEnd) {
if (!mInputFinished) {
return mCursor;
}
aToken = Token::EndOfFile();
return mEnd;
}
nsACString::size_type available = mEnd - mCursor;
uint32_t longestCustom = 0;
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (IsCustom(mCursor, *custom, &longestCustom)) {
aToken = *custom;
return mCursor + custom->mCustom.Length();
}
}
if (!mInputFinished && available < longestCustom) {
// Not enough data to deterministically decide.
return mCursor;
}
nsACString::const_char_iterator next = mCursor;
if (mMode == Mode::CUSTOM_ONLY) {
// We have to do a brute-force search for all of the enabled custom
// tokens.
while (next < mEnd) {
++next;
for (UniquePtr<Token> const& custom : mCustomTokens) {
if (IsCustom(next, *custom)) {
aToken = Token::Raw();
return next;
}
}
}
if (mInputFinished) {
// End of the data reached.
aToken = Token::Raw();
return next;
}
if (longestCustom < available && available > mMinRawDelivery) {
// We can return some data w/o waiting for either a custom token
// or call to FinishData() when we leave the tail where all the
// custom tokens potentially fit, so we can't lose only partially
// delivered tokens. This preserves reasonable granularity.
aToken = Token::Raw();
return mEnd - longestCustom + 1;
}
// Not enough data to deterministically decide.
return mCursor;
}
enum State {
PARSE_INTEGER,
PARSE_WORD,
@ -326,6 +451,9 @@ Tokenizer::Parse(Token& aToken) const
resultingNumber += static_cast<uint64_t>(*next - '0');
++next;
if (IsPending(next)) {
break;
}
if (IsEnd(next) || !IsNumber(*next)) {
if (!resultingNumber.isValid()) {
aToken = Token::Error();
@ -338,6 +466,9 @@ Tokenizer::Parse(Token& aToken) const
case PARSE_WORD:
++next;
if (IsPending(next)) {
break;
}
if (IsEnd(next) || !IsWord(*next)) {
aToken = Token::Word(Substring(mCursor, next));
return next;
@ -346,6 +477,9 @@ Tokenizer::Parse(Token& aToken) const
case PARSE_CRLF:
++next;
if (IsPending(next)) {
break;
}
if (!IsEnd(next) && *next == '\n') { // LF is optional
++next;
}
@ -369,17 +503,24 @@ Tokenizer::Parse(Token& aToken) const
} // switch (state)
} // while (next < end)
return next;
MOZ_ASSERT(!mInputFinished);
return mCursor;
}
bool
Tokenizer::IsEnd(const nsACString::const_char_iterator& caret) const
TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const
{
return caret == mEnd;
}
bool
Tokenizer::IsWordFirst(const char aInput) const
TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const
{
return IsEnd(caret) && !mInputFinished;
}
bool
TokenizerBase::IsWordFirst(const char aInput) const
{
// TODO: make this fully work with unicode
return (ToLowerCase(static_cast<uint32_t>(aInput)) !=
@ -389,50 +530,107 @@ Tokenizer::IsWordFirst(const char aInput) const
}
bool
Tokenizer::IsWord(const char aInput) const
TokenizerBase::IsWord(const char aInput) const
{
return IsWordFirst(aInput) || IsNumber(aInput);
}
bool
Tokenizer::IsNumber(const char aInput) const
TokenizerBase::IsNumber(const char aInput) const
{
// TODO: are there unicode numbers?
return aInput >= '0' && aInput <= '9';
}
// Tokenizer::Token
bool
TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret,
const Token & aCustomToken,
uint32_t * aLongest) const
{
MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0);
if (!aCustomToken.mCustomEnabled) {
return false;
}
Tokenizer::Token::Token(const Token& aOther)
if (aLongest) {
*aLongest = std::max(*aLongest, aCustomToken.mCustom.Length());
}
uint32_t inputLength = mEnd - caret;
if (aCustomToken.mCustom.Length() > inputLength) {
return false;
}
nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length());
if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) {
return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator());
}
return inputFragment.Equals(aCustomToken.mCustom);
}
void TokenizerBase::AssignFragment(Token& aToken,
nsACString::const_char_iterator begin,
nsACString::const_char_iterator end)
{
aToken.AssignFragment(begin, end);
}
// TokenizerBase::Token
TokenizerBase::Token::Token()
: mType(TOKEN_UNKNOWN)
, mChar(0)
, mInteger(0)
, mCustomCaseInsensitivity(CASE_SENSITIVE)
, mCustomEnabled(false)
{
}
TokenizerBase::Token::Token(const Token& aOther)
: mType(aOther.mType)
, mCustom(aOther.mCustom)
, mChar(aOther.mChar)
, mInteger(aOther.mInteger)
, mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity)
, mCustomEnabled(aOther.mCustomEnabled)
{
if (mType == TOKEN_WORD) {
if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) {
mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
}
}
Tokenizer::Token&
Tokenizer::Token::operator=(const Token& aOther)
TokenizerBase::Token&
TokenizerBase::Token::operator=(const Token& aOther)
{
mType = aOther.mType;
mCustom = aOther.mCustom;
mChar = aOther.mChar;
mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
mInteger = aOther.mInteger;
mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity;
mCustomEnabled = aOther.mCustomEnabled;
return *this;
}
void
Tokenizer::Token::AssignFragment(nsACString::const_char_iterator begin,
nsACString::const_char_iterator end)
TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin,
nsACString::const_char_iterator end)
{
mFragment.Rebind(begin, end - begin);
}
// static
Tokenizer::Token
Tokenizer::Token::Word(const nsACString& aValue)
TokenizerBase::Token
TokenizerBase::Token::Raw()
{
Token t;
t.mType = TOKEN_RAW;
return t;
}
// static
TokenizerBase::Token
TokenizerBase::Token::Word(const nsACString& aValue)
{
Token t;
t.mType = TOKEN_WORD;
@ -441,8 +639,8 @@ Tokenizer::Token::Word(const nsACString& aValue)
}
// static
Tokenizer::Token
Tokenizer::Token::Char(const char aValue)
TokenizerBase::Token
TokenizerBase::Token::Char(const char aValue)
{
Token t;
t.mType = TOKEN_CHAR;
@ -451,8 +649,8 @@ Tokenizer::Token::Char(const char aValue)
}
// static
Tokenizer::Token
Tokenizer::Token::Number(const uint64_t aValue)
TokenizerBase::Token
TokenizerBase::Token::Number(const uint64_t aValue)
{
Token t;
t.mType = TOKEN_INTEGER;
@ -461,8 +659,8 @@ Tokenizer::Token::Number(const uint64_t aValue)
}
// static
Tokenizer::Token
Tokenizer::Token::Whitespace()
TokenizerBase::Token
TokenizerBase::Token::Whitespace()
{
Token t;
t.mType = TOKEN_WS;
@ -471,8 +669,8 @@ Tokenizer::Token::Whitespace()
}
// static
Tokenizer::Token
Tokenizer::Token::NewLine()
TokenizerBase::Token
TokenizerBase::Token::NewLine()
{
Token t;
t.mType = TOKEN_EOL;
@ -480,8 +678,8 @@ Tokenizer::Token::NewLine()
}
// static
Tokenizer::Token
Tokenizer::Token::EndOfFile()
TokenizerBase::Token
TokenizerBase::Token::EndOfFile()
{
Token t;
t.mType = TOKEN_EOF;
@ -489,8 +687,8 @@ Tokenizer::Token::EndOfFile()
}
// static
Tokenizer::Token
Tokenizer::Token::Error()
TokenizerBase::Token
TokenizerBase::Token::Error()
{
Token t;
t.mType = TOKEN_ERROR;
@ -498,7 +696,7 @@ Tokenizer::Token::Error()
}
bool
Tokenizer::Token::Equals(const Token& aOther) const
TokenizerBase::Token::Equals(const Token& aOther) const
{
if (mType != aOther.mType) {
return false;
@ -517,21 +715,21 @@ Tokenizer::Token::Equals(const Token& aOther) const
}
char
Tokenizer::Token::AsChar() const
TokenizerBase::Token::AsChar() const
{
MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS);
return mChar;
}
nsDependentCSubstring
Tokenizer::Token::AsString() const
TokenizerBase::Token::AsString() const
{
MOZ_ASSERT(mType == TOKEN_WORD);
return mWord;
}
uint64_t
Tokenizer::Token::AsInteger() const
TokenizerBase::Token::AsInteger() const
{
MOZ_ASSERT(mType == TOKEN_INTEGER);
return mInteger;

View File

@ -9,32 +9,36 @@
#include "nsString.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
namespace mozilla {
/**
* This is a simple implementation of a lexical analyzer or maybe better
* called a tokenizer. It doesn't allow any user dictionaries or
* user define token types.
*
* It is limited only to ASCII input for now. UTF-8 or any other input
* encoding must yet be implemented.
*/
class Tokenizer {
class TokenizerBase
{
public:
/**
* The analyzer works with elements in the input cut to a sequence of token
* where each token has an elementary type
*/
enum TokenType {
enum TokenType : uint32_t
{
TOKEN_UNKNOWN,
TOKEN_RAW,
TOKEN_ERROR,
TOKEN_INTEGER,
TOKEN_WORD,
TOKEN_CHAR,
TOKEN_WS,
TOKEN_EOL,
TOKEN_EOF
TOKEN_EOF,
TOKEN_CUSTOM0 = 1000
};
enum ECaseSensitivity
{
CASE_SENSITIVE,
CASE_INSENSITIVE
};
/**
@ -42,23 +46,29 @@ public:
* to allow checks against it via methods of Tokenizer or are results of some of
* the Tokenizer's methods.
*/
class Token {
class Token
{
TokenType mType;
nsDependentCSubstring mWord;
nsCString mCustom;
char mChar;
uint64_t mInteger;
ECaseSensitivity mCustomCaseInsensitivity;
bool mCustomEnabled;
// If this token is a result of the parsing process, this member is referencing
// a sub-string in the input buffer. If this is externally created Token this
// member is left an empty string.
nsDependentCSubstring mFragment;
friend class Tokenizer;
friend class TokenizerBase;
void AssignFragment(nsACString::const_char_iterator begin,
nsACString::const_char_iterator end);
static Token Raw();
public:
Token() : mType(TOKEN_UNKNOWN), mChar(0), mInteger(0) {}
Token();
Token(const Token& aOther);
Token& operator=(const Token& aOther);
@ -83,6 +93,120 @@ public:
nsDependentCSubstring Fragment() const { return mFragment; }
};
/**
* Consumers may register a custom string that, when found in the input, is considered
* a token and returned by Next*() and accepted by Check*() methods.
* AddCustomToken() returns a reference to a token that can then be comapred using
* Token::Equals() againts the output from Next*() or be passed to Check*().
*/
Token AddCustomToken(const nsACString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true);
template <uint32_t N>
Token AddCustomToken(const char(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true)
{
return AddCustomToken(nsDependentCSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled);
}
void RemoveCustomToken(Token& aToken);
/**
* Only applies to a custom type of a Token (see AddCustomToken above.)
* This turns on and off token recognition. When a custom token is disabled,
* it's ignored as never added as a custom token.
*/
void EnableCustomToken(Token const& aToken, bool aEnable);
/**
* Mode of tokenization.
* FULL tokenization, the default, recognizes built-in tokens and any custom tokens,
* if added.
* CUSTOM_ONLY will only recognize custom tokens, the rest is seen as 'raw'.
* This mode can be understood as a 'binary' mode.
*/
enum class Mode
{
FULL,
CUSTOM_ONLY
};
void SetTokenizingMode(Mode aMode);
/**
* Return false iff the last Check*() call has returned false or when we've read past
* the end of the input string.
*/
MOZ_MUST_USE bool HasFailed() const;
protected:
explicit TokenizerBase(const char* aWhitespaces = nullptr,
const char* aAdditionalWordChars = nullptr);
// false if we have already read the EOF token.
bool HasInput() const;
// Main parsing function, it doesn't shift the read cursor, just returns the next
// token position.
nsACString::const_char_iterator Parse(Token& aToken) const;
// Is read cursor at the end?
bool IsEnd(const nsACString::const_char_iterator& caret) const;
// True, when we are at the end of the input data, but it has not been marked
// as complete yet. In that case we cannot proceed with providing a multi-char token.
bool IsPending(const nsACString::const_char_iterator & caret) const;
// Is read cursor on a character that is a word start?
bool IsWordFirst(const char aInput) const;
// Is read cursor on a character that is an in-word letter?
bool IsWord(const char aInput) const;
// Is read cursor on a character that is a valid number?
// TODO - support multiple radix
bool IsNumber(const char aInput) const;
// Is equal to the given custom token?
bool IsCustom(const nsACString::const_char_iterator& caret,
const Token& aCustomToken, uint32_t* aLongest = nullptr) const;
// Friendly helper to assign a fragment on a Token
static void AssignFragment(Token& aToken,
nsACString::const_char_iterator begin,
nsACString::const_char_iterator end);
// true iff we have already read the EOF token
bool mPastEof;
// true iff the last Check*() call has returned false, reverts to true on Rollback() call
bool mHasFailed;
// true if the input string is final (finished), false when we expect more data
// yet to be fed to the tokenizer (see IncrementalTokenizer derived class).
bool mInputFinished;
// custom only vs full tokenizing mode, see the Parse() method
Mode mMode;
// minimal raw data chunked delivery during incremental feed
uint32_t mMinRawDelivery;
// Customizable list of whitespaces
const char* mWhitespaces;
// Additinal custom word characters
const char* mAdditionalWordChars;
// All these point to the original buffer passed to the constructor or to the incremental
// buffer after FeedInput.
nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
nsACString::const_char_iterator mEnd; // End of the input position
// This is the list of tokens user has registered with AddCustomToken()
nsTArray<UniquePtr<Token>> mCustomTokens;
uint32_t mNextCustomTokenID;
private:
TokenizerBase() = delete;
TokenizerBase(const TokenizerBase&) = delete;
TokenizerBase(TokenizerBase&&) = delete;
TokenizerBase(const TokenizerBase&&) = delete;
TokenizerBase &operator=(const TokenizerBase&) = delete;
};
/**
* This is a simple implementation of a lexical analyzer or maybe better
* called a tokenizer. It doesn't allow any user dictionaries or
* user define token types.
*
* It is limited only to ASCII input for now. UTF-8 or any other input
* encoding must yet be implemented.
*/
class Tokenizer : public TokenizerBase
{
public:
/**
* @param aSource
@ -133,13 +257,6 @@ public:
MOZ_MUST_USE
bool Check(const Token& aToken);
/**
* Return false iff the last Check*() call has returned false or when we've read past
* the end of the input string.
*/
MOZ_MUST_USE
bool HasFailed() const;
/**
* SkipWhites method (below) may also skip new line characters automatically.
*/
@ -312,36 +429,9 @@ public:
ClaimInclusion aInclude = EXCLUDE_LAST);
protected:
// false if we have already read the EOF token.
bool HasInput() const;
// Main parsing function, it doesn't shift the read cursor, just returns the next
// token position.
nsACString::const_char_iterator Parse(Token& aToken) const;
// Is read cursor at the end?
bool IsEnd(const nsACString::const_char_iterator& caret) const;
// Is read cursor on a character that is a word start?
bool IsWordFirst(const char aInput) const;
// Is read cursor on a character that is an in-word letter?
bool IsWord(const char aInput) const;
// Is read cursor on a character that is a valid number?
// TODO - support multiple radix
bool IsNumber(const char aInput) const;
// true iff we have already read the EOF token
bool mPastEof;
// true iff the last Check*() call has returned false, reverts to true on Rollback() call
bool mHasFailed;
// Customizable list of whitespaces
const char* mWhitespaces;
// Additinal custom word characters
const char* mAdditionalWordChars;
// All these point to the original buffer passed to the Tokenizer
// All these point to the original buffer passed to the Tokenizer's constructor
nsACString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is
nsACString::const_char_iterator mRollback; // Position of the previous token start
nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
nsACString::const_char_iterator mEnd; // End of the input position
private:
Tokenizer() = delete;

View File

@ -83,12 +83,14 @@ EXPORTS += [
]
EXPORTS.mozilla += [
'IncrementalTokenizer.h',
'Observer.h',
'StickyTimeDuration.h',
'Tokenizer.h',
]
UNIFIED_SOURCES += [
'IncrementalTokenizer.cpp',
'nsArray.cpp',
'nsArrayEnumerator.cpp',
'nsArrayUtils.cpp',

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Tokenizer.h"
#include "mozilla/IncrementalTokenizer.h"
#include "mozilla/Unused.h"
#include "gtest/gtest.h"
using namespace mozilla;
@ -732,3 +734,401 @@ TEST(Tokenizer, SkipUntil)
EXPECT_TRUE(p.CheckEOF());
}
}
TEST(Tokenizer, Custom)
{
Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2");
Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE);
Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE);
// It's expected to NOT FIND the custom token if it's not on an edge
// between other recognizable tokens.
EXPECT_TRUE(p.CheckWord("aaaaaacustom"));
EXPECT_TRUE(p.CheckChar('-'));
EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1)));
EXPECT_TRUE(p.CheckEOL());
EXPECT_TRUE(p.CheckChar(','));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.CheckChar(','));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.CheckChar(','));
p.EnableCustomToken(c1, false);
EXPECT_TRUE(p.CheckWord("Custom"));
EXPECT_TRUE(p.CheckChar('-'));
EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1)));
EXPECT_TRUE(p.CheckChar(','));
EXPECT_TRUE(p.Check(Tokenizer::Token::Number(0)));
EXPECT_TRUE(p.Check(c2));
EXPECT_TRUE(p.CheckWord("xxxx"));
EXPECT_TRUE(p.CheckChar(','));
EXPECT_TRUE(p.CheckWord("CUSTOM"));
EXPECT_TRUE(p.CheckChar('-'));
EXPECT_TRUE(p.Check(Tokenizer::Token::Number(2)));
EXPECT_TRUE(p.CheckEOF());
}
TEST(Tokenizer, CustomRaw)
{
Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2");
Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE);
Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE);
// In this mode it's expected to find all custom tokens among any kind of input.
p.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
Tokenizer::Token t;
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral("aaaaaa"));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral("\r,"));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral(","));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral(","));
EXPECT_TRUE(p.Check(c1));
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral(",00"));
EXPECT_TRUE(p.Check(c2));
EXPECT_TRUE(p.Next(t));
EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW);
EXPECT_TRUE(t.Fragment().EqualsLiteral("xxxx,CUSTOM-2"));
EXPECT_TRUE(p.CheckEOF());
}
TEST(Tokenizer, Incremental)
{
typedef TokenizerBase::Token Token;
int test = 0;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break;
case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break;
case 4: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 7: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break;
case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
});
NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3");
auto cur = input.BeginReading();
auto end = input.EndReading();
for (; cur < end; ++cur) {
i.FeedInput(nsDependentCSubstring(cur, 1));
}
EXPECT_TRUE(test == 6);
i.FinishInput();
EXPECT_TRUE(test == 8);
}
TEST(Tokenizer, IncrementalRollback)
{
typedef TokenizerBase::Token Token;
int test = 0;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break;
case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2"))));
i.Rollback(); // so that we get the token again
break;
case 4: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break;
case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 7: EXPECT_TRUE(t.Equals(Token::Char(','))); break;
case 8: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break;
case 9: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
});
NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3");
auto cur = input.BeginReading();
auto end = input.EndReading();
for (; cur < end; ++cur) {
i.FeedInput(nsDependentCSubstring(cur, 1));
}
EXPECT_TRUE(test == 7);
i.FinishInput();
EXPECT_TRUE(test == 9);
}
TEST(Tokenizer, IncrementalNeedMoreInput)
{
typedef TokenizerBase::Token Token;
int test = 0;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
Token t2;
switch (++test) {
case 1:
EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("a"))));
break;
case 2:
case 3:
case 4:
case 5:
EXPECT_TRUE(t.Equals(Token::Whitespace()));
if (i.Next(t2)) {
EXPECT_TRUE(test == 5);
EXPECT_TRUE(t2.Equals(Token::Word(NS_LITERAL_CSTRING("bb"))));
} else {
EXPECT_TRUE(test < 5);
i.NeedMoreInput();
}
break;
case 6:
EXPECT_TRUE(t.Equals(Token::Char(',')));
break;
case 7:
EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("c"))));
return NS_ERROR_FAILURE;
default:
EXPECT_TRUE(false);
break;
}
return NS_OK;
});
NS_NAMED_LITERAL_CSTRING(input, "a bb,c");
auto cur = input.BeginReading();
auto end = input.EndReading();
nsresult rv;
for (; cur < end; ++cur) {
rv = i.FeedInput(nsDependentCSubstring(cur, 1));
if (NS_FAILED(rv)) {
break;
}
}
EXPECT_TRUE(rv == NS_OK);
EXPECT_TRUE(test == 6);
rv = i.FinishInput();
EXPECT_TRUE(rv == NS_ERROR_FAILURE);
EXPECT_TRUE(test == 7);
}
TEST(Tokenizer, IncrementalCustom)
{
typedef TokenizerBase::Token Token;
int test = 0;
Token custom;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Equals(custom)); break;
case 2: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("bla")))); break;
case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
}, nullptr, "-");
custom = i.AddCustomToken("some-test", Tokenizer::CASE_SENSITIVE);
i.FeedInput(NS_LITERAL_CSTRING("some-"));
EXPECT_TRUE(test == 0);
i.FeedInput(NS_LITERAL_CSTRING("tes"));
EXPECT_TRUE(test == 0);
i.FeedInput(NS_LITERAL_CSTRING("tbla"));
EXPECT_TRUE(test == 1);
i.FinishInput();
EXPECT_TRUE(test == 3);
}
TEST(Tokenizer, IncrementalCustomRaw)
{
typedef TokenizerBase::Token Token;
int test = 0;
Token custom;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("test1,")); break;
case 2: EXPECT_TRUE(t.Equals(custom)); break;
case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("!,,test3"));
i.Rollback();
i.SetTokenizingMode(Tokenizer::Mode::FULL);
break;
case 4: EXPECT_TRUE(t.Equals(Token::Char('!')));
i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
break;
case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral(",,test3")); break;
case 6: EXPECT_TRUE(t.Equals(custom)); break;
case 7: EXPECT_TRUE(t.Fragment().EqualsLiteral("tes")); break;
case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
});
custom = i.AddCustomToken("test2", Tokenizer::CASE_SENSITIVE);
i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
NS_NAMED_LITERAL_CSTRING(input, "test1,test2!,,test3test2tes");
auto cur = input.BeginReading();
auto end = input.EndReading();
for (; cur < end; ++cur) {
i.FeedInput(nsDependentCSubstring(cur, 1));
}
EXPECT_TRUE(test == 6);
i.FinishInput();
EXPECT_TRUE(test == 8);
}
TEST(Tokenizer, IncrementalCustomRemove)
{
typedef TokenizerBase::Token Token;
int test = 0;
Token custom;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Equals(custom));
i.RemoveCustomToken(custom);
break;
case 2: EXPECT_FALSE(t.Equals(custom)); break;
case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
});
custom = i.AddCustomToken("custom1", Tokenizer::CASE_SENSITIVE);
NS_NAMED_LITERAL_CSTRING(input, "custom1custom1");
i.FeedInput(input);
EXPECT_TRUE(test == 1);
i.FinishInput();
EXPECT_TRUE(test == 3);
}
TEST(Tokenizer, IncrementalBuffering1)
{
typedef TokenizerBase::Token Token;
int test = 0;
Token custom;
nsDependentCSubstring observedFragment;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("012")); break;
case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("3456789")); break;
case 3: EXPECT_TRUE(t.Equals(custom)); break;
case 4: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwe")); break;
case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("rt")); break;
case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
observedFragment.Rebind(t.Fragment().BeginReading(),
t.Fragment().Length());
return NS_OK;
}, nullptr, nullptr, 3);
custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE);
// This externally unused token is added only to check the internal algorithm
// does work correctly as expected when there are two different length tokens.
Unused << i.AddCustomToken("bb", Tokenizer::CASE_SENSITIVE);
i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
i.FeedInput(NS_LITERAL_CSTRING("01234"));
EXPECT_TRUE(test == 1);
EXPECT_TRUE(observedFragment.EqualsLiteral("012"));
i.FeedInput(NS_LITERAL_CSTRING("5"));
EXPECT_TRUE(test == 1);
i.FeedInput(NS_LITERAL_CSTRING("6789aa"));
EXPECT_TRUE(test == 2);
EXPECT_TRUE(observedFragment.EqualsLiteral("3456789"));
i.FeedInput(NS_LITERAL_CSTRING("aqwert"));
EXPECT_TRUE(test == 4);
EXPECT_TRUE(observedFragment.EqualsLiteral("qwe"));
i.FinishInput();
EXPECT_TRUE(test == 6);
}
TEST(Tokenizer, IncrementalBuffering2)
{
typedef TokenizerBase::Token Token;
int test = 0;
Token custom;
IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult
{
switch (++test) {
case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("01")); break;
case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("234567")); break;
case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("89")); break;
case 4: EXPECT_TRUE(t.Equals(custom)); break;
case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwert")); break;
case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break;
}
return NS_OK;
}, nullptr, nullptr, 3);
custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE);
// This externally unused token is added only to check the internal algorithm
// does work correctly as expected when there are two different length tokens.
Unused << i.AddCustomToken("bbbbb", Tokenizer::CASE_SENSITIVE);
i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
i.FeedInput(NS_LITERAL_CSTRING("01234"));
EXPECT_TRUE(test == 0);
i.FeedInput(NS_LITERAL_CSTRING("5"));
EXPECT_TRUE(test == 1);
i.FeedInput(NS_LITERAL_CSTRING("6789aa"));
EXPECT_TRUE(test == 2);
i.FeedInput(NS_LITERAL_CSTRING("aqwert"));
EXPECT_TRUE(test == 4);
i.FinishInput();
EXPECT_TRUE(test == 6);
}