diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index ff995df11e46..26e471264a55 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -10,6 +10,7 @@ #include "ApplicationAccessibleWrap.h" #include "InterfaceInitFuncs.h" #include "nsAccUtils.h" +#include "mozilla/a11y/PDocAccessible.h" #include "ProxyAccessible.h" #include "RootAccessible.h" #include "nsMai.h" @@ -771,7 +772,27 @@ AtkAttributeSet * getAttributesCB(AtkObject *aAtkObj) { AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); - return accWrap ? GetAttributeSet(accWrap) : nullptr; + if (accWrap) + return GetAttributeSet(accWrap); + + ProxyAccessible* proxy = GetProxy(aAtkObj); + if (!proxy) + return nullptr; + + nsAutoTArray attrs; + proxy->Attributes(&attrs); + if (attrs.IsEmpty()) + return nullptr; + + AtkAttributeSet* objAttributeSet = nullptr; + for (uint32_t i = 0; i < attrs.Length(); i++) { + AtkAttribute *objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute)); + objAttr->name = g_strdup(attrs[i].Name().get()); + objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(attrs[i].Value()).get()); + objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); + } + + return objAttributeSet; } const gchar* diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build index 96fc8cee15d7..557ca5d47b23 100644 --- a/accessible/atk/moz.build +++ b/accessible/atk/moz.build @@ -49,3 +49,5 @@ if CONFIG['MOZ_ENABLE_GTK']: if CONFIG['MOZ_ENABLE_DBUS']: CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS'] + + include('/ipc/chromium/chromium-config.mozbuild') diff --git a/accessible/ipc/DocAccessibleChild.cpp b/accessible/ipc/DocAccessibleChild.cpp index 75e76b079cd6..678e021a8920 100644 --- a/accessible/ipc/DocAccessibleChild.cpp +++ b/accessible/ipc/DocAccessibleChild.cpp @@ -8,6 +8,9 @@ #include "Accessible-inl.h" +#include "nsIPersistentProperties2.h" +#include "nsISimpleEnumerator.h" + namespace mozilla { namespace a11y { @@ -76,5 +79,43 @@ DocAccessibleChild::RecvDescription(const uint64_t& aID, nsString* aDesc) acc->Description(*aDesc); return true; } + +bool +DocAccessibleChild::RecvAttributes(const uint64_t& aID, nsTArray* aAttributes) +{ + Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID); + if (!acc) + return true; + + nsCOMPtr props = acc->Attributes(); + if (!props) + return true; + + nsCOMPtr propEnum; + nsresult rv = props->Enumerate(getter_AddRefs(propEnum)); + NS_ENSURE_SUCCESS(rv, false); + + bool hasMore; + while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr sup; + rv = propEnum->GetNext(getter_AddRefs(sup)); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr propElem(do_QueryInterface(sup)); + NS_ENSURE_TRUE(propElem, false); + + nsAutoCString name; + rv = propElem->GetKey(name); + NS_ENSURE_SUCCESS(rv, false); + + nsAutoString value; + rv = propElem->GetValue(value); + NS_ENSURE_SUCCESS(rv, false); + + aAttributes->AppendElement(Attribute(name, value)); + } + + return true; +} } } diff --git a/accessible/ipc/DocAccessibleChild.h b/accessible/ipc/DocAccessibleChild.h index 68c417795991..13ccf4cbcb37 100644 --- a/accessible/ipc/DocAccessibleChild.h +++ b/accessible/ipc/DocAccessibleChild.h @@ -48,6 +48,8 @@ public: */ virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE; + virtual bool RecvAttributes(const uint64_t& aID, nsTArray *aAttributes) MOZ_OVERRIDE; + private: DocAccessible* mDoc; }; diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl index a517160a783c..1b7fe86f8637 100644 --- a/accessible/ipc/PDocAccessible.ipdl +++ b/accessible/ipc/PDocAccessible.ipdl @@ -23,6 +23,12 @@ struct ShowEventData AccessibleData[] NewTree; }; +struct Attribute +{ + nsCString Name; + nsString Value; +}; + prio(normal upto high) sync protocol PDocAccessible { manager PContent; @@ -42,6 +48,7 @@ child: prio(high) sync State(uint64_t aID) returns(uint64_t states); prio(high) sync Name(uint64_t aID) returns(nsString name); prio(high) sync Description(uint64_t aID) returns(nsString desc); + prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes); }; } diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp index b84ba9653851..61b2861e655e 100644 --- a/accessible/ipc/ProxyAccessible.cpp +++ b/accessible/ipc/ProxyAccessible.cpp @@ -59,5 +59,11 @@ ProxyAccessible::Description(nsString& aDesc) const { unused << mDoc->SendDescription(mID, &aDesc); } + +void +ProxyAccessible::Attributes(nsTArray *aAttrs) const +{ + unused << mDoc->SendAttributes(mID, aAttrs); +} } } diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h index f820ab4ca962..a6383b0dbc91 100644 --- a/accessible/ipc/ProxyAccessible.h +++ b/accessible/ipc/ProxyAccessible.h @@ -14,6 +14,7 @@ namespace mozilla { namespace a11y { +class Attribute; class DocAccessibleParent; class ProxyAccessible @@ -68,6 +69,11 @@ public: */ void Description(nsString& aDesc) const; + /** + * Get the set of attributes on the proxied accessible. + */ + void Attributes(nsTArray *aAttrs) const; + /** * Allow the platform to store a pointers worth of data on us. */ diff --git a/addon-sdk/source/lib/sdk/util/iteration.js b/addon-sdk/source/lib/sdk/util/iteration.js index b0ed01e4f0ea..d15fb351cbc8 100644 --- a/addon-sdk/source/lib/sdk/util/iteration.js +++ b/addon-sdk/source/lib/sdk/util/iteration.js @@ -7,20 +7,11 @@ module.metadata = { "stability": "experimental" }; -// This is known as @@iterator in the ES6 spec. Until it is bound to -// some well-known name, find the @@iterator object by expecting it as -// the first property accessed on a for-of iterable. -const iteratorSymbol = (function() { - try { - for (var _ of Proxy.create({get: function(_, name) { throw name; } })) - break; - } catch (name) { - return name; - } - throw new TypeError; -})(); - -exports.iteratorSymbol = iteratorSymbol; +// This is known as @@iterator in the ES6 spec. In builds that have ES6 +// Symbols, use Symbol.iterator; otherwise use the legacy method name, +// "@@iterator". +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +exports.iteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; // An adaptor that, given an object that is iterable with for-of, is // suitable for being bound to __iterator__ in order to make the object diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm index 904337e9c830..0aaf2ce9b65c 100644 --- a/browser/components/customizableui/CustomizableUI.jsm +++ b/browser/components/customizableui/CustomizableUI.jsm @@ -50,6 +50,13 @@ const kSubviewEvents = [ "ViewHiding" ]; +/** + * The method name to use for ES6 iteration. If Symbols are enabled in + * this build, use Symbol.iterator; otherwise "@@iterator". + */ +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const kIteratorSymbol = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + /** * The current version. We can use this to auto-add new default widgets as necessary. * (would be const but isn't because of testing purposes) @@ -2679,7 +2686,7 @@ this.CustomizableUI = { * for (let window of CustomizableUI.windows) { ... } */ windows: { - "@@iterator": function*() { + *[kIteratorSymbol]() { for (let [window,] of gBuildWindows) yield window; } diff --git a/browser/devtools/debugger/debugger-panes.js b/browser/devtools/debugger/debugger-panes.js index 7c308449f547..b732b956c347 100644 --- a/browser/devtools/debugger/debugger-panes.js +++ b/browser/devtools/debugger/debugger-panes.js @@ -3260,10 +3260,15 @@ LineResults.prototype = { /** * A generator-iterator over the global, source or line results. + * + * The method name depends on whether symbols are enabled in + * this build. If so, use Symbol.iterator; otherwise "@@iterator". */ -GlobalResults.prototype["@@iterator"] = -SourceResults.prototype["@@iterator"] = -LineResults.prototype["@@iterator"] = function*() { +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; +GlobalResults.prototype[ITERATOR_SYMBOL] = +SourceResults.prototype[ITERATOR_SYMBOL] = +LineResults.prototype[ITERATOR_SYMBOL] = function*() { yield* this._store; }; diff --git a/browser/devtools/framework/gDevTools.jsm b/browser/devtools/framework/gDevTools.jsm index fcd91c2f2ac0..4556c2d253b8 100644 --- a/browser/devtools/framework/gDevTools.jsm +++ b/browser/devtools/framework/gDevTools.jsm @@ -25,6 +25,13 @@ const EventEmitter = devtools.require("devtools/toolkit/event-emitter"); const FORBIDDEN_IDS = new Set(["toolbox", ""]); const MAX_ORDINAL = 99; +/** + * The method name to use for ES6 iteration. If symbols are enabled in this + * build, use Symbol.iterator; otherwise "@@iterator". + */ +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + /** * DevTools is a class that represents a set of developer tools, it holds a * set of tools and keeps track of open toolboxes in the browser. @@ -486,7 +493,7 @@ DevTools.prototype = { /** * Iterator that yields each of the toolboxes. */ - '@@iterator': function*() { + *[ITERATOR_SYMBOL]() { for (let toolbox of this._toolboxes) { yield toolbox; } diff --git a/browser/devtools/shared/widgets/VariablesView.jsm b/browser/devtools/shared/widgets/VariablesView.jsm index b9d2009fa2ab..39cd5e97d781 100644 --- a/browser/devtools/shared/widgets/VariablesView.jsm +++ b/browser/devtools/shared/widgets/VariablesView.jsm @@ -18,6 +18,13 @@ const PAGE_SIZE_MAX_JUMPS = 30; const SEARCH_ACTION_MAX_DELAY = 300; // ms const ITEM_FLASH_DURATION = 300 // ms +/** + * The method name to use for ES6 iteration. If symbols are enabled in + * this build, use Symbol.iterator; otherwise "@@iterator". + */ +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); @@ -3056,10 +3063,10 @@ Property.prototype = Heritage.extend(Variable.prototype, { /** * A generator-iterator over the VariablesView, Scopes, Variables and Properties. */ -VariablesView.prototype["@@iterator"] = -Scope.prototype["@@iterator"] = -Variable.prototype["@@iterator"] = -Property.prototype["@@iterator"] = function*() { +VariablesView.prototype[ITERATOR_SYMBOL] = +Scope.prototype[ITERATOR_SYMBOL] = +Variable.prototype[ITERATOR_SYMBOL] = +Property.prototype[ITERATOR_SYMBOL] = function*() { yield* this._store; }; diff --git a/browser/devtools/shared/widgets/ViewHelpers.jsm b/browser/devtools/shared/widgets/ViewHelpers.jsm index 68b4be71259d..5d3647741145 100644 --- a/browser/devtools/shared/widgets/ViewHelpers.jsm +++ b/browser/devtools/shared/widgets/ViewHelpers.jsm @@ -13,6 +13,13 @@ const PANE_APPEARANCE_DELAY = 50; const PAGE_SIZE_ITEM_COUNT_RATIO = 5; const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]); +/** + * The method name to use for ES6 iteration. If symbols are enabled in + * this build, use Symbol.iterator; otherwise "@@iterator". + */ +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Timer.jsm"); @@ -1729,7 +1736,7 @@ this.WidgetMethods = { /** * A generator-iterator over all the items in this container. */ -Item.prototype["@@iterator"] = -WidgetMethods["@@iterator"] = function*() { +Item.prototype[ITERATOR_SYMBOL] = +WidgetMethods[ITERATOR_SYMBOL] = function*() { yield* this._itemsByElement.values(); }; diff --git a/configure.in b/configure.in index 18833d8f03d1..89ad80d4cfe9 100644 --- a/configure.in +++ b/configure.in @@ -6559,6 +6559,9 @@ case "$OS_TARGET:$NIGHTLY_BUILD" in WINNT:1) MOZ_CONTENT_SANDBOX=$MOZ_SANDBOX ;; +Darwin:1) + MOZ_CONTENT_SANDBOX=$MOZ_SANDBOX + ;; *) MOZ_ARG_ENABLE_BOOL(content-sandbox, [ --enable-content-sandbox Enable sandboxing support for content-processes], diff --git a/dom/base/nsCSPContext.cpp b/dom/base/nsCSPContext.cpp index 522b20193616..1c55efc5eb5a 100644 --- a/dom/base/nsCSPContext.cpp +++ b/dom/base/nsCSPContext.cpp @@ -815,6 +815,7 @@ class CSPReportSenderRunnable MOZ_FINAL : public nsRunnable , mInnerWindowID(aInnerWindowID) , mCSPContext(aCSPContext) { + NS_ASSERTION(!aViolatedDirective.IsEmpty(), "Can not send reports without a violated directive"); // the observer subject is an nsISupports: either an nsISupportsCString // from the arg passed in directly, or if that's empty, it's the blocked // source. diff --git a/dom/base/nsCSPUtils.cpp b/dom/base/nsCSPUtils.cpp index 7b8659dfdc73..fb8b30e54b01 100644 --- a/dom/base/nsCSPUtils.cpp +++ b/dom/base/nsCSPUtils.cpp @@ -927,16 +927,34 @@ nsCSPPolicy::directiveExists(enum CSPDirective aDir) const return false; } +/* + * Use this function only after ::allows() returned 'false'. Most and + * foremost it's used to get the violated directive before sending reports. + * The parameter outDirective is the equivalent of 'outViolatedDirective' + * for the ::permits() function family. + */ void nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType, nsAString& outDirective) const { + nsCSPDirective* defaultDir = nullptr; for (uint32_t i = 0; i < mDirectives.Length(); i++) { if (mDirectives[i]->restrictsContentType(aContentType)) { mDirectives[i]->toString(outDirective); return; } + if (mDirectives[i]->isDefaultDirective()) { + defaultDir = mDirectives[i]; + } } + // if we haven't found a matching directive yet, + // the contentType must be restricted by the default directive + if (defaultDir) { + defaultDir->toString(outDirective); + return; + } + NS_ASSERTION(false, "Can not query directive string for contentType!"); + outDirective.AppendASCII("couldNotQueryViolatedDirective"); } void diff --git a/dom/base/test/chrome/cpows_parent.xul b/dom/base/test/chrome/cpows_parent.xul index 2e4c87dc016b..78ed01d80bb6 100644 --- a/dom/base/test/chrome/cpows_parent.xul +++ b/dom/base/test/chrome/cpows_parent.xul @@ -58,9 +58,14 @@ ok(data.s === "hello", "string property"); ok(data.x.i === 10, "nested property"); ok(data.f() === 99, "function call"); + is(Object.getOwnPropertyDescriptor(data, "doesn't exist"), undefined, + "getOwnPropertyDescriptor returns undefined for non-existant properties"); + ok(Object.getOwnPropertyDescriptor(data, "i").value, 5, + "getOwnPropertyDescriptor.value works"); let obj = new data.ctor(); ok(obj.a === 3, "constructor call"); ok(document.title === "Hello, Kitty", "document node"); + is(typeof document.cookie, "string", "can get document.cookie"); data.i = 6; data.b = false; diff --git a/dom/base/test/csp/test_csp_report.html b/dom/base/test/csp/test_csp_report.html index 0f2dc31b4f5a..bb3acaee5f20 100644 --- a/dom/base/test/csp/test_csp_report.html +++ b/dom/base/test/csp/test_csp_report.html @@ -19,7 +19,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=548193 /* * Description of the test: * We try to load an inline-src using a policy that constrains - * all scripts from running (script-src 'none'). We verify that + * all scripts from running (default-src 'none'). We verify that * the generated csp-report contains the expceted values. If any * of the JSON is not formatted properly (e.g. not properly escaped) * then JSON.parse will fail, which allows to pinpoint such errors @@ -29,10 +29,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=548193 const testfile = "tests/dom/base/test/csp/file_csp_report.html"; const reportURI = "http://mochi.test:8888/foo.sjs"; -const policy = "script-src 'none'; report-uri " + reportURI; +const policy = "default-src 'none'; report-uri " + reportURI; const docUri = "http://mochi.test:8888/tests/dom/base/test/csp/file_csp_testserver.sjs" + "?file=tests/dom/base/test/csp/file_csp_report.html" + - "&csp=script-src%20%27none%27%3B%20report-uri%20http%3A//mochi.test%3A8888/foo.sjs"; + "&csp=default-src%20%27none%27%3B%20report-uri%20http%3A//mochi.test%3A8888/foo.sjs"; window.checkResults = function(reportObj) { var cspReport = reportObj["csp-report"]; @@ -50,9 +50,9 @@ window.checkResults = function(reportObj) { is(cspReport["blocked-uri"], "self", "Incorrect blocked-uri"); - is(cspReport["violated-directive"], "script-src 'none'", "Incorrect violated-directive"); + is(cspReport["violated-directive"], "default-src 'none'", "Incorrect violated-directive"); - is(cspReport["original-policy"], "script-src 'none'; report-uri http://mochi.test:8888/foo.sjs", + is(cspReport["original-policy"], "default-src 'none'; report-uri http://mochi.test:8888/foo.sjs", "Incorrect original-policy"); is(cspReport["source-file"], docUri, "Incorrect source-file"); diff --git a/dom/base/test/test_window_define_symbol.html b/dom/base/test/test_window_define_symbol.html index b5b936542d90..95ea9e6eed29 100644 --- a/dom/base/test/test_window_define_symbol.html +++ b/dom/base/test/test_window_define_symbol.html @@ -16,9 +16,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1082672
 
 
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 324a1816f6ca..b035ae7e6542 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2014,7 +2014,7 @@ class PropertyDefiner: getAvailableInTestFunc(interfaceMember), descriptor.checkPermissionsIndicesForMembers.get(interfaceMember.identifier.name)) - def generatePrefableArray(self, array, name, specTemplate, specTerminator, + def generatePrefableArray(self, array, name, specFormatter, specTerminator, specType, getCondition, getDataTuple, doIdArrays): """ This method generates our various arrays. @@ -2023,7 +2023,8 @@ class PropertyDefiner: name is the name as passed to generateArray - specTemplate is a template for each entry of the spec array + specFormatter is a function that takes a single argument, a tuple, + and returns a string, a spec array entry specTerminator is a terminator for the spec array (inserted every time our controlling pref changes and at the end of the array) @@ -2034,7 +2035,7 @@ class PropertyDefiner: returns the corresponding MemberCondition. getDataTuple is a callback function that takes an array entry and - returns a tuple suitable for substitution into specTemplate. + returns a tuple suitable to be passed to specFormatter. """ # We want to generate a single list of specs, but with specTerminator @@ -2077,7 +2078,7 @@ class PropertyDefiner: switchToCondition(self, curCondition) lastCondition = curCondition # And the actual spec - specs.append(specTemplate % getDataTuple(member)) + specs.append(specFormatter(getDataTuple(member))) specs.append(specTerminator) prefableSpecs.append(" { false, nullptr }") @@ -2354,9 +2355,15 @@ class MethodDefiner(PropertyDefiner): return (m["name"], accessor, jitinfo, m["length"], flags(m), selfHostedName) + def formatSpec(fields): + if fields[0].startswith("@@"): + fields = (fields[0][2:],) + fields[1:] + return ' JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields + return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields + return self.generatePrefableArray( array, name, - ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)', + formatSpec, ' JS_FS_END', 'JSFunctionSpec', condition, specData, doIdArrays) @@ -2460,7 +2467,7 @@ class AttrDefiner(PropertyDefiner): return self.generatePrefableArray( array, name, - ' { "%s", %s, %s, %s}', + lambda fields: ' { "%s", %s, %s, %s}' % fields, ' JS_PS_END', 'JSPropertySpec', PropertyDefiner.getControllingCondition, specData, doIdArrays) @@ -2488,7 +2495,7 @@ class ConstDefiner(PropertyDefiner): return self.generatePrefableArray( array, name, - ' { "%s", %s }', + lambda fields: ' { "%s", %s }' % fields, ' { 0, JS::UndefinedValue() }', 'ConstantSpec', PropertyDefiner.getControllingCondition, specData, doIdArrays) diff --git a/dom/bindings/test/test_sequence_detection.html b/dom/bindings/test/test_sequence_detection.html index 58ec07cebf2f..c48415a25098 100644 --- a/dom/bindings/test/test_sequence_detection.html +++ b/dom/bindings/test/test_sequence_detection.html @@ -16,18 +16,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1066432 var testInterfaceJS = new TestInterfaceJS(); ok(testInterfaceJS, "got a TestInterfaceJS object"); - var nonIterableObject = { "@@iterator": 5 }; - if (typeof Symbol === "function") { - // Make this test fail if Symbol.iterator is correctly implemented. - // This is here to make sure this test is updated when bug 918828 lands, - // at which point the correct code will be: - // var JS_HAS_SYMBOLS = typeof Symbol === "function"; - // var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; - // var nonIterableObject = { [std_iterator]: 5}; - // Otherwise, fixing bug 918828 would cause this test to silently stop - // testing what it's supposed to be testing. - nonIterableObject[Symbol.iterator] = Array.prototype[Symbol.iterator]; - } + var JS_HAS_SYMBOLS = typeof Symbol === "function"; + var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + var nonIterableObject = {[std_iterator]: 5}; try { testInterfaceJS.testSequenceOverload(nonIterableObject); diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 807460bb75b9..805ad832f232 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -444,7 +444,7 @@ HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute, NS_IMETHODIMP_(bool) HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const { - static const MappedAttributeEntry attributes[] { + static const MappedAttributeEntry attributes[] = { { &nsGkAtoms::wrap }, { nullptr } }; diff --git a/dom/html/test/test_formelements.html b/dom/html/test/test_formelements.html index b6836314d152..3e4a19e2d6d8 100644 --- a/dom/html/test/test_formelements.html +++ b/dom/html/test/test_formelements.html @@ -32,7 +32,9 @@ names = []; for (var name in x) { names.push(name); } -is(names.length, 10, "Should have 10 enumerated names"); +var JS_HAS_SYMBOLS = typeof Symbol === "function"; +is(names.length, JS_HAS_SYMBOLS ? 9 : 10, + "Should have 9 enumerated names (or 10 with '@@iterator')"); is(names[0], "0", "Enum entry 1"); is(names[1], "1", "Enum entry 2"); is(names[2], "2", "Enum entry 3"); @@ -41,8 +43,12 @@ is(names[4], "4", "Enum entry 5"); is(names[5], "something", "Enum entry 6"); is(names[6], "namedItem", "Enum entry 7"); is(names[7], "item", "Enum entry 8"); -is(names[8], "@@iterator", "Enum entry 9"); -is(names[9], "length", "Enum entry 10"); +if (JS_HAS_SYMBOLS) { + is(names[8], "length", "Enum entry 9"); +} else { + is(names[8], "@@iterator", "Enum entry 9"); + is(names[9], "length", "Enum entry 10"); +} names = Object.getOwnPropertyNames(x); is(names.length, 10, "Should have 10 items"); diff --git a/dom/html/test/test_htmlcollection.html b/dom/html/test/test_htmlcollection.html index e5dd5bfb0e8e..1016ea6bf1e3 100644 --- a/dom/html/test/test_htmlcollection.html +++ b/dom/html/test/test_htmlcollection.html @@ -28,7 +28,9 @@ var names = []; for (var name in x) { names.push(name); } -is(names.length, 9, "Should have 9 enumerated names"); +var JS_HAS_SYMBOLS = typeof Symbol === "function"; +is(names.length, JS_HAS_SYMBOLS ? 8 : 9, + "Should have 8 enumerated names (or 9 with '@@iterator')"); is(names[0], "0", "Enum entry 1") is(names[1], "1", "Enum entry 2") is(names[2], "2", "Enum entry 3") @@ -36,8 +38,12 @@ is(names[3], "3", "Enum entry 4") is(names[4], "something", "Enum entry 5") is(names[5], "item", "Enum entry 6") is(names[6], "namedItem", "Enum entry 7") -is(names[7], "@@iterator", "Enum entry 8") -is(names[8], "length", "Enum entry 9") +if (JS_HAS_SYMBOLS) { + is(names[7], "length", "Enum entry 8"); +} else { + is(names[7], "@@iterator", "Enum entry 8"); + is(names[8], "length", "Enum entry 9"); +} names = Object.getOwnPropertyNames(x); is(names.length, 9, "Should have 9 items"); diff --git a/dom/html/test/test_named_options.html b/dom/html/test/test_named_options.html index 160c0589440d..8be15d064376 100644 --- a/dom/html/test/test_named_options.html +++ b/dom/html/test/test_named_options.html @@ -42,7 +42,9 @@ var names2 = []; for (var name in opt) { names2.push(name); } -is(names2.length, 12, "Should have twelve enumerated names"); +var JS_HAS_SYMBOLS = typeof Symbol === "function"; +is(names2.length, JS_HAS_SYMBOLS ? 11 : 12, + "Should have eleven enumerated names (or twelve with '@@iterator')"); is(names2[0], "0", "Enum entry 1") is(names2[1], "1", "Enum entry 2") is(names2[2], "2", "Enum entry 3") @@ -54,7 +56,9 @@ is(names2[7], "length", "Enum entry 8") is(names2[8], "selectedIndex", "Enum entry 9") is(names2[9], "item", "Enum entry 10") is(names2[10], "namedItem", "Enum entry 11") -is(names2[11], "@@iterator", "Enum entry 12") +if (!JS_HAS_SYMBOLS) { + is(names2[11], "@@iterator", "Enum entry 12"); +} diff --git a/dom/html/test/test_rowscollection.html b/dom/html/test/test_rowscollection.html index 54483e9e2fc5..02914bec339a 100644 --- a/dom/html/test/test_rowscollection.html +++ b/dom/html/test/test_rowscollection.html @@ -38,7 +38,9 @@ var names = []; for (var name in x) { names.push(name); } -is(names.length, 11, "Should have 11 enumerated names"); +var JS_HAS_SYMBOLS = typeof Symbol === "function"; +is(names.length, JS_HAS_SYMBOLS ? 10 : 11, + "Should have 10 enumerated names (or 11 with '@@iterator')"); is(names[0], "0", "Enum entry 1") is(names[1], "1", "Enum entry 2") is(names[2], "2", "Enum entry 3") @@ -48,8 +50,12 @@ is(names[5], "5", "Enum entry 6") is(names[6], "something", "Enum entry 7") is(names[7], "item", "Enum entry 8") is(names[8], "namedItem", "Enum entry 9") -is(names[9], "@@iterator", "Enum entry 10") -is(names[10], "length", "Enum entry 11") +if (JS_HAS_SYMBOLS) { + is(names[9], "length", "Enum entry 10"); +} else { + is(names[9], "@@iterator", "Enum entry 10"); + is(names[10], "length", "Enum entry 11"); +} names = Object.getOwnPropertyNames(x); is(names.length, 11, "Should have 11 items"); diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 8021d26d6d3d..830094e01c69 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -2206,6 +2206,14 @@ SetDefaultPragmas(mozIStorageConnection* aConnection) return rv; } + if (IndexedDatabaseManager::FullSynchronous()) { + rv = aConnection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("PRAGMA synchronous = FULL;")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + return NS_OK; } diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index b2c33e02c4e9..13f969b5e64c 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -212,6 +212,7 @@ IndexedDatabaseManager::~IndexedDatabaseManager() } bool IndexedDatabaseManager::sIsMainProcess = false; +bool IndexedDatabaseManager::sFullSynchronousMode = false; mozilla::Atomic IndexedDatabaseManager::sLowDiskSpaceMode(false); // static @@ -299,6 +300,14 @@ IndexedDatabaseManager::Init() Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback, kTestingPref); + // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This + // guarantees (unlike synchronous = OFF) atomicity and consistency, but not + // necessarily durability in situations such as power loss. This preference + // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee + // durability, but with an extra fsync() and the corresponding performance + // hit. + sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous"); + return NS_OK; } @@ -502,6 +511,16 @@ IndexedDatabaseManager::InTestingMode() return gTestingMode; } +// static +bool +IndexedDatabaseManager::FullSynchronous() +{ + MOZ_ASSERT(gDBManager, + "FullSynchronous() called before indexedDB has been initialized!"); + + return sFullSynchronousMode; +} + already_AddRefed IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index c50c17d876b7..4933b03836fb 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -80,6 +80,9 @@ public: static bool InTestingMode(); + static bool + FullSynchronous(); + already_AddRefed GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, @@ -162,6 +165,7 @@ private: mozilla::Mutex mFileMutex; static bool sIsMainProcess; + static bool sFullSynchronousMode; static mozilla::Atomic sLowDiskSpaceMode; }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index fb8d61c814fb..bf7e4cd900ca 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -50,7 +50,7 @@ #define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" #include "nsDirectoryServiceDefs.h" -#elif defined(XP_LINUX) +#elif defined(XP_LINUX) || defined(XP_MACOSX) #include "mozilla/Sandbox.h" #endif #endif @@ -552,6 +552,76 @@ NS_INTERFACE_MAP_BEGIN(ContentChild) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +static bool +GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath) +{ + nsAutoCString appPath; + nsAutoCString appBinaryPath( + (CommandLine::ForCurrentProcess()->argv()[0]).c_str()); + + nsAutoCString::const_iterator start, end; + appBinaryPath.BeginReading(start); + appBinaryPath.EndReading(end); + if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) { + end = start; + ++end; ++end; ++end; ++end; + appBinaryPath.BeginReading(start); + appPath.Assign(Substring(start, end)); + } else { + return false; + } + + nsCOMPtr app, appBinary; + nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath), + true, getter_AddRefs(app)); + if (NS_FAILED(rv)) { + return false; + } + rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath), + true, getter_AddRefs(appBinary)); + if (NS_FAILED(rv)) { + return false; + } + + bool isLink; + app->IsSymlink(&isLink); + if (isLink) { + app->GetNativeTarget(aAppPath); + } else { + app->GetNativePath(aAppPath); + } + appBinary->IsSymlink(&isLink); + if (isLink) { + appBinary->GetNativeTarget(aAppBinaryPath); + } else { + appBinary->GetNativePath(aAppBinaryPath); + } + + return true; +} + +void +ContentChild::OnChannelConnected(int32_t aPid) +{ + nsAutoCString appPath, appBinaryPath; + if (!GetAppPaths(appPath, appBinaryPath)) { + MOZ_CRASH("Error resolving child process path"); + } + + MacSandboxInfo info; + info.type = MacSandboxType_Content; + info.appPath.Assign(appPath); + info.appBinaryPath.Assign(appBinaryPath); + + nsAutoCString err; + if (!mozilla::StartMacSandbox(info, err)) { + NS_WARNING(err.get()); + MOZ_CRASH("sandbox_init() failed"); + } +} +#endif + bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessHandle aParentHandle, @@ -586,7 +656,9 @@ ContentChild::Init(MessageLoop* aIOLoop, return false; } - Open(aChannel, aParentHandle, aIOLoop); + if (!Open(aChannel, aParentHandle, aIOLoop)) { + return false; + } sSingleton = this; // Make sure there's an nsAutoScriptBlocker on the stack when dispatching diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 80e411c4f1e9..60ec6598ea77 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -74,6 +74,10 @@ public: nsCString vendor; }; +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + void OnChannelConnected(int32_t aPid); +#endif + bool Init(MessageLoop* aIOLoop, base::ProcessHandle aParentHandle, IPC::Channel* aChannel); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 69c90d3feb1e..692eeefe0416 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -69,7 +69,7 @@ union MaybeNativeKeyBinding void_t; }; -prio(normal upto high) intr protocol PBrowser +prio(normal upto urgent) intr protocol PBrowser { manager PContent or PContentBridge; @@ -141,7 +141,7 @@ parent: * preference Native widget preference for IME updates * seqno Current seqno value on the chrome side */ - sync NotifyIMEFocus(bool focus) + prio(urgent) sync NotifyIMEFocus(bool focus) returns (nsIMEUpdatePreference preference, uint32_t seqno); /** @@ -157,8 +157,9 @@ parent: * for insertion, offset == end * for deletion, offset == newEnd */ - NotifyIMETextChange(uint32_t offset, uint32_t end, uint32_t newEnd, - bool causedByComposition); + prio(urgent) async NotifyIMETextChange(uint32_t offset, uint32_t end, + uint32_t newEnd, + bool causedByComposition); /** * Notifies chrome that there is a IME compostion rect updated @@ -168,8 +169,10 @@ parent: * caretOffset The offset of caret position * caretRect The rect of IME caret */ - NotifyIMESelectedCompositionRect(uint32_t offset, nsIntRect[] rect, - uint32_t caretOffset, nsIntRect caretRect); + prio(urgent) async NotifyIMESelectedCompositionRect(uint32_t offset, + nsIntRect[] rect, + uint32_t caretOffset, + nsIntRect caretRect); /** * Notifies chrome that there has been a change in selection @@ -180,22 +183,22 @@ parent: * focus Offset where the caret is * causedByComposition true if the change is caused by composition */ - NotifyIMESelection(uint32_t seqno, uint32_t anchor, uint32_t focus, - bool causedByComposition); + prio(urgent) async NotifyIMESelection(uint32_t seqno, uint32_t anchor, + uint32_t focus, bool causedByComposition); /** * Notifies chrome to refresh its text cache * * text The entire content of the text field */ - NotifyIMETextHint(nsString text); + prio(urgent) async NotifyIMETextHint(nsString text); /** * Notifies IME of mouse button event on a character in focused editor. * * Returns true if the mouse button event is consumd by IME. */ - sync NotifyIMEMouseButtonEvent(IMENotification notification) + prio(urgent) sync NotifyIMEMouseButtonEvent(IMENotification notification) returns (bool consumedByIME); /** @@ -209,7 +212,7 @@ parent: * if cancel is PR_FALSE, * widget should return the current composition text */ - sync EndIMEComposition(bool cancel) returns (nsString composition); + prio(urgent) sync EndIMEComposition(bool cancel) returns (nsString composition); /** * Request that the parent process move focus to the browser's frame. If @@ -217,16 +220,17 @@ parent: */ RequestFocus(bool canRaise); - sync GetInputContext() returns (int32_t IMEEnabled, int32_t IMEOpen, - intptr_t NativeIMEContext); + prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled, + int32_t IMEOpen, + intptr_t NativeIMEContext); - SetInputContext(int32_t IMEEnabled, - int32_t IMEOpen, - nsString type, - nsString inputmode, - nsString actionHint, - int32_t cause, - int32_t focusChange); + prio(urgent) async SetInputContext(int32_t IMEEnabled, + int32_t IMEOpen, + nsString type, + nsString inputmode, + nsString actionHint, + int32_t cause, + int32_t focusChange); sync IsParentWindowMainWidgetVisible() returns (bool visible); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index cc8c9c438ffe..ed1ada4546d6 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -330,7 +330,7 @@ union MaybeFileDesc { void_t; }; -prio(normal upto high) intr protocol PContent +prio(normal upto urgent) intr protocol PContent { parent spawns PPluginModule; diff --git a/dom/ipc/PContentBridge.ipdl b/dom/ipc/PContentBridge.ipdl index 9d00afbd961c..3a8bd1f28580 100644 --- a/dom/ipc/PContentBridge.ipdl +++ b/dom/ipc/PContentBridge.ipdl @@ -28,7 +28,7 @@ namespace dom { * allocate the PContentBridgeChild. This protocol allows these processes to * share PBrowsers and send messages to each other. */ -prio(normal upto high) intr protocol PContentBridge +prio(normal upto urgent) intr protocol PContentBridge { bridges PContent, PContent; diff --git a/dom/network/UDPSocketChild.cpp b/dom/network/UDPSocketChild.cpp index 4b9f530e386b..48bb0a1ee8dd 100644 --- a/dom/network/UDPSocketChild.cpp +++ b/dom/network/UDPSocketChild.cpp @@ -28,6 +28,7 @@ UDPSocketChildBase::ReleaseIPDLReference() { MOZ_ASSERT(mIPCOpen); mIPCOpen = false; + mSocket = nullptr; this->Release(); } diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 78577f3479dd..850c489fe08e 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -243,6 +243,14 @@ nsPluginHost::nsPluginHost() // No need to initialize members to nullptr, false etc because this class // has a zeroing operator new. { + // Bump the pluginchanged epoch on startup. This insures content gets a + // good plugin list the first time it requests it. Normally we'd just + // init this to 1, but due to the unique nature of our ctor we need to do + // this manually. + if (XRE_GetProcessType() == GeckoProcessType_Default) { + IncrementChromeEpoch(); + } + // check to see if pref is set at startup to let plugins take over in // full page mode for certain image mime types that we handle internally mOverrideInternalTypes = diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 92651a0c7c73..4b3a0ae0ec2b 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -692,23 +692,6 @@ MessageChannel::Send(Message* aMsg, Message* aReply) return true; } -struct AutoDeferMessages -{ - typedef IPC::Message Message; - - std::deque& mQueue; - mozilla::Vector mDeferred; - - AutoDeferMessages(std::deque& queue) : mQueue(queue) {} - ~AutoDeferMessages() { - mQueue.insert(mQueue.begin(), mDeferred.begin(), mDeferred.end()); - } - - void Defer(Message aMsg) { - mDeferred.append(aMsg); - } -}; - bool MessageChannel::SendAndWait(Message* aMsg, Message* aReply) { @@ -728,16 +711,28 @@ MessageChannel::SendAndWait(Message* aMsg, Message* aReply) mLink->SendMessage(msg.forget()); - AutoDeferMessages defer(mPending); - while (true) { - while (!mPending.empty()) { - Message msg = mPending.front(); - mPending.pop_front(); - if (ShouldDeferMessage(msg)) - defer.Defer(msg); - else - ProcessPendingRequest(msg); + // Loop until there aren't any more priority messages to process. + for (;;) { + mozilla::Vector toProcess; + + for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) { + Message &msg = *it; + if (!ShouldDeferMessage(msg)) { + toProcess.append(msg); + it = mPending.erase(it); + continue; + } + it++; + } + + if (toProcess.empty()) + break; + + // Processing these messages could result in more messages, so we + // loop around to check for more afterwards. + for (auto it = toProcess.begin(); it != toProcess.end(); it++) + ProcessPendingRequest(*it); } // See if we've received a reply. @@ -860,7 +855,7 @@ MessageChannel::Call(Message* aMsg, Message* aReply) // If the message is not Interrupt, we can dispatch it as normal. if (!recvd.is_interrupt()) { { - AutoEnterTransaction transaction(this, &recvd); + AutoEnterTransaction transaction(this, recvd); MonitorAutoUnlock unlock(*mMonitor); CxxStackFrame frame(*this, IN_MESSAGE, &recvd); DispatchMessage(recvd); @@ -947,7 +942,7 @@ MessageChannel::InterruptEventOccurred() } bool -MessageChannel::ProcessPendingRequest(Message aUrgent) +MessageChannel::ProcessPendingRequest(const Message &aUrgent) { AssertWorkerThread(); mMonitor->AssertCurrentThreadOwns(); @@ -965,7 +960,7 @@ MessageChannel::ProcessPendingRequest(Message aUrgent) { // In order to send the parent RPC messages and guarantee it will // wake up, we must re-use its transaction. - AutoEnterTransaction transaction(this, &aUrgent); + AutoEnterTransaction transaction(this, aUrgent); MonitorAutoUnlock unlock(*mMonitor); DispatchMessage(aUrgent); @@ -1029,7 +1024,7 @@ MessageChannel::OnMaybeDequeueOne() { // We should not be in a transaction yet if we're not blocked. MOZ_ASSERT(mCurrentTransaction == 0); - AutoEnterTransaction transaction(this, &recvd); + AutoEnterTransaction transaction(this, recvd); MonitorAutoUnlock unlock(*mMonitor); diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 98a9ee83526f..af770a7b5106 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -45,7 +45,6 @@ class MessageChannel : HasResultCodes { friend class ProcessLink; friend class ThreadLink; - friend class AutoEnterRPCTransaction; class CxxStackFrame; class InterruptFrame; @@ -228,7 +227,7 @@ class MessageChannel : HasResultCodes bool InterruptEventOccurred(); - bool ProcessPendingRequest(Message aUrgent); + bool ProcessPendingRequest(const Message &aUrgent); void MaybeUndeferIncall(); void EnqueuePendingMessages(); @@ -510,18 +509,18 @@ class MessageChannel : HasResultCodes if (mChan->mCurrentTransaction == 0) mChan->mCurrentTransaction = mChan->NextSeqno(); } - explicit AutoEnterTransaction(MessageChannel *aChan, Message *message) + explicit AutoEnterTransaction(MessageChannel *aChan, const Message &aMessage) : mChan(aChan), mOldTransaction(mChan->mCurrentTransaction) { mChan->mMonitor->AssertCurrentThreadOwns(); - if (!message->is_sync()) + if (!aMessage.is_sync()) return; - MOZ_ASSERT_IF(mChan->mSide == ParentSide && mOldTransaction != message->transaction_id(), - !mOldTransaction || message->priority() > mChan->AwaitingSyncReplyPriority()); - mChan->mCurrentTransaction = message->transaction_id(); + MOZ_ASSERT_IF(mChan->mSide == ParentSide && mOldTransaction != aMessage.transaction_id(), + !mOldTransaction || aMessage.priority() > mChan->AwaitingSyncReplyPriority()); + mChan->mCurrentTransaction = aMessage.transaction_id(); } ~AutoEnterTransaction() { mChan->mMonitor->AssertCurrentThreadOwns(); diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index eaaf2c3b0eb2..1b2c365cb725 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -678,7 +678,7 @@ function ArrayFrom(arrayLike, mapfn=undefined, thisArg=undefined) { var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE; // Steps 6-8. - var usingIterator = items["@@iterator"]; + var usingIterator = items[std_iterator]; if (usingIterator !== undefined) { // Steps 8.a-c. var A = IsConstructor(C) ? new C() : []; diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 75647458f415..d694dd288cbd 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -878,7 +878,7 @@ const Class MapIteratorObject::class_ = { }; const JSFunctionSpec MapIteratorObject::methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_FN("next", next, 0, 0), JS_FS_END }; @@ -1076,8 +1076,14 @@ MapObject::initClass(JSContext *cx, JSObject *obj) // Define its alias. RootedValue funval(cx, ObjectValue(*fun)); +#if JS_HAS_SYMBOLS + RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); + if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0)) + return nullptr; +#else if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0)) return nullptr; +#endif } return proto; } @@ -1528,7 +1534,7 @@ const Class SetIteratorObject::class_ = { }; const JSFunctionSpec SetIteratorObject::methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_FN("next", next, 0, 0), JS_FS_END }; @@ -1702,8 +1708,15 @@ SetObject::initClass(JSContext *cx, JSObject *obj) RootedValue funval(cx, ObjectValue(*fun)); if (!JS_DefineProperty(cx, proto, "keys", funval, 0)) return nullptr; + +#if JS_HAS_SYMBOLS + RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); + if (!JS_DefinePropertyById(cx, proto, iteratorId, funval, 0)) + return nullptr; +#else if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, 0)) return nullptr; +#endif } return proto; } diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index a7af0d67d2d2..8425c12801ea 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -31,7 +31,6 @@ // // The few items below here are either self-hosted or installing them under a // std_Foo name would require ugly contortions, so they just get aliased here. -var std_iterator = '@@iterator'; // FIXME: Change to be a symbol. var std_Array_indexOf = ArrayIndexOf; // WeakMap is a bare constructor without properties or methods. var std_WeakMap = WeakMap; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index e130c8d63c2a..039b51938c64 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2488,11 +2488,6 @@ EmitElemOpBase(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op) if (Emit1(cx, bce, op) < 0) return false; CheckTypeSet(cx, bce, op); - - if (op == JSOP_CALLELEM) { - if (Emit1(cx, bce, JSOP_SWAP) < 0) - return false; - } return true; } @@ -4730,9 +4725,16 @@ EmitIterator(ExclusiveContext *cx, BytecodeEmitter *bce) // Convert iterable to iterator. if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ return false; - if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR +#ifdef JS_HAS_SYMBOLS + if (Emit2(cx, bce, JSOP_SYMBOL, jsbytecode(JS::SymbolCode::iterator)) < 0) // OBJ OBJ @@ITERATOR return false; - if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR OBJ + if (!EmitElemOpBase(cx, bce, JSOP_CALLELEM)) // OBJ ITERFN + return false; +#else + if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ ITERFN + return false; +#endif + if (Emit1(cx, bce, JSOP_SWAP) < 0) // ITERFN OBJ return false; if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER return false; @@ -5598,17 +5600,8 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter, Parse if (!EmitTree(cx, bce, iter)) // ITERABLE return false; - - // Convert iterable to iterator. - if (Emit1(cx, bce, JSOP_DUP) < 0) // ITERABLE ITERABLE + if (!EmitIterator(cx, bce)) // ITER return false; - if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // ITERABLE @@ITERATOR - return false; - if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR ITERABLE - return false; - if (EmitCall(cx, bce, JSOP_CALL, 0, iter) < 0) // ITER - return false; - CheckTypeSet(cx, bce, JSOP_CALL); // Initial send value is undefined. if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RECEIVED @@ -6052,6 +6045,10 @@ EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_ELEM: if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, bce)) return false; + if (callop) { + if (Emit1(cx, bce, JSOP_SWAP) < 0) + return false; + } break; case PNK_FUNCTION: /* diff --git a/js/src/jit-test/lib/iteration.js b/js/src/jit-test/lib/iteration.js index 500b28303ec9..102dd7aa7c3c 100644 --- a/js/src/jit-test/lib/iteration.js +++ b/js/src/jit-test/lib/iteration.js @@ -5,8 +5,8 @@ load(libdir + "asserts.js"); -// FIXME: Import from std::iteration. -const std_iterator = '@@iterator'; +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : '@@iterator'; if (typeof assertIteratorResult === 'undefined') { var assertIteratorResult = function assertIteratorResult(result, value, done) { diff --git a/js/src/jit-test/tests/arguments/destructuring-exprbody.js b/js/src/jit-test/tests/arguments/destructuring-exprbody.js index 81289b92d1c6..03311216195d 100644 --- a/js/src/jit-test/tests/arguments/destructuring-exprbody.js +++ b/js/src/jit-test/tests/arguments/destructuring-exprbody.js @@ -1,7 +1,8 @@ // See bug 763313 +load(libdir + "iteration.js"); function f([a]) a var i = 0; -var o = {'@@iterator': function () { i++; return { +var o = {[std_iterator]: function () { i++; return { next: function () { i++; return {value: 42, done: false}; }}}}; assertEq(f(o), 42); assertEq(i, 2); diff --git a/js/src/jit-test/tests/basic/expression-autopsy.js b/js/src/jit-test/tests/basic/expression-autopsy.js index 26bd4100b42d..bb40609ff6ba 100644 --- a/js/src/jit-test/tests/basic/expression-autopsy.js +++ b/js/src/jit-test/tests/basic/expression-autopsy.js @@ -1,4 +1,5 @@ load(libdir + "asserts.js"); +load(libdir + "iteration.js"); function check_one(expected, f, err) { var failed = true; @@ -109,8 +110,11 @@ check("o[- (o)]"); // A few one off tests check_one("6", (function () { 6() }), " is not a function"); check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only"); -check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { var [{ x }] = [null, {}]; }, " is null"); -check_one("(intermediate value)['@@iterator'](...).next(...).value", function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined"); +var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; +check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`, + function () { var [{ x }] = [null, {}]; }, " is null"); +check_one(`(intermediate value)[${ITERATOR}](...).next(...).value`, + function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined"); // Check fallback behavior assertThrowsInstanceOf(function () { for (let x of undefined) {} }, TypeError); diff --git a/js/src/jit-test/tests/collections/WeakSet-error.js b/js/src/jit-test/tests/collections/WeakSet-error.js index 2b891ec347de..c5b7b65a318a 100644 --- a/js/src/jit-test/tests/collections/WeakSet-error.js +++ b/js/src/jit-test/tests/collections/WeakSet-error.js @@ -1,4 +1,5 @@ load(libdir + "asserts.js"); +load(libdir + "iteration.js"); function testMethod(name) { var method = WeakSet.prototype[name]; @@ -15,7 +16,7 @@ testMethod("delete"); testMethod("clear"); assertThrowsInstanceOf(function() { var ws = new WeakSet(); ws.add(1); }, TypeError); -assertThrowsInstanceOf(function() { new WeakSet({"@@iterator": 2}) }, TypeError); -assertEq(typeof []["@@iterator"], "function"); // Make sure we fail when @@iterator is removed +assertThrowsInstanceOf(function() { new WeakSet({[std_iterator]: 2}) }, TypeError); +assertEq(typeof [][std_iterator], "function"); assertThrowsInstanceOf(function() { WeakSet(); }, TypeError); diff --git a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js index 13c52dc8ba05..80e69f4978fc 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js +++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js @@ -7,7 +7,8 @@ function test(constructor) { var proto = Object.getPrototypeOf(constructor()[std_iterator]()); var names = Object.getOwnPropertyNames(proto); names.sort(); - assertDeepEq(names, [std_iterator, 'next']); + assertDeepEq(names, JS_HAS_SYMBOLS ? ['next'] : ['@@iterator', 'next']); + assertEq(proto.hasOwnProperty(std_iterator), true); var desc = Object.getOwnPropertyDescriptor(proto, 'next'); assertEq(desc.configurable, true); diff --git a/js/src/jit-test/tests/for-of/array-jit.js b/js/src/jit-test/tests/for-of/array-jit.js new file mode 100644 index 000000000000..3f139cc2bd27 --- /dev/null +++ b/js/src/jit-test/tests/for-of/array-jit.js @@ -0,0 +1,6 @@ +var arr = [1, 2, 3]; +var y = 0; +for (var i = 0; i < 10; i++) + for (var x of arr) + y += x; +assertEq(y, 60); diff --git a/js/src/jit-test/tests/for-of/next-3.js b/js/src/jit-test/tests/for-of/next-3.js index f0eadc64116a..ea2c71575ea9 100644 --- a/js/src/jit-test/tests/for-of/next-3.js +++ b/js/src/jit-test/tests/for-of/next-3.js @@ -8,6 +8,6 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); var g = newGlobal(); -g.eval("var it = [1, 2]['" + std_iterator + "']();"); +g.eval(`var it = [1, 2][${uneval(std_iterator)}]();`); assertIteratorNext(g.it, 1); assertThrowsInstanceOf([][std_iterator]().next.bind(g.it), TypeError) diff --git a/js/src/jit-test/tests/for-of/semantics-08.js b/js/src/jit-test/tests/for-of/semantics-08.js index 9c0cc08ea994..4150185c6189 100644 --- a/js/src/jit-test/tests/for-of/semantics-08.js +++ b/js/src/jit-test/tests/for-of/semantics-08.js @@ -3,7 +3,10 @@ load(libdir + "iteration.js"); var g = newGlobal(); -var it = g.eval("({ '" + std_iterator + "': function () { return this; }, " + - "next: function () { return { done: true } } });"); -for (x of it) +g.eval(` + var obj = {}; + obj[${uneval(std_iterator)}] = function () { return this; }; + obj.next = function () { return { done: true }; }; +`); +for (x of g.obj) throw 'FAIL'; diff --git a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js index 81e5fd841f1c..a9bf2a69b2ba 100644 --- a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js +++ b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js @@ -57,7 +57,9 @@ var iterProto = Object.getPrototypeOf(iter); assertEq(Object.getPrototypeOf(iterProto), Object.prototype); // Own properties for StringIterator.prototype: "next" and @@iterator -arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next", std_iterator].sort()); +arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), + JS_HAS_SYMBOLS ? ["next"] : ["@@iterator", "next"]); +assertEq(iterProto.hasOwnProperty(std_iterator), true); // StringIterator.prototype[@@iterator] is a built-in function assertBuiltinFunction(iterProto, std_iterator, 0); diff --git a/js/src/jit-test/tests/symbol/toNumber-2.js b/js/src/jit-test/tests/symbol/toNumber-2.js new file mode 100644 index 000000000000..9f1f59cede6a --- /dev/null +++ b/js/src/jit-test/tests/symbol/toNumber-2.js @@ -0,0 +1,14 @@ +// |jit-test| error: ReferenceError +function eq(e, a) { + passed = (a == e); +} +function f(e, a) { + fail(); + eq(e, a); +} +try { + f(); +} catch (exc1) {} +eq(.1, .1); +var sym = Symbol("method"); +evaluate("f(test, sym, 0)", {compileAndGo: true}); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 36471f97f023..9986acbcd30a 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1259,6 +1259,15 @@ BaselineCompiler::emit_JSOP_STRING() return true; } +bool +BaselineCompiler::emit_JSOP_SYMBOL() +{ + unsigned which = GET_UINT8(pc); + JS::Symbol *sym = cx->runtime()->wellKnownSymbols->get(which); + frame.push(SymbolValue(sym)); + return true; +} + typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind); static const VMFunction DeepCloneObjectLiteralInfo = FunctionInfo(DeepCloneObjectLiteral); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 1eb5148b5638..c6086465b023 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -61,6 +61,7 @@ namespace jit { _(JSOP_UINT24) \ _(JSOP_DOUBLE) \ _(JSOP_STRING) \ + _(JSOP_SYMBOL) \ _(JSOP_OBJECT) \ _(JSOP_CALLSITEOBJ) \ _(JSOP_REGEXP) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 9b12e64db680..6b37e6998adb 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -3417,8 +3417,8 @@ IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, Shape *shape, bool i } static bool -IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted, - bool isDOMProxy=false) +IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, + bool *isScripted, bool *isTemporarilyUnoptimizable, bool isDOMProxy=false) { MOZ_ASSERT(isScripted); @@ -3439,8 +3439,10 @@ IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh #ifdef JSGC_GENERATIONAL // Information from get prop call ICs may be used directly from Ion code, // and should not be nursery allocated. - if (IsInsideNursery(holder) || IsInsideNursery(func)) + if (IsInsideNursery(holder) || IsInsideNursery(func)) { + *isTemporarilyUnoptimizable = true; return false; + } #endif if (func->isNative()) { @@ -3448,8 +3450,10 @@ IsCacheableGetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh return true; } - if (!func->hasJITCode()) + if (!func->hasJITCode()) { + *isTemporarilyUnoptimizable = true; return false; + } *isScripted = true; return true; @@ -3533,7 +3537,8 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape, } static bool -IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, Shape* oldShape, bool *isScripted) +IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, + Shape* oldShape, bool *isScripted, bool *isTemporarilyUnoptimizable) { MOZ_ASSERT(isScripted); @@ -3565,8 +3570,10 @@ IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh #ifdef JSGC_GENERATIONAL // Information from set prop call ICs may be used directly from Ion code, // and should not be nursery allocated. - if (IsInsideNursery(holder) || IsInsideNursery(func)) + if (IsInsideNursery(holder) || IsInsideNursery(func)) { + *isTemporarilyUnoptimizable = true; return false; + } #endif if (func->isNative()) { @@ -3574,8 +3581,10 @@ IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh return true; } - if (!func->hasJITCode()) + if (!func->hasJITCode()) { + *isTemporarilyUnoptimizable = true; return false; + } *isScripted = true; return true; @@ -3817,7 +3826,9 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt } bool getterIsScripted = false; - if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted, /*isDOMProxy=*/false)) { + bool isTemporarilyUnoptimizable = false; + if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted, + &isTemporarilyUnoptimizable, /*isDOMProxy=*/false)) { RootedFunction getter(cx, &shape->getterObject()->as()); #if JS_HAS_NO_SUCH_METHOD @@ -4063,6 +4074,9 @@ DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub_ if (!TryAttachGetElemStub(cx, frame->script(), pc, stub, lhs, rhs, res)) return false; + // If we ever add a way to note unoptimizable accesses here, propagate the + // isTemporarilyUnoptimizable state from TryAttachNativeGetElemStub to here. + return true; } @@ -5840,7 +5854,9 @@ TryAttachGlobalNameStub(JSContext *cx, HandleScript script, jsbytecode *pc, // changes we need to make sure IonBuilder::getPropTryCommonGetter (which // requires a Baseline stub) handles non-outerized this objects correctly. bool isScripted; - if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted) && !isScripted) + bool isTemporarilyUnoptimizable = false; + if (IsCacheableGetPropCall(cx, global, current, shape, &isScripted, &isTemporarilyUnoptimizable) && + !isScripted) { ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); RootedFunction getter(cx, &shape->getterObject()->as()); @@ -6020,6 +6036,9 @@ DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub_ return false; } + // If we ever add a way to note unoptimizable accesses here, propagate the + // isTemporarilyUnoptimizable state from TryAttachGlobalNameStub to here. + return true; } @@ -6396,9 +6415,11 @@ static bool TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val, HandleShape oldShape, - HandleValue res, bool *attached) + HandleValue res, bool *attached, + bool *isTemporarilyUnoptimizable) { MOZ_ASSERT(!*attached); + MOZ_ASSERT(!*isTemporarilyUnoptimizable); if (!val.isObject()) return true; @@ -6459,7 +6480,8 @@ TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, } bool isScripted = false; - bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, isDOMProxy); + bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, + isTemporarilyUnoptimizable, isDOMProxy); // Try handling scripted getters. if (cacheableCall && isScripted && !isDOMProxy) { @@ -6766,6 +6788,11 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_ } bool attached = false; + // There are some reasons we can fail to attach a stub that are temporary. + // We want to avoid calling noteUnoptimizableAccess() if the reason we + // failed to attach a stub is one of those temporary reasons, since we might + // end up attaching a stub for the exact same access later. + bool isTemporarilyUnoptimizable = false; if (op == JSOP_LENGTH) { if (!TryAttachLengthStub(cx, frame->script(), stub, val, res, &attached)) @@ -6782,7 +6809,7 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_ RootedScript script(cx, frame->script()); if (!TryAttachNativeGetPropStub(cx, script, pc, stub, name, val, oldShape, - res, &attached)) + res, &attached, &isTemporarilyUnoptimizable)) return false; if (attached) return true; @@ -6803,7 +6830,8 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_ } MOZ_ASSERT(!attached); - stub->noteUnoptimizableAccess(); + if (!isTemporarilyUnoptimizable) + stub->noteUnoptimizableAccess(); return true; } @@ -7699,9 +7727,11 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset) static bool TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub, HandleObject obj, HandleShape oldShape, HandleTypeObject oldType, uint32_t oldSlots, - HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached) + HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached, + bool *isTemporarilyUnoptimizable) { MOZ_ASSERT(!*attached); + MOZ_ASSERT(!*isTemporarilyUnoptimizable); if (!obj->isNative() || obj->watched()) return true; @@ -7780,7 +7810,8 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr } bool isScripted = false; - bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape, oldShape, &isScripted); + bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape, oldShape, + &isScripted, isTemporarilyUnoptimizable); // Try handling scripted setters. if (cacheableCall && isScripted) { @@ -7906,8 +7937,13 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_ } bool attached = false; + // There are some reasons we can fail to attach a stub that are temporary. + // We want to avoid calling noteUnoptimizableAccess() if the reason we + // failed to attach a stub is one of those temporary reasons, since we might + // end up attaching a stub for the exact same access later. + bool isTemporarilyUnoptimizable = false; if (!TryAttachSetPropStub(cx, script, pc, stub, obj, oldShape, oldType, oldSlots, - name, id, rhs, &attached)) + name, id, rhs, &attached, &isTemporarilyUnoptimizable)) { return false; } @@ -7915,7 +7951,8 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_ return true; MOZ_ASSERT(!attached); - stub->noteUnoptimizableAccess(); + if (!isTemporarilyUnoptimizable) + stub->noteUnoptimizableAccess(); return true; } diff --git a/js/src/jsapi-tests/testForOfIterator.cpp b/js/src/jsapi-tests/testForOfIterator.cpp index e2abaf39289f..1f28d27f7c8e 100644 --- a/js/src/jsapi-tests/testForOfIterator.cpp +++ b/js/src/jsapi-tests/testForOfIterator.cpp @@ -8,9 +8,9 @@ #include "jsapi-tests/tests.h" #ifdef JS_HAS_SYMBOLS -#define IF_JS_HAS_SYMBOLS(x) x +#define STD_ITERATOR "Symbol.iterator" #else -#define IF_JS_HAS_SYMBOLS(x) +#define STD_ITERATOR "'@@iterator'" #endif BEGIN_TEST(testForOfIterator_basicNonIterable) @@ -18,9 +18,7 @@ BEGIN_TEST(testForOfIterator_basicNonIterable) JS::RootedValue v(cx); // Hack to make it simple to produce an object that has a property // named Symbol.iterator. - EVAL("var obj = {'@@iterator': 5" - IF_JS_HAS_SYMBOLS(", [Symbol.iterator]: Array.prototype[Symbol.iterator]") - "}; obj;", &v); + EVAL("({[" STD_ITERATOR "]: 5})", &v); JS::ForOfIterator iter(cx); bool ok = iter.init(v); CHECK(!ok); @@ -35,9 +33,7 @@ BEGIN_TEST(testForOfIterator_bug515273_part1) // Hack to make it simple to produce an object that has a property // named Symbol.iterator. - EVAL("var obj = {'@@iterator': 5" - IF_JS_HAS_SYMBOLS(", [Symbol.iterator]: Array.prototype[Symbol.iterator]") - "}; obj;", &v); + EVAL("({[" STD_ITERATOR "]: 5})", &v); JS::ForOfIterator iter(cx); bool ok = iter.init(v, JS::ForOfIterator::AllowNonIterable); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 7cbb898f82af..fccade0f1552 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5592,7 +5592,7 @@ JS::GetSymbolCode(Handle symbol) JS_PUBLIC_API(JS::Symbol *) JS::GetWellKnownSymbol(JSContext *cx, JS::SymbolCode which) { - return cx->runtime()->wellKnownSymbols->get(uint32_t(which)); + return cx->wellKnownSymbols().get(uint32_t(which)); } static bool diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 353e6f0cb46f..8f0b2629f6df 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2561,7 +2561,8 @@ struct JSFunctionSpec { * * The _SYM variants allow defining a function with a symbol key rather than a * string key. For example, use JS_SYM_FN(iterator, ...) to define an - * @@iterator method. + * @@iterator method. (In builds without ES6 symbols, it defines a method with + * the string id "@@iterator".) */ #define JS_FS(name,call,nargs,flags) \ JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr) @@ -2575,10 +2576,17 @@ struct JSFunctionSpec { JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName) #define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \ JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName) + +#ifdef JS_HAS_SYMBOLS #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ JS_FNSPEC(reinterpret_cast( \ uint32_t(::JS::SymbolCode::symbol) + 1), \ call, info, nargs, flags, selfHostedName) +#else +#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ + JS_FNSPEC("@@" #symbol, call, info, nargs, flags, selfHostedName) +#endif + #define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName) \ {name, {call, info}, nargs, flags, selfHostedName} diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index c0176f031202..6c0826d9d172 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3174,7 +3174,7 @@ static const JSFunctionSpec array_methods[] = { JS_SELF_HOSTED_FN("fill", "ArrayFill", 3,0), - JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0,0), + JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0,0), JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0), JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0), JS_FS_END diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 62a0c66bb7ce..2a1c717e192c 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -156,7 +156,7 @@ JSRuntime::initializeAtoms(JSContext *cx) if (!wellKnownSymbols) return false; - ImmutablePropertyNamePtr *descriptions = &commonNames->Symbol_iterator; + ImmutablePropertyNamePtr *descriptions = commonNames->wellKnownSymbolDescriptions(); ImmutableSymbolPtr *symbols = reinterpret_cast(wellKnownSymbols); for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) { JS::Symbol *symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 38b145918c77..8516f474aea6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -886,7 +886,7 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp) } static const JSFunctionSpec iterator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0), JS_FN("next", iterator_next, 0, 0), JS_FS_END }; @@ -965,7 +965,7 @@ const Class ArrayIteratorObject::class_ = { }; static const JSFunctionSpec array_iterator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "ArrayIteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0), JS_FS_END }; @@ -1004,7 +1004,7 @@ const Class StringIteratorObject::class_ = { }; static const JSFunctionSpec string_iterator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "StringIteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0), JS_FS_END }; @@ -1369,8 +1369,14 @@ ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavio args.setThis(ObjectValue(*iterableObj)); RootedValue callee(cx); +#ifdef JS_HAS_SYMBOLS + RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); + if (!JSObject::getGeneric(cx, iterableObj, iterableObj, iteratorId, &callee)) + return false; +#else if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee)) return false; +#endif // If obj[@@iterator] is undefined and we were asked to allow non-iterables, // bail out now without setting iterator. This will make valueIsIterable(), diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index b5a475346539..0b93fce23072 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1592,6 +1592,13 @@ ExpressionDecompiler::decompilePC(jsbytecode *pc) return sprinter.printf("%d", GetBytecodeInteger(pc)) >= 0; case JSOP_STRING: return quote(loadAtom(pc), '"'); + case JSOP_SYMBOL: { + unsigned i = uint8_t(pc[1]); + MOZ_ASSERT(i < JS::WellKnownSymbolLimit); + if (i < JS::WellKnownSymbolLimit) + return write(cx->names().wellKnownSymbolDescriptions()[i]); + break; + } case JSOP_UNDEFINED: return write(js_undefined_str); case JSOP_THIS: diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 8f060f8f87fb..c74f06e6a0ae 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4204,7 +4204,7 @@ static const JSFunctionSpec string_methods[] = { JS_SELF_HOSTED_FN("fontcolor","String_fontcolor", 1,0), JS_SELF_HOSTED_FN("fontsize", "String_fontsize", 1,0), - JS_SELF_HOSTED_FN("@@iterator", "String_iterator", 0,0), + JS_SELF_HOSTED_SYM_FN(iterator, "String_iterator", 0,0), JS_FS_END }; diff --git a/js/src/tests/ecma_6/Array/for_of_2.js b/js/src/tests/ecma_6/Array/for_of_2.js index 8d72fb7b50bb..6bf121613723 100644 --- a/js/src/tests/ecma_6/Array/for_of_2.js +++ b/js/src/tests/ecma_6/Array/for_of_2.js @@ -25,7 +25,7 @@ function TestChangeArrayIteratorNext() { return M2; } - var iter = ([])['@@iterator'](); + var iter = ([])[std_iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff --git a/js/src/tests/ecma_6/Array/for_of_3.js b/js/src/tests/ecma_6/Array/for_of_3.js index 9895da2f00a1..b659287f159c 100644 --- a/js/src/tests/ecma_6/Array/for_of_3.js +++ b/js/src/tests/ecma_6/Array/for_of_3.js @@ -27,7 +27,7 @@ function TestIncreaseArrayLength() { return M2; } - var iter = ([])['@@iterator'](); + var iter = ([])[std_iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff --git a/js/src/tests/ecma_6/Array/for_of_4.js b/js/src/tests/ecma_6/Array/for_of_4.js index 1596038839d4..303792014c4b 100644 --- a/js/src/tests/ecma_6/Array/for_of_4.js +++ b/js/src/tests/ecma_6/Array/for_of_4.js @@ -26,7 +26,7 @@ function TestDecreaseArrayLength() { return M2; } - var iter = ([])['@@iterator'](); + var iter = ([])[std_iterator](); var iterProto = Object.getPrototypeOf(iter); var OldNext = iterProto.next; var NewNext = function () { diff --git a/js/src/tests/ecma_6/Array/from_errors.js b/js/src/tests/ecma_6/Array/from_errors.js index ad494a3507c0..4266cb206dca 100644 --- a/js/src/tests/ecma_6/Array/from_errors.js +++ b/js/src/tests/ecma_6/Array/from_errors.js @@ -134,8 +134,8 @@ assertEq(obj instanceof C, true); for (var primitive of [undefined, null, 17]) { assertThrowsInstanceOf( () => Array.from({ - "@@iterator": () => { - next: () => primitive + [std_iterator]() { + return {next() { return primitive; }}; } }), TypeError); diff --git a/js/src/tests/ecma_6/Array/from_iterable.js b/js/src/tests/ecma_6/Array/from_iterable.js index 2fe3f890ad44..3e0993318ebf 100644 --- a/js/src/tests/ecma_6/Array/from_iterable.js +++ b/js/src/tests/ecma_6/Array/from_iterable.js @@ -8,7 +8,7 @@ // If an object has both .length and [@@iterator] properties, [@@iterator] is used. var a = ['a', 'e', 'i', 'o', 'u']; -a["@@iterator"] = function* () { +a[std_iterator] = function* () { for (var i = 5; i--; ) yield this[i]; }; diff --git a/js/src/tests/ecma_6/Array/from_proxy.js b/js/src/tests/ecma_6/Array/from_proxy.js index 40505e8a83e9..0374dfe4fff2 100644 --- a/js/src/tests/ecma_6/Array/from_proxy.js +++ b/js/src/tests/ecma_6/Array/from_proxy.js @@ -6,19 +6,19 @@ var log = []; function LoggingProxy(target) { var h = { defineProperty: function (t, id) { - log.push("define " + id); + log.push("define", id); return undefined; }, has: function (t, id) { - log.push("has " + id); + log.push("has", id); return id in t; }, get: function (t, id) { - log.push("get " + id); + log.push("get", id); return t[id]; }, set: function (t, id, v) { - log.push("set " + id); + log.push("set", id); t[id] = v; } }; @@ -30,23 +30,25 @@ function LoggingProxy(target) { // but handler.set to set the length. LoggingProxy.from = Array.from; LoggingProxy.from([3, 4, 5]); -assertDeepEq(log, ["define 0", "define 1", "define 2", "set length"]); +assertDeepEq(log, ["define", "0", "define", "1", "define", "2", "set", "length"]); // When the argument passed to Array.from is a Proxy, Array.from // calls handler.get on it. log = []; assertDeepEq(Array.from(new LoggingProxy([3, 4, 5])), [3, 4, 5]); -assertDeepEq(log, ["get @@iterator", - "get length", "get 0", "get length", "get 1", "get length", "get 2", - "get length"]); +assertDeepEq(log, ["get", std_iterator, + "get", "length", "get", "0", + "get", "length", "get", "1", + "get", "length", "get", "2", + "get", "length"]); // Array-like iteration only gets the length once. log = []; var arr = [5, 6, 7]; -arr["@@iterator"] = undefined; +arr[std_iterator] = undefined; assertDeepEq(Array.from(new LoggingProxy(arr)), [5, 6, 7]); -assertDeepEq(log, ["get @@iterator", - "get length", "get 0", "get 1", "get 2"]); +assertDeepEq(log, ["get", std_iterator, + "get", "length", "get", "0", "get", "1", "get", "2"]); if (typeof reportCompare === 'function') reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/Array/from_string.js b/js/src/tests/ecma_6/Array/from_string.js index 892954621789..c57bcc7f67e9 100644 --- a/js/src/tests/ecma_6/Array/from_string.js +++ b/js/src/tests/ecma_6/Array/from_string.js @@ -11,11 +11,11 @@ assertDeepEq(Array.from(gclef), [gclef]); assertDeepEq(Array.from(gclef + " G"), [gclef, " ", "G"]); // Array.from on a string calls the @@iterator method. -String.prototype["@@iterator"] = function* () { yield 1; yield 2; }; +String.prototype[std_iterator] = function* () { yield 1; yield 2; }; assertDeepEq(Array.from("anything"), [1, 2]); // If the iterator method is deleted, Strings are still arraylike. -delete String.prototype["@@iterator"]; +delete String.prototype[std_iterator]; assertDeepEq(Array.from("works"), ['w', 'o', 'r', 'k', 's']); assertDeepEq(Array.from(gclef), ['\uD834', '\uDD1E']); diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index 4c4ece9c3b84..e5f399913a45 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -17,8 +17,6 @@ function* g() { yield 1; } var GeneratorFunctionPrototype = Object.getPrototypeOf(g); var GeneratorFunction = GeneratorFunctionPrototype.constructor; var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; -// FIXME: This should be a symbol. -var std_iterator = "@@iterator"; // A generator function should have the same set of properties as any @@ -66,7 +64,9 @@ function TestGeneratorObjectPrototype() { assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype), GeneratorObjectPrototype); - var expected_property_names = ["next", "throw", "constructor", std_iterator]; + var expected_property_names = ["next", "throw", "constructor"]; + if (!JS_HAS_SYMBOLS) + expected_property_names.push(std_iterator); var found_property_names = Object.getOwnPropertyNames(GeneratorObjectPrototype); diff --git a/js/src/tests/ecma_6/Symbol/property-reflection.js b/js/src/tests/ecma_6/Symbol/property-reflection.js index c7e30ddb4d4e..c2a6ac6dbfd4 100644 --- a/js/src/tests/ecma_6/Symbol/property-reflection.js +++ b/js/src/tests/ecma_6/Symbol/property-reflection.js @@ -66,10 +66,8 @@ if (typeof Symbol === "function") { assertEq(descs.hasOwnProperty(s1), true); assertEq(descs.hasOwnProperty(s2), true); assertEq(descs.hasOwnProperty(s3), false); - assertEq([].hasOwnProperty(Symbol.iterator), false); - if (!("@@iterator" in [])) - throw new Error("Congratulations on implementing Symbol.iterator! Please update this test."); - assertEq(Array.prototype.hasOwnProperty(Symbol.iterator), false); // should be true + assertEq([].hasOwnProperty(std_iterator), false); + assertEq(Array.prototype.hasOwnProperty(std_iterator), true); // Object.prototype.propertyIsEnumerable assertEq(n.propertyIsEnumerable(s1), true); diff --git a/js/src/tests/js1_8/regress/regress-469625-03.js b/js/src/tests/js1_8/regress/regress-469625-03.js index e48034f6a5cc..cdffa7778f11 100644 --- a/js/src/tests/js1_8/regress/regress-469625-03.js +++ b/js/src/tests/js1_8/regress/regress-469625-03.js @@ -26,7 +26,8 @@ function test() var [a, b, [c0, c1]] = [x, x, x]; } - expect = 'TypeError: (intermediate value)[\'@@iterator\'](...).next(...).value is null'; + var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; + expect = `TypeError: (intermediate value)[${ITERATOR}](...).next(...).value is null`; actual = 'No Error'; try { diff --git a/js/src/tests/js1_8_5/extensions/decompile-for-of.js b/js/src/tests/js1_8_5/extensions/decompile-for-of.js new file mode 100644 index 000000000000..ecb428e5ddf8 --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/decompile-for-of.js @@ -0,0 +1,28 @@ +// The decompiler can handle the implicit call to @@iterator in a for-of loop. + +var x; +function check(code) { + var s = "no exception thrown"; + try { + eval(code); + } catch (exc) { + s = exc.message; + } + + var ITERATOR = JS_HAS_SYMBOLS ? "Symbol.iterator" : "'@@iterator'"; + assertEq(s, `x[${ITERATOR}] is not a function`); +} + +x = {}; +check("for (var v of x) throw fit;"); +check("[...x]"); +check("Math.hypot(...x)"); + +x[std_iterator] = "potato"; +check("for (var v of x) throw fit;"); + +x[std_iterator] = {}; +check("for (var v of x) throw fit;"); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/shell.js b/js/src/tests/shell.js index 33b03305b095..dc726cf1c632 100644 --- a/js/src/tests/shell.js +++ b/js/src/tests/shell.js @@ -876,3 +876,6 @@ function OptLevel( i ) { cx.setOptimizationLevel(i); } /* end of Rhino functions */ + +var JS_HAS_SYMBOLS = typeof Symbol === "function"; +var std_iterator = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 4389feed474f..79f72523e49d 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -247,7 +247,7 @@ const Class StarGeneratorObject::class_ = { }; static const JSFunctionSpec star_generator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0), JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0), JS_FS_END @@ -256,7 +256,7 @@ static const JSFunctionSpec star_generator_methods[] = { #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) static const JSFunctionSpec legacy_generator_methods[] = { - JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "LegacyGeneratorIteratorShim", 0, 0), // "send" is an alias for "next". JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM), JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM), diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index bda33ad36ad2..18c5c1d8cbce 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -324,7 +324,7 @@ InitBareBuiltinCtor(JSContext *cx, Handle global, JSProtoKey prot GlobalObject::initSelfHostingBuiltins(JSContext *cx, Handle global, const JSFunctionSpec *builtins) { - /* Define a top-level property 'undefined' with the undefined value. */ + // Define a top-level property 'undefined' with the undefined value. if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY)) @@ -332,6 +332,20 @@ GlobalObject::initSelfHostingBuiltins(JSContext *cx, Handle globa return false; } + // Define a top-level property 'std_iterator' with the name of the method + // used by for-of loops to create an iterator. + RootedValue std_iterator(cx); +#ifdef JS_HAS_SYMBOLS + std_iterator.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::iterator)); +#else + std_iterator.setString(cx->names().std_iterator); +#endif + if (!JS_DefineProperty(cx, global, "std_iterator", std_iterator, + JSPROP_PERMANENT | JSPROP_READONLY)) + { + return false; + } + return InitBareBuiltinCtor(cx, global, JSProto_Array) && InitBareBuiltinCtor(cx, global, JSProto_TypedArray) && InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) && diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 30051fc55465..4f76d1222d85 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1124,6 +1124,7 @@ HandleError(JSContext *cx, InterpreterRegs ®s) #define PUSH_BOOLEAN(b) REGS.sp++->setBoolean(b) #define PUSH_DOUBLE(d) REGS.sp++->setDouble(d) #define PUSH_INT32(i) REGS.sp++->setInt32(i) +#define PUSH_SYMBOL(s) REGS.sp++->setSymbol(s) #define PUSH_STRING(s) do { REGS.sp++->setString(s); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) #define PUSH_OBJECT(obj) do { REGS.sp++->setObject(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) #define PUSH_OBJECT_OR_NULL(obj) do { REGS.sp++->setObjectOrNull(obj); assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } while (0) @@ -1601,7 +1602,6 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_UNUSED2) -CASE(JSOP_UNUSED45) CASE(JSOP_UNUSED46) CASE(JSOP_UNUSED47) CASE(JSOP_UNUSED48) @@ -2706,6 +2706,10 @@ CASE(JSOP_TOSTRING) } END_CASE(JSOP_TOSTRING) +CASE(JSOP_SYMBOL) + PUSH_SYMBOL(cx->wellKnownSymbols().get(GET_UINT8(REGS.pc))); +END_CASE(JSOP_SYMBOL) + CASE(JSOP_OBJECT) { RootedNativeObject &ref = rootNativeObject0; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 877ef4094432..fc7eaa34e4f9 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -416,7 +416,15 @@ */ \ macro(JSOP_DUPAT, 44, "dupat", NULL, 4, 0, 1, JOF_UINT24) \ \ - macro(JSOP_UNUSED45, 45, "unused45", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Push a well-known symbol onto the operand stack. + * Category: Literals + * Type: Constants + * Operands: uint8_t n, the JS::SymbolCode of the symbol to use + * Stack: => symbol + */ \ + macro(JSOP_SYMBOL, 45, "symbol", NULL, 2, 0, 1, JOF_UINT8) \ + \ macro(JSOP_UNUSED46, 46, "unused46", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED47, 47, "unused47", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED48, 48, "unused48", NULL, 1, 0, 0, JOF_BYTE) \ diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp index ca2e6870f499..5cadc2ece57b 100644 --- a/js/src/vm/PIC.cpp +++ b/js/src/vm/PIC.cpp @@ -18,6 +18,12 @@ using namespace js; using namespace js::gc; +#ifdef JS_HAS_SYMBOLS +#define STD_ITERATOR_ID SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator) +#else +#define STD_ITERATOR_ID ::js::NameToId(cx->names().std_iterator) +#endif + bool js::ForOfPIC::Chain::initialize(JSContext *cx) { @@ -44,8 +50,8 @@ js::ForOfPIC::Chain::initialize(JSContext *cx) // do set disabled_ now, and clear it later when we succeed. disabled_ = true; - // Look up '@@iterator' on Array.prototype, ensure it's a slotful shape. - Shape *iterShape = arrayProto->lookup(cx, cx->names().std_iterator); + // Look up Array.prototype[@@iterator], ensure it's a slotful shape. + Shape *iterShape = arrayProto->lookup(cx, STD_ITERATOR_ID); if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter()) return true; @@ -143,8 +149,8 @@ js::ForOfPIC::Chain::tryOptimizeArray(JSContext *cx, HandleArrayObject array, bo if (!isOptimizableArray(array)) return true; - // Ensure array doesn't define '@@iterator' directly. - if (array->lookup(cx, cx->names().std_iterator)) + // Ensure array doesn't define @@iterator directly. + if (array->lookup(cx, STD_ITERATOR_ID)) return true; // Good to optimize now, create stub to add. @@ -197,7 +203,7 @@ js::ForOfPIC::Chain::isArrayStateStillSane() if (arrayProto_->lastProperty() != arrayProtoShape_) return false; - // Ensure that Array.prototype['@@iterator'] contains the + // Ensure that Array.prototype[@@iterator] contains the // canonical iterator function. if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_) return false; diff --git a/js/src/vm/PIC.h b/js/src/vm/PIC.h index 6e564482777c..93badceba1aa 100644 --- a/js/src/vm/PIC.h +++ b/js/src/vm/PIC.h @@ -130,7 +130,7 @@ struct ForOfPIC /* * A ForOfPIC has only one kind of stub for now: one that holds the shape - * of an array object that does not override its '@@iterator' property. + * of an array object that does not override its @@iterator property. */ class Stub : public BaseStub { @@ -164,8 +164,8 @@ struct ForOfPIC * ArrayIterator.prototype's shape (arrayIteratorProtoShape_) * To ensure that an ArrayIterator.prototype has not been modified. * - * Array.prototype's slot number for '@@iterator' (arrayProtoIteratorSlot_) - * Array.prototype's canonical value for '@@iterator' (canonicalIteratorFunc_) + * Array.prototype's slot number for @@iterator (arrayProtoIteratorSlot_) + * Array.prototype's canonical value for @@iterator (canonicalIteratorFunc_) * To quickly retreive and ensure that the iterator constructor * stored in the slot has not changed. * @@ -182,7 +182,7 @@ struct ForOfPIC HeapPtrNativeObject arrayIteratorProto_; // Shape of matching Array.prototype object, and slot containing - // the '@@iterator' for it, and the canonical value. + // the @@iterator for it, and the canonical value. HeapPtrShape arrayProtoShape_; uint32_t arrayProtoIteratorSlot_; HeapValue canonicalIteratorFunc_; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 7484423ccade..0a40112557c4 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -406,6 +406,10 @@ struct JSAtomState #define PROPERTYNAME_FIELD(name, code, init, clasp) js::ImmutablePropertyNamePtr name; JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD) #undef PROPERTYNAME_FIELD + + js::ImmutablePropertyNamePtr *wellKnownSymbolDescriptions() { + return &Symbol_iterator; + } }; namespace js { diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index c7593fdac0ad..31e2951a747f 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1501,6 +1501,12 @@ CloneValue(JSContext *cx, HandleValue selfHostedValue, MutableHandleValue vp) if (!clone) return false; vp.setString(clone); + } else if (selfHostedValue.isSymbol()) { + // Well-known symbols are shared. + JS::Symbol *sym = selfHostedValue.toSymbol(); + MOZ_ASSERT(sym->isWellKnownSymbol()); + MOZ_ASSERT(cx->wellKnownSymbols().get(size_t(sym->code())) == sym); + vp.set(selfHostedValue); } else { MOZ_CRASH("Self-hosting CloneValue can't clone given value."); } diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 9b9fc253800a..5a32e388c064 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -760,7 +760,7 @@ TypedArrayObject::subarray(JSContext *cx, unsigned argc, Value *vp) /* static */ const JSFunctionSpec TypedArrayObject::protoFunctions[] = { - JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), + JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0, 0), \ JS_FN("subarray", TypedArrayObject::subarray, 2, 0), JS_FN("set", TypedArrayObject::set, 2, 0), JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0), diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 21ca687a0e8b..2de2bb995923 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -27,8 +27,21 @@ namespace js { * this wiki page: * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode + * + * === GREETINGS, FELLOW SUBTRAHEND INCREMENTER! === + * For the time being, please increment the subtrahend by 2 each time it + * changes, because we have two flavors of bytecode: with JSOP_SYMBOL (in + * Nightly) and without (all others). FIXME: Bug 1066322 - Enable ES6 symbols + * in all builds. */ -static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 188); +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 190; +static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above"); +static const uint32_t XDR_BYTECODE_VERSION = + uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND +#ifdef JS_HAS_SYMBOLS + + 1 +#endif + )); class XDRBuffer { public: diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 31fe8c45b408..acda43778a39 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -146,6 +146,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 var isNightlyBuild = version.endsWith("a1"); var isReleaseBuild = !version.contains("a"); const jsHasSymbols = typeof Symbol === "function"; + const kIteratorSymbol = jsHasSymbols ? Symbol.iterator : "@@iterator"; var gPrototypeProperties = {}; gPrototypeProperties['Date'] = ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear", @@ -168,7 +169,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", - "findIndex", "copyWithin", "fill", "@@iterator", "entries", "keys", "constructor"]; + "findIndex", "copyWithin", "fill", kIteratorSymbol, "entries", "keys", "constructor"]; if (isNightlyBuild) { let pjsMethods = ['mapPar', 'reducePar', 'scanPar', 'scatterPar', 'filterPar']; gPrototypeProperties['Array'] = gPrototypeProperties['Array'].concat(pjsMethods); @@ -177,7 +178,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"]; } gPrototypeProperties['TypedArray'] = - ["length", "buffer", "byteLength", "byteOffset", "@@iterator", "subarray", + ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray", "set", "copyWithin"]; for (var c of errorObjectClasses) { @@ -500,10 +501,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 var typedArrayProto = Object.getPrototypeOf(Int8Array.prototype); - gPrototypeProperties['TypedArray'] = - ["length", "buffer", "byteLength", "byteOffset", "@@iterator", "subarray", - "set", "copyWithin"]; - var desiredInheritedProps = Object.getOwnPropertyNames(typedArrayProto).sort(); var inheritedProps = filterOut(desiredInheritedProps, ["BYTES_PER_ELEMENT", "constructor"]); diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index a03249ff13a6..c83c558cfe9b 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -252,24 +252,14 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) PR_INIT_CLIST(&mDOMMediaQueryLists); } -nsPresContext::~nsPresContext() +void +nsPresContext::Destroy() { - NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer"); - SetShell(nullptr); - - NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists), - "must not have media query lists left"); - - // Disconnect the refresh driver *after* the transition manager, which - // needs it. - if (mRefreshDriver && mRefreshDriver->PresContext() == this) { - mRefreshDriver->Disconnect(); - } - if (mEventManager) { // unclear if these are needed, but can't hurt mEventManager->NotifyDestroyPresContext(this); mEventManager->SetPresContext(nullptr); + mEventManager = nullptr; } if (mPrefChangedTimer) @@ -321,6 +311,24 @@ nsPresContext::~nsPresContext() Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "nglayout.debug.paint_flashing_chrome", this); + + // Disconnect the refresh driver *after* the transition manager, which + // needs it. + if (mRefreshDriver && mRefreshDriver->PresContext() == this) { + mRefreshDriver->Disconnect(); + mRefreshDriver = nullptr; + } +} + +nsPresContext::~nsPresContext() +{ + NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer"); + SetShell(nullptr); + + NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists), + "must not have media query lists left"); + + Destroy(); } NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext) @@ -368,13 +376,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? - if (tmp->mEventManager) { - // unclear if these are needed, but can't hurt - tmp->mEventManager->NotifyDestroyPresContext(tmp); - tmp->mEventManager->SetPresContext(nullptr); - tmp->mEventManager = nullptr; - } - // We own only the items in mDOMMediaQueryLists that have listeners; // this reference is managed by their AddListener and RemoveListener // methods. @@ -387,15 +388,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) } // NS_RELEASE(tmp->mLanguage); // an atom - // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings); - if (tmp->mPrefChangedTimer) - { - tmp->mPrefChangedTimer->Cancel(); - tmp->mPrefChangedTimer = nullptr; - } + + tmp->Destroy(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index efa40f549ac8..59c6c02eadc4 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -1172,6 +1172,9 @@ public: #endif protected: + // May be called multiple times (unlink, destructor) + void Destroy(); + void InvalidatePaintedLayers(); void AppUnitsPerDevPixelChanged(); diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 13b254e3e37c..90a2f8a42590 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -2956,11 +2956,11 @@ PropertyProvider::ComputeJustifiableCharacters(int32_t aOffset, int32_t aLength) run(mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aLength); run.SetOriginalOffset(aOffset); uint32_t justifiableChars = 0; - bool isCJK = IsChineseOrJapanese(mFrame); + bool isCJ = IsChineseOrJapanese(mFrame); while (run.NextRun()) { for (int32_t i = 0; i < run.GetRunLength(); ++i) { justifiableChars += - IsJustifiableCharacter(mFrag, run.GetOriginalOffset() + i, isCJK); + IsJustifiableCharacter(mFrag, run.GetOriginalOffset() + i, isCJ); } } return justifiableChars; @@ -3083,7 +3083,7 @@ PropertyProvider::GetSpacingInternal(uint32_t aStart, uint32_t aLength, gfxFloat halfJustificationSpace = mJustificationSpacing/2; // Scan non-skipped characters and adjust justifiable chars, adding // justification space on either side of the cluster - bool isCJK = IsChineseOrJapanese(mFrame); + bool isCJ = IsChineseOrJapanese(mFrame); gfxSkipCharsIterator justificationStart(mStart), justificationEnd(mStart); FindJustificationRange(&justificationStart, &justificationEnd); @@ -3094,7 +3094,7 @@ PropertyProvider::GetSpacingInternal(uint32_t aStart, uint32_t aLength, int32_t runOriginalOffset = run.GetOriginalOffset(); for (int32_t i = 0; i < run.GetRunLength(); ++i) { int32_t iterOriginalOffset = runOriginalOffset + i; - if (IsJustifiableCharacter(mFrag, iterOriginalOffset, isCJK)) { + if (IsJustifiableCharacter(mFrag, iterOriginalOffset, isCJ)) { iter.SetOriginalOffset(iterOriginalOffset); FindClusterStart(mTextRun, runOriginalOffset, &iter); uint32_t clusterFirstChar = iter.GetSkippedOffset(); @@ -8478,13 +8478,13 @@ nsTextFrame::TrimTrailingWhiteSpace(nsRenderingContext* aRC) // Check if any character in the last cluster is justifiable PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength, nullptr, 0, nsTextFrame::eInflated); - bool isCJK = IsChineseOrJapanese(this); + bool isCJ = IsChineseOrJapanese(this); gfxSkipCharsIterator justificationStart(start), justificationEnd(trimmedEndIter); provider.FindJustificationRange(&justificationStart, &justificationEnd); for (int32_t i = justificationEnd.GetOriginalOffset(); i < trimmed.GetEnd(); ++i) { - if (IsJustifiableCharacter(frag, i, isCJK)) { + if (IsJustifiableCharacter(frag, i, isCJ)) { result.mLastCharIsJustifiable = true; } } diff --git a/layout/reftests/css-gradients/reftest.list b/layout/reftests/css-gradients/reftest.list index 67510d9c69dd..58c835f79630 100644 --- a/layout/reftests/css-gradients/reftest.list +++ b/layout/reftests/css-gradients/reftest.list @@ -148,5 +148,5 @@ fuzzy-if(d2d,47,400) == linear-onestopposition-1.html linear-onestopposition-1-r == bug-916535-background-repeat-linear.html bug-916535-background-repeat-linear-ref.html fuzzy(1,800000) == large-gradient-1.html large-gradient-1-ref.html == large-gradient-2.html large-gradient-2-ref.html -fuzzy(1,800000) == large-gradient-3.html large-gradient-3-ref.html -== large-gradient-4.html large-gradient-4-ref.html +fails-if(browserIsRemote&&!B2G) fuzzy(1,800000) == large-gradient-3.html large-gradient-3-ref.html +fails-if(browserIsRemote&&!B2G) == large-gradient-4.html large-gradient-4-ref.html diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 44c375dbc6ec..72a1b2ef05c5 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -248,10 +248,10 @@ include outline/reftest.list include object/reftest.list # ogg-video/ -include ogg-video/reftest.list +skip-if(!B2G&&browserIsRemote) include ogg-video/reftest.list # webm-video/ -include webm-video/reftest.list +skip-if(!B2G&&browserIsRemote) include webm-video/reftest.list # parser/ include ../../parser/htmlparser/tests/reftest/reftest.list diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index dd28c0e64bae..50d950977216 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -37,6 +37,7 @@ generated-files = css_properties.js [test_animations.html] skip-if = toolkit == 'android' [test_animations_omta.html] +skip-if = buildapp == 'mulet' [test_animations_omta_start.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017 [test_animations_pausing.html] diff --git a/netwerk/cookie/PCookieService.ipdl b/netwerk/cookie/PCookieService.ipdl index 5f6ccfb01435..183a4ff3f9c2 100644 --- a/netwerk/cookie/PCookieService.ipdl +++ b/netwerk/cookie/PCookieService.ipdl @@ -27,7 +27,7 @@ namespace net { * @see nsICookiePermission */ -sync protocol PCookieService +prio(normal upto urgent) sync protocol PCookieService { manager PNecko; @@ -60,10 +60,10 @@ parent: * * @return the resulting cookie string. */ - sync GetCookieString(URIParams host, - bool isForeign, - bool fromHttp, - SerializedLoadContext loadContext) + prio(urgent) sync GetCookieString(URIParams host, + bool isForeign, + bool fromHttp, + SerializedLoadContext loadContext) returns (nsCString result); /* @@ -95,12 +95,12 @@ parent: * @see nsICookieService.setCookieStringFromHttp * @see mozIThirdPartyUtil.isThirdPartyChannel */ - SetCookieString(URIParams host, - bool isForeign, - nsCString cookieString, - nsCString serverTime, - bool fromHttp, - SerializedLoadContext loadContext); + prio(urgent) async SetCookieString(URIParams host, + bool isForeign, + nsCString cookieString, + nsCString serverTime, + bool fromHttp, + SerializedLoadContext loadContext); __delete__(); }; diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index ab6bac7d27af..010104b1bc6c 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -35,7 +35,7 @@ namespace mozilla { namespace net { //------------------------------------------------------------------- -sync protocol PNecko +prio(normal upto urgent) sync protocol PNecko { manager PContent; manages PHttpChannel; @@ -55,7 +55,7 @@ sync protocol PNecko parent: __delete__(); - PCookieService(); + prio(urgent) async PCookieService(); PHttpChannel(PBrowserOrId browser, SerializedLoadContext loadContext, HttpChannelCreationArgs args); diff --git a/security/manager/ssl/public/nsICertOverrideService.idl b/security/manager/ssl/public/nsICertOverrideService.idl index 14b5c5733fff..8648b8e41f29 100644 --- a/security/manager/ssl/public/nsICertOverrideService.idl +++ b/security/manager/ssl/public/nsICertOverrideService.idl @@ -18,7 +18,7 @@ interface nsIX509Cert; * {host:port, cert-fingerprint, allowed-overrides} * that the user wants to accept without further warnings. */ -[scriptable, uuid(31738d2a-77d3-4359-84c9-4be2f38fb8c5)] +[scriptable, uuid(be019e47-22fc-4355-9f16-9ab047d6742d)] interface nsICertOverrideService : nsISupports { /** @@ -108,15 +108,6 @@ interface nsICertOverrideService : nsISupports { void clearValidityOverride(in ACString aHostName, in int32_t aPort); - /** - * Obtain the full list of hostname:port for which overrides are known. - * - * @param aCount The number of host:port entries returned - * @param aHostsWithPortsArray The array of host:port entries returned - */ - void getAllOverrideHostsWithPorts(out uint32_t aCount, - [array, size_is(aCount)] out wstring aHostsWithPortsArray); - /** * Is the given cert used in rules? * diff --git a/security/manager/ssl/src/nsCertOverrideService.cpp b/security/manager/ssl/src/nsCertOverrideService.cpp index bd89dfb3ea24..0278bea89840 100644 --- a/security/manager/ssl/src/nsCertOverrideService.cpp +++ b/security/manager/ssl/src/nsCertOverrideService.cpp @@ -6,29 +6,24 @@ #include "nsCertOverrideService.h" -#include "pkix/pkixtypes.h" -#include "nsIX509Cert.h" #include "NSSCertDBTrustDomain.h" -#include "nsNSSCertificate.h" -#include "nsNSSCertHelper.h" -#include "nsCRT.h" +#include "ScopedNSSTypes.h" +#include "SharedSSLState.h" +#include "mozilla/Telemetry.h" #include "nsAppDirectoryServiceDefs.h" -#include "nsStreamUtils.h" -#include "nsNetUtil.h" +#include "nsCRT.h" #include "nsILineInputStream.h" #include "nsIObserver.h" #include "nsIObserverService.h" -#include "nsISupportsPrimitives.h" +#include "nsIX509Cert.h" +#include "nsNSSCertHelper.h" +#include "nsNSSCertificate.h" +#include "nsNSSComponent.h" +#include "nsNetUtil.h" #include "nsPromiseFlatString.h" -#include "nsThreadUtils.h" +#include "nsStreamUtils.h" #include "nsStringBuffer.h" -#include "ScopedNSSTypes.h" -#include "SharedSSLState.h" - -#include "nspr.h" -#include "pk11pub.h" -#include "certdb.h" -#include "sechash.h" +#include "nsThreadUtils.h" #include "ssl.h" // For SSL_ClearSessionCache using namespace mozilla; @@ -106,18 +101,11 @@ nsCertOverrideService::Init() return NS_ERROR_NOT_SAME_THREAD; } + // Note that the names of these variables would seem to indicate that at one + // point another hash algorithm was used and is still supported for backwards + // compatibility. This is not the case. It has always been SHA256. mOidTagForStoringNewHashes = SEC_OID_SHA256; - - SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes); - if (!od) - return NS_ERROR_FAILURE; - - char *dotted_oid = CERT_GetOidString(&od->oid); - if (!dotted_oid) - return NS_ERROR_FAILURE; - - mDottedOidForStoringNewHashes = dotted_oid; - PR_smprintf_free(dotted_oid); + mDottedOidForStoringNewHashes.Assign("OID.2.16.840.1.101.3.4.2.1"); nsCOMPtr observerService = mozilla::services::GetObserverService(); @@ -172,7 +160,7 @@ nsCertOverrideService::Observe(nsISupports *, mSettingsFile = nullptr; } Read(); - + CountPermanentOverrideTelemetry(); } return NS_OK; @@ -398,42 +386,6 @@ GetCertFingerprintByOidTag(nsIX509Cert *aCert, return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp); } -static nsresult -GetCertFingerprintByDottedOidString(CERTCertificate* nsscert, - const nsCString &dottedOid, - nsCString &fp) -{ - SECItem oid; - oid.data = nullptr; - oid.len = 0; - SECStatus srv = SEC_StringToOID(nullptr, &oid, - dottedOid.get(), dottedOid.Length()); - if (srv != SECSuccess) - return NS_ERROR_FAILURE; - - SECOidTag oid_tag = SECOID_FindOIDTag(&oid); - SECITEM_FreeItem(&oid, false); - - if (oid_tag == SEC_OID_UNKNOWN) - return NS_ERROR_FAILURE; - - return GetCertFingerprintByOidTag(nsscert, oid_tag, fp); -} - -static nsresult -GetCertFingerprintByDottedOidString(nsIX509Cert *aCert, - const nsCString &dottedOid, - nsCString &fp) -{ - - ScopedCERTCertificate nsscert(aCert->GetCert()); - if (!nsscert) { - return NS_ERROR_FAILURE; - } - - return GetCertFingerprintByDottedOidString(nsscert.get(), dottedOid, fp); -} - NS_IMETHODIMP nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName, int32_t aPort, @@ -546,14 +498,17 @@ nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, int32_t nsAutoCString fpStr; nsresult rv; + // This code was originally written in a way that suggested that other hash + // algorithms are supported for backwards compatibility. However, this was + // always unnecessary, because only SHA256 has ever been used here. if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr); + if (NS_FAILED(rv)) { + return rv; + } + } else { + return NS_ERROR_UNEXPECTED; } - else { - rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr); - } - if (NS_FAILED(rv)) - return rv; *_retval = settings.mFingerprint.Equals(fpStr); return NS_OK; @@ -649,15 +604,37 @@ nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, int32 mSettingsTable.RemoveEntry(hostPort.get()); Write(); } - SSL_ClearSessionCache(); + + if (EnsureNSSInitialized(nssEnsure)) { + SSL_ClearSessionCache(); + } else { + return NS_ERROR_NOT_AVAILABLE; + } + return NS_OK; } -NS_IMETHODIMP -nsCertOverrideService::GetAllOverrideHostsWithPorts(uint32_t *aCount, - char16_t ***aHostsWithPortsArray) +static PLDHashOperator +CountPermanentEntriesCallback(nsCertOverrideEntry* aEntry, void* aArg) { - return NS_ERROR_NOT_IMPLEMENTED; + uint32_t* overrideCount = reinterpret_cast(aArg); + if (aEntry && !aEntry->mSettings.mIsTemporary) { + *overrideCount = *overrideCount + 1; + return PL_DHASH_NEXT; + } + + return PL_DHASH_NEXT; +} + +void +nsCertOverrideService::CountPermanentOverrideTelemetry() +{ + ReentrantMonitorAutoEnter lock(monitor); + uint32_t overrideCount = 0; + mSettingsTable.EnumerateEntries(CountPermanentEntriesCallback, + &overrideCount); + Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES, + overrideCount); } static bool @@ -738,15 +715,11 @@ FindMatchingCertCallback(nsCertOverrideEntry *aEntry, if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) { nsAutoCString cert_fingerprint; - nsresult rv; + nsresult rv = NS_ERROR_UNEXPECTED; if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(cai->cert, cai->mOidTagForStoringNewHashes, cert_fingerprint); } - else { - rv = GetCertFingerprintByDottedOidString(cai->cert, - settings.mFingerprintAlgOID, cert_fingerprint); - } if (NS_SUCCEEDED(rv) && settings.mFingerprint.Equals(cert_fingerprint)) { cai->counter++; @@ -808,15 +781,11 @@ EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry, else { if (matchesDBKey(capac->cert, settings.mDBKey.get())) { nsAutoCString cert_fingerprint; - nsresult rv; + nsresult rv = NS_ERROR_UNEXPECTED; if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) { rv = GetCertFingerprintByOidTag(capac->cert, capac->mOidTagForStoringNewHashes, cert_fingerprint); } - else { - rv = GetCertFingerprintByDottedOidString(capac->cert, - settings.mFingerprintAlgOID, cert_fingerprint); - } if (NS_SUCCEEDED(rv) && settings.mFingerprint.Equals(cert_fingerprint)) { (*capac->enumerator)(settings, capac->userdata); @@ -860,4 +829,3 @@ nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, int32_t aPo } _retval.Assign(hostPort); } - diff --git a/security/manager/ssl/src/nsCertOverrideService.h b/security/manager/ssl/src/nsCertOverrideService.h index 51a17964b02b..ddcec05cf750 100644 --- a/security/manager/ssl/src/nsCertOverrideService.h +++ b/security/manager/ssl/src/nsCertOverrideService.h @@ -165,6 +165,8 @@ protected: SECOidTag mOidTagForStoringNewHashes; nsCString mDottedOidForStoringNewHashes; + void CountPermanentOverrideTelemetry(); + void RemoveAllFromMemory(); nsresult Read(); nsresult Write(); diff --git a/security/manager/ssl/src/nsNSSModule.cpp b/security/manager/ssl/src/nsNSSModule.cpp index e702cac49c83..c78c712f1b29 100644 --- a/security/manager/ssl/src/nsNSSModule.cpp +++ b/security/manager/ssl/src/nsNSSModule.cpp @@ -199,7 +199,6 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsStreamCipher) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObject) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObjectFactory) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsDataSignatureVerifier) -NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsCertOverrideService, Init) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsRandomGenerator) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, nsSSLStatus) NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo) @@ -207,6 +206,7 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo) typedef mozilla::psm::NSSErrorsService NSSErrorsService; NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NSSErrorsService, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSVersion) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCertOverrideService, Init) NS_DEFINE_NAMED_CID(NS_NSSCOMPONENT_CID); NS_DEFINE_NAMED_CID(NS_SSLSOCKETPROVIDER_CID); diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json index c832d5d558b8..a0faa1e38fa4 100644 --- a/security/manager/tools/PreloadedHPKPins.json +++ b/security/manager/tools/PreloadedHPKPins.json @@ -189,14 +189,6 @@ "VeriSign Universal Root Certification Authority", "XRamp Global CA Root" ] - }, - { - "name": "facebook", - "sha256_hashes": [ - "Verisign Class 3 Public Primary Certification Authority - G3", - "DigiCert High Assurance EV Root CA", - "DigiCert ECC Secure Server CA" - ] } ], @@ -234,10 +226,7 @@ // twitterCDN. More specific rules take precedence because we search for // exact domain name first. { "name": "twitter.com", "include_subdomains": true, - "pins": "twitterCDN", "test_mode": false }, - // Facebook (not pinned by Chrome) - { "name": "facebook.com", "include_subdomains": true, - "pins": "facebook", "test_mode": true } + "pins": "twitterCDN", "test_mode": false } ], "extra_certificates": [ diff --git a/security/sandbox/mac/Sandbox.h b/security/sandbox/mac/Sandbox.h index 3daf41f762e0..baa68fc8ac7c 100644 --- a/security/sandbox/mac/Sandbox.h +++ b/security/sandbox/mac/Sandbox.h @@ -11,6 +11,7 @@ enum MacSandboxType { MacSandboxType_Default = 0, MacSandboxType_Plugin, + MacSandboxType_Content, MacSandboxType_Invalid }; diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm index 4474deb665de..d91e4613363b 100644 --- a/security/sandbox/mac/Sandbox.mm +++ b/security/sandbox/mac/Sandbox.mm @@ -16,7 +16,7 @@ extern "C" void sandbox_free_error(char *errorbuf); namespace mozilla { -static const char rules[] = +static const char pluginSandboxRules[] = "(version 1)\n" "(deny default)\n" "(allow signal (target self))\n" @@ -38,28 +38,36 @@ static const char rules[] = " (literal \"%s\")\n" " (literal \"%s\"))\n"; +static const char contentSandboxRules[] = + "(version 1)\n" + "(allow default)\n"; + bool StartMacSandbox(MacSandboxInfo aInfo, nsCString &aErrorMessage) { - if (aInfo.type != MacSandboxType_Plugin) { + nsAutoCString profile; + if (aInfo.type == MacSandboxType_Plugin) { + if (nsCocoaFeatures::OnLionOrLater()) { + profile.AppendPrintf(pluginSandboxRules, ";", + aInfo.pluginInfo.pluginPath.get(), + aInfo.pluginInfo.pluginBinaryPath.get(), + aInfo.appPath.get(), + aInfo.appBinaryPath.get()); + } else { + profile.AppendPrintf(pluginSandboxRules, "", + aInfo.pluginInfo.pluginPath.get(), + aInfo.pluginInfo.pluginBinaryPath.get(), + aInfo.appPath.get(), + aInfo.appBinaryPath.get()); + } + } + else if (aInfo.type == MacSandboxType_Content) { + profile.AppendPrintf(contentSandboxRules); + } + else { aErrorMessage.AppendPrintf("Unexpected sandbox type %u", aInfo.type); return false; } - nsAutoCString profile; - if (nsCocoaFeatures::OnLionOrLater()) { - profile.AppendPrintf(rules, ";", - aInfo.pluginInfo.pluginPath.get(), - aInfo.pluginInfo.pluginBinaryPath.get(), - aInfo.appPath.get(), - aInfo.appBinaryPath.get()); - } else { - profile.AppendPrintf(rules, "", - aInfo.pluginInfo.pluginPath.get(), - aInfo.pluginInfo.pluginBinaryPath.get(), - aInfo.appPath.get(), - aInfo.appBinaryPath.get()); - } - char *errorbuf = NULL; if (sandbox_init(profile.get(), 0, &errorbuf)) { if (errorbuf) { diff --git a/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py b/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py index 32863d5e87b7..ff74fc4a295a 100644 --- a/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py +++ b/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py @@ -44,6 +44,7 @@ class MachFormatter(base.BaseFormatter): self.last_time = None self.terminal = terminal self.verbose = False + self._known_pids = set() self.summary_values = {"tests": 0, "subtests": 0, @@ -263,9 +264,14 @@ class MachFormatter(base.BaseFormatter): self.summary_values["expected"] += 1 def process_output(self, data): - return '"%s" (pid:%s command:%s)' % (data["data"], - data["process"], - data.get("command", "")) + rv = [] + + if "command" in data and data["process"] not in self._known_pids: + self._known_pids.add(data["process"]) + rv.append('(pid:%s) Full command: %s' % (data["process"], data["command"])) + + rv.append('(pid:%s) "%s"' % (data["process"], data["data"])) + return "\n".join(rv) def crash(self, data): test = self._get_test_id(data) diff --git a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.forms.html.ini b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.forms.html.ini index ba0fe7f1161f..bd3ff3cfe586 100644 --- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.forms.html.ini +++ b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.forms.html.ini @@ -3,6 +3,3 @@ [document.forms] expected: FAIL - [document.forms iteration] - expected: FAIL - diff --git a/toolkit/components/addoncompat/RemoteAddonsParent.jsm b/toolkit/components/addoncompat/RemoteAddonsParent.jsm index 761c21917a3c..f315b25fe78c 100644 --- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm @@ -709,7 +709,8 @@ RemoteBrowserElementInterposition.getters.contentWindow = function(addon, target }; let DummyContentDocument = { - readyState: "loading" + readyState: "loading", + location: { href: "about:blank" } }; RemoteBrowserElementInterposition.getters.contentDocument = function(addon, target) { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index adf33c2f74ef..ed625020a61f 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -6478,6 +6478,14 @@ "n_values": 24, "description": "Was a certificate error overridden on this handshake? What was it? (0=unknown error (indicating bug), 1=no, >1=a specific error)" }, + "SSL_PERMANENT_CERT_ERROR_OVERRIDES": { + "alert_emails": ["seceng@mozilla.org"], + "expires_in_version": "42", + "kind": "exponential", + "high": 1024, + "n_buckets": 10, + "description": "How many permanent certificate overrides a user has stored." + }, "TELEMETRY_TEST_EXPIRED": { "expires_in_version": "4.0a1", "kind": "flag", diff --git a/toolkit/modules/Promise-backend.js b/toolkit/modules/Promise-backend.js index 2390b01e650b..e8814963153f 100644 --- a/toolkit/modules/Promise-backend.js +++ b/toolkit/modules/Promise-backend.js @@ -37,6 +37,9 @@ const STATUS_REJECTED = 2; const salt = Math.floor(Math.random() * 100); const N_INTERNALS = "{private:internals:" + salt + "}"; +const JS_HAS_SYMBOLS = typeof Symbol === "function"; +const ITERATOR_SYMBOL = JS_HAS_SYMBOLS ? Symbol.iterator : "@@iterator"; + /////// Warn-upon-finalization mechanism // // One of the difficult problems with promises is locating uncaught @@ -511,7 +514,7 @@ Promise.reject = function (aReason) */ Promise.all = function (aValues) { - if (aValues == null || typeof(aValues["@@iterator"]) != "function") { + if (aValues == null || typeof(aValues[ITERATOR_SYMBOL]) != "function") { throw new Error("Promise.all() expects an iterable."); } @@ -562,7 +565,7 @@ Promise.all = function (aValues) */ Promise.race = function (aValues) { - if (aValues == null || typeof(aValues["@@iterator"]) != "function") { + if (aValues == null || typeof(aValues[ITERATOR_SYMBOL]) != "function") { throw new Error("Promise.race() expects an iterable."); }