Merge m-c to b2ginbound, a=merge

This commit is contained in:
Wes Kocher 2015-09-04 15:41:50 -07:00
commit 71600a33ad
360 changed files with 6933 additions and 3497 deletions

View File

@ -224,6 +224,28 @@ this.TraversalRules = { // jshint ignore:line
Filters.IGNORE;
}, null, true),
/* A rule for Android's section navigation, lands on landmarks, regions, and
on headings to aid navigation of traditionally structured documents */
Section: new BaseTraversalRule(
[],
function Section_match(aAccessible) {
if (aAccessible.role === Roles.HEADING) {
return Filters.MATCH;
}
let matchedRole = Utils.matchRoles(aAccessible, [
'banner',
'complementary',
'contentinfo',
'main',
'navigation',
'search',
'region'
]);
return matchedRole ? Filters.MATCH : Filters.IGNORE;
}, null, true),
Entry: new BaseTraversalRule(
[Roles.ENTRY,
Roles.PASSWORD_TEXT]),

View File

@ -82,16 +82,20 @@
</li>
<li id="listitem-2-3">JavaScript</li>
</ul>
<h6 id="heading-5">The last (visible) one!</h6>
<img id="image-1" src="http://example.com" alt="">
<img id="image-2" src="../moz.png" alt="stuff">
<div id="image-3" tabindex="0" role="img">Not actually an image</div>
<h4 id="heading-6" aria-hidden="true">Hidden header</h4>
<a id="link-1" href="http://www.mozilla.org">Link</a>
<a id="anchor-1">Words</a>
<a id="link-2" href="http://www.mozilla.org">Link the second</a>
<a id="anchor-2">Sentences</a>
<a id="link-3" href="http://www.example.com">Link the third</a>
<section>
<h6 id="heading-5">The last (visible) one!</h6>
<img id="image-1" src="http://example.com" alt="">
<img id="image-2" src="../moz.png" alt="stuff">
<div id="image-3" tabindex="0" role="img">Not actually an image</div>
</section>
<section>
<h4 id="heading-6" aria-hidden="true">Hidden header</h4>
<a id="link-1" href="http://www.mozilla.org">Link</a>
<a id="anchor-1">Words</a>
<a id="link-2" href="http://www.mozilla.org">Link the second</a>
<a id="anchor-2">Sentences</a>
<a id="link-3" href="http://www.example.com">Link the third</a>
</section>
<hr id="separator-1">
<h6 id="heading-6"></h6>
<table id="table-1">

View File

@ -83,7 +83,9 @@
testTraversalHelper('List',
['Programming Language', 'listitem-2-1', 'listitem-3-1']);
vc.position = null;
testTraversalHelper('Section',
['heading-1', 'heading-2', 'heading-3',
'heading-5', 'link-1', 'statusbar-1']);
SimpleTest.finish();
}

View File

@ -160,6 +160,10 @@ validate.define(Panel, contract({
is: ["string"],
ok: x => isLocalURL(x),
msg: "The `options.url` must be a valid local URI."
},
invertIconForLightTheme: {
is: ["boolean", "undefined"],
msg: "The `options.invertIconForLightTheme` must be a boolean."
}
}));

View File

@ -42,7 +42,8 @@ const Tool = Class({
this.themes = themes;
each(([key, Panel]) => {
const { url, label, tooltip, icon } = validate(Panel.prototype);
const { url, label, tooltip, icon,
invertIconForLightTheme } = validate(Panel.prototype);
const { id } = Panel.prototype;
gDevTools.registerTool({
@ -51,6 +52,7 @@ const Tool = Class({
label: label,
tooltip: tooltip,
icon: icon,
invertIconForLightTheme: invertIconForLightTheme,
isTargetSupported: target => target.isLocalTab,
build: (window, toolbox) => {
const panel = new Panel();

View File

@ -203,6 +203,9 @@ pref("geo.provider.use_mls", false);
pref("geo.cell.scan", true);
pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%");
// base url for the stumbler
pref("geo.stumbler.url", "https://location.services.mozilla.com/v1/geosubmit?key=%MOZILLA_API_KEY%");
// enable geo
pref("geo.enabled", true);

View File

@ -17,12 +17,12 @@ xul|window xul|scrollbar {
}
}
html xul|scrollbar[root="true"] {
xul|scrollbar[root="true"] {
position: relative;
z-index: 2147483647;
}
html xul|scrollbar {
xul|scrollbar {
-moz-appearance: none !important;
background-color: transparent !important;
background-image: none !important;

View File

@ -356,10 +356,6 @@ pref("browser.helperApps.deleteTempFileOnExit", true);
// search engines URL
pref("browser.search.searchEnginesURL", "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
// Tell the search service to load search plugins from the locale JAR
pref("browser.search.loadFromJars", true);
pref("browser.search.jarURIs", "chrome://browser/locale/searchplugins/");
// pointer to the default engine name
pref("browser.search.defaultenginename", "chrome://browser-region/locale/region.properties");

View File

@ -172,9 +172,14 @@ Site.prototype = {
let link = this._querySelector(".newtab-link");
link.setAttribute("title", tooltip);
link.setAttribute("href", url);
this._querySelector(".newtab-title").textContent = title;
this.node.setAttribute("type", this.link.type);
let titleNode = this._querySelector(".newtab-title");
titleNode.textContent = title;
if (this.link.titleBgColor) {
titleNode.style.backgroundColor = this.link.titleBgColor;
}
if (this.link.targetedSite) {
if (this.node.getAttribute("type") != "sponsored") {
this._querySelector(".newtab-sponsored").textContent =

View File

@ -1,29 +1,152 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
test_tab("about:blank");
test_tab("about:license");
// Test for the docshell active state of local and remote browsers.
const kTestPage = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
function promiseNewTabSwitched() {
return new Promise(resolve => {
gBrowser.addEventListener("TabSwitchDone", function onSwitch() {
gBrowser.removeEventListener("TabSwitchDone", onSwitch);
executeSoon(resolve);
});
});
}
function test_tab(url) {
let originalTab = gBrowser.selectedTab;
function getParentTabState(aTab) {
return aTab.linkedBrowser.docShellIsActive;
}
function getChildTabState(aTab) {
return ContentTask.spawn(aTab.linkedBrowser, {}, function* () {
return docShell.isActive;
});
}
function checkState(parentSide, childSide, value, message) {
is(parentSide, value, message + " (parent side)");
is(childSide, value, message + " (child side)");
}
function waitForMs(aMs) {
return new Promise((resolve) => {
setTimeout(done, aMs);
function done() {
resolve(true);
}
});
}
add_task(function *() {
let url = kTestPage;
let originalTab = gBrowser.selectedTab; // test tab
let newTab = gBrowser.addTab(url, {skipAnimation: true});
is(tabIsActive(newTab), false, "newly added " + url + " tab is not active");
is(tabIsActive(originalTab), true, "original tab is active initially");
let parentSide, childSide;
// new tab added but not selected checks
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, false, "newly added " + url + " tab is not active");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, true, "original tab is active initially");
// select the newly added tab and wait for TabSwitchDone event
let tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = newTab;
is(tabIsActive(newTab), true, "newly added " + url + " tab is active after selection");
is(tabIsActive(originalTab), false, "original tab is not active while unselected");
yield tabSwitchedPromise;
if (Services.appinfo.browserTabsRemoteAutostart) {
ok(newTab.linkedBrowser.isRemoteBrowser, "for testing we need a remote tab");
}
// check active state of both tabs
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, true, "newly added " + url + " tab is active after selection");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, false, "original tab is not active while unselected");
// switch back to the original test tab and wait for TabSwitchDone event
tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = originalTab;
is(tabIsActive(newTab), false, "newly added " + url + " tab is not active after switch back");
is(tabIsActive(originalTab), true, "original tab is active again after switch back");
gBrowser.removeTab(newTab);
}
yield tabSwitchedPromise;
function tabIsActive(tab) {
let browser = tab.linkedBrowser;
return browser.docShell.isActive;
}
// check active state of both tabs
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, false, "newly added " + url + " tab is not active after switch back");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, true, "original tab is active again after switch back");
// switch to the new tab and wait for TabSwitchDone event
tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = newTab;
yield tabSwitchedPromise;
// check active state of both tabs
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, true, "newly added " + url + " tab is not active after switch back");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, false, "original tab is active again after switch back");
gBrowser.removeTab(newTab);
});
add_task(function *() {
let url = "about:about";
let originalTab = gBrowser.selectedTab; // test tab
let newTab = gBrowser.addTab(url, {skipAnimation: true});
let parentSide, childSide;
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, false, "newly added " + url + " tab is not active");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, true, "original tab is active initially");
let tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = newTab;
yield tabSwitchedPromise;
if (Services.appinfo.browserTabsRemoteAutostart) {
ok(!newTab.linkedBrowser.isRemoteBrowser, "for testing we need a local tab");
}
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, true, "newly added " + url + " tab is active after selection");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, false, "original tab is not active while unselected");
tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = originalTab;
yield tabSwitchedPromise;
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, false, "newly added " + url + " tab is not active after switch back");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, true, "original tab is active again after switch back");
tabSwitchedPromise = promiseNewTabSwitched();
gBrowser.selectedTab = newTab;
yield tabSwitchedPromise;
parentSide = getParentTabState(newTab);
childSide = yield getChildTabState(newTab);
checkState(parentSide, childSide, true, "newly added " + url + " tab is not active after switch back");
parentSide = getParentTabState(originalTab);
childSide = yield getChildTabState(originalTab);
checkState(parentSide, childSide, false, "original tab is active again after switch back");
gBrowser.removeTab(newTab);
});

View File

@ -50,3 +50,4 @@ support-files =
[browser_newtab_bug1145428.js]
[browser_newtab_bug1178586.js]
[browser_newtab_bug1194895.js]
[browser_newtab_1188015.js]

View File

@ -0,0 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const PRELOAD_PREF = "browser.newtab.preload";
gDirectorySource = "data:application/json," + JSON.stringify({
"directory": [{
url: "http://example1.com/",
enhancedImageURI: "data:image/png;base64,helloWORLD2",
title: "title1",
type: "affiliate",
titleBgColor: "green"
}]
});
function runTests() {
registerCleanupFunction(() => {
Services.prefs.clearUserPref(PRELOAD_PREF);
});
Services.prefs.setBoolPref(PRELOAD_PREF, false);
// Make the page have a directory link
yield setLinks([]);
yield addNewTabPageTab();
let titleNode = getCell(0).node.querySelector(".newtab-title");
is(titleNode.style.backgroundColor, "green", "title bg color is green");
}

View File

@ -219,7 +219,6 @@ DirectoryProvider::GetFiles(const char *aKey, nsISimpleEnumerator* *aResult)
* - extension search plugin locations (prepended below using
* NS_NewUnionEnumerator)
* - user search plugin locations (profile)
* - app search plugin location (shipped engines)
*/
nsresult rv;
@ -245,7 +244,6 @@ DirectoryProvider::GetFiles(const char *aKey, nsISimpleEnumerator* *aResult)
nsCOMArray<nsIFile> baseFiles;
AppendFileKey(NS_APP_USER_SEARCH_DIR, dirSvc, baseFiles);
AppendFileKey(NS_APP_SEARCH_DIR, dirSvc, baseFiles);
nsCOMPtr<nsISimpleEnumerator> baseEnum;
rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles);

View File

@ -140,6 +140,7 @@ BrowserAction.prototype = {
panel.setAttribute("flip", "slide");
node.appendChild(panel);
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");

View File

@ -5,3 +5,5 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
JAR_MANIFESTS += ['jar.mn']
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']

View File

@ -0,0 +1,8 @@
[DEFAULT]
skip-if = os == 'android' || buildapp == 'b2g' || os == 'mac'
[browser_extensions_simple.js]
[browser_ext_browserAction_simple.js]
[browser_ext_tabs_executeScript.js]
[browser_ext_tabs_query.js]
[browser_ext_tabs_update.js]

View File

@ -0,0 +1,36 @@
add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {
"default_popup": "popup.html"
}
},
files: {
"popup.html": `
<!DOCTYPE html>
<html><body>
<script src="popup.js"></script>
</body></html>
`,
"popup.js": function() {
browser.runtime.sendMessage("from-popup");
}
},
background: function() {
browser.runtime.onMessage.addListener(msg => {
browser.test.assertEq(msg, "from-popup", "correct message received");
browser.test.notifyPass("browser_action.simple");
});
},
});
yield extension.startup();
// FIXME: Should really test opening the popup here.
yield extension.awaitFinish("browser_action.simple");
yield extension.unload();
});

View File

@ -0,0 +1,32 @@
add_task(function* () {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
},
background: function() {
browser.runtime.onMessage.addListener((msg, sender) => {
browser.test.assertEq(msg, "script ran", "script ran");
browser.test.notifyPass("executeScript");
});
browser.tabs.executeScript({
file: "script.js"
});
},
files: {
"script.js": function() {
browser.runtime.sendMessage("script ran");
}
}
});
yield extension.startup();
yield extension.awaitFinish("executeScript");
yield extension.unload();
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -0,0 +1,48 @@
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
gBrowser.selectedTab = tab1;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
},
background: function() {
browser.tabs.query({
lastFocusedWindow: true
}, function(tabs) {
browser.test.assertEq(tabs.length, 3, "should have three tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
tabs.shift();
browser.test.assertTrue(tabs[0].active, "tab 0 active");
browser.test.assertFalse(tabs[1].active, "tab 1 inactive");
browser.test.assertFalse(tabs[0].pinned, "tab 0 unpinned");
browser.test.assertFalse(tabs[1].pinned, "tab 1 unpinned");
browser.test.assertEq(tabs[0].url, "about:robots", "tab 0 url correct");
browser.test.assertEq(tabs[1].url, "about:config", "tab 1 url correct");
browser.test.assertEq(tabs[0].status, "complete", "tab 0 status correct");
browser.test.assertEq(tabs[1].status, "complete", "tab 1 status correct");
browser.test.assertEq(tabs[0].title, "Gort! Klaatu barada nikto!", "tab 0 title correct");
browser.test.notifyPass("tabs.query");
});
},
});
yield extension.startup();
yield extension.awaitFinish("tabs.query");
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
yield BrowserTestUtils.removeTab(tab2);
});

View File

@ -0,0 +1,42 @@
add_task(function* () {
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
gBrowser.selectedTab = tab1;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
},
background: function() {
browser.tabs.query({
lastFocusedWindow: true,
}, function(tabs) {
browser.test.assertEq(tabs.length, 3, "should have three tabs");
tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
browser.test.assertEq(tabs[0].url, "about:blank", "first tab blank");
tabs.shift();
browser.test.assertTrue(tabs[0].active, "tab 0 active");
browser.test.assertFalse(tabs[1].active, "tab 1 inactive");
browser.tabs.update(tabs[1].id, {active: true}, function() {
browser.test.sendMessage("check");
});
});
},
});
yield extension.startup();
yield extension.awaitMessage("check");
ok(gBrowser.selectedTab == tab2, "correct tab selected");
yield extension.unload();
yield BrowserTestUtils.removeTab(tab1);
yield BrowserTestUtils.removeTab(tab2);
});

View File

@ -0,0 +1,22 @@
add_task(function* test_simple() {
let extension = ExtensionTestUtils.loadExtension("simple");
info("load complete");
yield extension.startup();
info("startup complete");
yield extension.unload();
info("extension unloaded successfully");
});
add_task(function* test_background() {
let extension = ExtensionTestUtils.loadExtension("background");
info("load complete");
yield extension.startup();
let x = yield extension.awaitMessage("running");
is(x, 1, "got correct value from extension");
info("startup complete");
extension.sendMessage(10, 20);
yield extension.awaitFinish();
info("test complete");
yield extension.unload();
info("extension unloaded successfully");
});

View File

@ -390,7 +390,9 @@ function GetWindowsPasswordsResource(aProfileFolder) {
_rowToLoginInfo(row) {
let loginInfo = {
username: row.getResultByName("username_value"),
password: crypto.decryptData(row.getResultByName("password_value")),
password: crypto.
decryptData(crypto.arrayToString(row.getResultByName("password_value")),
null),
hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
submitURL: null,
httpRealm: null,

View File

@ -9,6 +9,7 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const kLoginsKey = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -17,11 +18,17 @@ Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource:///modules/MSMigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
"resource://gre/modules/ctypes.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
"resource://gre/modules/OSCrypto.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
"resource://gre/modules/WindowsRegistry.jsm");
Cu.importGlobalProperties(["URL"]);
////////////////////////////////////////////////////////////////////////////////
//// Resources
@ -116,6 +123,270 @@ History.prototype = {
}
};
// IE password migrator supporting windows from XP until 7 and IE from 7 until 11
function IE7FormPasswords () {
}
IE7FormPasswords.prototype = {
type: MigrationUtils.resourceTypes.PASSWORDS,
get exists() {
try {
let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
let key = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(nsIWindowsRegKey);
key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
nsIWindowsRegKey.ACCESS_READ);
let count = key.valueCount;
key.close();
return count > 0;
} catch (e) {
return false;
}
},
migrate(aCallback) {
let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
createInstance(Ci.nsISimpleEnumerator);
let uris = []; // the uris of the websites that are going to be migrated
while (historyEnumerator.hasMoreElements()) {
let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
// MSIE stores some types of URLs in its history that we don't handle, like HTMLHelp
// and others. Since we are not going to import the logins that are performed in these URLs
// we can just skip them.
if (["http", "https", "ftp"].indexOf(uri.scheme) == -1) {
continue;
}
uris.push(uri);
}
this._migrateURIs(uris);
aCallback(true);
},
/**
* Migrate the logins that were saved for the uris arguments.
* @param {nsIURI[]} uris - the uris that are going to be migrated.
*/
_migrateURIs(uris) {
this.ctypesHelpers = new MSMigrationUtils.CtypesHelpers();
this._crypto = new OSCrypto();
let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
let key = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(nsIWindowsRegKey);
key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
nsIWindowsRegKey.ACCESS_READ);
let urlsSet = new Set(); // set of the already processed urls.
// number of the successfully decrypted registry values
let successfullyDecryptedValues = 0;
/* The logins are stored in the registry, where the key is a hashed URL and its
* value contains the encrypted details for all logins for that URL.
*
* First iterate through IE history, hashing each URL and looking for a match. If
* found, decrypt the value, using the URL as a salt. Finally add any found logins
* to the Firefox password manager.
*/
for (let uri of uris) {
try {
// remove the query and the ref parts of the URL
let urlObject = new URL(uri.spec);
let url = urlObject.origin + urlObject.pathname;
// if the current url is already processed, it should be skipped
if (urlsSet.has(url)) {
continue;
}
urlsSet.add(url);
// hash value of the current uri
let hashStr = this._crypto.getIELoginHash(url);
if (!key.hasValue(hashStr)) {
continue;
}
let value = key.readBinaryValue(hashStr);
// if no value was found, the uri is skipped
if (value == null) {
continue;
}
let data;
try {
// the url is used as salt to decrypt the registry value
data = this._crypto.decryptData(value, url, true);
} catch (e) {
continue;
}
// extract the login details from the decrypted data
let ieLogins = this._extractDetails(data, uri);
// if at least a credential was found in the current data, successfullyDecryptedValues should
// be incremented by one
if (ieLogins.length) {
successfullyDecryptedValues++;
}
this._addLogins(ieLogins);
} catch (e) {
Cu.reportError("Error while importing logins for " + uri.spec + ": " + e);
}
}
// if the number of the imported values is less than the number of values in the key, it means
// that not all the values were imported and an error should be reported
if (successfullyDecryptedValues < key.valueCount) {
Cu.reportError("We failed to decrypt and import some logins. " +
"This is likely because we didn't find the URLs where these " +
"passwords were submitted in the IE history and which are needed to be used " +
"as keys in the decryption.");
}
key.close();
this._crypto.finalize();
this.ctypesHelpers.finalize();
},
_crypto: null,
/**
* Add the logins to the password manager.
* @param {Object[]} logins - array of the login details.
*/
_addLogins(ieLogins) {
function addLogin(login, existingLogins) {
// Add the login only if it doesn't already exist
// if the login is not already available, it s going to be added or merged with another
// login
if (existingLogins.some(l => login.matches(l, true))) {
return;
}
let isUpdate = false; // the login is just an update for an old one
for (let existingLogin of existingLogins) {
if (login.username == existingLogin.username && login.password != existingLogin.password) {
// if a login with the same username and different password already exists and it's older
// than the current one, that login needs to be updated using the current one details
if (login.timePasswordChanged > existingLogin.timePasswordChanged) {
// Bug 1187190: Password changes should be propagated depending on timestamps.
// the existing login password and timestamps should be updated
let propBag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag);
propBag.setProperty("password", login.password);
propBag.setProperty("timePasswordChanged", login.timePasswordChanged);
Services.logins.modifyLogin(existingLogin, propBag);
// make sure not to add the new login
isUpdate = true;
}
}
}
// if the new login is not an update, add it.
if (!isUpdate) {
Services.logins.addLogin(login);
}
}
for (let ieLogin of ieLogins) {
try {
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
login.init(ieLogin.url, "", null,
ieLogin.username, ieLogin.password, "", "");
login.QueryInterface(Ci.nsILoginMetaInfo);
login.timeCreated = ieLogin.creation;
login.timeLastUsed = ieLogin.creation;
login.timePasswordChanged = ieLogin.creation;
// login.timesUsed is going to set to the default value 1
// Add the login only if there's not an existing entry
let existingLogins = Services.logins.findLogins({}, login.hostname, "", null);
addLogin(login, existingLogins);
} catch (e) {
Cu.reportError(e);
}
}
},
/**
* Extract the details of one or more logins from the raw decrypted data.
* @param {string} data - the decrypted data containing raw information.
* @param {nsURI} uri - the nsURI of page where the login has occur.
* @returns {Object[]} array of objects where each of them contains the username, password, URL,
* and creation time representing all the logins found in the data arguments.
*/
_extractDetails(data, uri) {
// the structure of the header of the IE7 decrypted data for all the logins sharing the same URL
let loginData = new ctypes.StructType("loginData", [
// Bytes 0-3 are not needed and not documented
{"unknown1": ctypes.uint32_t},
// Bytes 4-7 are the header size
{"headerSize": ctypes.uint32_t},
// Bytes 8-11 are the data size
{"dataSize": ctypes.uint32_t},
// Bytes 12-19 are not needed and not documented
{"unknown2": ctypes.uint32_t},
{"unknown3": ctypes.uint32_t},
// Bytes 20-23 are the data count: each username and password is considered as a data
{"dataMax": ctypes.uint32_t},
// Bytes 24-35 are not needed and not documented
{"unknown4": ctypes.uint32_t},
{"unknown5": ctypes.uint32_t},
{"unknown6": ctypes.uint32_t}
]);
// the structure of a IE7 decrypted login item
let loginItem = new ctypes.StructType("loginItem", [
// Bytes 0-3 are the offset of the username
{"usernameOffset": ctypes.uint32_t},
// Bytes 4-11 are the date
{"loDateTime": ctypes.uint32_t},
{"hiDateTime": ctypes.uint32_t},
// Bytes 12-15 are not needed and not documented
{"foo": ctypes.uint32_t},
// Bytes 16-19 are the offset of the password
{"passwordOffset": ctypes.uint32_t},
// Bytes 20-31 are not needed and not documented
{"unknown1": ctypes.uint32_t},
{"unknown2": ctypes.uint32_t},
{"unknown3": ctypes.uint32_t}
]);
let url = uri.prePath;
let results = [];
let arr = this._crypto.stringToArray(data);
// convert data to ctypes.unsigned_char.array(arr.length)
let cdata = ctypes.unsigned_char.array(arr.length)(arr);
// Bytes 0-35 contain the loginData data structure for all the logins sharing the same URL
let currentLoginData = ctypes.cast(cdata, loginData);
let headerSize = currentLoginData.headerSize;
let currentInfoIndex = loginData.size;
// pointer to the current login item
let currentLoginItemPointer = ctypes.cast(cdata.addressOfElement(currentInfoIndex),
loginItem.ptr);
// currentLoginData.dataMax is the data count: each username and password is considered as
// a data. So, the number of logins is the number of data dived by 2
let numLogins = currentLoginData.dataMax / 2;
for (let n = 0; n < numLogins; n++) {
// Bytes 0-31 starting from currentInfoIndex contain the loginItem data structure for the
// current login
let currentLoginItem = currentLoginItemPointer.contents;
let creation = this.ctypesHelpers.
fileTimeToSecondsSinceEpoch(currentLoginItem.hiDateTime,
currentLoginItem.loDateTime) * 1000;
let currentResult = {
creation: creation,
url: url,
};
// The username is UTF-16 and null-terminated.
currentResult.username =
ctypes.cast(cdata.addressOfElement(headerSize + 12 + currentLoginItem.usernameOffset),
ctypes.char16_t.ptr).readString();
// The password is UTF-16 and null-terminated.
currentResult.password =
ctypes.cast(cdata.addressOfElement(headerSize + 12 + currentLoginItem.passwordOffset),
ctypes.char16_t.ptr).readString();
results.push(currentResult);
// move to the next login item
currentLoginItemPointer = currentLoginItemPointer.increment();
}
return results;
},
};
function Settings() {
}
@ -243,6 +514,7 @@ Settings.prototype = {
function IEProfileMigrator()
{
this.wrappedJSObject = this; // export this to be able to use it in the unittest.
}
IEProfileMigrator.prototype = Object.create(MigratorPrototype);
@ -252,6 +524,7 @@ IEProfileMigrator.prototype.getResources = function IE_getResources() {
MSMigrationUtils.getBookmarksMigrator()
, new History()
, MSMigrationUtils.getCookiesMigrator()
, new IE7FormPasswords()
, new Settings()
];
return [r for each (r in resources) if (r.exists)];

View File

@ -30,48 +30,45 @@ Cu.importGlobalProperties(["File"]);
////////////////////////////////////////////////////////////////////////////////
//// Helpers.
let CtypesHelpers = {
_structs: {},
_functions: {},
_libs: {},
function CtypesHelpers() {
this._structs = {};
this._functions = {};
this._libs = {};
/**
* Must be invoked once before first use of any of the provided helpers.
*/
initialize() {
const WORD = ctypes.uint16_t;
const DWORD = ctypes.uint32_t;
const BOOL = ctypes.int;
const WORD = ctypes.uint16_t;
const DWORD = ctypes.uint32_t;
const BOOL = ctypes.int;
this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
{wYear: WORD},
{wMonth: WORD},
{wDayOfWeek: WORD},
{wDay: WORD},
{wHour: WORD},
{wMinute: WORD},
{wSecond: WORD},
{wMilliseconds: WORD}
]);
this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
{wYear: WORD},
{wMonth: WORD},
{wDayOfWeek: WORD},
{wDay: WORD},
{wHour: WORD},
{wMinute: WORD},
{wSecond: WORD},
{wMilliseconds: WORD}
]);
this._structs.FILETIME = new ctypes.StructType('FILETIME', [
{dwLowDateTime: DWORD},
{dwHighDateTime: DWORD}
]);
this._structs.FILETIME = new ctypes.StructType('FILETIME', [
{dwLowDateTime: DWORD},
{dwHighDateTime: DWORD}
]);
try {
this._libs.kernel32 = ctypes.open("Kernel32");
this._functions.FileTimeToSystemTime =
this._libs.kernel32.declare("FileTimeToSystemTime",
ctypes.default_abi,
BOOL,
this._structs.FILETIME.ptr,
this._structs.SYSTEMTIME.ptr);
} catch (ex) {
this.finalize();
}
},
try {
this._libs.kernel32 = ctypes.open("Kernel32");
this._functions.FileTimeToSystemTime =
this._libs.kernel32.declare("FileTimeToSystemTime",
ctypes.default_abi,
BOOL,
this._structs.FILETIME.ptr,
this._structs.SYSTEMTIME.ptr);
} catch (ex) {
this.finalize();
}
}
CtypesHelpers.prototype = {
/**
* Must be invoked once after last use of any of the provided helpers.
*/
@ -433,7 +430,7 @@ Cookies.prototype = {
},
migrate(aCallback) {
CtypesHelpers.initialize();
this.ctypesHelpers = new CtypesHelpers();
let cookiesGenerator = (function genCookie() {
let success = false;
@ -460,7 +457,7 @@ Cookies.prototype = {
}
}
CtypesHelpers.finalize();
this.ctypesHelpers.finalize();
aCallback(success);
}).apply(this);
@ -536,8 +533,8 @@ Cookies.prototype = {
host = "." + host;
}
let expireTime = CtypesHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
Number(expireTimeLo));
let expireTime = this.ctypesHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
Number(expireTimeLo));
Services.cookies.add(host,
path,
name,
@ -554,6 +551,7 @@ Cookies.prototype = {
let MSMigrationUtils = {
MIGRATION_TYPE_IE: 1,
MIGRATION_TYPE_EDGE: 2,
CtypesHelpers: CtypesHelpers,
getBookmarksMigrator(migrationType = this.MIGRATION_TYPE_IE) {
return new Bookmarks(migrationType);
},

View File

@ -84,7 +84,7 @@ function promiseSetPassword(login) {
SET password_value = :password_value
WHERE rowid = :rowid
`);
let passwordValue = crypto.encryptData(login.password);
let passwordValue = crypto.stringToArray(crypto.encryptData(login.password));
stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
stmt.params.rowid = login.id;

View File

@ -0,0 +1,386 @@
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
"resource://gre/modules/WindowsRegistry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
"resource://gre/modules/OSCrypto.jsm");
const CRYPT_PROTECT_UI_FORBIDDEN = 1;
const LOGINS_KEY = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
const EXTENSION = "-backup";
const TESTED_WEBSITES = {
twitter: {
uri: makeURI("https://twitter.com"),
hash: "A89D42BC6406E27265B1AD0782B6F376375764A301",
data: [12, 0, 0, 0, 56, 0, 0, 0, 38, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 8, 0, 0, 0, 18, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 9, 0, 0, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 0, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, 0, 0, 0],
logins: [
{
username: "abcdefgh",
password: "123456789",
hostname: "https://twitter.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439325854000,
timeLastUsed: 1439325854000,
timePasswordChanged: 1439325854000,
timesUsed: 1,
},
],
},
facebook: {
uri: makeURI("https://www.facebook.com/"),
hash: "EF44D3E034009CB0FD1B1D81A1FF3F3335213BD796",
data: [12, 0, 0, 0, 152, 0, 0, 0, 160, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 182, 125, 18, 121, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 88, 182, 125, 18, 121, 212, 208, 1, 9, 0, 0, 0, 40, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0, 0, 0, 60, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0, 0, 0, 80, 0, 0, 0, 45, 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 100, 0, 0, 0, 45, 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 120, 0, 0, 0, 28, 10, 193, 80, 121, 212, 208, 1, 9, 0, 0, 0, 140, 0, 0, 0, 28, 10, 193, 80, 121, 212, 208, 1, 9, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 48, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 48, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 49, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 49, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 50, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 50, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 51, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 51, 0, 0, 0],
logins: [
{
username: "username0",
password: "password0",
hostname: "https://www.facebook.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439326966000,
timeLastUsed: 1439326966000,
timePasswordChanged: 1439326966000,
timesUsed: 1,
},
{
username: "username1",
password: "password1",
hostname: "https://www.facebook.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439326997000,
timeLastUsed: 1439326997000,
timePasswordChanged: 1439326997000,
timesUsed: 1,
},
{
username: "username2",
password: "password2",
hostname: "https://www.facebook.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439327040000,
timeLastUsed: 1439327040000,
timePasswordChanged: 1439327040000,
timesUsed: 1,
},
{
username: "username3",
password: "password3",
hostname: "https://www.facebook.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439327070000,
timeLastUsed: 1439327070000,
timePasswordChanged: 1439327070000,
timesUsed: 1,
},
],
},
live: {
uri: makeURI("https://login.live.com/"),
hash: "7B506F2D6B81D939A8E0456F036EE8970856FF705E",
data: [12, 0, 0, 0, 56, 0, 0, 0, 44, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 17, 219, 140, 148, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 212, 17, 219, 140, 148, 212, 208, 1, 11, 0, 0, 0, 114, 0, 105, 0, 97, 0, 100, 0, 104, 0, 49, 6, 74, 6, 39, 6, 54, 6, 0, 0, 39, 6, 66, 6, 49, 6, 35, 6, 80, 0, 192, 0, 223, 0, 119, 0, 246, 0, 114, 0, 100, 0, 0, 0],
logins: [
{
username: "riadhرياض",
password: "اقرأPÀßwörd",
hostname: "https://login.live.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439338767000,
timeLastUsed: 1439338767000,
timePasswordChanged: 1439338767000,
timesUsed: 1,
},
],
},
reddit: {
uri: makeURI("http://www.reddit.com/"),
hash: "B644028D1C109A91EC2C4B9D1F145E55A1FAE42065",
data: [12, 0, 0, 0, 152, 0, 0, 0, 212, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 8, 234, 114, 153, 212, 208, 1, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 97, 93, 131, 116, 153, 212, 208, 1, 3, 0, 0, 0, 14, 0, 0, 0, 97, 93, 131, 116, 153, 212, 208, 1, 16, 0, 0, 0, 48, 0, 0, 0, 88, 150, 78, 174, 153, 212, 208, 1, 4, 0, 0, 0, 58, 0, 0, 0, 88, 150, 78, 174, 153, 212, 208, 1, 29, 0, 0, 0, 118, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208, 1, 15, 0, 0, 0, 150, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208, 1, 30, 0, 0, 0, 97, 0, 0, 0, 0, 0, 252, 140, 173, 138, 146, 48, 0, 0, 66, 0, 105, 0, 116, 0, 116, 0, 101, 0, 32, 0, 98, 0, 101, 0, 115, 0, 116, 0, 228, 0, 116, 0, 105, 0, 103, 0, 101, 0, 110, 0, 0, 0, 205, 145, 110, 127, 198, 91, 1, 120, 0, 0, 31, 4, 48, 4, 64, 4, 62, 4, 59, 4, 76, 4, 32, 0, 67, 4, 65, 4, 63, 4, 53, 4, 72, 4, 61, 4, 62, 4, 32, 0, 65, 4, 49, 4, 64, 4, 62, 4, 72, 4, 53, 4, 61, 4, 46, 0, 32, 0, 18, 4, 62, 4, 57, 4, 66, 4, 56, 4, 0, 0, 40, 6, 51, 6, 69, 6, 32, 0, 39, 6, 68, 6, 68, 6, 71, 6, 32, 0, 39, 6, 68, 6, 49, 6, 45, 6, 69, 6, 70, 6, 0, 0, 118, 0, 101, 0, 117, 0, 105, 0, 108, 0, 108, 0, 101, 0, 122, 0, 32, 0, 108, 0, 101, 0, 32, 0, 118, 0, 233, 0, 114, 0, 105, 0, 102, 0, 105, 0, 101, 0, 114, 0, 32, 0, 224, 0, 32, 0, 110, 0, 111, 0, 117, 0, 118, 0, 101, 0, 97, 0, 117, 0, 0, 0],
logins: [
{
username: "購読を",
password: "Bitte bestätigen",
hostname: "http://www.reddit.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439340874000,
timeLastUsed: 1439340874000,
timePasswordChanged: 1439340874000,
timesUsed: 1,
},
{
username: "重置密码",
password: "Пароль успешно сброшен. Войти",
hostname: "http://www.reddit.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439340971000,
timeLastUsed: 1439340971000,
timePasswordChanged: 1439340971000,
timesUsed: 1,
},
{
username: "بسم الله الرحمن",
password: "veuillez le vérifier à nouveau",
hostname: "http://www.reddit.com",
formSubmitURL: "",
httpRealm: null,
usernameField: "",
passwordField: "",
timeCreated: 1439341166000,
timeLastUsed: 1439341166000,
timePasswordChanged: 1439341166000,
timesUsed: 1,
},
],
},
};
const TESTED_URLS = [
"http://a.foo.com",
"http://b.foo.com",
"http://c.foo.com",
"http://www.test.net",
"http://www.test.net/home",
"http://www.test.net/index",
"https://a.bar.com",
"https://b.bar.com",
"https://c.bar.com",
];
let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
let Storage2Key;
/*
* If the key value exists, it's going to be backed up and replaced, so the value could be restored.
* Otherwise a new value is going to be created.
*/
function backupAndStore(key, name, value) {
if (key.hasValue(name)) {
// backup the the current value
let type = key.getValueType(name);
// create a new value using use the current value name followed by EXTENSION as its new name
switch (type) {
case nsIWindowsRegKey.TYPE_STRING:
key.writeStringValue(name + EXTENSION, key.readStringValue(name));
break;
case nsIWindowsRegKey.TYPE_BINARY:
key.writeBinaryValue(name + EXTENSION, key.readBinaryValue(name));
break;
case nsIWindowsRegKey.TYPE_INT:
key.writeIntValue(name + EXTENSION, key.readIntValue(name));
break;
case nsIWindowsRegKey.TYPE_INT64:
key.writeInt64Value(name + EXTENSION, key.readInt64Value(name));
break;
}
}
key.writeBinaryValue(name, value);
}
// Remove all values where their names are members of the names array from the key of registry
function removeAllValues(key, names) {
for (let name of names) {
key.removeValue(name);
}
}
// Restore all the backed up values
function restore(key) {
let count = key.valueCount;
let names = []; // the names of the key values
for (let i = 0; i < count; ++i) {
names.push(key.getValueName(i));
}
for (let name of names) {
// backed up values have EXTENSION at the end of their names
if (name.lastIndexOf(EXTENSION) == name.length - EXTENSION.length) {
let valueName = name.substr(0, name.length - EXTENSION.length);
let type = key.getValueType(name);
// create a new value using the name before the backup and removed the backed up one
switch (type) {
case nsIWindowsRegKey.TYPE_STRING:
key.writeStringValue(valueName, key.readStringValue(name));
key.removeValue(name);
break;
case nsIWindowsRegKey.TYPE_BINARY:
key.writeBinaryValue(valueName, key.readBinaryValue(name));
key.removeValue(name);
break;
case nsIWindowsRegKey.TYPE_INT:
key.writeIntValue(valueName, key.readIntValue(name));
key.removeValue(name);
break;
case nsIWindowsRegKey.TYPE_INT64:
key.writeInt64Value(valueName, key.readInt64Value(name));
key.removeValue(name);
break;
}
}
}
}
function checkLoginsAreEqual(passwordManagerLogin, IELogin, id) {
passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
for (let attribute in IELogin) {
Assert.equal(passwordManagerLogin[attribute], IELogin[attribute],
"The two logins ID " + id + " have the same " + attribute);
}
}
function createRegistryPath(path) {
let loginPath = path.split("\\");
let parentKey = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(nsIWindowsRegKey);
let currentPath =[];
for (let currentKey of loginPath) {
parentKey.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, currentPath.join("\\"),
nsIWindowsRegKey.ACCESS_ALL);
if (!parentKey.hasChild(currentKey)) {
parentKey.createChild(currentKey, 0);
}
currentPath.push(currentKey);
parentKey.close();
}
}
function getFirstResourceOfType(type) {
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=ie"]
.createInstance(Ci.nsISupports)
.wrappedJSObject;
let migrators = migrator.getResources();
for (let m of migrators) {
if (m.type == type) {
return m;
}
}
throw new Error("failed to find the " + type + " migrator");
}
function makeURI(aURL) {
return Services.io.newURI(aURL, null, null);
}
add_task(function* setup() {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
Assert.throws(() => getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS),
"The migrator doesn't exist for win8+");
return;
}
// create the path to Storage2 in the registry if it doest exist.
createRegistryPath(LOGINS_KEY);
Storage2Key = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(nsIWindowsRegKey);
Storage2Key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, LOGINS_KEY,
nsIWindowsRegKey.ACCESS_ALL);
// create a dummy value otherwise the migrator doesn't exist
if (!Storage2Key.hasValue("dummy")) {
Storage2Key.writeBinaryValue("dummy", "dummy");
}
});
add_task(function* test_passwordsNotAvailable() {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
return;
}
let migrator = getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS);
Assert.ok(migrator.exists, "The migrator has to exist");
let logins = Services.logins.getAllLogins({});
Assert.equal(logins.length, 0, "There are no logins at the beginning of the test");
let uris = []; // the uris of the migrated logins
for (let url of TESTED_URLS) {
uris.push(makeURI(url));
// in this test, there is no IE login data in the registry, so after the migration, the number
// of logins in the store should be 0
migrator._migrateURIs(uris);
logins = Services.logins.getAllLogins({});
Assert.equal(logins.length, 0,
"There are no logins after doing the migration without adding values to the registry");
}
});
add_task(function* test_passwordsAvailable() {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
return;
}
let crypto = new OSCrypto();
let hashes = []; // the hashes of all migrator websites, this is going to be used for the clean up
do_register_cleanup(() => {
Services.logins.removeAllLogins();
logins = Services.logins.getAllLogins({});
Assert.equal(logins.length, 0, "There are no logins after the cleanup");
//remove all the values created in this test from the registry
removeAllValues(Storage2Key, hashes);
// restore all backed up values
restore(Storage2Key);
// clean the dummy value
if (Storage2Key.hasValue("dummy")) {
Storage2Key.removeValue("dummy");
}
Storage2Key.close();
crypto.finalize();
});
let migrator = getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS);
Assert.ok(migrator.exists, "The migrator has to exist");
let logins = Services.logins.getAllLogins({});
Assert.equal(logins.length, 0, "There are no logins at the beginning of the test");
let uris = []; // the uris of the migrated logins
let loginCount = 0;
for (let current in TESTED_WEBSITES) {
let website = TESTED_WEBSITES[current];
// backup the current the registry value if it exists and replace the existing value/create a
// new value with the encrypted data
backupAndStore(Storage2Key, website.hash,
crypto.encryptData(crypto.arrayToString(website.data),
website.uri.spec, true));
Assert.ok(migrator.exists, "The migrator has to exist");
uris.push(website.uri);
hashes.push(website.hash);
migrator._migrateURIs(uris);
logins = Services.logins.getAllLogins({});
// check that the number of logins in the password manager has increased as expected which means
// that all the values for the current website were imported
loginCount += website.logins.length;
Assert.equal(logins.length, loginCount,
"The number of logins has increased after the migration");
let startIndex = loginCount - website.logins.length;
// compares the imported password manager logins with their expected logins
for (let i = 0; i < website.logins.length; i++) {
checkLoginsAreEqual(logins[startIndex + i], website.logins[i],
" " + current + " - " + i + " ");
}
}
});

View File

@ -17,5 +17,7 @@ skip-if = os != "win"
skip-if = os != "win"
[test_IE_cookies.js]
skip-if = os != "win"
[test_IE7_passwords.js]
skip-if = os != "win"
[test_Safari_bookmarks.js]
skip-if = os != "mac"

View File

@ -8,11 +8,20 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
const policy = Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject
.policy;
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
XPCOMUtils.defineLazyGetter(this, "gPolicy", () => {
try {
return Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject
.policy;
} catch (e) {
return undefined;
}
});
XPCOMUtils.defineLazyGetter(this, "reporter", () => {
return Cc["@mozilla.org/datareporting/service;1"]
@ -44,14 +53,25 @@ MozSelfSupportInterface.prototype = {
},
get healthReportDataSubmissionEnabled() {
return policy.healthReportUploadEnabled;
if (gPolicy) {
return gPolicy.healthReportUploadEnabled;
}
// The datareporting service is unavailable or disabled.
return Preferences.get(PREF_FHR_UPLOAD_ENABLED, false);
},
set healthReportDataSubmissionEnabled(enabled) {
let reason = "Self-support interface sent " +
(enabled ? "opt-in" : "opt-out") +
" command.";
policy.recordHealthReportUploadEnabled(enabled, reason);
if (gPolicy) {
let reason = "Self-support interface sent " +
(enabled ? "opt-in" : "opt-out") +
" command.";
gPolicy.recordHealthReportUploadEnabled(enabled, reason);
return;
}
// The datareporting service is unavailable or disabled.
Preferences.set(PREF_FHR_UPLOAD_ENABLED, enabled);
},
getHealthReportPayload: function () {

View File

@ -98,7 +98,9 @@ function testParseCssProperty(doc, parser) {
"url(red.svg#blue)\"><span>",
"blur(1px) drop-shadow(0 0 0 ",
{name: "blue", value: "#00F"},
") url(\"red.svg#blue\")</span></span>"])
") url(\"red.svg#blue\")</span></span>"]),
makeColorTest("color", "currentColor", ["currentColor"]),
];
let target = doc.querySelector("div");

View File

@ -197,5 +197,13 @@ add_task(function*() {
yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
// Clearing items
yield gWindow.clear();
yield gUI.once("store-objects-cleared");
is($$("#value .table-widget-cell").length, 0,
"Table should be cleared");
yield finishTests();
});

View File

@ -146,6 +146,7 @@ StorageUI.prototype = {
if (response.hasOwnProperty(type) && response[type].indexOf(host) > -1) {
this.table.clear();
this.hideSidebar();
this.emit("store-objects-cleared");
}
},

View File

@ -571,6 +571,10 @@ getUserMedia.noScreen.label = No Screen
getUserMedia.noWindow.label = No Window
getUserMedia.noAudio.label = No Audio
getUserMedia.shareEntireScreen.label = Entire screen
# LOCALIZATION NOTE (getUserMedia.shareMonitor.label):
# %S is screen number (digits 1, 2, etc)
# Example: Screen 1, Screen 2,..
getUserMedia.shareMonitor.label = Screen %S
# LOCALIZATION NOTE (getUserMedia.shareApplicationWindowCount.label):
# Semicolon-separated list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -151,6 +151,7 @@
locale/browser/syncQuota.dtd (%chrome/browser/syncQuota.dtd)
locale/browser/syncQuota.properties (%chrome/browser/syncQuota.properties)
#endif
% resource search-plugins chrome://browser/locale/searchplugins/
locale/browser/searchplugins/list.txt (.deps/generated_@AB_CD@/list.txt)
locale/browser/searchplugins/ (.deps/generated_@AB_CD@/*.xml)
% locale browser-region @AB_CD@ %locale/browser-region/

View File

@ -432,12 +432,18 @@ function prompt(aBrowser, aRequest) {
menupopup.appendChild(chromeDoc.createElement("menuseparator"));
// Build the list of 'devices'.
let monitorIndex = 1;
for (let i = 0; i < devices.length; ++i) {
let name;
// Screen has a special treatment because we currently only support
// sharing the primary screen and want to display a localized string.
// Building screen list from available screens.
if (type == "screen") {
name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
if (devices[i].name == "Primary Monitor") {
name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
} else {
name = stringBundle.getFormattedString("getUserMedia.shareMonitor.label",
[monitorIndex]);
++monitorIndex;
}
}
else {
name = devices[i].name;

View File

@ -708,33 +708,6 @@ AST_MATCHER(MemberExpr, isAddRefOrRelease) {
/// This matcher will select classes which are refcounted.
AST_MATCHER(QualType, isRefCounted) { return isClassRefCounted(Node); }
#if CLANG_VERSION_FULL < 304
/// The 'equalsBoundeNode' matcher was added in clang 3.4.
/// Since infra runs clang 3.3, we polyfill it here.
AST_POLYMORPHIC_MATCHER_P(equalsBoundNode, std::string, ID) {
BoundNodesTree bindings = Builder->build();
bool haveMatchingResult = false;
struct Visitor : public BoundNodesTree::Visitor {
const NodeType &Node;
std::string ID;
bool &haveMatchingResult;
Visitor(const NodeType &Node, const std::string &ID,
bool &haveMatchingResult)
: Node(Node), ID(ID), haveMatchingResult(haveMatchingResult) {}
void visitMatch(const BoundNodes &BoundNodesView) override {
if (BoundNodesView.getNodeAs<NodeType>(ID) == &Node) {
haveMatchingResult = true;
}
}
};
Visitor visitor(Node, ID, haveMatchingResult);
bindings.visitMatches(&visitor);
return haveMatchingResult;
}
#endif
AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
}

View File

@ -46,6 +46,9 @@ gyp_vars = {
'moz_webrtc_omx': 0,
'moz_webrtc_mediacodec': 0,
# Turn off multi monitor screen share
'multi_monitor_screenshare%' : 0,
# (for vp8) chromium sets to 0 also
'use_temporal_layers': 0,

View File

@ -265,7 +265,9 @@ NS_IMETHODIMP
nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult)
{
nsresult rv;
NS_ASSERTION(aChromeURI, "null url!");
if (NS_WARN_IF(!aChromeURI)) {
return NS_ERROR_INVALID_ARG;
}
if (mOverrideTable.Get(aChromeURI, aResult))
return NS_OK;

View File

@ -0,0 +1,50 @@
/* -*- 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 "AbstractTimelineMarker.h"
#include "mozilla/TimeStamp.h"
namespace mozilla {
AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType)
: mName(aName)
, mTracingType(aTracingType)
{
MOZ_COUNT_CTOR(AbstractTimelineMarker);
SetCurrentTime();
}
AbstractTimelineMarker::AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType)
: mName(aName)
, mTracingType(aTracingType)
{
MOZ_COUNT_CTOR(AbstractTimelineMarker);
SetCustomTime(aTime);
}
AbstractTimelineMarker::~AbstractTimelineMarker()
{
MOZ_COUNT_DTOR(AbstractTimelineMarker);
}
void
AbstractTimelineMarker::SetCurrentTime()
{
TimeStamp now = TimeStamp::Now();
SetCustomTime(now);
}
void
AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime)
{
bool isInconsistent = false;
mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
}
} // namespace mozilla

View File

@ -0,0 +1,57 @@
/* -*- 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_AbstractTimelineMarker_h_
#define mozilla_AbstractTimelineMarker_h_
#include "TimelineMarkerEnums.h" // for MarkerTracingType
#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
struct JSContext;
namespace mozilla {
class TimeStamp;
namespace dom {
struct ProfileTimelineMarker;
}
class AbstractTimelineMarker
{
private:
AbstractTimelineMarker() = delete;
AbstractTimelineMarker(const AbstractTimelineMarker& aOther) = delete;
void operator=(const AbstractTimelineMarker& aOther) = delete;
public:
AbstractTimelineMarker(const char* aName,
MarkerTracingType aTracingType);
AbstractTimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType);
virtual ~AbstractTimelineMarker();
virtual bool Equals(const AbstractTimelineMarker& aOther) = 0;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
const char* GetName() const { return mName; }
DOMHighResTimeStamp GetTime() const { return mTime; }
MarkerTracingType GetTracingType() const { return mTracingType; }
private:
const char* mName;
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
};
} // namespace mozilla
#endif /* mozilla_AbstractTimelineMarker_h_ */

View File

@ -27,7 +27,7 @@ public:
}
}
virtual bool Equals(const TimelineMarker& aOther) override
virtual bool Equals(const AbstractTimelineMarker& aOther) override
{
if (!TimelineMarker::Equals(aOther)) {
return false;

View File

@ -11,11 +11,8 @@ namespace mozilla {
TimelineMarker::TimelineMarker(const char* aName,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest)
: mName(aName)
, mTracingType(aTracingType)
: AbstractTimelineMarker(aName, aTracingType)
{
MOZ_COUNT_CTOR(TimelineMarker);
SetCurrentTime();
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
@ -23,31 +20,46 @@ TimelineMarker::TimelineMarker(const char* aName,
const TimeStamp& aTime,
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest)
: mName(aName)
, mTracingType(aTracingType)
: AbstractTimelineMarker(aName, aTime, aTracingType)
{
MOZ_COUNT_CTOR(TimelineMarker);
SetCustomTime(aTime);
CaptureStackIfNecessary(aTracingType, aStackRequest);
}
TimelineMarker::~TimelineMarker()
bool
TimelineMarker::Equals(const AbstractTimelineMarker& aOther)
{
MOZ_COUNT_DTOR(TimelineMarker);
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
return strcmp(GetName(), aOther.GetName()) == 0;
}
void
TimelineMarker::SetCurrentTime()
TimelineMarker::AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker)
{
TimeStamp now = TimeStamp::Now();
SetCustomTime(now);
// Nothing to do here for plain markers.
}
JSObject*
TimelineMarker::GetStack()
{
if (mStackTrace.initialized()) {
return mStackTrace;
}
return nullptr;
}
void
TimelineMarker::SetCustomTime(const TimeStamp& aTime)
TimelineMarker::CaptureStack()
{
bool isInconsistent = false;
mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
if (ctx) {
JS::RootedObject stack(ctx);
if (JS::CaptureCurrentStack(ctx, &stack)) {
mStackTrace.init(ctx, stack.get());
} else {
JS_ClearPendingException(ctx);
}
}
}
void

View File

@ -7,21 +7,15 @@
#ifndef mozilla_TimelineMarker_h_
#define mozilla_TimelineMarker_h_
#include "nsString.h"
#include "nsContentUtils.h"
#include "TimelineMarkerEnums.h"
class nsDocShell;
#include "AbstractTimelineMarker.h"
namespace mozilla {
namespace dom {
struct ProfileTimelineMarker;
}
// Objects of this type can be added to the timeline if there is an interested
// consumer. The class can also be subclassed to let a given marker creator
// provide custom details.
class TimelineMarker
class TimelineMarker : public AbstractTimelineMarker
{
public:
TimelineMarker(const char* aName,
@ -33,61 +27,21 @@ public:
MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
virtual ~TimelineMarker();
virtual bool Equals(const AbstractTimelineMarker& aOther) override;
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
// Check whether two markers should be considered the same, for the purpose
// of pairing start and end markers. Normally this definition suffices.
virtual bool Equals(const TimelineMarker& aOther)
{
return strcmp(mName, aOther.mName) == 0;
}
// Add details specific to this marker type to aMarker. The standard elements
// have already been set. This method is called on both the starting and
// ending markers of a pair. Ordinarily the ending marker doesn't need to do
// anything here.
virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker)
{}
const char* GetName() const { return mName; }
DOMHighResTimeStamp GetTime() const { return mTime; }
MarkerTracingType GetTracingType() const { return mTracingType; }
JSObject* GetStack()
{
if (mStackTrace.initialized()) {
return mStackTrace;
}
return nullptr;
}
JSObject* GetStack();
protected:
void CaptureStack()
{
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
if (ctx) {
JS::RootedObject stack(ctx);
if (JS::CaptureCurrentStack(ctx, &stack)) {
mStackTrace.init(ctx, stack.get());
} else {
JS_ClearPendingException(ctx);
}
}
}
void CaptureStack();
private:
const char* mName;
DOMHighResTimeStamp mTime;
MarkerTracingType mTracingType;
// While normally it is not a good idea to make a persistent root,
// in this case changing nsDocShell to participate in cycle
// collection was deemed too invasive, and the markers are only held
// here temporarily to boot.
JS::PersistentRooted<JSObject*> mStackTrace;
void SetCurrentTime();
void SetCustomTime(const TimeStamp& aTime);
void CaptureStackIfNecessary(MarkerTracingType aTracingType,
MarkerStackRequest aStackRequest);
};

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla += [
'AbstractTimelineMarker.h',
'AutoGlobalTimelineMarker.h',
'AutoTimelineMarker.h',
'ConsoleTimelineMarker.h',
@ -20,6 +21,7 @@ EXPORTS.mozilla += [
]
UNIFIED_SOURCES += [
'AbstractTimelineMarker.cpp',
'AutoGlobalTimelineMarker.cpp',
'AutoTimelineMarker.cpp',
'ObservedDocShell.cpp',

View File

@ -5,6 +5,8 @@
running on the compositor or not</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css"
@ -13,7 +15,7 @@
@keyframes anim {
to { transform: translate(100px) }
}
.target {
div {
/* Element needs geometry to be eligible for layerization */
width: 100px;
height: 100px;
@ -24,42 +26,47 @@
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045994"
target="_blank">Mozilla Bug 1045994</a>
<div class="target"></div>
<script>
'use strict';
/** Test for bug 1045994 - Add a chrome-only property to inspect if an
animation is running on the compositor or not **/
SimpleTest.waitForExplicitFinish();
var div = document.querySelector('div.target');
function addDiv(attrs) {
var div = document.createElement('div');
if (attrs) {
for (var attrName in attrs) {
div.setAttribute(attrName, attrs[attrName]);
}
}
document.body.appendChild(div);
return div;
}
const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote &&
SpecialPowers.getBoolPref(OMTAPrefKey);
// FIXME: When we implement Element.animate, use that here instead of CSS
// so that we remove any dependency on the CSS mapping.
div.style.animation = 'anim 100s';
var animation = div.getAnimations()[0];
add_task(function* play_and_pause_from_style() {
// FIXME: When we implement Element.animate, use that here instead of CSS
// so that we remove any dependency on the CSS mapping.
var div = addDiv({ style: 'animation: anim 100s' });
var animation = div.getAnimations()[0];
yield animation.ready;
animation.ready.then(function() {
is(animation.isRunningOnCompositor, omtaEnabled,
'Animation reports that it is running on the compositor'
+ ' during playback');
div.style.animationPlayState = 'paused';
window.getComputedStyle(div).animationPlayState;
// FIXME: When we implement deferred pausing (bug 1109390), we should wait
// on animation.ready here.
window.requestAnimationFrame(function() {
is(animation.isRunningOnCompositor, false,
'Animation reports that it is NOT running on the compositor'
+ ' when paused');
SimpleTest.finish();
});
yield animation.ready;
is(animation.isRunningOnCompositor, false,
'Animation reports that it is NOT running on the compositor'
+ ' when paused');
div.parentNode.removeChild(div);
});
</script>
</body>

View File

@ -8505,7 +8505,7 @@ class CGMemberJITInfo(CGThing):
"""
{
{ ${opName} },
prototypes::id::${name},
{ prototypes::id::${name} },
PrototypeTraits<prototypes::id::${name}>::Depth,
JSJitInfo::${opType},
JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
@ -8886,7 +8886,7 @@ class CGStaticMethodJitinfo(CGGeneric):
"\n"
"static const JSJitInfo %s_methodinfo = {\n"
" { (JSJitGetterOp)%s },\n"
" prototypes::id::_ID_Count, 0, JSJitInfo::StaticMethod,\n"
" { prototypes::id::_ID_Count }, 0, JSJitInfo::StaticMethod,\n"
" JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
" false, false, 0\n"
"};\n" %

View File

@ -758,7 +758,7 @@ Event::GetEventPopupControlState(WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent)
abuse = openControlled;
}
break;
case NS_XUL_COMMAND:
case eXULCommand:
abuse = openControlled;
break;
default:

View File

@ -688,7 +688,7 @@ NON_IDL_EVENT(compositionend,
EventNameType_XUL,
eCompositionEventClass)
NON_IDL_EVENT(command,
NS_XUL_COMMAND,
eXULCommand,
EventNameType_XUL,
eInputEventClass)
NON_IDL_EVENT(close,
@ -696,27 +696,27 @@ NON_IDL_EVENT(close,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(popupshowing,
NS_XUL_POPUP_SHOWING,
eXULPopupShowing,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(popupshown,
NS_XUL_POPUP_SHOWN,
eXULPopupShown,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(popuphiding,
NS_XUL_POPUP_HIDING,
eXULPopupHiding,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(popuphidden,
NS_XUL_POPUP_HIDDEN,
eXULPopupHidden,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(broadcast,
NS_XUL_BROADCAST,
eXULBroadcast,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(commandupdate,
NS_XUL_COMMAND_UPDATE,
eXULCommandUpdate,
EventNameType_XUL,
eBasicEventClass)
NON_IDL_EVENT(dragexit,

View File

@ -27,6 +27,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/ScriptSettings.h"
@ -47,28 +48,27 @@ namespace dom {
using namespace workers;
class WorkerFetchResolver final : public FetchDriverObserver,
public WorkerFeature
class WorkerFetchResolver final : public FetchDriverObserver
{
friend class MainThreadFetchRunnable;
friend class WorkerFetchResponseEndRunnable;
friend class WorkerFetchResponseRunnable;
workers::WorkerPrivate* mWorkerPrivate;
Mutex mCleanUpLock;
bool mCleanedUp;
// The following are initialized and used exclusively on the worker thread.
nsRefPtr<Promise> mFetchPromise;
nsRefPtr<Response> mResponse;
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
public:
WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise)
: mWorkerPrivate(aWorkerPrivate)
, mCleanUpLock("WorkerFetchResolver")
, mCleanedUp(false)
, mFetchPromise(aPromise)
// Returns null if worker is shutting down.
static already_AddRefed<WorkerFetchResolver>
Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise);
if (!proxy) {
return nullptr;
}
nsRefPtr<WorkerFetchResolver> r = new WorkerFetchResolver(proxy);
return r.forget();
}
void
@ -77,58 +77,16 @@ public:
void
OnResponseEnd() override;
bool
Notify(JSContext* aCx, Status aStatus) override
{
if (aStatus > Running) {
CleanUp(aCx);
}
return true;
}
void
CleanUp(JSContext* aCx)
{
MutexAutoLock lock(mCleanUpLock);
if (mCleanedUp) {
return;
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
mWorkerPrivate->RemoveFeature(aCx, this);
CleanUpUnchecked();
}
void
CleanUpUnchecked()
{
mResponse = nullptr;
if (mFetchPromise) {
mFetchPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
mFetchPromise = nullptr;
}
mCleanedUp = true;
}
workers::WorkerPrivate*
GetWorkerPrivate() const
{
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
// the assertion when we should not.
MOZ_ASSERT(!mCleanedUp);
return mWorkerPrivate;
}
private:
~WorkerFetchResolver()
explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy)
: mPromiseProxy(aProxy)
{
MOZ_ASSERT(mCleanedUp);
MOZ_ASSERT(!mFetchPromise);
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mPromiseProxy);
}
~WorkerFetchResolver()
{}
};
class MainThreadFetchResolver final : public FetchDriverObserver
@ -153,34 +111,31 @@ class MainThreadFetchRunnable : public nsRunnable
nsRefPtr<InternalRequest> mRequest;
public:
MainThreadFetchRunnable(WorkerPrivate* aWorkerPrivate,
Promise* aPromise,
MainThreadFetchRunnable(WorkerFetchResolver* aResolver,
InternalRequest* aRequest)
: mResolver(new WorkerFetchResolver(aWorkerPrivate, aPromise))
: mResolver(aResolver)
, mRequest(aRequest)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), mResolver)) {
NS_WARNING("Could not add WorkerFetchResolver feature to worker");
mResolver->CleanUpUnchecked();
mResolver = nullptr;
}
MOZ_ASSERT(mResolver);
}
NS_IMETHODIMP
Run()
{
AssertIsOnMainThread();
// AddFeature() call failed, don't bother running.
if (!mResolver) {
nsRefPtr<PromiseWorkerProxy> proxy = mResolver->mPromiseProxy;
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
NS_WARNING("Aborting Fetch because worker already shut down");
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = mResolver->GetWorkerPrivate()->GetPrincipal();
nsCOMPtr<nsILoadGroup> loadGroup = mResolver->GetWorkerPrivate()->GetLoadGroup();
nsCOMPtr<nsIPrincipal> principal = proxy->GetWorkerPrivate()->GetPrincipal();
MOZ_ASSERT(principal);
nsCOMPtr<nsILoadGroup> loadGroup = proxy->GetWorkerPrivate()->GetLoadGroup();
MOZ_ASSERT(loadGroup);
nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest, principal, loadGroup);
nsIDocument* doc = mResolver->GetWorkerPrivate()->GetDocument();
nsIDocument* doc = proxy->GetWorkerPrivate()->GetDocument();
if (doc) {
fetch->SetDocument(doc);
}
@ -262,10 +217,15 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
r->SetSkipServiceWorker();
}
nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(worker, p, r);
if (NS_FAILED(NS_DispatchToMainThread(run))) {
NS_WARNING("MainThreadFetchRunnable dispatch failed!");
nsRefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p);
if (!resolver) {
NS_WARNING("Could not add WorkerFetchResolver feature to worker");
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(resolver, r);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(run)));
}
return p.forget();
@ -304,8 +264,10 @@ class WorkerFetchResponseRunnable final : public WorkerRunnable
// Passed from main thread to worker thread after being initialized.
nsRefPtr<InternalResponse> mInternalResponse;
public:
WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver, InternalResponse* aResponse)
: WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate,
WorkerFetchResolver* aResolver,
InternalResponse* aResponse)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mResolver(aResolver)
, mInternalResponse(aResponse)
{
@ -316,15 +278,13 @@ public:
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
nsRefPtr<Promise> promise = mResolver->mFetchPromise.forget();
nsRefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise();
if (mInternalResponse->Type() != ResponseType::Error) {
nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
mResolver->mResponse = new Response(global, mInternalResponse);
promise->MaybeResolve(mResolver->mResponse);
nsRefPtr<Response> response = new Response(global, mInternalResponse);
promise->MaybeResolve(response);
} else {
ErrorResult result;
result.ThrowTypeError(MSG_FETCH_FAILED);
@ -338,8 +298,9 @@ class WorkerFetchResponseEndRunnable final : public WorkerRunnable
{
nsRefPtr<WorkerFetchResolver> mResolver;
public:
explicit WorkerFetchResponseEndRunnable(WorkerFetchResolver* aResolver)
: WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate,
WorkerFetchResolver* aResolver)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mResolver(aResolver)
{
}
@ -349,9 +310,8 @@ public:
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
mResolver->CleanUp(aCx);
mResolver->mPromiseProxy->CleanUp(aCx);
return true;
}
};
@ -361,17 +321,19 @@ WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
{
AssertIsOnMainThread();
MutexAutoLock lock(mCleanUpLock);
if (mCleanedUp) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return;
}
nsRefPtr<WorkerFetchResponseRunnable> r =
new WorkerFetchResponseRunnable(this, aResponse);
new WorkerFetchResponseRunnable(mPromiseProxy->GetWorkerPrivate(), this,
aResponse);
AutoSafeJSContext cx;
if (!r->Dispatch(cx)) {
NS_WARNING("Could not dispatch fetch resolve");
AutoJSAPI jsapi;
jsapi.Init();
if (!r->Dispatch(jsapi.cx())) {
NS_WARNING("Could not dispatch fetch response");
}
}
@ -379,17 +341,18 @@ void
WorkerFetchResolver::OnResponseEnd()
{
AssertIsOnMainThread();
MutexAutoLock lock(mCleanUpLock);
if (mCleanedUp) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return;
}
nsRefPtr<WorkerFetchResponseEndRunnable> r =
new WorkerFetchResponseEndRunnable(this);
new WorkerFetchResponseEndRunnable(mPromiseProxy->GetWorkerPrivate(), this);
AutoSafeJSContext cx;
if (!r->Dispatch(cx)) {
NS_WARNING("Could not dispatch fetch resolve end");
AutoJSAPI jsapi;
jsapi.Init();
if (!r->Dispatch(jsapi.cx())) {
NS_WARNING("Could not dispatch fetch response end");
}
}

View File

@ -5,7 +5,7 @@
#include "domstubs.idl"
[scriptable, uuid(531b902b-b551-4faa-9814-1a73e8299ac4)]
[scriptable, uuid(3dd203e4-66ec-40fd-acde-43f0b35c98e9)]
interface nsITabParent : nsISupports
{
void injectTouchEvent(in AString aType,
@ -23,7 +23,10 @@ interface nsITabParent : nsISupports
readonly attribute boolean useAsyncPanZoom;
void setIsDocShellActive(in bool aIsActive);
/**
* Manages the docshell active state of the remote browser.
*/
attribute boolean docShellIsActive;
/**
* During interactions where painting performance

View File

@ -722,9 +722,9 @@ child:
SetUpdateHitRegion(bool aEnabled);
/**
* Tell the child to update its docShell's active state.
* Update the child side docShell active (resource use) state.
*/
SetIsDocShellActive(bool aIsActive);
SetDocShellIsActive(bool aIsActive);
/**
* Notify the child that it shouldn't paint the offscreen displayport.

View File

@ -2572,7 +2572,7 @@ TabChild::RecvSetUpdateHitRegion(const bool& aEnabled)
}
bool
TabChild::RecvSetIsDocShellActive(const bool& aIsActive)
TabChild::RecvSetDocShellIsActive(const bool& aIsActive)
{
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {

View File

@ -512,7 +512,7 @@ protected:
virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
virtual bool RecvDestroy() override;
virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
virtual bool RecvSetDocShellIsActive(const bool& aIsActive) override;
virtual bool RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) override;
virtual bool RecvRequestNotifyAfterRemotePaint() override;

View File

@ -276,6 +276,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mDefaultScale(0)
, mUpdatedDimensions(false)
, mManager(aManager)
, mDocShellIsActive(false)
, mMarkedDestroying(false)
, mIsDestroyed(false)
, mIsDetached(true)
@ -3044,10 +3045,19 @@ TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
return NS_OK;
}
// defined in nsITabParent
NS_IMETHODIMP
TabParent::SetIsDocShellActive(bool isActive)
TabParent::SetDocShellIsActive(bool isActive)
{
unused << SendSetIsDocShellActive(isActive);
mDocShellIsActive = isActive;
unused << SendSetDocShellIsActive(isActive);
return NS_OK;
}
NS_IMETHODIMP
TabParent::GetDocShellIsActive(bool* aIsActive)
{
*aIsActive = mDocShellIsActive;
return NS_OK;
}

View File

@ -504,6 +504,9 @@ private:
bool AsyncPanZoomEnabled() const;
// Cached value indicating the docshell active state of the remote browser.
bool mDocShellIsActive;
// Update state prior to routing an APZ-aware event to the child process.
// |aOutTargetGuid| will contain the identifier
// of the APZC instance that handled the event. aOutTargetGuid may be null.

View File

@ -1253,8 +1253,8 @@ PeerConnectionWrapper.prototype = {
info(this.label + ": iceCandidate = " + JSON.stringify(anEvent.candidate));
ok(anEvent.candidate.candidate.length > 0, "ICE candidate contains candidate");
// we don't support SDP MID's yet
ok(anEvent.candidate.sdpMid.length === 0, "SDP MID has length zero");
ok(anEvent.candidate.sdpMid.length > 0, "SDP mid not empty");
ok(typeof anEvent.candidate.sdpMLineIndex === 'number', "SDP MLine Index needs to exist");
this._local_ice_candidates.push(anEvent.candidate);
candidateHandler(this.label, anEvent.candidate);

View File

@ -1718,7 +1718,7 @@ public:
void
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
nsRefPtr<Promise> workerPromise = mPromiseProxy->GetWorkerPromise();
nsRefPtr<Promise> workerPromise = mPromiseProxy->WorkerPromise();
ErrorResult result;
nsAutoTArray<nsRefPtr<Notification>, 5> notifications;
@ -1767,27 +1767,19 @@ public:
{
AssertIsOnMainThread();
MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return NS_OK;
}
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate());
nsRefPtr<WorkerGetResultRunnable> r =
new WorkerGetResultRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy,
new WorkerGetResultRunnable(proxy->GetWorkerPrivate(),
proxy,
Move(mStrings));
if (!r->Dispatch(aCx)) {
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy);
DebugOnly<bool> ok = cr->Dispatch(aCx);
MOZ_ASSERT(ok);
}
mPromiseProxy = nullptr;
r->Dispatch(aCx);
return NS_OK;
}
@ -1804,31 +1796,18 @@ class WorkerGetRunnable final : public nsRunnable
const nsString mTag;
const nsString mScope;
public:
WorkerGetRunnable(WorkerPrivate* aWorkerPrivate,
Promise* aWorkerPromise,
WorkerGetRunnable(PromiseWorkerProxy* aProxy,
const nsAString& aTag,
const nsAString& aScope)
: mTag(aTag), mScope(aScope)
: mPromiseProxy(aProxy), mTag(aTag), mScope(aScope)
{
aWorkerPrivate->AssertIsOnWorkerThread();
mPromiseProxy =
PromiseWorkerProxy::Create(aWorkerPrivate,
aWorkerPromise);
if (!mPromiseProxy || !mPromiseProxy->GetWorkerPromise()) {
aWorkerPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
mPromiseProxy = nullptr;
}
MOZ_ASSERT(mPromiseProxy);
}
NS_IMETHOD
Run() override
{
AssertIsOnMainThread();
if (!mPromiseProxy) {
return NS_OK;
}
nsCOMPtr<nsINotificationStorageCallback> callback =
new WorkerGetCallback(mPromiseProxy, mScope);
@ -1843,8 +1822,8 @@ public:
return rv;
}
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
@ -1883,13 +1862,18 @@ Notification::WorkerGet(WorkerPrivate* aWorkerPrivate,
return nullptr;
}
nsRefPtr<WorkerGetRunnable> r =
new WorkerGetRunnable(aWorkerPrivate, p, aFilter.mTag, aScope);
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
nsRefPtr<PromiseWorkerProxy> proxy =
PromiseWorkerProxy::Create(aWorkerPrivate, p);
if (!proxy) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
nsRefPtr<WorkerGetRunnable> r =
new WorkerGetRunnable(proxy, aFilter.mTag, aScope);
// Since this is called from script via
// ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return p.forget();
}

View File

@ -1509,8 +1509,7 @@ public:
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
MOZ_ASSERT(mPromiseWorkerProxy);
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
MOZ_ASSERT(workerPromise);
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->WorkerPromise();
// Here we convert the buffer to a JS::Value.
JS::Rooted<JS::Value> value(aCx);
@ -1553,11 +1552,10 @@ PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate,
// We do this to make sure the worker thread won't shut down before the
// promise is resolved/rejected on the worker thread.
if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) {
if (!proxy->AddRefObject()) {
// Probably the worker is terminating. We cannot complete the operation
// and we have to release all the resources.
proxy->mCleanedUp = true;
proxy->mWorkerPromise = nullptr;
proxy->CleanProperties();
return nullptr;
}
@ -1574,40 +1572,76 @@ PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
, mCleanedUp(false)
, mCallbacks(aCallbacks)
, mCleanUpLock("cleanUpLock")
, mFeatureAdded(false)
{
}
PromiseWorkerProxy::~PromiseWorkerProxy()
{
MOZ_ASSERT(mCleanedUp);
MOZ_ASSERT(!mFeatureAdded);
MOZ_ASSERT(!mWorkerPromise);
MOZ_ASSERT(!mWorkerPrivate);
}
workers::WorkerPrivate*
PromiseWorkerProxy::GetWorkerPrivate() const
void
PromiseWorkerProxy::CleanProperties()
{
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
// the assertion when we should not.
MOZ_ASSERT(!mCleanedUp);
#ifdef DEBUG
if (NS_IsMainThread()) {
mCleanUpLock.AssertCurrentThreadOwns();
}
#endif
return mWorkerPrivate;
}
Promise*
PromiseWorkerProxy::GetWorkerPromise() const
{
#ifdef DEBUG
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
#endif
// Ok to do this unprotected from Create().
// CleanUp() holds the lock before calling this.
mCleanedUp = true;
mWorkerPromise = nullptr;
mWorkerPrivate = nullptr;
}
bool
PromiseWorkerProxy::AddRefObject()
{
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(!mFeatureAdded);
if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(),
this)) {
return false;
}
mFeatureAdded = true;
// Maintain a reference so that we have a valid object to clean up when
// removing the feature.
AddRef();
return true;
}
workers::WorkerPrivate*
PromiseWorkerProxy::GetWorkerPrivate() const
{
#ifdef DEBUG
if (NS_IsMainThread()) {
mCleanUpLock.AssertCurrentThreadOwns();
}
#endif
// Safe to check this without a lock since we assert lock ownership on the
// main thread above.
MOZ_ASSERT(!mCleanedUp);
MOZ_ASSERT(mFeatureAdded);
return mWorkerPrivate;
}
Promise*
PromiseWorkerProxy::WorkerPromise() const
{
#ifdef DEBUG
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
#endif
MOZ_ASSERT(mWorkerPromise);
return mWorkerPromise;
}
@ -1621,14 +1655,6 @@ PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
mSupportsArray.AppendElement(supports);
}
bool
PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx,
workers::WorkerPrivate* aWorkerPrivate)
{
mProxy->CleanUp(aCx);
return true;
}
void
PromiseWorkerProxy::RunCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue,
@ -1636,9 +1662,9 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx,
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(GetCleanUpLock());
MutexAutoLock lock(Lock());
// If the worker thread's been cancelled we don't need to resolve the Promise.
if (IsClean()) {
if (CleanedUp()) {
return;
}
@ -1657,11 +1683,7 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx,
Move(buffer),
aFunc);
if (!runnable->Dispatch(aCx)) {
nsRefPtr<WorkerControlRunnable> runnable =
new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this);
mWorkerPrivate->DispatchControlRunnable(runnable.forget());
}
runnable->Dispatch(aCx);
}
void
@ -1681,10 +1703,6 @@ PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
bool
PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
{
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
if (aStatus >= Canceling) {
CleanUp(aCx);
}
@ -1695,24 +1713,29 @@ PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
void
PromiseWorkerProxy::CleanUp(JSContext* aCx)
{
MutexAutoLock lock(mCleanUpLock);
// Can't release Mutex while it is still locked, so scope the lock.
{
MutexAutoLock lock(Lock());
// |mWorkerPrivate| might not be safe to use anymore if we have already
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
if (mCleanedUp) {
return;
// |mWorkerPrivate| is not safe to use anymore if we have already
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
if (CleanedUp()) {
return;
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
// Release the Promise and remove the PromiseWorkerProxy from the features of
// the worker thread since the Promise has been resolved/rejected or the
// worker thread has been cancelled.
MOZ_ASSERT(mFeatureAdded);
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
mFeatureAdded = false;
CleanProperties();
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
// Release the Promise and remove the PromiseWorkerProxy from the features of
// the worker thread since the Promise has been resolved/rejected or the
// worker thread has been cancelled.
mWorkerPromise = nullptr;
mWorkerPrivate->RemoveFeature(aCx, this);
mCleanedUp = true;
Release();
}
// Specializations of MaybeRejectBrokenly we actually support.

View File

@ -24,8 +24,8 @@ namespace workers {
class WorkerPrivate;
} // namespace workers
// A proxy to catch the resolved/rejected Promise's result from the main thread
// and resolve/reject that on the worker thread eventually.
// A proxy to (eventually) mirror a resolved/rejected Promise's result from the
// main thread to a Promise on the worker thread.
//
// How to use:
//
@ -36,13 +36,27 @@ class WorkerPrivate;
// if (aRv.Failed()) {
// return nullptr;
// }
// // Pass |promise| around to the WorkerMainThreadRunnable
//
// 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
// worker is shutting down and you should fail the original call. This is
// only likely to happen in (Gecko-specific) worker onclose handlers.
//
// nsRefPtr<PromiseWorkerProxy> proxy =
// PromiseWorkerProxy::Create(workerPrivate, promise);
// if (!proxy) {
// // You may also reject the Promise with an AbortError or similar.
// return nullptr;
// }
//
// 3. Dispatch a runnable to the main thread, with a reference to the proxy to
// perform the main thread operation. PromiseWorkerProxy is thread-safe
// refcounted.
//
// 4. Return the worker thread promise to the JS caller:
//
// return promise.forget();
//
// 2. In your WorkerMainThreadRunnable's ctor, create a PromiseWorkerProxy
// which holds a nsRefPtr<Promise> to the Promise created at #1.
//
// 3. In your WorkerMainThreadRunnable::MainThreadRun(), obtain a Promise on
// 5. In your main thread runnable Run(), obtain a Promise on
// the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
// to bind the PromiseWorkerProxy created at #2.
//
@ -52,17 +66,49 @@ class WorkerPrivate;
//
// PromiseWorkerProxy can also be used in situations where there is no main
// thread Promise, or where special handling is required on the worker thread
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
// 2 above. When the main thread is ready to resolve the worker thread promise,
// dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
// worker. This might be null! In the WorkerRunnable's WorkerRun() use
// GetWorkerPromise() to access the Promise and resolve/reject it. Then call
// CleanUp() on the worker thread.
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
// above. When the main thread is ready to resolve the worker thread promise:
//
// IMPORTANT: Dispatching the runnable to the worker thread may fail causing
// the promise to leak. To successfully release the promise on the
// worker thread in this case, use |PromiseWorkerProxyControlRunnable| to
// dispatch a control runnable that will deref the object on the correct thread.
// 1. Acquire the mutex before attempting to access the worker private.
//
// AssertIsOnMainThread();
// MutexAutoLock lock(proxy->Lock());
// if (proxy->CleanedUp()) {
// // Worker has already shut down, can't access worker private.
// return;
// }
//
// 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
// worker.
//
// nsRefPtr<FinishTaskWorkerRunnable> runnable =
// new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy, result);
// AutoJSAPI jsapi;
// jsapi.Init();
// if (!r->Dispatch(jsapi.cx())) {
// // Worker is alive but not Running any more, so the Promise can't
// // be resolved, give up. The proxy will get Release()d at some
// // point.
//
// // Usually do nothing, but you may want to log the fact.
// }
//
// 3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the
// Promise and resolve/reject it. Then call CleanUp().
//
// bool
// WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
// {
// aWorkerPrivate->AssertIsOnWorkerThread();
// nsRefPtr<Promise> promise = mProxy->WorkerPromise();
// promise->MaybeResolve(mResult);
// mProxy->CleanUp(aCx);
// }
//
// Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
// can happen if the main thread Promise is never fulfilled - it will
// stay alive till the worker reaches a Canceling state, even if all external
// references to it are dropped.
class PromiseWorkerProxy : public PromiseNativeHandler,
public workers::WorkerFeature
@ -77,20 +123,29 @@ public:
Promise* aWorkerPromise,
const JSStructuredCloneCallbacks* aCallbacks = nullptr);
// Main thread callers must hold Lock() and check CleanUp() before calling this.
// Worker thread callers, this will assert that the proxy has not been cleaned
// up.
workers::WorkerPrivate* GetWorkerPrivate() const;
Promise* GetWorkerPromise() const;
// This should only be used within WorkerRunnable::WorkerRun() running on the
// worker thread! Do not call this after calling CleanUp().
Promise* WorkerPromise() const;
void StoreISupports(nsISupports* aSupports);
// Worker thread only. Calling this invalidates several assumptions, so be
// sure this is the last thing you do.
// 1. WorkerPrivate() will no longer return a valid worker.
// 2. WorkerPromise() will crash!
void CleanUp(JSContext* aCx);
Mutex& GetCleanUpLock()
Mutex& Lock()
{
return mCleanUpLock;
}
bool IsClean() const
bool CleanedUp() const
{
mCleanUpLock.AssertCurrentThreadOwns();
return mCleanedUp;
@ -112,6 +167,11 @@ private:
virtual ~PromiseWorkerProxy();
bool AddRefObject();
// If not called from Create(), be sure to hold Lock().
void CleanProperties();
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
typedef void (Promise::*RunCallbackFunc)(JSContext*,
JS::Handle<JS::Value>);
@ -120,11 +180,15 @@ private:
JS::Handle<JS::Value> aValue,
RunCallbackFunc aFunc);
// Any thread with appropriate checks.
workers::WorkerPrivate* mWorkerPrivate;
// This lives on the worker thread.
// Worker thread only.
nsRefPtr<Promise> mWorkerPromise;
// Modified on the worker thread.
// It is ok to *read* this without a lock on the worker.
// Main thread must always acquire a lock.
bool mCleanedUp; // To specify if the cleanUp() has been done.
const JSStructuredCloneCallbacks* mCallbacks;
@ -135,32 +199,10 @@ private:
// Ensure the worker and the main thread won't race to access |mCleanedUp|.
Mutex mCleanUpLock;
// Maybe get rid of this entirely and rely on mCleanedUp
DebugOnly<bool> mFeatureAdded;
};
// Helper runnable used for releasing the proxied promise when the worker
// is not accepting runnables and the promise object would leak.
// See the instructions above.
class PromiseWorkerProxyControlRunnable final : public workers::WorkerControlRunnable
{
nsRefPtr<PromiseWorkerProxy> mProxy;
public:
PromiseWorkerProxyControlRunnable(workers::WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aProxy)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mProxy(aProxy)
{
MOZ_ASSERT(aProxy);
}
virtual bool
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override;
private:
~PromiseWorkerProxyControlRunnable()
{}
};
} // namespace dom
} // namespace mozilla

View File

@ -216,30 +216,6 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEnd
return sub.forget();
}
namespace {
// The caller MUST take ownership of the proxy's lock before it calls this.
void
ReleasePromiseWorkerProxy(already_AddRefed<PromiseWorkerProxy> aProxy)
{
AssertIsOnMainThread();
nsRefPtr<PromiseWorkerProxy> proxy = aProxy;
MOZ_ASSERT(proxy);
proxy->GetCleanUpLock().AssertCurrentThreadOwns();
if (proxy->IsClean()) {
return;
}
AutoJSAPI jsapi;
jsapi.Init();
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(),
proxy);
MOZ_ALWAYS_TRUE(cr->Dispatch(jsapi.cx()));
}
} // anonymous namespace
class UnsubscribeResultRunnable final : public WorkerRunnable
{
public:
@ -260,15 +236,14 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
if (NS_SUCCEEDED(mStatus)) {
promise->MaybeResolve(mSuccess);
} else {
promise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
}
proxy->CleanUp(aCx);
mProxy->CleanUp(aCx);
return true;
}
private:
@ -295,12 +270,12 @@ public:
OnUnsubscribe(nsresult aStatus, bool aSuccess) override
{
AssertIsOnMainThread();
if (!mProxy) {
return NS_OK;
}
MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (mProxy->IsClean()) {
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return NS_OK;
}
@ -308,29 +283,14 @@ public:
jsapi.Init();
nsRefPtr<UnsubscribeResultRunnable> r =
new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess);
if (!r->Dispatch(jsapi.cx())) {
ReleasePromiseWorkerProxy(mProxy.forget());
}
mProxy = nullptr;
new UnsubscribeResultRunnable(proxy, aStatus, aSuccess);
r->Dispatch(jsapi.cx());
return NS_OK;
}
private:
~WorkerUnsubscribeResultCallback()
{
AssertIsOnMainThread();
if (mProxy) {
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (!mProxy->IsClean()) {
AutoJSAPI jsapi;
jsapi.Init();
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
cr->Dispatch(jsapi.cx());
}
}
}
nsRefPtr<PromiseWorkerProxy> mProxy;
@ -354,8 +314,8 @@ public:
Run() override
{
AssertIsOnMainThread();
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (mProxy->IsClean()) {
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return NS_OK;
}
@ -448,8 +408,7 @@ public:
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
if (NS_SUCCEEDED(mStatus)) {
if (mEndpoint.IsEmpty()) {
promise->MaybeResolve(JS::NullHandleValue);
@ -462,7 +421,7 @@ public:
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
proxy->CleanUp(aCx);
mProxy->CleanUp(aCx);
return true;
}
private:
@ -490,13 +449,12 @@ public:
OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
{
AssertIsOnMainThread();
MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
if (!mProxy) {
return NS_OK;
}
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (mProxy->IsClean()) {
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return NS_OK;
}
@ -504,30 +462,14 @@ public:
jsapi.Init();
nsRefPtr<GetSubscriptionResultRunnable> r =
new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope);
if (!r->Dispatch(jsapi.cx())) {
ReleasePromiseWorkerProxy(mProxy.forget());
}
mProxy = nullptr;
new GetSubscriptionResultRunnable(proxy, aStatus, aEndpoint, mScope);
r->Dispatch(jsapi.cx());
return NS_OK;
}
protected:
~GetSubscriptionCallback()
{
AssertIsOnMainThread();
if (mProxy) {
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (!mProxy->IsClean()) {
AutoJSAPI jsapi;
jsapi.Init();
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
cr->Dispatch(jsapi.cx());
}
}
}
{}
private:
nsRefPtr<PromiseWorkerProxy> mProxy;
@ -550,8 +492,8 @@ public:
Run() override
{
AssertIsOnMainThread();
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (mProxy->IsClean()) {
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return NS_OK;
}
@ -564,9 +506,11 @@ public:
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
uint32_t permission = nsIPermissionManager::DENY_ACTION;
nsresult rv = permManager->TestExactPermissionFromPrincipal(
mProxy->GetWorkerPrivate()->GetPrincipal(),
principal,
"push",
&permission);
@ -582,9 +526,6 @@ public:
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
mProxy = nullptr;
if (mAction == WorkerPushManager::SubscribeAction) {
rv = client->Subscribe(mScope, principal, callback);
} else {
@ -667,17 +608,17 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
if (NS_SUCCEEDED(mStatus)) {
MOZ_ASSERT(uint32_t(mState) < ArrayLength(PushPermissionStateValues::strings));
nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value, PushPermissionStateValues::strings[uint32_t(mState)].length);
nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value,
PushPermissionStateValues::strings[uint32_t(mState)].length);
promise->MaybeResolve(NS_ConvertUTF8toUTF16(stringState));
} else {
promise->MaybeReject(aCx, JS::UndefinedHandleValue);
}
proxy->CleanUp(aCx);
mProxy->CleanUp(aCx);
return true;
}
@ -701,8 +642,8 @@ public:
Run() override
{
AssertIsOnMainThread();
MutexAutoLock lock(mProxy->GetCleanUpLock());
if (mProxy->IsClean()) {
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return NS_OK;
}
@ -740,9 +681,7 @@ public:
jsapi.Init();
nsRefPtr<PermissionResultRunnable> r =
new PermissionResultRunnable(mProxy, rv, state);
if (!r->Dispatch(jsapi.cx())) {
ReleasePromiseWorkerProxy(mProxy.forget());
}
r->Dispatch(jsapi.cx());
return NS_OK;
}

View File

@ -15,6 +15,7 @@
*/
#include "GonkGPSGeolocationProvider.h"
#include "mozstumbler/MozStumbler.h"
#include <pthread.h>
#include <hardware/gps.h>
@ -87,6 +88,72 @@ AGpsCallbacks GonkGPSGeolocationProvider::mAGPSCallbacks;
AGpsRilCallbacks GonkGPSGeolocationProvider::mAGPSRILCallbacks;
#endif // MOZ_B2G_RIL
double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
{
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
const double radsInDeg = M_PI / 180.0;
const double rNewLat = aLat * radsInDeg;
const double rNewLon = aLon * radsInDeg;
const double rOldLat = aLastLat * radsInDeg;
const double rOldLon = aLastLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
if (cosDelta > 1.0) {
cosDelta = 1.0;
} else if (cosDelta < -1.0) {
cosDelta = -1.0;
}
return acos(cosDelta) * 6378137;
}
class RequestCellInfoEvent : public nsRunnable {
public:
RequestCellInfoEvent(StumblerInfo *callback)
: mRequestCallback(callback)
{}
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
// Get Cell Info
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnectionService \n");
return NS_OK;
}
nsCOMPtr<nsIMobileConnection> connection;
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
service->GetNumItems(&numberOfRilServices);
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
if (!connection) {
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
} else {
cellInfoNum++;
connection->GetCellInfoList(mRequestCallback);
}
}
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
// Get Wifi AP Info
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
if (!wifi) {
mRequestCallback->SetWifiInfoResponseReceived();
nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIWifi interface\n");
return NS_OK;
}
wifi->GetWifiScanResults(mRequestCallback);
return NS_OK;
}
private:
nsRefPtr<StumblerInfo> mRequestCallback;
};
void
GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
{
@ -141,7 +208,38 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
location->accuracy);
}
NS_DispatchToMainThread(new UpdateLocationEvent(somewhere));
nsRefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
NS_DispatchToMainThread(event);
const double kMinChangeInMeters = 30;
static int64_t lastTime_ms = 0;
static double sLastLat = 0;
static double sLastLon = 0;
double delta = -1.0;
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
if (0 != sLastLon || 0 != sLastLat) {
delta = CalculateDeltaInMeter(location->latitude, location->longitude, sLastLat, sLastLon);
}
if (gDebug_isLoggingEnabled) {
nsContentUtils::LogMessageToConsole("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
location->longitude, location->latitude, timediff, delta);
}
// Consecutive GPS locations must be 30 meters and 3 seconds apart
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
sLastLat = location->latitude;
sLastLon = location->longitude;
nsRefPtr<StumblerInfo> requestCallback = new StumblerInfo(somewhere);
nsRefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
NS_DispatchToMainThread(runnable);
} else {
if (gDebug_isLoggingEnabled) {
nsContentUtils::LogMessageToConsole(
"Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
}
}
}
void
@ -886,23 +984,7 @@ GonkGPSGeolocationProvider::NetworkLocationUpdate::Update(nsIDOMGeoPosition *pos
static double sLastMLSPosLon = 0;
if (0 != sLastMLSPosLon || 0 != sLastMLSPosLat) {
// Use spherical law of cosines to calculate difference
// Not quite as correct as the Haversine but simpler and cheaper
// Should the following be a utility function? Others might need this calc.
const double radsInDeg = M_PI / 180.0;
const double rNewLat = lat * radsInDeg;
const double rNewLon = lon * radsInDeg;
const double rOldLat = sLastMLSPosLat * radsInDeg;
const double rOldLon = sLastMLSPosLon * radsInDeg;
// WGS84 equatorial radius of earth = 6378137m
double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
(cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
if (cosDelta > 1.0) {
cosDelta = 1.0;
} else if (cosDelta < -1.0) {
cosDelta = -1.0;
}
delta = acos(cosDelta) * 6378137;
delta = CalculateDeltaInMeter(lat, lon, sLastMLSPosLat, sLastMLSPosLon);
}
sLastMLSPosLat = lat;

View File

@ -34,6 +34,7 @@ XPIDL_MODULE = 'dom_system_gonk'
EXPORTS += [
'GonkGPSGeolocationProvider.h',
'mozstumbler/MozStumbler.h',
'nsVolume.h',
'nsVolumeService.h',
]
@ -46,6 +47,10 @@ UNIFIED_SOURCES += [
'MozMtpDatabase.cpp',
'MozMtpServer.cpp',
'MozMtpStorage.cpp',
'mozstumbler/MozStumbler.cpp',
'mozstumbler/StumblerLogging.cpp',
'mozstumbler/UploadStumbleRunnable.cpp',
'mozstumbler/WriteStumbleOnThread.cpp',
'NetIdManager.cpp',
'NetworkUtils.cpp',
'NetworkWorker.cpp',

View File

@ -0,0 +1,323 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "MozStumbler.h"
#include "nsGeoPosition.h"
#include "nsPrintfCString.h"
#include "StumblerLogging.h"
#include "WriteStumbleOnThread.h"
#include "nsNetCID.h"
#include "nsDataHashtable.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
void
StumblerInfo::SetWifiInfoResponseReceived()
{
mIsWifiInfoResponseReceived = true;
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetWifiInfoResponseReceived\n");
DumpStumblerInfo();
}
}
void
StumblerInfo::SetCellInfoResponsesExpected(uint8_t count)
{
mCellInfoResponsesExpected = count;
STUMBLER_DBG("SetCellInfoNum (%d)\n", count);
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetCellInfoResponsesExpected\n");
DumpStumblerInfo();
}
}
#define TEXT_LAT NS_LITERAL_CSTRING("latitude")
#define TEXT_LON NS_LITERAL_CSTRING("longitude")
#define TEXT_ACC NS_LITERAL_CSTRING("accuracy")
#define TEXT_ALT NS_LITERAL_CSTRING("altitude")
#define TEXT_ALTACC NS_LITERAL_CSTRING("altitudeAccuracy")
#define TEXT_HEAD NS_LITERAL_CSTRING("heading")
#define TEXT_SPD NS_LITERAL_CSTRING("speed")
nsresult
StumblerInfo::LocationInfoToString(nsACString& aLocDesc)
{
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
mPosition->GetCoords(getter_AddRefs(coords));
if (!coords) {
return NS_ERROR_FAILURE;
}
nsDataHashtable<nsCStringHashKey, double> info;
double val;
coords->GetLatitude(&val);
info.Put(TEXT_LAT, val);
coords->GetLongitude(&val);
info.Put(TEXT_LON, val);
coords->GetAccuracy(&val);
info.Put(TEXT_ACC, val);
coords->GetAltitude(&val);
info.Put(TEXT_ALT, val);
coords->GetAltitudeAccuracy(&val);
info.Put(TEXT_ALTACC, val);
coords->GetHeading(&val);
info.Put(TEXT_HEAD, val);
coords->GetSpeed(&val);
info.Put(TEXT_SPD, val);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
val = it.UserData();
if (!IsNaN(val)) {
aLocDesc += nsPrintfCString("\"%s\":%f,", key.BeginReading(), val);
}
}
aLocDesc += nsPrintfCString("\"timestamp\":%lld,", PR_Now() / PR_USEC_PER_MSEC).get();
return NS_OK;
}
#define TEXT_RADIOTYPE NS_LITERAL_CSTRING("radioType")
#define TEXT_MCC NS_LITERAL_CSTRING("mobileCountryCode")
#define TEXT_MNC NS_LITERAL_CSTRING("mobileNetworkCode")
#define TEXT_LAC NS_LITERAL_CSTRING("locationAreaCode")
#define TEXT_CID NS_LITERAL_CSTRING("cellId")
#define TEXT_PSC NS_LITERAL_CSTRING("psc")
#define TEXT_STRENGTH_ASU NS_LITERAL_CSTRING("asu")
#define TEXT_STRENGTH_DBM NS_LITERAL_CSTRING("signalStrength")
#define TEXT_REGISTERED NS_LITERAL_CSTRING("serving")
#define TEXT_TIMEING_ADVANCE NS_LITERAL_CSTRING("timingAdvance")
template <class T> void
ExtractCommonNonCDMACellInfoItems(nsCOMPtr<T>& cell, nsDataHashtable<nsCStringHashKey, int32_t>& info)
{
int32_t mcc, mnc, cid, sig;
cell->GetMcc(&mcc);
cell->GetMnc(&mnc);
cell->GetCid(&cid);
cell->GetSignalStrength(&sig);
info.Put(TEXT_MCC, mcc);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_CID, cid);
info.Put(TEXT_STRENGTH_ASU, sig);
}
void
StumblerInfo::CellNetworkInfoToString(nsACString& aCellDesc)
{
aCellDesc += "\"cellTowers\": [";
for (uint32_t idx = 0; idx < mCellInfo.Length() ; idx++) {
const char* radioType = 0;
int32_t type;
mCellInfo[idx]->GetType(&type);
bool registered;
mCellInfo[idx]->GetRegistered(&registered);
if (idx) {
aCellDesc += ",{";
} else {
aCellDesc += "{";
}
STUMBLER_DBG("type=%d\n", type);
nsDataHashtable<nsCStringHashKey, int32_t> info;
info.Put(TEXT_REGISTERED, registered);
if(type == nsICellInfo::CELL_INFO_TYPE_GSM) {
radioType = "gsm";
nsCOMPtr<nsIGsmCellInfo> gsmCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(gsmCellInfo, info);
int32_t lac;
gsmCellInfo->GetLac(&lac);
info.Put(TEXT_LAC, lac);
} else if (type == nsICellInfo::CELL_INFO_TYPE_WCDMA) {
radioType = "wcdma";
nsCOMPtr<nsIWcdmaCellInfo> wcdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(wcdmaCellInfo, info);
int32_t lac, psc;
wcdmaCellInfo->GetLac(&lac);
wcdmaCellInfo->GetPsc(&psc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_PSC, psc);
} else if (type == nsICellInfo::CELL_INFO_TYPE_CDMA) {
radioType = "cdma";
nsCOMPtr<nsICdmaCellInfo> cdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
int32_t mnc, lac, cid, sig;
cdmaCellInfo->GetSystemId(&mnc);
cdmaCellInfo->GetNetworkId(&lac);
cdmaCellInfo->GetBaseStationId(&cid);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_CID, cid);
cdmaCellInfo->GetEvdoDbm(&sig);
if (sig < 0 || sig == nsICellInfo::UNKNOWN_VALUE) {
cdmaCellInfo->GetCdmaDbm(&sig);
}
if (sig > -1 && sig != nsICellInfo::UNKNOWN_VALUE) {
sig *= -1;
info.Put(TEXT_STRENGTH_DBM, sig);
}
} else if (type == nsICellInfo::CELL_INFO_TYPE_LTE) {
radioType = "lte";
nsCOMPtr<nsILteCellInfo> lteCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(lteCellInfo, info);
int32_t lac, timingAdvance, pcid, rsrp;
lteCellInfo->GetTac(&lac);
lteCellInfo->GetTimingAdvance(&timingAdvance);
lteCellInfo->GetPcid(&pcid);
lteCellInfo->GetRsrp(&rsrp);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_TIMEING_ADVANCE, timingAdvance);
info.Put(TEXT_PSC, pcid);
if (rsrp != nsICellInfo::UNKNOWN_VALUE) {
info.Put(TEXT_STRENGTH_DBM, rsrp * -1);
}
}
aCellDesc += nsPrintfCString("\"%s\":\"%s\"", TEXT_RADIOTYPE.get(), radioType);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
int32_t value = it.UserData();
if (value != nsICellInfo::UNKNOWN_VALUE) {
aCellDesc += nsPrintfCString(",\"%s\":%d", key.BeginReading(), value);
}
}
aCellDesc += "}";
}
aCellDesc += "]";
}
void
StumblerInfo::DumpStumblerInfo()
{
if (!mIsWifiInfoResponseReceived || mCellInfoResponsesReceived != mCellInfoResponsesExpected) {
STUMBLER_DBG("CellInfoReceived=%d (Expected=%d), WifiInfoResponseReceived=%d\n",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
return;
}
mIsWifiInfoResponseReceived = false;
mCellInfoResponsesReceived = 0;
nsAutoCString desc;
nsresult rv = LocationInfoToString(desc);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("LocationInfoToString failed, skip this dump");
return;
}
CellNetworkInfoToString(desc);
desc += mWifiDesc;
STUMBLER_DBG("dispatch write event to thread\n");
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(desc);
target->Dispatch(event, NS_DISPATCH_NORMAL);
}
/* void notifyGetCellInfoList (in uint32_t count, [array, size_is (count)] in nsICellInfo result); */
NS_IMETHODIMP
StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d cellinfo in the result\n", count);
for (uint32_t i = 0; i < count; i++) {
mCellInfo.AppendElement(aCellInfos[i]);
}
mCellInfoResponsesReceived++;
DumpStumblerInfo();
return NS_OK;
}
/* void notifyGetCellInfoListFailed (in DOMString error); */
NS_IMETHODIMP StumblerInfo::NotifyGetCellInfoListFailed(const nsAString& error)
{
MOZ_ASSERT(NS_IsMainThread());
mCellInfoResponsesReceived++;
STUMBLER_ERR("NotifyGetCellInfoListFailedm CellInfoReadyNum=%d, mCellInfoResponsesExpected=%d, mIsWifiInfoResponseReceived=%d",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onready(uint32_t count, nsIWifiScanResult** results)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d wifiAPinfo in the result\n",count);
mWifiDesc += ",\"wifiAccessPoints\": [";
bool firstItem = true;
for (uint32_t i = 0 ; i < count ; i++) {
nsString ssid;
results[i]->GetSsid(ssid);
if (ssid.IsEmpty()) {
STUMBLER_DBG("no ssid, skip this AP\n");
continue;
}
if (ssid.Length() >= 6) {
if (StringEndsWith(ssid, NS_LITERAL_STRING("_nomap"))) {
STUMBLER_DBG("end with _nomap. skip this AP(ssid :%s)\n", ssid.get());
continue;
}
}
if (firstItem) {
mWifiDesc += "{";
firstItem = false;
} else {
mWifiDesc += ",{";
}
// mac address
nsString bssid;
results[i]->GetBssid(bssid);
// 00:00:00:00:00:00 --> 000000000000
bssid.StripChars(":");
mWifiDesc += "\"macAddress\":\"";
mWifiDesc += NS_ConvertUTF16toUTF8(bssid);
uint32_t signal;
results[i]->GetSignalStrength(&signal);
mWifiDesc += "\",\"signalStrength\":";
mWifiDesc.AppendInt(signal);
mWifiDesc += "}";
}
mWifiDesc += "]";
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onfailure()
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_ERR("GetWifiScanResults Onfailure\n");
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}

View File

@ -0,0 +1,45 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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_system_mozstumbler_h__
#define mozilla_system_mozstumbler_h__
#include "nsIDOMEventTarget.h"
#include "nsICellInfo.h"
#include "nsIWifi.h"
#define STUMBLE_INTERVAL_MS 3000
class nsGeoPosition;
class StumblerInfo final : public nsICellInfoListCallback,
public nsIWifiScanResultsReady
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICELLINFOLISTCALLBACK
NS_DECL_NSIWIFISCANRESULTSREADY
explicit StumblerInfo(nsGeoPosition* position)
: mPosition(position), mCellInfoResponsesExpected(0), mCellInfoResponsesReceived(0), mIsWifiInfoResponseReceived(0)
{}
void SetWifiInfoResponseReceived();
void SetCellInfoResponsesExpected(uint8_t count);
private:
~StumblerInfo() {}
void DumpStumblerInfo();
nsresult LocationInfoToString(nsACString& aLocDesc);
void CellNetworkInfoToString(nsACString& aCellDesc);
nsTArray<nsRefPtr<nsICellInfo>> mCellInfo;
nsCString mWifiDesc;
nsRefPtr<nsGeoPosition> mPosition;
int mCellInfoResponsesExpected;
int mCellInfoResponsesReceived;
bool mIsWifiInfoResponseReceived;
};
#endif // mozilla_system_mozstumbler_h__

View File

@ -0,0 +1,13 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "StumblerLogging.h"
PRLogModuleInfo* GetLog()
{
static PRLogModuleInfo* log = PR_NewLogModule("mozstumbler");
return log;
}

View File

@ -0,0 +1,18 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 STUMBLERLOGGING_H
#define STUMBLERLOGGING_H
#include "mozilla/Logging.h"
PRLogModuleInfo* GetLog();
#define STUMBLER_DBG(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Debug, ("STUMBLER - %s: " arg, __func__, ##__VA_ARGS__))
#define STUMBLER_LOG(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Info, ("STUMBLER - %s: " arg, __func__, ##__VA_ARGS__))
#define STUMBLER_ERR(arg, ...) MOZ_LOG(GetLog(), mozilla::LogLevel::Error, ("STUMBLER -%s: " arg, __func__, ##__VA_ARGS__))
#endif

View File

@ -0,0 +1,151 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "UploadStumbleRunnable.h"
#include "StumblerLogging.h"
#include "mozilla/dom/Event.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURLFormatter.h"
#include "nsIVariant.h"
#include "nsIXMLHttpRequest.h"
#include "nsNetUtil.h"
UploadStumbleRunnable::UploadStumbleRunnable(const nsACString& aUploadData)
: mUploadData(aUploadData)
{
}
NS_IMETHODIMP
UploadStumbleRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = Upload();
if (NS_FAILED(rv)) {
WriteStumbleOnThread::UploadEnded(false);
}
return NS_OK;
}
nsresult
UploadStumbleRunnable::Upload()
{
nsresult rv;
nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance("@mozilla.org/variant;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = variant->SetAsACString(mUploadData);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXMLHttpRequest> xhr = do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptSecurityManager> secman =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> systemPrincipal;
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURLFormatter> formatter =
do_CreateInstance("@mozilla.org/toolkit/URLFormatterService;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsString url;
rv = formatter->FormatURLPref(NS_LITERAL_STRING("geo.stumbler.url"), url);
NS_ENSURE_SUCCESS(rv, rv);
rv = xhr->Open(NS_LITERAL_CSTRING("POST"), NS_ConvertUTF16toUTF8(url), false, EmptyString(), EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
xhr->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), NS_LITERAL_CSTRING("application/json"));
xhr->SetMozBackgroundRequest(true);
// 60s timeout
xhr->SetTimeout(60 * 1000);
nsCOMPtr<EventTarget> target(do_QueryInterface(xhr));
nsRefPtr<nsIDOMEventListener> listener = new UploadEventListener(xhr, mUploadData.Length());
const char* const sEventStrings[] = {
// nsIXMLHttpRequestEventTarget event types
"abort",
"error",
"load",
"timeout"
};
for (uint32_t index = 0; index < MOZ_ARRAY_LENGTH(sEventStrings); index++) {
nsAutoString eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
rv = target->AddEventListener(eventType, listener, false);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = xhr->Send(variant);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMPL_ISUPPORTS(UploadEventListener, nsIDOMEventListener)
UploadEventListener::UploadEventListener(nsIXMLHttpRequest* aXHR, int64_t aFileSize)
: mXHR(aXHR), mFileSize(aFileSize)
{
}
NS_IMETHODIMP
UploadEventListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsString type;
if (NS_FAILED(aEvent->GetType(type))) {
STUMBLER_ERR("Failed to get event type");
WriteStumbleOnThread::UploadEnded(false);
return NS_ERROR_FAILURE;
}
if (type.EqualsLiteral("load")) {
STUMBLER_DBG("Got load Event : size %lld", mFileSize);
} else if (type.EqualsLiteral("error") && mXHR) {
STUMBLER_ERR("Upload Error");
} else {
STUMBLER_DBG("Receive %s Event", NS_ConvertUTF16toUTF8(type).get());
}
uint32_t statusCode = 0;
bool doDelete = false;
if (!mXHR) {
return NS_OK;
}
nsresult rv = mXHR->GetStatus(&statusCode);
if (NS_SUCCEEDED(rv)) {
STUMBLER_DBG("statuscode %d \n", statusCode);
}
if (200 == statusCode || 400 == statusCode) {
doDelete = true;
}
WriteStumbleOnThread::UploadEnded(doDelete);
nsCOMPtr<EventTarget> target(do_QueryInterface(mXHR));
const char* const sEventStrings[] = {
// nsIXMLHttpRequestEventTarget event types
"abort",
"error",
"load",
"timeout"
};
for (uint32_t index = 0; index < MOZ_ARRAY_LENGTH(sEventStrings); index++) {
nsAutoString eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
rv = target->RemoveEventListener(eventType, this, false);
}
mXHR = nullptr;
return NS_OK;
}

View File

@ -0,0 +1,46 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 UPLOADSTUMBLERUNNABLE_H
#define UPLOADSTUMBLERUNNABLE_H
#include "nsIDOMEventListener.h"
class nsIXMLHttpRequest;
/*
This runnable is managed by WriteStumbleOnThread only, see that class
for how this is scheduled.
*/
class UploadStumbleRunnable final : public nsRunnable
{
public:
explicit UploadStumbleRunnable(const nsACString& aUploadData);
NS_IMETHOD Run() override;
private:
virtual ~UploadStumbleRunnable() {}
const nsCString mUploadData;
nsresult Upload();
};
class UploadEventListener : public nsIDOMEventListener
{
public:
UploadEventListener(nsIXMLHttpRequest* aXHR, int64_t aFileSize);
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
protected:
virtual ~UploadEventListener() {}
nsCOMPtr<nsIXMLHttpRequest> mXHR;
int64_t mFileSize;
};
#endif

View File

@ -0,0 +1,317 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 "WriteStumbleOnThread.h"
#include "StumblerLogging.h"
#include "UploadStumbleRunnable.h"
#include "nsDumpUtils.h"
#include "nsGZFileWriter.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsPrintfCString.h"
#define MAXFILESIZE_KB (15 * 1024)
#define ONEDAY_IN_MSEC (24 * 60 * 60 * 1000)
#define MAX_UPLOAD_ATTEMPTS 20
mozilla::Atomic<bool> WriteStumbleOnThread::sIsUploading(false);
mozilla::Atomic<bool> WriteStumbleOnThread::sIsAlreadyRunning(false);
WriteStumbleOnThread::UploadFreqGuard WriteStumbleOnThread::sUploadFreqGuard = {0};
#define FILENAME_INPROGRESS NS_LITERAL_CSTRING("stumbles.json")
#define FILENAME_COMPLETED NS_LITERAL_CSTRING("stumbles.done.json")
#define OUTPUT_DIR NS_LITERAL_CSTRING("mozstumbler")
class DeleteRunnable : public nsRunnable
{
public:
DeleteRunnable() {}
NS_IMETHODIMP
Run() override
{
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED,
getter_AddRefs(tmpFile),
OUTPUT_DIR,
nsDumpUtils::CREATE);
if (NS_SUCCEEDED(rv)) {
tmpFile->Remove(true);
}
// critically, this sets this flag to false so writing can happen again
WriteStumbleOnThread::sIsUploading = false;
return NS_OK;
}
private:
~DeleteRunnable() {}
};
void
WriteStumbleOnThread::UploadEnded(bool deleteUploadFile)
{
if (!deleteUploadFile) {
sIsUploading = false;
return;
}
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
nsCOMPtr<nsIRunnable> event = new DeleteRunnable();
target->Dispatch(event, NS_DISPATCH_NORMAL);
}
#define DUMP(o, s) \
do { \
const char* s2 = (s); \
uint32_t dummy; \
nsresult rv = (o)->Write((s2), strlen(s2), &dummy); \
if (NS_WARN_IF(NS_FAILED(rv))) \
STUMBLER_ERR("write err"); \
} while (0)
void
WriteStumbleOnThread::WriteJSON(Partition aPart)
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv;
rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Open a file for stumble failed");
return;
}
nsCOMPtr<nsIFileOutputStream> ostream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
rv = ostream->Init(tmpFile, PR_WRONLY | PR_APPEND, 0666, 0);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Open a file for stumble failed");
return;
}
/*
The json format is like below.
{items:[
{item},
{item},
{item}
]}
*/
// Need to add "]}" after the last item
if (aPart == Partition::End) {
DUMP(ostream, "]}");
rv = ostream->Close();
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream finish failed");
}
nsCOMPtr<nsIFile> targetFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(targetFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
nsAutoString targetFilename;
rv = targetFile->GetLeafName(targetFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Get Filename failed");
return;
}
rv = targetFile->Remove(true);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Remove File failed");
return;
}
// Rename tmpfile
rv = tmpFile->MoveTo(/* directory */ nullptr, targetFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Rename File failed");
return;
}
return;
}
// Need to add "{items:[" before the first item
if (aPart == Partition::Begining) {
DUMP(ostream, "{\"items\":[{");
} else if (aPart == Partition::Middle) {
DUMP(ostream, ",{");
}
DUMP(ostream, mDesc.get());
// one item is ended with '}' (e.g. {item})
DUMP(ostream, "}");
rv = ostream->Close();
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("ostream finish failed");
}
// check if it is the end of this file
int64_t fileSize = 0;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return;
}
if (fileSize >= MAXFILESIZE_KB) {
WriteJSON(Partition::End);
return;
}
}
WriteStumbleOnThread::Partition
WriteStumbleOnThread::GetWritePosition()
{
MOZ_ASSERT(!NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("Open a file for stumble failed");
return Partition::Unknown;
}
int64_t fileSize = 0;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return Partition::Unknown;
}
if (fileSize == 0) {
return Partition::Begining;
} else if (fileSize >= MAXFILESIZE_KB) {
return Partition::End;
} else {
return Partition::Middle;
}
}
NS_IMETHODIMP
WriteStumbleOnThread::Run()
{
MOZ_ASSERT(!NS_IsMainThread());
bool b = sIsAlreadyRunning.exchange(true);
if (b) {
return NS_OK;
}
UploadFileStatus status = GetUploadFileStatus();
if (UploadFileStatus::NoFile != status) {
if (UploadFileStatus::ExistsAndReadyToUpload == status) {
Upload();
}
} else {
Partition partition = GetWritePosition();
if (partition == Partition::Unknown) {
STUMBLER_ERR("GetWritePosition failed, skip once");
} else {
WriteJSON(partition);
}
}
sIsAlreadyRunning = false;
return NS_OK;
}
/*
If the upload file exists, then check if it is one day old.
if it is a day old -> ExistsAndReadyToUpload
if it is less than the current day old -> Exists
otherwise -> NoFile
The Exists case means that the upload and the stumbling is rate limited
per-day to the size of the one file.
*/
WriteStumbleOnThread::UploadFileStatus
WriteStumbleOnThread::GetUploadFileStatus()
{
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
int64_t fileSize;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
return UploadFileStatus::NoFile;
}
if (fileSize <= 0) {
tmpFile->Remove(true);
return UploadFileStatus::NoFile;
}
PRTime lastModifiedTime;
tmpFile->GetLastModifiedTime(&lastModifiedTime);
if ((PR_Now() / PR_USEC_PER_MSEC) - lastModifiedTime >= ONEDAY_IN_MSEC) {
return UploadFileStatus::ExistsAndReadyToUpload;
}
return UploadFileStatus::Exists;
}
void
WriteStumbleOnThread::Upload()
{
MOZ_ASSERT(!NS_IsMainThread());
bool b = sIsUploading.exchange(true);
if (b) {
return;
}
time_t seconds = time(0);
int day = seconds / (60 * 60 * 24);
if (sUploadFreqGuard.daySinceEpoch < day) {
sUploadFreqGuard.daySinceEpoch = day;
sUploadFreqGuard.attempts = 0;
}
sUploadFreqGuard.attempts++;
if (sUploadFreqGuard.attempts > MAX_UPLOAD_ATTEMPTS) {
STUMBLER_ERR("Too many upload attempts today");
sIsUploading = false;
return;
}
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile),
OUTPUT_DIR, nsDumpUtils::CREATE);
int64_t fileSize;
rv = tmpFile->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("GetFileSize failed");
sIsUploading = false;
return;
}
if (fileSize <= 0) {
sIsUploading = false;
return;
}
// prepare json into nsIInputStream
nsCOMPtr<nsIInputStream> inStream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile, -1, -1,
nsIFileInputStream::DEFER_OPEN);
if (NS_FAILED(rv)) {
sIsUploading = false;
return;
}
nsCString bufStr;
rv = NS_ReadInputStreamToString(inStream, bufStr, fileSize);
if (NS_FAILED(rv)) {
sIsUploading = false;
return;
}
nsRefPtr<nsIRunnable> uploader = new UploadStumbleRunnable(bufStr);
NS_DispatchToMainThread(uploader);
}

View File

@ -0,0 +1,84 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=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 WriteStumbleOnThread_H
#define WriteStumbleOnThread_H
#include "mozilla/Atomics.h"
/*
This class is the entry point to stumbling, in that it
receives the location+cell+wifi string and writes it
to disk, or instead, it calls UploadStumbleRunnable
to upload the data.
Writes will happen until the file is a max size, then stop.
Uploads will happen only when the file is one day old.
The purpose of these decisions is to have very simple rate-limiting
on the writes, as well as the uploads.
There is only one file active; it is either being used for writing,
or for uploading. If the file is ready for uploading, no further
writes will take place until this file has been uploaded.
This can mean writing might not take place for days until the uploaded
file is processed. This is correct by-design.
A notable limitation is that the upload is triggered by a location event,
this is used as an arbitrary and simple trigger. In future, there are
better events that can be used, such as detecting network activity.
This thread is guarded so that only one instance is active (see the
mozilla::Atomics used for this).
*/
class WriteStumbleOnThread : public nsRunnable
{
public:
explicit WriteStumbleOnThread(const nsCString& aDesc)
: mDesc(aDesc)
{}
NS_IMETHODIMP Run() override;
static void UploadEnded(bool deleteUploadFile);
// Don't write while uploading is happening
static mozilla::Atomic<bool> sIsUploading;
private:
enum class Partition {
Begining,
Middle,
End,
Unknown
};
enum class UploadFileStatus {
NoFile, Exists, ExistsAndReadyToUpload
};
~WriteStumbleOnThread() {}
Partition GetWritePosition();
UploadFileStatus GetUploadFileStatus();
void WriteJSON(Partition aPart);
void Upload();
nsCString mDesc;
// Only run one instance of this
static mozilla::Atomic<bool> sIsAlreadyRunning;
// Limit the upload attempts per day. If the device is rebooted
// this resets the allowed attempts, which is acceptable.
struct UploadFreqGuard {
int attempts;
int daySinceEpoch;
};
static UploadFreqGuard sUploadFreqGuard;
};
#endif

View File

@ -65,7 +65,7 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
Promise* promise = mPromiseProxy->GetWorkerPromise();
Promise* promise = mPromiseProxy->WorkerPromise();
MOZ_ASSERT(promise);
nsTArray<nsRefPtr<ServiceWorkerClient>> ret;
@ -74,30 +74,24 @@ public:
new ServiceWorkerWindowClient(promise->GetParentObject(),
mValue.ElementAt(i))));
}
promise->MaybeResolve(ret);
// release the reference on the worker thread.
mPromiseProxy->CleanUp(aCx);
return true;
}
};
class MatchAllRunnable final : public nsRunnable
{
WorkerPrivate* mWorkerPrivate;
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
nsCString mScope;
public:
MatchAllRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aPromiseProxy,
MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
const nsCString& aScope)
: mWorkerPrivate(aWorkerPrivate),
mPromiseProxy(aPromiseProxy),
: mPromiseProxy(aPromiseProxy),
mScope(aScope)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mPromiseProxy);
}
NS_IMETHOD
@ -105,33 +99,22 @@ public:
{
AssertIsOnMainThread();
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
// Don't resolve the promise if it was already released.
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
nsTArray<ServiceWorkerClientInfo> result;
swm->GetAllClients(mWorkerPrivate->GetPrincipal(), mScope, result);
swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, result);
nsRefPtr<ResolvePromiseWorkerRunnable> r =
new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result);
AutoSafeJSContext cx;
if (r->Dispatch(cx)) {
return NS_OK;
}
// Dispatch to worker thread failed because the worker is shutting down.
// Use a control runnable to release the runnable on the worker thread.
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
new PromiseWorkerProxyControlRunnable(mWorkerPrivate, mPromiseProxy);
if (!releaseRunnable->Dispatch(cx)) {
NS_RUNTIMEABORT("Failed to dispatch MatchAll promise control runnable.");
}
new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy, result);
AutoJSAPI jsapi;
jsapi.Init();
r->Dispatch(jsapi.cx());
return NS_OK;
}
};
@ -158,7 +141,7 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
Promise* promise = mPromiseProxy->GetWorkerPromise();
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
MOZ_ASSERT(promise);
if (NS_SUCCEEDED(mResult)) {
@ -167,9 +150,7 @@ public:
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
}
// Release the reference on the worker thread.
mPromiseProxy->CleanUp(aCx);
return true;
}
};
@ -178,14 +159,14 @@ class ClaimRunnable final : public nsRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
nsCString mScope;
// We grab the ID so we don't have to hold a lock the entire time the claim
// operation is happening on the main thread.
uint64_t mServiceWorkerID;
public:
ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope)
: mPromiseProxy(aPromiseProxy)
, mScope(aScope)
// Safe to call GetWorkerPrivate() since we are being called on the worker
// thread via script (so no clean up has occured yet).
, mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID())
{
MOZ_ASSERT(aPromiseProxy);
@ -194,9 +175,8 @@ public:
NS_IMETHOD
Run() override
{
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
// Don't resolve the promise if it was already released.
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
@ -214,20 +194,7 @@ public:
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
if (r->Dispatch(cx)) {
return NS_OK;
}
// Dispatch to worker thread failed because the worker is shutting down.
// Use a control runnable to release the runnable on the worker thread.
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
if (!releaseRunnable->Dispatch(cx)) {
NS_RUNTIMEABORT("Failed to dispatch Claim control runnable.");
}
r->Dispatch(jsapi.cx());
return NS_OK;
}
};
@ -257,21 +224,15 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
nsRefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (!promiseProxy->GetWorkerPromise()) {
// Don't dispatch if adding the worker feature failed.
if (!promiseProxy) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
}
nsRefPtr<MatchAllRunnable> r =
new MatchAllRunnable(workerPrivate,
promiseProxy,
new MatchAllRunnable(promiseProxy,
NS_ConvertUTF16toUTF8(scope));
nsresult rv = NS_DispatchToMainThread(r);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return promise.forget();
}
@ -301,8 +262,8 @@ ServiceWorkerClients::Claim(ErrorResult& aRv)
nsRefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (!promiseProxy->GetWorkerPromise()) {
// Don't dispatch if adding the worker feature failed.
if (!promiseProxy) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
}
@ -312,10 +273,6 @@ ServiceWorkerClients::Claim(ErrorResult& aRv)
nsRefPtr<ClaimRunnable> runnable =
new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
aRv = NS_DispatchToMainThread(runnable);
if (NS_WARN_IF(aRv.Failed())) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return promise.forget();
}

View File

@ -301,7 +301,7 @@ public:
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
Promise* promise = mPromiseProxy->GetWorkerPromise();
Promise* promise = mPromiseProxy->WorkerPromise();
if (NS_SUCCEEDED(mStatus)) {
promise->MaybeResolve(JS::UndefinedHandleValue);
} else {
@ -318,7 +318,6 @@ class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallbac
~WorkerThreadUpdateCallback()
{
Finish(NS_ERROR_FAILURE);
}
public:
@ -351,8 +350,8 @@ public:
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
MutexAutoLock lock(proxy->GetCleanUpLock());
if (proxy->IsClean()) {
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return;
}
@ -361,11 +360,7 @@ public:
nsRefPtr<UpdateResultRunnable> r =
new UpdateResultRunnable(proxy, aStatus);
if (!r->Dispatch(jsapi.cx())) {
nsRefPtr<PromiseWorkerProxyControlRunnable> r =
new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(), proxy);
r->Dispatch(jsapi.cx());
}
r->Dispatch(jsapi.cx());
}
};
@ -384,8 +379,8 @@ public:
AssertIsOnMainThread();
ErrorResult result;
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
@ -445,10 +440,9 @@ class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
Maybe<bool> mState;
public:
FulfillUnregisterPromiseRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aProxy,
FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy,
Maybe<bool> aState)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
: WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
, mPromiseWorkerProxy(aProxy)
, mState(aState)
{
@ -459,8 +453,7 @@ public:
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
Promise* promise = mPromiseWorkerProxy->GetWorkerPromise();
MOZ_ASSERT(promise);
nsRefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise();
if (mState.isSome()) {
promise->MaybeResolve(mState.value());
} else {
@ -481,6 +474,7 @@ public:
explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
: mPromiseWorkerProxy(aProxy)
{
MOZ_ASSERT(aProxy);
}
NS_IMETHODIMP
@ -501,7 +495,7 @@ public:
private:
~WorkerUnregisterCallback()
{ }
{}
void
Finish(Maybe<bool> aState)
@ -511,24 +505,18 @@ private:
return;
}
MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
if (mPromiseWorkerProxy->IsClean()) {
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget();
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return;
}
nsRefPtr<WorkerRunnable> r =
new FulfillUnregisterPromiseRunnable(mPromiseWorkerProxy->GetWorkerPrivate(),
mPromiseWorkerProxy, aState);
new FulfillUnregisterPromiseRunnable(proxy, aState);
AutoJSAPI jsapi;
jsapi.Init();
if (!r->Dispatch(jsapi.cx())) {
nsRefPtr<WorkerControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(
mPromiseWorkerProxy->GetWorkerPrivate(),
mPromiseWorkerProxy);
cr->Dispatch(jsapi.cx());
}
r->Dispatch(jsapi.cx());
}
};
@ -544,12 +532,12 @@ class StartUnregisterRunnable final : public nsRunnable
const nsString mScope;
public:
StartUnregisterRunnable(WorkerPrivate* aWorker, Promise* aPromise,
StartUnregisterRunnable(PromiseWorkerProxy* aProxy,
const nsAString& aScope)
: mPromiseWorkerProxy(PromiseWorkerProxy::Create(aWorker, aPromise))
: mPromiseWorkerProxy(aProxy)
, mScope(aScope)
{
// mPromiseWorkerProxy may be null if AddFeature failed.
MOZ_ASSERT(aProxy);
}
NS_IMETHOD
@ -557,8 +545,6 @@ public:
{
AssertIsOnMainThread();
nsRefPtr<WorkerUnregisterCallback> cb = new WorkerUnregisterCallback(mPromiseWorkerProxy);
// XXXnsm: There is a rare chance of this failing if the worker gets
// destroyed. In that case, unregister() called from a SW is no longer
// guaranteed to run. We should fix this by having a main thread proxy
@ -566,8 +552,8 @@ public:
// principal. Can that be trusted?
nsCOMPtr<nsIPrincipal> principal;
{
MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
if (mPromiseWorkerProxy->IsClean()) {
MutexAutoLock lock(mPromiseWorkerProxy->Lock());
if (mPromiseWorkerProxy->CleanedUp()) {
return NS_OK;
}
@ -577,6 +563,8 @@ public:
}
MOZ_ASSERT(principal);
nsRefPtr<WorkerUnregisterCallback> cb =
new WorkerUnregisterCallback(mPromiseWorkerProxy);
nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::services::GetServiceWorkerManager();
nsresult rv = swm->Unregister(principal, cb, mScope);
@ -962,8 +950,8 @@ ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
nsRefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
if (!proxy) {
promise->MaybeResolve(NS_ERROR_DOM_ABORT_ERR);
return promise.forget();
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
nsRefPtr<UpdateRunnable> r = new UpdateRunnable(proxy, mScope);
@ -992,7 +980,13 @@ ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
return nullptr;
}
nsRefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(worker, promise, mScope);
nsRefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
if (!proxy) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
nsRefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return promise.forget();

View File

@ -51,7 +51,7 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
Promise* promise = mPromiseProxy->GetWorkerPromise();
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
MOZ_ASSERT(promise);
if (mClientInfo) {
@ -80,7 +80,6 @@ public:
, mPromiseProxy(aPromiseProxy)
{
MOZ_ASSERT(mPromiseProxy);
MOZ_ASSERT(mPromiseProxy->GetWorkerPromise());
}
NS_IMETHOD
@ -110,28 +109,18 @@ private:
DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
{
AssertIsOnMainThread();
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return;
}
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
MOZ_ASSERT(workerPrivate);
nsRefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
new ResolveOrRejectPromiseRunnable(workerPrivate, mPromiseProxy,
Move(aClientInfo));
new ResolveOrRejectPromiseRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy, Move(aClientInfo));
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
if (!resolveRunnable->Dispatch(cx)) {
nsRefPtr<PromiseWorkerProxyControlRunnable> controlRunnable =
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
if (!controlRunnable->Dispatch(cx)) {
NS_RUNTIMEABORT("Failed to dispatch Focus promise control runnable.");
}
}
resolveRunnable->Dispatch(jsapi.cx());
}
};
@ -155,17 +144,14 @@ ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
nsRefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (!promiseProxy->GetWorkerPromise()) {
// Don't dispatch if adding the worker feature failed.
return promise.forget();
if (promiseProxy) {
nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
promiseProxy);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
} else {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
promiseProxy);
aRv = NS_DispatchToMainThread(r);
if (NS_WARN_IF(aRv.Failed())) {
promise->MaybeReject(aRv);
}
} else {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
}

View File

@ -546,9 +546,7 @@ public:
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
Promise* promise = mPromiseProxy->GetWorkerPromise();
MOZ_ASSERT(promise);
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
promise->MaybeResolve(JS::UndefinedHandleValue);
// Release the reference on the worker thread.
@ -579,13 +577,12 @@ public:
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
MOZ_ASSERT(swm);
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
MOZ_ASSERT(workerPrivate);
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope,
workerPrivate->ServiceWorkerID());
@ -594,20 +591,7 @@ public:
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
if (runnable->Dispatch(cx)) {
return NS_OK;
}
// Dispatch to worker thread failed because the worker is shutting down.
// Use a control runnable to release the runnable on the worker thread.
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
if (!releaseRunnable->Dispatch(cx)) {
NS_RUNTIMEABORT("Failed to dispatch Claim control runnable.");
}
runnable->Dispatch(jsapi.cx());
return NS_OK;
}
};
@ -627,8 +611,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
nsRefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(mWorkerPrivate, promise);
if (!promiseProxy->GetWorkerPromise()) {
// Don't dispatch if adding the worker feature failed.
if (!promiseProxy) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
@ -637,11 +620,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
new WorkerScopeSkipWaitingRunnable(promiseProxy,
NS_ConvertUTF16toUTF8(mScope));
aRv = NS_DispatchToMainThread(runnable);
if (NS_WARN_IF(aRv.Failed())) {
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return promise.forget();
}

View File

@ -890,7 +890,7 @@ XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
// This is the right <observes> element. Execute the
// |onbroadcast| event handler
WidgetEvent event(true, NS_XUL_BROADCAST);
WidgetEvent event(true, eXULBroadcast);
nsCOMPtr<nsIPresShell> shell = GetShell();
if (shell) {

View File

@ -397,7 +397,7 @@ nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
}
#endif
WidgetEvent event(true, NS_XUL_COMMAND_UPDATE);
WidgetEvent event(true, eXULCommandUpdate);
EventDispatcher::Dispatch(content, nullptr, &event);
}
return NS_OK;

View File

@ -1267,7 +1267,7 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
(IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner)) &&
(aVisitor.mEvent->mMessage == eMouseClick ||
aVisitor.mEvent->mMessage == eMouseDoubleClick ||
aVisitor.mEvent->mMessage == NS_XUL_COMMAND ||
aVisitor.mEvent->mMessage == eXULCommand ||
aVisitor.mEvent->mMessage == eContextMenu ||
aVisitor.mEvent->mMessage == eDragStart ||
aVisitor.mEvent->mMessage == eLegacyDragGesture)) {
@ -1276,7 +1276,7 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
aVisitor.mParentTarget = nullptr;
return NS_OK;
}
if (aVisitor.mEvent->mMessage == NS_XUL_COMMAND &&
if (aVisitor.mEvent->mMessage == eXULCommand &&
aVisitor.mEvent->mClass == eInputEventClass &&
aVisitor.mEvent->originalTarget == static_cast<nsIContent*>(this) &&
!IsXULElement(nsGkAtoms::command)) {

View File

@ -1548,17 +1548,13 @@ APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const
{
mTreeLock.AssertCurrentThreadOwns();
struct RootForLayersIdMatcher {
uint64_t mLayersId;
bool operator()(const HitTestingTreeNode* aNode) const {
AsyncPanZoomController* apzc = aNode->GetApzc();
return apzc
&& apzc->GetLayersId() == mLayersId
&& apzc->IsRootForLayersId();
}
};
const HitTestingTreeNode* resultNode = BreadthFirstSearch(mRootNode.get(),
RootForLayersIdMatcher{aLayersId});
[aLayersId](const HitTestingTreeNode* aNode) {
AsyncPanZoomController* apzc = aNode->GetApzc();
return apzc
&& apzc->GetLayersId() == aLayersId
&& apzc->IsRootForLayersId();
});
return resultNode ? resultNode->GetApzc() : nullptr;
}
@ -1567,17 +1563,13 @@ APZCTreeManager::FindRootContentApzcForLayersId(uint64_t aLayersId) const
{
mTreeLock.AssertCurrentThreadOwns();
struct RootContentForLayersIdMatcher {
uint64_t mLayersId;
bool operator()(const HitTestingTreeNode* aNode) const {
AsyncPanZoomController* apzc = aNode->GetApzc();
return apzc
&& apzc->GetLayersId() == mLayersId
&& apzc->IsRootContent();
}
};
const HitTestingTreeNode* resultNode = BreadthFirstSearch(mRootNode.get(),
RootContentForLayersIdMatcher{aLayersId});
[aLayersId](const HitTestingTreeNode* aNode) {
AsyncPanZoomController* apzc = aNode->GetApzc();
return apzc
&& apzc->GetLayersId() == aLayersId
&& apzc->IsRootContent();
});
return resultNode ? resultNode->GetApzc() : nullptr;
}

View File

@ -1024,13 +1024,6 @@ HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
return hb_unicode_combining_class_t(GetCombiningClass(aCh));
}
static unsigned int
HBGetEastAsianWidth(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh,
void *user_data)
{
return GetEastAsianWidth(aCh);
}
// Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
// note that some letters do not have a dagesh presForm encoded
static const char16_t sDageshForms[0x05EA - 0x05D0 + 1] = {
@ -1304,9 +1297,6 @@ gfxHarfBuzzShaper::Initialize()
hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
HBGetCombiningClass,
nullptr, nullptr);
hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
HBGetEastAsianWidth,
nullptr, nullptr);
hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
HBUnicodeCompose,
nullptr, nullptr);

View File

@ -25,10 +25,10 @@
#include "imgIScriptedNotificationObserver.h"
#include "gfxPlatform.h"
using namespace mozilla;
using namespace mozilla::image;
using namespace mozilla::gfx;
namespace mozilla {
namespace image {
/* ========== imgITools implementation ========== */
@ -349,3 +349,6 @@ imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(loader, aCache);
}
} // namespace image
} // namespace mozilla

View File

@ -17,6 +17,9 @@
{0xbd, 0xef, 0x2c, 0x7a, 0xe2, 0x49, 0x96, 0x7a} \
}
namespace mozilla {
namespace image {
class imgTools final : public imgITools
{
public:
@ -28,4 +31,8 @@ public:
private:
virtual ~imgTools();
};
} // namespace image
} // namespace mozilla
#endif // mozilla_image_imgITools_h

View File

@ -63,7 +63,7 @@ diff --git a/intl/icu/source/i18n/timezone.cpp b/intl/icu/source/i18n/timezone.c
diff --git a/intl/icu/source/i18n/unicode/timezone.h b/intl/icu/source/i18n/unicode/timezone.h
--- a/intl/icu/source/i18n/unicode/timezone.h
+++ b/intl/icu/source/i18n/unicode/timezone.h
@@ -299,16 +299,18 @@ public:
@@ -299,16 +299,19 @@ public:
* and made the default.
*
* @return A default TimeZone. Clients are responsible for deleting the time zone
@ -72,6 +72,7 @@ diff --git a/intl/icu/source/i18n/unicode/timezone.h b/intl/icu/source/i18n/unic
*/
static TimeZone* U_EXPORT2 createDefault(void);
+#define ICU_TZ_HAS_RECREATE_DEFAULT
+ static void U_EXPORT2 recreateDefault();
+
/**

View File

@ -304,6 +304,7 @@ public:
*/
static TimeZone* U_EXPORT2 createDefault(void);
#define ICU_TZ_HAS_RECREATE_DEFAULT
static void U_EXPORT2 recreateDefault();
/**

View File

@ -954,32 +954,6 @@ SendFunctionsToPerf(JSContext* cx, AsmJSModule& module)
return true;
}
static bool
SendBlocksToPerf(JSContext* cx, AsmJSModule& module)
{
if (!PerfBlockEnabled())
return true;
unsigned long funcBaseAddress = (unsigned long) module.codeBase();
const char* filename = module.scriptSource()->filename();
for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
const AsmJSModule::ProfiledBlocksFunction& func = module.perfProfiledBlocksFunction(i);
size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset;
JSAutoByteString bytes;
const char* name = AtomToPrintableString(cx, func.name, &bytes);
if (!name)
return false;
writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset,
func.endInlineCodeOffset, size, filename, name, func.blocks);
}
return true;
}
#endif
static bool
@ -995,8 +969,6 @@ SendModuleToAttachedProfiler(JSContext* cx, AsmJSModule& module)
size_t firstEntryCode = size_t(module.codeBase() + module.functionBytes());
writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, module.codeBytes() - module.functionBytes());
}
if (!SendBlocksToPerf(cx, module))
return false;
if (!SendFunctionsToPerf(cx, module))
return false;
#endif

View File

@ -151,10 +151,6 @@ AsmJSModule::trace(JSTracer* trc)
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
for (unsigned i = 0; i < profiledFunctions_.length(); i++)
profiledFunctions_[i].trace(trc);
#endif
#if defined(JS_ION_PERF)
for (unsigned i = 0; i < perfProfiledBlocksFunctions_.length(); i++)
perfProfiledBlocksFunctions_[i].trace(trc);
#endif
if (globalArgumentName_)
TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
@ -184,9 +180,6 @@ AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModu
functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
#endif
#if defined(JS_ION_PERF)
perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) +
#endif
staticLinkData_.sizeOfExcludingThis(mallocSizeOf);
}
@ -435,20 +428,6 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
pf.pod.endCodeOffset = masm.actualOffset(pf.pod.endCodeOffset);
}
#endif
#ifdef JS_ION_PERF
for (size_t i = 0; i < perfProfiledBlocksFunctions_.length(); i++) {
ProfiledBlocksFunction& pbf = perfProfiledBlocksFunctions_[i];
pbf.pod.startCodeOffset = masm.actualOffset(pbf.pod.startCodeOffset);
pbf.endInlineCodeOffset = masm.actualOffset(pbf.endInlineCodeOffset);
pbf.pod.endCodeOffset = masm.actualOffset(pbf.pod.endCodeOffset);
BasicBlocksVector& basicBlocks = pbf.blocks;
for (uint32_t i = 0; i < basicBlocks.length(); i++) {
Record& r = basicBlocks[i];
r.startOffset = masm.actualOffset(r.startOffset);
r.endOffset = masm.actualOffset(r.endOffset);
}
}
#endif
return true;
}

View File

@ -862,9 +862,6 @@ class AsmJSModule
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
Vector<ProfiledFunction, 0, SystemAllocPolicy> profiledFunctions_;
#endif
#if defined(JS_ION_PERF)
Vector<ProfiledBlocksFunction, 0, SystemAllocPolicy> perfProfiledBlocksFunctions_;
#endif
ScriptSource * scriptSource_;
PropertyName * globalArgumentName_;
@ -1225,21 +1222,6 @@ class AsmJSModule
return profiledFunctions_[i];
}
#endif
#ifdef JS_ION_PERF
bool addProfiledBlocks(ProfiledBlocksFunction&& func)
{
MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
return perfProfiledBlocksFunctions_.append(mozilla::Move(func));
}
unsigned numPerfBlocksFunctions() const {
MOZ_ASSERT(isFinishedWithModulePrologue());
return perfProfiledBlocksFunctions_.length();
}
ProfiledBlocksFunction& perfProfiledBlocksFunction(unsigned i) {
MOZ_ASSERT(isFinishedWithModulePrologue());
return perfProfiledBlocksFunctions_[i];
}
#endif
/*************************************************************************/

File diff suppressed because it is too large Load Diff

View File

@ -55,6 +55,7 @@
#include "asmjs/AsmJSModule.h"
#include "jit/AtomicOperations.h"
#include "jit/InlinableNatives.h"
#include "js/Class.h"
#include "vm/GlobalObject.h"
#include "vm/SharedTypedArrayObject.h"
@ -1214,20 +1215,20 @@ js::FutexRuntime::wake(WakeReason reason)
}
const JSFunctionSpec AtomicsMethods[] = {
JS_FN("compareExchange", atomics_compareExchange, 4,0),
JS_FN("load", atomics_load, 2,0),
JS_FN("store", atomics_store, 3,0),
JS_FN("exchange", atomics_exchange, 3,0),
JS_FN("fence", atomics_fence, 0,0),
JS_FN("add", atomics_add, 3,0),
JS_FN("sub", atomics_sub, 3,0),
JS_FN("and", atomics_and, 3,0),
JS_FN("or", atomics_or, 3,0),
JS_FN("xor", atomics_xor, 3,0),
JS_FN("isLockFree", atomics_isLockFree, 1,0),
JS_FN("futexWait", atomics_futexWait, 4,0),
JS_FN("futexWake", atomics_futexWake, 3,0),
JS_FN("futexWakeOrRequeue", atomics_futexWakeOrRequeue, 5,0),
JS_INLINABLE_FN("compareExchange", atomics_compareExchange, 4,0, AtomicsCompareExchange),
JS_INLINABLE_FN("load", atomics_load, 2,0, AtomicsLoad),
JS_INLINABLE_FN("store", atomics_store, 3,0, AtomicsStore),
JS_INLINABLE_FN("exchange", atomics_exchange, 3,0, AtomicsExchange),
JS_INLINABLE_FN("fence", atomics_fence, 0,0, AtomicsFence),
JS_INLINABLE_FN("add", atomics_add, 3,0, AtomicsAdd),
JS_INLINABLE_FN("sub", atomics_sub, 3,0, AtomicsSub),
JS_INLINABLE_FN("and", atomics_and, 3,0, AtomicsAnd),
JS_INLINABLE_FN("or", atomics_or, 3,0, AtomicsOr),
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
JS_FN("futexWait", atomics_futexWait, 4,0),
JS_FN("futexWake", atomics_futexWake, 3,0),
JS_FN("futexWakeOrRequeue", atomics_futexWakeOrRequeue, 5,0),
JS_FS_END
};

View File

@ -260,6 +260,7 @@ ModuleObject::create(ExclusiveContext* cx)
void
ModuleObject::init(HandleScript script)
{
MOZ_ASSERT(!script->enclosingStaticScope());
initReservedSlot(ScriptSlot, PrivateValue(script));
}
@ -283,6 +284,14 @@ ModuleObject::initImportExportData(HandleArrayObject requestedModules,
initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
}
bool
ModuleObject::hasScript() const
{
// When modules are parsed via the Reflect.parse() API, the module object
// doesn't have a script.
return !getReservedSlot(ScriptSlot).isUndefined();
}
JSScript*
ModuleObject::script() const
{
@ -295,13 +304,24 @@ ModuleObject::initialEnvironment() const
return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
}
JSObject*
ModuleObject::enclosingStaticScope() const
{
// A ModuleObject is always the last thing on the scope chain before the global.
// TODO: This may no longer be true when we get top-level lexical scopes.
MOZ_ASSERT_IF(hasScript(), !script()->enclosingStaticScope());
return nullptr;
}
/* static */ void
ModuleObject::trace(JSTracer* trc, JSObject* obj)
{
ModuleObject& module = obj->as<ModuleObject>();
JSScript* script = module.script();
TraceManuallyBarrieredEdge(trc, &script, "Module script");
module.setReservedSlot(ScriptSlot, PrivateValue(script));
if (module.hasScript()) {
JSScript* script = module.script();
TraceManuallyBarrieredEdge(trc, &script, "Module script");
module.setReservedSlot(ScriptSlot, PrivateValue(script));
}
}
DEFINE_GETTER_FUNCTIONS(ModuleObject, initialEnvironment, InitialEnvironmentSlot)
@ -485,10 +505,10 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
bool
ModuleBuilder::processExport(frontend::ParseNode* pn)
{
MOZ_ASSERT(pn->isArity(PN_UNARY));
MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
ParseNode* kid = pn->pn_kid;
bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;
ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid;
switch (kid->getKind()) {
case PNK_EXPORT_SPEC_LIST:

View File

@ -105,9 +105,12 @@ class ModuleObject : public NativeObject
ArrayObject& localExportEntries() const;
ArrayObject& indirectExportEntries() const;
ArrayObject& starExportEntries() const;
JSObject* enclosingStaticScope() const;
private:
static void trace(JSTracer* trc, JSObject* obj);
bool hasScript() const;
};
typedef Rooted<ModuleObject*> RootedModuleObject;

View File

@ -33,6 +33,12 @@ using JS::AutoValueArray;
using mozilla::ArrayLength;
using mozilla::DebugOnly;
enum class ParseTarget
{
Script,
Module
};
enum ASTType {
AST_ERROR = -1,
#define ASTDEF(ast, str, method) ast,
@ -2302,12 +2308,13 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst)
MOZ_ASSERT(pn->isKind(PNK_EXPORT) ||
pn->isKind(PNK_EXPORT_FROM) ||
pn->isKind(PNK_EXPORT_DEFAULT));
MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_right->isKind(PNK_STRING));
RootedValue decl(cx, NullValue());
NodeVector elts(cx);
ParseNode* kid = pn->isKind(PNK_EXPORT_FROM) ? pn->pn_left: pn->pn_kid;
ParseNode* kid = pn->isKind(PNK_EXPORT) ? pn->pn_kid : pn->pn_left;
switch (ParseNodeKind kind = kid->getKind()) {
case PNK_EXPORT_SPEC_LIST:
if (!elts.reserve(pn->pn_left->pn_count))
@ -2340,7 +2347,7 @@ ASTSerializer::exportDeclaration(ParseNode* pn, MutableHandleValue dst)
case PNK_CONST:
case PNK_GLOBALCONST:
case PNK_LET:
if (!variableDeclaration(kid, kind == PNK_LET, &decl))
if (!variableDeclaration(kid, (kind == PNK_LET || kind == PNK_CONST), &decl))
return false;
break;
@ -3716,8 +3723,8 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
ScopedJSFreePtr<char> filename;
uint32_t lineno = 1;
bool loc = true;
RootedObject builder(cx);
ParseTarget target = ParseTarget::Script;
RootedValue arg(cx, args.get(1));
@ -3782,6 +3789,36 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
}
builder = &prop.toObject();
}
/* config.target */
RootedId targetId(cx, NameToId(cx->names().target));
RootedValue scriptVal(cx, StringValue(cx->names().script));
if (!GetPropertyDefault(cx, config, targetId, scriptVal, &prop))
return false;
if (!prop.isString()) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
prop, nullptr, "not 'script' or 'module'", nullptr);
return false;
}
RootedString stringProp(cx, prop.toString());
bool isScript = false;
bool isModule = false;
if (!EqualStrings(cx, stringProp, cx->names().script, &isScript))
return false;
if (!EqualStrings(cx, stringProp, cx->names().module, &isModule))
return false;
if (isScript) {
target = ParseTarget::Script;
} else if (isModule) {
target = ParseTarget::Module;
} else {
JS_ReportError(cx, "Bad target value, expected 'script' or 'module'");
return false;
}
}
/* Extract the builder methods first to report errors before parsing. */
@ -3808,9 +3845,23 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
serialize.setParser(&parser);
ParseNode* pn = parser.parse();
if (!pn)
return false;
ParseNode* pn;
if (target == ParseTarget::Script) {
pn = parser.parse();
if (!pn)
return false;
} else {
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
if (!module)
return false;
pn = parser.standaloneModule(module);
if (!pn)
return false;
MOZ_ASSERT(pn->getKind() == PNK_MODULE);
pn = pn->pn_body;
}
RootedValue val(cx);
if (!serialize.program(pn, &val)) {

View File

@ -11,6 +11,7 @@
#include "jscntxt.h"
#include "irregexp/RegExpParser.h"
#include "jit/InlinableNatives.h"
#include "vm/RegExpStatics.h"
#include "vm/StringBuffer.h"
@ -560,8 +561,8 @@ const JSFunctionSpec js::regexp_methods[] = {
#endif
JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
JS_FN("compile", regexp_compile, 2,0),
JS_FN("exec", regexp_exec, 1,0),
JS_FN("test", regexp_test, 1,0),
JS_INLINABLE_FN("exec", regexp_exec, 1,0, RegExpExec),
JS_INLINABLE_FN("test", regexp_test, 1,0, RegExpTest),
JS_FS_END
};

View File

@ -38,7 +38,7 @@ function indirectCallCannotGC(fullCaller, fullVariable)
if (name == "op" && /GetWeakmapKeyDelegate/.test(caller))
return true;
var CheckCallArgs = "AsmJSValidate.cpp:uint8 CheckCallArgs(FunctionBuilder*, js::frontend::ParseNode*, (uint8)(FunctionBuilder*,js::frontend::ParseNode*,Type)*, Signature*)";
var CheckCallArgs = "AsmJSValidate.cpp:uint8 CheckCallArgs(FunctionValidator*, js::frontend::ParseNode*, (uint8)(FunctionValidator*,js::frontend::ParseNode*,Type)*, Signature*)";
if (name == "checkArg" && caller == CheckCallArgs)
return true;

View File

@ -21,6 +21,8 @@ Optimization succeeded.
### Disabled
The optimization has been explicitly disallowed.
### NoTypeInfo
Optimization failed because there was no type information associated with
@ -28,9 +30,16 @@ object containing the property. This failure mode is unlikely, and occurs
if the target object is obtained in some roundabout way.
### NoAnalysisInfo
TODO
### NoShapeInfo
The baseline compiler recorded no usable shape information for this operation.
### UnknownObject
### UnknownProperties
The type of the object is not known. This can happen if the operation sees many different types of objects, and so the type of the input to the operation cannot be resolved to a single type.
### UnknownProperties
@ -40,7 +49,8 @@ defined on the object, or if `delete` is used to remove one of the object's
properties.
### Singleton
### NotSingleton
One of the types present in the typeset was a singleton type, preventing the optimization from being enabled.
### NotSingleton
@ -50,8 +60,13 @@ have a 'singleton' type. Singleton types are assigned to objects that are
global scope, and top-level function objects.
### NotFixedSlot
The property being accessed is not stored at a known location in the object. This can occur if one of the expected types of objects to be used in this operation has unknown properties, or if different instances of the object store the property at different locations (for example, some instances have the property assigned in a different order than others).
### InconsistentFixedSlot
The property being accessed is not stored at a known location in the object. This can occur if the operation is polymorphic on different object types and one or more of the object types contain the property at a different slot than the others.
### NotObject
Optimization failed because the stored in the property could potentially
@ -63,16 +78,28 @@ optimization strategy fails in this case.
The object holding the property is not a typed struct object.
### NotUnboxed
The object whose property is being accessed is not formatted as an
unboxed object.
### UnboxedConvertedToNative
The object whose property is being accessed was previously unboxed,
but was deoptimized and converted to a native object.
### StructNoField
The property being accessed does not correspond to a field on typed
The unboxed property being accessed does not correspond to a field on typed
object.
### InconsistentFieldType
The type of an unboxed field is not consistent across all the different types of objects it could be accessed from.
### InconsistentFieldOffset
The offset of an unboxed field is not consistent across all the different types of objects it could be accessed from.
### NeedsTypeBarrier
Optimization failed because somehow the property was accessed in a way
@ -80,16 +107,49 @@ that returned a different type than the expected constant. This is an
unlikely failure mode, and should not occur.
### InDictionaryMode
The object whose property is being accessed is in dictionary mode. Objects which are used in ways that suggest they are hashtables, are turned into dictionary objects and their types marked as such.
### NoProtoFound
A prototype object was not found for all the object used by this operation.
### MultiProtoPaths
Objects used in this operation had differing prototypes.
### NonWritableProperty
The property being assigned to is not writable for some types of objects which are used in this operation.
### ProtoIndexedProps
The object being accessed has indexed properties that are exotic (for example, defined as a property on a prototype object and left as a hole in the underlying object).
### ArrayBadFlags
The array being accessed may have flags that the optimization strategy cannot handle. For example, if the array has sparse indexes, or has indexes that overflow the array's length, the optimization strategy may fail.
### ArrayDoubleConversion
The type-system indicates that some arrays at this site should be converted to packed arrays of doubles, while others should not. The optimization strategy fails for this condition.
### ArrayRange
Could not accurately calculate the range attributes of an inline array creation.
### ArraySeenNegativeIndex
Arrays at this element access location have seen negative indexes.
### TypedObjectNeutered
The typed object being accessed at this location may have been neutered (a neutered typed object is one where the underlying byte buffer has been removed or transferred to a worker).
### TypedObjectArrayRange
Failed to do range check of element access on a typed object.
### AccessNotDense
### AccessNotSimdObject

View File

@ -7,11 +7,11 @@ and function calls.
Optimization information is currently collected for the following operations:
- [GetProperty][getprop] (`obj.prop`)
- [SetProperty][setprop] (`obj.prop = val`)
- [GetElement][getelem] (`obj[elemName]`)
- [SetElement][setelem] (`obj[elemName] = val`)
- [Call][call] (`func(...)`)
- [GetProperty](#getprop) (`obj.prop`)
- [SetProperty](#setprop) (`obj.prop = val`)
- [GetElement](#getelem) (`obj[elemName]`)
- [SetElement](#setelem) (`obj[elemName] = val`)
- [Call](#call) (`func(...)`)
At each operation site, IonMonkey tries a battery of <i>strategies</i>, from
the most optimized but most restrictive to the least optimized but least
@ -24,7 +24,7 @@ speed-up they provide, what kind of program characteristics can prevent them
from being used, and common ways to enable the engine to utilize that
optimization.
## GetProperty
## <a name="getprop"></a>GetProperty
### GetProperty_ArgumentsLength
@ -78,27 +78,23 @@ after it was first assigned, is likely a constant property. It then directly
inlines the value of the property into hot code that accesses it. For
example, in the following code:
```language-js
var Constants = {};
Constants.N = 100;
var Constants = {};
Constants.N = 100;
function testArray(array) {
for (var i = 0; i < array.length; i++) {
if (array[i] > Constants.N)
return true;
}
return false;
}
```
function testArray(array) {
for (var i = 0; i < array.length; i++) {
if (array[i] > Constants.N)
return true;
}
return false;
}
Will have the loop compiled into the following when `testArray` gets hot.
```language-js
for (var i = 0; i < array.length; i++) {
if (array[i] > 100)
return true;
}
```
for (var i = 0; i < array.length; i++) {
if (array[i] > 100)
return true;
}
When this optimization is successful, property access is eliminated entirely
and replaced with an inline constant.
@ -145,14 +141,12 @@ This is the best case for a regular "field" type property that is not
turned into a constant. It compiles down to a single CPU-level load
instruction.
```language-js
function SomeConstructor() {
this.x = 10; // x is a definite slot property
this.y = 10; // y is a definite slot property
someComplicatedFunctionCall();
this.z = 20; // z is not a definite slot property.
}
```
function SomeConstructor() {
this.x = 10; // x is a definite slot property
this.y = 10; // y is a definite slot property
someComplicatedFunctionCall();
this.z = 20; // z is not a definite slot property.
}
In the above example, the properties `x` and `y` can be determined to always
exist on any instance of `SomeConstructor` at definite locations, allowing
@ -173,12 +167,10 @@ additionally have been observed to only store values of one kind of value.
Consider the following constructor:
```language-js
function Point(x, y) {
this.x = x;
this.y = y;
}
```
function Point(x, y) {
this.x = x;
this.y = y;
}
If only integers are ever stored in the `x` and `y` properties,
then the instances of `Point` will be represented in an "unboxed" mode -
@ -197,18 +189,16 @@ base class, where the property access refers to a getter on the base
class.
Consider the following example:
```language-js
function Base() {}
Base.prototype = {
get x() { return 3; }
};
function Base() {}
Base.prototype = {
get x() { return 3; }
};
function Derived1() {}
Derived1.prototype = Object.create(Base.prototype);
function Derived1() {}
Derived1.prototype = Object.create(Base.prototype);
function Derived2() {}
Derived1.prototype = Object.create(Base.prototype);
```
function Derived2() {}
Derived1.prototype = Object.create(Base.prototype);
If a property access for `d.x` sees only instances of both `Derived1` and
`Derived2` for `d`, it can optimize the access to `x` to a call to the
@ -234,16 +224,14 @@ This optimization compiles down to one-or more shape-guarded direct loads
from the object. The following pseudocode describes the kind of machine
code generated by this optimization:
```
if obj.shape == Shape1 then
obj.slots[0]
elif obj.shape == Shape2 then
obj.slots[5]
elif obj.shape == Shape3 then
obj.slots[2]
...
end
```
if obj.shape == Shape1 then
obj.slots[0]
elif obj.shape == Shape2 then
obj.slots[5]
elif obj.shape == Shape3 then
obj.slots[2]
...
end
### GetProp_Innerize
@ -275,7 +263,7 @@ Inline caches are an order of magnitude slower than the other optimization
strategies, and are an indication that the type inference engine has
failed to collect enough information to guide the optimization process.
## SetProperty
## <a name="setprop"></a>SetProperty
### SetProp_CommonSetter
@ -288,18 +276,16 @@ base class, where the property access refers to a setter on the base
class.
Consider the following example:
```language-js
function Base() {}
Base.prototype = {
set x(val) { ... }
};
function Base() {}
Base.prototype = {
set x(val) { ... }
};
function Derived1() {}
Derived1.prototype = Object.create(Base.prototype);
function Derived1() {}
Derived1.prototype = Object.create(Base.prototype);
function Derived2() {}
Derived1.prototype = Object.create(Base.prototype);
```
function Derived2() {}
Derived1.prototype = Object.create(Base.prototype);
If a property write for `d.x = val` sees only instances of both `Derived1` and
`Derived2` for `d`, it can optimize the write to `x` to a call to the
@ -324,14 +310,12 @@ This is the best case for a regular "field" type property that is not
turned into a constant. It compiles down to a single CPU-level load
instruction.
```language-js
function SomeConstructor() {
this.x = 10; // x is a definite slot property
this.y = 10; // y is a definite slot property
someComplicatedFunctionCall();
this.z = 20; // z is not a definite slot property.
}
```
function SomeConstructor() {
this.x = 10; // x is a definite slot property
this.y = 10; // y is a definite slot property
someComplicatedFunctionCall();
this.z = 20; // z is not a definite slot property.
}
In the above example, the properties `x` and `y` can be determined to always
exist on any instance of `SomeConstructor` at definite locations, allowing
@ -352,12 +336,10 @@ additionally have been observed to only store values of one kind of value.
Consider the following constructor:
```language-js
function Point(x, y) {
this.x = x;
this.y = y;
}
```
function Point(x, y) {
this.x = x;
this.y = y;
}
If only integers are ever stored in the `x` and `y` properties,
then the instances of `Point` will be represented in an "unboxed" mode -
@ -382,16 +364,14 @@ This optimization compiles down to one-or more shape-guarded direct stores
to the object. The following pseudocode describes the kind of machine
code generated by this optimization:
```
if obj.shape == Shape1 then
obj.slots[0] = val
elif obj.shape == Shape2 then
obj.slots[5] = val
elif obj.shape == Shape3 then
obj.slots[2] = val
...
end
```
if obj.shape == Shape1 then
obj.slots[0] = val
elif obj.shape == Shape2 then
obj.slots[5] = val
elif obj.shape == Shape3 then
obj.slots[2] = val
...
end
### SetProp_InlineCache
@ -411,7 +391,7 @@ Inline caches are an order of magnitude slower than the other optimization
strategies, and are an indication that the type inference engine has
failed to collect enough information to guide the optimization process.
## GetElement
## <a name="getelem"></a>GetElement
### GetElem_TypedObject
@ -467,25 +447,21 @@ access of the form `arguments[i]` can be directly translated into a
direct reference to the corresponding argument value in the inlined call.
Consider the following:
```language-js
function foo(arg) {
return bar(arg, 3);
}
function bar() {
return arguments[0] + arguments[1];
}
```
function foo(arg) {
return bar(arg, 3);
}
function bar() {
return arguments[0] + arguments[1];
}
In the above case, if `foo` is compiled with Ion, and the call to `bar`
is inlined, then this optimization can transform the entire procedure to
the following pseudo-code:
```
compiled foo(arg):
// inlined call to bar(arg, 3) {
return arg + 3;
// }
```
compiled foo(arg):
// inlined call to bar(arg, 3) {
return arg + 3;
// }
### GetElem_InlineCache
@ -505,7 +481,7 @@ Inline caches are an order of magnitude slower than the other optimization
strategies, and are an indication that the type inference engine has
failed to collect enough information to guide the optimization process.
## SetElement
## <a name="setelem"></a>SetElement
### SetElem_TypedObject
@ -567,7 +543,7 @@ Inline caches are an order of magnitude slower than the other optimization
strategies, and are an indication that the type inference engine has
failed to collect enough information to guide the optimization process.
## Call
## <a name="call"></a>Call
### Call_Inline

View File

@ -144,6 +144,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
emittingRunOnceLambda(false),
insideEval(insideEval),
insideNonGlobalEval(insideNonGlobalEval),
insideModule(false),
emitterMode(emitterMode)
{
MOZ_ASSERT_IF(evalCaller, insideEval);
@ -359,7 +360,6 @@ BytecodeEmitter::emitDupAt(unsigned slotFromTop)
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
const char js_with_statement_str[] = "with statement";
const char js_finally_block_str[] = "finally block";
const char js_script_str[] = "script";
static const char * const statementName[] = {
"label statement", /* LABEL */
@ -1429,6 +1429,7 @@ BytecodeEmitter::isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn)
case Definition::PLACEHOLDER:
case Definition::NAMED_LAMBDA:
case Definition::MISSING:
case Definition::IMPORT:
MOZ_CRASH("unexpected dn->kind");
}
return false;
@ -1591,6 +1592,11 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn)
if (insideNonGlobalEval)
return false;
// If we are inside a module then unbound names in a function may refer to
// imports, so we can't use GNAME ops here.
if (insideModule)
return false;
// Skip trying to use GNAME ops if we know our script has a non-syntactic
// scope, since they'll just get treated as NAME ops anyway.
if (script->hasNonSyntacticScope())
@ -1842,10 +1848,11 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
}
case Definition::PLACEHOLDER:
case Definition::IMPORT:
return true;
case Definition::MISSING:
MOZ_CRASH("missing");
MOZ_CRASH("unexpected definition kind");
}
// The hop count is the number of dynamic scopes during execution that must
@ -1863,7 +1870,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
* alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
* associated TypeSet.
*/
if (bceOfDef != this && !bceOfDef->sc->isFunctionBox())
if (bceOfDef != this && bceOfDef->sc->isGlobalContext())
return true;
if (!pn->pn_scopecoord.set(parser->tokenStream, hops, slot))
@ -3356,6 +3363,16 @@ BytecodeEmitter::emitYieldOp(JSOp op)
return emit1(JSOP_DEBUGAFTERYIELD);
}
static bool
IsModuleOnScopeChain(JSObject* obj)
{
for (StaticScopeIter<NoGC> ssi(obj); !ssi.done(); ssi++) {
if (ssi.type() == StaticScopeIter<NoGC>::Module)
return true;
}
return false;
}
bool
BytecodeEmitter::emitFunctionScript(ParseNode* body)
{
@ -3375,6 +3392,9 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
// may walk the scope chain of currently compiling scripts.
JSScript::linkToFunctionFromEmitter(cx, script, funbox);
// Determine whether the function is defined inside a module.
insideModule = IsModuleOnScopeChain(sc->staticScope());
if (funbox->argumentsHasLocalBinding()) {
MOZ_ASSERT(offset() == 0); /* See JSScript::argumentsBytecode. */
switchToPrologue();
@ -3485,6 +3505,8 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body)
bool
BytecodeEmitter::emitModuleScript(ParseNode* body)
{
insideModule = true;
if (!updateLocalsToFrameSlots())
return false;
@ -5733,18 +5755,18 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
/*
* Set the EMITTEDFUNCTION flag in function definitions once they have
* been emitted. Function definitions that need hoisting to the top of the
* Set the |wasEmitted| flag in the funbox once the function has been
* emitted. Function definitions that need hoisting to the top of the
* function will be seen by emitFunction in two places.
*/
if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
if (funbox->wasEmitted) {
MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
MOZ_ASSERT(pn->functionIsHoisted());
MOZ_ASSERT(sc->isFunctionBox());
return true;
}
pn->pn_dflags |= PND_EMITTEDFUNCTION;
funbox->wasEmitted = true;
/*
* Mark as singletons any function which will only be executed once, or
@ -7863,12 +7885,9 @@ BytecodeEmitter::emitTree(ParseNode* pn)
case PNK_EXPORT_DEFAULT:
if (!checkIsModule())
return false;
if (pn->pn_kid->isDefn()) {
ok = emitTree(pn->pn_kid);
} else {
// TODO: Emit a definition of *default* from child expression.
ok = true;
}
ok = emitTree(pn->pn_kid);
if (ok && pn->pn_right)
ok = emitLexicalInitialization(pn->pn_right, JSOP_DEFCONST) && emit1(JSOP_POP);
break;
case PNK_EXPORT_FROM:

View File

@ -200,6 +200,8 @@ struct BytecodeEmitter
const bool insideNonGlobalEval:1; /* True if this is a direct eval
call in some non-global scope. */
bool insideModule:1; /* True if compiling inside a module. */
enum EmitterMode {
Normal,

View File

@ -1775,11 +1775,14 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_COMPUTED_NAME:
case PNK_SPREAD:
case PNK_EXPORT:
case PNK_EXPORT_DEFAULT:
case PNK_VOID:
MOZ_ASSERT(pn->isArity(PN_UNARY));
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
case PNK_EXPORT_DEFAULT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_SEMI:
MOZ_ASSERT(pn->isArity(PN_UNARY));
if (ParseNode*& expr = pn->pn_kid)

View File

@ -520,8 +520,9 @@ class FullParseHandler
return pn;
}
ParseNode* newExportDefaultDeclaration(ParseNode* kid, const TokenPos& pos) {
return new_<UnaryNode>(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid);
ParseNode* newExportDefaultDeclaration(ParseNode* kid, ParseNode* maybeBinding,
const TokenPos& pos) {
return new_<BinaryNode>(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid, maybeBinding);
}
ParseNode* newExprStatement(ParseNode* expr, uint32_t end) {

View File

@ -412,7 +412,6 @@ class NameResolver
case PNK_SPREAD:
case PNK_MUTATEPROTO:
case PNK_EXPORT:
case PNK_EXPORT_DEFAULT:
MOZ_ASSERT(cur->isArity(PN_UNARY));
if (!resolve(cur->pn_kid, prefix))
return false;
@ -517,13 +516,15 @@ class NameResolver
case PNK_IMPORT:
case PNK_EXPORT_FROM:
case PNK_EXPORT_DEFAULT:
MOZ_ASSERT(cur->isArity(PN_BINARY));
// The left halves of these nodes don't contain any unconstrained
// expressions, but it's very hard to assert this to safely rely on
// it. So recur anyway.
if (!resolve(cur->pn_left, prefix))
return false;
MOZ_ASSERT(cur->pn_right->isKind(PNK_STRING));
MOZ_ASSERT_IF(!cur->isKind(PNK_EXPORT_DEFAULT),
cur->pn_right->isKind(PNK_STRING));
break;
// Ternary nodes with three expression children.

View File

@ -242,7 +242,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_SPREAD:
case PNK_MUTATEPROTO:
case PNK_EXPORT:
case PNK_EXPORT_DEFAULT:
return PushUnaryNodeChild(pn, stack);
// Nodes with a single nullable child.
@ -367,6 +366,15 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
return PushResult::Recyclable;
}
case PNK_EXPORT_DEFAULT: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT_IF(pn->pn_right, pn->pn_right->isKind(PNK_NAME));
stack->push(pn->pn_left);
if (pn->pn_right)
stack->push(pn->pn_right);
return PushResult::Recyclable;
}
// Ternary nodes with all children non-null.
case PNK_CONDITIONAL: {
MOZ_ASSERT(pn->isArity(PN_TERNARY));
@ -643,11 +651,19 @@ ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, Pars
const char*
Definition::kindString(Kind kind)
{
static const char * const table[] = {
"", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown"
static const char* const table[] = {
"",
js_var_str,
js_const_str,
js_const_str,
js_let_str,
"argument",
js_function_str,
"unknown",
js_import_str
};
MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
MOZ_ASSERT(kind < ArrayLength(table));
return table[kind];
}
@ -1172,6 +1188,7 @@ ObjectBox::trace(JSTracer* trc)
} else if (box->isModuleBox()) {
ModuleBox* modulebox = box->asModuleBox();
modulebox->bindings.trace(trc);
modulebox->exportNames.trace(trc);
}
box = box->traceLink;
}

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