зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
85079555b0
|
@ -26,7 +26,6 @@ const {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm", {})
|
|||
// use to select our configuration.
|
||||
if (gTestPath.includes("test-oop-extensions")) {
|
||||
SpecialPowers.pushPrefEnv({set: [
|
||||
["dom.ipc.processCount.extension", 1],
|
||||
["extensions.webextensions.remote", true],
|
||||
]});
|
||||
// We don't want to reset this at the end of the test, so that we don't have
|
||||
|
|
|
@ -3,6 +3,7 @@ import shutil
|
|||
import time
|
||||
|
||||
from marionette_harness import MarionetteTestCase
|
||||
from marionette_driver.errors import NoAlertPresentException
|
||||
|
||||
|
||||
class TestFirefoxRefresh(MarionetteTestCase):
|
||||
|
@ -269,11 +270,17 @@ class TestFirefoxRefresh(MarionetteTestCase):
|
|||
""")
|
||||
self.assertSequenceEqual(tabURIs, ["about:welcomeback"])
|
||||
|
||||
# Dismiss modal dialog if any. This is mainly to dismiss the check for
|
||||
# default browser dialog if it shows up.
|
||||
try:
|
||||
alert = self.marionette.switch_to_alert()
|
||||
alert.dismiss()
|
||||
except NoAlertPresentException:
|
||||
pass
|
||||
|
||||
tabURIs = self.runAsyncCode("""
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let fs = function() {
|
||||
content.document.getElementById("errorTryAgain").click();
|
||||
};
|
||||
|
||||
let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
|
||||
window.addEventListener("SSWindowStateReady", function testSSPostReset() {
|
||||
window.removeEventListener("SSWindowStateReady", testSSPostReset, false);
|
||||
|
@ -281,6 +288,17 @@ class TestFirefoxRefresh(MarionetteTestCase):
|
|||
marionetteScriptFinished([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
|
||||
});
|
||||
}, false);
|
||||
|
||||
let fs = function() {
|
||||
if (content.document.readyState === "complete") {
|
||||
content.document.getElementById("errorTryAgain").click();
|
||||
} else {
|
||||
content.window.addEventListener("load", function(event) {
|
||||
content.document.getElementById("errorTryAgain").click();
|
||||
}, { once: true });
|
||||
}
|
||||
};
|
||||
|
||||
mm.loadFrameScript("data:application/javascript,(" + fs.toString() + ")()", true);
|
||||
""")
|
||||
self.assertSequenceEqual(tabURIs, self._expectedURLs)
|
||||
|
|
|
@ -8,8 +8,33 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|||
const APP_UPDATE_URL_PREF = "app.update.url";
|
||||
const REPLACE_KEY = "%OS_VERSION%";
|
||||
|
||||
const AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_RESULT_CODE";
|
||||
// The system is not vulnerable to Bug 1296630.
|
||||
const CPU_NO_BUG1296630 = 1;
|
||||
// The system is vulnerable to Bug 1296630.
|
||||
const CPU_YES_BUG1296630 = 2;
|
||||
// An error occured when checking if the system is vulnerable to Bug 1296630.
|
||||
const CPU_ERR_BUG1296630 = 3;
|
||||
// It is unknown whether the system is vulnerable to Bug 1296630 (should never happen).
|
||||
const CPU_UNKNOWN_BUG1296630 = 4;
|
||||
|
||||
const AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_ERROR_CODE";
|
||||
const CPU_SUCCESS = 0;
|
||||
const CPU_REG_OPEN_ERROR = 1;
|
||||
const CPU_VENDOR_ID_ERROR = 2;
|
||||
const CPU_ID_ERROR = 4;
|
||||
const CPU_REV_ERROR = 8;
|
||||
|
||||
const AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME = "aushelper.websense_reg_version";
|
||||
const AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_REG_EXISTS";
|
||||
|
||||
const AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_ERROR_CODE";
|
||||
const WEBSENSE_SUCCESS = 0;
|
||||
const WEBSENSE_REG_OPEN_ERROR = 1;
|
||||
const WEBSENSE_REG_READ_ERROR = 2;
|
||||
const WEBSENSE_ALREADY_MODIFIED = 4;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryLog.jsm");
|
||||
|
||||
function startup() {
|
||||
if (Services.appinfo.OS != "WINNT") {
|
||||
|
@ -18,12 +43,13 @@ function startup() {
|
|||
|
||||
const regCPUPath = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
|
||||
let wrk;
|
||||
let cpuErrorCode = CPU_SUCCESS;
|
||||
try {
|
||||
wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
|
||||
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, regCPUPath, wrk.ACCESS_READ);
|
||||
} catch (e) {
|
||||
Cu.reportError("Unable to open registry. Exception: " + e);
|
||||
TelemetryLog.log("AUSHELPER_FATAL_ERROR", [e]);
|
||||
Cu.reportError("AUSHelper - unable to open registry. Exception: " + e);
|
||||
cpuErrorCode |= CPU_REG_OPEN_ERROR;
|
||||
}
|
||||
|
||||
// If any of the following values are successfully retrieved and they don't
|
||||
|
@ -41,9 +67,9 @@ function startup() {
|
|||
cpuVendorIDMatch = true;
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("AUSHelper - error getting CPU vendor indentifier. Exception: " + e);
|
||||
cpuVendorIDMatch = null;
|
||||
Cu.reportError("Error getting CPU vendor indentifier. Exception: " + e);
|
||||
TelemetryLog.log("AUSHELPER_CPU_VENDOR_ID_ERROR", [e]);
|
||||
cpuErrorCode |= CPU_VENDOR_ID_ERROR;
|
||||
}
|
||||
|
||||
let cpuIDMatch = false;
|
||||
|
@ -53,9 +79,9 @@ function startup() {
|
|||
cpuIDMatch = true;
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("AUSHelper - error getting CPU indentifier. Exception: " + e);
|
||||
cpuIDMatch = null;
|
||||
Cu.reportError("Error getting CPU indentifier. Exception: " + e);
|
||||
TelemetryLog.log("AUSHELPER_CPU_ID_ERROR", [e]);
|
||||
cpuErrorCode |= CPU_ID_ERROR;
|
||||
}
|
||||
|
||||
let microCodeVersions = [0xe, 0x11, 0x12, 0x13, 0x16, 0x18, 0x19];
|
||||
|
@ -67,8 +93,8 @@ function startup() {
|
|||
let regVal = wrk.readBinaryValue(keyNames[i]);
|
||||
if (regVal.length == 8) {
|
||||
let hexVal = [];
|
||||
// We are only inyterested in the highest byte and return the little
|
||||
// endian value for it.
|
||||
// We are only inyterested in the upper 4 bytes and the little endian
|
||||
// value for it.
|
||||
for (let j = 4; j < 8; j++) {
|
||||
let c = regVal.charCodeAt(j).toString(16);
|
||||
if (c.length == 1) {
|
||||
|
@ -86,44 +112,78 @@ function startup() {
|
|||
if (i == keyNames.length - 1) {
|
||||
// The registry key name's value was not successfully queried.
|
||||
cpuRevMatch = null;
|
||||
TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [e]);
|
||||
cpuErrorCode |= CPU_REV_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
wrk.close();
|
||||
} catch (ex) {
|
||||
Cu.reportError("AUSHelper - error getting CPU revision. Exception: " + ex);
|
||||
cpuRevMatch = null;
|
||||
Cu.reportError("Error getting CPU revision. Exception: " + ex);
|
||||
TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [ex]);
|
||||
cpuErrorCode |= CPU_REV_ERROR;
|
||||
}
|
||||
|
||||
let resultCode = 3;
|
||||
let newValue = "(unkBug1296630v1)";
|
||||
let cpuResult = CPU_UNKNOWN_BUG1296630;
|
||||
let cpuValue = "(unkBug1296630v1)";
|
||||
// The following uses strict equality checks since the values can be true,
|
||||
// false, or null.
|
||||
if (cpuVendorIDMatch === false || cpuIDMatch === false || cpuRevMatch === false) {
|
||||
// Since one of the values is false then the system won't be affected by
|
||||
// bug 1296630 according to the conditions set out in bug 1311515.
|
||||
newValue = "(noBug1296630v1)";
|
||||
resultCode = 0;
|
||||
cpuValue = "(noBug1296630v1)";
|
||||
cpuResult = CPU_NO_BUG1296630;
|
||||
} else if (cpuVendorIDMatch === null || cpuIDMatch === null || cpuRevMatch === null) {
|
||||
// Since one of the values is null we can't say for sure if the system will
|
||||
// be affected by bug 1296630.
|
||||
newValue = "(errBug1296630v1)";
|
||||
resultCode = 2;
|
||||
cpuValue = "(errBug1296630v1)";
|
||||
cpuResult = CPU_ERR_BUG1296630;
|
||||
} else if (cpuVendorIDMatch === true && cpuIDMatch === true && cpuRevMatch === true) {
|
||||
// Since all of the values are true we can say that the system will be
|
||||
// affected by bug 1296630.
|
||||
newValue = "(yesBug1296630v1)";
|
||||
resultCode = 1;
|
||||
cpuValue = "(yesBug1296630v1)";
|
||||
cpuResult = CPU_YES_BUG1296630;
|
||||
}
|
||||
|
||||
let defaultBranch = Services.prefs.getDefaultBranch("");
|
||||
let curPrefValue = defaultBranch.getCharPref(APP_UPDATE_URL_PREF);
|
||||
let newPrefValue = curPrefValue.replace(REPLACE_KEY + "/", REPLACE_KEY + newValue + "/");
|
||||
defaultBranch.setCharPref(APP_UPDATE_URL_PREF, newPrefValue);
|
||||
TelemetryLog.log("AUSHELPER_RESULT", [resultCode]);
|
||||
}
|
||||
Services.telemetry.getHistogramById(AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID).add(cpuResult);
|
||||
Services.telemetry.getHistogramById(AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID).add(cpuErrorCode);
|
||||
|
||||
const regWebsensePath = "Websense\\Agent";
|
||||
let websenseErrorCode = WEBSENSE_SUCCESS;
|
||||
let websenseVersion = "";
|
||||
try {
|
||||
let regModes = [wrk.ACCESS_READ, wrk.ACCESS_READ | wrk.WOW64_64];
|
||||
for (let i = 0; i < regModes.length; ++i) {
|
||||
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE", regModes[i]);
|
||||
try {
|
||||
if (wrk.hasChild(regWebsensePath)) {
|
||||
let childKey = wrk.openChild(regWebsensePath, wrk.ACCESS_READ);
|
||||
websenseVersion = childKey.readStringValue("InstallVersion");
|
||||
Services.telemetry.scalarSet(AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME, websenseVersion);
|
||||
}
|
||||
wrk.close();
|
||||
} catch (e) {
|
||||
Cu.reportError("AUSHelper - unable to read registry. Exception: " + e);
|
||||
websenseErrorCode |= WEBSENSE_REG_READ_ERROR;
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError("AUSHelper - unable to open registry. Exception: " + ex);
|
||||
websenseErrorCode |= WEBSENSE_REG_OPEN_ERROR;
|
||||
}
|
||||
|
||||
Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID).add(!!websenseVersion);
|
||||
let websenseValue = "(" + (websenseVersion ? "websense-" + websenseVersion : "nowebsense") + ")";
|
||||
|
||||
let branch = Services.prefs.getDefaultBranch("");
|
||||
let curValue = branch.getCharPref(APP_UPDATE_URL_PREF);
|
||||
if (curValue.indexOf(REPLACE_KEY + "/") > -1) {
|
||||
let newValue = curValue.replace(REPLACE_KEY + "/", REPLACE_KEY + cpuValue + websenseValue + "/");
|
||||
branch.setCharPref(APP_UPDATE_URL_PREF, newValue);
|
||||
} else {
|
||||
websenseErrorCode |= WEBSENSE_ALREADY_MODIFIED;
|
||||
}
|
||||
Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID).add(websenseErrorCode);
|
||||
}
|
||||
function shutdown() {}
|
||||
function install() {}
|
||||
function uninstall() {}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>aushelper@mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:version>2.0</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.7.290
|
||||
Current extension version is: 1.7.297
|
||||
|
|
|
@ -3632,8 +3632,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
|
|||
}
|
||||
};
|
||||
}();
|
||||
exports.version = '1.7.290';
|
||||
exports.build = 'b509a3f8';
|
||||
exports.version = '1.7.297';
|
||||
exports.build = '425ad309';
|
||||
exports.getDocument = getDocument;
|
||||
exports.PDFDataRangeTransport = PDFDataRangeTransport;
|
||||
exports.PDFWorker = PDFWorker;
|
||||
|
@ -4650,8 +4650,8 @@ if (!globalScope.PDFJS) {
|
|||
globalScope.PDFJS = {};
|
||||
}
|
||||
var PDFJS = globalScope.PDFJS;
|
||||
PDFJS.version = '1.7.290';
|
||||
PDFJS.build = 'b509a3f8';
|
||||
PDFJS.version = '1.7.297';
|
||||
PDFJS.build = '425ad309';
|
||||
PDFJS.pdfBug = false;
|
||||
if (PDFJS.verbosity !== undefined) {
|
||||
sharedUtil.setVerbosityLevel(PDFJS.verbosity);
|
||||
|
@ -7143,8 +7143,8 @@ exports.TilingPattern = TilingPattern;
|
|||
|
||||
"use strict";
|
||||
|
||||
var pdfjsVersion = '1.7.290';
|
||||
var pdfjsBuild = 'b509a3f8';
|
||||
var pdfjsVersion = '1.7.297';
|
||||
var pdfjsBuild = '425ad309';
|
||||
var pdfjsSharedUtil = __w_pdfjs_require__(0);
|
||||
var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
|
||||
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
|
||||
|
|
|
@ -88,7 +88,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
|||
/******/ __w_pdfjs_require__.p = "";
|
||||
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __w_pdfjs_require__(__w_pdfjs_require__.s = 36);
|
||||
/******/ return __w_pdfjs_require__(__w_pdfjs_require__.s = 35);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
|
@ -1732,8 +1732,8 @@ exports.isStream = isStream;
|
|||
|
||||
var sharedUtil = __w_pdfjs_require__(0);
|
||||
var corePrimitives = __w_pdfjs_require__(1);
|
||||
var coreJbig2 = __w_pdfjs_require__(28);
|
||||
var coreJpg = __w_pdfjs_require__(29);
|
||||
var coreJbig2 = __w_pdfjs_require__(27);
|
||||
var coreJpg = __w_pdfjs_require__(28);
|
||||
var coreJpx = __w_pdfjs_require__(13);
|
||||
var Util = sharedUtil.Util;
|
||||
var error = sharedUtil.error;
|
||||
|
@ -12357,7 +12357,7 @@ exports.Parser = Parser;
|
|||
|
||||
var sharedUtil = __w_pdfjs_require__(0);
|
||||
var corePrimitives = __w_pdfjs_require__(1);
|
||||
var corePsParser = __w_pdfjs_require__(34);
|
||||
var corePsParser = __w_pdfjs_require__(33);
|
||||
var error = sharedUtil.error;
|
||||
var info = sharedUtil.info;
|
||||
var isArray = sharedUtil.isArray;
|
||||
|
@ -18279,7 +18279,7 @@ exports.ArithmeticDecoder = ArithmeticDecoder;
|
|||
"use strict";
|
||||
|
||||
var sharedUtil = __w_pdfjs_require__(0);
|
||||
var coreCharsets = __w_pdfjs_require__(22);
|
||||
var coreCharsets = __w_pdfjs_require__(21);
|
||||
var coreEncodings = __w_pdfjs_require__(4);
|
||||
var error = sharedUtil.error;
|
||||
var info = sharedUtil.info;
|
||||
|
@ -24768,15 +24768,15 @@ var sharedUtil = __w_pdfjs_require__(0);
|
|||
var corePrimitives = __w_pdfjs_require__(1);
|
||||
var coreStream = __w_pdfjs_require__(2);
|
||||
var coreParser = __w_pdfjs_require__(5);
|
||||
var coreImage = __w_pdfjs_require__(27);
|
||||
var coreImage = __w_pdfjs_require__(26);
|
||||
var coreColorSpace = __w_pdfjs_require__(3);
|
||||
var coreMurmurHash3 = __w_pdfjs_require__(31);
|
||||
var coreFonts = __w_pdfjs_require__(26);
|
||||
var coreMurmurHash3 = __w_pdfjs_require__(30);
|
||||
var coreFonts = __w_pdfjs_require__(25);
|
||||
var coreFunction = __w_pdfjs_require__(6);
|
||||
var corePattern = __w_pdfjs_require__(32);
|
||||
var coreCMap = __w_pdfjs_require__(23);
|
||||
var coreMetrics = __w_pdfjs_require__(30);
|
||||
var coreBidi = __w_pdfjs_require__(21);
|
||||
var corePattern = __w_pdfjs_require__(31);
|
||||
var coreCMap = __w_pdfjs_require__(22);
|
||||
var coreMetrics = __w_pdfjs_require__(29);
|
||||
var coreBidi = __w_pdfjs_require__(20);
|
||||
var coreEncodings = __w_pdfjs_require__(4);
|
||||
var coreStandardFonts = __w_pdfjs_require__(15);
|
||||
var coreUnicode = __w_pdfjs_require__(16);
|
||||
|
@ -33906,10 +33906,10 @@ exports.getUnicodeForGlyph = getUnicodeForGlyph;
|
|||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
/* WEBPACK VAR INJECTION */(function(module) {
|
||||
|
||||
var sharedUtil = __w_pdfjs_require__(0);
|
||||
var corePrimitives = __w_pdfjs_require__(1);
|
||||
var corePdfManager = __w_pdfjs_require__(33);
|
||||
var corePdfManager = __w_pdfjs_require__(32);
|
||||
var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
|
||||
var InvalidPDFException = sharedUtil.InvalidPDFException;
|
||||
var MessageHandler = sharedUtil.MessageHandler;
|
||||
|
@ -34588,13 +34588,18 @@ function initializeWorker() {
|
|||
WorkerMessageHandler.setup(handler, self);
|
||||
handler.send('ready', null);
|
||||
}
|
||||
if (typeof window === 'undefined' && !(typeof module !== 'undefined' && module.require)) {
|
||||
function isNodeJS() {
|
||||
if (typeof __pdfjsdev_webpack__ === 'undefined') {
|
||||
return typeof process === 'object' && process + '' === '[object process]';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (typeof window === 'undefined' && !isNodeJS()) {
|
||||
initializeWorker();
|
||||
}
|
||||
exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
|
||||
exports.WorkerTask = WorkerTask;
|
||||
exports.WorkerMessageHandler = WorkerMessageHandler;
|
||||
/* WEBPACK VAR INJECTION */}.call(exports, __w_pdfjs_require__(19)(module)))
|
||||
|
||||
/***/ }),
|
||||
/* 18 */
|
||||
|
@ -34614,34 +34619,6 @@ module.exports = g;
|
|||
|
||||
/***/ }),
|
||||
/* 19 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = function (module) {
|
||||
if (!module.webpackPolyfill) {
|
||||
module.deprecate = function () {
|
||||
};
|
||||
module.paths = [];
|
||||
if (!module.children)
|
||||
module.children = [];
|
||||
Object.defineProperty(module, "loaded", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return module.l;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(module, "id", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return module.i;
|
||||
}
|
||||
});
|
||||
module.webpackPolyfill = 1;
|
||||
}
|
||||
return module;
|
||||
};
|
||||
|
||||
/***/ }),
|
||||
/* 20 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -34667,6 +34644,7 @@ var Dict = corePrimitives.Dict;
|
|||
var isDict = corePrimitives.isDict;
|
||||
var isName = corePrimitives.isName;
|
||||
var isRef = corePrimitives.isRef;
|
||||
var isStream = corePrimitives.isStream;
|
||||
var Stream = coreStream.Stream;
|
||||
var ColorSpace = coreColorSpace.ColorSpace;
|
||||
var Catalog = coreObj.Catalog;
|
||||
|
@ -34760,30 +34738,13 @@ var Annotation = function AnnotationClosure() {
|
|||
rect[1] - minY * yRatio
|
||||
];
|
||||
}
|
||||
function getDefaultAppearance(dict) {
|
||||
var appearanceState = dict.get('AP');
|
||||
if (!isDict(appearanceState)) {
|
||||
return;
|
||||
}
|
||||
var appearance;
|
||||
var appearances = appearanceState.get('N');
|
||||
if (isDict(appearances)) {
|
||||
var as = dict.get('AS');
|
||||
if (as && appearances.has(as.name)) {
|
||||
appearance = appearances.get(as.name);
|
||||
}
|
||||
} else {
|
||||
appearance = appearances;
|
||||
}
|
||||
return appearance;
|
||||
}
|
||||
function Annotation(params) {
|
||||
var dict = params.dict;
|
||||
this.setFlags(dict.get('F'));
|
||||
this.setRectangle(dict.getArray('Rect'));
|
||||
this.setColor(dict.getArray('C'));
|
||||
this.setBorderStyle(dict);
|
||||
this.appearance = getDefaultAppearance(dict);
|
||||
this.setAppearance(dict);
|
||||
this.data = {};
|
||||
this.data.id = params.id;
|
||||
this.data.subtype = params.subtype;
|
||||
|
@ -34887,6 +34848,26 @@ var Annotation = function AnnotationClosure() {
|
|||
this.borderStyle.setWidth(0);
|
||||
}
|
||||
},
|
||||
setAppearance: function Annotation_setAppearance(dict) {
|
||||
this.appearance = null;
|
||||
var appearanceStates = dict.get('AP');
|
||||
if (!isDict(appearanceStates)) {
|
||||
return;
|
||||
}
|
||||
var normalAppearanceState = appearanceStates.get('N');
|
||||
if (isStream(normalAppearanceState)) {
|
||||
this.appearance = normalAppearanceState;
|
||||
return;
|
||||
}
|
||||
if (!isDict(normalAppearanceState)) {
|
||||
return;
|
||||
}
|
||||
var as = dict.get('AS');
|
||||
if (!isName(as) || !normalAppearanceState.has(as.name)) {
|
||||
return;
|
||||
}
|
||||
this.appearance = normalAppearanceState.get(as.name);
|
||||
},
|
||||
_preparePopup: function Annotation_preparePopup(dict) {
|
||||
if (!dict.has('C')) {
|
||||
this.data.color = null;
|
||||
|
@ -35332,7 +35313,7 @@ exports.AnnotationBorderStyle = AnnotationBorderStyle;
|
|||
exports.AnnotationFactory = AnnotationFactory;
|
||||
|
||||
/***/ }),
|
||||
/* 21 */
|
||||
/* 20 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -36085,7 +36066,7 @@ function bidi(str, startLevel, vertical) {
|
|||
exports.bidi = bidi;
|
||||
|
||||
/***/ }),
|
||||
/* 22 */
|
||||
/* 21 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -36583,7 +36564,7 @@ exports.ExpertCharset = ExpertCharset;
|
|||
exports.ExpertSubsetCharset = ExpertSubsetCharset;
|
||||
|
||||
/***/ }),
|
||||
/* 23 */
|
||||
/* 22 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -37488,7 +37469,7 @@ exports.CMapFactory = CMapFactory;
|
|||
exports.IdentityCMap = IdentityCMap;
|
||||
|
||||
/***/ }),
|
||||
/* 24 */
|
||||
/* 23 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -37500,7 +37481,7 @@ var coreObj = __w_pdfjs_require__(14);
|
|||
var coreParser = __w_pdfjs_require__(5);
|
||||
var coreCrypto = __w_pdfjs_require__(11);
|
||||
var coreEvaluator = __w_pdfjs_require__(12);
|
||||
var coreAnnotation = __w_pdfjs_require__(20);
|
||||
var coreAnnotation = __w_pdfjs_require__(19);
|
||||
var MissingDataException = sharedUtil.MissingDataException;
|
||||
var Util = sharedUtil.Util;
|
||||
var assert = sharedUtil.assert;
|
||||
|
@ -37988,7 +37969,7 @@ exports.Page = Page;
|
|||
exports.PDFDocument = PDFDocument;
|
||||
|
||||
/***/ }),
|
||||
/* 25 */
|
||||
/* 24 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -38774,7 +38755,7 @@ var FontRendererFactory = function FontRendererFactoryClosure() {
|
|||
exports.FontRendererFactory = FontRendererFactory;
|
||||
|
||||
/***/ }),
|
||||
/* 26 */
|
||||
/* 25 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -38783,11 +38764,11 @@ var sharedUtil = __w_pdfjs_require__(0);
|
|||
var corePrimitives = __w_pdfjs_require__(1);
|
||||
var coreStream = __w_pdfjs_require__(2);
|
||||
var coreGlyphList = __w_pdfjs_require__(7);
|
||||
var coreFontRenderer = __w_pdfjs_require__(25);
|
||||
var coreFontRenderer = __w_pdfjs_require__(24);
|
||||
var coreEncodings = __w_pdfjs_require__(4);
|
||||
var coreStandardFonts = __w_pdfjs_require__(15);
|
||||
var coreUnicode = __w_pdfjs_require__(16);
|
||||
var coreType1Parser = __w_pdfjs_require__(35);
|
||||
var coreType1Parser = __w_pdfjs_require__(34);
|
||||
var coreCFFParser = __w_pdfjs_require__(9);
|
||||
var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
|
||||
var FontType = sharedUtil.FontType;
|
||||
|
@ -40003,7 +39984,7 @@ var Font = function FontClosure() {
|
|||
useTable = true;
|
||||
} else if (platformId === 1 && encodingId === 0) {
|
||||
useTable = true;
|
||||
} else if (platformId === 3 && encodingId === 1 && (!isSymbolicFont && hasEncoding || !potentialTable)) {
|
||||
} else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {
|
||||
useTable = true;
|
||||
if (!isSymbolicFont) {
|
||||
canBreak = true;
|
||||
|
@ -41740,7 +41721,7 @@ exports.ToUnicodeMap = ToUnicodeMap;
|
|||
exports.getFontType = getFontType;
|
||||
|
||||
/***/ }),
|
||||
/* 27 */
|
||||
/* 26 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -42231,7 +42212,7 @@ var PDFImage = function PDFImageClosure() {
|
|||
exports.PDFImage = PDFImage;
|
||||
|
||||
/***/ }),
|
||||
/* 28 */
|
||||
/* 27 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -43432,7 +43413,7 @@ var Jbig2Image = function Jbig2ImageClosure() {
|
|||
exports.Jbig2Image = Jbig2Image;
|
||||
|
||||
/***/ }),
|
||||
/* 29 */
|
||||
/* 28 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -44349,7 +44330,7 @@ var JpegImage = function JpegImageClosure() {
|
|||
exports.JpegImage = JpegImage;
|
||||
|
||||
/***/ }),
|
||||
/* 30 */
|
||||
/* 29 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -47297,7 +47278,7 @@ var getMetrics = getLookupTableFactory(function (t) {
|
|||
exports.getMetrics = getMetrics;
|
||||
|
||||
/***/ }),
|
||||
/* 31 */
|
||||
/* 30 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -47418,7 +47399,7 @@ var MurmurHash3_64 = function MurmurHash3_64Closure(seed) {
|
|||
exports.MurmurHash3_64 = MurmurHash3_64;
|
||||
|
||||
/***/ }),
|
||||
/* 32 */
|
||||
/* 31 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -48257,7 +48238,7 @@ exports.Pattern = Pattern;
|
|||
exports.getTilingPatternIR = getTilingPatternIR;
|
||||
|
||||
/***/ }),
|
||||
/* 33 */
|
||||
/* 32 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -48265,7 +48246,7 @@ exports.getTilingPatternIR = getTilingPatternIR;
|
|||
var sharedUtil = __w_pdfjs_require__(0);
|
||||
var coreStream = __w_pdfjs_require__(2);
|
||||
var coreChunkedStream = __w_pdfjs_require__(10);
|
||||
var coreDocument = __w_pdfjs_require__(24);
|
||||
var coreDocument = __w_pdfjs_require__(23);
|
||||
var warn = sharedUtil.warn;
|
||||
var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl;
|
||||
var shadow = sharedUtil.shadow;
|
||||
|
@ -48443,7 +48424,7 @@ exports.LocalPdfManager = LocalPdfManager;
|
|||
exports.NetworkPdfManager = NetworkPdfManager;
|
||||
|
||||
/***/ }),
|
||||
/* 34 */
|
||||
/* 33 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -48642,7 +48623,7 @@ exports.PostScriptLexer = PostScriptLexer;
|
|||
exports.PostScriptParser = PostScriptParser;
|
||||
|
||||
/***/ }),
|
||||
/* 35 */
|
||||
/* 34 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
@ -49188,13 +49169,13 @@ var Type1Parser = function Type1ParserClosure() {
|
|||
exports.Type1Parser = Type1Parser;
|
||||
|
||||
/***/ }),
|
||||
/* 36 */
|
||||
/* 35 */
|
||||
/***/ (function(module, exports, __w_pdfjs_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var pdfjsVersion = '1.7.290';
|
||||
var pdfjsBuild = 'b509a3f8';
|
||||
var pdfjsVersion = '1.7.297';
|
||||
var pdfjsBuild = '425ad309';
|
||||
var pdfjsCoreWorker = __w_pdfjs_require__(17);
|
||||
;
|
||||
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"llvm_revision": "292415",
|
||||
"llvm_revision": "295482",
|
||||
"stages": "1",
|
||||
"build_libcxx": true,
|
||||
"build_type": "Release",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"llvm_revision": "292415",
|
||||
"llvm_revision": "295482",
|
||||
"stages": "1",
|
||||
"build_libcxx": true,
|
||||
"build_type": "Release",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"llvm_revision": "292415",
|
||||
"llvm_revision": "295482",
|
||||
"stages": "1",
|
||||
"build_libcxx": false,
|
||||
"build_type": "Release",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"llvm_revision": "292415",
|
||||
"llvm_revision": "295482",
|
||||
"stages": "1",
|
||||
"build_libcxx": false,
|
||||
"build_type": "Release",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
// Tests the various highlight command parameters and options
|
||||
|
||||
requestLongerTimeout(3);
|
||||
requestLongerTimeout(4);
|
||||
|
||||
// Creating a test page with many elements to test the --showall option
|
||||
var TEST_PAGE = "data:text/html;charset=utf-8,<body><ul>";
|
||||
|
|
|
@ -1094,50 +1094,9 @@ Inspector.prototype = {
|
|||
type: "separator",
|
||||
}));
|
||||
|
||||
let copySubmenu = new Menu();
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyinner",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyInnerHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyouter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyOuterHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyuniqueselector",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
hidden: !this.canGetUniqueSelector,
|
||||
click: () => this.copyUniqueSelector(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copycsspath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
hidden: !this.canGetCssPath,
|
||||
click: () => this.copyCssPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyimagedatauri",
|
||||
label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
|
||||
disabled: !isSelectionElement || !markupContainer ||
|
||||
!markupContainer.isPreviewable(),
|
||||
click: () => this.copyImageDataUri(),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyHTMLSubmenu.label"),
|
||||
submenu: copySubmenu,
|
||||
submenu: this._getCopySubmenu(markupContainer, isSelectionElement),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
|
@ -1210,6 +1169,51 @@ Inspector.prototype = {
|
|||
return menu;
|
||||
},
|
||||
|
||||
_getCopySubmenu: function (markupContainer, isSelectionElement) {
|
||||
let copySubmenu = new Menu();
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyinner",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyInnerHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyouter",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
click: () => this.copyOuterHTML(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyuniqueselector",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
hidden: !this.canGetUniqueSelector,
|
||||
click: () => this.copyUniqueSelector(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copycsspath",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
|
||||
disabled: !isSelectionElement,
|
||||
hidden: !this.canGetCssPath,
|
||||
click: () => this.copyCssPath(),
|
||||
}));
|
||||
copySubmenu.append(new MenuItem({
|
||||
id: "node-menu-copyimagedatauri",
|
||||
label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
|
||||
disabled: !isSelectionElement || !markupContainer ||
|
||||
!markupContainer.isPreviewable(),
|
||||
click: () => this.copyImageDataUri(),
|
||||
}));
|
||||
|
||||
return copySubmenu;
|
||||
},
|
||||
|
||||
_getPasteSubmenu: function (isEditableElement) {
|
||||
let isPasteable = isEditableElement && this._getClipboardContentForPaste();
|
||||
let disableAdjacentPaste = !isPasteable ||
|
||||
|
@ -1283,6 +1287,14 @@ Inspector.prototype = {
|
|||
disabled: !isEditableElement,
|
||||
click: () => this.onAddAttribute(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-copy-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
|
||||
isAttributeClicked ? `"${nodeInfo.value}"` : ""),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this.onCopyAttributeValue(),
|
||||
}));
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-edit-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
|
||||
|
@ -1291,13 +1303,11 @@ Inspector.prototype = {
|
|||
disabled: !isAttributeClicked,
|
||||
click: () => this.onEditAttribute(),
|
||||
}));
|
||||
|
||||
attributesSubmenu.append(new MenuItem({
|
||||
id: "node-menu-remove-attribute",
|
||||
label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
|
||||
isAttributeClicked ? `"${nodeInfo.name}"` : ""),
|
||||
accesskey:
|
||||
INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
|
||||
accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
|
||||
disabled: !isAttributeClicked,
|
||||
click: () => this.onRemoveAttribute(),
|
||||
}));
|
||||
|
@ -1817,6 +1827,14 @@ Inspector.prototype = {
|
|||
container.addAttribute();
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy attribute value for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
*/
|
||||
onCopyAttributeValue: function () {
|
||||
clipboardHelper.copyString(this.nodeMenuTriggerInfo.value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit attribute for node.
|
||||
* Used for node context menu and shouldn't be called directly.
|
||||
|
|
|
@ -215,6 +215,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
|||
[browser_rules_selector-highlighter_04.js]
|
||||
[browser_rules_selector-highlighter_05.js]
|
||||
[browser_rules_selector_highlight.js]
|
||||
[browser_rules_shorthand-overridden-lists.js]
|
||||
[browser_rules_strict-search-filter-computed-list_01.js]
|
||||
[browser_rules_strict-search-filter_01.js]
|
||||
[browser_rules_strict-search-filter_02.js]
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that the rule view shorthand overridden list works correctly,
|
||||
// can be shown and hidden correctly, and contain the right subproperties.
|
||||
|
||||
var TEST_URI = `
|
||||
<style type="text/css">
|
||||
div {
|
||||
margin: 0px 1px 2px 3px;
|
||||
top: 0px;
|
||||
}
|
||||
#testid {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
<div id="testid">Styled Node</div>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("#testid", inspector);
|
||||
yield testComputedList(inspector, view);
|
||||
});
|
||||
|
||||
function* testComputedList(inspector, view) {
|
||||
let rule = getRuleViewRuleEditor(view, 2).rule;
|
||||
let propEditor = rule.textProps[0].editor;
|
||||
let expander = propEditor.expander;
|
||||
let overriddenItems = propEditor.shorthandOverridden.children;
|
||||
let propNames = [
|
||||
"margin-right",
|
||||
"margin-left"
|
||||
];
|
||||
|
||||
ok(!expander.hasAttribute("open"), "margin computed list is closed.");
|
||||
ok(!propEditor.shorthandOverridden.hasAttribute("hidden"),
|
||||
"The shorthandOverridden list should be open.");
|
||||
|
||||
is(overriddenItems.length, propNames.length,
|
||||
"There should be 2 overridden shorthand value.");
|
||||
for (let i = 0; i < propNames.length; i++) {
|
||||
let overriddenItem = overriddenItems[i].querySelector(".ruleview-propertyname");
|
||||
is(overriddenItem.textContent, propNames[i],
|
||||
"The overridden item #" + i + " should be " + propNames[i]);
|
||||
}
|
||||
|
||||
info("Opening the computed list of margin property.");
|
||||
expander.click();
|
||||
ok(expander.hasAttribute("open"), "margin computed list is open.");
|
||||
ok(propEditor.shorthandOverridden.hasAttribute("hidden"),
|
||||
"The shorthandOverridden list should be hidden.");
|
||||
|
||||
info("Closing the computed list of margin property.");
|
||||
expander.click();
|
||||
ok(!expander.hasAttribute("open"), "margin computed list is closed.");
|
||||
ok(!propEditor.shorthandOverridden.hasAttribute("hidden"),
|
||||
"The shorthandOverridden list should be open.");
|
||||
|
||||
for (let i = 0; i < propNames.length; i++) {
|
||||
let overriddenItem = overriddenItems[i].querySelector(".ruleview-propertyname");
|
||||
is(overriddenItem.textContent, propNames[i],
|
||||
"The overridden item #" + i + " should still be " + propNames[i]);
|
||||
}
|
||||
}
|
|
@ -197,6 +197,12 @@ TextPropertyEditor.prototype = {
|
|||
class: "ruleview-computedlist",
|
||||
});
|
||||
|
||||
// Holds the viewers for the overridden shorthand properties.
|
||||
// will be populated in |_updateShorthandOverridden|.
|
||||
this.shorthandOverridden = createChild(this.element, "ul", {
|
||||
class: "ruleview-overridden-items",
|
||||
});
|
||||
|
||||
// Only bind event handlers if the rule is editable.
|
||||
if (this.ruleEditor.isEditable) {
|
||||
this.enable.addEventListener("click", this._onEnableClicked, true);
|
||||
|
@ -454,8 +460,9 @@ TextPropertyEditor.prototype = {
|
|||
elToClick.click();
|
||||
}
|
||||
|
||||
// Populate the computed styles.
|
||||
// Populate the computed styles and shorthand overridden styles.
|
||||
this._updateComputed();
|
||||
this._updateShorthandOverridden();
|
||||
|
||||
// Update the rule property highlight.
|
||||
this.ruleView._updatePropertyHighlight(this);
|
||||
|
@ -469,7 +476,7 @@ TextPropertyEditor.prototype = {
|
|||
|
||||
/**
|
||||
* Update the visibility of the enable checkbox, the warning indicator and
|
||||
* the filter property, as well as the overriden state of the property.
|
||||
* the filter property, as well as the overridden state of the property.
|
||||
*/
|
||||
updatePropertyState: function () {
|
||||
if (this.prop.enabled) {
|
||||
|
@ -527,45 +534,86 @@ TextPropertyEditor.prototype = {
|
|||
continue;
|
||||
}
|
||||
|
||||
let li = createChild(this.computed, "li", {
|
||||
class: "ruleview-computed"
|
||||
});
|
||||
|
||||
if (computed.overridden) {
|
||||
li.classList.add("ruleview-overridden");
|
||||
}
|
||||
|
||||
createChild(li, "span", {
|
||||
class: "ruleview-propertyname theme-fg-color5",
|
||||
textContent: computed.name
|
||||
});
|
||||
appendText(li, ": ");
|
||||
|
||||
let outputParser = this.ruleView._outputParser;
|
||||
let frag = outputParser.parseCssProperty(
|
||||
computed.name, computed.value, {
|
||||
colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
|
||||
urlClass: "theme-link",
|
||||
baseURI: this.sheetHref
|
||||
}
|
||||
);
|
||||
|
||||
// Store the computed property value that was parsed for output
|
||||
computed.parsedValue = frag.textContent;
|
||||
|
||||
createChild(li, "span", {
|
||||
class: "ruleview-propertyvalue theme-fg-color1",
|
||||
child: frag
|
||||
});
|
||||
|
||||
appendText(li, ";");
|
||||
|
||||
// Store the computed style element for easy access when highlighting
|
||||
// styles
|
||||
computed.element = li;
|
||||
computed.element = this._createComputedListItem(this.computed, computed,
|
||||
"ruleview-computed");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the indicator for overridden shorthand styles. The shorthand
|
||||
* overridden styles themselves are populated on demand, when they
|
||||
* become visible.
|
||||
*/
|
||||
_updateShorthandOverridden: function () {
|
||||
this.shorthandOverridden.innerHTML = "";
|
||||
|
||||
this._populatedShorthandOverridden = false;
|
||||
this._populateShorthandOverridden();
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate the list of overridden shorthand styles.
|
||||
*/
|
||||
_populateShorthandOverridden: function () {
|
||||
if (this._populatedShorthandOverridden || this.prop.overridden) {
|
||||
return;
|
||||
}
|
||||
this._populatedShorthandOverridden = true;
|
||||
|
||||
for (let computed of this.prop.computed) {
|
||||
// Don't display duplicate information or show properties
|
||||
// that are completely overridden.
|
||||
if (computed.name === this.prop.name || !computed.overridden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this._createComputedListItem(this.shorthandOverridden, computed,
|
||||
"ruleview-overridden-item");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and populates a list item with the computed CSS property.
|
||||
*/
|
||||
_createComputedListItem: function (parentEl, computed, className) {
|
||||
let li = createChild(parentEl, "li", {
|
||||
class: className
|
||||
});
|
||||
|
||||
if (computed.overridden) {
|
||||
li.classList.add("ruleview-overridden");
|
||||
}
|
||||
|
||||
createChild(li, "span", {
|
||||
class: "ruleview-propertyname theme-fg-color5",
|
||||
textContent: computed.name
|
||||
});
|
||||
appendText(li, ": ");
|
||||
|
||||
let outputParser = this.ruleView._outputParser;
|
||||
let frag = outputParser.parseCssProperty(
|
||||
computed.name, computed.value, {
|
||||
colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
|
||||
urlClass: "theme-link",
|
||||
baseURI: this.sheetHref
|
||||
}
|
||||
);
|
||||
|
||||
// Store the computed property value that was parsed for output
|
||||
computed.parsedValue = frag.textContent;
|
||||
|
||||
createChild(li, "span", {
|
||||
class: "ruleview-propertyvalue theme-fg-color1",
|
||||
child: frag
|
||||
});
|
||||
|
||||
appendText(li, ";");
|
||||
|
||||
return li;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles clicks on the disabled property.
|
||||
*/
|
||||
|
@ -593,9 +641,12 @@ TextPropertyEditor.prototype = {
|
|||
this.expander.removeAttribute("open");
|
||||
this.computed.removeAttribute("filter-open");
|
||||
this.computed.removeAttribute("user-open");
|
||||
this.shorthandOverridden.removeAttribute("hidden");
|
||||
this._populateShorthandOverridden();
|
||||
} else {
|
||||
this.expander.setAttribute("open", "true");
|
||||
this.computed.setAttribute("user-open", "");
|
||||
this.shorthandOverridden.setAttribute("hidden", "true");
|
||||
this._populateComputed();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ const ALL_MENU_ITEMS = [
|
|||
"node-menu-scrollnodeintoview",
|
||||
"node-menu-screenshotnode",
|
||||
"node-menu-add-attribute",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS);
|
||||
|
@ -70,6 +71,7 @@ const TEST_CASES = [
|
|||
clipboardDataType: "html",
|
||||
disabled: [
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
],
|
||||
|
@ -86,6 +88,7 @@ const TEST_CASES = [
|
|||
"node-menu-pasteafter",
|
||||
"node-menu-pastefirstchild",
|
||||
"node-menu-pastelastchild",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
],
|
||||
|
@ -99,6 +102,7 @@ const TEST_CASES = [
|
|||
"node-menu-copyimagedatauri",
|
||||
"node-menu-pastebefore",
|
||||
"node-menu-pasteafter",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]
|
||||
|
@ -109,6 +113,7 @@ const TEST_CASES = [
|
|||
clipboardDataType: "html",
|
||||
selector: "img",
|
||||
disabled: [
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]
|
||||
|
@ -123,6 +128,7 @@ const TEST_CASES = [
|
|||
"node-menu-pastebefore",
|
||||
"node-menu-pasteafter",
|
||||
"node-menu-screenshotnode",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
],
|
||||
|
@ -133,6 +139,7 @@ const TEST_CASES = [
|
|||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-screenshotnode",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
@ -144,6 +151,7 @@ const TEST_CASES = [
|
|||
selector: "#paste-area",
|
||||
disabled: [
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]
|
||||
|
@ -157,6 +165,7 @@ const TEST_CASES = [
|
|||
selector: "#paste-area",
|
||||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
@ -168,6 +177,7 @@ const TEST_CASES = [
|
|||
selector: "#paste-area",
|
||||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
@ -179,6 +189,7 @@ const TEST_CASES = [
|
|||
selector: "#paste-area",
|
||||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
@ -189,6 +200,7 @@ const TEST_CASES = [
|
|||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-screenshotnode",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
@ -199,6 +211,7 @@ const TEST_CASES = [
|
|||
disabled: PASTE_MENU_ITEMS.concat([
|
||||
"node-menu-copyimagedatauri",
|
||||
"node-menu-screenshotnode",
|
||||
"node-menu-copy-attribute",
|
||||
"node-menu-edit-attribute",
|
||||
"node-menu-remove-attribute"
|
||||
]),
|
||||
|
|
|
@ -12,6 +12,7 @@ add_task(function* () {
|
|||
yield selectNode("#attributes", inspector);
|
||||
|
||||
yield testAddAttribute();
|
||||
yield testCopyAttributeValue();
|
||||
yield testEditAttribute();
|
||||
yield testRemoveAttribute();
|
||||
|
||||
|
@ -29,6 +30,20 @@ add_task(function* () {
|
|||
ok(hasAttribute, "attribute was successfully added");
|
||||
}
|
||||
|
||||
function* testCopyAttributeValue() {
|
||||
info("Testing 'Copy Attribute Value' and waiting for clipboard promise to resolve");
|
||||
let copyAttributeValue = getMenuItem("node-menu-copy-attribute");
|
||||
|
||||
info("Triggering 'Copy Attribute Value' and waiting for clipboard to copy the value");
|
||||
inspector.nodeMenuTriggerInfo = {
|
||||
type: "attribute",
|
||||
name: "data-edit",
|
||||
value: "the"
|
||||
};
|
||||
|
||||
yield waitForClipboardPromise(() => copyAttributeValue.click(), "the");
|
||||
}
|
||||
|
||||
function* testEditAttribute() {
|
||||
info("Testing 'Edit Attribute' menu item");
|
||||
let editAttribute = getMenuItem("node-menu-edit-attribute");
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
<p id="console-var">Paragraph for testing console variables</p>
|
||||
<p id="console-var-multi">Paragraph for testing multiple console variables</p>
|
||||
<p id="attributes" data-edit="original" data-remove="thing">Attributes are going to be changed here</p>
|
||||
<p id="attributes" data-copy="the" data-edit="original" data-remove="thing">Attributes are going to be changed here</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -117,6 +117,13 @@ inspectorEditAttribute.accesskey=E
|
|||
inspectorRemoveAttribute.label=Remove Attribute %S
|
||||
inspectorRemoveAttribute.accesskey=R
|
||||
|
||||
# LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a
|
||||
# sub-menu "Attribute" in the inspector contextual-menu that appears
|
||||
# when the user right-clicks on the attribute of a node in the inspector,
|
||||
# and that allows to copy the attribute value to clipboard.
|
||||
inspectorCopyAttributeValue.label=Copy Attribute Value %S
|
||||
inspectorCopyAttributeValue.accesskey=V
|
||||
|
||||
# LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):
|
||||
# This string is displayed in a tooltip that is shown when hovering over a DOM
|
||||
# node preview (e.g. something like "div#foo.bar").
|
||||
|
|
|
@ -123,13 +123,15 @@
|
|||
}
|
||||
|
||||
.ruleview-computedlist,
|
||||
.ruleview-overridden-items[hidden],
|
||||
.ruleview-overridden-rule-filter[hidden],
|
||||
.ruleview-warning[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ruleview-computedlist[user-open],
|
||||
.ruleview-computedlist[filter-open] {
|
||||
.ruleview-computedlist[filter-open],
|
||||
.ruleview-overridden-items {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
@ -362,6 +364,42 @@
|
|||
margin-inline-start: 35px;
|
||||
}
|
||||
|
||||
.ruleview-overridden-items {
|
||||
margin: 0px 0px 0px 5px;
|
||||
list-style: none;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.ruleview-overridden-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ruleview-overridden-item::before {
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
top: 0px;
|
||||
content: '';
|
||||
display: block;
|
||||
border-left: 1px solid var(--theme-highlight-gray);
|
||||
height: 0.7em;
|
||||
border-bottom: 1px solid var(--theme-highlight-gray);
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.ruleview-overridden-item::after {
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
bottom: -7px;
|
||||
content: '';
|
||||
display: block;
|
||||
border-left: 1px solid var(--theme-highlight-gray);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ruleview-overridden-item:last-child:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ruleview-grid,
|
||||
.ruleview-swatch {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -353,7 +353,7 @@ DragDataProducer::GetNodeString(nsIContent* inNode,
|
|||
|
||||
// use a range to get the text-equivalent of the node
|
||||
nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
|
||||
mozilla::ErrorResult rv;
|
||||
mozilla::IgnoredErrorResult rv;
|
||||
RefPtr<nsRange> range = doc->CreateRange(rv);
|
||||
if (range) {
|
||||
range->SelectNode(*node, rv);
|
||||
|
|
|
@ -7967,13 +7967,8 @@ nsDocument::FlushPendingNotifications(FlushType aType)
|
|||
mParentDocument->FlushPendingNotifications(parentType);
|
||||
}
|
||||
|
||||
// Call nsIPresShell::NeedFlush (inline, non-virtual) to check whether we
|
||||
// really need to flush the shell (virtual, and needs a strong reference).
|
||||
if (nsIPresShell* shell = GetShell()) {
|
||||
if (shell->NeedFlush(aType)) {
|
||||
nsCOMPtr<nsIPresShell> presShell = shell;
|
||||
presShell->FlushPendingNotifications(aType);
|
||||
}
|
||||
shell->FlushPendingNotifications(aType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2179,7 +2179,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
|
||||
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
|
||||
|
@ -2260,7 +2259,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
|
||||
|
@ -3240,7 +3238,11 @@ nsGlobalWindow::PreloadLocalStorage()
|
|||
// private browsing windows do not persist local storage to disk so we should
|
||||
// only try to precache storage when we're not a private browsing window.
|
||||
if (principal->GetPrivateBrowsingId() == 0) {
|
||||
storageManager->PrecacheStorage(principal);
|
||||
nsCOMPtr<nsIDOMStorage> storage;
|
||||
rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mLocalStorage = static_cast<Storage*>(storage.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11906,49 +11908,66 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool isPrivateBrowsing = IsPrivateBrowsing();
|
||||
if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
|
||||
(!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
|
||||
if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
|
||||
// We need these for our private-browsing check below; so save them off.
|
||||
// (And we can't do the private-browsing enforcement check as part of our
|
||||
// outer conditional because otherwise we'll trigger an NS_WARNING if control
|
||||
// flow reaches the bottom of this method.)
|
||||
bool isNonPrivateLocalStorageChange =
|
||||
!nsCRT::strcmp(aTopic, "dom-storage2-changed");
|
||||
bool isPrivateLocalStorageChange =
|
||||
!nsCRT::strcmp(aTopic, "dom-private-storage2-changed");
|
||||
if (isNonPrivateLocalStorageChange || isPrivateLocalStorageChange) {
|
||||
// Enforce that the source storage area's private browsing state matches
|
||||
// this window's state. These flag checks and their maintenance independent
|
||||
// from the principal's OriginAttributes matter because chrome docshells
|
||||
// that are part of private browsing windows can be private browsing without
|
||||
// having their OriginAttributes set (because they have the system
|
||||
// principal).
|
||||
bool isPrivateBrowsing = IsPrivateBrowsing();
|
||||
if ((isNonPrivateLocalStorageChange && isPrivateBrowsing) ||
|
||||
(isPrivateLocalStorageChange && !isPrivateBrowsing)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIPrincipal *principal;
|
||||
nsresult rv;
|
||||
// We require that aData be either u"SessionStorage" or u"localStorage".
|
||||
// Assert under debug, but ignore the bogus event under non-debug.
|
||||
MOZ_ASSERT(aData);
|
||||
if (!aData) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// LocalStorage can only exist on an inner window, and we don't want to
|
||||
// generate events on frozen or otherwise-navigated-away from windows.
|
||||
// (Actually, this code used to try and buffer events for frozen windows,
|
||||
// but it never worked, so we've removed it. See bug 1285898.)
|
||||
if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow() || IsFrozen()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
if (!principal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
|
||||
if (!event) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<Storage> changingStorage = event->GetStorageArea();
|
||||
if (!changingStorage) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
|
||||
|
||||
bool fireMozStorageChanged = false;
|
||||
nsAutoString eventType;
|
||||
eventType.AssignLiteral("storage");
|
||||
principal = GetPrincipal();
|
||||
if (!principal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (!NS_strcmp(aData, u"sessionStorage")) {
|
||||
nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
|
||||
MOZ_ASSERT(changingStorage);
|
||||
|
||||
switch (changingStorage->GetType())
|
||||
{
|
||||
case Storage::SessionStorage:
|
||||
{
|
||||
bool check = false;
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
|
||||
if (storageManager) {
|
||||
rv = storageManager->CheckStorage(principal, istorage, &check);
|
||||
nsresult rv = storageManager->CheckStorage(principal, changingStorage,
|
||||
&check);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -11969,58 +11988,48 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
if (fireMozStorageChanged) {
|
||||
eventType.AssignLiteral("MozSessionStorageChanged");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Storage::LocalStorage:
|
||||
{
|
||||
// Allow event fire only for the same principal storages
|
||||
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
|
||||
nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
|
||||
else {
|
||||
MOZ_ASSERT(!NS_strcmp(aData, u"localStorage"));
|
||||
nsIPrincipal* storagePrincipal = event->GetPrincipal();
|
||||
if (!storagePrincipal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool equals = false;
|
||||
rv = storagePrincipal->Equals(principal, &equals);
|
||||
nsresult rv = storagePrincipal->Equals(principal, &equals);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!equals)
|
||||
if (!equals) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
fireMozStorageChanged = mLocalStorage == event->GetStorageArea();
|
||||
|
||||
fireMozStorageChanged = mLocalStorage == changingStorage;
|
||||
if (fireMozStorageChanged) {
|
||||
eventType.AssignLiteral("MozLocalStorageChanged");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clone the storage event included in the observer notification. We want
|
||||
// to dispatch clones rather than the original event.
|
||||
ErrorResult error;
|
||||
RefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType, event, error);
|
||||
RefPtr<StorageEvent> clonedEvent =
|
||||
CloneStorageEvent(eventType, event, error);
|
||||
if (error.Failed()) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
newEvent->SetTrusted(true);
|
||||
clonedEvent->SetTrusted(true);
|
||||
|
||||
if (fireMozStorageChanged) {
|
||||
WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
|
||||
WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
|
||||
internalEvent->mFlags.mOnlyChromeDispatch = true;
|
||||
}
|
||||
|
||||
if (IsFrozen()) {
|
||||
// This window is frozen, rather than firing the events here,
|
||||
// store the domain in which the change happened and fire the
|
||||
// events if we're ever thawed.
|
||||
|
||||
mPendingStorageEvents.AppendElement(newEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool defaultActionEnabled;
|
||||
DispatchEvent(newEvent, &defaultActionEnabled);
|
||||
DispatchEvent(clonedEvent, &defaultActionEnabled);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -12111,10 +12120,19 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
|
|||
aEvent->GetUrl(dict.mUrl);
|
||||
|
||||
RefPtr<Storage> storageArea = aEvent->GetStorageArea();
|
||||
MOZ_ASSERT(storageArea);
|
||||
|
||||
RefPtr<Storage> storage;
|
||||
if (storageArea->GetType() == Storage::LocalStorage) {
|
||||
|
||||
// If null, this is a localStorage event received by IPC.
|
||||
if (!storageArea) {
|
||||
storage = GetLocalStorage(aRv);
|
||||
if (aRv.Failed() || !storage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We must apply the current change to the 'local' localStorage.
|
||||
storage->ApplyEvent(aEvent);
|
||||
} else if (storageArea->GetType() == Storage::LocalStorage) {
|
||||
storage = GetLocalStorage(aRv);
|
||||
} else {
|
||||
MOZ_ASSERT(storageArea->GetType() == Storage::SessionStorage);
|
||||
|
@ -12126,7 +12144,7 @@ nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
|
|||
}
|
||||
|
||||
MOZ_ASSERT(storage);
|
||||
MOZ_ASSERT(storage->IsForkOf(storageArea));
|
||||
MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
|
||||
|
||||
dict.mStorageArea = storage;
|
||||
|
||||
|
@ -12419,11 +12437,6 @@ nsGlobalWindow::FireDelayedDOMEvents()
|
|||
{
|
||||
FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
|
||||
|
||||
for (uint32_t i = 0, len = mPendingStorageEvents.Length(); i < len; ++i) {
|
||||
Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
|
||||
Observe(mPendingStorageEvents[i], "dom-private-storage2-changed", nullptr);
|
||||
}
|
||||
|
||||
if (mApplicationCache) {
|
||||
static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
|
||||
}
|
||||
|
@ -13293,6 +13306,13 @@ nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
|
|||
aType == nsGkAtoms::onvrdisplaypresentchange) {
|
||||
NotifyVREventListenerAdded();
|
||||
}
|
||||
|
||||
// We need to initialize localStorage in order to receive notifications.
|
||||
if (aType == nsGkAtoms::onstorage) {
|
||||
ErrorResult rv;
|
||||
GetLocalStorage(rv);
|
||||
rv.SuppressException();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1946,9 +1946,6 @@ protected:
|
|||
// These member variables are used on both inner and the outer windows.
|
||||
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
|
||||
|
||||
typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsStorageEventArray;
|
||||
nsStorageEventArray mPendingStorageEvents;
|
||||
|
||||
uint32_t mSuspendDepth;
|
||||
uint32_t mFreezeDepth;
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
[DEFAULT]
|
||||
skip-if = os == 'android'
|
||||
support-files = nonchrome_webgl_debug_renderer_info.html
|
||||
|
||||
[test_webgl_debug_renderer_info.html]
|
||||
subsuite = gpu
|
||||
[test_drawWindow_widget_layers.html]
|
||||
skip-if = os == 'android'
|
||||
support-files = ../file_drawWindow_source.html ../file_drawWindow_common.js
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<script>
|
||||
|
||||
|
||||
// This file has the portion of the test_webgl_renderer_info chrome mochitest
|
||||
// that has to run as non-chrome to check that this WebGL extension is not exposed to content
|
||||
|
||||
// we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
|
||||
// So we remote it to the chrome test.
|
||||
|
||||
function ok(res, msg) {
|
||||
// Note we post to ourselves as posting to the chrome code doesn't seem to work here.
|
||||
// This works by having the chrome code put an event handler on our own window.
|
||||
window.postMessage({ subTestFinished: true, result: res, message: msg }, "*");
|
||||
}
|
||||
|
||||
function messageListener(e) {
|
||||
// This is how the chrome test tells us to start running -- we have to wait for this
|
||||
// message to avoid running before it's set up its event handler.
|
||||
if (e.data.run) {
|
||||
var canBeUnprivileged = e.data.canBeUnprivileged;
|
||||
run(canBeUnprivileged);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", messageListener, true);
|
||||
|
||||
function run(canBeUnprivileged) {
|
||||
const UNMASKED_VENDOR_WEBGL = 0x9245;
|
||||
const UNMASKED_RENDERER_WEBGL = 0x9246;
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var gl = canvas.getContext("experimental-webgl");
|
||||
|
||||
ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
|
||||
|
||||
ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the"
|
||||
+ " WEBGL_debug_renderer_info extension");
|
||||
ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the"
|
||||
+ " WEBGL_debug_renderer_info extension");
|
||||
|
||||
var exts = gl.getSupportedExtensions();
|
||||
if (canBeUnprivileged) {
|
||||
ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
|
||||
"WEBGL_debug_renderer_info should be listed by getSupportedExtensions in"
|
||||
+ " non-chrome contexts on non-RELEASE_OR_BETAs");
|
||||
|
||||
var ext = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
ok(!!ext,
|
||||
"WEBGL_debug_renderer_info should be available through getExtension in non-chrome"
|
||||
+ " contexts on non-RELEASE_OR_BETAs");
|
||||
|
||||
ok(gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_VENDOR_WEBGL if enabling"
|
||||
+ " WEBGL_debug_renderer_info succeeded");
|
||||
ok(gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_RENDERER_WEBGL if enabling"
|
||||
+ " WEBGL_debug_renderer_info succeeded");
|
||||
} else {
|
||||
ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
|
||||
"WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in"
|
||||
+ " non-chrome contexts");
|
||||
var ext = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
ok(!ext,
|
||||
"WEBGL_debug_renderer_info should not be available through getExtension in"
|
||||
+ " non-chrome contexts");
|
||||
|
||||
ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_VENDOR_WEBGL if enabling"
|
||||
+ " WEBGL_debug_renderer_info failed");
|
||||
ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_RENDERER_WEBGL if enabling"
|
||||
+ " WEBGL_debug_renderer_info failed");
|
||||
|
||||
}
|
||||
window.postMessage({allTestsFinished: true}, "*");
|
||||
}
|
||||
|
||||
</script>
|
||||
</html>
|
|
@ -4,99 +4,48 @@
|
|||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for WEBGL_debug_renderer_info chrome-only extension</title>
|
||||
<title>Test that WEBGL_debug_renderer_info works in chrome code</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<pre id="test">
|
||||
<script>
|
||||
|
||||
const UNMASKED_VENDOR_WEBGL = 0x9245;
|
||||
const UNMASKED_RENDERER_WEBGL = 0x9246;
|
||||
|
||||
|
||||
var Cu = parent.Components.utils;
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
// This gives us `AppConstants` in the global scope.
|
||||
// We need this because we only expose debug_renderer_info #ifndef RELEASE_OR_BETA.
|
||||
// This should match AppConstants.RELEASE_OR_BETA.
|
||||
|
||||
const canBeUnprivileged = !AppConstants.RELEASE_OR_BETA;
|
||||
|
||||
|
||||
function isNonEmptyString(s)
|
||||
{
|
||||
function isNonEmptyString(s) {
|
||||
return s && (typeof s) == "string";
|
||||
}
|
||||
|
||||
function messageListener(e) {
|
||||
if (e.data.allTestsFinished) {
|
||||
SimpleTest.finish();
|
||||
} else if (e.data.subTestFinished) {
|
||||
ok(e.data.result, "content iframe: " + e.data.message);
|
||||
}
|
||||
}
|
||||
var canvas = document.createElement("canvas");
|
||||
var gl = canvas.getContext("experimental-webgl");
|
||||
ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
|
||||
|
||||
function checkChromeCase(canvas) {
|
||||
ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
|
||||
ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
|
||||
|
||||
var gl = canvas.getContext("experimental-webgl");
|
||||
ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
|
||||
var exts = gl.getSupportedExtensions();
|
||||
ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
|
||||
"WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
|
||||
var ext = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
ok(ext,
|
||||
"WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
|
||||
|
||||
ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
|
||||
ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
|
||||
"Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
|
||||
ok(ext.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
|
||||
"UNMASKED_VENDOR_WEBGL has the correct value");
|
||||
ok(ext.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
|
||||
"UNMASKED_RENDERER_WEBGL has the correct value");
|
||||
|
||||
var exts = gl.getSupportedExtensions();
|
||||
ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
|
||||
"WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
|
||||
var ext = gl.getExtension("WEBGL_debug_renderer_info");
|
||||
ok(ext,
|
||||
"WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
|
||||
ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
|
||||
ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
|
||||
|
||||
ok(ext.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
|
||||
"UNMASKED_VENDOR_WEBGL has the correct value");
|
||||
ok(ext.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
|
||||
"UNMASKED_RENDERER_WEBGL has the correct value");
|
||||
|
||||
ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
|
||||
ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
|
||||
"Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
checkChromeCase(document.createElement("canvas"));
|
||||
|
||||
// Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "http://mochi.test:8888/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
|
||||
|
||||
iframe.onload = function () {
|
||||
|
||||
// test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
|
||||
// this is useful to check in itself, and is also useful so the subsequent non-chrome test
|
||||
// will also test that doing so doesn't confuse our chrome-only check.
|
||||
checkChromeCase(iframe.contentDocument.createElement("canvas"));
|
||||
|
||||
iframe.contentWindow.addEventListener("message", messageListener);
|
||||
iframe.contentWindow.postMessage({run: true,
|
||||
canBeUnprivileged: canBeUnprivileged},
|
||||
"*");
|
||||
};
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -31,7 +31,7 @@ var defaultExts = [
|
|||
['OES_texture_half_float_linear' , [ENSURE , FORBID ]],
|
||||
['OES_vertex_array_object' , [ENSURE , FORBID ]],
|
||||
['WEBGL_compressed_texture_s3tc' , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
|
||||
// ['WEBGL_debug_renderer_info' , [FORBID , FORBID ]], // Complicated!
|
||||
['WEBGL_debug_renderer_info' , [ENSURE , ENSURE ]],
|
||||
['WEBGL_debug_shaders' , [FORBID , FORBID ]],
|
||||
['WEBGL_depth_texture' , [MACHINE_SPECIFIC, FORBID ]],
|
||||
['WEBGL_draw_buffers' , [MACHINE_SPECIFIC, FORBID ]],
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -34,6 +36,7 @@ protected:
|
|||
nsString mNewValue;
|
||||
nsString mUrl;
|
||||
RefPtr<Storage> mStorageArea;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
public:
|
||||
virtual StorageEvent* AsStorageEvent();
|
||||
|
@ -79,6 +82,18 @@ public:
|
|||
{
|
||||
return mStorageArea;
|
||||
}
|
||||
|
||||
// Non WebIDL methods
|
||||
void SetPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
MOZ_ASSERT(!mPrincipal);
|
||||
mPrincipal = aPrincipal;
|
||||
}
|
||||
|
||||
nsIPrincipal* GetPrincipal() const
|
||||
{
|
||||
return mPrincipal;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -293,12 +293,37 @@ HTMLTrackElement::LoadResource()
|
|||
mChannel = nullptr;
|
||||
}
|
||||
|
||||
// According to https://www.w3.org/TR/html5/embedded-content-0.html#sourcing-out-of-band-text-tracks
|
||||
//
|
||||
// "8: If the track element's parent is a media element then let CORS mode
|
||||
// be the state of the parent media element's crossorigin content attribute.
|
||||
// Otherwise, let CORS mode be No CORS."
|
||||
//
|
||||
CORSMode corsMode = mMediaParent ? mMediaParent->GetCORSMode() : CORS_NONE;
|
||||
|
||||
// Determine the security flag based on corsMode.
|
||||
nsSecurityFlags secFlags;
|
||||
if (CORS_NONE == corsMode) {
|
||||
// Same-origin is required for track element.
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
|
||||
} else {
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
|
||||
if (CORS_ANONYMOUS == corsMode) {
|
||||
secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
|
||||
} else if (CORS_USE_CREDENTIALS == corsMode) {
|
||||
secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
|
||||
} else {
|
||||
NS_WARNING("Unknown CORS mode.");
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
uri,
|
||||
static_cast<Element*>(this),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
|
||||
secFlags,
|
||||
nsIContentPolicy::TYPE_INTERNAL_TRACK,
|
||||
loadGroup,
|
||||
nullptr, // aCallbacks
|
||||
|
@ -313,7 +338,11 @@ HTMLTrackElement::LoadResource()
|
|||
|
||||
LOG(LogLevel::Debug, ("opening webvtt channel"));
|
||||
rv = channel->AsyncOpen2(mListener);
|
||||
NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
SetReadyState(TextTrackReadyState::FailedToLoad);
|
||||
return;
|
||||
}
|
||||
|
||||
mChannel = channel;
|
||||
}
|
||||
|
|
|
@ -51,9 +51,9 @@
|
|||
let url =
|
||||
URL.createObjectURL(new Blob(["(", dummyWorkerScript.toSource(), ")()"]));
|
||||
|
||||
let worker = new Worker(url);
|
||||
let worker1 = new Worker(url);
|
||||
try {
|
||||
worker.postMessage(mutableFile);
|
||||
worker1.postMessage(mutableFile);
|
||||
ok(false, "Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -61,7 +61,6 @@
|
|||
is(e.name, "DataCloneError", "Good error.");
|
||||
is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.")
|
||||
}
|
||||
worker.terminate();
|
||||
|
||||
mutableFile.onerror = errorHandler;
|
||||
|
||||
|
@ -77,10 +76,10 @@
|
|||
|
||||
let file = event.target.result;
|
||||
|
||||
worker = new Worker(url);
|
||||
let worker2 = new Worker(url);
|
||||
URL.revokeObjectURL(url);
|
||||
try {
|
||||
worker.postMessage(file);
|
||||
worker2.postMessage(file);
|
||||
ok(false, "Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -88,7 +87,6 @@
|
|||
is(e.name, "DataCloneError", "Good error.");
|
||||
is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.")
|
||||
}
|
||||
worker.terminate();
|
||||
|
||||
let objectStore =
|
||||
db.transaction("Foo", "readwrite").objectStore("Foo");
|
||||
|
@ -126,14 +124,18 @@
|
|||
|
||||
url = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
|
||||
|
||||
worker = new Worker(url);
|
||||
let worker3 = new Worker(url);
|
||||
URL.revokeObjectURL(url);
|
||||
worker.postMessage(name);
|
||||
worker.onmessage = grabEventAndContinueHandler;
|
||||
worker3.postMessage(name);
|
||||
worker3.onmessage = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.data, "success", "Good response.");
|
||||
worker.terminate();
|
||||
|
||||
todo(false, "Terminate all workers at the end of the test to work around bug 1340941.");
|
||||
worker1.terminate();
|
||||
worker2.terminate();
|
||||
worker3.terminate();
|
||||
|
||||
finishTest();
|
||||
}
|
||||
|
|
|
@ -20,8 +20,15 @@ interface nsIDOMStorageManager : nsISupports
|
|||
/**
|
||||
* This starts async preloading of a storage cache for scope
|
||||
* defined by the principal.
|
||||
*
|
||||
* Because of how multi-e10s support was implemented in bug 1285898, the
|
||||
* StorageCache instance can no longer use a timer to keep itself alive. So a
|
||||
* Storage instance is returned if precaching believes the principal may have
|
||||
* localStorage data. (Previously the StorageCache would be brought into
|
||||
* existence and kept alive by the timer so that it could be returned if a
|
||||
* call to createStorage was made due to a request by the page.)
|
||||
*/
|
||||
void precacheStorage(in nsIPrincipal aPrincipal);
|
||||
nsIDOMStorage precacheStorage(in nsIPrincipal aPrincipal);
|
||||
|
||||
/**
|
||||
* Returns instance of DOM storage object for given principal.
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "mozilla/dom/MemoryReportRequest.h"
|
||||
#include "mozilla/dom/ProcessGlobal.h"
|
||||
#include "mozilla/dom/PushNotifier.h"
|
||||
#include "mozilla/dom/Storage.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
||||
|
@ -3035,6 +3036,20 @@ ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
Storage::DispatchStorageEvent(Storage::LocalStorage,
|
||||
aDocumentURI, aKey, aOldValue, aNewValue,
|
||||
aPrincipal, aIsPrivate, nullptr, true);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||
bool
|
||||
ContentChild::SendGetA11yContentId()
|
||||
|
|
|
@ -391,6 +391,14 @@ public:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvFilePathUpdate(const nsString& aStorageType,
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/Storage.h"
|
||||
#include "mozilla/dom/StorageIPC.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
|
@ -4771,6 +4772,25 @@ ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aUR
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const Principal& aPrincipal,
|
||||
const bool& aIsPrivate)
|
||||
{
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
if (cp != this) {
|
||||
Unused << cp->SendDispatchLocalStorageChange(
|
||||
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
|
||||
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
|
||||
}
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
|
||||
{
|
||||
|
|
|
@ -574,6 +574,14 @@ public:
|
|||
virtual mozilla::ipc::IPCResult
|
||||
RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
|
||||
const nsString& aKey,
|
||||
const nsString& aOldValue,
|
||||
const nsString& aNewValue,
|
||||
const IPC::Principal& aPrincipal,
|
||||
const bool& aIsPrivate) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvGetA11yContentId(uint32_t* aContentId) override;
|
||||
|
||||
|
|
|
@ -676,6 +676,12 @@ child:
|
|||
|
||||
async BlobURLUnregistration(nsCString aURI);
|
||||
|
||||
async DispatchLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
Principal principal,
|
||||
bool isPrivate);
|
||||
|
||||
async GMPsChanged(GMPCapabilityData[] capabilities);
|
||||
|
||||
|
@ -1178,6 +1184,13 @@ parent:
|
|||
|
||||
async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
|
||||
|
||||
async BroadcastLocalStorageChange(nsString documentURI,
|
||||
nsString key,
|
||||
nsString oldValue,
|
||||
nsString newValue,
|
||||
Principal principal,
|
||||
bool isPrivate);
|
||||
|
||||
/**
|
||||
* Messages for communicating child Telemetry to the parent process
|
||||
*/
|
||||
|
|
|
@ -769,7 +769,7 @@ public:
|
|||
* Returns false if the data was not appended because no such track exists
|
||||
* or the stream was already finished.
|
||||
*/
|
||||
bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
|
||||
virtual bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
|
||||
/**
|
||||
* Get the stream time of the end of the data that has been appended so far.
|
||||
* Can be called from any thread but won't be useful if it can race with
|
||||
|
|
|
@ -130,11 +130,12 @@ MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
|
|||
mReadyState(MediaStreamTrackState::Live),
|
||||
mEnabled(true), mConstraints(aConstraints)
|
||||
{
|
||||
|
||||
GetSource().RegisterSink(this);
|
||||
|
||||
mPrincipalHandleListener = new PrincipalHandleListener(this);
|
||||
AddListener(mPrincipalHandleListener);
|
||||
if (GetOwnedStream()) {
|
||||
mPrincipalHandleListener = new PrincipalHandleListener(this);
|
||||
AddListener(mPrincipalHandleListener);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
||||
|
|
|
@ -394,7 +394,7 @@ public:
|
|||
* Adds a MediaStreamTrackListener to the MediaStreamGraph representation of
|
||||
* this track.
|
||||
*/
|
||||
void AddListener(MediaStreamTrackListener* aListener);
|
||||
virtual void AddListener(MediaStreamTrackListener* aListener);
|
||||
|
||||
/**
|
||||
* Removes a MediaStreamTrackListener from the MediaStreamGraph representation
|
||||
|
@ -408,7 +408,7 @@ public:
|
|||
* the listener succeeded (tracks originating from SourceMediaStreams) or
|
||||
* failed (e.g., WebAudio originated tracks).
|
||||
*/
|
||||
void AddDirectListener(DirectMediaStreamTrackListener *aListener);
|
||||
virtual void AddDirectListener(DirectMediaStreamTrackListener *aListener);
|
||||
void RemoveDirectListener(DirectMediaStreamTrackListener *aListener);
|
||||
|
||||
/**
|
||||
|
|
|
@ -2720,6 +2720,26 @@ ForceWindowless(InfallibleTArray<nsCString>& names,
|
|||
}
|
||||
}
|
||||
#endif // windows or linux
|
||||
#if defined(XP_WIN)
|
||||
static void
|
||||
ForceDirect(InfallibleTArray<nsCString>& names,
|
||||
InfallibleTArray<nsCString>& values)
|
||||
{
|
||||
nsCaseInsensitiveUTF8StringArrayComparator comparator;
|
||||
NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
|
||||
NS_NAMED_LITERAL_CSTRING(directAttributeValue, "direct");
|
||||
auto wmodeAttributeIndex =
|
||||
names.IndexOf(wmodeAttributeName, 0, comparator);
|
||||
if (wmodeAttributeIndex != names.NoIndex) {
|
||||
if (values[wmodeAttributeIndex].EqualsLiteral("window")) {
|
||||
values[wmodeAttributeIndex].Assign(directAttributeValue);
|
||||
}
|
||||
} else {
|
||||
names.AppendElement(wmodeAttributeName);
|
||||
values.AppendElement(directAttributeValue);
|
||||
}
|
||||
}
|
||||
#endif // windows
|
||||
|
||||
nsresult
|
||||
PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
|
||||
|
@ -2758,6 +2778,8 @@ PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
|
|||
#ifdef XP_WIN
|
||||
bool supportsAsyncRender =
|
||||
Preferences::GetBool("dom.ipc.plugins.asyncdrawing.enabled", false);
|
||||
bool supportsForceDirect =
|
||||
Preferences::GetBool("dom.ipc.plugins.forcedirect.enabled", false);
|
||||
if (supportsAsyncRender) {
|
||||
// Prefs indicates we want async plugin rendering, make sure
|
||||
// the flash module has support.
|
||||
|
@ -2777,6 +2799,14 @@ PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
|
|||
#elif defined(MOZ_WIDGET_GTK)
|
||||
// We no longer support windowed mode on Linux.
|
||||
ForceWindowless(names, values);
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
// For all builds that use async rendering force use of the accelerated
|
||||
// direct path for flash objects that have wmode=window or no wmode
|
||||
// specified.
|
||||
if (supportsAsyncRender && supportsForceDirect) {
|
||||
ForceDirect(names, values);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include "nsIPrincipal.h"
|
||||
#include "nsICookiePermission.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/StorageBinding.h"
|
||||
#include "mozilla/dom/StorageEvent.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
|
@ -25,6 +28,9 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mManager, mPrincipal, mWindow)
|
||||
|
@ -58,7 +64,6 @@ Storage::Storage(nsPIDOMWindowInner* aWindow,
|
|||
|
||||
Storage::~Storage()
|
||||
{
|
||||
mCache->KeepAlive();
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
|
@ -215,6 +220,30 @@ void
|
|||
Storage::BroadcastChangeNotification(const nsSubstring& aKey,
|
||||
const nsSubstring& aOldValue,
|
||||
const nsSubstring& aNewValue)
|
||||
{
|
||||
if (!XRE_IsParentProcess() && GetType() == LocalStorage && mPrincipal) {
|
||||
// If we are in a child process, we want to send a message to the parent in
|
||||
// order to broadcast the StorageEvent correctly to any child process.
|
||||
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
|
||||
Unused << NS_WARN_IF(!cc->SendBroadcastLocalStorageChange(
|
||||
mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
|
||||
IPC::Principal(mPrincipal), mIsPrivate));
|
||||
}
|
||||
|
||||
DispatchStorageEvent(GetType(), mDocumentURI, aKey, aOldValue, aNewValue,
|
||||
mPrincipal, mIsPrivate, this, false);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Storage::DispatchStorageEvent(StorageType aStorageType,
|
||||
const nsAString& aDocumentURI,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aOldValue,
|
||||
const nsAString& aNewValue,
|
||||
nsIPrincipal* aPrincipal,
|
||||
bool aIsPrivate,
|
||||
Storage* aStorage,
|
||||
bool aImmediateDispatch)
|
||||
{
|
||||
StorageEventInit dict;
|
||||
dict.mBubbles = false;
|
||||
|
@ -222,21 +251,67 @@ Storage::BroadcastChangeNotification(const nsSubstring& aKey,
|
|||
dict.mKey = aKey;
|
||||
dict.mNewValue = aNewValue;
|
||||
dict.mOldValue = aOldValue;
|
||||
dict.mStorageArea = this;
|
||||
dict.mUrl = mDocumentURI;
|
||||
dict.mStorageArea = aStorage;
|
||||
dict.mUrl = aDocumentURI;
|
||||
|
||||
// Note, this DOM event should never reach JS. It is cloned later in
|
||||
// nsGlobalWindow.
|
||||
RefPtr<StorageEvent> event =
|
||||
StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
|
||||
|
||||
event->SetPrincipal(aPrincipal);
|
||||
|
||||
RefPtr<StorageNotifierRunnable> r =
|
||||
new StorageNotifierRunnable(event,
|
||||
GetType() == LocalStorage
|
||||
aStorageType == LocalStorage
|
||||
? u"localStorage"
|
||||
: u"sessionStorage",
|
||||
IsPrivate());
|
||||
NS_DispatchToMainThread(r);
|
||||
aIsPrivate);
|
||||
|
||||
if (aImmediateDispatch) {
|
||||
Unused << r->Run();
|
||||
} else {
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
// If we are in the parent process and we have the principal, we want to
|
||||
// broadcast this event to every other process.
|
||||
if (aStorageType == LocalStorage && XRE_IsParentProcess() && aPrincipal) {
|
||||
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
|
||||
Unused << cp->SendDispatchLocalStorageChange(
|
||||
nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
|
||||
nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Storage::ApplyEvent(StorageEvent* aStorageEvent)
|
||||
{
|
||||
MOZ_ASSERT(aStorageEvent);
|
||||
|
||||
nsAutoString key;
|
||||
nsAutoString old;
|
||||
nsAutoString value;
|
||||
|
||||
aStorageEvent->GetKey(key);
|
||||
aStorageEvent->GetNewValue(value);
|
||||
|
||||
// No key means clearing the full storage.
|
||||
if (key.IsVoid()) {
|
||||
MOZ_ASSERT(value.IsVoid());
|
||||
mCache->Clear(this, StorageCache::E10sPropagated);
|
||||
return;
|
||||
}
|
||||
|
||||
// No new value means removing the key.
|
||||
if (value.IsVoid()) {
|
||||
mCache->RemoveItem(this, key, old, StorageCache::E10sPropagated);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we set the new value.
|
||||
mCache->SetItem(this, key, value, old, StorageCache::E10sPropagated);
|
||||
}
|
||||
|
||||
static const char kPermissionType[] = "cookie";
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace dom {
|
|||
|
||||
class StorageManagerBase;
|
||||
class StorageCache;
|
||||
class StorageEvent;
|
||||
|
||||
class Storage final
|
||||
: public nsIDOMStorage
|
||||
|
@ -129,6 +130,28 @@ public:
|
|||
return mCache == aOther->mCache;
|
||||
}
|
||||
|
||||
// aStorage can be null if this method is called by ContentChild.
|
||||
//
|
||||
// aImmediateDispatch is for use by (main-thread) IPC code so that PContent
|
||||
// ordering can be maintained. Without this, the event would be enqueued and
|
||||
// run in a future turn of the event loop, potentially allowing other PContent
|
||||
// Recv* methods to trigger script that wants to assume our localstorage
|
||||
// changes have already been applied. This is the case for message manager
|
||||
// messages which are used by ContentTask testing logic and webextensions.
|
||||
static void
|
||||
DispatchStorageEvent(StorageType aStorageType,
|
||||
const nsAString& aDocumentURI,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aOldValue,
|
||||
const nsAString& aNewValue,
|
||||
nsIPrincipal* aPrincipal,
|
||||
bool aIsPrivate,
|
||||
Storage* aStorage,
|
||||
bool aImmediateDispatch);
|
||||
|
||||
void
|
||||
ApplyEvent(StorageEvent* aStorageEvent);
|
||||
|
||||
protected:
|
||||
// The method checks whether the caller can use a storage.
|
||||
// CanUseStorage is called before any DOM initiated operation
|
||||
|
|
|
@ -59,7 +59,7 @@ GetDataSetIndex(const Storage* aStorage)
|
|||
|
||||
NS_IMPL_ADDREF(StorageCacheBridge)
|
||||
|
||||
// Since there is no consumer of return value of Release, we can turn this
|
||||
// Since there is no consumer of return value of Release, we can turn this
|
||||
// method to void to make implementation of asynchronous StorageCache::Release
|
||||
// much simpler.
|
||||
NS_IMETHODIMP_(void) StorageCacheBridge::Release(void)
|
||||
|
@ -149,7 +149,7 @@ StorageCache::Init(StorageManagerBase* aManager,
|
|||
// Check the quota string has (or has not) the identical origin suffix as
|
||||
// this storage cache is bound to.
|
||||
MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
|
||||
MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
|
||||
MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
|
||||
NS_LITERAL_CSTRING("^")));
|
||||
|
||||
mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
|
||||
|
@ -198,28 +198,33 @@ StorageCache::DataSet(const Storage* aStorage)
|
|||
}
|
||||
|
||||
bool
|
||||
StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta)
|
||||
StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta,
|
||||
const MutationSource aSource)
|
||||
{
|
||||
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
|
||||
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
|
||||
}
|
||||
|
||||
bool
|
||||
StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
|
||||
StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
|
||||
const MutationSource aSource)
|
||||
{
|
||||
// Check if we are in a low disk space situation
|
||||
if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
|
||||
if (aSource == ContentMutation &&
|
||||
aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check limit per this origin
|
||||
Data& data = mData[aGetDataSetIndex];
|
||||
uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
|
||||
if (aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
|
||||
if (aSource == ContentMutation &&
|
||||
aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check eTLD+1 limit
|
||||
if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
|
||||
if (mUsage &&
|
||||
!mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -246,73 +251,6 @@ StorageCache::Preload()
|
|||
|
||||
namespace {
|
||||
|
||||
// This class is passed to timer as a tick observer. It refers the cache
|
||||
// and keeps it alive for a time.
|
||||
class StorageCacheHolder : public nsITimerCallback, public nsINamed
|
||||
{
|
||||
virtual ~StorageCacheHolder() {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD
|
||||
Notify(nsITimer* aTimer) override
|
||||
{
|
||||
mCache = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
GetName(nsACString& aName) override
|
||||
{
|
||||
aName.AssignASCII("StorageCacheHolder_timer");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
SetName(const char* aName) override
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
RefPtr<StorageCache> mCache;
|
||||
|
||||
public:
|
||||
explicit StorageCacheHolder(StorageCache* aCache) : mCache(aCache) {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(StorageCacheHolder, nsITimerCallback, nsINamed)
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
StorageCache::KeepAlive()
|
||||
{
|
||||
// Missing reference back to the manager means the cache is not responsible
|
||||
// for its lifetime. Used for keeping sessionStorage live forever.
|
||||
if (!mManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// Timer and the holder must be initialized on the main thread.
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &StorageCache::KeepAlive));
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<StorageCacheHolder> holder = new StorageCacheHolder(this);
|
||||
timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
|
||||
mKeepAliveTimer.swap(timer);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// The AutoTimer provided by telemetry headers is only using static,
|
||||
// i.e. compile time known ID, but here we know the ID only at run time.
|
||||
// Hence a new class.
|
||||
|
@ -452,7 +390,8 @@ StorageCache::GetItem(const Storage* aStorage, const nsAString& aKey,
|
|||
|
||||
nsresult
|
||||
StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
|
||||
const nsString& aValue, nsString& aOld)
|
||||
const nsString& aValue, nsString& aOld,
|
||||
const MutationSource aSource)
|
||||
{
|
||||
// Size of the cache that will change after this action.
|
||||
int64_t delta = 0;
|
||||
|
@ -475,7 +414,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
|
|||
delta += static_cast<int64_t>(aValue.Length()) -
|
||||
static_cast<int64_t>(aOld.Length());
|
||||
|
||||
if (!ProcessUsageDelta(aStorage, delta)) {
|
||||
if (!ProcessUsageDelta(aStorage, delta, aSource)) {
|
||||
return NS_ERROR_DOM_QUOTA_REACHED;
|
||||
}
|
||||
|
||||
|
@ -485,7 +424,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
|
|||
|
||||
data.mKeys.Put(aKey, aValue);
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
if (aSource == ContentMutation && Persist(aStorage)) {
|
||||
if (!sDatabase) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
|
@ -504,7 +443,7 @@ StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
|
|||
|
||||
nsresult
|
||||
StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
|
||||
nsString& aOld)
|
||||
nsString& aOld, const MutationSource aSource)
|
||||
{
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
|
||||
|
@ -522,10 +461,10 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
|
|||
// Recalculate the cached data size
|
||||
const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
|
||||
static_cast<int64_t>(aKey.Length()));
|
||||
Unused << ProcessUsageDelta(aStorage, delta);
|
||||
Unused << ProcessUsageDelta(aStorage, delta, aSource);
|
||||
data.mKeys.Remove(aKey);
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
if (aSource == ContentMutation && Persist(aStorage)) {
|
||||
if (!sDatabase) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
|
@ -539,7 +478,7 @@ StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
|
|||
}
|
||||
|
||||
nsresult
|
||||
StorageCache::Clear(const Storage* aStorage)
|
||||
StorageCache::Clear(const Storage* aStorage, const MutationSource aSource)
|
||||
{
|
||||
bool refresh = false;
|
||||
if (Persist(aStorage)) {
|
||||
|
@ -561,11 +500,11 @@ StorageCache::Clear(const Storage* aStorage)
|
|||
bool hadData = !!data.mKeys.Count();
|
||||
|
||||
if (hadData) {
|
||||
Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
|
||||
Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
|
||||
data.mKeys.Clear();
|
||||
}
|
||||
|
||||
if (Persist(aStorage) && (refresh || hadData)) {
|
||||
if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
|
||||
if (!sDatabase) {
|
||||
NS_ERROR("Writing to localStorage after the database has been shut down"
|
||||
", data lose!");
|
||||
|
@ -680,9 +619,6 @@ StorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
|
|||
void
|
||||
StorageCache::LoadDone(nsresult aRv)
|
||||
{
|
||||
// Keep the preloaded cache alive for a time
|
||||
KeepAlive();
|
||||
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
mLoadResult = aRv;
|
||||
mLoaded = true;
|
||||
|
@ -743,12 +679,13 @@ StorageUsage::LoadUsage(const int64_t aUsage)
|
|||
|
||||
bool
|
||||
StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
|
||||
const int64_t aDelta)
|
||||
const int64_t aDelta, const StorageCache::MutationSource aSource)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
|
||||
if (aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
|
||||
if (aSource == StorageCache::ContentMutation &&
|
||||
aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,8 +78,24 @@ class StorageCache : public StorageCacheBridge
|
|||
public:
|
||||
NS_IMETHOD_(void) Release(void);
|
||||
|
||||
// Note: We pass aOriginNoSuffix through the ctor here, because
|
||||
// StorageCacheHashKey's ctor is creating this class and
|
||||
enum MutationSource {
|
||||
// The mutation is a result of an explicit JS mutation in this process.
|
||||
// The mutation should be sent to the sDatabase. Quota will be checked and
|
||||
// QuotaExceededError may be returned without the mutation being applied.
|
||||
ContentMutation,
|
||||
// The mutation initially was triggered in a different process and is being
|
||||
// propagated to this cache via Storage::ApplyEvent. The mutation should
|
||||
// not be sent to the sDatabase because the originating process is already
|
||||
// doing that. (In addition to the redundant writes being wasteful, there
|
||||
// is the potential for other processes to see inconsistent state from the
|
||||
// database while preloading.) Quota will be updated but not checked
|
||||
// because it's assumed it was checked in another process and data-coherency
|
||||
// is more important than slightly exceeding quota.
|
||||
E10sPropagated
|
||||
};
|
||||
|
||||
// Note: We pass aOriginNoSuffix through the ctor here, because
|
||||
// StorageCacheHashKey's ctor is creating this class and
|
||||
// accepts reversed-origin-no-suffix as an argument - the hashing key.
|
||||
explicit StorageCache(const nsACString* aOriginNoSuffix);
|
||||
|
||||
|
@ -96,10 +112,6 @@ public:
|
|||
// Starts async preload of this cache if it persistent and not loaded.
|
||||
void Preload();
|
||||
|
||||
// Keeps the cache alive (i.e. present in the manager's hash table) for a
|
||||
// time.
|
||||
void KeepAlive();
|
||||
|
||||
// The set of methods that are invoked by DOM storage web API.
|
||||
// We are passing the Storage object just to let the cache
|
||||
// read properties like mPrivate, mPrincipal and mSessionOnly.
|
||||
|
@ -109,10 +121,13 @@ public:
|
|||
nsresult GetItem(const Storage* aStorage, const nsAString& aKey,
|
||||
nsAString& aRetval);
|
||||
nsresult SetItem(const Storage* aStorage, const nsAString& aKey,
|
||||
const nsString& aValue, nsString& aOld);
|
||||
const nsString& aValue, nsString& aOld,
|
||||
const MutationSource aSource=ContentMutation);
|
||||
nsresult RemoveItem(const Storage* aStorage, const nsAString& aKey,
|
||||
nsString& aOld);
|
||||
nsresult Clear(const Storage* aStorage);
|
||||
nsString& aOld,
|
||||
const MutationSource aSource=ContentMutation);
|
||||
nsresult Clear(const Storage* aStorage,
|
||||
const MutationSource aSource=ContentMutation);
|
||||
|
||||
void GetKeys(const Storage* aStorage, nsTArray<nsString>& aKeys);
|
||||
|
||||
|
@ -182,8 +197,18 @@ private:
|
|||
|
||||
// Changes the quota usage on the given data set if it fits the quota.
|
||||
// If not, then false is returned and no change to the set must be done.
|
||||
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
|
||||
bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta);
|
||||
// A special case is if aSource==E10sPropagated, then we will return true even
|
||||
// if the change would put us over quota. This is done to ensure coherency of
|
||||
// caches between processes in the face of races. It does allow an attacker
|
||||
// to potentially use N multiples of the quota storage limit if they can
|
||||
// arrange for their origin to execute code in N processes. However, this is
|
||||
// not considered a particularly concerning threat model because it's already
|
||||
// very possible for a rogue page to attempt to intentionally fill up the
|
||||
// user's storage through the use of multiple domains.
|
||||
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
|
||||
const MutationSource aSource=ContentMutation);
|
||||
bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta,
|
||||
const MutationSource aSource=ContentMutation);
|
||||
|
||||
private:
|
||||
// When a cache is reponsible for its life time (in case of localStorage data
|
||||
|
@ -196,9 +221,6 @@ private:
|
|||
// Obtained from the manager during initialization (Init method).
|
||||
RefPtr<StorageUsage> mUsage;
|
||||
|
||||
// Timer that holds this cache alive for a while after it has been preloaded.
|
||||
nsCOMPtr<nsITimer> mKeepAliveTimer;
|
||||
|
||||
// Principal the cache has been initially created for, this is used only for
|
||||
// sessionStorage access checks since sessionStorage objects are strictly
|
||||
// scoped by a principal. localStorage objects on the other hand are scoped
|
||||
|
@ -211,7 +233,7 @@ private:
|
|||
// The origin attributes suffix
|
||||
nsCString mOriginSuffix;
|
||||
|
||||
// The eTLD+1 scope used to count quota usage. It is in the reversed format
|
||||
// The eTLD+1 scope used to count quota usage. It is in the reversed format
|
||||
// and contains the origin attributes suffix.
|
||||
nsCString mQuotaOriginScope;
|
||||
|
||||
|
@ -277,7 +299,8 @@ class StorageUsage : public StorageUsageBridge
|
|||
public:
|
||||
explicit StorageUsage(const nsACString& aOriginScope);
|
||||
|
||||
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
|
||||
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta,
|
||||
const StorageCache::MutationSource aSource);
|
||||
|
||||
private:
|
||||
virtual const nsCString& OriginScope() { return mOriginScope; }
|
||||
|
|
|
@ -224,6 +224,12 @@ StorageDBChild::RecvObserve(const nsCString& aTopic,
|
|||
mozilla::ipc::IPCResult
|
||||
StorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
|
||||
{
|
||||
// Force population of mOriginsHavingData even if there are no origins so that
|
||||
// ShouldPreloadOrigin does not generate false positives for all origins.
|
||||
if (!aOrigins.Length()) {
|
||||
Unused << OriginsHavingData();
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
|
||||
OriginsHavingData().PutEntry(aOrigins[i]);
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ StorageManagerBase::DropCache(StorageCache* aCache)
|
|||
}
|
||||
|
||||
nsresult
|
||||
StorageManagerBase::GetStorageInternal(bool aCreate,
|
||||
StorageManagerBase::GetStorageInternal(CreateMode aCreateMode,
|
||||
mozIDOMWindow* aWindow,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
|
@ -331,12 +331,12 @@ StorageManagerBase::GetStorageInternal(bool aCreate,
|
|||
|
||||
// Get or create a cache for the given scope
|
||||
if (!cache) {
|
||||
if (!aCreate) {
|
||||
if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
|
||||
*aRetval = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aRetval) {
|
||||
if (aCreateMode == CreateMode::CreateIfShouldPreload) {
|
||||
// This is a demand to just preload the cache, if the scope has
|
||||
// no data stored, bypass creation and preload of the cache.
|
||||
StorageDBBridge* db = StorageCache::GetDatabase();
|
||||
|
@ -372,10 +372,11 @@ StorageManagerBase::GetStorageInternal(bool aCreate,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal)
|
||||
StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
|
||||
nullptr);
|
||||
return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
|
||||
aPrincipal, EmptyString(), false, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -385,8 +386,8 @@ StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow,
|
|||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
|
||||
aRetval);
|
||||
return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
|
||||
aDocumentURI, aPrivate, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -395,8 +396,8 @@ StorageManagerBase::GetStorage(mozIDOMWindow* aWindow,
|
|||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
|
||||
aRetval);
|
||||
return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
|
||||
aPrincipal, EmptyString(), aPrivate, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -90,8 +90,17 @@ private:
|
|||
const nsACString& aOriginNoSuffix,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
enum class CreateMode {
|
||||
// GetStorage: do not create if it's not already in memory.
|
||||
UseIfExistsNeverCreate,
|
||||
// CreateStorage: Create it if it's not already in memory.
|
||||
CreateAlways,
|
||||
// PrecacheStorage: Create only if the database says we ShouldPreloadOrigin.
|
||||
CreateIfShouldPreload
|
||||
};
|
||||
|
||||
// Helper for creation of DOM storage objects
|
||||
nsresult GetStorageInternal(bool aCreate,
|
||||
nsresult GetStorageInternal(CreateMode aCreate,
|
||||
mozIDOMWindow* aWindow,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
support-files =
|
||||
browser_frame_elements.html
|
||||
page_privatestorageevent.html
|
||||
page_localstorage_e10s.html
|
||||
position.html
|
||||
test-console-api.html
|
||||
test_bug1004814.html
|
||||
|
@ -43,6 +44,8 @@ skip-if = e10s
|
|||
skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
|
||||
[browser_largeAllocation_non_win32.js]
|
||||
skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
|
||||
[browser_localStorage_e10s.js]
|
||||
skip-if = !e10s # This is a test of e10s functionality.
|
||||
[browser_localStorage_privatestorageevent.js]
|
||||
[browser_test__content.js]
|
||||
[browser_test_focus_after_modal_state.js]
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
const HELPER_PAGE_URL =
|
||||
"http://example.com/browser/dom/tests/browser/page_localstorage_e10s.html";
|
||||
const HELPER_PAGE_ORIGIN = "http://example.com/";
|
||||
|
||||
// Simple tab wrapper abstracting our messaging mechanism;
|
||||
class KnownTab {
|
||||
constructor(name, tab) {
|
||||
this.name = name;
|
||||
this.tab = tab;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.tab = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple data structure class to help us track opened tabs and their pids.
|
||||
class KnownTabs {
|
||||
constructor() {
|
||||
this.byPid = new Map();
|
||||
this.byName = new Map();
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.byPid = null;
|
||||
this.byName = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open our helper page in a tab in its own content process, asserting that it
|
||||
* really is in its own process.
|
||||
*/
|
||||
function* openTestTabInOwnProcess(name, knownTabs) {
|
||||
let url = HELPER_PAGE_URL + '?' + encodeURIComponent(name);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
|
||||
ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
|
||||
ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
|
||||
|
||||
let knownTab = new KnownTab(name, tab);
|
||||
knownTabs.byPid.set(pid, knownTab);
|
||||
knownTabs.byName.set(name, knownTab);
|
||||
return knownTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all the tabs we opened.
|
||||
*/
|
||||
function* cleanupTabs(knownTabs) {
|
||||
for (let knownTab of knownTabs.byName.values()) {
|
||||
yield BrowserTestUtils.removeTab(knownTab.tab);
|
||||
knownTab.cleanup();
|
||||
}
|
||||
knownTabs.cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the origin's storage so that "OriginsHavingData" will return false for
|
||||
* our origin. Note that this is only the case for AsyncClear() which is
|
||||
* explicitly issued against a cache, or AsyncClearAll() which we can trigger
|
||||
* by wiping all storage. However, the more targeted domain clearings that
|
||||
* we can trigger via observer, AsyncClearMatchingOrigin and
|
||||
* AsyncClearMatchingOriginAttributes will not clear the hashtable entry for
|
||||
* the origin.
|
||||
*
|
||||
* So we explicitly access the cache here in the parent for the origin and issue
|
||||
* an explicit clear. Clearing all storage might be a little easier but seems
|
||||
* like asking for intermittent failures.
|
||||
*/
|
||||
function clearOriginStorageEnsuringNoPreload() {
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
|
||||
HELPER_PAGE_ORIGIN);
|
||||
// We want to use createStorage to force the cache to be created so we can
|
||||
// issue the clear. It's possible for getStorage to return false but for the
|
||||
// origin preload hash to still have our origin in it.
|
||||
let storage = Services.domStorageManager.createStorage(null, principal, "");
|
||||
storage.clear();
|
||||
// We don't need to wait for anything. The clear call will have queued the
|
||||
// clear operation on the database thread, and the child process requests
|
||||
// for origins will likewise be answered via the database thread.
|
||||
}
|
||||
|
||||
function* verifyTabPreload(knownTab, expectStorageExists) {
|
||||
let storageExists = yield ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
HELPER_PAGE_ORIGIN,
|
||||
function(origin) {
|
||||
let principal =
|
||||
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
|
||||
origin);
|
||||
return !!Services.domStorageManager.getStorage(null, principal);
|
||||
});
|
||||
is(storageExists, expectStorageExists, "Storage existence === preload");
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct the given tab to execute the given series of mutations. For
|
||||
* simplicity, the mutations representation matches the expected events rep.
|
||||
*/
|
||||
function* mutateTabStorage(knownTab, mutations) {
|
||||
yield ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{ mutations },
|
||||
function(args) {
|
||||
return content.wrappedJSObject.mutateStorage(args.mutations);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct the given tab to add a "storage" event listener and record all
|
||||
* received events. verifyTabStorageEvents is the corresponding method to
|
||||
* check and assert the recorded events.
|
||||
*/
|
||||
function* recordTabStorageEvents(knownTab) {
|
||||
yield ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{},
|
||||
function() {
|
||||
return content.wrappedJSObject.listenForStorageEvents();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current localStorage contents perceived by the tab and assert
|
||||
* that they match the provided expected state.
|
||||
*/
|
||||
function* verifyTabStorageState(knownTab, expectedState) {
|
||||
let actualState = yield ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{},
|
||||
function() {
|
||||
return content.wrappedJSObject.getStorageState();
|
||||
});
|
||||
|
||||
for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
|
||||
ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
|
||||
is(actualState[expectedKey], expectedValue, "value correct");
|
||||
}
|
||||
for (let actualKey of Object.keys(actualState)) {
|
||||
if (!expectedState.hasOwnProperty(actualKey)) {
|
||||
ok(false, "actual state has key it shouldn't have: " + actualKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and clear the storage events recorded by the tab and assert that
|
||||
* they match the provided expected events. For simplicity, the expected events
|
||||
* representation is the same as that used by mutateTabStorage.
|
||||
*/
|
||||
function* verifyTabStorageEvents(knownTab, expectedEvents) {
|
||||
let actualEvents = yield ContentTask.spawn(
|
||||
knownTab.tab.linkedBrowser,
|
||||
{},
|
||||
function() {
|
||||
return content.wrappedJSObject.returnAndClearStorageEvents();
|
||||
});
|
||||
|
||||
is(actualEvents.length, expectedEvents.length, "right number of events");
|
||||
for (let i = 0; i < actualEvents.length; i++) {
|
||||
let [actualKey, actualNewValue, actualOldValue] = actualEvents[i];
|
||||
let [expectedKey, expectedNewValue, expectedOldValue] = expectedEvents[i];
|
||||
is(actualKey, expectedKey, "keys match");
|
||||
is(actualNewValue, expectedNewValue, "new values match");
|
||||
is(actualOldValue, expectedOldValue, "old values match");
|
||||
}
|
||||
}
|
||||
|
||||
// We spin up a ton of child processes.
|
||||
requestLongerTimeout(4);
|
||||
|
||||
/**
|
||||
* Verify the basics of our multi-e10s localStorage support. We are focused on
|
||||
* whitebox testing two things. When this is being written, broadcast filtering
|
||||
* is not in place, but the test is intended to attempt to verify that its
|
||||
* implementation does not break things.
|
||||
*
|
||||
* 1) That pages see the same localStorage state in a timely fashion when
|
||||
* engaging in non-conflicting operations. We are not testing races or
|
||||
* conflict resolution; the spec does not cover that.
|
||||
*
|
||||
* 2) That there are no edge-cases related to when the Storage instance is
|
||||
* created for the page or the StorageCache for the origin. (StorageCache is
|
||||
* what actually backs the Storage binding exposed to the page.) This
|
||||
* matters because the following reasons can exist for them to be created:
|
||||
* - Preload, on the basis of knowing the origin uses localStorage. The
|
||||
* interesting edge case is when we have the same origin open in different
|
||||
* processes and the origin starts using localStorage when it did not
|
||||
* before. Preload will not have instantiated bindings, which could impact
|
||||
* correctness.
|
||||
* - The page accessing localStorage for read or write purposes. This is the
|
||||
* obvious, boring one.
|
||||
* - The page adding a "storage" listener. This is less obvious and
|
||||
* interacts with the preload edge-case mentioned above. The page needs to
|
||||
* hear "storage" events even if the page has not touched localStorage
|
||||
* itself and its origin had nothing stored in localStorage when the page
|
||||
* was created.
|
||||
*
|
||||
* We use the same simple child page in all tabs that:
|
||||
* - can be instructed to listen for and record "storage" events
|
||||
* - can be instructed to issue a series of localStorage writes
|
||||
* - can be instructed to return the current entire localStorage contents
|
||||
*
|
||||
* We open the 5 following tabs:
|
||||
* - Open a "writer" tab that does not listen for "storage" events and will
|
||||
* issue only writes.
|
||||
* - Open a "listener" tab instructed to listen for "storage" events
|
||||
* immediately. We expect it to capture all events.
|
||||
* - Open an "reader" tab that does not listen for "storage" events and will
|
||||
* only issue reads when instructed.
|
||||
* - Open a "lateWriteThenListen" tab that initially does nothing. We will
|
||||
* later tell it to issue a write and then listen for events to make sure it
|
||||
* captures the later events.
|
||||
* - Open "lateOpenSeesPreload" tab after we've done everything and ensure that
|
||||
* it preloads/precaches the data without us having touched localStorage or
|
||||
* added an event listener.
|
||||
*/
|
||||
add_task(function*() {
|
||||
// - Boost process count so all of our tabs get new processes.
|
||||
// Our test wants to assert things about the precache status which is only
|
||||
// populated at process startup and never updated. (Per analysis at
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1312022 this still makes
|
||||
// sense.) https://bugzilla.mozilla.org/show_bug.cgi?id=1312022 introduced
|
||||
// a mechanism for keeping an arbitrary number of processes alive, modifying
|
||||
// all browser chrome tests to keep alive whatever dom.ipc.processCount is set
|
||||
// to. The mechanism was slightly modified later to be type-based, so now
|
||||
// it's "dom.ipc.keepProcessesAlive.web" we care about.
|
||||
//
|
||||
// Our options for ensuring we get a new process are to either:
|
||||
// 1) Try and push keepalive down to 1 and kill off the processes that are
|
||||
// already hanging around.
|
||||
// 2) Just bump the process count up enough so that every tab we open will
|
||||
// get a new process.
|
||||
//
|
||||
// The first option turns out to be hard to get right. Specifically,
|
||||
// although one can set the keepalive and process counts to 1 and open and
|
||||
// close tabs to try and trigger process termination down to 1, since we don't
|
||||
// know how many processes might exist, we can't reliably listen for observer
|
||||
// notifications of their shutdown to ensure we're avoiding shutdown races.
|
||||
// (If there are races then the processes won't actually be shut down.) So
|
||||
// it's easiest to just boost the limit.
|
||||
let keepAliveCount =
|
||||
SpecialPowers.getIntPref("dom.ipc.keepProcessesAlive.web", 1);
|
||||
let safeProcessCount = keepAliveCount + 6;
|
||||
info("dom.ipc.keepProcessesAlive.web is " + keepAliveCount + ", boosting " +
|
||||
"process count temporarily to " + safeProcessCount);
|
||||
|
||||
// (There's already one about:blank page open and we open 5 new tabs, so 6
|
||||
// processes. Actually, 7, just in case.)
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["dom.ipc.processCount", safeProcessCount],
|
||||
["dom.ipc.processCount.web", safeProcessCount]
|
||||
]
|
||||
});
|
||||
|
||||
// Ensure that there is no localstorage data or potential false positives for
|
||||
// localstorage preloads by forcing the origin to be cleared prior to the
|
||||
// start of our test.
|
||||
clearOriginStorageEnsuringNoPreload();
|
||||
|
||||
// - Open tabs. Don't configure any of them yet.
|
||||
const knownTabs = new KnownTabs();
|
||||
const writerTab = yield* openTestTabInOwnProcess("writer", knownTabs);
|
||||
const listenerTab = yield* openTestTabInOwnProcess("listener", knownTabs);
|
||||
const readerTab = yield* openTestTabInOwnProcess("reader", knownTabs);
|
||||
const lateWriteThenListenTab = yield* openTestTabInOwnProcess(
|
||||
"lateWriteThenListen", knownTabs);
|
||||
|
||||
// Sanity check that preloading did not occur in the tabs.
|
||||
yield* verifyTabPreload(writerTab, false);
|
||||
yield* verifyTabPreload(listenerTab, false);
|
||||
yield* verifyTabPreload(readerTab, false);
|
||||
|
||||
// - Configure the tabs.
|
||||
yield* recordTabStorageEvents(listenerTab);
|
||||
|
||||
// - Issue the initial batch of writes and verify.
|
||||
const initialWriteMutations = [
|
||||
//[key (null=clear), newValue (null=delete), oldValue (verification)]
|
||||
["getsCleared", "1", null],
|
||||
["alsoGetsCleared", "2", null],
|
||||
[null, null, null],
|
||||
["stays", "3", null],
|
||||
["clobbered", "pre", null],
|
||||
["getsDeletedLater", "4", null],
|
||||
["getsDeletedImmediately", "5", null],
|
||||
["getsDeletedImmediately", null, "5"],
|
||||
["alsoStays", "6", null],
|
||||
["getsDeletedLater", null, "4"],
|
||||
["clobbered", "post", "pre"]
|
||||
];
|
||||
const initialWriteState = {
|
||||
stays: "3",
|
||||
clobbered: "post",
|
||||
alsoStays: "6"
|
||||
};
|
||||
|
||||
yield* mutateTabStorage(writerTab, initialWriteMutations);
|
||||
|
||||
yield* verifyTabStorageState(writerTab, initialWriteState);
|
||||
yield* verifyTabStorageEvents(listenerTab, initialWriteMutations);
|
||||
yield* verifyTabStorageState(listenerTab, initialWriteState);
|
||||
yield* verifyTabStorageState(readerTab, initialWriteState);
|
||||
|
||||
// - Issue second set of writes from lateWriteThenListen
|
||||
const lateWriteMutations = [
|
||||
["lateStays", "10", null],
|
||||
["lateClobbered", "latePre", null],
|
||||
["lateDeleted", "11", null],
|
||||
["lateClobbered", "lastPost", "latePre"],
|
||||
["lateDeleted", null, "11"]
|
||||
];
|
||||
const lateWriteState = Object.assign({}, initialWriteState, {
|
||||
lateStays: "10",
|
||||
lateClobbered: "lastPost"
|
||||
});
|
||||
|
||||
yield* mutateTabStorage(lateWriteThenListenTab, lateWriteMutations);
|
||||
yield* recordTabStorageEvents(lateWriteThenListenTab);
|
||||
|
||||
yield* verifyTabStorageState(writerTab, lateWriteState);
|
||||
yield* verifyTabStorageEvents(listenerTab, lateWriteMutations);
|
||||
yield* verifyTabStorageState(listenerTab, lateWriteState);
|
||||
yield* verifyTabStorageState(readerTab, lateWriteState);
|
||||
|
||||
// - Issue last set of writes from writerTab.
|
||||
const lastWriteMutations = [
|
||||
["lastStays", "20", null],
|
||||
["lastDeleted", "21", null],
|
||||
["lastClobbered", "lastPre", null],
|
||||
["lastClobbered", "lastPost", "lastPre"],
|
||||
["lastDeleted", null, "21"]
|
||||
];
|
||||
const lastWriteState = Object.assign({}, lateWriteState, {
|
||||
lastStays: "20",
|
||||
lastClobbered: "lastPost"
|
||||
});
|
||||
|
||||
yield* mutateTabStorage(writerTab, lastWriteMutations);
|
||||
|
||||
yield* verifyTabStorageState(writerTab, lastWriteState);
|
||||
yield* verifyTabStorageEvents(listenerTab, lastWriteMutations);
|
||||
yield* verifyTabStorageState(listenerTab, lastWriteState);
|
||||
yield* verifyTabStorageState(readerTab, lastWriteState);
|
||||
yield* verifyTabStorageEvents(lateWriteThenListenTab, lastWriteMutations);
|
||||
yield* verifyTabStorageState(lateWriteThenListenTab, lastWriteState);
|
||||
|
||||
// - Open a fresh tab and make sure it sees the precache/preload
|
||||
const lateOpenSeesPreload =
|
||||
yield* openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
|
||||
yield* verifyTabPreload(lateOpenSeesPreload, true);
|
||||
|
||||
// - Clean up.
|
||||
yield* cleanupTabs(knownTabs);
|
||||
|
||||
clearOriginStorageEnsuringNoPreload();
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
/**
|
||||
* Helper page used by browser_localStorage_e10s.js.
|
||||
**/
|
||||
var pageName = document.location.search.substring(1);
|
||||
window.addEventListener(
|
||||
"load",
|
||||
() => { document.getElementById("pageNameH").textContent = pageName; });
|
||||
|
||||
var recordedEvents = null;
|
||||
function storageListener(event) {
|
||||
recordedEvents.push([event.key, event.newValue, event.oldValue]);
|
||||
}
|
||||
|
||||
function listenForStorageEvents() {
|
||||
recordedEvents = [];
|
||||
window.addEventListener("storage", storageListener);
|
||||
}
|
||||
|
||||
function mutateStorage(mutations) {
|
||||
mutations.forEach(function([key, value]) {
|
||||
if (key !== null) {
|
||||
if (value === null) {
|
||||
localStorage.removeItem(key);
|
||||
} else {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
} else {
|
||||
localStorage.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getStorageState() {
|
||||
let numKeys = localStorage.length;
|
||||
let state = {};
|
||||
for (var iKey = 0; iKey < numKeys; iKey++) {
|
||||
let key = localStorage.key(iKey);
|
||||
state[key] = localStorage.getItem(key);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function returnAndClearStorageEvents() {
|
||||
let loggedEvents = recordedEvents;
|
||||
recordedEvents = [];
|
||||
return loggedEvents;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body><h2 id="pageNameH"></h2></body>
|
||||
</html>
|
|
@ -24,7 +24,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=674770
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true], ["dom.ipc.processCount", 1]]}, startTests);
|
||||
SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTests);
|
||||
});
|
||||
|
||||
function startTests() {
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#include "plbase64.h"
|
||||
#include "plstr.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Tokenizer.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "prmem.h"
|
||||
#include "prnetdb.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
@ -43,6 +46,7 @@
|
|||
#include "nsIHttpAuthenticatorCallback.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
@ -633,13 +637,20 @@ nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
|
|||
return false;
|
||||
if (NS_FAILED(uri->GetAsciiHost(host)))
|
||||
return false;
|
||||
if (NS_FAILED(uri->GetPort(&port)))
|
||||
|
||||
port = NS_GetRealPort(uri);
|
||||
if (port == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *hostList;
|
||||
if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
|
||||
return false;
|
||||
|
||||
struct FreePolicy { void operator()(void* p) { free(p); } };
|
||||
mozilla::UniquePtr<char[], FreePolicy> hostListScope;
|
||||
hostListScope.reset(hostList);
|
||||
|
||||
// pseudo-BNF
|
||||
// ----------
|
||||
//
|
||||
|
@ -652,24 +663,19 @@ nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
|
|||
// "https://, http://office.foo.com"
|
||||
//
|
||||
|
||||
char *start = hostList, *end;
|
||||
for (;;) {
|
||||
// skip past any whitespace
|
||||
while (*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
end = strchr(start, ',');
|
||||
if (!end)
|
||||
end = start + strlen(start);
|
||||
if (start == end)
|
||||
break;
|
||||
if (MatchesBaseURI(scheme, host, port, start, end))
|
||||
mozilla::Tokenizer t(hostList);
|
||||
while (!t.CheckEOF()) {
|
||||
t.SkipWhites();
|
||||
nsDependentCSubstring url;
|
||||
mozilla::Unused << t.ReadUntil(mozilla::Tokenizer::Token::Char(','), url);
|
||||
if (url.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (MatchesBaseURI(scheme, host, port, url)) {
|
||||
return true;
|
||||
if (*end == '\0')
|
||||
break;
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(hostList);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -677,54 +683,90 @@ bool
|
|||
nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
|
||||
const nsCSubstring &matchHost,
|
||||
int32_t matchPort,
|
||||
const char *baseStart,
|
||||
const char *baseEnd)
|
||||
nsDependentCSubstring const& url)
|
||||
{
|
||||
// check if scheme://host:port matches baseURI
|
||||
// check if scheme://host:port matches baseURI
|
||||
|
||||
// parse the base URI
|
||||
const char *hostStart, *schemeEnd = strstr(baseStart, "://");
|
||||
if (schemeEnd) {
|
||||
// the given scheme must match the parsed scheme exactly
|
||||
if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
|
||||
return false;
|
||||
hostStart = schemeEnd + 3;
|
||||
}
|
||||
else
|
||||
hostStart = baseStart;
|
||||
// parse the base URI
|
||||
mozilla::Tokenizer t(url);
|
||||
mozilla::Tokenizer::Token token;
|
||||
|
||||
// XXX this does not work for IPv6-literals
|
||||
const char *hostEnd = strchr(hostStart, ':');
|
||||
if (hostEnd && hostEnd < baseEnd) {
|
||||
// the given port must match the parsed port exactly
|
||||
int port = atoi(hostEnd + 1);
|
||||
if (matchPort != (int32_t) port)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
hostEnd = baseEnd;
|
||||
t.SkipWhites();
|
||||
|
||||
// We don't know if the url to check against starts with scheme
|
||||
// or a host name. Start recording here.
|
||||
t.Record();
|
||||
|
||||
// if we didn't parse out a host, then assume we got a match.
|
||||
if (hostStart == hostEnd)
|
||||
return true;
|
||||
mozilla::Unused << t.Next(token);
|
||||
|
||||
uint32_t hostLen = hostEnd - hostStart;
|
||||
|
||||
// matchHost must either equal host or be a subdomain of host
|
||||
if (matchHost.Length() < hostLen)
|
||||
return false;
|
||||
|
||||
const char *end = matchHost.EndReading();
|
||||
if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
|
||||
// if matchHost ends with host from the base URI, then make sure it is
|
||||
// either an exact match, or prefixed with a dot. we don't want
|
||||
// "foobar.com" to match "bar.com"
|
||||
if (matchHost.Length() == hostLen ||
|
||||
*(end - hostLen) == '.' ||
|
||||
*(end - hostLen - 1) == '.')
|
||||
return true;
|
||||
// The ipv6 literals MUST be enclosed with [] in the preference.
|
||||
bool ipv6 = false;
|
||||
if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
|
||||
nsDependentCSubstring ipv6BareLiteral;
|
||||
if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) {
|
||||
// Broken ipv6 literal
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
nsDependentCSubstring ipv6Literal;
|
||||
t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST);
|
||||
if (!matchHost.Equals(ipv6Literal, nsCaseInsensitiveUTF8StringComparator()) &&
|
||||
!matchHost.Equals(ipv6BareLiteral, nsCaseInsensitiveUTF8StringComparator())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ipv6 = true;
|
||||
} else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) {
|
||||
if (!matchScheme.Equals(token.Fragment())) {
|
||||
return false;
|
||||
}
|
||||
// Re-start recording the hostname from the point after scheme://.
|
||||
t.Record();
|
||||
}
|
||||
|
||||
while (t.Next(token)) {
|
||||
bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile());
|
||||
bool port = token.Equals(mozilla::Tokenizer::Token::Char(':'));
|
||||
|
||||
if (eof || port) {
|
||||
if (!ipv6) { // Match already performed above.
|
||||
nsDependentCSubstring hostName;
|
||||
t.Claim(hostName);
|
||||
|
||||
// An empty hostname means to accept everything for the schema
|
||||
if (!hostName.IsEmpty()) {
|
||||
if (hostName.First() == '.') {
|
||||
if (!StringEndsWith(matchHost, hostName, nsCaseInsensitiveUTF8StringComparator())) {
|
||||
return false;
|
||||
}
|
||||
} else { // host to match doesn't begin with '.', do a full compare
|
||||
if (!matchHost.Equals(hostName, nsCaseInsensitiveUTF8StringComparator())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (port) {
|
||||
uint16_t portNumber;
|
||||
if (!t.ReadInteger(&portNumber)) {
|
||||
// Missing port number
|
||||
return false;
|
||||
}
|
||||
if (matchPort != portNumber) {
|
||||
return false;
|
||||
}
|
||||
if (!t.CheckEOF()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (ipv6) {
|
||||
// After an ipv6 literal there can only be EOF or :port. Everything else
|
||||
// must be treated as non-match/broken input.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All negative checks has passed positively.
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -34,10 +34,9 @@ private:
|
|||
bool TestPref(nsIURI *, const char *pref);
|
||||
|
||||
bool MatchesBaseURI(const nsCSubstring &scheme,
|
||||
const nsCSubstring &host,
|
||||
int32_t port,
|
||||
const char *baseStart,
|
||||
const char *baseEnd);
|
||||
const nsCSubstring &host,
|
||||
int32_t port,
|
||||
nsDependentCSubstring const& url);
|
||||
// Thread for GenerateCredentialsAsync
|
||||
RefPtr<mozilla::LazyIdleThread> mNegotiateThread;
|
||||
};
|
||||
|
|
|
@ -130,7 +130,7 @@ nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
rv = xpc->EvalInSandboxObject(convertedScript, filename, cx,
|
||||
autoconfigSb, JSVERSION_LATEST, &v);
|
||||
autoconfigSb, &v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -40,14 +40,36 @@ SourceSurfaceSkia::GetFormat() const
|
|||
return mFormat;
|
||||
}
|
||||
|
||||
static sk_sp<SkData>
|
||||
MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
|
||||
{
|
||||
CheckedInt<size_t> size = aStride;
|
||||
size *= aSize.height;
|
||||
if (size.isValid()) {
|
||||
void* mem = sk_malloc_flags(size.value(), 0);
|
||||
if (mem) {
|
||||
if (aData) {
|
||||
memcpy(mem, aData, size.value());
|
||||
}
|
||||
return SkData::MakeFromMalloc(mem, size.value());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
SourceSurfaceSkia::InitFromData(unsigned char* aData,
|
||||
const IntSize &aSize,
|
||||
int32_t aStride,
|
||||
SurfaceFormat aFormat)
|
||||
{
|
||||
SkPixmap pixmap(MakeSkiaImageInfo(aSize, aFormat), aData, aStride);
|
||||
mImage = SkImage::MakeRasterCopy(pixmap);
|
||||
sk_sp<SkData> data = MakeSkData(aData, aSize, aStride);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
|
||||
mImage = SkImage::MakeRasterData(info, data, aStride);
|
||||
if (!mImage) {
|
||||
return false;
|
||||
}
|
||||
|
@ -106,14 +128,10 @@ SourceSurfaceSkia::GetData()
|
|||
#ifdef USE_SKIA_GPU
|
||||
if (mImage->isTextureBacked()) {
|
||||
sk_sp<SkImage> raster;
|
||||
CheckedInt<size_t> size = mStride;
|
||||
size *= mSize.height;
|
||||
if (size.isValid()) {
|
||||
if (sk_sp<SkData> data = SkData::MakeUninitialized(size.value())) {
|
||||
SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
|
||||
if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
|
||||
raster = SkImage::MakeRasterData(info, data, mStride);
|
||||
}
|
||||
if (sk_sp<SkData> data = MakeSkData(nullptr, mSize, mStride)) {
|
||||
SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
|
||||
if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
|
||||
raster = SkImage::MakeRasterData(info, data, mStride);
|
||||
}
|
||||
}
|
||||
if (raster) {
|
||||
|
|
|
@ -51,7 +51,8 @@ static const char* sEGLExtensionNames[] = {
|
|||
"EGL_ANDROID_native_fence_sync",
|
||||
"EGL_ANDROID_image_crop",
|
||||
"EGL_ANGLE_platform_angle",
|
||||
"EGL_ANGLE_platform_angle_d3d"
|
||||
"EGL_ANGLE_platform_angle_d3d",
|
||||
"EGL_ANGLE_d3d_share_handle_client_buffer"
|
||||
};
|
||||
|
||||
#if defined(ANDROID)
|
||||
|
@ -414,6 +415,7 @@ GLLibraryEGL::EnsureInitialized(bool forceAccel, nsACString* const out_failureId
|
|||
SYMBOL(BindTexImage),
|
||||
SYMBOL(ReleaseTexImage),
|
||||
SYMBOL(QuerySurface),
|
||||
SYMBOL(CreatePbufferFromClientBuffer),
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ public:
|
|||
mSymbols.fDestroySurface = nullptr;
|
||||
mSymbols.fCreateWindowSurface = nullptr;
|
||||
mSymbols.fCreatePbufferSurface = nullptr;
|
||||
mSymbols.fCreatePbufferFromClientBuffer = nullptr;
|
||||
mSymbols.fCreatePixmapSurface = nullptr;
|
||||
mSymbols.fBindAPI = nullptr;
|
||||
mSymbols.fInitialize = nullptr;
|
||||
|
@ -185,6 +186,7 @@ public:
|
|||
EGL_ANDROID_image_crop,
|
||||
ANGLE_platform_angle,
|
||||
ANGLE_platform_angle_d3d,
|
||||
ANGLE_d3d_share_handle_client_buffer,
|
||||
Extensions_Max
|
||||
};
|
||||
|
||||
|
@ -289,6 +291,14 @@ public:
|
|||
return surf;
|
||||
}
|
||||
|
||||
EGLSurface fCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list)
|
||||
{
|
||||
BEFORE_GL_CALL;
|
||||
EGLSurface ret = mSymbols.fCreatePbufferFromClientBuffer(dpy, buftype, buffer, config, attrib_list);
|
||||
AFTER_GL_CALL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EGLSurface fCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list)
|
||||
{
|
||||
BEFORE_GL_CALL;
|
||||
|
@ -585,6 +595,8 @@ public:
|
|||
pfnCreateWindowSurface fCreateWindowSurface;
|
||||
typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list);
|
||||
pfnCreatePbufferSurface fCreatePbufferSurface;
|
||||
typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferFromClientBuffer)(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
|
||||
pfnCreatePbufferFromClientBuffer fCreatePbufferFromClientBuffer;
|
||||
typedef EGLSurface (GLAPIENTRY * pfnCreatePixmapSurface)(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list);
|
||||
pfnCreatePixmapSurface fCreatePixmapSurface;
|
||||
typedef EGLBoolean (GLAPIENTRY * pfnBindAPI)(EGLenum api);
|
||||
|
|
|
@ -107,6 +107,8 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
dateStyle = UDAT_SHORT;
|
||||
break;
|
||||
case kDateFormatYearMonth:
|
||||
case kDateFormatYearMonthLong:
|
||||
case kDateFormatMonthLong:
|
||||
case kDateFormatWeekday:
|
||||
dateStyle = UDAT_PATTERN;
|
||||
break;
|
||||
|
@ -169,7 +171,23 @@ DateTimeFormat::FormatUDateTime(const nsDateFormatSelector aDateFormatSelector,
|
|||
}
|
||||
}
|
||||
|
||||
nsAutoString skeleton(aDateFormatSelector == kDateFormatYearMonth ? u"yyyyMM " : u"EEE ");
|
||||
nsAutoString skeleton;
|
||||
switch (aDateFormatSelector) {
|
||||
case kDateFormatYearMonth:
|
||||
skeleton.AssignLiteral("yyyyMM ");
|
||||
break;
|
||||
case kDateFormatYearMonthLong:
|
||||
skeleton.AssignLiteral("yyyyMMMM ");
|
||||
break;
|
||||
case kDateFormatMonthLong:
|
||||
skeleton.AssignLiteral("MMMM ");
|
||||
break;
|
||||
case kDateFormatWeekday:
|
||||
skeleton.AssignLiteral("EEE ");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int32_t dateSkeletonLen = skeleton.Length();
|
||||
|
||||
if (timeStyle != UDAT_NONE) {
|
||||
|
|
|
@ -13,6 +13,8 @@ enum
|
|||
kDateFormatLong, // provides the long date format for the given locale
|
||||
kDateFormatShort, // provides the short date format for the given locale
|
||||
kDateFormatYearMonth, // formats using only the year and month
|
||||
kDateFormatYearMonthLong, // long version of kDateFormatYearMonth
|
||||
kDateFormatMonthLong, // long format of month name only
|
||||
kDateFormatWeekday // week day (e.g. Mon, Tue)
|
||||
};
|
||||
%}
|
||||
|
|
|
@ -58,6 +58,14 @@ TEST(DateTimeFormat, DateFormatSelectors) {
|
|||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("01/1970", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonthLong, kTimeFormatNone, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January 1970", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatMonthLong, kTimeFormatNone, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("January", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
||||
rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatNoSeconds, &prExplodedTime, formattedTime);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_STREQ("01/1970, 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
|
||||
|
|
|
@ -545,6 +545,22 @@ IProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
|
|||
Manager()->SetEventTargetForActorInternal(aActor, aEventTarget);
|
||||
}
|
||||
|
||||
nsIEventTarget*
|
||||
IProtocol::GetActorEventTarget()
|
||||
{
|
||||
// We should only call this function when this actor has been registered and
|
||||
// is not unregistered yet.
|
||||
MOZ_RELEASE_ASSERT(mId != kNullActorId && mId != kFreedActorId);
|
||||
RefPtr<nsIEventTarget> target = Manager()->GetActorEventTargetInternal(this);
|
||||
return target;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
IProtocol::GetActorEventTargetInternal(IProtocol* aActor)
|
||||
{
|
||||
return Manager()->GetActorEventTargetInternal(aActor);
|
||||
}
|
||||
|
||||
IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId, Side aSide)
|
||||
: IProtocol(aSide),
|
||||
mProtocolId(aProtoId),
|
||||
|
@ -802,7 +818,7 @@ IToplevelProtocol::GetMessageEventTarget(const Message& aMsg)
|
|||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
|
||||
IToplevelProtocol::GetActorEventTargetInternal(IProtocol* aActor)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId && aActor->Id() != kFreedActorId);
|
||||
|
||||
|
@ -811,10 +827,26 @@ IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
|
|||
return target.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIEventTarget>
|
||||
IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
|
||||
{
|
||||
return GetActorEventTargetInternal(aActor);
|
||||
}
|
||||
|
||||
nsIEventTarget*
|
||||
IToplevelProtocol::GetActorEventTarget()
|
||||
{
|
||||
// The EventTarget of a ToplevelProtocol shall never be set.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
IToplevelProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
|
||||
nsIEventTarget* aEventTarget)
|
||||
{
|
||||
// The EventTarget of a ToplevelProtocol shall never be set.
|
||||
MOZ_RELEASE_ASSERT(aActor != this);
|
||||
|
||||
// We should only call this function on actors that haven't been used for IPC
|
||||
// code yet. Otherwise we'll be posting stuff to the wrong event target before
|
||||
// we're called.
|
||||
|
|
|
@ -189,6 +189,9 @@ public:
|
|||
// aActor.
|
||||
void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget);
|
||||
|
||||
// Returns the event target set by SetEventTargetForActor() if available.
|
||||
virtual nsIEventTarget* GetActorEventTarget();
|
||||
|
||||
protected:
|
||||
friend class IToplevelProtocol;
|
||||
|
||||
|
@ -198,6 +201,9 @@ protected:
|
|||
|
||||
virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
|
||||
|
||||
virtual already_AddRefed<nsIEventTarget>
|
||||
GetActorEventTargetInternal(IProtocol* aActor);
|
||||
|
||||
static const int32_t kNullActorId = 0;
|
||||
static const int32_t kFreedActorId = 1;
|
||||
|
||||
|
@ -365,12 +371,18 @@ public:
|
|||
already_AddRefed<nsIEventTarget>
|
||||
GetActorEventTarget(IProtocol* aActor);
|
||||
|
||||
virtual nsIEventTarget*
|
||||
GetActorEventTarget();
|
||||
|
||||
protected:
|
||||
virtual already_AddRefed<nsIEventTarget>
|
||||
GetConstructedEventTarget(const Message& aMsg) { return nullptr; }
|
||||
|
||||
virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
|
||||
|
||||
virtual already_AddRefed<nsIEventTarget>
|
||||
GetActorEventTargetInternal(IProtocol* aActor);
|
||||
|
||||
private:
|
||||
ProtocolId mProtocolId;
|
||||
UniquePtr<Transport> mTrans;
|
||||
|
|
|
@ -19,6 +19,7 @@ ipdl: $(ALL_IPDLSRCS)
|
|||
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) \
|
||||
$(srcdir)/ipdl.py \
|
||||
--sync-msg-list=$(srcdir)/sync-messages.ini \
|
||||
--outheaders-dir=_ipdlheaders \
|
||||
--outcpp-dir=. \
|
||||
$(IPDLDIRS:%=-I%) \
|
||||
|
|
|
@ -7,6 +7,7 @@ from cStringIO import StringIO
|
|||
from mozbuild.pythonutil import iter_modules_in_path
|
||||
import mozpack.path as mozpath
|
||||
import itertools
|
||||
from ConfigParser import RawConfigParser
|
||||
|
||||
import ipdl
|
||||
|
||||
|
@ -20,6 +21,8 @@ op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
|
|||
op.add_option('-I', '--include', dest='includedirs', default=[ ],
|
||||
action='append',
|
||||
help='Additional directory to search for included protocol specifications')
|
||||
op.add_option('-s', '--sync-msg-list', dest='syncMsgList', default='sync-messages.ini',
|
||||
help="Config file listing allowed sync messages")
|
||||
op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
|
||||
help='Verbose logging (specify -vv or -vvv for very verbose logging)')
|
||||
op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
|
||||
|
@ -35,9 +38,9 @@ A protocol Foo in the namespace bar will cause the sources
|
|||
cppdir/FooParent.cpp, cppdir/FooChild.cpp
|
||||
to be generated""")
|
||||
|
||||
|
||||
options, files = op.parse_args()
|
||||
_verbosity = options.verbosity
|
||||
syncMsgList = options.syncMsgList
|
||||
headersdir = options.headersdir
|
||||
cppdir = options.cppdir
|
||||
includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
|
||||
|
@ -113,6 +116,11 @@ def normalizedFilename(f):
|
|||
return '<stdin>'
|
||||
return f
|
||||
|
||||
log(2, 'Reading sync message list')
|
||||
parser = RawConfigParser()
|
||||
parser.readfp(open(options.syncMsgList))
|
||||
syncMsgList = parser.sections()
|
||||
|
||||
# First pass: parse and type-check all protocols
|
||||
for f in files:
|
||||
log(2, os.path.basename(f))
|
||||
|
@ -135,6 +143,10 @@ for f in files:
|
|||
print >>sys.stderr, 'Specification is not well typed.'
|
||||
sys.exit(1)
|
||||
|
||||
if not ipdl.checkSyncMessage(ast, syncMsgList):
|
||||
print >>sys.stderr, 'Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s' % options.syncMsgList
|
||||
sys.exit(1)
|
||||
|
||||
if _verbosity > 2:
|
||||
log(3, ' pretty printed code:')
|
||||
ipdl.genipdl(ast, codedir)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# 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/.
|
||||
|
||||
__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified' ]
|
||||
__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified', 'checkSyncMessage' ]
|
||||
|
||||
import os, sys
|
||||
from cStringIO import StringIO
|
||||
|
@ -11,6 +11,7 @@ from ipdl.cgen import IPDLCodeGen
|
|||
from ipdl.lower import LowerToCxx, msgenums
|
||||
from ipdl.parser import Parser, ParseError
|
||||
from ipdl.type import TypeCheck
|
||||
from ipdl.checker import checkSyncMessage
|
||||
|
||||
from ipdl.cxx.cgen import CxxCodeGen
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# vim: set ts=4 sw=4 tw=99 et:
|
||||
# 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/.
|
||||
|
||||
import sys
|
||||
from ipdl.ast import Visitor, ASYNC
|
||||
|
||||
class SyncMessageChecker(Visitor):
|
||||
def __init__(self, syncMsgList):
|
||||
self.syncMsgList = syncMsgList
|
||||
self.errors = []
|
||||
|
||||
def prettyMsgName(self, msg):
|
||||
return "%s::%s" % (self.currentProtocol, msg)
|
||||
|
||||
def errorUnknownSyncMessage(self, loc, msg):
|
||||
self.errors.append('%s: error: Unknown sync IPC message %s' %
|
||||
(str(loc), msg))
|
||||
|
||||
def errorAsyncMessageCanRemove(self, loc, msg):
|
||||
self.errors.append('%s: error: IPC message %s is async, can be delisted' %
|
||||
(str(loc), msg))
|
||||
|
||||
def visitProtocol(self, p):
|
||||
self.currentProtocol = p.name
|
||||
Visitor.visitProtocol(self, p)
|
||||
|
||||
def visitMessageDecl(self, md):
|
||||
pn = self.prettyMsgName(md.name)
|
||||
if md.sendSemantics is not ASYNC and pn not in self.syncMsgList:
|
||||
self.errorUnknownSyncMessage(md.loc, pn)
|
||||
if md.sendSemantics is ASYNC and pn in self.syncMsgList:
|
||||
self.errorAsyncMessageCanRemove(md.loc, pn)
|
||||
|
||||
def checkSyncMessage(tu, syncMsgList, errout=sys.stderr):
|
||||
checker = SyncMessageChecker(syncMsgList)
|
||||
tu.accept(checker)
|
||||
if len(checker.errors):
|
||||
for error in checker.errors:
|
||||
print >>errout, error
|
||||
return False
|
||||
return True
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -13,5 +13,6 @@ check::
|
|||
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) \
|
||||
$(topsrcdir)/ipc/ipdl/ipdl.py \
|
||||
-s $(srcdir)/sync-messages.ini \
|
||||
OKTESTS $(OKTESTS) \
|
||||
ERRORTESTS $(ERRORTESTS)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
protocol asyncMessageListed {
|
||||
parent:
|
||||
async Msg();
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
intr protocol unknownSyncMessage {
|
||||
parent:
|
||||
intr Msg();
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
sync protocol unknownSyncMessage {
|
||||
parent:
|
||||
sync Msg();
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
[actorreturn::Msg]
|
||||
[array_Union::Msg]
|
||||
[union_Basic::Msg]
|
||||
[Struct::test]
|
||||
[intrProtocol::SyncMsg]
|
||||
[intrProtocol::InterruptMsg]
|
||||
[shmem::SyncMsg]
|
||||
[shmem::InterruptMsg]
|
||||
[syncProtocol::SyncMsg]
|
||||
[messageVerify::__delete__]
|
||||
[messageVerify::msg3]
|
||||
[messageVerify::msg4]
|
||||
[messageVerifyTopLevel::messageVerify]
|
||||
[DeleteSub::__delete__]
|
||||
[intrMessageCompress::foo]
|
||||
[intrMessageCompress::bar]
|
||||
[syncMessageCompress::foo]
|
||||
[syncParentToChild::Msg]
|
||||
[tooWeakIntrSync::Msg]
|
||||
[tooWeakSyncAsync::Msg]
|
||||
[undeclReturnType::Msg]
|
||||
[asyncMessageListed::Msg]
|
|
@ -8052,6 +8052,8 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
|
|||
|
||||
MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
|
||||
RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword));
|
||||
if (!name)
|
||||
return null();
|
||||
if (name == context->names().let) {
|
||||
error(JSMSG_LET_COMP_BINDING);
|
||||
return null();
|
||||
|
|
|
@ -35,6 +35,9 @@ function mismatchError(actual, expect) {
|
|||
return RegExp(str);
|
||||
}
|
||||
|
||||
const emptyStackError = /from empty stack/;
|
||||
const unusedValuesError = /unused values not explicitly dropped by end of block/;
|
||||
|
||||
function jsify(wasmVal) {
|
||||
if (wasmVal === 'nan')
|
||||
return NaN;
|
||||
|
@ -179,4 +182,4 @@ function wasmGetScriptBreakpoints(wasmScript) {
|
|||
result.push({str: line.trim(), line: i + 1, offset: lineOffsets[0]});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
if (helperThreadCount() === 0)
|
||||
quit();
|
||||
setInterruptCallback(function() { print("MainThread Interrupt!"); cooperativeYield(); return true; });
|
||||
evalInCooperativeThread('\
|
||||
setInterruptCallback(function() { print("Worker Interrupt!"); cooperativeYield(); return true; });\
|
||||
for (var i = 0; i < 10; i++) {\
|
||||
print("Worker: " + i);\
|
||||
interruptIf(true);\
|
||||
}\
|
||||
print("Worker Done!");\
|
||||
');
|
||||
for (var i = 0; i < 10; i++) {
|
||||
print("MainThread: " + i);
|
||||
interruptIf(true);
|
||||
}
|
||||
print("MainThread Done!");
|
|
@ -0,0 +1,7 @@
|
|||
if (helperThreadCount() === 0)
|
||||
quit();
|
||||
evalInCooperativeThread("var x = 3");
|
||||
evalInCooperativeThread("cooperativeYield()");
|
||||
cooperativeYield();
|
||||
evalInCooperativeThread("cooperativeYield(); gc();");
|
||||
gc();
|
|
@ -58,8 +58,8 @@ wasmFailValidateText('(module (func) (func) (export "a" 0) (export "a" 1))', /du
|
|||
// ----------------------------------------------------------------------------
|
||||
// signatures
|
||||
|
||||
wasmFailValidateText('(module (func (result i32)))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (nop)))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (nop)))', emptyStackError);
|
||||
|
||||
wasmValidateText('(module (func (nop)))');
|
||||
wasmValidateText('(module (func (result i32) (i32.const 42)))');
|
||||
|
@ -196,7 +196,7 @@ wasmFullPass('(module (func (result f32) (param i32) (local f32) (get_local 1))
|
|||
|
||||
wasmFailValidateText('(module (func (set_local 0 (i32.const 0))))', /set_local index out of range/);
|
||||
wasmFailValidateText('(module (func (local f32) (set_local 0 (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (local f32) (set_local 0 (nop))))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (local f32) (set_local 0 (nop))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (local i32) (local f32) (set_local 0 (get_local 1))))', mismatchError("f32", "i32"));
|
||||
wasmFailValidateText('(module (func (local i32) (local f32) (set_local 1 (get_local 0))))', mismatchError("i32", "f32"));
|
||||
|
||||
|
@ -217,9 +217,9 @@ wasmValidateText('(module (func (local i32) (local $a f32) (set_local 0 (i32.con
|
|||
|
||||
wasmFullPass('(module (func (block )) (export "run" 0))', undefined);
|
||||
|
||||
wasmFailValidateText('(module (func (result i32) (block )))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (block (block ))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (local i32) (set_local 0 (block ))))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (result i32) (block )))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block (block ))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (local i32) (set_local 0 (block ))))', emptyStackError);
|
||||
|
||||
wasmFullPass('(module (func (block (block ))) (export "run" 0))', undefined);
|
||||
wasmFullPass('(module (func (result i32) (block i32 (i32.const 42))) (export "run" 0))', 42);
|
||||
|
@ -234,9 +234,9 @@ wasmFullPass('(module (func (result i32) (local i32) (set_local 0 (i32.const 42)
|
|||
// ----------------------------------------------------------------------------
|
||||
// calls
|
||||
|
||||
wasmFailValidateText('(module (func (nop)) (func (call 0 (i32.const 0))))', /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText('(module (func (nop)) (func (call 0 (i32.const 0))))', unusedValuesError);
|
||||
|
||||
wasmFailValidateText('(module (func (param i32) (nop)) (func (call 0)))', /peeking at value from outside block/);
|
||||
wasmFailValidateText('(module (func (param i32) (nop)) (func (call 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (param f32) (nop)) (func (call 0 (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (nop)) (func (call 3)))', /callee index out of range/);
|
||||
|
||||
|
@ -250,8 +250,8 @@ assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 1)) (func (call 0
|
|||
wasmValidateText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (f32.const nan))))');
|
||||
wasmFailValidateText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
|
||||
wasmFailValidateText('(module (import "a" "") (func (call 0 (i32.const 0))))', /unused values not explicitly dropped by end of block/);
|
||||
wasmFailValidateText('(module (import "a" "" (param i32)) (func (call 0)))', /peeking at value from outside block/);
|
||||
wasmFailValidateText('(module (import "a" "") (func (call 0 (i32.const 0))))', unusedValuesError);
|
||||
wasmFailValidateText('(module (import "a" "" (param i32)) (func (call 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (import "a" "" (param f32)) (func (call 0 (i32.const 0))))', mismatchError("i32", "f32"));
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call 1)))'), TypeError, noImportObj);
|
||||
|
@ -462,11 +462,12 @@ wasmValidateText('(module (import $bar "" "a") (func (call $bar)) (func $foo (no
|
|||
wasmFailValidateText('(module (func (select (i32.const 0) (i32.const 0) (f32.const 0))))', mismatchError("f32", "i32"));
|
||||
|
||||
wasmFailValidateText('(module (func (select (i32.const 0) (f32.const 0) (i32.const 0))) (export "" 0))', /select operand types must match/);
|
||||
wasmFailValidateText('(module (func (select (block ) (i32.const 0) (i32.const 0))) (export "" 0))', /popping value from empty stack/);
|
||||
assertEq(wasmEvalText('(module (func (select (return) (i32.const 0) (i32.const 0))) (export "" 0))').exports[""](), undefined);
|
||||
assertEq(wasmEvalText('(module (func (i32.add (i32.const 0) (select (return) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), undefined);
|
||||
wasmFailValidateText('(module (func (select (block ) (i32.const 0) (i32.const 0))) (export "" 0))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (select (return) (i32.const 0) (i32.const 0))) (export "" 0))', unusedValuesError);
|
||||
assertEq(wasmEvalText('(module (func (drop (select (return) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), undefined);
|
||||
assertEq(wasmEvalText('(module (func (result i32) (i32.add (i32.const 0) (select (return (i32.const 42)) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), 42);
|
||||
wasmFailValidateText('(module (func (select (if i32 (i32.const 1) (i32.const 0) (f32.const 0)) (i32.const 0) (i32.const 0))) (export "" 0))', mismatchError("f32", "i32"));
|
||||
wasmFailValidateText('(module (func) (func (select (call 0) (call 0) (i32.const 0))) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func) (func (select (call 0) (call 0) (i32.const 0))) (export "" 0))', emptyStackError);
|
||||
|
||||
(function testSideEffects() {
|
||||
|
||||
|
|
|
@ -157,11 +157,11 @@ assertEq(counter, 0);
|
|||
|
||||
// "if" doesn't return an expression value
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (i32.const 0))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (drop (i32.const 0)))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (drop (i32.const 0)))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
|
||||
wasmEvalText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))');
|
||||
|
||||
|
@ -170,18 +170,18 @@ wasmEvalText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (i32.cons
|
|||
|
||||
wasmFullPass('(module (func (return)) (export "run" 0))', undefined);
|
||||
wasmFullPass('(module (func (result i32) (return (i32.const 1))) (export "run" 0))', 1);
|
||||
wasmFullPass('(module (func (if (return) (i32.const 0))) (export "run" 0))', undefined);
|
||||
wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (if (return) (i32.const 0))) (export "run" 0))', unusedValuesError);
|
||||
wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', emptyStackError);
|
||||
wasmFullPass('(module (func (return (i32.const 1))) (export "run" 0))', undefined);
|
||||
wasmFailValidateText('(module (func (result f32) (return (i32.const 1))) (export "" 0))', mismatchError("i32", "f32"));
|
||||
wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', emptyStackError);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// br / br_if
|
||||
|
||||
wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" 0))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (br 0)) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" 0))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (br 0)) (export "" 0))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))', emptyStackError);
|
||||
|
||||
const DEPTH_OUT_OF_BOUNDS = /branch depth exceeds current nesting level/;
|
||||
|
||||
|
@ -197,14 +197,14 @@ wasmFailValidateText('(module (func (block (br_if 2 (i32.const 0)))))', DEPTH_OU
|
|||
wasmFailValidateText('(module (func (loop (br_if 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
|
||||
|
||||
wasmFailValidateText(`(module (func (result i32)
|
||||
(block
|
||||
(if
|
||||
(br 0)
|
||||
(i32.const 0)
|
||||
(i32.const 2)
|
||||
)
|
||||
)
|
||||
) (export "" 0))`, mismatchError("void", "i32"));
|
||||
block
|
||||
br 0
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 2
|
||||
end
|
||||
end
|
||||
) (export "" 0))`, unusedValuesError);
|
||||
|
||||
wasmFullPass(`(module (func (block $out (br_if $out (br 0)))) (export "run" 0))`, undefined);
|
||||
|
||||
|
@ -273,6 +273,7 @@ wasmFullPass(`(module (func
|
|||
(i32.const 0)
|
||||
(return (br 0))
|
||||
)
|
||||
drop
|
||||
)
|
||||
(return)
|
||||
) (export "run" 0))`, undefined);
|
||||
|
@ -308,9 +309,9 @@ assertEq(isNonZero(-1), 1);
|
|||
|
||||
// branches with values
|
||||
// br/br_if and block
|
||||
wasmFailValidateText('(module (func (result i32) (br 0)))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func (result i32) (br 0)))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (br 0 (f32.const 42))))', mismatchError("f32", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (block (br 0))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (block (br 0))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block f32 (br 0 (f32.const 42)))))', mismatchError("f32", "i32"));
|
||||
|
||||
wasmFailValidateText(`(module (func (result i32) (param i32) (block (if i32 (get_local 0) (br 0 (i32.const 42))))) (export "" 0))`, /if without else with a result value/);
|
||||
|
@ -319,8 +320,8 @@ wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if (get
|
|||
wasmFullPass('(module (func (result i32) (br 0 (i32.const 42)) (i32.const 13)) (export "run" 0))', 42);
|
||||
wasmFullPass('(module (func (result i32) (block i32 (br 0 (i32.const 42)) (i32.const 13))) (export "run" 0))', 42);
|
||||
|
||||
wasmFailValidateText('(module (func) (func (block i32 (br 0 (call 0)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func) (func (block i32 (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
|
||||
wasmFailValidateText('(module (func) (func (block i32 (br 0 (call 0)) (i32.const 13))) (export "" 0))', emptyStackError);
|
||||
wasmFailValidateText('(module (func) (func (block i32 (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))', emptyStackError);
|
||||
|
||||
var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (i32.const 43))) (export "" 0))`).exports[""];
|
||||
assertEq(f(0), 43);
|
||||
|
@ -414,7 +415,7 @@ assertEq(called, 0);
|
|||
|
||||
// br/br_if and loop
|
||||
wasmFullPass(`(module (func (param i32) (result i32) (loop $out $in i32 (br $out (get_local 0)))) (export "run" 0))`, 1, {}, 1);
|
||||
wasmFullPass(`(module (func (param i32) (result i32) (loop $in (br 1 (get_local 0)))) (export "run" 0))`, 1, {}, 1);
|
||||
wasmFullPass(`(module (func (param i32) (result i32) (loop $in i32 (br 1 (get_local 0)))) (export "run" 0))`, 1, {}, 1);
|
||||
wasmFullPass(`(module (func (param i32) (result i32) (block $out i32 (loop $in i32 (br $out (get_local 0))))) (export "run" 0))`, 1, {}, 1);
|
||||
|
||||
wasmFailValidateText(`(module (func (param i32) (result i32)
|
||||
|
@ -432,11 +433,14 @@ wasmFullPass(`(module
|
|||
(result i32)
|
||||
(local i32)
|
||||
(block $out i32
|
||||
(loop $in
|
||||
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
|
||||
(if (i32.ge_s (get_local 0) (i32.const 7)) (br $out (get_local 0)))
|
||||
(br $in)
|
||||
)
|
||||
(loop $in i32
|
||||
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
|
||||
(if
|
||||
(i32.ge_s (get_local 0) (i32.const 7))
|
||||
(br $out (get_local 0))
|
||||
)
|
||||
(br $in)
|
||||
)
|
||||
)
|
||||
)
|
||||
(export "run" 0))`, 7);
|
||||
|
@ -446,7 +450,7 @@ wasmFullPass(`(module
|
|||
(result i32)
|
||||
(local i32)
|
||||
(block $out i32
|
||||
(loop $in
|
||||
(loop $in i32
|
||||
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
|
||||
(br_if $out (get_local 0) (i32.ge_s (get_local 0) (i32.const 7)))
|
||||
(br $in)
|
||||
|
@ -687,7 +691,7 @@ assertEq(f(2), 2);
|
|||
assertEq(f(3), -1);
|
||||
|
||||
// br_table with values
|
||||
wasmFailValidateText('(module (func (result i32) (block (br_table 0 (i32.const 0)))))', mismatchError("void", "i32"));
|
||||
wasmFailValidateText('(module (func (result i32) (block i32 (br_table 0 (i32.const 0)))))', emptyStackError);
|
||||
wasmFailValidateText('(module (func (result i32) (block i32 (br_table 0 (f32.const 0) (i32.const 0)))))', mismatchError("f32", "i32"));
|
||||
|
||||
wasmFailValidateText(`(module
|
||||
|
@ -730,7 +734,7 @@ assertEq(f(4), 13);
|
|||
|
||||
const UNREACHABLE = /unreachable/;
|
||||
assertErrorMessage(wasmEvalText(`(module (func (unreachable)) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (i32.const 0))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (nop))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (unreachable))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
|
||||
|
|
|
@ -16,7 +16,7 @@ wasmFullPass(`(module
|
|||
i32.const 0
|
||||
call 3
|
||||
i32.const 42
|
||||
f32.add
|
||||
i32.add
|
||||
)
|
||||
(func) (func) (func)
|
||||
(export "run" 0))`, 43);
|
||||
|
|
|
@ -16,7 +16,7 @@ wasmFailValidateText(`(module
|
|||
(func (result i32) (param i32)
|
||||
(loop (if (i32.const 0) (br 0)) (drop (get_local 0))))
|
||||
(export "" 0)
|
||||
)`, mismatchError("void", "i32"));
|
||||
)`, emptyStackError);
|
||||
|
||||
assertEq(wasmEvalText(`(module
|
||||
(func (result i32) (param i32)
|
||||
|
@ -113,19 +113,20 @@ wasmEvalText(`
|
|||
}).exports.foo();
|
||||
|
||||
assertEq(wasmEvalText(`(module (func
|
||||
(return)
|
||||
return
|
||||
(select
|
||||
(loop (i32.const 1))
|
||||
(loop (i32.const 2))
|
||||
(loop i32 (i32.const 1))
|
||||
(loop i32 (i32.const 2))
|
||||
(i32.const 3)
|
||||
)
|
||||
drop
|
||||
) (export "" 0))`).exports[""](), undefined);
|
||||
|
||||
wasmEvalText(`(module (func (result i32)
|
||||
(return (i32.const 0))
|
||||
(select
|
||||
(loop (i32.const 1))
|
||||
(loop (i32.const 2))
|
||||
(loop i32 (i32.const 1))
|
||||
(loop i32 (i32.const 2))
|
||||
(i32.const 3)
|
||||
)
|
||||
))`);
|
||||
|
@ -203,7 +204,7 @@ wasmFailValidateText(`
|
|||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, mismatchError("i32", "void"));
|
||||
)`, /br_table targets must all have the same value type/);
|
||||
|
||||
wasmFailValidateText(`
|
||||
(module
|
||||
|
@ -213,7 +214,7 @@ wasmFailValidateText(`
|
|||
(br_table 1 0 (i32.const 15))
|
||||
)
|
||||
)
|
||||
)`, mismatchError("i32", "void"));
|
||||
)`, /br_table targets must all have the same value type/);
|
||||
|
||||
wasmValidateText(`
|
||||
(module
|
||||
|
|
|
@ -25,7 +25,7 @@ for (let body of bodies) {
|
|||
wasmFullPass(`
|
||||
(module
|
||||
(func $f (param $x i32) (result i32)
|
||||
loop $top
|
||||
loop $top i32
|
||||
get_local $x
|
||||
if
|
||||
get_local $x
|
||||
|
|
|
@ -6,7 +6,13 @@ var m1 = wasmEvalText(
|
|||
`(module
|
||||
(type $type0 (func))
|
||||
(func $func0
|
||||
(select (unreachable) (return (nop)) (loop (i32.const 1))))
|
||||
(select
|
||||
(unreachable)
|
||||
(return (nop))
|
||||
(loop i32 (i32.const 1))
|
||||
)
|
||||
drop
|
||||
)
|
||||
(export "" 0))`).exports[""];
|
||||
|
||||
try {
|
||||
|
@ -20,7 +26,13 @@ var m2 = wasmEvalText(
|
|||
`(module
|
||||
(type $type0 (func))
|
||||
(func $func0
|
||||
(select (i32.const 26) (unreachable) (i32.const 3)))
|
||||
(select
|
||||
(i32.const 26)
|
||||
(unreachable)
|
||||
(i32.const 3)
|
||||
)
|
||||
drop
|
||||
)
|
||||
(export "" 0))`).exports[""];
|
||||
|
||||
try {
|
||||
|
|
|
@ -1401,6 +1401,17 @@
|
|||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-arg-num-vs-arg-num
|
||||
(block
|
||||
(block f32
|
||||
(br_table 0 1 (f32.const 0) (i64.const 0))
|
||||
)
|
||||
(drop)
|
||||
)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-index-void-vs-i32
|
||||
|
|
|
@ -361,8 +361,40 @@
|
|||
"unknown type"
|
||||
)
|
||||
|
||||
;; invalid table
|
||||
|
||||
;; Unbound function in table
|
||||
|
||||
(assert_invalid
|
||||
(module (table anyfunc (elem 0 0)))
|
||||
"unknown function 0"
|
||||
)
|
||||
|
||||
|
||||
;; Invalid bounds for elements
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(table 10 anyfunc)
|
||||
(elem (i32.const 10) $f)
|
||||
(func $f)
|
||||
)
|
||||
"elements segment does not fit"
|
||||
)
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(table 10 anyfunc)
|
||||
(elem (i32.const -1) $f)
|
||||
(func $f)
|
||||
)
|
||||
"elements segment does not fit"
|
||||
)
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(table 10 anyfunc)
|
||||
(elem (i32.const -10) $f)
|
||||
(func $f)
|
||||
)
|
||||
"elements segment does not fit"
|
||||
)
|
||||
|
|
|
@ -156,12 +156,21 @@
|
|||
|
||||
(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined")
|
||||
|
||||
(module $Pt
|
||||
(module
|
||||
(table (import "Mt" "tab") 0 anyfunc)
|
||||
(elem (i32.const 9) $f)
|
||||
(func $f)
|
||||
)
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(table (import "Mt" "tab") 0 anyfunc)
|
||||
(elem (i32.const 10) $f)
|
||||
(func $f)
|
||||
)
|
||||
"elements segment does not fit"
|
||||
)
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(table (import "Mt" "tab") 10 anyfunc)
|
||||
|
@ -240,12 +249,20 @@
|
|||
(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2))
|
||||
(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7))
|
||||
|
||||
(module $Pm
|
||||
(module
|
||||
(memory (import "Mm" "mem") 0)
|
||||
(data (i32.const 1000) "abc")
|
||||
(data (i32.const 0xffff) "a")
|
||||
)
|
||||
|
||||
(module $Qm
|
||||
(assert_unlinkable
|
||||
(module
|
||||
(memory (import "Mm" "mem") 0)
|
||||
(data (i32.const 0x10000) "a")
|
||||
)
|
||||
"data segment does not fit"
|
||||
)
|
||||
|
||||
(module $Pm
|
||||
(memory (import "Mm" "mem") 1 8)
|
||||
|
||||
(func (export "grow") (param $a i32) (result i32)
|
||||
|
@ -253,14 +270,14 @@
|
|||
)
|
||||
)
|
||||
|
||||
(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 1))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 2)) (i32.const 1))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 3))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const 4))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const -1))
|
||||
(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1))
|
||||
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
|
||||
|
||||
(assert_unlinkable
|
||||
(module
|
||||
|
|
|
@ -82,6 +82,14 @@
|
|||
(module (memory 0 1) (data (i32.const 0) "a"))
|
||||
"data segment does not fit"
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (memory 1 2) (data (i32.const -1) "a"))
|
||||
"data segment does not fit"
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (memory 1 2) (data (i32.const -1000) "a"))
|
||||
"data segment does not fit"
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 98304) "b"))
|
||||
"data segment does not fit"
|
||||
|
@ -94,13 +102,20 @@
|
|||
(module (memory 1) (data (i32.const 0x12000) ""))
|
||||
"data segment does not fit"
|
||||
)
|
||||
(assert_unlinkable
|
||||
(module (memory 1 2) (data (i32.const -1) ""))
|
||||
"data segment does not fit"
|
||||
)
|
||||
;; This seems to cause a time-out on Travis.
|
||||
(;assert_unlinkable
|
||||
(module (memory 0x10000) (data (i32.const 0xffffffff) "ab"))
|
||||
"" ;; either out of memory or segment does not fit
|
||||
;)
|
||||
(assert_unlinkable
|
||||
(module (global (import "spectest" "global") i32) (memory 0) (data (get_global 0) "a"))
|
||||
(module
|
||||
(global (import "spectest" "global") i32)
|
||||
(memory 0) (data (get_global 0) "a")
|
||||
)
|
||||
"data segment does not fit"
|
||||
)
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
var importedArgs = ['soft-fail.wast']; load(scriptdir + '../wast.js');
|
|
@ -75,6 +75,9 @@
|
|||
(func (export "as-br_table-value-index") (result i32)
|
||||
(block i32 (br_table 0 0 (i32.const 6) (unreachable)) (i32.const 7))
|
||||
)
|
||||
(func (export "as-br_table-value-and-index") (result i32)
|
||||
(block i32 (br_table 0 0 (unreachable)) (i32.const 8))
|
||||
)
|
||||
|
||||
(func (export "as-return-value") (result i64)
|
||||
(return (unreachable))
|
||||
|
|
|
@ -1,21 +1,35 @@
|
|||
;; Test soft failures
|
||||
;; These are invalid Wasm, but the failure is in dead code, which
|
||||
;; implementations are not required to validate. If they do, they shall
|
||||
;; diagnose the correct error.
|
||||
;; Failures in unreachable code.
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $local-index (unreachable) (drop (get_local 0))))
|
||||
"unknown local"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $global-index (unreachable) (drop (get_global 0))))
|
||||
"unknown global"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $func-index (unreachable) (call 1)))
|
||||
"unknown function"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $label-index (unreachable) (br 1)))
|
||||
"unknown label"
|
||||
)
|
||||
|
||||
(assert_invalid
|
||||
(module (func $type-num-vs-num
|
||||
(unreachable) (drop (i64.eqz (i32.const 0))))
|
||||
)
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-poly-num-vs-num (result i32)
|
||||
(unreachable) (i64.const 0) (i32.const 0) (select)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-poly-transitive-num-vs-num (result i32)
|
||||
(unreachable)
|
||||
(i64.const 0) (i32.const 0) (select)
|
||||
|
@ -24,275 +38,279 @@
|
|||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-const (unreachable) (i32.const 0)))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-result (unreachable) (i32.eqz)))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-result2
|
||||
(unreachable) (i32.const 0) (i32.add)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-poly0 (unreachable) (select)))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select)))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unconsumed-poly2
|
||||
(unreachable) (i32.const 0) (i32.const 0) (select)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-select-any (result i32) (i32.const 1) (if i64 (then (i64.const 0)) (else (unreachable) (select)))))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-after-break
|
||||
(block (br 0) (block (drop (i32.eqz (nop)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-after-break
|
||||
(block (br 0) (drop (i32.eqz (f32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-after-break
|
||||
(block (br 0) (block (drop (f32.eq (i32.const 1)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-after-break
|
||||
(block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-after-break
|
||||
(block (br 0) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-after-break (result i32)
|
||||
(block i32 (i32.const 1) (br 0) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-void-after-break
|
||||
(block (loop (br 1) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-num-after-break (result i32)
|
||||
(loop i32 (br 1 (i32.const 1)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-void-after-break
|
||||
(br 0) (i32.const 1)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-num-after-break (result i32)
|
||||
(br 0 (i32.const 1)) (f32.const 0)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-after-return
|
||||
(return) (block (drop (i32.eqz (nop))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-after-return
|
||||
(return) (drop (i32.eqz (f32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-after-return
|
||||
(return) (block (drop (f32.eq (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-after-return
|
||||
(return) (drop (f32.eq (i32.const 1) (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-after-return
|
||||
(block (return) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-after-return (result i32)
|
||||
(block i32 (i32.const 1) (return (i32.const 0)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-void-after-return
|
||||
(block (loop (return) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-num-after-return (result i32)
|
||||
(loop i32 (return (i32.const 1)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-void-after-return
|
||||
(return) (i32.const 1)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-num-after-return (result i32)
|
||||
(return (i32.const 1)) (f32.const 0)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-after-unreachable
|
||||
(unreachable) (block (drop (i32.eqz (nop))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-after-unreachable
|
||||
(unreachable) (drop (i32.eqz (f32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-after-unreachable
|
||||
(unreachable) (block (drop (f32.eq (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-after-unreachable
|
||||
(unreachable) (drop (f32.eq (i32.const 1) (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-after-unreachable
|
||||
(block (unreachable) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-after-unreachable (result i32)
|
||||
(block i32 (i32.const 1) (unreachable) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-void-after-unreachable
|
||||
(block (loop (unreachable) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-num-after-unreachable (result i32)
|
||||
(loop i32 (unreachable) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-void-after-unreachable
|
||||
(unreachable) (i32.const 1)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-num-after-unreachable (result i32)
|
||||
(unreachable) (f32.const 0)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-after-nested-unreachable
|
||||
(block (unreachable)) (block (drop (i32.eqz (nop))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-after-nested-unreachable
|
||||
(block (unreachable)) (drop (i32.eqz (f32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-after-nested-unreachable
|
||||
(block (unreachable)) (block (drop (f32.eq (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-after-nested-unreachable
|
||||
(block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-after-nested-unreachable
|
||||
(block (block (unreachable)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-after-nested-unreachable
|
||||
(result i32)
|
||||
(block i32 (i32.const 1) (block (unreachable)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-void-after-nested-unreachable
|
||||
(block (loop (block (unreachable)) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-num-after-nested-unreachable
|
||||
(result i32)
|
||||
(loop i32 (block (unreachable)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-void-after-nested-unreachable
|
||||
(block (unreachable)) (i32.const 1)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-num-after-nested-unreachable
|
||||
(result i32)
|
||||
(block (unreachable)) (f32.const 0)
|
||||
|
@ -300,243 +318,275 @@
|
|||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-after-infinite-loop
|
||||
(loop (br 0)) (block (drop (i32.eqz (nop))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-after-infinite-loop
|
||||
(loop (br 0)) (drop (i32.eqz (f32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-after-infinite-loop
|
||||
(loop (br 0)) (block (drop (f32.eq (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-after-infinite-loop
|
||||
(loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-after-infinite-loop
|
||||
(block (loop (br 0)) (i32.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-after-infinite-loop (result i32)
|
||||
(block i32 (i32.const 1) (loop (br 0)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-void-after-infinite-loop
|
||||
(block (loop (loop (br 0)) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32)
|
||||
(loop i32 (loop (br 0)) (f32.const 0))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-void-after-infinite-loop
|
||||
(loop (br 0)) (i32.const 1)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-func-value-num-vs-num-after-infinite-loop (result i32)
|
||||
(loop (br 0)) (f32.const 0)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-void-in-dead-body
|
||||
(if (i32.const 0) (then (drop (i32.eqz (nop)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-unary-num-vs-num-in-dead-body
|
||||
(if (i32.const 0) (then (drop (i32.eqz (f32.const 1)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-void-in-dead-body
|
||||
(if (i32.const 0) (then (drop (f32.eq (i32.const 1)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-binary-num-vs-num-in-dead-body
|
||||
(if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-if-value-num-vs-void-in-dead-body
|
||||
(if (i32.const 0) (then (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-if-value-num-vs-num-in-dead-body (result i32)
|
||||
(if i32 (i32.const 0) (then (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-in-dead-body
|
||||
(if (i32.const 0) (then (block (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-in-dead-body (result i32)
|
||||
(if i32 (i32.const 0) (then (block i32 (f32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-void-in-dead-body
|
||||
(if (i32.const 0) (then (loop (i32.const 1))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-num-vs-num-in-dead-body (result i32)
|
||||
(if i32 (i32.const 0) (then (loop i32 (f32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-return-second-num-vs-num (result i32)
|
||||
(return (i32.const 1)) (return (f64.const 1))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-br-second-num-vs-num (result i32)
|
||||
(block i32 (br 0 (i32.const 1)) (br 0 (f64.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-br_if-cond-num-vs-num-after-unreachable
|
||||
(block (br_if 0 (unreachable) (f32.const 0)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-br_table-num-vs-num-after-unreachable
|
||||
(block (br_table 0 (unreachable) (f32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-br_table-label-num-vs-num-after-unreachable (result i32)
|
||||
(block i32 (unreachable) (br_table 0 (f32.const 0) (i32.const 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-br_table-label-num-vs-label-void-after-unreachable
|
||||
(block
|
||||
(block f32
|
||||
(unreachable)
|
||||
(br_table 0 1 0 (i32.const 1))
|
||||
)
|
||||
(drop)
|
||||
)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_invalid
|
||||
(module (func $type-br_table-label-num-vs-label-num-after-unreachable
|
||||
(block f64
|
||||
(block f32
|
||||
(unreachable)
|
||||
(br_table 0 1 1 (i32.const 1))
|
||||
)
|
||||
(drop)
|
||||
(f64.const 0)
|
||||
)
|
||||
(drop)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-unreachable-num-vs-void
|
||||
(block (i32.const 3) (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-unreachable-void-vs-num (result i32)
|
||||
(block (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-unreachable-num-vs-num (result i32)
|
||||
(block i64 (i64.const 0) (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-unreachable-num2-vs-void (result i32)
|
||||
(block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-br-num-vs-void
|
||||
(block (i32.const 3) (block (br 1)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-br-void-vs-num (result i32)
|
||||
(block i32 (block (br 1 (i32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-br-num-vs-num (result i32)
|
||||
(block i32 (i64.const 0) (block (br 1 (i32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested2-br-num-vs-void
|
||||
(block (block (i32.const 3) (block (br 2))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested2-br-void-vs-num (result i32)
|
||||
(block i32 (block (block (br 2 (i32.const 0)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested2-br-num-vs-num (result i32)
|
||||
(block i32 (block i64 (i64.const 0) (block (br 2 (i32.const 0)))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested2-br-num2-vs-void (result i32)
|
||||
(block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9)
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-return-num-vs-void
|
||||
(block (i32.const 3) (block (return)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-return-void-vs-num (result i32)
|
||||
(block (block (return (i32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-return-num-vs-num (result i32)
|
||||
(block i64 (i64.const 0) (block (return (i32.const 0))))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-block-value-nested-return-num2-vs-void (result i32)
|
||||
(block (i32.const 3) (block (i64.const 1) (return (i32.const 0))))
|
||||
(i32.const 9)
|
||||
|
@ -544,32 +594,32 @@
|
|||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-nested-unreachable-num-vs-void
|
||||
(loop (i32.const 3) (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-nested-unreachable-void-vs-num (result i32)
|
||||
(loop (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-loop-value-nested-unreachable-num-vs-num (result i32)
|
||||
(loop i64 (i64.const 0) (block (unreachable)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-cont-last-void-vs-empty (result i32)
|
||||
(loop (br 0 (nop)))
|
||||
))
|
||||
"type mismatch"
|
||||
)
|
||||
(assert_soft_invalid
|
||||
(assert_invalid
|
||||
(module (func $type-cont-last-num-vs-empty (result i32)
|
||||
(loop (br 0 (i32.const 0)))
|
||||
))
|
|
@ -0,0 +1 @@
|
|||
var importedArgs = ['unreached-invalid.wast']; load(scriptdir + '../wast.js');
|
|
@ -62,7 +62,7 @@ wasmFailValidateText(
|
|||
)
|
||||
(export "test" 0)
|
||||
(memory 1 10)
|
||||
)`, /popping value from empty stack/);
|
||||
)`, emptyStackError);
|
||||
|
||||
// function calls
|
||||
runTest(`
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
load(libdir + "wasm.js");
|
||||
|
||||
// In unreachable code, the current design is that validation is disabled,
|
||||
// meaning we have to have a special mode in the decoder for decoding code
|
||||
// that won't actually run.
|
||||
|
||||
wasmFullPass(`(module
|
||||
(func (result i32)
|
||||
(return (i32.const 42))
|
||||
(i32.add (f64.const 1.0) (f32.const 0.0))
|
||||
(return (f64.const 2.0))
|
||||
(if (f32.const 3.0) (i64.const 2) (i32.const 1))
|
||||
(select (f64.const -5.0) (f32.const 2.3) (f64.const 8.9))
|
||||
)
|
||||
(export "run" 0)
|
||||
)`, 42);
|
||||
|
||||
wasmFullPass(`(module
|
||||
(func (result i32) (param i32)
|
||||
(block
|
||||
(br_if 1 (i32.const 41) (get_local 0))
|
||||
(br 1 (i32.const 42))
|
||||
)
|
||||
(i32.add (f32.const 0.0) (f64.const 1.0))
|
||||
(return (f64.const 2.0))
|
||||
(if (f32.const 3.0) (i64.const 2) (i32.const 1))
|
||||
(select (f64.const -5.0) (f32.const 2.3) (f64.const 8.9))
|
||||
)
|
||||
(export "run" 0)
|
||||
)`, 42, {}, 0);
|
|
@ -435,23 +435,6 @@ function exec(e) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (exprName === "assert_soft_invalid") {
|
||||
let moduleText = e.list[1].toString();
|
||||
let errMsg = e.list[2];
|
||||
if (errMsg) {
|
||||
assert(errMsg.quoted, "assert_soft_invalid second argument must be a string");
|
||||
errMsg.quoted = false;
|
||||
}
|
||||
|
||||
try {
|
||||
new WebAssembly.Module(wasmTextToBinary(moduleText));
|
||||
} catch(e) {
|
||||
debug('assert_soft_invalid caught:\nExpected:', errMsg, '\nActual:', e.toString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (exprName === 'assert_trap') {
|
||||
let caught = false;
|
||||
let errMsg = e.list[2];
|
||||
|
|
|
@ -474,6 +474,24 @@ JS_NewContext(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRunt
|
|||
return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSContext*)
|
||||
JS_NewCooperativeContext(JSContext* siblingContext)
|
||||
{
|
||||
return NewCooperativeContext(siblingContext);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_YieldCooperativeContext(JSContext* cx)
|
||||
{
|
||||
YieldCooperativeContext(cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_ResumeCooperativeContext(JSContext* cx)
|
||||
{
|
||||
ResumeCooperativeContext(cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_DestroyContext(JSContext* cx)
|
||||
{
|
||||
|
|
|
@ -981,11 +981,37 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun);
|
|||
* See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
|
||||
*/
|
||||
|
||||
// Create a new runtime, with a single cooperative context for this thread.
|
||||
// On success, the new context will be the active context for the runtime.
|
||||
extern JS_PUBLIC_API(JSContext*)
|
||||
JS_NewContext(uint32_t maxbytes,
|
||||
uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
|
||||
JSRuntime* parentRuntime = nullptr);
|
||||
|
||||
// The methods below for controlling the active context in a cooperatively
|
||||
// multithreaded runtime are not threadsafe, and the caller must ensure they
|
||||
// are called serially if there is a chance for contention between threads.
|
||||
|
||||
// Called from the active context for a runtime, yield execution so that
|
||||
// this context is no longer active and can no longer use the API.
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_YieldCooperativeContext(JSContext* cx);
|
||||
|
||||
// Called from a context whose runtime has no active context, this thread
|
||||
// becomes the active context for that runtime and may use the API.
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ResumeCooperativeContext(JSContext* cx);
|
||||
|
||||
// Create a new context on this thread for cooperative multithreading in the
|
||||
// same runtime as siblingContext. Called on a runtime (as indicated by
|
||||
// siblingContet) which has no active context, on success the new context will
|
||||
// become the runtime's active context.
|
||||
extern JS_PUBLIC_API(JSContext*)
|
||||
JS_NewCooperativeContext(JSContext* siblingContext);
|
||||
|
||||
// Destroy a context allocated with JS_NewContext or JS_NewCooperativeContext.
|
||||
// The context must be the current active context in the runtime, and after
|
||||
// this call the runtime will have no active context.
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_DestroyContext(JSContext* cx);
|
||||
|
||||
|
|
|
@ -99,36 +99,43 @@ js::TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set)
|
|||
}
|
||||
|
||||
bool
|
||||
JSContext::init()
|
||||
JSContext::init(ContextKind kind)
|
||||
{
|
||||
// Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.
|
||||
// Skip most of the initialization if this thread will not be running JS.
|
||||
if (kind == ContextKind::Cooperative) {
|
||||
// Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.
|
||||
#ifdef XP_WIN
|
||||
size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
|
||||
THREAD_QUERY_INFORMATION;
|
||||
HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
|
||||
if (!self)
|
||||
size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
|
||||
THREAD_QUERY_INFORMATION;
|
||||
HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
|
||||
if (!self)
|
||||
return false;
|
||||
static_assert(sizeof(HANDLE) <= sizeof(threadNative_), "need bigger field");
|
||||
threadNative_ = (size_t)self;
|
||||
static_assert(sizeof(HANDLE) <= sizeof(threadNative_), "need bigger field");
|
||||
threadNative_ = (size_t)self;
|
||||
#else
|
||||
static_assert(sizeof(pthread_t) <= sizeof(threadNative_), "need bigger field");
|
||||
threadNative_ = (size_t)pthread_self();
|
||||
static_assert(sizeof(pthread_t) <= sizeof(threadNative_), "need bigger field");
|
||||
threadNative_ = (size_t)pthread_self();
|
||||
#endif
|
||||
|
||||
if (!regexpStack.ref().init())
|
||||
return false;
|
||||
if (!regexpStack.ref().init())
|
||||
return false;
|
||||
|
||||
if (!fx.initInstance())
|
||||
return false;
|
||||
if (!fx.initInstance())
|
||||
return false;
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
simulator_ = js::jit::Simulator::Create(this);
|
||||
if (!simulator_)
|
||||
return false;
|
||||
simulator_ = js::jit::Simulator::Create(this);
|
||||
if (!simulator_)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (!wasm::EnsureSignalHandlers(this))
|
||||
return false;
|
||||
if (!wasm::EnsureSignalHandlers(this))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the ContextKind last, so that ProtectedData checks will allow us to
|
||||
// initialize this context before it becomes the runtime's active context.
|
||||
kind_ = kind;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -156,8 +163,7 @@ js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRun
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (!cx->init()) {
|
||||
if (!cx->init(ContextKind::Cooperative)) {
|
||||
js_delete(cx);
|
||||
js_delete(runtime);
|
||||
return nullptr;
|
||||
|
@ -166,6 +172,39 @@ js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRun
|
|||
return cx;
|
||||
}
|
||||
|
||||
JSContext*
|
||||
js::NewCooperativeContext(JSContext* siblingContext)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!TlsContext.get());
|
||||
|
||||
JSRuntime* runtime = siblingContext->runtime();
|
||||
|
||||
JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
|
||||
if (!cx || !cx->init(ContextKind::Cooperative)) {
|
||||
js_delete(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
runtime->setNewbornActiveContext(cx);
|
||||
return cx;
|
||||
}
|
||||
|
||||
void
|
||||
js::YieldCooperativeContext(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(cx == TlsContext.get());
|
||||
MOZ_ASSERT(cx->runtime()->activeContext() == cx);
|
||||
cx->runtime()->setActiveContext(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
js::ResumeCooperativeContext(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(cx == TlsContext.get());
|
||||
MOZ_ASSERT(cx->runtime()->activeContext() == nullptr);
|
||||
cx->runtime()->setActiveContext(cx);
|
||||
}
|
||||
|
||||
void
|
||||
js::DestroyContext(JSContext* cx)
|
||||
{
|
||||
|
@ -186,6 +225,8 @@ js::DestroyContext(JSContext* cx)
|
|||
// Destroy the runtime along with its last context.
|
||||
cx->runtime()->destroyRuntime();
|
||||
js_delete(cx->runtime());
|
||||
|
||||
js_delete_poison(cx);
|
||||
} else {
|
||||
DebugOnly<bool> found = false;
|
||||
for (size_t i = 0; i < cx->runtime()->cooperatingContexts().length(); i++) {
|
||||
|
@ -197,9 +238,9 @@ js::DestroyContext(JSContext* cx)
|
|||
}
|
||||
}
|
||||
MOZ_ASSERT(found);
|
||||
}
|
||||
|
||||
js_delete_poison(cx);
|
||||
cx->runtime()->deleteActiveContext(cx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1092,6 +1133,7 @@ JSContext::alreadyReportedError()
|
|||
|
||||
JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
||||
: runtime_(runtime),
|
||||
kind_(ContextKind::Background),
|
||||
threadNative_(0),
|
||||
helperThread_(nullptr),
|
||||
options_(options),
|
||||
|
@ -1171,15 +1213,20 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|||
{
|
||||
MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
|
||||
JS::RootingContext::get(this));
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
|
||||
nativeStackQuota[i] = 0;
|
||||
|
||||
MOZ_ASSERT(!TlsContext.get());
|
||||
TlsContext.set(this);
|
||||
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
|
||||
nativeStackQuota[i] = 0;
|
||||
}
|
||||
|
||||
JSContext::~JSContext()
|
||||
{
|
||||
// Clear the ContextKind first, so that ProtectedData checks will allow us to
|
||||
// destroy this context even if the runtime is already gone.
|
||||
kind_ = ContextKind::Background;
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (threadNative_)
|
||||
CloseHandle((HANDLE)threadNative_.ref());
|
||||
|
|
|
@ -80,6 +80,12 @@ void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
|
|||
/* Thread Local Storage slot for storing the context for a thread. */
|
||||
extern MOZ_THREAD_LOCAL(JSContext*) TlsContext;
|
||||
|
||||
enum class ContextKind
|
||||
{
|
||||
Cooperative,
|
||||
Background
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
|
@ -89,13 +95,14 @@ extern MOZ_THREAD_LOCAL(JSContext*) TlsContext;
|
|||
struct JSContext : public JS::RootingContext,
|
||||
public js::MallocProvider<JSContext>
|
||||
{
|
||||
explicit JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
|
||||
JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
|
||||
~JSContext();
|
||||
|
||||
bool init();
|
||||
bool init(js::ContextKind kind);
|
||||
|
||||
private:
|
||||
js::UnprotectedData<JSRuntime*> runtime_;
|
||||
js::WriteOnceData<js::ContextKind> kind_;
|
||||
|
||||
// System handle for the thread this context is associated with.
|
||||
js::WriteOnceData<size_t> threadNative_;
|
||||
|
@ -112,6 +119,7 @@ struct JSContext : public JS::RootingContext,
|
|||
// currently operating on.
|
||||
void setRuntime(JSRuntime* rt);
|
||||
|
||||
bool isCooperativelyScheduled() const { return kind_ == js::ContextKind::Cooperative; }
|
||||
size_t threadNative() const { return threadNative_; }
|
||||
|
||||
inline js::gc::ArenaLists* arenas() const { return arenas_; }
|
||||
|
@ -639,7 +647,7 @@ struct JSContext : public JS::RootingContext,
|
|||
const js::AutoCycleDetector::Set& cycleDetectorSet() const { return cycleDetectorSet_.ref(); }
|
||||
|
||||
/* Client opaque pointer. */
|
||||
void* data;
|
||||
js::UnprotectedData<void*> data;
|
||||
|
||||
void initJitStackLimit();
|
||||
void resetJitStackLimit();
|
||||
|
@ -951,6 +959,15 @@ struct MOZ_RAII AutoResolving {
|
|||
extern JSContext*
|
||||
NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime);
|
||||
|
||||
extern JSContext*
|
||||
NewCooperativeContext(JSContext* siblingContext);
|
||||
|
||||
extern void
|
||||
YieldCooperativeContext(JSContext* cx);
|
||||
|
||||
extern void
|
||||
ResumeCooperativeContext(JSContext* cx);
|
||||
|
||||
extern void
|
||||
DestroyContext(JSContext* cx);
|
||||
|
||||
|
|
|
@ -984,9 +984,10 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
|
|||
if (op == JSOP_TRY) {
|
||||
TryNoteArray* trynotes = script->trynotes();
|
||||
uint32_t i;
|
||||
size_t mainOffset = script->mainOffset();
|
||||
for(i = 0; i < trynotes->length; i++) {
|
||||
JSTryNote note = trynotes->vector[i];
|
||||
if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
|
||||
if (note.kind == JSTRY_CATCH && note.start + mainOffset == loc + 1) {
|
||||
if (!sp->jsprintf(" %u (%+d)",
|
||||
unsigned(loc + note.length + 1),
|
||||
int(note.length + 1)))
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче