diff --git a/.eslintignore b/.eslintignore index 86e31991e58..545843dd9ae 100644 --- a/.eslintignore +++ b/.eslintignore @@ -39,7 +39,6 @@ parser/** probes/** python/** rdf/** -services/** startupcache/** testing/** tools/** @@ -170,6 +169,11 @@ mobile/android/components/Snippets.js # Bug 1178739: Ignore this file as a quick fix for "Illegal yield expression" mobile/android/modules/HomeProvider.jsm +# services/ exclusions + +# Uses `#filter substitution` +services/sync/modules/constants.js + # toolkit/ exclusions # Not part of the default build diff --git a/accessible/xpcom/xpcAccessibleGeneric.h b/accessible/xpcom/xpcAccessibleGeneric.h index 1b7c6c40270..8f6d9878746 100644 --- a/accessible/xpcom/xpcAccessibleGeneric.h +++ b/accessible/xpcom/xpcAccessibleGeneric.h @@ -39,7 +39,7 @@ public: } xpcAccessibleGeneric(ProxyAccessible* aProxy, uint32_t aInterfaces) : - mIntl(aProxy) + mIntl(aProxy), mSupportedIfaces(0) { if (aInterfaces & Interfaces::SELECTION) { mSupportedIfaces |= eSelectable; diff --git a/b2g/components/PersistentDataBlock.jsm b/b2g/components/PersistentDataBlock.jsm index 72085a9b568..a26efe56d82 100644 --- a/b2g/components/PersistentDataBlock.jsm +++ b/b2g/components/PersistentDataBlock.jsm @@ -66,7 +66,7 @@ function toHexString(data) { let hexString = ""; if (typeof data === "string") { hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join(""); - } else if (typeof data === "array") { + } else if (data instanceof Array) { hexString = data.map(toHexChar).join(""); } return hexString; diff --git a/b2g/components/test/unit/file_persistentdatablock.js b/b2g/components/test/unit/file_persistentdatablock.js index 691cead2ae6..6ce3072ac3a 100644 --- a/b2g/components/test/unit/file_persistentdatablock.js +++ b/b2g/components/test/unit/file_persistentdatablock.js @@ -33,7 +33,7 @@ function toHexString(data) { let hexString = ""; if (typeof data === "string") { hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join(""); - } else if (typeof data === "array") { + } else if (data instanceof Array) { hexString = data.map(toHexChar).join(""); } return hexString; diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 4633a631b9d..4b632ffd65b 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -543,3 +543,5 @@ skip-if = !e10s || !crashreporter skip-if = !e10s || !crashreporter [browser_aboutTabCrashed_showForm.js] skip-if = !e10s || !crashreporter +[browser_aboutTabCrashed_withoutDump.js] +skip-if = !e10s diff --git a/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js b/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js new file mode 100644 index 00000000000..4a41b16d4d2 --- /dev/null +++ b/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js @@ -0,0 +1,44 @@ +"use strict"; + +const PAGE = "data:text/html,A%20regular,%20everyday,%20normal%20page."; + +/** + * Monkey patches TabCrashHandler.getDumpID to return null in order to test + * about:tabcrashed when a dump is not available. + */ +add_task(function* setup() { + let originalGetDumpID = TabCrashHandler.getDumpID; + TabCrashHandler.getDumpID = function(browser) { return null; }; + registerCleanupFunction(() => { + TabCrashHandler.getDumpID = originalGetDumpID; + }); +}); + +/** + * Tests tab crash page when a dump is not available. + */ +add_task(function* test_without_dump() { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + let tab = gBrowser.getTabForBrowser(browser); + yield BrowserTestUtils.crashBrowser(browser); + + let tabRemovedPromise = BrowserTestUtils.removeTab(tab, { dontRemove: true }); + + yield ContentTask.spawn(browser, null, function*() { + let doc = content.document; + ok(!doc.documentElement.classList.contains("crashDumpAvailable"), + "doesn't have crash dump"); + + let container = doc.getElementById("crash-reporter-container"); + ok(container, "has crash-reporter-container"); + ok(container.hidden, "crash-reporter-container is hidden"); + + doc.getElementById("closeTab").click(); + }); + + yield tabRemovedPromise; + }); +}); diff --git a/browser/extensions/shumway/content/ShumwayStreamConverter.jsm b/browser/extensions/shumway/content/ShumwayStreamConverter.jsm index f0627d61179..53c6d99f411 100644 --- a/browser/extensions/shumway/content/ShumwayStreamConverter.jsm +++ b/browser/extensions/shumway/content/ShumwayStreamConverter.jsm @@ -32,6 +32,7 @@ const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://gre/modules/NetUtil.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils', 'resource://gre/modules/PrivateBrowsingUtils.jsm'); @@ -304,17 +305,10 @@ ShumwayStreamConverterBase.prototype = { // Create a new channel that loads the viewer as a chrome resource. var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html'; - // TODO use only newChannel2 after FF37 is released. - var channel = Services.io.newChannel2 ? - Services.io.newChannel2(viewerUrl, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER) : - Services.io.newChannel(viewerUrl, null, null); + var channel = NetUtil.newChannel({ + uri: viewerUrl, + loadUsingSystemPrincipal: true + }); var converter = this; var listener = this.listener; @@ -370,7 +364,7 @@ ShumwayStreamConverterBase.prototype = { .getService(Ci.nsIScriptSecurityManager); var resourcePrincipal = securityManager.getSystemPrincipal(); aRequest.owner = resourcePrincipal; - channel.asyncOpen(proxy, aContext); + channel.asyncOpen2(proxy); }, // nsIRequestObserver::onStopRequest diff --git a/browser/modules/ContentCrashHandlers.jsm b/browser/modules/ContentCrashHandlers.jsm index 336dc8ff2c4..4ebef698e5b 100644 --- a/browser/modules/ContentCrashHandlers.jsm +++ b/browser/modules/ContentCrashHandlers.jsm @@ -228,7 +228,7 @@ this.TabCrashHandler = { let dumpID = this.getDumpID(browser); if (!dumpID) { - message.target.sendAsyncMessge("SetCrashReportAvailable", { + message.target.sendAsyncMessage("SetCrashReportAvailable", { hasReport: false, }); return; diff --git a/config/config.mk b/config/config.mk index 648b8c455b7..b5cd9c6f6e2 100644 --- a/config/config.mk +++ b/config/config.mk @@ -189,19 +189,12 @@ OS_LDFLAGS += $(_DEBUG_LDFLAGS) # XXX: What does this? Bug 482434 filed for better explanation. ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_) -ifdef MOZ_DEBUG -ifneq (,$(MOZ_BROWSE_INFO)$(MOZ_BSCFILE)) -OS_CFLAGS += -FR -OS_CXXFLAGS += -FR -endif -else # ! MOZ_DEBUG +ifndef MOZ_DEBUG # MOZ_DEBUG_SYMBOLS generates debug symbols in separate PDB files. # Used for generating an optimized build with debugging symbols. # Used in the Windows nightlies to generate symbols for crash reporting. ifdef MOZ_DEBUG_SYMBOLS -OS_CXXFLAGS += -UDEBUG -DNDEBUG -OS_CFLAGS += -UDEBUG -DNDEBUG ifdef HAVE_64BIT_BUILD OS_LDFLAGS += -DEBUG -OPT:REF,ICF else @@ -211,10 +204,8 @@ endif # # Handle DMD in optimized builds. -# No opt to give sane callstacks. # ifdef MOZ_DMD -MOZ_OPTIMIZE_FLAGS=-Zi -Od -UDEBUG -DNDEBUG ifdef HAVE_64BIT_BUILD OS_LDFLAGS = -DEBUG -OPT:REF,ICF else diff --git a/configure.in b/configure.in index 41b69e4e1f7..7c1c999be69 100644 --- a/configure.in +++ b/configure.in @@ -8748,7 +8748,6 @@ if test -n "$MOZ_TELEMETRY_REPORTING" || test -n "$MOZ_SERVICES_HEALTHREPORT" || fi dnl win32 options -AC_SUBST(MOZ_BROWSE_INFO) AC_SUBST(WIN32_REDIST_DIR) AC_SUBST(MAKENSISU) diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js index 0da0bfd1bf1..cdb26a42db7 100644 --- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -294,6 +294,10 @@ pref("devtools.webconsole.persistlog", false); // any timestamps. pref("devtools.webconsole.timestampMessages", false); +// Web Console automatic multiline mode: |true| if you want incomplete statements +// to automatically trigger multiline editing (equivalent to shift + enter). +pref("devtools.webconsole.autoMultiline", true); + // The number of lines that are displayed in the web console for the Net, // CSS, JS and Web Developer categories. These defaults should be kept in sync // with DEFAULT_LOG_LIMIT in the webconsole frontend. diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini index 2f0d875f8af..ccef9fd6f7e 100644 --- a/devtools/client/webconsole/test/browser.ini +++ b/devtools/client/webconsole/test/browser.ini @@ -329,6 +329,7 @@ skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout) [browser_webconsole_live_filtering_of_message_types.js] [browser_webconsole_live_filtering_on_search_strings.js] [browser_webconsole_message_node_id.js] +[browser_webconsole_multiline_input.js] [browser_webconsole_netlogging.js] [browser_webconsole_netlogging_basic.js] [browser_webconsole_netlogging_panel.js] diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js index f790d188ef6..38ce77c6cfc 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js @@ -7,9 +7,13 @@ const TEST_URI = "data:text/html;charset=utf-8,

bug 585991 - autocomplete " + "popup keyboard usage test"; + +// We should turn off auto-multiline editing during these tests +const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline"; var HUD, popup, jsterm, inputNode, completeNode; add_task(function*() { + Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false); yield loadTab(TEST_URI); let hud = yield openConsole(); @@ -23,6 +27,7 @@ add_task(function*() { yield popupHideAfterCompletionInText(); HUD = popup = jsterm = inputNode = completeNode = null; + Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true); }); var consoleOpened = Task.async(function*(aHud) { diff --git a/devtools/client/webconsole/test/browser_webconsole_multiline_input.js b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js new file mode 100644 index 00000000000..f3164590e63 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js @@ -0,0 +1,70 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests that the console waits for more input instead of evaluating +// when valid, but incomplete, statements are present upon pressing enter +// -or- when the user ends a line with shift + enter. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +let SHOULD_ENTER_MULTILINE = [ + {input: "function foo() {" }, + {input: "var a = 1," }, + {input: "var a = 1;", shiftKey: true }, + {input: "function foo() { }", shiftKey: true }, + {input: "function" }, + {input: "(x) =>" }, + {input: "let b = {" }, + {input: "let a = [" }, + {input: "{" }, + {input: "{ bob: 3343," }, + {input: "function x(y=" }, + {input: "Array.from(" }, + // shift + enter creates a new line despite parse errors + {input: "{2,}", shiftKey: true }, +]; +let SHOULD_EXECUTE = [ + {input: "function foo() { }" }, + {input: "var a = 1;" }, + {input: "function foo() { var a = 1; }" }, + {input: '"asdf"' }, + {input: '99 + 3' }, + {input: '1, 2, 3' }, + // errors + {input: 'function f(x) { let y = 1, }' }, + {input: 'function f(x=,) {' }, + {input: '{2,}' }, +]; + +add_task(function* () { + let { tab, browser } = yield loadTab(TEST_URI); + let hud = yield openConsole(); + let inputNode = hud.jsterm.inputNode; + + for (let test of SHOULD_ENTER_MULTILINE) { + hud.jsterm.setInputValue(test.input); + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey }); + let inputValue = hud.jsterm.getInputValue(); + is(inputNode.selectionStart, inputNode.selectionEnd, + "selection is collapsed"); + is(inputNode.selectionStart, inputValue.length, + "caret at end of multiline input"); + let inputWithNewline = test.input + "\n"; + is(inputValue, inputWithNewline, "Input value is correct"); + } + + for (let test of SHOULD_EXECUTE) { + hud.jsterm.setInputValue(test.input); + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey }); + let inputValue = hud.jsterm.getInputValue(); + is(inputNode.selectionStart, 0, "selection starts/ends at 0"); + is(inputNode.selectionEnd, 0, "selection starts/ends at 0"); + is(inputValue, "", "Input value is cleared"); + } + +}); diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index 3b12a9eaede..3626684ce3e 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -11,6 +11,7 @@ const {Cc, Ci, Cu} = require("chrome"); const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} = require("devtools/shared/webconsole/utils"); const promise = require("promise"); +const Debugger = require("Debugger"); loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", @@ -198,6 +199,7 @@ const MIN_FONT_SIZE = 10; const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout"; const PREF_PERSISTLOG = "devtools.webconsole.persistlog"; const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages"; +const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline"; const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount"; /** @@ -3880,11 +3882,13 @@ JSTerm.prototype = { break; } return; - } else if (event.shiftKey && - event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) { - // shift return - // TODO: expand the inputNode height by one line - return; + } else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) { + let autoMultiline = Services.prefs.getBoolPref(PREF_AUTO_MULTILINE); + if (event.shiftKey || + (!Debugger.isCompilableUnit(inputNode.value) && autoMultiline)) { + // shift return or incomplete statement + return; + } } switch (event.keyCode) { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index fc8826eefc3..2a3f759b50f 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -2272,7 +2272,7 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, // Refresh the principal on the compartment. if (nsPIDOMWindowInner* win = GetInnerWindow()) { - win->RefreshCompartmentPrincipal(); + nsGlobalWindow::Cast(win)->RefreshCompartmentPrincipal(); } } @@ -13413,5 +13413,5 @@ nsIDocument::ReportHasScrollLinkedEffect() nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Async Pan/Zoom"), this, nsContentUtils::eLAYOUT_PROPERTIES, - "ScrollLinkedEffectFound"); + "ScrollLinkedEffectFound2"); } diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index edfa2e9919f..57e3aa2db2e 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -480,7 +480,7 @@ public: bool DispatchResizeEvent(const mozilla::CSSIntSize& aSize); // Inner windows only. - virtual void RefreshCompartmentPrincipal() override; + void RefreshCompartmentPrincipal(); // For accessing protected field mFullScreen friend class FullscreenTransitionTask; @@ -1427,7 +1427,7 @@ public: // |interval| is in milliseconds. nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, int32_t interval, - bool aIsInterval, int32_t* aReturn) override; + bool aIsInterval, int32_t* aReturn); int32_t SetTimeoutOrInterval(JSContext* aCx, mozilla::dom::Function& aFunction, int32_t aTimeout, @@ -1437,13 +1437,7 @@ public: int32_t aTimeout, bool aIsInterval, mozilla::ErrorResult& aError); void ClearTimeoutOrInterval(int32_t aTimerID, - mozilla::ErrorResult& aError); - nsresult ClearTimeoutOrInterval(int32_t aTimerID) override - { - mozilla::ErrorResult rv; - ClearTimeoutOrInterval(aTimerID, rv); - return rv.StealNSResult(); - } + mozilla::ErrorResult& aError); // JS specific timeout functions (JS args grabbed from context). nsresult ResetTimersForNonBackgroundWindow(); diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index c76852e5a65..77bbac8b93e 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -151,32 +151,6 @@ public: return mParentTarget; } - bool HasMutationListeners(uint32_t aMutationEventType) const - { - MOZ_ASSERT(IsInnerWindow()); - - if (!mOuterWindow) { - NS_ERROR("HasMutationListeners() called on orphan inner window!"); - - return false; - } - - return (mMutationBits & aMutationEventType) != 0; - } - - void SetMutationListeners(uint32_t aType) - { - MOZ_ASSERT(IsInnerWindow()); - - if (!mOuterWindow) { - NS_ERROR("HasMutationListeners() called on orphan inner window!"); - - return; - } - - mMutationBits |= aType; - } - virtual void MaybeUpdateTouchState() {} nsIDocument* GetExtantDoc() const @@ -242,14 +216,6 @@ public: virtual bool IsFrozen() const = 0; - // Add a timeout to this window. - virtual nsresult SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, - int32_t interval, - bool aIsInterval, int32_t *aReturn) = 0; - - // Clear a timeout from this window. - virtual nsresult ClearTimeoutOrInterval(int32_t aTimerID) = 0; - nsPIDOMWindowOuter* GetOuterWindow() { return mIsInnerWindow ? mOuterWindow.get() : AsOuter(); @@ -383,43 +349,6 @@ public: */ virtual void FinishFullscreenChange(bool aIsFullscreen) = 0; - /** - * Call this to check whether some node (this window, its document, - * or content in that document) has a mouseenter/leave event listener. - */ - bool HasMouseEnterLeaveEventListeners() - { - return mMayHaveMouseEnterLeaveEventListener; - } - - /** - * Call this to indicate that some node (this window, its document, - * or content in that document) has a mouseenter/leave event listener. - */ - void SetHasMouseEnterLeaveEventListeners() - { - mMayHaveMouseEnterLeaveEventListener = true; - } - - /** - * Call this to check whether some node (this window, its document, - * or content in that document) has a Pointerenter/leave event listener. - */ - bool HasPointerEnterLeaveEventListeners() - { - return mMayHavePointerEnterLeaveEventListener; - } - - /** - * Call this to indicate that some node (this window, its document, - * or content in that document) has a Pointerenter/leave event listener. - */ - void SetHasPointerEnterLeaveEventListeners() - { - mMayHavePointerEnterLeaveEventListener = true; - } - - virtual JSObject* GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey) = 0; virtual void CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey, JS::Handle aHandler) = 0; @@ -578,14 +507,6 @@ public: */ virtual bool DispatchCustomEvent(const nsAString& aEventName) = 0; - /** - * Call when the document principal may have changed and the compartment - * principal needs to be updated. - * - * Inner windows only. - */ - virtual void RefreshCompartmentPrincipal() = 0; - /** * Like nsIDOMWindow::Open, except that we don't navigate to the given URL. * @@ -798,6 +719,65 @@ public: nsPerformance* GetPerformance(); + bool HasMutationListeners(uint32_t aMutationEventType) const + { + if (!mOuterWindow) { + NS_ERROR("HasMutationListeners() called on orphan inner window!"); + + return false; + } + + return (mMutationBits & aMutationEventType) != 0; + } + + void SetMutationListeners(uint32_t aType) + { + if (!mOuterWindow) { + NS_ERROR("HasMutationListeners() called on orphan inner window!"); + + return; + } + + mMutationBits |= aType; + } + + /** + * Call this to check whether some node (this window, its document, + * or content in that document) has a mouseenter/leave event listener. + */ + bool HasMouseEnterLeaveEventListeners() + { + return mMayHaveMouseEnterLeaveEventListener; + } + + /** + * Call this to indicate that some node (this window, its document, + * or content in that document) has a mouseenter/leave event listener. + */ + void SetHasMouseEnterLeaveEventListeners() + { + mMayHaveMouseEnterLeaveEventListener = true; + } + + /** + * Call this to check whether some node (this window, its document, + * or content in that document) has a Pointerenter/leave event listener. + */ + bool HasPointerEnterLeaveEventListeners() + { + return mMayHavePointerEnterLeaveEventListener; + } + + /** + * Call this to indicate that some node (this window, its document, + * or content in that document) has a Pointerenter/leave event listener. + */ + void SetHasPointerEnterLeaveEventListeners() + { + mMayHavePointerEnterLeaveEventListener = true; + } + + protected: void CreatePerformanceObjectIfNeeded(); }; diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index 12e14d3b692..ed3bb84f76c 100644 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -511,7 +511,7 @@ nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent, } bool -nsXHTMLContentSerializer::CheckElementEnd(Element* aElement, +nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElement, bool& aForceFormat, nsAString& aStr) { diff --git a/dom/bindings/GenerateCSS2PropertiesWebIDL.py b/dom/bindings/GenerateCSS2PropertiesWebIDL.py index 5cf358e73b3..f92b24b45b6 100644 --- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py +++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py @@ -5,20 +5,34 @@ import sys import string +# Generates a line of WebIDL with the given spelling of the property name +# (whether camelCase, _underscorePrefixed, etc.) and the given array of +# extended attributes. +def generateLine(propName, extendedAttrs): + return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs), + propName) propList = eval(sys.stdin.read()) props = "" for [name, prop, id, flags, pref, proptype] in propList: if "CSS_PROPERTY_INTERNAL" in flags: continue + # Unfortunately, even some of the getters here are fallible + # (e.g. on nsComputedDOMStyle). extendedAttrs = ["Throws", "TreatNullAs=EmptyString"] if pref is not "": extendedAttrs.append('Pref="%s"' % pref) + + # webkit properties get a capitalized "WebkitFoo" accessor (added here) + # as well as a camelcase "webkitFoo" accessor (added next). + if (prop.startswith("Webkit")): + props += generateLine(prop, extendedAttrs) + + # Generate a line with camelCase spelling of property-name (or capitalized, + # for Moz-prefixed properties): if not prop.startswith("Moz"): prop = prop[0].lower() + prop[1:] - # Unfortunately, even some of the getters here are fallible - # (e.g. on nsComputedDOMStyle). - props += " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs), - prop) + props += generateLine(prop, extendedAttrs) + # Per spec, what's actually supposed to happen here is that we're supposed # to have properties for: # @@ -30,18 +44,17 @@ for [name, prop, id, flags, pref, proptype] in propList: # Note that "float" will cause a property called "float" to exist due to (1) # in that list. # - # In practice, cssFloat is the only case in which "name" doesn't contain "-" - # but also doesn't match "prop". So the stuff we did with "prop" covers (3) - # and all of (1) except "float". If we now output attributes for all the - # cases where "name" doesn't match "prop" and "name" doesn't start with "-", - # that will cover "float" and (2). + # In practice, cssFloat is the only case in which "name" doesn't contain + # "-" but also doesn't match "prop". So the above generatePropLine() call + # covered (3) and all of (1) except "float". If we now output attributes + # for all the cases where "name" doesn't match "prop" and "name" doesn't + # start with "-", that will cover "float" and (2). if prop != name and name[0] != "-": extendedAttrs.append('BinaryName="%s"' % prop) # Throw in a '_' before the attribute name, because some of these # property names collide with IDL reserved words. - props += " [%s] attribute DOMString _%s;\n" % ( - ", ".join(extendedAttrs), - name) + props += generateLine("_" + name, extendedAttrs) + idlFile = open(sys.argv[1], "r") idlTemplate = idlFile.read() diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 2d1989def2d..5bb46165e19 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -1304,8 +1304,7 @@ WebGLContext::ClearScreen() const bool changeDrawBuffers = (mDefaultFB_DrawBuffer0 != LOCAL_GL_BACK); if (changeDrawBuffers) { - const GLenum back = LOCAL_GL_BACK; - gl->fDrawBuffers(1, &back); + gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK); } GLbitfield bufferBits = LOCAL_GL_COLOR_BUFFER_BIT; @@ -1317,7 +1316,7 @@ WebGLContext::ClearScreen() ForceClearFramebufferWithDefaultValues(bufferBits, mNeedsFakeNoAlpha); if (changeDrawBuffers) { - gl->fDrawBuffers(1, &mDefaultFB_DrawBuffer0); + gl->Screen()->SetDrawBuffer(mDefaultFB_DrawBuffer0); } } diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index a7eeba956f2..dafbe702306 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -827,7 +827,8 @@ protected: public: void Disable(GLenum cap); void Enable(GLenum cap); - bool GetStencilBits(GLint* out_stencilBits); + bool GetStencilBits(GLint* const out_stencilBits); + bool GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val); virtual JS::Value GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv); void GetParameter(JSContext* cx, GLenum pname, diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index ef3716142ae..34543116fea 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -994,6 +994,10 @@ WebGLContext::Hint(GLenum target, GLenum mode) switch (target) { case LOCAL_GL_GENERATE_MIPMAP_HINT: + // Deprecated and removed in desktop GL Core profiles. + if (gl->IsCoreProfile()) + return; + isValid = true; break; diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp index 8f3563a0dcf..ea818e4004f 100644 --- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -73,7 +73,7 @@ StringValue(JSContext* cx, const nsAString& str, ErrorResult& rv) } bool -WebGLContext::GetStencilBits(GLint* out_stencilBits) +WebGLContext::GetStencilBits(GLint* const out_stencilBits) { *out_stencilBits = 0; if (mBoundDrawFramebuffer) { @@ -97,9 +97,107 @@ WebGLContext::GetStencilBits(GLint* out_stencilBits) return true; } +bool +WebGLContext::GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val) +{ + if (mBoundDrawFramebuffer) { + if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) + return false; + } + + if (!mBoundDrawFramebuffer) { + switch (pname) { + case LOCAL_GL_RED_BITS: + case LOCAL_GL_GREEN_BITS: + case LOCAL_GL_BLUE_BITS: + *out_val = 8; + break; + + case LOCAL_GL_ALPHA_BITS: + *out_val = (mOptions.alpha ? 8 : 0); + break; + + case LOCAL_GL_DEPTH_BITS: + if (mOptions.depth) { + const auto& glFormats = gl->GetGLFormats(); + + GLenum depthFormat = glFormats.depth; + if (mOptions.stencil && glFormats.depthStencil) { + depthFormat = glFormats.depthStencil; + } + + if (depthFormat == LOCAL_GL_DEPTH_COMPONENT16) { + *out_val = 16; + } else { + *out_val = 24; + } + } else { + *out_val = 0; + } + break; + + case LOCAL_GL_STENCIL_BITS: + *out_val = (mOptions.stencil ? 8 : 0); + break; + + default: + MOZ_CRASH("bad pname"); + } + return true; + } + + if (!gl->IsCoreProfile()) { + gl->fGetIntegerv(pname, out_val); + return true; + } + + GLenum fbAttachment = 0; + GLenum fbPName = 0; + switch (pname) { + case LOCAL_GL_RED_BITS: + fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE; + break; + + case LOCAL_GL_GREEN_BITS: + fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE; + break; + + case LOCAL_GL_BLUE_BITS: + fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE; + break; + + case LOCAL_GL_ALPHA_BITS: + fbAttachment = LOCAL_GL_COLOR_ATTACHMENT0; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE; + break; + + case LOCAL_GL_DEPTH_BITS: + fbAttachment = LOCAL_GL_DEPTH_ATTACHMENT; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE; + break; + + case LOCAL_GL_STENCIL_BITS: + fbAttachment = LOCAL_GL_STENCIL_ATTACHMENT; + fbPName = LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE; + break; + + default: + MOZ_CRASH("bad pname"); + } + + gl->fGetFramebufferAttachmentParameteriv(LOCAL_GL_DRAW_FRAMEBUFFER, fbAttachment, + fbPName, out_val); + return true; +} + JS::Value WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) { + const char funcName[] = "getParameter"; + if (IsContextLost()) return JS::NullValue(); @@ -356,11 +454,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::Int32Value(refValue & stencilMask); } - case LOCAL_GL_STENCIL_BITS: { - GLint stencilBits = 0; - GetStencilBits(&stencilBits); - return JS::Int32Value(stencilBits); - } + case LOCAL_GL_STENCIL_CLEAR_VALUE: case LOCAL_GL_UNPACK_ALIGNMENT: case LOCAL_GL_PACK_ALIGNMENT: @@ -370,28 +464,26 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) case LOCAL_GL_MAX_VERTEX_ATTRIBS: case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: - case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: - case LOCAL_GL_RED_BITS: - case LOCAL_GL_GREEN_BITS: - case LOCAL_GL_BLUE_BITS: { + case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(i); } - case LOCAL_GL_DEPTH_BITS: { - GLint i = 0; - if (!mNeedsFakeNoDepth) { - gl->fGetIntegerv(pname, &i); - } - return JS::Int32Value(i); - } - case LOCAL_GL_ALPHA_BITS: { - GLint i = 0; - if (!mNeedsFakeNoAlpha) { - gl->fGetIntegerv(pname, &i); - } - return JS::Int32Value(i); + + case LOCAL_GL_RED_BITS: + case LOCAL_GL_GREEN_BITS: + case LOCAL_GL_BLUE_BITS: + case LOCAL_GL_ALPHA_BITS: + case LOCAL_GL_DEPTH_BITS: + case LOCAL_GL_STENCIL_BITS: { + // Deprecated and removed in GL Core profiles, so special handling required. + GLint val; + if (!GetChannelBits(funcName, pname, &val)) + return JS::NullValue(); + + return JS::Int32Value(val); } + case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(mImplMaxTextureSize); diff --git a/dom/canvas/WebGLExtensionTextureFloat.cpp b/dom/canvas/WebGLExtensionTextureFloat.cpp index a7a58939ad1..4568e994ee9 100644 --- a/dom/canvas/WebGLExtensionTextureFloat.cpp +++ b/dom/canvas/WebGLExtensionTextureFloat.cpp @@ -32,15 +32,17 @@ WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) fua->AllowUnsizedTexFormat(pi, usage); }; - const bool needSizedInternal = !gl->IsGLES(); - MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle)); + const bool needsSwizzle = gl->IsCoreProfile(); + MOZ_ASSERT_IF(needsSwizzle, gl->IsSupported(gl::GLFeature::texture_swizzle)); + + const bool needsSizedFormat = !gl->IsGLES(); //////////////// pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT}; dui = {pi.format, pi.format, pi.type}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSizedFormat) { dui.internalFormat = LOCAL_GL_RGBA32F; } fnAdd(webgl::EffectiveFormat::RGBA32F); @@ -50,7 +52,7 @@ WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) pi = {LOCAL_GL_RGB, LOCAL_GL_FLOAT}; dui = {pi.format, pi.format, pi.type}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSizedFormat) { dui.internalFormat = LOCAL_GL_RGB32F; } fnAdd(webgl::EffectiveFormat::RGB32F); @@ -60,9 +62,11 @@ WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT}; dui = {pi.format, pi.format, pi.type}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT}; swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_LUMINANCE32F_ARB; } fnAdd(webgl::EffectiveFormat::Luminance32F); @@ -71,9 +75,11 @@ WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) pi = {LOCAL_GL_ALPHA, LOCAL_GL_FLOAT}; dui = {pi.format, pi.format, pi.type}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT}; swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_ALPHA32F_ARB; } fnAdd(webgl::EffectiveFormat::Alpha32F); @@ -82,9 +88,11 @@ WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT}; dui = {pi.format, pi.format, pi.type}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, LOCAL_GL_FLOAT}; swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_LUMINANCE_ALPHA32F_ARB; } fnAdd(webgl::EffectiveFormat::Luminance32FAlpha32F); } @@ -101,10 +109,9 @@ WebGLExtensionTextureFloat::IsSupported(const WebGLContext* webgl) if (!gl->IsSupported(gl::GLFeature::texture_float)) return false; - const bool needSizedInternal = !gl->IsGLES(); + const bool needsSwizzle = gl->IsCoreProfile(); const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle); - - if (needSizedInternal && !hasSwizzle) + if (needsSwizzle && !hasSwizzle) return false; return true; diff --git a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp index a5770999d67..e59f9b232ee 100644 --- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp +++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp @@ -30,8 +30,10 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web fua->AllowUnsizedTexFormat(pi, usage); }; - const bool needSizedInternal = !gl->IsGLES(); - MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle)); + const bool needsSwizzle = gl->IsCoreProfile(); + MOZ_ASSERT_IF(needsSwizzle, gl->IsSupported(gl::GLFeature::texture_swizzle)); + + const bool needsSizedFormat = !gl->IsGLES(); GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT; if (!gl->IsSupported(gl::GLFeature::texture_half_float)) { @@ -44,7 +46,7 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web pi = {LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES}; dui = {pi.format, pi.format, driverUnpackType}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSizedFormat) { dui.internalFormat = LOCAL_GL_RGBA16F; } fnAdd(webgl::EffectiveFormat::RGBA16F); @@ -54,7 +56,7 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web pi = {LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT_OES}; dui = {pi.format, pi.format, driverUnpackType}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSizedFormat) { dui.internalFormat = LOCAL_GL_RGB16F; } fnAdd(webgl::EffectiveFormat::RGB16F); @@ -64,9 +66,11 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_HALF_FLOAT_OES}; dui = {pi.format, pi.format, driverUnpackType}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType}; swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_LUMINANCE16F_ARB; } fnAdd(webgl::EffectiveFormat::Luminance16F); @@ -75,9 +79,11 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web pi = {LOCAL_GL_ALPHA, LOCAL_GL_HALF_FLOAT_OES}; dui = {pi.format, pi.format, driverUnpackType}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType}; swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_ALPHA16F_ARB; } fnAdd(webgl::EffectiveFormat::Alpha16F); @@ -86,9 +92,11 @@ WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* web pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES}; dui = {pi.format, pi.format, driverUnpackType}; swizzle = nullptr; - if (needSizedInternal) { + if (needsSwizzle) { dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, driverUnpackType}; swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA; + } else if (needsSizedFormat) { + dui.internalFormat = LOCAL_GL_LUMINANCE_ALPHA16F_ARB; } fnAdd(webgl::EffectiveFormat::Luminance16FAlpha16F); } @@ -108,10 +116,9 @@ WebGLExtensionTextureHalfFloat::IsSupported(const WebGLContext* webgl) return false; } - const bool needSizedInternal = !gl->IsGLES(); + const bool needsSwizzle = gl->IsCoreProfile(); const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle); - - if (needSizedInternal && !hasSwizzle) + if (needsSwizzle && !hasSwizzle) return false; return true; diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index ffa942025ce..a157e9d91c8 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -7,7 +7,7 @@ TEST_DIRS += ['compiledtest'] # Number changes to this file to avoid bug 1081323 (clobber after changing a manifest): -# 3 +# 5 MOCHITEST_MANIFESTS += [ 'test/crossorigin/mochitest.ini', diff --git a/dom/canvas/test/webgl-mochitest.ini b/dom/canvas/test/webgl-mochitest.ini index d6cd17dd6b4..138ee94d696 100644 --- a/dom/canvas/test/webgl-mochitest.ini +++ b/dom/canvas/test/webgl-mochitest.ini @@ -3,10 +3,51 @@ subsuite = webgl skip-if = ((os == 'linux') && (buildapp == 'b2g')) support-files = + webgl-mochitest/ensure-exts/ensure-ext.js webgl-mochitest/driver-info.js webgl-mochitest/es3-data.js webgl-mochitest/webgl-util.js +[webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html] +fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6') +[webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html] +fail-if = (os == 'android') +[webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html] +fail-if = (os == 'android') +[webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win') +[webgl-mochitest/ensure-exts/test_EXT_frag_depth.html] +fail-if = (os == 'android') +[webgl-mochitest/ensure-exts/test_EXT_sRGB.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac' && os_version == '10.6') || (os == 'win') +[webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') +[webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html] +fail-if = (os == 'android') || (os == 'linux') +[webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html] +fail-if = (os == 'android') +[webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html] +fail-if = (os == 'android') +[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win') +[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win') +[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html] +# Win7 is 6.1 +fail-if = (os == 'linux') || (os == 'mac') || (os == 'win' && (os_version == '5.1' || os_version == '6.1')) +[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html] +fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win') +[webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html] +fail-if = (os == 'android') || (os == 'linux') +[webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html] +fail-if = (os == 'mac' && os_version == '10.6') +[webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html] +# Win7 is 6.1 +fail-if = (os == 'android') || (os == 'win' && (os_version == '5.1' || os_version == '6.1')) + +[webgl-mochitest/ensure-exts/test_common.html] + + [webgl-mochitest/test_backbuffer_channels.html] fail-if = (os == 'b2g') [webgl-mochitest/test_depth_readpixels.html] diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/ensure-ext.js b/dom/canvas/test/webgl-mochitest/ensure-exts/ensure-ext.js new file mode 100644 index 00000000000..798067d5e42 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/ensure-ext.js @@ -0,0 +1,32 @@ +'use strict'; + +function EnsureExt(name, shouldBe = true) { + var c = document.createElement('canvas'); + var gl = c.getContext('experimental-webgl'); + + if (shouldBe) { + ok(gl.getExtension(name), 'Should have extension ' + name + '.'); + } else { + ok(!gl.getExtension(name), 'Should not have extension ' + name + '.'); + } +} + +function EnsureDraftExt(name, shouldBe = true) { + SimpleTest.waitForExplicitFinish(); + + var fnEnsure = function() { + EnsureExt(name, shouldBe); + SimpleTest.finish(); + }; + + if ('SpecialPowers' in window) { + var prefStateList = [ + ['webgl.enable-draft-extensions', true], + ]; + var prefEnv = {'set': prefStateList}; + SpecialPowers.pushPrefEnv(prefEnv, fnEnsure); + } else { + console.log('Couldn\'t use SpecialPowers to enable draft extensions.'); + fnEnsure(); + } +} diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html new file mode 100644 index 00000000000..04b24d90c10 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_ANGLE_instanced_arrays.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html new file mode 100644 index 00000000000..903269e6b66 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_blend_minmax.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html new file mode 100644 index 00000000000..3ac21299c68 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_color_buffer_half_float.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html new file mode 100644 index 00000000000..e0b90adbbb5 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_disjoint_timer_query.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_frag_depth.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_frag_depth.html new file mode 100644 index 00000000000..3c7f3867917 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_frag_depth.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_sRGB.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_sRGB.html new file mode 100644 index 00000000000..da5aa0a3fbf --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_sRGB.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html new file mode 100644 index 00000000000..e64ff94848c --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_shader_texture_lod.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html new file mode 100644 index 00000000000..877c4440bf3 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_EXT_texture_filter_anisotropic.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html new file mode 100644 index 00000000000..3e7b7a39622 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_OES_standard_derivatives.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html new file mode 100644 index 00000000000..aeb8f271ea0 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_color_buffer_float.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html new file mode 100644 index 00000000000..6d68f0b4e2b --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_atc.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html new file mode 100644 index 00000000000..21c824ad854 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_es3.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html new file mode 100644 index 00000000000..327625c0184 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_etc1.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html new file mode 100644 index 00000000000..c9548139842 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_pvrtc.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html new file mode 100644 index 00000000000..6ad80011064 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_compressed_texture_s3tc.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html new file mode 100644 index 00000000000..957171c160e --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_depth_texture.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html new file mode 100644 index 00000000000..18774d0d68a --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_WEBGL_draw_buffers.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html new file mode 100644 index 00000000000..3b5907a8a88 --- /dev/null +++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html @@ -0,0 +1,75 @@ + + + + + + + + + + + diff --git a/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py b/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py index bac482c3d73..919ba232804 100644 --- a/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py +++ b/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py @@ -28,7 +28,8 @@ def ReadLocalFile(include): return data -kSimpleTestReplacement = '''\n +kSimpleTestReplacement = ''' +

-\n''' + +''' fin = open(mochiPath, 'rb') fout = open(testPath, 'wb') diff --git a/dom/devicestorage/test/mochitest.ini b/dom/devicestorage/test/mochitest.ini index cee91b441da..a2367739f9c 100644 --- a/dom/devicestorage/test/mochitest.ini +++ b/dom/devicestorage/test/mochitest.ini @@ -10,7 +10,6 @@ support-files = devicestorage_common.js [test_available.html] [test_basic.html] [test_dirs.html] -skip-if = e10s # Bug 1063569. # [test_diskSpace.html] # Possible race between the time we write a file, and the # time it takes to be reflected by statfs(). Bug # 791287 diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 7b1d1c25f7d..a9410f5d8cf 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -1017,7 +1017,7 @@ HTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL, do_QueryInterface(inst)); if (formSubmitObserver) { rv = formSubmitObserver->Notify(this, - window->GetCurrentInnerWindow(), + window ? window->GetCurrentInnerWindow() : nullptr, aActionURL, aCancelSubmit); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/locales/en-US/chrome/layout/layout_errors.properties b/dom/locales/en-US/chrome/layout/layout_errors.properties index b276ea27a7f..11acea9ebbb 100644 --- a/dom/locales/en-US/chrome/layout/layout_errors.properties +++ b/dom/locales/en-US/chrome/layout/layout_errors.properties @@ -9,4 +9,4 @@ ImageMapPolyWrongNumberOfCoords=The "coords" attribute of the tag is missing the last "y" coordinate (the correct format is "x1,y1,x2,y2 …"). TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect. -ScrollLinkedEffectFound=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developers.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features! +ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features! diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 20f42b1e0c9..a83e5e8059b 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -936,6 +936,7 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult) mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); MOZ_ASSERT(mVideoTracks.mDemuxer); info.mVideo = *mVideoTracks.mDemuxer->GetInfo()->GetAsVideoInfo(); + info.mVideo.mTrackId = 2; } uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); @@ -944,6 +945,7 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult) mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); MOZ_ASSERT(mAudioTracks.mDemuxer); info.mAudio = *mAudioTracks.mDemuxer->GetInfo()->GetAsAudioInfo(); + info.mAudio.mTrackId = 1; } int64_t videoDuration = numVideos ? info.mVideo.mDuration : 0; diff --git a/dom/permission/tests/test_permissions_api.html b/dom/permission/tests/test_permissions_api.html index c936b46e91a..5cced3597af 100644 --- a/dom/permission/tests/test_permissions_api.html +++ b/dom/permission/tests/test_permissions_api.html @@ -31,14 +31,6 @@ const UNSUPPORTED_PERMISSIONS = [ 'midi' ]; -function setup() { - return new Promise((resolve, reject) => { - SpecialPowers.pushPrefEnv({'set': [ - ['dom.permissions.enabled', true], - ]}, resolve); - }); -} - function setPermissions(action) { let permissions = PERMISSIONS.map(x => { return { 'type': x.perm, 'allow': action, 'context': document }; @@ -111,8 +103,7 @@ function testInvalidQuery() { } function runTests() { - setup() - .then(checkUnsupportedPermissions) + checkUnsupportedPermissions() .then(checkUserVisiblePushPermission) .then(() => setPermissions(UNKNOWN_ACTION)) .then(() => checkPermissions('prompt')) diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index aad18ebbe43..904c02b3d2e 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1850,7 +1850,8 @@ nsPluginHost::GetSpecialType(const nsACString & aMIMEType) } if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") || - aMIMEType.LowerCaseEqualsASCII("application/futuresplash")) { + aMIMEType.LowerCaseEqualsASCII("application/futuresplash") || + aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash-test")) { return eSpecialType_Flash; } diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 4ee3198bc4f..1db10e3fc66 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -673,6 +673,7 @@ PluginModuleParent::PluginModuleParent(bool aIsChrome, bool aAllowAsyncInit) , mNPPIface(nullptr) , mPlugin(nullptr) , mTaskFactory(this) + , mSandboxLevel(0) , mIsFlashPlugin(false) , mIsStartingAsync(false) , mNPInitialized(false) @@ -736,7 +737,6 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, , mHangUIParent(nullptr) , mHangUIEnabled(true) , mIsTimerReset(true) - , mSandboxLevel(aSandboxLevel) #ifdef MOZ_CRASHREPORTER , mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex") , mCrashReporter(nullptr) @@ -754,6 +754,7 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, { NS_ASSERTION(mSubprocess, "Out of memory!"); sInstantiated = true; + mSandboxLevel = aSandboxLevel; mRunID = GeckoChildProcessHost::GetUniqueID(); #ifdef MOZ_ENABLE_PROFILER_SPS @@ -2669,6 +2670,23 @@ PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance, if (mIsFlashPlugin) { parentInstance->InitMetadata(strPluginType, srcAttribute); +#ifdef XP_WIN + // Force windowless mode (bug 1201904) when sandbox level >= 2 + if (mSandboxLevel >= 2) { + NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode"); + NS_NAMED_LITERAL_CSTRING(opaqueAttributeValue, "opaque"); + auto wmodeAttributeIndex = + names.IndexOf(wmodeAttributeName, 0, comparator); + if (wmodeAttributeIndex != names.NoIndex) { + if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) { + values[wmodeAttributeIndex].Assign(opaqueAttributeValue); + } + } else { + names.AppendElement(wmodeAttributeName); + values.AppendElement(opaqueAttributeValue); + } + } +#endif } // Release the surrogate reference that was in pdata diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index d60def64662..f4784b6f1fc 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -325,6 +325,7 @@ protected: TimeDuration mTimeBlocked; nsCString mPluginName; nsCString mPluginVersion; + int32_t mSandboxLevel; bool mIsFlashPlugin; #ifdef MOZ_X11 @@ -548,7 +549,6 @@ private: PluginHangUIParent *mHangUIParent; bool mHangUIEnabled; bool mIsTimerReset; - int32_t mSandboxLevel; #ifdef MOZ_CRASHREPORTER /** * This mutex protects the crash reporter when the Plugin Hang UI event diff --git a/dom/plugins/test/mochitest/mochitest.ini b/dom/plugins/test/mochitest/mochitest.ini index dffa7d8f428..b4f6c49acc2 100644 --- a/dom/plugins/test/mochitest/mochitest.ini +++ b/dom/plugins/test/mochitest/mochitest.ini @@ -120,6 +120,8 @@ skip-if = toolkit != "cocoa" [test_twostreams.html] [test_windowed_invalidate.html] skip-if = os != "win" +[test_windowless_flash.html] +skip-if = !(os == "win" && processor == "x86_64") [test_windowless_ime.html] skip-if = os != "win" || e10s [test_visibility.html] diff --git a/dom/plugins/test/mochitest/test_windowless_flash.html b/dom/plugins/test/mochitest/test_windowless_flash.html new file mode 100644 index 00000000000..8274a78aeb7 --- /dev/null +++ b/dom/plugins/test/mochitest/test_windowless_flash.html @@ -0,0 +1,33 @@ + + + + + + + + + + +

+ +
+ + + +
+ + diff --git a/dom/storage/DOMStorageCache.cpp b/dom/storage/DOMStorageCache.cpp index c2f049569b2..4c84ac0dcbb 100644 --- a/dom/storage/DOMStorageCache.cpp +++ b/dom/storage/DOMStorageCache.cpp @@ -564,9 +564,16 @@ DOMStorageCache::Clear(const DOMStorage* aStorage) void DOMStorageCache::CloneFrom(const DOMStorageCache* aThat) { - mLoaded = aThat->mLoaded; + // This will never be called on anything else than SessionStorage. + // This means mData will never be touched on any other thread than + // the main thread and it never went through the loading process. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mPersistent); + MOZ_ASSERT(!(bool)aThat->mLoaded); + + mLoaded = false; mInitialized = aThat->mInitialized; - mPersistent = aThat->mPersistent; + mPersistent = false; mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive; for (uint32_t i = 0; i < kDataSetCount; ++i) { diff --git a/dom/storage/DOMStorageCache.h b/dom/storage/DOMStorageCache.h index 3ee3ce1e5c7..2bd4737f45b 100644 --- a/dom/storage/DOMStorageCache.h +++ b/dom/storage/DOMStorageCache.h @@ -15,6 +15,7 @@ #include "nsHashKeys.h" #include "mozilla/Monitor.h" #include "mozilla/Telemetry.h" +#include "mozilla/Atomics.h" #include "nsAutoPtr.h" namespace mozilla { @@ -220,8 +221,9 @@ private: // Flag that is initially false. When the cache is about to work with // the database (i.e. it is persistent) this flags is set to true after // all keys and coresponding values are loaded from the database. - // This flag never goes from true back to false. - bool mLoaded; + // This flag never goes from true back to false. Since this flag is + // critical for mData hashtable synchronization, it's made atomic. + Atomic mLoaded; // Result of load from the database. Valid after mLoaded flag has been set. nsresult mLoadResult; diff --git a/dom/system/gonk/SystemProperty.cpp b/dom/system/gonk/SystemProperty.cpp index 6fd1e86666c..1f874ce908b 100644 --- a/dom/system/gonk/SystemProperty.cpp +++ b/dom/system/gonk/SystemProperty.cpp @@ -5,8 +5,11 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SystemProperty.h" -#include +#include +#include + +#include "nsDebug.h" #include "prinit.h" namespace mozilla { diff --git a/dom/system/gonk/VolumeCommand.h b/dom/system/gonk/VolumeCommand.h index a2e7c69b82b..022965b5e02 100644 --- a/dom/system/gonk/VolumeCommand.h +++ b/dom/system/gonk/VolumeCommand.h @@ -48,13 +48,13 @@ public: // Response codes from the 200, 400, and 500 series all indicated that // the command has completed. - return (mResponseCode >= ResponseCode::CommandOkay) - && (mResponseCode < ResponseCode::UnsolicitedInformational); + return (mResponseCode >= ::ResponseCode::CommandOkay) + && (mResponseCode < ::ResponseCode::UnsolicitedInformational); } bool WasSuccessful() const { - return mResponseCode == ResponseCode::CommandOkay; + return mResponseCode == ::ResponseCode::CommandOkay; } bool IsPending() const { return mPending; } @@ -79,7 +79,7 @@ private: #else mResponseStr = aResponseStr; #endif - if (mResponseCode >= ResponseCode::CommandOkay) { + if (mResponseCode >= ::ResponseCode::CommandOkay) { // This is a final response. mPending = false; } diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index cb08be68149..74ee74909e5 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -100,7 +100,7 @@ interface NavigatorFeatures { }; partial interface Navigator { - [Throws, Pref="dom.permissions.enabled"] + [Throws] readonly attribute Permissions permissions; }; diff --git a/dom/webidl/PermissionStatus.webidl b/dom/webidl/PermissionStatus.webidl index 27ae490c391..3abfd66f407 100644 --- a/dom/webidl/PermissionStatus.webidl +++ b/dom/webidl/PermissionStatus.webidl @@ -13,8 +13,7 @@ enum PermissionState { "prompt" }; -[Exposed=(Window), - Pref="dom.permissions.enabled"] +[Exposed=(Window)] interface PermissionStatus : EventTarget { readonly attribute PermissionState state; attribute EventHandler onchange; diff --git a/dom/webidl/Permissions.webidl b/dom/webidl/Permissions.webidl index 776a22ff843..17574c75e59 100644 --- a/dom/webidl/Permissions.webidl +++ b/dom/webidl/Permissions.webidl @@ -22,8 +22,7 @@ dictionary PushPermissionDescriptor : PermissionDescriptor { boolean userVisible = false; }; -[Exposed=(Window), - Pref="dom.permissions.enabled"] +[Exposed=(Window)] interface Permissions { [Throws] Promise query(object permission); diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 7f4af258525..2a0d81ae540 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -1242,11 +1242,6 @@ public: swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER); - // "If registration's waiting worker's skip waiting flag is set" - if (mRegistration->mWaitingWorker->SkipWaitingFlag()) { - mRegistration->PurgeActiveWorker(); - } - Done(NS_OK); // Activate() is invoked out of band of atomic. mRegistration->TryToActivate(); diff --git a/editor/libeditor/nsEditor.cpp b/editor/libeditor/nsEditor.cpp index bc604c141df..ba0c1fa9913 100644 --- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -1480,7 +1480,7 @@ nsEditor::JoinNodes(nsINode& aLeftNode, nsINode& aRightNode) parent->AsDOMNode()); } - nsresult result; + nsresult result = NS_OK; RefPtr txn = CreateTxnForJoinNode(aLeftNode, aRightNode); if (txn) { result = DoTransaction(txn); diff --git a/editor/libeditor/nsHTMLEditor.cpp b/editor/libeditor/nsHTMLEditor.cpp index f635c16d8b2..ca5684a5aad 100644 --- a/editor/libeditor/nsHTMLEditor.cpp +++ b/editor/libeditor/nsHTMLEditor.cpp @@ -547,7 +547,7 @@ nsHTMLEditor::BeginningOfDocument() // Find first editable thingy bool done = false; nsCOMPtr curNode = rootElement.get(), selNode; - int32_t curOffset = 0, selOffset; + int32_t curOffset = 0, selOffset = 0; while (!done) { nsWSRunObject wsObj(this, curNode, curOffset); int32_t visOffset = 0; diff --git a/editor/txtsvc/nsTextServicesDocument.cpp b/editor/txtsvc/nsTextServicesDocument.cpp index 587b4ad9f9e..46a6eedf0ec 100644 --- a/editor/txtsvc/nsTextServicesDocument.cpp +++ b/editor/txtsvc/nsTextServicesDocument.cpp @@ -2754,7 +2754,7 @@ nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBloc nsCOMPtr startParent, endParent; int32_t startOffset, endOffset; int32_t rangeCount, tableCount, i; - int32_t e1s1, e1s2, e2s1, e2s2; + int32_t e1s1 = 0, e1s2 = 0, e2s1 = 0, e2s2 = 0; OffsetEntry *eStart, *eEnd; int32_t eStartOffset, eEndOffset; diff --git a/gfx/2d/BaseMargin.h b/gfx/2d/BaseMargin.h index 28bc648c5de..a4ef5fe2424 100644 --- a/gfx/2d/BaseMargin.h +++ b/gfx/2d/BaseMargin.h @@ -21,10 +21,10 @@ struct Sides final { mBits = aSideBits; } bool IsEmpty() const { return mBits == 0; } - bool Top() const { return mBits & eSideBitsTop; } - bool Right() const { return mBits & eSideBitsRight; } - bool Bottom() const { return mBits & eSideBitsBottom; } - bool Left() const { return mBits & eSideBitsLeft; } + bool Top() const { return (mBits & eSideBitsTop) != 0; } + bool Right() const { return (mBits & eSideBitsRight) != 0; } + bool Bottom() const { return (mBits & eSideBitsBottom) != 0; } + bool Left() const { return (mBits & eSideBitsLeft) != 0; } bool Contains(SideBits aSideBits) const { MOZ_ASSERT((aSideBits & ~eSideBitsAll) == 0, "illegal side bits"); diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 53203477b70..1bd826bedcc 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -298,8 +298,8 @@ public: */ bool HasNonIntegerTranslation() const { return HasNonTranslation() || - !FuzzyEqual(_31, floor(_31 + 0.5)) || - !FuzzyEqual(_32, floor(_32 + 0.5)); + !FuzzyEqual(_31, floor(_31 + Float(0.5))) || + !FuzzyEqual(_32, floor(_32 + Float(0.5))); } /** diff --git a/gfx/gl/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp index f67b283365b..1739afb324b 100755 --- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -846,7 +846,8 @@ DrawBuffer::Create(GLContext* const gl, DrawBuffer::~DrawBuffer() { - mGL->MakeCurrent(); + if (!mGL->MakeCurrent()) + return; GLuint fb = mFB; GLuint rbs[] = { @@ -923,7 +924,8 @@ ReadBuffer::Create(GLContext* gl, ReadBuffer::~ReadBuffer() { - mGL->MakeCurrent(); + if (!mGL->MakeCurrent()) + return; GLuint fb = mFB; GLuint rbs[] = { diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index ea23b50c0f5..c28fc904b81 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -137,7 +137,7 @@ APZCTreeManager::UpdateHitTestingTree(CompositorParent* aCompositor, { APZThreadUtils::AssertOnCompositorThread(); - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); // For testing purposes, we log some data to the APZTestData associated with // the layers id that originated this update. @@ -1186,7 +1186,7 @@ void APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, const Maybe& aConstraints) { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); RefPtr node = GetTargetNode(aGuid, nullptr); MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC @@ -1245,7 +1245,7 @@ APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform() // matched APZCs is the same. It is simplest to ensure that by flushing the // pending repaint requests, which makes all of the untransforms empty (and // therefore equal). - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); mTreeLock.AssertCurrentThreadOwns(); ForEachNode(mRootNode.get(), @@ -1270,7 +1270,7 @@ APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid) void APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); RefPtr apzc = FindRootContentOrRootApzc(); if (apzc) { apzc->AdjustScrollForSurfaceShift(aShift); @@ -1286,7 +1286,7 @@ APZCTreeManager::ClearTree() APZThreadUtils::RunOnControllerThread(NewRunnableMethod( mInputQueue.get(), &InputQueue::Clear)); - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); // Collect the nodes into a list, and then destroy each one. // We can't destroy them as we collect them, because ForEachNode() @@ -1308,7 +1308,7 @@ APZCTreeManager::ClearTree() RefPtr APZCTreeManager::GetRootNode() const { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); return mRootNode; } @@ -1500,7 +1500,7 @@ APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint) already_AddRefed APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid) { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); RefPtr node = GetTargetNode(aGuid, nullptr); MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC RefPtr apzc = node ? node->GetApzc() : nullptr; @@ -1519,7 +1519,7 @@ APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid, already_AddRefed APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHitResult) { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); HitTestResult hitResult = HitNothing; ParentLayerPoint point = ViewAs(aPoint, PixelCastJustification::ScreenIsParentLayerForRoot); @@ -1552,7 +1552,7 @@ APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics) { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); return DepthFirstSearch(mRootNode.get(), [&aDragMetrics](HitTestingTreeNode* aNode) { @@ -1877,7 +1877,7 @@ ScreenToParentLayerMatrix4x4 APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const { Matrix4x4 result; - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); // The comments below assume there is a chain of layers L..R with L and P having APZC instances as // explained in the comment above. This function is called with aApzc at L, and the loop @@ -1918,7 +1918,7 @@ ParentLayerToScreenMatrix4x4 APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const { Matrix4x4 result; - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); // The comments below assume there is a chain of layers L..R with L and P having APZC instances as // explained in the comment above. This function is called with aApzc at L, and the loop @@ -1947,7 +1947,7 @@ APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) co already_AddRefed APZCTreeManager::GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const { - MonitorAutoLock lock(mTreeLock); + MutexAutoLock lock(mTreeLock); RefPtr apzc; // For now, we only ever want to do pinching on the root-content APZC for // a given layers id. diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index 56e70ead86a..d4dffe0fc85 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -17,7 +17,7 @@ #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/layers/APZUtils.h" // for HitTestResult #include "mozilla/layers/TouchCounter.h"// for TouchCounter -#include "mozilla/Monitor.h" // for Monitor +#include "mozilla/Mutex.h" // for Mutex #include "mozilla/TimeStamp.h" // for mozilla::TimeStamp #include "mozilla/Vector.h" // for mozilla::Vector #include "nsAutoPtr.h" // for nsRefPtr @@ -534,7 +534,7 @@ private: * is considered part of the APZC tree management state. * Finally, the lock needs to be held when accessing mZoomConstraints. * IMPORTANT: See the note about lock ordering at the top of this file. */ - mutable mozilla::Monitor mTreeLock; + mutable mozilla::Mutex mTreeLock; RefPtr mRootNode; /* Holds the zoom constraints for scrollable layers, as determined by the * the main-thread gecko code. */ diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 51ae4c49be8..9bcc9473f5b 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -155,6 +155,14 @@ using mozilla::gfx::PointTyped; * pixels would make us drop to low-res at y=490...990.\n * This value is in layer pixels. * + * \li\b apz.displayport_expiry_ms + * While a scrollable frame is scrolling async, we set a displayport on it + * to make sure it is layerized. However this takes up memory, so once the + * scrolling stops we want to remove the displayport. This pref controls how + * long after scrolling stops the displayport is removed. A value of 0 will + * disable the expiry behavior entirely. + * Units: milliseconds + * * \li\b apz.enlarge_displayport_when_clipped * Pref that enables enlarging of the displayport along one axis when the * generated displayport's size is beyond that of the scrollable rect on the @@ -2887,6 +2895,7 @@ AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics, aFrameMetrics.GetScrollOffset().x) < EPSILON && fabsf(mLastPaintRequestMetrics.GetScrollOffset().y - aFrameMetrics.GetScrollOffset().y) < EPSILON && + aFrameMetrics.GetPresShellResolution() == mLastPaintRequestMetrics.GetPresShellResolution() && aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() && fabsf(aFrameMetrics.GetViewport().width - mLastPaintRequestMetrics.GetViewport().width) < EPSILON && @@ -3315,6 +3324,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri / mFrameMetrics.GetCumulativeResolution(); float presShellResolutionChange = aLayerMetrics.GetPresShellResolution() / mFrameMetrics.GetPresShellResolution(); + if (presShellResolutionChange != 1.0f) { + needContentRepaint = true; + } mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange); } else { // Take the new zoom as either device scale or composition width or diff --git a/gfx/layers/apz/src/Axis.cpp b/gfx/layers/apz/src/Axis.cpp index 8b9a0cd1110..7e2e54df639 100644 --- a/gfx/layers/apz/src/Axis.cpp +++ b/gfx/layers/apz/src/Axis.cpp @@ -30,6 +30,13 @@ namespace mozilla { namespace layers { +// When we compute the velocity we do so by taking two input events and +// dividing the distance delta over the time delta. In some cases the time +// delta can be really small, which can make the velocity computation very +// volatile. To avoid this we impose a minimum time delta below which we do +// not recompute the velocity. +const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5; + bool FuzzyEqualsCoordinate(float aValue1, float aValue2) { return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON) @@ -40,7 +47,8 @@ extern StaticAutoPtr gVelocityCurveFunction; Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController) : mPos(0), - mPosTimeMs(0), + mVelocitySampleTimeMs(0), + mVelocitySamplePos(0), mVelocity(0.0f), mAxisLocked(false), mAsyncPanZoomController(aAsyncPanZoomController), @@ -67,16 +75,20 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord // mVelocityQueue is controller-thread only APZThreadUtils::AssertOnControllerThread(); - if (aTimestampMs == mPosTimeMs) { - // This could be a duplicate event, or it could be a legitimate event - // on some platforms that generate events really fast. As a compromise - // update mPos so we don't run into problems like bug 1042734, even though - // that means the velocity will be stale. Better than doing a divide-by-zero. + if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) { + // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS. + // We still update mPos so that the positioning is correct (and we don't run + // into problems like bug 1042734) but the velocity will remain where it was. + // In particular we don't update either mVelocitySampleTimeMs or + // mVelocitySamplePos so that eventually when we do get an event with the + // required time delta we use the corresponding distance delta as well. + AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n", + mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs)); mPos = aPos; return; } - float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs); + float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs); if (gfxPrefs::APZMaxVelocity() > 0.0f) { bool velocityIsNegative = (newVelocity < 0); newVelocity = fabs(newVelocity); @@ -107,7 +119,8 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord mAsyncPanZoomController, Name(), newVelocity); mVelocity = newVelocity; mPos = aPos; - mPosTimeMs = aTimestampMs; + mVelocitySampleTimeMs = aTimestampMs; + mVelocitySamplePos = aPos; // Limit queue size pased on pref mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, mVelocity)); @@ -119,7 +132,8 @@ void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) { mStartPos = aPos; mPos = aPos; - mPosTimeMs = aTimestampMs; + mVelocitySampleTimeMs = aTimestampMs; + mVelocitySamplePos = aPos; mAxisLocked = false; } diff --git a/gfx/layers/apz/src/Axis.h b/gfx/layers/apz/src/Axis.h index 1da9dcf3e42..3771d45c210 100644 --- a/gfx/layers/apz/src/Axis.h +++ b/gfx/layers/apz/src/Axis.h @@ -251,7 +251,14 @@ public: protected: ParentLayerCoord mPos; - uint32_t mPosTimeMs; + + // mVelocitySampleTimeMs and mVelocitySamplePos are the time and position + // used in the last velocity sampling. They get updated when a new sample is + // taken (which may not happen on every input event, if the time delta is too + // small). + uint32_t mVelocitySampleTimeMs; + ParentLayerCoord mVelocitySamplePos; + ParentLayerCoord mStartPos; float mVelocity; // Units: ParentLayerCoords per millisecond bool mAxisLocked; // Whether movement on this axis is locked. diff --git a/gfx/layers/apz/test/mochitest/helper_subframe_style.css b/gfx/layers/apz/test/mochitest/helper_subframe_style.css index a911bea7115..5af9640802f 100644 --- a/gfx/layers/apz/test/mochitest/helper_subframe_style.css +++ b/gfx/layers/apz/test/mochitest/helper_subframe_style.css @@ -3,7 +3,7 @@ body { } .inner-frame { - margin-top: 25%; + margin-top: 50px; /* this should be at least 30px */ height: 200%; width: 75%; overflow: scroll; diff --git a/gfx/layers/apz/test/mochitest/test_layerization.html b/gfx/layers/apz/test/mochitest/test_layerization.html index 99e25edceb9..b25c6054b04 100644 --- a/gfx/layers/apz/test/mochitest/test_layerization.html +++ b/gfx/layers/apz/test/mochitest/test_layerization.html @@ -72,6 +72,8 @@ function scrollWheelOver(element) { var gTestContinuation = null; var utils; +const DISPLAYPORT_EXPIRY = 100; + // Return whether the element with id |elementId| has been layerized. // Assumes |elementId| will be present in the content description for the // element, and not in the content descriptions of other elements. @@ -91,6 +93,14 @@ function isLayerized(elementId) { return false; } +// Helper function to pass to waitForAllPaints rather than passing driveTest +// directly. If there are no paints pending waitForAllPaints will invoke the +// callback synchronously, and if we did waitForAllPaints(driveTest) that might +// cause reentrancy into driveTest which is bad. +function callDriveTestAsync() { + setTimeout(driveTest, 0); +} + function* runTest() { utils = SpecialPowers.getDOMWindowUtils(window); @@ -126,6 +136,72 @@ function* runTest() { yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4')); ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized"); ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized"); + + // Now we enable displayport expiry, and verify that things are still + // layerized as they were before. + yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, driveTest); + ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry"); + ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry"); + ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry"); + ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry"); + ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry"); + ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry"); + ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry"); + ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry"); + + // Now we trigger a scroll on some of the things still layerized, so that + // the displayport expiry gets triggered. + + // Expire displayport with scrolling on outer1 + yield scrollWheelOver(document.getElementById('outer1')); + yield waitForAllPaints(function() { + flushApzRepaints(driveTest); + }); + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry"); + ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry"); + + // Expire displayport with scrolling on inner2 + yield scrollWheelOver(document.getElementById('inner2')); + yield waitForAllPaints(function() { + flushApzRepaints(driveTest); + }); + // Once the expiry elapses, it will trigger expiry on outer2, so we check + // both, one at a time. + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry"); + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2"); + + // Scroll on inner3. inner3 isn't layerized, and this will cause it to + // get layerized, but it will also trigger displayport expiration for inner3 + // which will eventually trigger displayport expiration on outer3. + yield scrollWheelOver(document.getElementById('outer3').contentDocument.getElementById('inner3')); + yield waitForAllPaints(function() { + flushApzRepaints(driveTest); + }); + ok(isLayerized('inner3'), "inner3 becomes layerized after scroll"); + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry"); + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry"); + + // Scroll outer4 and wait for the expiry. It should NOT get expired because + // inner4 is still layerized + yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement); + yield waitForAllPaints(function() { + flushApzRepaints(driveTest); + }); + // Wait for the expiry to elapse + yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); + yield waitForAllPaints(callDriveTestAsync); + ok(isLayerized('inner4'), "inner4 is still layerized because it never expired"); + ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized"); } function driveTest() { @@ -153,7 +229,8 @@ function startTest() { } SimpleTest.waitForExplicitFinish(); -SimpleTest.testInChaosMode(); +SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout"); +SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856) // Disable smooth scrolling, because it results in long-running scroll // animations that can result in a 'scroll' event triggered by an earlier @@ -161,6 +238,7 @@ SimpleTest.testInChaosMode(); // Also enable APZ test logging, since we use that data to determine whether // a scroll frame was layerized. SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false], + ["apz.displayport_expiry_ms", 0], ["apz.test.logging_enabled", true]]}, function() { SimpleTest.waitForFocus(startTest, window); diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index d7fda20665b..1d4446d1ba5 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -178,11 +178,12 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag #endif RefPtr texture = image->GetTextureClient(this); + const bool hasTextureClient = !!texture; for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) { if (mBuffers[i].mImageSerial == image->GetSerial()) { - if (texture) { - MOZ_ASSERT(texture == mBuffers[i].mTextureClient); + if (hasTextureClient) { + MOZ_ASSERT(image->GetTextureClient(this) == mBuffers[i].mTextureClient); } else { texture = mBuffers[i].mTextureClient; } diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index 25f103d1a6f..03be61b016e 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -339,12 +339,14 @@ DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface, NS_WARNING("No D3D device to update the texture."); return false; } - mSize = aSurface->GetSize(); - uint32_t bpp = 0; + uint32_t bpp = BytesPerPixel(aSurface->GetFormat()); + DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(); + + mSize = aSurface->GetSize(); + mFormat = aSurface->GetFormat(); _D3DFORMAT format = D3DFMT_A8R8G8B8; - mFormat = aSurface->GetFormat(); switch (mFormat) { case SurfaceFormat::B8G8R8X8: format = D3DFMT_X8R8G8B8; @@ -364,18 +366,100 @@ DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface, } int32_t maxSize = mCompositor->GetMaxTextureSize(); - DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager(); if ((mSize.width <= maxSize && mSize.height <= maxSize) || - (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) { - mTexture = DataToTexture(deviceManager, - aSurface->GetData(), aSurface->Stride(), - IntSize(mSize), format, bpp); + (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) { + mIsTiled = false; + + if (mTexture) { + D3DSURFACE_DESC currentDesc; + mTexture->GetLevelDesc(0, ¤tDesc); + + // Make sure there's no size mismatch, if there is, recreate. + if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height || + currentDesc.Format != format) { + mTexture = nullptr; + // Make sure we upload the whole surface. + aDestRegion = nullptr; + } + } + if (!mTexture) { - NS_WARNING("Could not upload texture"); + // TODO Improve: Reallocating this texture is costly enough + // that it causes us to skip frames on scrolling + // important pages like Facebook. + mTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_DEFAULT, this); + mIsTiled = false; + if (!mTexture) { + Reset(); + return false; + } + + if (mFlags & TextureFlags::COMPONENT_ALPHA) { + aDestRegion = nullptr; + } + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + gfxCriticalError() << "Failed to map surface."; Reset(); return false; } - mIsTiled = false; + + nsIntRegion regionToUpdate = aDestRegion ? *aDestRegion : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)); + + RefPtr srcTexture; + RefPtr srcSurface; + + if (mFormat == SurfaceFormat::A8) { + // A8 doesn't appear to work with CreateOffscreenPlainSurface + srcTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_SYSTEMMEM, this); + if (!srcTexture) { + aSurface->Unmap(); + return false; + } + srcTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + } else { + HRESULT hr = mCompositor->device()->CreateOffscreenPlainSurface(mSize.width, mSize.height, format, D3DPOOL_SYSTEMMEM, getter_AddRefs(srcSurface), nullptr); + if (FAILED(hr)) { + aSurface->Unmap(); + return false; + } + } + + RefPtr destSurface; + mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface)); + + D3DLOCKED_RECT rect; + srcSurface->LockRect(&rect, nullptr, 0); + + for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) { + const nsIntRect& iterRect = iter.Get(); + uint8_t* src = map.mData + map.mStride * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x; + uint8_t* dest = reinterpret_cast(rect.pBits) + rect.Pitch * iterRect.y + BytesPerPixel(aSurface->GetFormat()) * iterRect.x; + + for (int y = 0; y < iterRect.height; y++) { + memcpy(dest + rect.Pitch * y, + src + map.mStride * y, + iterRect.width * bpp); + } + } + + srcSurface->UnlockRect(); + aSurface->Unmap(); + + for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) { + const nsIntRect& iterRect = iter.Get(); + + RECT updateRect; + updateRect.left = iterRect.x; + updateRect.top = iterRect.y; + updateRect.right = iterRect.XMost(); + updateRect.bottom = iterRect.YMost(); + POINT point = { updateRect.left, updateRect.top }; + + mCompositor->device()->UpdateSurface(srcSurface, &updateRect, destSurface, &point); + } } else { mIsTiled = true; uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) * diff --git a/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp b/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp index 6e014feded1..ad4f33f7d9e 100644 --- a/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp +++ b/gfx/skia/skia/src/gpu/SkGrPixelRef.cpp @@ -51,7 +51,8 @@ bool SkROLockPixelsPixelRef::onLockPixelsAreWritable() const { /////////////////////////////////////////////////////////////////////////////// static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT, - SkColorProfileType dstPT, const SkIRect* subset) { + SkAlphaType dstAT, SkColorProfileType dstPT, + const SkIRect* subset) { if (nullptr == texture || kUnknown_SkColorType == dstCT) { return nullptr; } @@ -75,7 +76,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp srcRect = *subset; } desc.fFlags = kRenderTarget_GrSurfaceFlag; - desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT); + desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, dstAT, dstPT); GrTexture* dst = context->textureProvider()->createTexture(desc, false, nullptr, 0); if (nullptr == dst) { @@ -88,8 +89,7 @@ static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorTyp context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0), GrContext::kFlushWrites_PixelOp); - SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType, - dstPT); + SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, dstAT, dstPT); SkGrPixelRef* pixelRef = new SkGrPixelRef(info, dst); SkSafeUnref(dst); return pixelRef; @@ -142,7 +142,8 @@ SkPixelRef* SkGrPixelRef::deepCopy(SkColorType dstCT, SkColorProfileType dstPT, // a GrTexture owned elsewhere (e.g., SkGpuDevice), and cannot live // independently of that texture. Texture-backed pixel refs, on the other // hand, own their GrTextures, and are thus self-contained. - return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, dstPT, subset); + return copy_to_new_texture_pixelref(fSurface->asTexture(), dstCT, this->info().alphaType(), + dstPT, subset); } static bool tryAllocBitmapPixels(SkBitmap* bitmap) { diff --git a/gfx/src/nsFont.cpp b/gfx/src/nsFont.cpp index 99693ccb186..777a320228d 100644 --- a/gfx/src/nsFont.cpp +++ b/gfx/src/nsFont.cpp @@ -52,29 +52,7 @@ nsFont::Init() variantPosition = NS_FONT_VARIANT_POSITION_NORMAL; } -nsFont::nsFont(const nsFont& aOther) - : fontlist(aOther.fontlist) -{ - style = aOther.style; - systemFont = aOther.systemFont; - weight = aOther.weight; - stretch = aOther.stretch; - smoothing = aOther.smoothing; - size = aOther.size; - sizeAdjust = aOther.sizeAdjust; - kerning = aOther.kerning; - synthesis = aOther.synthesis; - fontFeatureSettings = aOther.fontFeatureSettings; - languageOverride = aOther.languageOverride; - variantAlternates = aOther.variantAlternates; - variantCaps = aOther.variantCaps; - variantEastAsian = aOther.variantEastAsian; - variantLigatures = aOther.variantLigatures; - variantNumeric = aOther.variantNumeric; - variantPosition = aOther.variantPosition; - alternateValues = aOther.alternateValues; - featureValueLookup = aOther.featureValueLookup; -} +nsFont::nsFont(const nsFont& aOther) = default; nsFont::nsFont() { @@ -111,30 +89,7 @@ bool nsFont::Equals(const nsFont& aOther) const return false; } -nsFont& nsFont::operator=(const nsFont& aOther) -{ - fontlist = aOther.fontlist; - style = aOther.style; - systemFont = aOther.systemFont; - weight = aOther.weight; - stretch = aOther.stretch; - smoothing = aOther.smoothing; - size = aOther.size; - sizeAdjust = aOther.sizeAdjust; - kerning = aOther.kerning; - synthesis = aOther.synthesis; - fontFeatureSettings = aOther.fontFeatureSettings; - languageOverride = aOther.languageOverride; - variantAlternates = aOther.variantAlternates; - variantCaps = aOther.variantCaps; - variantEastAsian = aOther.variantEastAsian; - variantLigatures = aOther.variantLigatures; - variantNumeric = aOther.variantNumeric; - variantPosition = aOther.variantPosition; - alternateValues = aOther.alternateValues; - featureValueLookup = aOther.featureValueLookup; - return *this; -} +nsFont& nsFont::operator=(const nsFont& aOther) = default; void nsFont::CopyAlternates(const nsFont& aOther) diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index a46944e6807..4c4d4ae8560 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -950,11 +950,6 @@ namespace { result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber; return result; } - SizePair& operator=(const SizePair& aOther) { - mSizeContainingRect = aOther.mSizeContainingRect; - mSize = aOther.mSize; - return *this; - } bool operator<(const SizePair& aOther) const { if (mSizeContainingRect < aOther.mSizeContainingRect) return true; diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index ad2f85f8fa8..95af19c2485 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -148,6 +148,7 @@ private: DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300); DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50); DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100); + DECL_GFX_PREF(Live, "apz.displayport_expiry_ms", APZDisplayPortExpiryTime, uint32_t, 15000); DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false); DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false); DECL_GFX_PREF(Live, "apz.fling_accel_base_mult", APZFlingAccelBaseMultiplier, float, 1.0f); diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 302ffa8913c..a1acf253188 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -178,8 +178,15 @@ public: : mUserFontEntry(aUserFontEntry) {} virtual ots::TableAction GetTableAction(uint32_t aTag) override { - // preserve Graphite, color glyph and SVG tables - if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') || + // Preserve Graphite, color glyph and SVG tables + if ( +#ifdef RELEASE_BUILD // For Beta/Release, also allow OT Layout tables through + // unchecked, and rely on harfbuzz to handle them safely. + aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') || + aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') || + aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') || +#endif + aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') || aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') || aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') || aTag == TRUETYPE_TAG('G', 'l', 'a', 't') || diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 02964e962e6..9c27c0ba6ee 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -2584,12 +2584,15 @@ SimdToExpr(SimdType type, SimdOperation op) switch (type) { case SimdType::Int32x4: { ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32CASE) + break; } case SimdType::Float32x4: { ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32CASE) + break; } case SimdType::Bool32x4: { ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32CASE) + break; } default: break; } diff --git a/js/src/builtin/Eval.cpp b/js/src/builtin/Eval.cpp index d9ed9f6d875..b84a9aee4b0 100644 --- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -288,7 +288,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); - RootedObject enclosing(cx); + Rooted enclosing(cx); if (evalType == DIRECT_EVAL) enclosing = callerScript->innermostStaticScope(pc); else @@ -374,7 +374,7 @@ js::DirectEvalStringFromIon(JSContext* cx, if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); - RootedObject enclosing(cx, callerScript->innermostStaticScope(pc)); + Rooted enclosing(cx, callerScript->innermostStaticScope(pc)); Rooted staticScope(cx, StaticEvalScope::create(cx, enclosing)); if (!staticScope) return false; @@ -476,7 +476,8 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri // Unlike the non-syntactic scope chain API used by the subscript loader, // this API creates a fresh block scope each time. - RootedObject enclosingStaticScope(cx, script->enclosingStaticScope()); + Rooted enclosingStaticScope(cx, + &script->enclosingStaticScope()->as()); scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope); if (!scope) return false; diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index f0decbf4f6a..13d6c9f7d07 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -573,7 +573,7 @@ ModuleObject::isInstance(HandleValue value) } /* static */ ModuleObject* -ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope) +ModuleObject::create(ExclusiveContext* cx, Handle enclosingStaticScope) { RootedObject proto(cx, cx->global()->getModulePrototype()); RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); @@ -581,7 +581,11 @@ ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope) return nullptr; RootedModuleObject self(cx, &obj->as()); - self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(enclosingStaticScope)); + Rooted scope(cx, StaticModuleScope::create(cx, self, + enclosingStaticScope)); + if (!scope) + return nullptr; + self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(scope)); Zone* zone = cx->zone(); IndirectBindingMap* bindings = zone->new_(zone); @@ -729,10 +733,10 @@ ModuleObject::initialEnvironment() const return getReservedSlot(InitialEnvironmentSlot).toObject().as(); } -JSObject* -ModuleObject::enclosingStaticScope() const +StaticModuleScope* +ModuleObject::staticScope() const { - return getReservedSlot(StaticScopeSlot).toObjectOrNull(); + return &getReservedSlot(StaticScopeSlot).toObject().as(); } /* static */ void diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h index 1bf80aa9bad..80c3088cb4d 100644 --- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -21,6 +21,8 @@ namespace js { class ModuleEnvironmentObject; class ModuleObject; +class StaticScope; +class StaticModuleScope; namespace frontend { class ParseNode; @@ -225,7 +227,7 @@ class ModuleObject : public NativeObject static bool isInstance(HandleValue value); - static ModuleObject* create(ExclusiveContext* cx, HandleObject enclosingStaticScope); + static ModuleObject* create(ExclusiveContext* cx, Handle enclosingStaticScope); void init(HandleScript script); void setInitialEnvironment(Handle initialEnvironment); void initImportExportData(HandleArrayObject requestedModules, @@ -235,7 +237,7 @@ class ModuleObject : public NativeObject HandleArrayObject starExportEntries); JSScript* script() const; - JSObject* enclosingStaticScope() const; + StaticModuleScope* staticScope() const; ModuleEnvironmentObject& initialEnvironment() const; ModuleEnvironmentObject* environment() const; ModuleNamespaceObject* namespace_(); diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 9ba70fd5865..9413dba3cc6 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -213,6 +213,8 @@ static const JSFunctionSpec SimdTypedObjectMethods[] = { namespace js { namespace jit { +static_assert(uint64_t(SimdOperation::Last) <= UINT16_MAX, "SimdOperation must fit in uint16_t"); + // See also JitInfo_* in MCallOptimize.cpp. We provide a JSJitInfo for all the // named functions here. The default JitInfo_SimdInt32x4 etc structs represent the // SimdOperation::Constructor. diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 6ea4d065215..85ce1bf84db 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -878,7 +878,11 @@ GetBooleanSimdType(SimdType t) // // C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to // avoid clashes. -enum class SimdOperation : uint8_t { +// +// Note: because of a gcc < v4.8's compiler bug, uint8_t can't be used as the +// storage class here. See bug 1243810. See also +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 . +enum class SimdOperation { // The constructor call. No Fn_ prefix here. Constructor, @@ -901,6 +905,8 @@ enum class SimdOperation : uint8_t { Fn_fromUint32x4Bits, Fn_fromFloat32x4Bits, Fn_fromFloat64x2Bits, + + Last = Fn_fromFloat64x2Bits }; class SimdObject : public JSObject diff --git a/js/src/configure.in b/js/src/configure.in index 8bb39f0276e..863748fffd4 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -3623,9 +3623,6 @@ AC_SUBST(MOZ_PKG_SPECIAL) AC_SUBST(MOZILLA_OFFICIAL) -dnl win32 options -AC_SUBST(MOZ_BROWSE_INFO) - dnl Echo the CFLAGS to remove extra whitespace. CFLAGS=`echo \ $_WARNINGS_CFLAGS \ diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md index 8048fcd8fd7..df275002fcd 100644 --- a/js/src/doc/Debugger/Debugger.md +++ b/js/src/doc/Debugger/Debugger.md @@ -536,3 +536,13 @@ other kinds of objects. `TypeError`. Determine which global is designated by global using the same rules as [`Debugger.prototype.addDebuggee`][add]. +## Static methods of the Debugger Object + +The functions described below are not called with a `this` value. + +isCompilableUnit(source) +: Given a string of source code, designated by source, return false if + the string might become a valid JavaScript statement with the addition of + more lines. Otherwise return true. The intent is to support interactive + compilation - accumulate lines in a buffer until isCompilableUnit is true, + then pass it to the compiler. diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 3ea9abe339f..2c7d10c6795 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -72,7 +72,7 @@ class MOZ_STACK_CLASS BytecodeCompiler bool canLazilyParse(); bool createParser(); bool createSourceAndParser(); - bool createScript(HandleObject staticScope, bool savedCallerFun = false); + bool createScript(Handle staticScope, bool savedCallerFun = false); bool createEmitter(SharedContext* sharedContext, HandleScript evalCaller = nullptr, bool insideNonGlobalEval = false); bool isEvalCompilationUnit(); @@ -255,7 +255,7 @@ BytecodeCompiler::createSourceAndParser() } bool -BytecodeCompiler::createScript(HandleObject staticScope, bool savedCallerFun) +BytecodeCompiler::createScript(Handle staticScope, bool savedCallerFun) { script = JSScript::Create(cx, staticScope, savedCallerFun, options, sourceObject, /* sourceStart = */ 0, sourceBuffer.length()); @@ -284,11 +284,8 @@ BytecodeCompiler::isEvalCompilationUnit() bool BytecodeCompiler::isNonGlobalEvalCompilationUnit() { - if (!isEvalCompilationUnit()) - return false; - StaticEvalScope& eval = enclosingStaticScope->as(); - JSObject* enclosing = eval.enclosingScopeForStaticScopeIter(); - return !IsStaticGlobalLexicalScope(enclosing); + return isEvalCompilationUnit() && + !IsStaticGlobalLexicalScope(enclosingStaticScope->enclosingScope()); } bool @@ -562,7 +559,8 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller return script; } -ModuleObject* BytecodeCompiler::compileModule() +ModuleObject* +BytecodeCompiler::compileModule() { if (!createSourceAndParser()) return nullptr; @@ -571,7 +569,8 @@ ModuleObject* BytecodeCompiler::compileModule() if (!module) return nullptr; - if (!createScript(module)) + Rooted moduleScope(cx, module->staticScope()); + if (!createScript(moduleScope)) return nullptr; module->init(script); @@ -651,7 +650,8 @@ BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun, if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); - if (!createScript(enclosingStaticScope)) + Rooted scope(cx, fn->pn_funbox->staticScope()); + if (!createScript(scope)) return false; script->bindings = fn->pn_funbox->bindings; @@ -803,11 +803,12 @@ frontend::CompileLazyFunction(JSContext* cx, Handle lazy, const cha if (!NameFunctions(cx, pn)) return false; - RootedObject enclosingScope(cx, lazy->enclosingScope()); + Rooted staticScope(cx, pn->pn_funbox->staticScope()); + MOZ_ASSERT(staticScope); RootedScriptSource sourceObject(cx, lazy->sourceObject()); MOZ_ASSERT(sourceObject); - Rooted script(cx, JSScript::Create(cx, enclosingScope, false, options, + Rooted script(cx, JSScript::Create(cx, staticScope, false, options, sourceObject, lazy->begin(), lazy->end())); if (!script) return false; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 30660c59ce6..d6f472e43cc 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -733,7 +733,7 @@ BytecodeEmitter::pushLoopStatement(LoopStmtInfo* stmt, StmtType type, ptrdiff_t } } -JSObject* +StaticScope* BytecodeEmitter::innermostStaticScope() const { if (StmtInfoBCE* stmt = innermostScopeStmt()) @@ -1362,9 +1362,7 @@ BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const if (sc->staticScope()->is()) { bool bl = !stmt->enclosing; MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, stmt->staticScope - ->as() - .enclosingStaticScope() == sc->staticScope()); + MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope()); return bl; } return !stmt; @@ -1467,8 +1465,8 @@ BytecodeEmitter::computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definitio // object. Aliased block bindings do not need adjusting; see // computeAliasedSlots. uint32_t slot = dn->pn_scopecoord.slot(); - if (blockScopeOfDef(dn)->is() || - blockScopeOfDef(dn)->is()) + if (blockScopeOfDef(dn)->is() || + blockScopeOfDef(dn)->is()) { MOZ_ASSERT(IsArgOp(*op) || slot < bceOfDef->script->bindings.numBodyLevelLocals()); MOZ_ALWAYS_TRUE(bceOfDef->lookupAliasedName(bceOfDef->script, dn->name(), &slot)); @@ -1569,8 +1567,11 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) // Look up for name in function and block scopes. if (ssi.type() == StaticScopeIter::Function) { RootedScript funScript(cx, ssi.funScript()); - if (funScript->funHasExtensibleScope() || ssi.fun().atom() == pn->pn_atom) + if (funScript->funHasExtensibleScope() || + ssi.fun().function().atom() == pn->pn_atom) + { return false; + } // Skip the current function, since we're trying to convert a // free name. @@ -1582,7 +1583,7 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) } } } else if (ssi.type() == StaticScopeIter::Module) { - RootedScript moduleScript(cx, ssi.moduleScript()); + RootedScript moduleScript(cx, ssi.module().script()); uint32_t slot_; if (lookupAliasedName(moduleScript, name, &slot_, pn)) { slot = Some(slot_); @@ -1590,7 +1591,8 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn) } // Convert module import accesses to use JSOP_GETIMPORT. - RootedModuleEnvironmentObject env(cx, &ssi.module().initialEnvironment()); + RootedModuleEnvironmentObject env(cx, &ssi.module().moduleObject() + .initialEnvironment()); RootedPropertyName propName(cx, name); MOZ_ASSERT(env); if (env->hasImportBinding(propName)) { @@ -1833,12 +1835,11 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn) * Currently, the ALIASEDVAR ops do not support accessing the * callee of a DeclEnvObject, so use NAME. */ - JSFunction* fun = sc->asFunctionBox()->function(); - if (blockScopeOfDef(dn) != fun) + if (blockScopeOfDef(dn) != sc->asFunctionBox()->staticScope()) return true; - MOZ_ASSERT(fun->isLambda()); - MOZ_ASSERT(pn->pn_atom == fun->atom()); + MOZ_ASSERT(sc->asFunctionBox()->function()->isLambda()); + MOZ_ASSERT(pn->pn_atom == sc->asFunctionBox()->function()->atom()); /* * Leave pn->isOp(JSOP_GETNAME) if this->fun needs a CallObject to @@ -3538,9 +3539,9 @@ BytecodeEmitter::emitSetThis(ParseNode* pn) } static bool -IsModuleOnScopeChain(JSObject* obj) +IsModuleOnScopeChain(StaticScope* scope) { - for (StaticScopeIter ssi(obj); !ssi.done(); ssi++) { + for (StaticScopeIter ssi(scope); !ssi.done(); ssi++) { if (ssi.type() == StaticScopeIter::Module) return true; } @@ -6390,14 +6391,21 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) SharedContext* outersc = sc; if (fun->isInterpretedLazy()) { if (!fun->lazyScript()->sourceObject()) { - JSObject* scope = innermostStaticScope(); + // Two cases that can arise during parsing can cause the static + // scope chain to be incorrectly linked up: (1) the + // transformation of blocks from non-scopeful to scopeful when + // the first block-scoped declaration is found; (2) legacy + // comprehension expression transplantation. The + // setEnclosingScope call below fixes these cases. + Rooted enclosingScope(cx, innermostStaticScope()); + fun->lazyScript()->staticScope()->setEnclosingScope(enclosingScope); + JSObject* source = script->sourceObject(); - fun->lazyScript()->setParent(scope, &source->as()); + fun->lazyScript()->initSource(&source->as()); } if (emittingRunOnceLambda) fun->lazyScript()->setTreatAsRunOnce(); } else { - if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals()) funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript); @@ -6410,9 +6418,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) const TransitiveCompileOptions& transitiveOptions = parser->options(); CompileOptions options(cx, transitiveOptions); - Rooted enclosingScope(cx, innermostStaticScope()); + // See comment above regarding funScope->setEnclosingScope(). + Rooted funScope(cx, funbox->staticScope()); + Rooted enclosingScope(cx, innermostStaticScope()); + funScope->setEnclosingScope(enclosingScope); + Rooted sourceObject(cx, script->sourceObject()); - Rooted script(cx, JSScript::Create(cx, enclosingScope, false, options, + Rooted script(cx, JSScript::Create(cx, funScope, false, options, sourceObject, funbox->bufStart, funbox->bufEnd)); if (!script) @@ -6426,8 +6438,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) insideNonGlobalEval, lineNum, emitterMode); if (!bce2.init()) return false; - - /* We measured the max scope depth when we parsed the function. */ if (!bce2.emitFunctionScript(pn->pn_body)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index b7cacb6249d..0d4348a21e1 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -249,9 +249,9 @@ struct BytecodeEmitter StmtInfoBCE* innermostStmt() const { return stmtStack.innermost(); } StmtInfoBCE* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } - JSObject* innermostStaticScope() const; - JSObject* blockScopeOfDef(Definition* dn) const { - return parser->blockScopes[dn->pn_blockid]; + StaticScope* innermostStaticScope() const; + StaticScope* blockScopeOfDef(Definition* dn) const { + return &parser->blockScopes[dn->pn_blockid].get()->as(); } bool atBodyLevel(StmtInfoBCE* stmt) const; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 30b35bd7eb7..e97278253bb 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -1193,8 +1193,8 @@ FunctionBox::trace(JSTracer* trc) { ObjectBox::trace(trc); bindings.trace(trc); - if (enclosingStaticScope_) - TraceRoot(trc, &enclosingStaticScope_, "funbox-enclosingStaticScope"); + if (staticScope_) + TraceRoot(trc, &staticScope_, "funbox-staticScope"); } void diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 984b8cf720b..93d1dccf781 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -57,9 +57,9 @@ JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContex { fun_->unsetEnvironment(); fun_->setFunctionBox(funbox); - funbox->computeAllowSyntax(fun_); - funbox->computeInWith(fun_); - funbox->computeThisBinding(fun_); + funbox->computeAllowSyntax(funbox->staticScope_); + funbox->computeInWith(funbox->staticScope_); + funbox->computeThisBinding(funbox->staticScope_); } JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox() @@ -119,18 +119,18 @@ MarkUsesAsHoistedLexical(ParseNode* pn) } void -SharedContext::computeAllowSyntax(JSObject* staticScope) +SharedContext::computeAllowSyntax(StaticScope* staticScope) { for (StaticScopeIter it(context, staticScope); !it.done(); it++) { - if (it.type() == StaticScopeIter::Function && !it.fun().isArrow()) { + if (it.type() == StaticScopeIter::Function && !it.fun().function().isArrow()) { // Any function supports new.target. allowNewTarget_ = true; - allowSuperProperty_ = it.fun().allowSuperProperty(); + allowSuperProperty_ = it.fun().function().allowSuperProperty(); if (it.maybeFunctionBox()) { superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject(); allowSuperCall_ = it.maybeFunctionBox()->isDerivedClassConstructor(); } else { - allowSuperCall_ = it.fun().isDerivedClassConstructor(); + allowSuperCall_ = it.fun().function().isDerivedClassConstructor(); } break; } @@ -138,7 +138,7 @@ SharedContext::computeAllowSyntax(JSObject* staticScope) } void -SharedContext::computeThisBinding(JSObject* staticScope) +SharedContext::computeThisBinding(StaticScope* staticScope) { for (StaticScopeIter it(context, staticScope); !it.done(); it++) { if (it.type() == StaticScopeIter::Module) { @@ -147,9 +147,10 @@ SharedContext::computeThisBinding(JSObject* staticScope) } if (it.type() == StaticScopeIter::Function) { + RootedFunction fun(context, &it.fun().function()); // Arrow functions and generator expression lambdas don't have // their own `this` binding. - if (it.fun().isArrow()) + if (fun->isArrow()) continue; bool isDerived; if (it.maybeFunctionBox()) { @@ -157,9 +158,9 @@ SharedContext::computeThisBinding(JSObject* staticScope) continue; isDerived = it.maybeFunctionBox()->isDerivedClassConstructor(); } else { - if (it.fun().nonLazyScript()->isGeneratorExp()) + if (fun->nonLazyScript()->isGeneratorExp()) continue; - isDerived = it.fun().isDerivedClassConstructor(); + isDerived = fun->isDerivedClassConstructor(); } // Derived class constructors (including nested arrow functions and @@ -176,7 +177,7 @@ SharedContext::computeThisBinding(JSObject* staticScope) } void -SharedContext::computeInWith(JSObject* staticScope) +SharedContext::computeInWith(StaticScope* staticScope) { for (StaticScopeIter it(context, staticScope); !it.done(); it++) { if (it.type() == StaticScopeIter::With) { @@ -195,8 +196,8 @@ SharedContext::markSuperScopeNeedsHomeObject() return; for (StaticScopeIter it(context, staticScope()); !it.done(); it++) { - if (it.type() == StaticScopeIter::Function && !it.fun().isArrow()) { - MOZ_ASSERT(it.fun().allowSuperProperty()); + if (it.type() == StaticScopeIter::Function && !it.fun().function().isArrow()) { + MOZ_ASSERT(it.fun().function().allowSuperProperty()); // If we are still emitting the outer function that needs a home // object, mark it as needing one. Otherwise, we must be emitting // an eval script, and the outer function must already be marked @@ -204,7 +205,7 @@ SharedContext::markSuperScopeNeedsHomeObject() if (it.maybeFunctionBox()) it.maybeFunctionBox()->setNeedsHomeObject(); else - MOZ_ASSERT(it.fun().nonLazyScript()->needsHomeObject()); + MOZ_ASSERT(it.funScript()->needsHomeObject()); superScopeAlreadyNeedsHomeObject_ = true; return; } @@ -754,12 +755,12 @@ Parser::newObjectBox(JSObject* obj) template FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun, - JSObject* enclosingStaticScope, ParseContext* outerpc, + ParseContext* outerpc, Directives directives, bool extraWarnings, GeneratorKind generatorKind) : ObjectBox(fun, traceListHead), SharedContext(cx, directives, extraWarnings), bindings(), - enclosingStaticScope_(enclosingStaticScope), + staticScope_(nullptr), bufStart(0), bufEnd(0), startLine(1), @@ -782,13 +783,21 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunct MOZ_ASSERT(fun->isTenured()); } +bool +FunctionBox::initStaticScope(Handle enclosingScope) +{ + RootedFunction fun(context, function()); + staticScope_ = StaticFunctionScope::create(context, fun, enclosingScope); + return staticScope_ != nullptr; +} + template FunctionBox* Parser::newFunctionBox(Node fn, JSFunction* fun, ParseContext* outerpc, Directives inheritedDirectives, GeneratorKind generatorKind, - JSObject* enclosingStaticScope) + Handle enclosingStaticScope) { MOZ_ASSERT_IF(outerpc, enclosingStaticScope == outerpc->innermostStaticScope()); MOZ_ASSERT(fun); @@ -801,15 +810,17 @@ Parser::newFunctionBox(Node fn, JSFunction* fun, * function. */ FunctionBox* funbox = - alloc.new_(context, traceListHead, fun, enclosingStaticScope, outerpc, - inheritedDirectives, options().extraWarningsOption, - generatorKind); + alloc.new_(context, traceListHead, fun, outerpc, inheritedDirectives, + options().extraWarningsOption, generatorKind); if (!funbox) { ReportOutOfMemory(context); return nullptr; } - traceListHead = funbox; + + if (!funbox->initStaticScope(enclosingStaticScope)) + return nullptr; + if (fn) handler.setFunctionBox(fn, funbox); @@ -1158,7 +1169,7 @@ Parser::standaloneFunctionBody(HandleFunction fun, GeneratorKind generatorKind, Directives inheritedDirectives, Directives* newDirectives, - HandleObject enclosingStaticScope) + Handle enclosingStaticScope) { MOZ_ASSERT(checkOptionsCalled); @@ -2844,7 +2855,9 @@ Parser::finishFunctionDefinition(Node pn, FunctionBox* funbo size_t numInnerFunctions = pc->innerFunctions.length(); RootedFunction fun(context, funbox->function()); - LazyScript* lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions, + Rooted funScope(context, funbox->staticScope()); + LazyScript* lazy = LazyScript::CreateRaw(context, fun, funScope, + numFreeVariables, numInnerFunctions, versionNumber(), funbox->bufStart, funbox->bufEnd, funbox->startLine, funbox->startColumn); if (!lazy) @@ -3047,7 +3060,7 @@ Parser::standaloneLazyFunction(HandleFunction fun, bool strict if (!tokenStream.peekTokenPos(&pn->pn_pos)) return null(); - RootedObject enclosing(context, fun->lazyScript()->enclosingScope()); + Rooted enclosing(context, fun->lazyScript()->enclosingScope()); Directives directives(/* strict = */ strict); FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, enclosing); if (!funbox) @@ -6501,8 +6514,11 @@ template <> ParseNode* Parser::withStatement(YieldHandling yieldHandling) { - // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements - // in syntax-parse mode. See bug 892583. + // This is intentionally different from other abortIfSyntaxParser() + // bailouts: `with` statements rule out syntax-only parsing for the entire + // compilation unit. This `return null()` causes us to bail out all the way + // to BytecodeCompiler::compileScript(), which retries with syntax parsing + // disabled. See bug 892583. if (handler.syntaxParser) { handler.disableSyntaxParser(); abortedSyntaxParse = true; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index f95e255497c..abfad067a28 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -290,7 +290,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); } StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); } - JSObject* innermostStaticScope() const { + StaticScope* innermostStaticScope() const { if (StmtInfoPC* stmt = innermostScopeStmt()) return stmt->staticScope; return sc->staticScope(); @@ -310,9 +310,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext if (sc->staticScope()->is()) { bool bl = !stmt->enclosing; MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, stmt->staticScope - ->template as() - .enclosingStaticScope() == sc->staticScope()); + MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope()); return bl; } return !stmt; @@ -507,11 +505,11 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter ObjectBox* newObjectBox(JSObject* obj); FunctionBox* newFunctionBox(Node fn, JSFunction* fun, ParseContext* outerpc, Directives directives, GeneratorKind generatorKind, - JSObject* enclosingStaticScope); + Handle enclosingStaticScope); // Use when the funbox is the outermost. FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives, - GeneratorKind generatorKind, HandleObject enclosingStaticScope) + GeneratorKind generatorKind, Handle enclosingStaticScope) { return newFunctionBox(fn, fun, nullptr, directives, generatorKind, enclosingStaticScope); @@ -521,7 +519,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext* outerpc, Directives directives, GeneratorKind generatorKind) { - RootedObject enclosing(context, outerpc->innermostStaticScope()); + Rooted enclosing(context, outerpc->innermostStaticScope()); return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing); } @@ -534,7 +532,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind, HandleObject proto); - bool generateBlockId(JSObject* staticScope, uint32_t* blockIdOut) { + bool generateBlockId(StaticScope* staticScope, uint32_t* blockIdOut) { if (blockScopes.length() == StmtInfoPC::BlockIdLimit) { tokenStream.reportError(JSMSG_NEED_DIET, "program"); return false; @@ -600,7 +598,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node standaloneFunctionBody(HandleFunction fun, Handle formals, GeneratorKind generatorKind, Directives inheritedDirectives, Directives* newDirectives, - HandleObject enclosingStaticScope); + Handle enclosingStaticScope); // Parse a function, given only its arguments and body. Used for lazily // parsed functions. diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index a70fe79c80c..dd2ad6dcd9a 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -229,10 +229,10 @@ class SharedContext // GlobalSharedContexts are stack allocated and thus may use RootedObject // for the static scope. FunctionBoxes are LifoAlloc'd and need to // manually trace their static scope. - virtual JSObject* staticScope() const = 0; - void computeAllowSyntax(JSObject* staticScope); - void computeInWith(JSObject* staticScope); - void computeThisBinding(JSObject* staticScope); + virtual StaticScope* staticScope() const = 0; + void computeAllowSyntax(StaticScope* staticScope); + void computeInWith(StaticScope* staticScope); + void computeThisBinding(StaticScope* staticScope); virtual ObjectBox* toObjectBox() { return nullptr; } bool isObjectBox() { return toObjectBox() != nullptr; } @@ -300,19 +300,19 @@ class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext // non-function scope, so we have to compute our ThisBinding based on // the actual callee. if (maybeEvalCaller) - computeThisBinding(maybeEvalCaller); + computeThisBinding(maybeEvalCaller->nonLazyScript()->staticScope()); else computeThisBinding(staticScope); } - JSObject* staticScope() const override { return staticScope_; } + StaticScope* staticScope() const override { return staticScope_; } }; class FunctionBox : public ObjectBox, public SharedContext { public: Bindings bindings; /* bindings for this function */ - JSObject* enclosingStaticScope_; + StaticFunctionScope* staticScope_; uint32_t bufStart; uint32_t bufEnd; uint32_t startLine; @@ -335,13 +335,15 @@ class FunctionBox : public ObjectBox, public SharedContext template FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun, - JSObject* enclosingStaticScope, ParseContext* pc, - Directives directives, bool extraWarnings, GeneratorKind generatorKind); + ParseContext* pc, Directives directives, bool extraWarnings, + GeneratorKind generatorKind); + + bool initStaticScope(Handle enclosingScope); ObjectBox* toObjectBox() override { return this; } JSFunction* function() const { return &object->as(); } - JSObject* staticScope() const override { return function(); } - JSObject* enclosingStaticScope() const { return enclosingStaticScope_; } + StaticFunctionScope* staticScope() const override { return staticScope_; } + StaticScope* enclosingStaticScope() const { return staticScope_->enclosingScope(); } GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } bool isGenerator() const { return generatorKind() != NotGenerator; } @@ -427,7 +429,7 @@ class ModuleBox : public ObjectBox, public SharedContext ObjectBox* toObjectBox() override { return this; } ModuleObject* module() const { return &object->as(); } - JSObject* staticScope() const override { return module(); } + StaticModuleScope* staticScope() const override { return module()->staticScope(); } void trace(JSTracer* trc) override; }; diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 2700e1f01c0..c1fdf3fa885 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -955,8 +955,8 @@ LazyScript::traceChildren(JSTracer* trc) if (sourceObject_) TraceEdge(trc, &sourceObject_, "sourceObject"); - if (enclosingScope_) - TraceEdge(trc, &enclosingScope_, "enclosingScope"); + if (staticScope_) + TraceEdge(trc, &staticScope_, "staticScope"); // We rely on the fact that atoms are always tenured. FreeVariable* freeVariables = this->freeVariables(); @@ -981,8 +981,8 @@ js::GCMarker::eagerlyMarkChildren(LazyScript *thing) if (thing->sourceObject_) traverseEdge(thing, static_cast(thing->sourceObject_)); - if (thing->enclosingScope_) - traverseEdge(thing, static_cast(thing->enclosingScope_)); + if (thing->staticScope_) + traverseEdge(thing, static_cast(thing->staticScope_)); // We rely on the fact that atoms are always tenured. LazyScript::FreeVariable* freeVariables = thing->freeVariables(); diff --git a/js/src/gc/Policy.h b/js/src/gc/Policy.h index 8243ae7efc7..1159d6ea835 100644 --- a/js/src/gc/Policy.h +++ b/js/src/gc/Policy.h @@ -100,6 +100,8 @@ class JitCode; D(js::ScriptSourceObject*) \ D(js::Shape*) \ D(js::SharedArrayBufferObject*) \ + D(js::StaticFunctionScope*) \ + D(js::StaticScope*) \ D(js::StructTypeDescr*) \ D(js::UnownedBaseShape*) \ D(js::jit::JitCode*) diff --git a/js/src/jit-test/tests/debug/Debugger-findObjects-11.js b/js/src/jit-test/tests/debug/Debugger-findObjects-11.js new file mode 100644 index 00000000000..f0330447f0b --- /dev/null +++ b/js/src/jit-test/tests/debug/Debugger-findObjects-11.js @@ -0,0 +1,7 @@ +// This shouldn't segfault. + +var g = newGlobal(); +g.eval(`function f() { return function() { + function g() {} +}; }`); +new Debugger(g).findObjects(); diff --git a/js/src/jit-test/tests/debug/Debugger-isCompilableUnit.js b/js/src/jit-test/tests/debug/Debugger-isCompilableUnit.js new file mode 100644 index 00000000000..d872541c191 --- /dev/null +++ b/js/src/jit-test/tests/debug/Debugger-isCompilableUnit.js @@ -0,0 +1,50 @@ +load(libdir + "asserts.js"); + +const bad_types = [ + 2112, + {geddy: "lee"}, + () => 1, + [], + Array +] + +// We only accept strings around here! +for (var badType of bad_types) { + assertThrowsInstanceOf(() => { + Debugger.isCompilableUnit(badType); + }, TypeError); +} + +const compilable_units = [ + "wubba-lubba-dub-dub", + "'Get Schwifty!'", + "1 + 2", + "function f(x) {}", + "function x(f,) {", // statements with bad syntax are always compilable + "let x = 100", + ";;;;;;;;", + "", + " ", + "\n", + "let x", +] + +const non_compilable_units = [ + "function f(x) {", + "(...d) =>", + "{geddy:", + "{", + "[1, 2", + "[", + "1 +", + "let x =", + "3 ==", +] + +for (var code of compilable_units) { + assertEq(Debugger.isCompilableUnit(code), true); +} + +for (var code of non_compilable_units) { + assertEq(Debugger.isCompilableUnit(code), false); +} diff --git a/js/src/jit-test/tests/gc/withStatementOffThread.js b/js/src/jit-test/tests/gc/withStatementOffThread.js new file mode 100644 index 00000000000..fa5a4edd452 --- /dev/null +++ b/js/src/jit-test/tests/gc/withStatementOffThread.js @@ -0,0 +1,12 @@ +if (helperThreadCount() == 0) + quit(); + +var code = ` +function f() { + [function() { { function g() { } } }, + function() { with ({}) { } }]; +} +`; + +offThreadCompileScript(code); +runOffThreadScript(); diff --git a/js/src/jit-test/tests/xdr/scope.js b/js/src/jit-test/tests/xdr/scope.js index 417a402d24d..90698ceebd9 100644 --- a/js/src/jit-test/tests/xdr/scope.js +++ b/js/src/jit-test/tests/xdr/scope.js @@ -1,6 +1,50 @@ load(libdir + 'bytecode-cache.js'); var test = ""; +// code a nested function after calling it +test = ` + function f() { + function g() { + return [f, g]; + } + return g(); + } + f() +`; +evalWithCache(test, { + assertEqBytecode: true, + checkAfter(ctx) { + let [f, g] = ctx.global.f(); + assertEq(f, ctx.global.f); + assertEq(f()[0], f); + assertEq(f()[1] === g, false); // second call, fresh g closure + assertEq(f()[1].toString(), g.toString()); // but the same source code + assertEq(g()[0], f); + assertEq(g()[1], g); + } +}); + +// code an unused function that contains an unused nested function +test = ` + function f() { + function g() { + return [f, g]; + } + return g; + } + f +`; +evalWithCache(test, { + assertEqBytecode: true, + checkAfter(ctx) { + let f = ctx.global.f; + let g = f(); + let [f1, g1] = g(); + assertEq(f1, f); + assertEq(g1, g); + } +}); + // code a function which has both used and unused inner functions. test = (function () { function f() { @@ -16,4 +60,4 @@ test = (function () { return "var obj = { x : 2 };" + f.toSource() + "; f()"; })(); -evalWithCache(test, { assertEqBytecode: true, assertEqResult : true }); +evalWithCache(test, { assertEqBytecode: true, assertEqResult: true }); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 36ab64bde01..6fd562a0b81 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -3695,8 +3695,12 @@ jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, JSFunction* fun, &inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr); if (!builder.build()) { - if (cx->isThrowingOverRecursed() || builder.abortReason() == AbortReason_Alloc) + if (cx->isThrowingOverRecursed() || + cx->isThrowingOutOfMemory() || + builder.abortReason() == AbortReason_Alloc) + { return false; + } MOZ_ASSERT(!cx->isExceptionPending()); return true; } diff --git a/js/src/jit/TypePolicy.h b/js/src/jit/TypePolicy.h index ff31b547674..9d671bb20f4 100644 --- a/js/src/jit/TypePolicy.h +++ b/js/src/jit/TypePolicy.h @@ -525,14 +525,6 @@ class FilterTypeSetPolicy final : public TypePolicy virtual bool adjustInputs(TempAllocator& alloc, MInstruction* ins) override; }; -static inline bool -CoercesToDouble(MIRType type) -{ - if (type == MIRType_Undefined || IsFloatingPointType(type)) - return true; - return false; -} - #undef SPECIALIZATION_DATA_ #undef INHERIT_DATA_ #undef EMPTY_DATA_ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c33cce9ae00..65808e35f91 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3456,8 +3456,9 @@ CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain, staticScopeObj.set(&globalLexical->staticBlock()); if (!scopeChain.empty()) { - staticScopeObj.set(StaticNonSyntacticScope::create(cx, staticScopeObj)); - if (!staticScopeObj) + Rooted scope(cx, + StaticNonSyntacticScope::create(cx, staticScopeObj)); + if (!scope) return false; // The XPConnect subscript loader, which may pass in its own dynamic @@ -3478,10 +3479,11 @@ CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain, // TODOshu: disallow the subscript loader from using non-distinguished // objects as dynamic scopes. dynamicScopeObj.set( - cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, staticScopeObj, - dynamicScopeObj)); + cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, scope, dynamicScopeObj)); if (!dynamicScopeObj) return false; + + staticScopeObj.set(scope); } return true; @@ -3495,7 +3497,7 @@ IsFunctionCloneable(HandleFunction fun) // If a function was compiled to be lexically nested inside some other // script, we cannot clone it without breaking the compiler's assumptions. - if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) { + if (StaticScope* scope = fun->nonLazyScript()->enclosingStaticScope()) { // If the script is directly under the global scope, we can clone it. if (IsStaticGlobalLexicalScope(scope)) return true; @@ -3512,7 +3514,7 @@ IsFunctionCloneable(HandleFunction fun) if (block.needsClone()) return false; - JSObject* enclosing = block.enclosingStaticScope(); + StaticScope* enclosing = block.enclosingScope(); // If the script is an indirect eval that is immediately scoped // under the global, we can clone it. diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 77fb3f6a2d3..e27a274ac49 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -505,7 +505,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandle desc) ClonedBlockObject* JSCompartment::getOrCreateNonSyntacticLexicalScope(JSContext* cx, - HandleObject enclosingStatic, + Handle enclosingStatic, HandleObject enclosingScope) { if (!nonSyntacticLexicalScopes_) { diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 700d74f40f0..fb58d4b8acf 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -32,6 +32,7 @@ template class ComponentFinder; struct NativeIterator; class ClonedBlockObject; +class StaticNonSyntacticScope; /* * A single-entry cache for some base-10 double-to-string conversions. This @@ -529,9 +530,10 @@ struct JSCompartment explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {} }; - js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(JSContext* cx, - js::HandleObject enclosingStatic, - js::HandleObject enclosingScope); + js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope( + JSContext* cx, + js::Handle enclosingStatic, + js::HandleObject enclosingScope); js::ClonedBlockObject* getNonSyntacticLexicalScope(JSObject* enclosingScope) const; /* diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index e57dca138fd..b56346ee65a 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -405,9 +405,9 @@ js::GetOutermostEnclosingFunctionOfScriptedCaller(JSContext* cx) return nullptr; RootedFunction curr(cx, iter.callee(cx)); - for (StaticScopeIter i(curr); !i.done(); i++) { + for (StaticScopeIter i(curr->nonLazyScript()->staticScope()); !i.done(); i++) { if (i.type() == StaticScopeIter::Function) - curr = &i.fun(); + curr = &i.fun().function(); } return curr; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 1a63c781ae3..d368a092dd8 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -42,6 +42,7 @@ #include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" +#include "vm/ScopeObject.h" #include "vm/Shape.h" #include "vm/StringBuffer.h" #include "vm/WrapperObject.h" @@ -535,8 +536,8 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) template bool -js::XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, - MutableHandleFunction objp) +js::XDRInterpretedFunction(XDRState* xdr, Handle enclosingScope, + HandleScript enclosingScript, MutableHandleFunction objp) { enum FirstWordFlag { HasAtom = 0x1, @@ -652,10 +653,12 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, Han } template bool -js::XDRInterpretedFunction(XDRState*, HandleObject, HandleScript, MutableHandleFunction); +js::XDRInterpretedFunction(XDRState*, Handle, HandleScript, + MutableHandleFunction); template bool -js::XDRInterpretedFunction(XDRState*, HandleObject, HandleScript, MutableHandleFunction); +js::XDRInterpretedFunction(XDRState*, Handle, HandleScript, + MutableHandleFunction); /* * [[HasInstance]] internal method for Function objects: fetch the .prototype @@ -751,15 +754,14 @@ static JSObject* CreateFunctionPrototype(JSContext* cx, JSProtoKey key) { Rooted self(cx, cx->global()); - RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject()); - /* - * Bizarrely, |Function.prototype| must be an interpreted function, so - * give it the guts to be one. - */ + + // Bizarrely, |Function.prototype| must be an interpreted function, so + // give it the guts to be one. + Rooted globalLexicalEnv(cx, &self->lexicalScope()); JSObject* functionProto_ = NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED, - self, nullptr, objectProto, AllocKind::FUNCTION, + globalLexicalEnv, nullptr, objectProto, AllocKind::FUNCTION, SingletonObject); if (!functionProto_) return nullptr; @@ -787,8 +789,12 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options)) return nullptr; + Rooted globalScope(cx, &globalLexicalEnv->staticBlock()); + Rooted funScope(cx, StaticFunctionScope::create(cx, functionProto, globalScope)); + if (!funScope) + return nullptr; RootedScript script(cx, JSScript::Create(cx, - /* enclosingScope = */ nullptr, + funScope, /* savedCallerFun = */ false, options, sourceObject, @@ -1445,7 +1451,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti } if (script) { - RootedObject enclosingScope(cx, lazy->enclosingScope()); + Rooted enclosingScope(cx, lazy->enclosingScope()); RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script)); if (!clonedScript) return false; @@ -2052,7 +2058,7 @@ js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject par JSFunction* js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, - HandleObject newStaticScope, + Handle newStaticScope, gc::AllocKind allocKind /* = FUNCTION */, HandleObject proto /* = nullptr */) { diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 4b4cff9740b..dc9cfafcebe 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -728,7 +728,7 @@ CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent, // Functions whose scripts are cloned are always given singleton types. extern JSFunction* CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, - HandleObject newStaticScope, + Handle newStaticScope, gc::AllocKind kind = gc::AllocKind::FUNCTION, HandleObject proto = nullptr); @@ -789,7 +789,7 @@ JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen); template bool -XDRInterpretedFunction(XDRState* xdr, HandleObject enclosingScope, +XDRInterpretedFunction(XDRState* xdr, Handle enclosingScope, HandleScript enclosingScript, MutableHandleFunction objp); /* diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index b2ef7055c05..88d35534711 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -92,7 +92,7 @@ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObjec RootedScript script(cx, fun->getOrCreateScript(cx)); if (!script) return nullptr; - RootedObject staticScope(cx, script->enclosingStaticScope()); + Rooted staticScope(cx, script->enclosingStaticScope()); return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 25fa074500a..d8c26b313ca 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3410,6 +3410,7 @@ GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadT for (unsigned index = 0 ; index < BackgroundFinalizePhases[phase].length ; ++index) { AllocKind kind = BackgroundFinalizePhases[phase].kinds[index]; ArenaHeader* arenas = zone->arenas.arenaListsToSweep[kind]; + MOZ_RELEASE_ASSERT(uintptr_t(arenas) != uintptr_t(-1)); if (arenas) ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas); } @@ -6831,7 +6832,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) // Get the static global lexical scope of the target compartment. Static // scopes need to be fixed up below. - RootedObject targetStaticGlobalLexicalScope(rt); + Rooted targetStaticGlobalLexicalScope(rt); targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock(); for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 2ceaa7bbdd4..54ce98097d8 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -594,7 +594,7 @@ class ArenaLists enum BackgroundFinalizeStateEnum { BFS_DONE, BFS_RUN }; - typedef mozilla::Atomic + typedef mozilla::Atomic BackgroundFinalizeState; /* The current background finalization state, accessed atomically. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 0f4598a528a..5e0c41d2a0b 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -590,9 +590,7 @@ inline bool IsInternalFunctionObject(JSObject& funobj) { JSFunction& fun = funobj.as(); - MOZ_ASSERT_IF(fun.isLambda(), - fun.isInterpreted() || fun.isAsmJSNative()); - return fun.isLambda() && fun.isInterpreted() && !fun.environment(); + return fun.isInterpreted() && !fun.environment(); } /* diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index e404ea4dc69..a33dba27301 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -516,7 +516,7 @@ XDRLazyFreeVariables(XDRState* xdr, MutableHandle lazy) template static bool XDRRelazificationInfo(XDRState* xdr, HandleFunction fun, HandleScript script, - HandleObject enclosingScope, MutableHandle lazy) + Handle funScope, MutableHandle lazy) { MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript()); MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions()); @@ -546,8 +546,8 @@ XDRRelazificationInfo(XDRState* xdr, HandleFunction fun, HandleScript scri return false; if (mode == XDR_DECODE) { - lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script, - packedFields, begin, end, lineno, column)); + lazy.set(LazyScript::Create(cx, fun, script, funScope, script, packedFields, + begin, end, lineno, column)); // As opposed to XDRLazyScript, we need to restore the runtime bits // of the script, as we are trying to match the fact this function @@ -592,8 +592,8 @@ enum XDRClassKind { template bool -js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript, - HandleFunction fun, MutableHandleScript scriptp) +js::XDRScript(XDRState* xdr, Handle enclosingScopeArg, + HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp) { /* NB: Keep this in sync with CopyScript. */ @@ -637,7 +637,7 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript JSContext* cx = xdr->cx(); RootedScript script(cx); - RootedObject enclosingScope(cx, enclosingScopeArg); + Rooted enclosingScope(cx, enclosingScopeArg); natoms = nsrcnotes = 0; nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0; @@ -849,6 +849,12 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript return false; } + if (fun) { + enclosingScope = StaticFunctionScope::create(cx, fun, enclosingScope); + if (!enclosingScope) + return false; + } + script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)), options, sourceObject, 0, 0); if (!script) @@ -1045,28 +1051,33 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript } if (!xdr->codeUint32(&enclosingStaticScopeIndex)) return false; - Rooted enclosingStaticScope(cx); + Rooted enclosingStaticScope(cx); if (mode == XDR_DECODE) { if (enclosingStaticScopeIndex != UINT32_MAX) { MOZ_ASSERT(enclosingStaticScopeIndex < i); - enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex]; + enclosingStaticScope = &script->objects()->vector[enclosingStaticScopeIndex] + ->as(); } else { // This is not ternary because MSVC can't typecheck the // ternary. if (fun) - enclosingStaticScope = fun; + enclosingStaticScope = script->staticScope(); else enclosingStaticScope = enclosingScope; } } if (classk == CK_BlockObject) { - Rooted tmp(cx, static_cast(objp->get())); + Rooted tmp(cx); + if (mode == XDR_ENCODE) + tmp = &(*objp)->as(); if (!XDRStaticBlockScope(xdr, enclosingStaticScope, &tmp)) return false; *objp = tmp; } else { - Rooted tmp(cx, static_cast(objp->get())); + Rooted tmp(cx); + if (mode == XDR_ENCODE) + tmp = &(*objp)->as(); if (!XDRStaticWithScope(xdr, enclosingStaticScope, &tmp)) return false; *objp = tmp; @@ -1077,7 +1088,7 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript case CK_JSFunction: { /* Code the nested function's enclosing scope. */ uint32_t funEnclosingScopeIndex = 0; - RootedObject funEnclosingScope(cx); + Rooted funEnclosingScope(cx); if (mode == XDR_ENCODE) { RootedFunction function(cx, &(*objp)->as()); @@ -1124,12 +1135,13 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript // This is not ternary because MSVC can't typecheck the // ternary. if (fun) - funEnclosingScope = fun; + funEnclosingScope = script->staticScope(); else funEnclosingScope = enclosingScope; } else { MOZ_ASSERT(funEnclosingScopeIndex < i); - funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex]; + funEnclosingScope = &script->objects()->vector[funEnclosingScopeIndex] + .get()->as(); } } @@ -1206,7 +1218,10 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript if (mode == XDR_ENCODE) lazy = script->maybeLazyScript(); - if (!XDRRelazificationInfo(xdr, fun, script, enclosingScope, &lazy)) + Rooted lazyScope(cx, mode == XDR_DECODE + ? &enclosingScope->as() + : nullptr); + if (!XDRRelazificationInfo(xdr, fun, script, lazyScope, &lazy)) return false; if (mode == XDR_DECODE) @@ -1225,17 +1240,18 @@ js::XDRScript(XDRState* xdr, HandleObject enclosingScopeArg, HandleScript } template bool -js::XDRScript(XDRState*, HandleObject, HandleScript, HandleFunction, +js::XDRScript(XDRState*, Handle, HandleScript, HandleFunction, MutableHandleScript); template bool -js::XDRScript(XDRState*, HandleObject, HandleScript, HandleFunction, +js::XDRScript(XDRState*, Handle, HandleScript, HandleFunction, MutableHandleScript); template bool -js::XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, - HandleFunction fun, MutableHandle lazy) +js::XDRLazyScript(XDRState* xdr, Handle enclosingScope, + HandleScript enclosingScript, HandleFunction fun, + MutableHandle lazy) { JSContext* cx = xdr->cx(); @@ -1268,7 +1284,11 @@ js::XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript } if (mode == XDR_DECODE) { - lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript, + Rooted funScope(cx, + StaticFunctionScope::create(cx, fun, enclosingScope)); + if (!funScope) + return false; + lazy.set(LazyScript::Create(cx, fun, nullptr, funScope, enclosingScript, packedFields, begin, end, lineno, column)); if (!lazy) return false; @@ -1283,13 +1303,14 @@ js::XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript // Code inner functions. { RootedFunction func(cx); + Rooted funScope(cx, lazy->staticScope()); HeapPtrFunction* innerFunctions = lazy->innerFunctions(); size_t numInnerFunctions = lazy->numInnerFunctions(); for (size_t i = 0; i < numInnerFunctions; i++) { if (mode == XDR_ENCODE) func = innerFunctions[i]; - if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func)) + if (!XDRInterpretedFunction(xdr, funScope, enclosingScript, &func)) return false; if (mode == XDR_DECODE) @@ -1301,11 +1322,11 @@ js::XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript } template bool -js::XDRLazyScript(XDRState*, HandleObject, HandleScript, +js::XDRLazyScript(XDRState*, Handle, HandleScript, HandleFunction, MutableHandle); template bool -js::XDRLazyScript(XDRState*, HandleObject, HandleScript, +js::XDRLazyScript(XDRState*, Handle, HandleScript, HandleFunction, MutableHandle); void @@ -2747,7 +2768,7 @@ JSScript::initCompartment(ExclusiveContext* cx) } /* static */ JSScript* -JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCallerFun, +JSScript::Create(ExclusiveContext* cx, Handle staticScope, bool savedCallerFun, const ReadOnlyCompileOptions& options, HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd) { @@ -2760,7 +2781,7 @@ JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCa PodZero(script.get()); new (&script->bindings) Bindings; - script->enclosingStaticScope_ = enclosingScope; + script->staticScope_ = staticScope; script->savedCallerFun_ = savedCallerFun; script->initCompartment(cx); @@ -2768,10 +2789,12 @@ JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCa script->noScriptRval_ = options.noScriptRval; script->treatAsRunOnce_ = options.isRunOnce; - // Compute whether this script is under a non-syntactic scope. We don't - // need to walk the entire static scope chain if the script is nested in a - // function. In that case, we can propagate the cached value from the - // outer script. + // Compute whether this script is under a non-syntactic scope, passing + // staticScope->enclosingScope() in a case where staticScope itself is not + // a non-syntactic scope and may not be fully initialized yet. + Rooted enclosingScope(cx, staticScope); + if (staticScope && staticScope->is()) + enclosingScope = staticScope->enclosingScope(); script->hasNonSyntacticScope_ = HasNonSyntacticStaticScopeChain(enclosingScope); script->version = options.version; @@ -3123,15 +3146,20 @@ JSScript::uninlinedGlobal() const void JSScript::fixEnclosingStaticGlobalLexicalScope() { - MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingStaticScope_)); - enclosingStaticScope_ = &global().lexicalScope().staticBlock(); + if (function_) { + MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope())); + staticScope_->setEnclosingScope(&global().lexicalScope().staticBlock()); + } else { + MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_)); + staticScope_ = &global().lexicalScope().staticBlock(); + } } void LazyScript::fixEnclosingStaticGlobalLexicalScope() { - MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingScope_)); - enclosingScope_ = &function_->global().lexicalScope().staticBlock(); + MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope())); + staticScope_->setEnclosingScope(&function_->global().lexicalScope().staticBlock()); } void @@ -3409,7 +3437,8 @@ Rebase(JSScript* dst, JSScript* src, T* srcp) } static JSObject* -CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun) +CloneInnerInterpretedFunction(JSContext* cx, Handle enclosingScope, + HandleFunction srcFun) { /* NB: Keep this in sync with XDRInterpretedFunction. */ RootedObject cloneProto(cx); @@ -3449,7 +3478,7 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle } bool -js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, +js::detail::CopyScript(JSContext* cx, Handle scriptStaticScope, HandleScript src, HandleScript dst) { if (src->treatAsRunOnce() && !src->functionNonDelazifying()) { @@ -3496,14 +3525,15 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri if (obj->is()) { Rooted innerBlock(cx, &obj->as()); - RootedObject enclosingScope(cx); + Rooted enclosingScope(cx); if (NestedStaticScope* enclosingBlock = innerBlock->enclosingNestedScope()) { if (IsStaticGlobalLexicalScope(enclosingBlock)) { MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) || scriptStaticScope->is()); enclosingScope = scriptStaticScope; } else { - enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)]; + enclosingScope = &objects[FindScopeObjectIndex(src, *enclosingBlock)] + .get()->as(); } } else { enclosingScope = scriptStaticScope; @@ -3525,13 +3555,14 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri if (!innerFun->getOrCreateScript(cx)) return false; } - RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope()); + Rooted staticScope(cx, innerFun->nonLazyScript() + ->enclosingStaticScope()); StaticScopeIter ssi(cx, staticScope); - RootedObject enclosingScope(cx); + Rooted enclosingScope(cx); if (ssi.done() || ssi.type() == StaticScopeIter::NonSyntactic) { enclosingScope = scriptStaticScope; } else if (ssi.type() == StaticScopeIter::Function) { - MOZ_ASSERT(scriptStaticScope->is()); + MOZ_ASSERT(scriptStaticScope->is()); enclosingScope = scriptStaticScope; } else if (ssi.type() == StaticScopeIter::Block) { if (ssi.block().isGlobal()) { @@ -3539,10 +3570,12 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri scriptStaticScope->is()); enclosingScope = scriptStaticScope; } else { - enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())]; + enclosingScope = &objects[FindScopeObjectIndex(src, ssi.block())] + .get()->as(); } } else { - enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())]; + enclosingScope = &objects[FindScopeObjectIndex(src, ssi.staticWith())] + .get()->as(); } clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun); @@ -3656,7 +3689,7 @@ js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScri } static JSScript* -CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src) +CreateEmptyScriptForClone(JSContext* cx, Handle enclosingScope, HandleScript src) { /* * Wrap the script source object as needed. Self-hosted scripts may be @@ -3708,18 +3741,23 @@ js::CloneGlobalScript(JSContext* cx, Handle enclosingScope, Handle } JSScript* -js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, +js::CloneScriptIntoFunction(JSContext* cx, Handle enclosingScope, HandleFunction fun, HandleScript src) { MOZ_ASSERT(fun->isInterpreted()); + Rooted funScope(cx, StaticFunctionScope::create(cx, fun, + enclosingScope)); + if (!funScope) + return nullptr; + // Allocate the destination script up front and set it as the script of // |fun|, which is to be its container. // // This is so that when cloning nested functions, they can walk the static // scope chain via fun and correctly compute the presence of a // non-syntactic global. - RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src)); + RootedScript dst(cx, CreateEmptyScriptForClone(cx, funScope, src)); if (!dst) return nullptr; @@ -3735,7 +3773,7 @@ js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFu fun->initScript(dst); } - if (!detail::CopyScript(cx, fun, src, dst)) { + if (!detail::CopyScript(cx, funScope, src, dst)) { if (lazy) fun->initLazyScript(lazy); else @@ -3980,8 +4018,8 @@ JSScript::traceChildren(JSTracer* trc) if (module_) TraceEdge(trc, &module_, "module"); - if (enclosingStaticScope_) - TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope"); + if (staticScope_) + TraceEdge(trc, &staticScope_, "staticScope"); if (maybeLazyScript()) TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript"); @@ -4083,20 +4121,18 @@ JSScript::getStaticBlockScope(jsbytecode* pc) return blockChain; } -JSObject* +StaticScope* JSScript::innermostStaticScopeInScript(jsbytecode* pc) { - if (JSObject* scope = getStaticBlockScope(pc)) + if (NestedStaticScope* scope = getStaticBlockScope(pc)) return scope; - if (module()) - return module(); - return functionNonDelazifying(); + return staticScope_; } -JSObject* +StaticScope* JSScript::innermostStaticScope(jsbytecode* pc) { - if (JSObject* scope = innermostStaticScopeInScript(pc)) + if (StaticScope* scope = innermostStaticScopeInScript(pc)) return scope; return enclosingStaticScope(); } @@ -4141,8 +4177,13 @@ js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame, // Note that here and below, it is insufficient to only check for // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the // arguments slot. - if (IsOptimizedPlaceholderMagicValue(frame.callObj().as().aliasedVar(ScopeCoordinate(pc)))) - frame.callObj().as().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj)); + if (IsOptimizedPlaceholderMagicValue(frame.callObj().as() + .aliasedVar(ScopeCoordinate(pc)))) + { + frame.callObj().as().setAliasedVar(cx, ScopeCoordinate(pc), + cx->names().arguments, + ObjectValue(*argsobj)); + } } else { if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(bi.frameIndex()))) frame.unaliasedLocal(bi.frameIndex()) = ObjectValue(*argsobj); @@ -4240,10 +4281,12 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot) return argsObjAliasesFormals() && !formalIsAliased(argSlot); } -LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) +LazyScript::LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table, + uint64_t packedFields, uint32_t begin, uint32_t end, + uint32_t lineno, uint32_t column) : script_(nullptr), function_(fun), - enclosingScope_(nullptr), + staticScope_(funScope), sourceObject_(nullptr), table_(table), packedFields_(packedFields), @@ -4271,13 +4314,10 @@ LazyScript::resetScript() } void -LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject) +LazyScript::initSource(ScriptSourceObject* sourceObject) { - MOZ_ASSERT(!sourceObject_ && !enclosingScope_); - MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment()); + MOZ_ASSERT(!sourceObject_); MOZ_ASSERT(function_->compartment() == sourceObject->compartment()); - - enclosingScope_ = enclosingScope; sourceObject_ = sourceObject; } @@ -4295,6 +4335,7 @@ LazyScript::maybeForwardedScriptSource() const /* static */ LazyScript* LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, + Handle funScope, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) { @@ -4324,11 +4365,12 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, cx->compartment()->scheduleDelazificationForDebugger(); - return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column); + return new (res) LazyScript(fun, funScope, table.forget(), packed, begin, end, lineno, column); } /* static */ LazyScript* LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, + Handle funScope, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) { @@ -4349,14 +4391,15 @@ LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun, p.isDerivedClassConstructor = false; p.needsHomeObject = false; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields, + begin, end, lineno, column); MOZ_ASSERT_IF(res, res->version() == version); return res; } /* static */ LazyScript* LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, - HandleScript script, HandleObject enclosingScope, + HandleScript script, Handle funScope, HandleScript sourceObjectScript, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) @@ -4368,7 +4411,8 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, // holding this lazy script. HandleFunction dummyFun = fun; - LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields, + begin, end, lineno, column); if (!res) return nullptr; @@ -4386,7 +4430,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, // Set the enclosing scope of the lazy function, this would later be // used to define the environment when the function would be used. MOZ_ASSERT(!res->sourceObject()); - res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap()); + res->initSource(&sourceObjectScript->scriptSourceUnwrap()); MOZ_ASSERT(!res->hasScript()); if (script) @@ -4419,10 +4463,10 @@ LazyScript::hasUncompiledEnclosingScript() const // If the enclosing scope is a function with a null script or has a script // without code, it was not successfully compiled. - if (!enclosingScope() || !enclosingScope()->is()) + if (!enclosingScope() || !enclosingScope()->is()) return false; - JSFunction& fun = enclosingScope()->as(); + JSFunction& fun = enclosingScope()->as().function(); return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code(); } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index b13598cbe0a..e70ec4f8171 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -50,6 +50,7 @@ class LazyScript; class ModuleObject; class NestedStaticScope; class StaticScope; +class StaticFunctionScope; class RegExpObject; struct SourceCompressionTask; class Shape; @@ -66,7 +67,8 @@ namespace detail { // Do not call this directly! It is exposed for the friend declarations in // this file. bool -CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst); +CopyScript(JSContext* cx, Handle scriptStaticScope, HandleScript src, + HandleScript dst); } // namespace detail @@ -146,7 +148,7 @@ struct BlockScopeArray { class YieldOffsetArray { friend bool - detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, + detail::CopyScript(JSContext* cx, Handle scriptStaticScope, HandleScript src, HandleScript dst); uint32_t* vector_; // Array of bytecode offsets. @@ -920,20 +922,20 @@ GeneratorKindFromBits(unsigned val) { * subsequent set-up of owning function or script object and then call * CallNewScriptHook. */ -template +template bool -XDRScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, - HandleFunction fun, MutableHandleScript scriptp); +XDRScript(XDRState* xdr, Handle enclosingScope, + HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp); -template +template bool -XDRLazyScript(XDRState* xdr, HandleObject enclosingScope, HandleScript enclosingScript, - HandleFunction fun, MutableHandle lazy); +XDRLazyScript(XDRState* xdr, Handle enclosingScope, + HandleScript enclosingScript, HandleFunction fun, MutableHandle lazy); /* * Code any constant value. */ -template +template bool XDRScriptConst(XDRState* xdr, MutableHandleValue vp); @@ -944,13 +946,13 @@ class JSScript : public js::gc::TenuredCell template friend bool - js::XDRScript(js::XDRState* xdr, js::HandleObject enclosingScope, + js::XDRScript(js::XDRState* xdr, js::Handle enclosingScope, js::HandleScript enclosingScript, js::HandleFunction fun, js::MutableHandleScript scriptp); friend bool - js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src, - js::HandleScript dst); + js::detail::CopyScript(JSContext* cx, js::Handle scriptStaticScope, + js::HandleScript src, js::HandleScript dst); public: // @@ -1004,7 +1006,22 @@ class JSScript : public js::gc::TenuredCell js::HeapPtrFunction function_; js::HeapPtr module_; - js::HeapPtrObject enclosingStaticScope_; + + // The static scope this script runs in. + // + // Specifically, it depends on the case: + // + // * direct eval: staticScope_ is the StaticEvalScope for the eval call. + // + // * function script: staticScope_ is function_'s StaticFunctionScope. + // + // * module script: staticScope_ is module_'s StaticModuleScope. + // + // * plain old global script or indirect eval: staticScope_ is the static + // global lexical scope (regardless of whether the script uses any + // global lexical bindings). + // + js::HeapPtr staticScope_; /* * Information attached by Ion. Nexto a valid IonScript this could be @@ -1045,11 +1062,10 @@ class JSScript : public js::gc::TenuredCell uint32_t sourceStart_; uint32_t sourceEnd_; - uint32_t warmUpCount; /* Number of times the script has been called - * or has had backedges taken. When running in - * ion, also increased for any inlined scripts. - * Reset if the script's JIT code is forcibly - * discarded. */ + // Number of times the script has been called or has had backedges taken. + // When running in ion, also increased for any inlined scripts. Reset if + // the script's JIT code is forcibly discarded. + mozilla::Atomic warmUpCount; // 16-bit fields. @@ -1214,7 +1230,7 @@ class JSScript : public js::gc::TenuredCell public: static JSScript* Create(js::ExclusiveContext* cx, - js::HandleObject enclosingScope, bool savedCallerFun, + js::Handle staticScope, bool savedCallerFun, const JS::ReadOnlyCompileOptions& options, js::HandleObject sourceObject, uint32_t sourceStart, uint32_t sourceEnd); @@ -1699,10 +1715,13 @@ class JSScript : public js::gc::TenuredCell inline js::GlobalObject& global() const; js::GlobalObject& uninlinedGlobal() const; - /* See StaticScopeIter comment. */ - JSObject* enclosingStaticScope() const { - return enclosingStaticScope_; - } + js::StaticScope* staticScope() const { return staticScope_; } + + /* + * The static scope this script runs in, skipping the StaticFunctionScope + * if this is a non-eval function script. + */ + inline js::StaticScope* enclosingStaticScope() const; // Switch the script over from the off-thread compartment's static // global lexical scope to the main thread compartment's. @@ -1714,7 +1733,7 @@ class JSScript : public js::gc::TenuredCell public: uint32_t getWarmUpCount() const { return warmUpCount; } uint32_t incWarmUpCounter(uint32_t amount = 1) { return warmUpCount += amount; } - uint32_t* addressOfWarmUpCounter() { return &warmUpCount; } + uint32_t* addressOfWarmUpCounter() { return reinterpret_cast(&warmUpCount); } static size_t offsetOfWarmUpCounter() { return offsetof(JSScript, warmUpCount); } void resetWarmUpCounter() { incWarmUpResetCounter(); warmUpCount = 0; } @@ -1873,13 +1892,13 @@ class JSScript : public js::gc::TenuredCell // Returns the innermost static scope at pc if it falls within the extent // of the script. Returns nullptr otherwise. - JSObject* innermostStaticScopeInScript(jsbytecode* pc); + js::StaticScope* innermostStaticScopeInScript(jsbytecode* pc); // As innermostStaticScopeInScript, but returns the enclosing static scope // if the innermost static scope falls without the extent of the script. - JSObject* innermostStaticScope(jsbytecode* pc); + js::StaticScope* innermostStaticScope(jsbytecode* pc); - JSObject* innermostStaticScope() { return innermostStaticScope(main()); } + js::StaticScope* innermostStaticScope() { return innermostStaticScope(main()); } /* * The isEmpty method tells whether this script has code that computes any @@ -2142,11 +2161,12 @@ class LazyScript : public gc::TenuredCell // Original function with which the lazy script is associated. HeapPtrFunction function_; - // Function or block chain in which the script is nested, or nullptr. - HeapPtrObject enclosingScope_; + // Static scope of this function. (All lazy scripts are for functions; + // global scripts and eval scripts are never lazified.) + HeapPtr staticScope_; - // ScriptSourceObject, or nullptr if the script in which this is nested - // has not been compiled yet. This is never a CCW; we don't clone + // ScriptSourceObject. We leave this set to nullptr until we generate + // bytecode for our immediate parent. This is never a CCW; we don't clone // LazyScripts into other compartments. HeapPtrObject sourceObject_; @@ -2195,13 +2215,14 @@ class LazyScript : public gc::TenuredCell uint32_t lineno_; uint32_t column_; - LazyScript(JSFunction* fun, void* table, uint64_t packedFields, + LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); // Create a LazyScript without initializing the freeVariables and the // innerFunctions. To be GC-safe, the caller must initialize both vectors // with valid atoms and functions. static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun, + Handle funScope, uint64_t packedData, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); @@ -2210,6 +2231,7 @@ class LazyScript : public gc::TenuredCell // innerFunctions. To be GC-safe, the caller must initialize both vectors // with valid atoms and functions. static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun, + Handle funScope, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); @@ -2224,7 +2246,7 @@ class LazyScript : public gc::TenuredCell // The sourceObjectScript argument must be non-null and is the script that // should be used to get the sourceObject_ of this lazyScript. static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun, - HandleScript script, HandleObject enclosingScope, + HandleScript script, Handle funScope, HandleScript sourceObjectScript, uint64_t packedData, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); @@ -2249,9 +2271,8 @@ class LazyScript : public gc::TenuredCell return bool(script_); } - JSObject* enclosingScope() const { - return enclosingScope_; - } + StaticFunctionScope* staticScope() const { return staticScope_; } + StaticScope* enclosingScope() const; // Switch the script over from the off-thread compartment's static // global lexical scope to the main thread compartment's. @@ -2270,7 +2291,7 @@ class LazyScript : public gc::TenuredCell return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version); } - void setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject); + void initSource(ScriptSourceObject* sourceObject); uint32_t numFreeVariables() const { return p_.numFreeVariables; @@ -2530,7 +2551,7 @@ DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScr LineOption opt = NOT_CALLED_FROM_JSOP_EVAL); JSScript* -CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, +CloneScriptIntoFunction(JSContext* cx, Handle enclosingScope, HandleFunction fun, HandleScript src); JSScript* @@ -2542,7 +2563,7 @@ CloneGlobalScript(JSContext* cx, Handle enclosingScope, HandleScri // with no associated compartment. namespace JS { namespace ubi { -template<> +template <> struct Concrete : TracerConcrete { CoarseType coarseType() const final { return CoarseType::Script; } Size size(mozilla::MallocSizeOf mallocSizeOf) const override; diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index 31ff63e4f02..e1ebacd8c71 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -92,8 +92,23 @@ LazyScript::functionDelazifying(JSContext* cx) const return function_; } +inline StaticScope* +LazyScript::enclosingScope() const +{ + return staticScope_->enclosingScope(); +} + } // namespace js +inline js::StaticScope* +JSScript::enclosingStaticScope() const +{ + // The static scope of a function script is the function scope (which + // contains arguments and local variables). This method's callers want to + // skip that scope. + return function_ ? staticScope_->enclosingScope() : staticScope_; +} + inline JSFunction* JSScript::functionDelazifying() const { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 81c85c02127..475bd9ae024 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -144,6 +144,7 @@ struct ShellRuntime bool isWorker; double timeoutInterval; Atomic serviceInterrupt; + Atomic haveInterruptFunc; JS::PersistentRootedValue interruptFunc; bool lastWarningEnabled; JS::PersistentRootedValue lastWarning; @@ -290,6 +291,7 @@ ShellRuntime::ShellRuntime() : isWorker(false), timeoutInterval(-1.0), serviceInterrupt(false), + haveInterruptFunc(false), lastWarningEnabled(false), watchdogLock(nullptr), watchdogWakeup(nullptr), @@ -430,12 +432,11 @@ ShellInterruptCallback(JSContext* cx) sr->serviceInterrupt = false; bool result; - RootedValue interruptFunc(cx, sr->interruptFunc); - if (!interruptFunc.isNull()) { + if (sr->haveInterruptFunc) { JS::AutoSaveExceptionState savedExc(cx); - JSAutoCompartment ac(cx, &interruptFunc.toObject()); + JSAutoCompartment ac(cx, &sr->interruptFunc.toObject()); RootedValue rval(cx); - if (!JS_CallFunctionValue(cx, nullptr, interruptFunc, + if (!JS_CallFunctionValue(cx, nullptr, sr->interruptFunc, JS::HandleValueArray::empty(), &rval)) { return false; @@ -3126,7 +3127,7 @@ CancelExecution(JSRuntime* rt) sr->serviceInterrupt = true; JS_RequestInterruptCallback(rt); - if (!sr->interruptFunc.isNull()) { + if (sr->haveInterruptFunc) { static const char msg[] = "Script runs for too long, terminating.\n"; fputs(msg, stderr); } @@ -3175,6 +3176,7 @@ Timeout(JSContext* cx, unsigned argc, Value* vp) return false; } sr->interruptFunc = value; + sr->haveInterruptFunc = true; } args.rval().setUndefined(); @@ -3246,6 +3248,7 @@ SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) return false; } GetShellRuntime(cx)->interruptFunc = value; + GetShellRuntime(cx)->haveInterruptFunc = true; args.rval().setUndefined(); return true; @@ -4666,6 +4669,8 @@ DumpStaticScopeChain(JSContext* cx, unsigned argc, Value* vp) return false; } script = fun->getOrCreateScript(cx); + if (!script) + return false; } else { script = obj->as().script(); } diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index cd5d41996f0..f9f832e5ac3 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -19,6 +19,7 @@ #include "jswrapper.h" #include "frontend/BytecodeCompiler.h" +#include "frontend/Parser.h" #include "gc/Marking.h" #include "jit/BaselineDebugModeOSR.h" #include "jit/BaselineJIT.h" @@ -4522,6 +4523,53 @@ Debugger::endTraceLogger(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args[0].isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_NOT_EXPECTED_TYPE, "Debugger.isCompilableUnit", + "string", InformalValueTypeName(args[0])); + return false; + } + + JSString* str = args[0].toString(); + size_t length = GetStringLength(str); + + AutoStableStringChars chars(cx); + if (!chars.initTwoByte(cx, str)) + return false; + + bool result = true; + + CompileOptions options(cx); + frontend::Parser parser(cx, &cx->tempLifoAlloc(), + options, chars.twoByteChars(), + length, /* foldConstants = */ true, + nullptr, nullptr); + JSErrorReporter older = JS_SetErrorReporter(cx->runtime(), nullptr); + if (!parser.checkOptions() || !parser.parse()) { + // We ran into an error. If it was because we ran out of memory we report + // it in the usual way. + if (cx->isThrowingOutOfMemory()) { + JS_SetErrorReporter(cx->runtime(), older); + return false; + } + + // If it was because we ran out of source, we return false so our caller + // knows to try to collect more [source]. + if (parser.isUnexpectedEOF()) + result = false; + + cx->clearPendingException(); + } + JS_SetErrorReporter(cx->runtime(), older); + args.rval().setBoolean(result); + return true; +} + bool Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp) { @@ -4643,7 +4691,11 @@ const JSFunctionSpec Debugger::methods[] = { JS_FS_END }; - +const JSFunctionSpec Debugger::static_methods[] { + JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0), + JS_FS_END +}; + /*** Debugger.Script *****************************************************************************/ static inline JSScript* @@ -8415,8 +8467,8 @@ JS_DefineDebuggerObject(JSContext* cx, HandleObject obj) return false; debugProto = InitClass(cx, obj, objProto, &Debugger::jsclass, Debugger::construct, - 1, Debugger::properties, Debugger::methods, nullptr, nullptr, - debugCtor.address()); + 1, Debugger::properties, Debugger::methods, nullptr, + Debugger::static_methods, debugCtor.address()); if (!debugProto) return false; diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 1acd3e1ec69..bee5e81741f 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -564,6 +564,7 @@ class Debugger : private mozilla::LinkedListElement static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp); static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp); static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp); + static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp); #ifdef NIGHTLY_BUILD static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp); static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp); @@ -571,6 +572,7 @@ class Debugger : private mozilla::LinkedListElement static bool construct(JSContext* cx, unsigned argc, Value* vp); static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; + static const JSFunctionSpec static_methods[]; static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame); static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index bfdbcc8aa97..8ee25af3c2f 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -927,9 +927,8 @@ js::TypeOfValue(const Value& v) */ bool js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, - HandleObject staticWith) + Handle staticWith) { - MOZ_ASSERT(staticWith->is()); RootedObject obj(cx); if (val.isObject()) { obj = &val.toObject(); @@ -1039,8 +1038,8 @@ SettleOnTryNote(JSContext* cx, JSTryNote* tn, ScopeIter& si, InterpreterRegs& re // Unwind the scope to the beginning of the JSOP_TRY. UnwindScope(cx, si, UnwindScopeToTryPc(regs.fp()->script(), tn)); - // Set pc to the first bytecode after the the try note to point - // to the beginning of catch or finally. + // Set pc to the first bytecode after the span of the try note, the + // beginning of the first catch or finally block. regs.pc = regs.fp()->script()->main() + tn->start + tn->length; regs.sp = regs.spForStackDepth(tn->stackDepth); } @@ -1494,6 +1493,9 @@ class ReservedRooted : public ReservedRootedBase operator Rooted&() { return *savedRoot; } MutableHandle operator&() { return &*savedRoot; } + template + Handle as() { return savedRoot->template as(); } + DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get()) DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get()) DECLARE_POINTER_CONSTREF_OPS(T) @@ -1873,7 +1875,7 @@ CASE(JSOP_ENTERWITH) REGS.sp--; ReservedRooted staticWith(&rootObject0, script->getObject(REGS.pc)); - if (!EnterWithOperation(cx, REGS.fp(), val, staticWith)) + if (!EnterWithOperation(cx, REGS.fp(), val, staticWith.as())) goto error; } END_CASE(JSOP_ENTERWITH) diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 7fef263104f..8185d160f19 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -21,6 +21,7 @@ namespace js { class ScopeIter; +class StaticWithScope; /* * For a given |call|, convert null/undefined |this| into the global object for @@ -428,8 +429,8 @@ unsigned GetInitDataPropAttrs(JSOp op); bool -EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith); - +EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, + Handle staticWith); bool InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval, diff --git a/js/src/vm/ScopeObject-inl.h b/js/src/vm/ScopeObject-inl.h index 7db235859b4..3b523db45fa 100644 --- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -84,49 +84,39 @@ template inline void StaticScopeIter::operator++(int) { - if (obj->template is()) { - obj = obj->template as().enclosingScope(); - } else if (obj->template is()) { - obj = obj->template as().enclosingScope(); - } else if (obj->template is()) { - obj = obj->template as().enclosingScope(); - } else if (obj->template is()) { - obj = obj->template as().enclosingStaticScope(); - } else if (onNamedLambda || !obj->template as().isNamedLambda()) { + if (!scope->template is()) { + scope = scope->enclosingScope(); + } else if (onNamedLambda || !scope->template as().isNamedLambda()) { onNamedLambda = false; - JSFunction& fun = obj->template as(); - if (fun.isBeingParsed()) - obj = fun.functionBox()->enclosingStaticScope(); - else - obj = fun.nonLazyScript()->enclosingStaticScope(); + scope = scope->enclosingScope(); } else { onNamedLambda = true; } - MOZ_ASSERT_IF(obj, IsStaticScope(obj)); - MOZ_ASSERT_IF(onNamedLambda, obj->template is()); + MOZ_ASSERT_IF(scope, scope->template is()); + MOZ_ASSERT_IF(onNamedLambda, scope->template is()); } template inline bool StaticScopeIter::hasSyntacticDynamicScopeObject() const { - if (obj->template is()) { - JSFunction& fun = obj->template as(); + if (scope->template is()) { + JSFunction& fun = scope->template as().function(); if (fun.isBeingParsed()) return fun.functionBox()->needsCallObject(); return fun.needsCallObject(); } - if (obj->template is()) + if (scope->template is()) return true; - if (obj->template is()) { - return obj->template as().needsClone() || - obj->template as().isGlobal(); + if (scope->template is()) { + return scope->template as().needsClone() || + scope->template as().isGlobal(); } - if (obj->template is()) + if (scope->template is()) return true; - if (obj->template is()) - return obj->template as().isStrict(); - MOZ_ASSERT(obj->template is()); + if (scope->template is()) + return scope->template as().isStrict(); + MOZ_ASSERT(scope->template is()); return false; } @@ -139,8 +129,8 @@ StaticScopeIter::scopeShape() const if (type() == Block) return block().lastProperty(); if (type() == Module) - return moduleScript()->callObjShape(); - return funScript()->callObjShape(); + return module().environmentShape(); + return fun().environmentShape(); } template @@ -149,17 +139,17 @@ StaticScopeIter::type() const { if (onNamedLambda) return NamedLambda; - if (obj->template is()) + if (scope->template is()) return Block; - if (obj->template is()) - return With; - if (obj->template is()) - return Eval; - if (obj->template is()) - return NonSyntactic; - if (obj->template is()) + if (scope->template is()) return Module; - MOZ_ASSERT(obj->template is()); + if (scope->template is()) + return With; + if (scope->template is()) + return Eval; + if (scope->template is()) + return NonSyntactic; + MOZ_ASSERT(scope->template is()); return Function; } @@ -168,7 +158,7 @@ inline StaticBlockScope& StaticScopeIter::block() const { MOZ_ASSERT(type() == Block); - return obj->template as(); + return scope->template as(); } template @@ -176,7 +166,7 @@ inline StaticWithScope& StaticScopeIter::staticWith() const { MOZ_ASSERT(type() == With); - return obj->template as(); + return scope->template as(); } template @@ -184,7 +174,7 @@ inline StaticEvalScope& StaticScopeIter::eval() const { MOZ_ASSERT(type() == Eval); - return obj->template as(); + return scope->template as(); } template @@ -192,7 +182,7 @@ inline StaticNonSyntacticScope& StaticScopeIter::nonSyntactic() const { MOZ_ASSERT(type() == NonSyntactic); - return obj->template as(); + return scope->template as(); } template @@ -200,15 +190,15 @@ inline JSScript* StaticScopeIter::funScript() const { MOZ_ASSERT(type() == Function); - return obj->template as().nonLazyScript(); + return scope->template as().function().nonLazyScript(); } template -inline JSFunction& +inline StaticFunctionScope& StaticScopeIter::fun() const { MOZ_ASSERT(type() == Function); - return obj->template as(); + return scope->template as(); } template @@ -216,25 +206,17 @@ inline frontend::FunctionBox* StaticScopeIter::maybeFunctionBox() const { MOZ_ASSERT(type() == Function); - if (fun().isBeingParsed()) - return fun().functionBox(); + if (fun().function().isBeingParsed()) + return fun().function().functionBox(); return nullptr; } template -inline JSScript* -StaticScopeIter::moduleScript() const -{ - MOZ_ASSERT(type() == Module); - return obj->template as().script(); -} - -template -inline ModuleObject& +inline StaticModuleScope& StaticScopeIter::module() const { MOZ_ASSERT(type() == Module); - return obj->template as(); + return scope->template as(); } } /* namespace js */ @@ -251,7 +233,8 @@ JSObject::enclosingScope() if (is()) return nullptr; - MOZ_ASSERT_IF(is(), as().isInterpreted()); + MOZ_ASSERT(!is()); + MOZ_ASSERT(!is()); return &global(); } diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 7405336b9a5..4ae250452ab 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -39,10 +39,10 @@ typedef MutableHandle MutableHandleArgumentsObject; /*** Static scope objects ************************************************************************/ void -StaticScope::setEnclosingScope(HandleObject obj) +StaticScope::setEnclosingScope(StaticScope* scope) { - MOZ_ASSERT_IF(obj->is(), obj->isDelegate()); - setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*obj)); + MOZ_ASSERT_IF(scope->is(), scope->isDelegate()); + setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*scope)); } bool @@ -118,6 +118,63 @@ StaticBlockScope::addVar(ExclusiveContext* cx, Handle block, /* allowDictionary = */ false); } +const Class StaticFunctionScope::class_ = { + "StaticFunctionScope", + JSCLASS_HAS_RESERVED_SLOTS(StaticFunctionScope::RESERVED_SLOTS) | + JSCLASS_IS_ANONYMOUS +}; + +StaticFunctionScope* +StaticFunctionScope::create(ExclusiveContext* cx, Handle functionObject, + Handle enclosingScope) +{ + Rooted proto(cx, TaggedProto(nullptr)); + JSObject* obj = NewObjectWithGivenTaggedProto(cx, &class_, proto, TenuredObject, + BaseShape::DELEGATE); + if (!obj) + return nullptr; + + StaticFunctionScope* scope = &obj->as(); + scope->initEnclosingScope(enclosingScope); + scope->setReservedSlot(FUNCTION_OBJECT_SLOT, ObjectValue(*functionObject)); + return scope; +} + +StaticModuleScope* +StaticModuleScope::create(ExclusiveContext* cx, Handle moduleObject, + Handle enclosingScope) +{ + Rooted nullProto(cx, TaggedProto(nullptr)); + JSObject* obj = NewObjectWithGivenTaggedProto(cx, &ModuleEnvironmentObject::class_, nullProto, + TenuredObject, BaseShape::DELEGATE); + if (!obj) + return nullptr; + + StaticModuleScope* scope = &obj->as(); + scope->initEnclosingScope(enclosingScope); + scope->setReservedSlot(MODULE_OBJECT_SLOT, ObjectValue(*moduleObject)); + return scope; +} + +ModuleObject& +StaticModuleScope::moduleObject() +{ + return getReservedSlot(MODULE_OBJECT_SLOT).toObject().as(); +} + +JSScript* +StaticModuleScope::script() +{ + return moduleObject().script(); +} + +Shape* +StaticModuleScope::environmentShape() +{ + ModuleObject* module = &getReservedSlot(MODULE_OBJECT_SLOT).toObject().as(); + return module->script()->bindings.callObjShape(); +} + const Class StaticWithScope::class_ = { "WithTemplate", JSCLASS_HAS_RESERVED_SLOTS(StaticWithScope::RESERVED_SLOTS) | @@ -132,7 +189,7 @@ StaticWithScope::create(ExclusiveContext* cx) template bool -js::XDRStaticWithScope(XDRState* xdr, HandleObject enclosingScope, +js::XDRStaticWithScope(XDRState* xdr, Handle enclosingScope, MutableHandle objp) { if (mode == XDR_DECODE) { @@ -151,10 +208,12 @@ js::XDRStaticWithScope(XDRState* xdr, HandleObject enclosingScope, } template bool -js::XDRStaticWithScope(XDRState*, HandleObject, MutableHandle); +js::XDRStaticWithScope(XDRState*, Handle, + MutableHandle); template bool -js::XDRStaticWithScope(XDRState*, HandleObject, MutableHandle); +js::XDRStaticWithScope(XDRState*, Handle, + MutableHandle); /*****************************************************************************/ @@ -440,6 +499,9 @@ const Class CallObject::class_ = { /*****************************************************************************/ +static_assert(StaticModuleScope::RESERVED_SLOTS == ModuleEnvironmentObject::RESERVED_SLOTS, + "static module scopes and dynamic module environments share a Class"); + const Class ModuleEnvironmentObject::class_ = { "ModuleEnvironmentObject", JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) | @@ -480,7 +542,8 @@ ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module) RootedShape shape(cx, script->bindings.callObjShape()); MOZ_ASSERT(shape->getObjectClass() == &class_); - RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); + Rooted proto(cx, module->staticScope()); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, proto)); if (!group) return nullptr; @@ -525,6 +588,12 @@ ModuleEnvironmentObject::module() return getReservedSlot(MODULE_SLOT).toObject().as(); } +StaticModuleScope& +ModuleEnvironmentObject::staticScope() +{ + return getProto()->as(); +} + IndirectBindingMap& ModuleEnvironmentObject::importBindings() { @@ -708,8 +777,9 @@ DeclEnvObject::create(JSContext* cx, HandleObject enclosing, HandleFunction call return obj; } -static JSObject* -CloneStaticWithScope(JSContext* cx, HandleObject enclosingScope, Handle srcWith) +static StaticWithScope* +CloneStaticWithScope(JSContext* cx, Handle enclosingScope, + Handle srcWith) { Rooted clone(cx, StaticWithScope::create(cx)); if (!clone) @@ -722,10 +792,8 @@ CloneStaticWithScope(JSContext* cx, HandleObject enclosingScope, Handle staticWith, WithKind kind) { - MOZ_ASSERT(staticWith->is()); - Rooted proto(cx, TaggedProto(staticWith)); Rooted obj(cx); obj = NewObjectWithGivenTaggedProto(cx, proto, GenericObject, @@ -848,16 +916,16 @@ const Class DynamicWithObject::class_ = { }; /* static */ StaticEvalScope* -StaticEvalScope::create(JSContext* cx, HandleObject enclosing) +StaticEvalScope::create(JSContext* cx, Handle enclosing) { - StaticEvalScope* obj = + StaticEvalScope* scope = NewObjectWithNullTaggedProto(cx, TenuredObject, BaseShape::DELEGATE); - if (!obj) + if (!scope) return nullptr; - obj->setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(enclosing)); - obj->setReservedSlot(STRICT_SLOT, BooleanValue(false)); - return obj; + scope->initEnclosingScope(enclosing); + scope->setReservedSlot(STRICT_SLOT, BooleanValue(false)); + return scope; } const Class StaticEvalScope::class_ = { @@ -981,10 +1049,10 @@ ClonedBlockObject::createGlobal(JSContext* cx, Handle global) } /* static */ ClonedBlockObject* -ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic, +ClonedBlockObject::createNonSyntactic(JSContext* cx, + Handle enclosingStatic, HandleObject enclosingScope) { - MOZ_ASSERT(enclosingStatic->is()); MOZ_ASSERT(!IsSyntacticScope(enclosingScope)); Rooted staticLexical(cx, StaticBlockScope::create(cx)); @@ -1103,7 +1171,7 @@ const Class ClonedBlockObject::class_ = { template bool -js::XDRStaticBlockScope(XDRState* xdr, HandleObject enclosingScope, +js::XDRStaticBlockScope(XDRState* xdr, Handle enclosingScope, MutableHandle objp) { /* NB: Keep this in sync with CloneStaticBlockScope. */ @@ -1209,13 +1277,16 @@ js::XDRStaticBlockScope(XDRState* xdr, HandleObject enclosingScope, } template bool -js::XDRStaticBlockScope(XDRState*, HandleObject, MutableHandle); +js::XDRStaticBlockScope(XDRState*, Handle, + MutableHandle); template bool -js::XDRStaticBlockScope(XDRState*, HandleObject, MutableHandle); +js::XDRStaticBlockScope(XDRState*, Handle, + MutableHandle); -static JSObject* -CloneStaticBlockScope(JSContext* cx, HandleObject enclosingScope, Handle srcBlock) +static StaticBlockScope* +CloneStaticBlockScope(JSContext* cx, Handle enclosingScope, + Handle srcBlock) { /* NB: Keep this in sync with XDRStaticBlockScope. */ @@ -1256,16 +1327,16 @@ CloneStaticBlockScope(JSContext* cx, HandleObject enclosingScope, Handle enclosingScope, Handle srcBlock) { if (srcBlock->is()) { - Rooted blockScope(cx, &srcBlock->as()); - return CloneStaticBlockScope(cx, enclosingScope, blockScope); + return CloneStaticBlockScope(cx, enclosingScope, + HandleObject(srcBlock).as()); } else { - Rooted withScope(cx, &srcBlock->as()); - return CloneStaticWithScope(cx, enclosingScope, withScope); + return CloneStaticWithScope(cx, enclosingScope, + HandleObject(srcBlock).as()); } } @@ -1391,7 +1462,7 @@ ScopeIter::ScopeIter(JSContext* cx, const ScopeIter& si MOZ_GUARD_OBJECT_NOTIFIER_INIT; } -ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope +ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : ssi_(cx, staticScope), scope_(cx, scope), @@ -1476,7 +1547,7 @@ ScopeIter::settle() if (!ssi_.done() && hasAnyScopeObject()) { switch (ssi_.type()) { case StaticScopeIter::Module: - MOZ_ASSERT(scope_->as().module() == ssi_.module()); + MOZ_ASSERT(scope_->as().staticScope() == ssi_.module()); break; case StaticScopeIter::Function: MOZ_ASSERT(scope_->as().callee().nonLazyScript() == ssi_.funScript()); @@ -1547,7 +1618,7 @@ ScopeIter::scope() const return scope_->as(); } -JSObject* +StaticScope* ScopeIter::maybeStaticScope() const { if (ssi_.done()) @@ -1555,17 +1626,12 @@ ScopeIter::maybeStaticScope() const switch (ssi_.type()) { case StaticScopeIter::Function: - return &fun(); case StaticScopeIter::Module: - return &module(); case StaticScopeIter::Block: - return &staticBlock(); case StaticScopeIter::With: - return &staticWith(); case StaticScopeIter::Eval: - return &staticEval(); case StaticScopeIter::NonSyntactic: - return &staticNonSyntactic(); + return ssi_.staticScope(); case StaticScopeIter::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); default: @@ -2578,7 +2644,7 @@ DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& MOZ_ASSERT(!si.hasSyntacticScopeObject()); MOZ_ASSERT(cx->compartment() == debugScope.compartment()); // Generators should always reify their scopes. - MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator()); + MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().function().isGenerator()); if (!CanUseDebugScopeMaps(cx)) return true; @@ -2936,7 +3002,8 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) break; case ScopeIter::Call: { - RootedFunction callee(cx, &si.fun()); + RootedFunction callee(cx, &si.fun().function()); + // Generators should always reify their scopes. MOZ_ASSERT(!callee->isGenerator()); @@ -3093,7 +3160,7 @@ js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, // Construct With object wrappers for the things on this scope // chain and use the result as the thing to scope the function to. Rooted staticWith(cx); - RootedObject staticEnclosingScope(cx); + Rooted staticEnclosingScope(cx); Rooted dynamicWith(cx); RootedObject dynamicEnclosingScope(cx, dynamicTerminatingScope); for (size_t i = scopeChain.length(); i > 0; ) { @@ -3115,7 +3182,7 @@ js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, } bool -js::HasNonSyntacticStaticScopeChain(JSObject* staticScope) +js::HasNonSyntacticStaticScopeChain(StaticScope* staticScope) { for (StaticScopeIter ssi(staticScope); !ssi.done(); ssi++) { // If we hit a function scope, we can short circuit the logic, as @@ -3129,7 +3196,7 @@ js::HasNonSyntacticStaticScopeChain(JSObject* staticScope) } uint32_t -js::StaticScopeChainLength(JSObject* staticScope) +js::StaticScopeChainLength(StaticScope* staticScope) { uint32_t length = 0; for (StaticScopeIter ssi(staticScope); !ssi.done(); ssi++) @@ -3146,7 +3213,7 @@ js::GetModuleEnvironmentForScript(JSScript* script) if (ssi.done()) return nullptr; - return ssi.module().environment(); + return ssi.module().moduleObject().environment(); } bool @@ -3159,10 +3226,10 @@ js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr fra return true; } - if (si.type() != ScopeIter::Call || si.fun().hasLexicalThis()) + if (si.type() != ScopeIter::Call || si.fun().function().hasLexicalThis()) continue; - RootedScript script(cx, si.fun().nonLazyScript()); + RootedScript script(cx, si.fun().function().nonLazyScript()); if (!script->functionHasThisBinding()) { MOZ_ASSERT(!script->isDerivedClassConstructor(), @@ -3346,11 +3413,11 @@ js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script, void js::DumpStaticScopeChain(JSScript* script) { - DumpStaticScopeChain(script->enclosingStaticScope()); + DumpStaticScopeChain(script->staticScope()); } void -js::DumpStaticScopeChain(JSObject* staticScope) +js::DumpStaticScopeChain(StaticScope* staticScope) { for (StaticScopeIter ssi(staticScope); !ssi.done(); ssi++) { switch (ssi.type()) { @@ -3358,10 +3425,12 @@ js::DumpStaticScopeChain(JSObject* staticScope) fprintf(stdout, "module [%p]", &ssi.module()); break; case StaticScopeIter::Function: - if (ssi.fun().isBeingParsed()) - fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun()); - else - fprintf(stdout, "function [%p]", &ssi.fun()); + if (ssi.fun().function().isBeingParsed()) { + fprintf(stdout, "funbox [%p box=%p fun=%p]", + &ssi.fun(), ssi.maybeFunctionBox(), &ssi.fun().function()); + } else { + fprintf(stdout, "function [%p fun=%p]", &ssi.fun(), &ssi.fun().function()); + } break; case StaticScopeIter::Block: fprintf(stdout, "block [%p]", &ssi.block()); diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index a8109db504a..0de7f2f0c14 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -50,11 +50,11 @@ typedef Handle HandleModuleObject; * StaticBlockScope * Scope for non-function body blocks. e.g., |{ let x; }| * - * JSFunction + * StaticFunctionScope * Scope for function bodies. e.g., |function f() { var x; let y; }| * - * ModuleObject - * Scope for moddules. + * StaticModuleScope + * Scope for modules. * * StaticWithScope * Scope for |with|. e.g., |with ({}) { ... }| @@ -82,16 +82,14 @@ class StaticScope : public NativeObject static const uint32_t ENCLOSING_SCOPE_SLOT = 0; static const unsigned RESERVED_SLOTS = ENCLOSING_SCOPE_SLOT + 1; - inline JSObject* enclosingScope() const { - return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull(); - } + inline StaticScope* enclosingScope() const; - void initEnclosingScope(JSObject* obj) { + void initEnclosingScope(StaticScope* scope) { MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined()); - setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(obj)); + setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(scope)); } - void setEnclosingScope(HandleObject obj); + void setEnclosingScope(StaticScope* obj); }; class NestedStaticScope : public StaticScope @@ -109,7 +107,7 @@ class NestedStaticScope : public StaticScope * called separately in the emitter. 'reset' is just for asserting * stackiness. */ - void initEnclosingScopeFromParser(JSObject* prev) { + void initEnclosingScopeFromParser(StaticScope* prev) { setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(prev)); } @@ -163,11 +161,6 @@ class StaticBlockScope : public NestedStaticScope */ bool isExtensible() const; - /* See StaticScopeIter comment. */ - JSObject* enclosingStaticScope() const { - return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull(); - } - /* * Return the index (in the range [0, numVariables()) corresponding to the * given shape of a block object. @@ -180,7 +173,7 @@ class StaticBlockScope : public NestedStaticScope /* * A refinement of enclosingStaticScope that returns nullptr if the enclosing - * static scope is a JSFunction. + * static scope is a StaticFunctionScope. */ inline StaticBlockScope* enclosingBlock() const; @@ -213,7 +206,7 @@ class StaticBlockScope : public NestedStaticScope /* * A let binding is aliased if accessed lexically by nested functions or - * dynamically through dynamic name lookup (eval, with, function::, etc). + * dynamically through dynamic name lookup (eval, with, etc). */ bool isAliased(unsigned i) { return slotValue(i).isTrue(); @@ -232,7 +225,10 @@ class StaticBlockScope : public NestedStaticScope // Is this the static global lexical scope? bool isGlobal() const { - return !enclosingStaticScope(); + // This method is called from js::gc::MergeCompartments() on scopes + // that may be otherwise unreachable (!) and whose enclosing scope slot + // may be `undefined`. + return getFixedSlot(ENCLOSING_SCOPE_SLOT).isNull(); } bool isSyntactic() const { @@ -300,6 +296,45 @@ class StaticBlockScope : public NestedStaticScope bool constant, unsigned index, bool* redeclared); }; +class StaticFunctionScope : public StaticScope +{ + static const unsigned FUNCTION_OBJECT_SLOT = StaticScope::RESERVED_SLOTS; + + public: + static const unsigned RESERVED_SLOTS = FUNCTION_OBJECT_SLOT + 1; + static const Class class_; + + static StaticFunctionScope* create(ExclusiveContext* cx, HandleFunction functionObject, + Handle enclosingScope); + + JSFunction& function() { + return getFixedSlot(FUNCTION_OBJECT_SLOT).toObject().as(); + } + + bool isNamedLambda() { return function().isNamedLambda(); } + + Shape* environmentShape() { + return function().nonLazyScript()->callObjShape(); + } +}; + +// The top-level scope of a module. +// Shares ModuleEnvironmentObject::class_. +class StaticModuleScope : public StaticScope +{ + static const unsigned MODULE_OBJECT_SLOT = StaticScope::RESERVED_SLOTS; + + public: + static const unsigned RESERVED_SLOTS = MODULE_OBJECT_SLOT + 1; + + static StaticModuleScope* create(ExclusiveContext* cx, Handle moduleObject, + Handle enclosingScope); + + ModuleObject& moduleObject(); + JSScript* script(); + Shape* environmentShape(); +}; + // Represents the lexical scope of a 'with' statement. class StaticWithScope : public NestedStaticScope { @@ -311,7 +346,7 @@ class StaticWithScope : public NestedStaticScope template bool -XDRStaticWithScope(XDRState* xdr, HandleObject enclosingScope, +XDRStaticWithScope(XDRState* xdr, Handle enclosingScope, MutableHandle objp); /* @@ -327,11 +362,7 @@ class StaticEvalScope : public StaticScope public: static const Class class_; - static StaticEvalScope* create(JSContext* cx, HandleObject enclosing); - - JSObject* enclosingScopeForStaticScopeIter() { - return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull(); - } + static StaticEvalScope* create(JSContext* cx, Handle enclosing); void setStrict() { setReservedSlot(STRICT_SLOT, BooleanValue(true)); @@ -458,64 +489,51 @@ class StaticNonSyntacticScope : public StaticScope static const Class class_; static StaticNonSyntacticScope* create(JSContext* cx, HandleObject enclosing); - - JSObject* enclosingScopeForStaticScopeIter() { - return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull(); - } }; template class StaticScopeIter { - typename MaybeRooted::RootType obj; + typename MaybeRooted::RootType scope; bool onNamedLambda; - static bool IsStaticScope(JSObject* obj) { - return obj->is() || - obj->is() || - obj->is() || - obj->is() || - obj->is() || - obj->is(); - } - public: - StaticScopeIter(ExclusiveContext* cx, JSObject* obj) - : obj(cx, obj), onNamedLambda(false) + StaticScopeIter(ExclusiveContext* cx, StaticScope* scope) + : scope(cx, scope), onNamedLambda(false) { static_assert(allowGC == CanGC, "the context-accepting constructor should only be used " "in CanGC code"); - MOZ_ASSERT_IF(obj, IsStaticScope(obj)); + MOZ_ASSERT_IF(scope, scope->is()); } StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter& ssi) - : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda) + : scope(cx, ssi.scope), onNamedLambda(ssi.onNamedLambda) { JS_STATIC_ASSERT(allowGC == CanGC); } - explicit StaticScopeIter(JSObject* obj) - : obj((ExclusiveContext*) nullptr, obj), onNamedLambda(false) + explicit StaticScopeIter(StaticScope* scope) + : scope((ExclusiveContext*) nullptr, scope), onNamedLambda(false) { static_assert(allowGC == NoGC, "the constructor not taking a context should only be " "used in NoGC code"); - MOZ_ASSERT_IF(obj, IsStaticScope(obj)); + MOZ_ASSERT_IF(scope, scope->is()); } explicit StaticScopeIter(const StaticScopeIter& ssi) - : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda) + : scope((ExclusiveContext*) nullptr, ssi.scope), onNamedLambda(ssi.onNamedLambda) { static_assert(allowGC == NoGC, "the constructor not taking a context should only be " "used in NoGC code"); } - bool done() const { return !obj; } + bool done() const { return !scope; } void operator++(int); - JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; } + StaticScope* staticScope() const { MOZ_ASSERT(!done()); return scope; } // Return whether this static scope will have a syntactic scope (i.e. a // ScopeObject that isn't a non-syntactic With or @@ -527,14 +545,13 @@ class StaticScopeIter Type type() const; StaticBlockScope& block() const; + StaticModuleScope& module() const; StaticWithScope& staticWith() const; StaticEvalScope& eval() const; StaticNonSyntacticScope& nonSyntactic() const; + StaticFunctionScope& fun() const; JSScript* funScript() const; - JSFunction& fun() const; frontend::FunctionBox* maybeFunctionBox() const; - JSScript* moduleScript() const; - ModuleObject& module() const; }; @@ -602,42 +619,46 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc); * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of * scope objects is: * - * JSObject Generic object - * | | - * | StaticScope Created at compile time - * | | | | - * | | | StaticNonSyntacticScope See "Non-syntactic scopes" - * | | | - * | | StaticEvalScope Placeholder so eval scopes may be iterated through - * | | - * | NestedStaticScope Enclosing scope is in the same JSScript - * | | | - * | | StaticBlockScope See NB - * | | - * | StaticWithScope Template for "with" object in static scope chain + * JSObject Generic object * | - * ScopeObject Engine-internal scope - * | | | - * | | DeclEnvObject Holds name of recursive/needsCallObject named lambda + * +--StaticScope Created at compile time * | | - * | LexicalScopeBase Shared base for function and modules scopes - * | | | - * | | CallObject Scope of entire function or strict eval + * | +--StaticNonSyntacticScope See "Non-syntactic scopes" * | | - * | ModuleEnvironmentObject Module top-level scope on run-time scope chain + * | +--StaticEvalScope Placeholder so eval scopes may be iterated through + * | | + * | +--StaticFunctionScope Scope in a function + * | | + * | +--StaticModuleScope Toplevel scope in a module + * | | + * | +--NestedStaticScope Enclosing scope is in the same JSScript + * | | + * | +--StaticBlockScope See "N.B." below. + * | | + * | +--StaticWithScope Template for "with" object in static scope chain * | - * NestedScopeObject Statement scopes; don't cross script boundaries - * | | - * | DynamicWithObject Run-time "with" object on scope chain - * | - * ClonedBlockObject let, switch, catch, for + * +--ScopeObject Engine-internal scope + * | + * +--DeclEnvObject Holds name of recursive/needsCallObject named lambda + * | + * +--LexicalScopeBase Shared base for function and modules scopes + * | | + * | +--CallObject Scope of entire function or strict eval + * | | + * | +--ModuleEnvironmentObject Module top-level scope on run-time scope chain + * | + * +--NestedScopeObject Statement scopes; don't cross script boundaries + * | + * +--ClonedBlockObject let, switch, catch, for + * | + * +--DynamicWithObject Run-time "with" object on scope chain * * This hierarchy represents more than just the interface hierarchy: reserved * slots in base classes are fixed for all derived classes. Thus, for example, * ScopeObject::enclosingScope() can simply access a fixed slot without further * dynamic type information. * - * NB: Static block objects are a special case: these objects are created at + * N.B. Static block objects are a special case: these objects are created at * compile time to hold the shape/binding information from which block objects * are cloned at runtime. These objects should never escape into the wild and * support a restricted set of ScopeObject operations. @@ -750,23 +771,13 @@ class CallObject : public LexicalScopeBase static CallObject* createHollowForDebug(JSContext* cx, HandleFunction callee); /* True if this is for a strict mode eval frame. */ - bool isForEval() const { - if (is()) - return false; - MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull()); - MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(), - getFixedSlot(CALLEE_SLOT).toObject().is()); - return getFixedSlot(CALLEE_SLOT).isNull(); - } + inline bool isForEval() const; /* * Returns the function for which this CallObject was created. (This may * only be called if !isForEval.) */ - JSFunction& callee() const { - MOZ_ASSERT(!is()); - return getFixedSlot(CALLEE_SLOT).toObject().as(); - } + inline JSFunction& callee() const; /* For jit access. */ static size_t offsetOfCallee() { @@ -789,6 +800,7 @@ class ModuleEnvironmentObject : public LexicalScopeBase static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module); ModuleObject& module(); + StaticModuleScope& staticScope(); IndirectBindingMap& importBindings(); bool createImportBinding(JSContext* cx, HandleAtom importName, HandleModuleObject module, @@ -885,8 +897,8 @@ class DynamicWithObject : public NestedScopeObject }; static DynamicWithObject* - create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith, - WithKind kind = SyntacticWith); + create(JSContext* cx, HandleObject object, HandleObject enclosing, + Handle staticWith, WithKind kind = SyntacticWith); StaticWithScope& staticWith() const { return getProto()->as(); @@ -939,7 +951,8 @@ class ClonedBlockObject : public NestedScopeObject static ClonedBlockObject* createGlobal(JSContext* cx, Handle global); - static ClonedBlockObject* createNonSyntactic(JSContext* cx, HandleObject enclosingStatic, + static ClonedBlockObject* createNonSyntactic(JSContext* cx, + Handle enclosingStatic, HandleObject enclosingScope); static ClonedBlockObject* createHollowForDebug(JSContext* cx, @@ -1046,11 +1059,11 @@ class RuntimeLexicalErrorObject : public ScopeObject template bool -XDRStaticBlockScope(XDRState* xdr, HandleObject enclosingScope, +XDRStaticBlockScope(XDRState* xdr, Handle enclosingScope, MutableHandle objp); -extern JSObject* -CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope, +extern NestedStaticScope* +CloneNestedScopeObject(JSContext* cx, Handle enclosingScope, Handle src); @@ -1081,7 +1094,7 @@ class MOZ_RAII ScopeIter // Constructing from a dynamic scope, static scope pair. All scopes are // considered not to be withinInitialFrame, since no frame is given. - ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope + ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope MOZ_GUARD_OBJECT_NOTIFIER_PARAM); // Constructing from a frame. Places the ScopeIter on the innermost scope @@ -1105,13 +1118,13 @@ class MOZ_RAII ScopeIter inline bool canHaveSyntacticScopeObject() const; ScopeObject& scope() const; - JSObject* maybeStaticScope() const; + StaticScope* maybeStaticScope() const; StaticBlockScope& staticBlock() const { return ssi_.block(); } + StaticModuleScope& module() const { return ssi_.module(); } StaticWithScope& staticWith() const { return ssi_.staticWith(); } StaticEvalScope& staticEval() const { return ssi_.eval(); } StaticNonSyntacticScope& staticNonSyntactic() const { return ssi_.nonSyntactic(); } - JSFunction& fun() const { return ssi_.fun(); } - ModuleObject& module() const { return ssi_.module(); } + StaticFunctionScope& fun() const { return ssi_.fun(); } bool withinInitialFrame() const { return !!frame_; } AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; } @@ -1137,7 +1150,7 @@ class MissingScopeKey friend class LiveScopeVal; AbstractFramePtr frame_; - JSObject* staticScope_; + StaticScope* staticScope_; public: explicit MissingScopeKey(const ScopeIter& si) @@ -1146,9 +1159,9 @@ class MissingScopeKey { } AbstractFramePtr frame() const { return frame_; } - JSObject* staticScope() const { return staticScope_; } + StaticScope* staticScope() const { return staticScope_; } - void updateStaticScope(JSObject* obj) { staticScope_ = obj; } + void updateStaticScope(StaticScope* scope) { staticScope_ = scope; } void updateFrame(AbstractFramePtr frame) { frame_ = frame; } // For use as hash policy. @@ -1170,7 +1183,7 @@ class LiveScopeVal friend class MissingScopeKey; AbstractFramePtr frame_; - RelocatablePtrObject staticScope_; + RelocatablePtr staticScope_; static void staticAsserts(); @@ -1181,7 +1194,7 @@ class LiveScopeVal { } AbstractFramePtr frame() const { return frame_; } - JSObject* staticScope() const { return staticScope_; } + StaticScope* staticScope() const { return staticScope_; } void updateFrame(AbstractFramePtr frame) { frame_ = frame; } @@ -1347,6 +1360,13 @@ JSObject::is() const return hasClass(&js::ClonedBlockObject::class_) && !getProto(); } +template<> +inline bool +JSObject::is() const +{ + return hasClass(&js::ModuleEnvironmentObject::class_) && !getProto(); +} + template<> inline bool JSObject::is() const @@ -1360,10 +1380,19 @@ inline bool JSObject::is() const { return is() || + is() || + is() || is() || is(); } +template<> +inline bool +JSObject::is() const +{ + return hasClass(&js::ModuleEnvironmentObject::class_) && !!getProto(); +} + template<> inline bool JSObject::is() const @@ -1440,6 +1469,13 @@ IsGlobalLexicalScope(JSObject* scope) return scope->is() && scope->as().isGlobal(); } +inline js::StaticScope* +js::StaticScope::enclosingScope() const +{ + JSObject *obj = getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull(); + return obj ? &obj->as() : nullptr; +} + inline NestedStaticScope* NestedStaticScope::enclosingNestedScope() const { @@ -1533,13 +1569,31 @@ ScopeIter::enclosingScope() const return *scope_; } +inline bool +js::CallObject::isForEval() const +{ + if (is()) + return false; + MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull()); + MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(), + getFixedSlot(CALLEE_SLOT).toObject().is()); + return getFixedSlot(CALLEE_SLOT).isNull(); +} + +inline JSFunction& +js::CallObject::callee() const +{ + MOZ_ASSERT(!is()); + return getFixedSlot(CALLEE_SLOT).toObject().as(); +} + extern bool CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain, HandleObject dynamicTerminatingScope, MutableHandleObject dynamicScopeObj); -bool HasNonSyntacticStaticScopeChain(JSObject* staticScope); -uint32_t StaticScopeChainLength(JSObject* staticScope); +bool HasNonSyntacticStaticScopeChain(StaticScope* staticScope); +uint32_t StaticScopeChainLength(StaticScope* staticScope); ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script); @@ -1561,7 +1615,7 @@ bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script, #ifdef DEBUG void DumpStaticScopeChain(JSScript* script); -void DumpStaticScopeChain(JSObject* staticScope); +void DumpStaticScopeChain(StaticScope* staticScope); bool AnalyzeEntrainedVariables(JSContext* cx, HandleScript script); #endif diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 4d0acb9d5ae..a353a124c63 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -2225,9 +2225,10 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject) : selfHostedFunction->getAllocKind(); MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global())); Rooted globalLexical(cx, &cx->global()->lexicalScope()); - RootedObject staticGlobalLexical(cx, &globalLexical->staticBlock()); + Rooted staticGlobalLexical(cx, &globalLexical->staticBlock()); clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical, staticGlobalLexical, kind); + // To be able to re-lazify the cloned function, its name in the // self-hosting compartment has to be stored on the clone. if (clone && hasName) { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 44cec89a9dd..5209fe71acd 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -105,7 +105,7 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* { #ifdef DEBUG RootedObject originalScope(cx, scope); - RootedObject enclosingScope(cx, script->enclosingStaticScope()); + Rooted enclosingScope(cx, script->enclosingStaticScope()); for (StaticScopeIter i(enclosingScope); !i.done(); i++) { if (i.type() == StaticScopeIter::NonSyntactic) { while (scope->is() || @@ -119,7 +119,7 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* } else if (i.hasSyntacticDynamicScopeObject()) { switch (i.type()) { case StaticScopeIter::Module: - MOZ_ASSERT(scope->as().module().script() == i.moduleScript()); + MOZ_ASSERT(scope->as().module().staticScope() == &i.module()); scope = &scope->as().enclosingScope(); break; case StaticScopeIter::Function: diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp index f648c4318d5..bb49a5420fd 100644 --- a/js/src/vm/Xdr.cpp +++ b/js/src/vm/Xdr.cpp @@ -118,7 +118,7 @@ XDRState::codeFunction(MutableHandleFunction objp) if (!VersionCheck(this)) return false; - RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock()); + Rooted staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock()); return XDRInterpretedFunction(this, staticLexical, nullptr, objp); } @@ -132,7 +132,7 @@ XDRState::codeScript(MutableHandleScript scriptp) if (!VersionCheck(this)) return false; - RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock()); + Rooted staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock()); if (!XDRScript(this, staticLexical, nullptr, nullptr, scriptp)) return false; diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h index b33eba5146a..9ad7d7ac468 100644 --- a/js/xpconnect/src/XPCInlines.h +++ b/js/xpconnect/src/XPCInlines.h @@ -537,9 +537,7 @@ XPCWrappedNative::HasInterfaceNoQI(const nsIID& iid) inline void XPCWrappedNative::SweepTearOffs() { - XPCWrappedNativeTearOffChunk* chunk; - for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { - XPCWrappedNativeTearOff* to = &chunk->mTearOff; + for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) { bool marked = to->IsMarked(); to->Unmark(); if (marked) diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 9a3aae5b7d9..5d0ba354295 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -113,9 +113,7 @@ XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb) // (see nsXPConnect::Traverse), but if their JS object has been finalized // then the tearoff is only reachable through the XPCWrappedNative, so we // record an edge here. - XPCWrappedNativeTearOffChunk* chunk; - for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { - XPCWrappedNativeTearOff* to = &chunk->mTearOff; + for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) { JSObject* jso = to->GetJSObjectPreserveColor(); if (!jso) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative"); @@ -898,9 +896,7 @@ XPCWrappedNative::FlatJSObjectFinalized() // dying tearoff object. We can safely assume that those remaining // JSObjects are about to be finalized too. - XPCWrappedNativeTearOffChunk* chunk; - for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { - XPCWrappedNativeTearOff* to = &chunk->mTearOff; + for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) { JSObject* jso = to->GetJSObjectPreserveColor(); if (jso) { JS_SetPrivate(jso, nullptr); @@ -971,7 +967,7 @@ XPCWrappedNative::SystemIsBeingShutDown() // We leak mIdentity (see above). - // short circuit future finalization + // Short circuit future finalization. JS_SetPrivate(mFlatJSObject, nullptr); mFlatJSObject = nullptr; mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); @@ -987,11 +983,8 @@ XPCWrappedNative::SystemIsBeingShutDown() delete mScriptableInfo; } - // cleanup the tearoffs... - - XPCWrappedNativeTearOffChunk* chunk; - for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { - XPCWrappedNativeTearOff* to = &chunk->mTearOff; + // Cleanup the tearoffs. + for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) { if (JSObject* jso = to->GetJSObjectPreserveColor()) { JS_SetPrivate(jso, nullptr); to->SetJSObject(nullptr); @@ -1001,11 +994,6 @@ XPCWrappedNative::SystemIsBeingShutDown() Unused << to->TakeNative().take(); to->SetInterface(nullptr); } - - if (mFirstChunk.mNextChunk) { - delete mFirstChunk.mNextChunk; - mFirstChunk.mNextChunk = nullptr; - } } /***************************************************************************/ @@ -1058,12 +1046,10 @@ XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface, XPCWrappedNativeTearOff* to; XPCWrappedNativeTearOff* firstAvailable = nullptr; - XPCWrappedNativeTearOffChunk* lastChunk; - XPCWrappedNativeTearOffChunk* chunk; - for (lastChunk = chunk = &mFirstChunk; - chunk; - lastChunk = chunk, chunk = chunk->mNextChunk) { - to = &chunk->mTearOff; + XPCWrappedNativeTearOff* lastTearOff; + for (lastTearOff = to = &mFirstTearOff; + to; + lastTearOff = to, to = to->GetNextTearOff()) { if (to->GetInterface() == aInterface) { if (needJSObject && !to->GetJSObjectPreserveColor()) { AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to); @@ -1089,9 +1075,7 @@ XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface, to = firstAvailable; if (!to) { - auto newChunk = new XPCWrappedNativeTearOffChunk(); - lastChunk->mNextChunk = newChunk; - to = &newChunk->mTearOff; + to = lastTearOff->AddTearOff(); } { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 00646467b0b..b3a844180d6 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -85,6 +85,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" #include "mozilla/dom/ScriptSettings.h" @@ -1971,6 +1972,15 @@ public: void Unmark() {mJSObject.unsetFlags(1);} bool IsMarked() const {return mJSObject.hasFlag(1);} + XPCWrappedNativeTearOff* AddTearOff() + { + MOZ_ASSERT(!mNextTearOff); + mNextTearOff = mozilla::MakeUnique(); + return mNextTearOff.get(); + } + + XPCWrappedNativeTearOff* GetNextTearOff() {return mNextTearOff.get();} + private: XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete; XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete; @@ -1981,25 +1991,9 @@ private: // nsISupports pointer. RefPtr mNative; JS::TenuredHeap mJSObject; + mozilla::UniquePtr mNextTearOff; }; -/***********************************************/ -// XPCWrappedNativeTearOffChunk is a linked list of XPCWrappedNativeTearOff -// objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and -// link the sets - rather than only having the option of linking single -// XPCWrappedNativeTearOff objects. - -class XPCWrappedNativeTearOffChunk -{ -friend class XPCWrappedNative; -private: - XPCWrappedNativeTearOffChunk() : mNextChunk(nullptr) {} - ~XPCWrappedNativeTearOffChunk() {delete mNextChunk;} - -private: - XPCWrappedNativeTearOff mTearOff; - XPCWrappedNativeTearOffChunk* mNextChunk; -}; /***************************************************************************/ // XPCWrappedNative the wrapper around one instance of a native xpcom object @@ -2268,13 +2262,13 @@ public: private: union { - XPCWrappedNativeScope* mMaybeScope; - XPCWrappedNativeProto* mMaybeProto; + XPCWrappedNativeScope* mMaybeScope; + XPCWrappedNativeProto* mMaybeProto; }; - XPCNativeSet* mSet; - JS::TenuredHeap mFlatJSObject; - XPCNativeScriptableInfo* mScriptableInfo; - XPCWrappedNativeTearOffChunk mFirstChunk; + XPCNativeSet* mSet; + JS::TenuredHeap mFlatJSObject; + XPCNativeScriptableInfo* mScriptableInfo; + XPCWrappedNativeTearOff mFirstTearOff; }; /*************************************************************************** diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 2cd96e32136..44572c52a07 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1175,14 +1175,16 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, } } - // Display port margins changing means that the set of visible images may - // have drastically changed. Check if we should schedule an update. nsIFrame* frame = GetScrollFrameFromContent(aContent); nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr; if (!scrollableFrame) { return true; } + scrollableFrame->TriggerDisplayPortExpiration(); + + // Display port margins changing means that the set of visible images may + // have drastically changed. Check if we should schedule an update. nsRect oldDisplayPort; bool hadDisplayPort = scrollableFrame->GetDisplayPortAtLastImageVisibilityUpdate(&oldDisplayPort); @@ -1249,6 +1251,13 @@ nsLayoutUtils::HasCriticalDisplayPort(nsIContent* aContent) return GetCriticalDisplayPort(aContent, nullptr); } +void +nsLayoutUtils::RemoveDisplayPort(nsIContent* aContent) +{ + aContent->DeleteProperty(nsGkAtoms::DisplayPort); + aContent->DeleteProperty(nsGkAtoms::DisplayPortMargins); +} + nsContainerFrame* nsLayoutUtils::LastContinuationWithChild(nsContainerFrame* aFrame) { @@ -3168,6 +3177,37 @@ nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame* aFra } } +void +nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame) +{ + nsIFrame* frame = aFrame; + while (frame) { + frame = nsLayoutUtils::GetCrossDocParentFrame(frame); + if (!frame) { + break; + } + nsIScrollableFrame* scrollAncestor = GetAsyncScrollableAncestorFrame(frame); + if (!scrollAncestor) { + break; + } + frame = do_QueryFrame(scrollAncestor); + MOZ_ASSERT(frame); + MOZ_ASSERT(scrollAncestor->WantAsyncScroll() || + frame->PresContext()->PresShell()->GetRootScrollFrame() == frame); + if (nsLayoutUtils::AsyncPanZoomEnabled(frame) && + nsLayoutUtils::HasDisplayPort(frame->GetContent())) { + scrollAncestor->TriggerDisplayPortExpiration(); + // Stop after the first trigger. If it failed, there's no point in + // continuing because all the rest of the frames we encounter are going + // to be ancestors of |scrollAncestor| which will keep its displayport. + // If the trigger succeeded, we stop because when the trigger executes + // it will call this function again to trigger the next ancestor up the + // chain. + break; + } + } +} + nsresult nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame, const nsRegion& aDirtyRegion, nscolor aBackstop, @@ -4542,19 +4582,23 @@ AddIntrinsicSizeOffset(nsRenderingContext* aRenderingContext, pctTotal += pctOutsideSize; nscoord size; - if (GetAbsoluteCoord(aStyleSize, size) || - GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame, - PROP_WIDTH, size)) { + if (aType == nsLayoutUtils::MIN_ISIZE && + (((aStyleSize.HasPercent() || aStyleMaxSize.HasPercent()) && + aFrame->IsFrameOfType(nsIFrame::eReplacedSizing)) || + (aStyleSize.HasPercent() && + aFrame->GetType() == nsGkAtoms::textInputFrame))) { + // A percentage width or max-width on replaced elements means they + // can shrink to 0. + // This is also true for percentage widths (but not max-widths) on + // text inputs. + // Note that if this is max-width, this overrides the fixed-width + // rule in the next condition. + result = 0; // let |min| handle padding/border/margin + } else if (GetAbsoluteCoord(aStyleSize, size) || + GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame, + PROP_WIDTH, size)) { result = nsLayoutUtils::AddPercents(aType, size + coordOutsideSize, pctOutsideSize); - } else if (aType == nsLayoutUtils::MIN_ISIZE && - // The only cases of coord-percent-calc() units that - // GetAbsoluteCoord didn't handle are percent and calc()s - // containing percent. - aStyleSize.IsCoordPercentCalcUnit() && - aFrame->IsFrameOfType(nsIFrame::eReplaced)) { - // A percentage width on replaced elements means they can shrink to 0. - result = 0; // let |min| handle padding/border/margin } else { // NOTE: We could really do a lot better for percents and for some // cases of calc() containing percent (certainly including any where diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 54532ba20ff..152c2ecfa82 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -242,6 +242,11 @@ public: */ static bool HasCriticalDisplayPort(nsIContent* aContent); + /** + * Remove the displayport for the given element. + */ + static void RemoveDisplayPort(nsIContent* aContent); + /** * Use heuristics to figure out the child list that * aChildFrame is currently in. @@ -2686,6 +2691,12 @@ public: */ static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame* aFrame, RepaintMode aRepaintMode); + /** + * Finds the closest ancestor async scrollable frame from aFrame that has a + * displayport and attempts to trigger the displayport expiry on that + * ancestor. + */ + static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame); static bool IsOutlineStyleAutoEnabled(); diff --git a/layout/base/tests/test_bug976963.html b/layout/base/tests/test_bug976963.html index 374759cdeba..4b8da3a6e8b 100644 --- a/layout/base/tests/test_bug976963.html +++ b/layout/base/tests/test_bug976963.html @@ -15,7 +15,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=976963 SimpleTest.requestFlakyTimeout("untriaged"); SpecialPowers.pushPrefEnv({ "set": [ - ["dom.w3c_pointer_events.enabled", true] + ["dom.w3c_pointer_events.enabled", true], + ["layout.reflow.synthMouseMove", false] ] }, startTest); } diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 01c9b7a275d..e84acc20895 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -12,6 +12,7 @@ #include "nsBlockFrame.h" #include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" #include "nsCOMPtr.h" #include "nsAbsoluteContainingBlock.h" @@ -907,21 +908,44 @@ nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, return NS_OK; } +/** + * Return whether aNewAvailableSpace is smaller *on either side* + * (inline-start or inline-end) than aOldAvailableSpace, so that we know + * if we need to redo layout on an line, replaced block, or block + * formatting context, because its height (which we used to compute + * aNewAvailableSpace) caused it to intersect additional floats. + */ static bool AvailableSpaceShrunk(WritingMode aWM, const LogicalRect& aOldAvailableSpace, - const LogicalRect& aNewAvailableSpace) + const LogicalRect& aNewAvailableSpace, + bool aCanGrow /* debug-only */) { if (aNewAvailableSpace.ISize(aWM) == 0) { // Positions are not significant if the inline size is zero. return aOldAvailableSpace.ISize(aWM) != 0; } - NS_ASSERTION(aOldAvailableSpace.IStart(aWM) <= - aNewAvailableSpace.IStart(aWM) && - aOldAvailableSpace.IEnd(aWM) >= - aNewAvailableSpace.IEnd(aWM), - "available space should never grow"); - return aOldAvailableSpace.ISize(aWM) != aNewAvailableSpace.ISize(aWM); + if (aCanGrow) { + NS_ASSERTION(aNewAvailableSpace.IStart(aWM) <= + aOldAvailableSpace.IStart(aWM) || + aNewAvailableSpace.IEnd(aWM) <= aOldAvailableSpace.IEnd(aWM), + "available space should not shrink on the start side and " + "grow on the end side"); + NS_ASSERTION(aNewAvailableSpace.IStart(aWM) >= + aOldAvailableSpace.IStart(aWM) || + aNewAvailableSpace.IEnd(aWM) >= aOldAvailableSpace.IEnd(aWM), + "available space should not grow on the start side and " + "shrink on the end side"); + } else { + NS_ASSERTION(aOldAvailableSpace.IStart(aWM) <= + aNewAvailableSpace.IStart(aWM) && + aOldAvailableSpace.IEnd(aWM) >= + aNewAvailableSpace.IEnd(aWM), + "available space should never grow"); + } + // Have we shrunk on either side? + return aNewAvailableSpace.IStart(aWM) > aOldAvailableSpace.IStart(aWM) || + aNewAvailableSpace.IEnd(aWM) < aOldAvailableSpace.IEnd(aWM); } static LogicalSize @@ -3319,10 +3343,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // construct the html reflow state for the block. ReflowBlock - // will initialize it - nsHTMLReflowState - blockHtmlRS(aState.mPresContext, aState.mReflowState, frame, - availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm)); + // will initialize it. + Maybe blockHtmlRS; + blockHtmlRS.emplace( + aState.mPresContext, aState.mReflowState, frame, + availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm)); nsFloatManager::SavedState floatManagerState; nsReflowStatus frameReflowStatus; @@ -3347,16 +3372,16 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } if (mayNeedRetry) { - blockHtmlRS.mDiscoveredClearance = &clearanceFrame; + blockHtmlRS->mDiscoveredClearance = &clearanceFrame; } else if (!applyBStartMargin) { - blockHtmlRS.mDiscoveredClearance = + blockHtmlRS->mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance; } frameReflowStatus = NS_FRAME_COMPLETE; brc.ReflowBlock(availSpace, applyBStartMargin, aState.mPrevBEndMargin, clearance, aState.IsAdjacentWithTop(), - aLine.get(), blockHtmlRS, frameReflowStatus, aState); + aLine.get(), *blockHtmlRS, frameReflowStatus, aState); // Now the block has a height. Using that height, get the // available space again and call ComputeBlockAvailSpace again. @@ -3377,8 +3402,18 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Restore the height to the position of the next band. floatAvailableSpace.mRect.BSize(wm) = oldFloatAvailableSpaceRect.BSize(wm); + // Determine whether the available space shrunk on either side, + // because (the first time round) we now know the block's height, + // and it may intersect additional floats, or (on later + // iterations) because narrowing the width relative to the + // previous time may cause the block to become taller. Note that + // since we're reflowing the block, narrowing the width might also + // make it shorter, so we must pass aCanGrow as true. if (!AvailableSpaceShrunk(wm, oldFloatAvailableSpaceRect, - floatAvailableSpace.mRect)) { + floatAvailableSpace.mRect, true)) { + // The size and position we chose before are fine (i.e., they + // don't cause intersecting with floats that requires a change + // in size or position), so we're done. break; } @@ -3433,11 +3468,10 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, clearance = 0; } - blockHtmlRS.~nsHTMLReflowState(); - new (&blockHtmlRS) nsHTMLReflowState(aState.mPresContext, - aState.mReflowState, frame, - availSpace.Size(wm).ConvertTo( - frame->GetWritingMode(), wm)); + blockHtmlRS.reset(); + blockHtmlRS.emplace( + aState.mPresContext, aState.mReflowState, frame, + availSpace.Size(wm).ConvertTo(frame->GetWritingMode(), wm)); } while (true); if (mayNeedRetry && clearanceFrame) { @@ -3449,7 +3483,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, aState.mPrevChild = frame; - if (blockHtmlRS.WillReflowAgainForClearance()) { + if (blockHtmlRS->WillReflowAgainForClearance()) { // If an ancestor of ours is going to reflow for clearance, we // need to avoid calling PlaceBlock, because it unsets dirty bits // on the child block (both itself, and through its call to @@ -3487,7 +3521,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, !floatAvailableSpace.mHasFloats; nsCollapsingMargin collapsedBEndMargin; nsOverflowAreas overflowAreas; - *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(), + *aKeepReflowGoing = brc.PlaceBlock(*blockHtmlRS, forceFit, aLine.get(), collapsedBEndMargin, overflowAreas, frameReflowStatus); @@ -4416,8 +4450,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, aFloatAvailableSpace.BSize(wm) = oldFloatAvailableSpace.BSize(wm); // If the available space between the floats is smaller now that we // know the height, return false (and cause another pass with - // LINE_REFLOW_REDO_MORE_FLOATS). - if (AvailableSpaceShrunk(wm, oldFloatAvailableSpace, aFloatAvailableSpace)) { + // LINE_REFLOW_REDO_MORE_FLOATS). We ensure aAvailableSpaceHeight + // never decreases, which means that we can't reduce the set of floats + // we intersect, which means that the available space cannot grow. + if (AvailableSpaceShrunk(wm, oldFloatAvailableSpace, aFloatAvailableSpace, + false)) { return false; } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 5f8c2014bcc..c4bbe107032 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1861,6 +1861,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter, , mHasBeenScrolledRecently(false) , mCollapsedResizer(false) , mWillBuildScrollableLayer(false) + , mIsScrollParent(false) , mIsScrollableLayerInRootContainer(false) , mHasBeenScrolled(false) , mIgnoreMomentumScroll(false) @@ -1908,6 +1909,10 @@ ScrollFrameHelper::~ScrollFrameHelper() mScrollActivityTimer->Cancel(); mScrollActivityTimer = nullptr; } + if (mDisplayPortExpiryTimer) { + mDisplayPortExpiryTimer->Cancel(); + mDisplayPortExpiryTimer = nullptr; + } } /* @@ -2331,6 +2336,43 @@ bool ScrollFrameHelper::IsAlwaysActive() const styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN); } +/*static*/ void +RemoveDisplayPortCallback(nsITimer* aTimer, void* aClosure) +{ + ScrollFrameHelper* helper = static_cast(aClosure); + + // This function only ever gets called from the expiry timer, so it must + // be non-null here. Set it to null here so that we don't keep resetting + // it unnecessarily in MarkRecentlyScrolled(). + MOZ_ASSERT(helper->mDisplayPortExpiryTimer); + helper->mDisplayPortExpiryTimer = nullptr; + + if (helper->IsAlwaysActive() || helper->mIsScrollParent) { + // If this is a scroll parent for some other scrollable frame, don't + // expire the displayport because it would break scroll handoff. Once the + // descendant scrollframes have their displayports expired, they will + // trigger the displayport expiration on this scrollframe as well, and + // mIsScrollParent will presumably be false when that kicks in. + return; + } + + // Remove the displayport from this scrollframe if it's been a while + // since it's scrolled, except if it needs to be always active. Note that + // there is one scrollframe that doesn't fall under this general rule, and + // that is the one that nsLayoutUtils::MaybeCreateDisplayPort decides to put + // a displayport on (i.e. the first scrollframe that WantAsyncScroll()s). + // If that scrollframe is this one, we remove the displayport anyway, and + // as part of the next paint MaybeCreateDisplayPort will put another + // displayport back on it. Although the displayport will "flicker" off and + // back on, the layer itself should never disappear, because this all + // happens between actual painting. If the displayport is reset to a + // different position that's ok; this scrollframe hasn't been scrolled + // recently and so the reset should be correct. + nsLayoutUtils::RemoveDisplayPort(helper->mOuter->GetContent()); + nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(helper->mOuter); + helper->mOuter->SchedulePaint(); +} + void ScrollFrameHelper::MarkNotRecentlyScrolled() { if (!mHasBeenScrolledRecently) @@ -2354,6 +2396,36 @@ void ScrollFrameHelper::MarkRecentlyScrolled() } gScrollFrameActivityTracker->AddObject(this); } + + // If we just scrolled and there's a displayport expiry timer in place, + // reset the timer. + ResetDisplayPortExpiryTimer(); +} + +void ScrollFrameHelper::ResetDisplayPortExpiryTimer() +{ + if (mDisplayPortExpiryTimer) { + mDisplayPortExpiryTimer->InitWithFuncCallback( + RemoveDisplayPortCallback, this, + gfxPrefs::APZDisplayPortExpiryTime(), nsITimer::TYPE_ONE_SHOT); + } +} + +void ScrollFrameHelper::TriggerDisplayPortExpiration() +{ + if (IsAlwaysActive()) { + return; + } + + if (!gfxPrefs::APZDisplayPortExpiryTime()) { + // a zero time disables the expiry + return; + } + + if (!mDisplayPortExpiryTimer) { + mDisplayPortExpiryTimer = do_CreateInstance("@mozilla.org/timer;1"); + } + ResetDisplayPortExpiryTimer(); } void ScrollFrameHelper::ScrollVisual() @@ -3179,6 +3251,9 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, clipNonCaret, scrollClipNonCaret); } + if (aBuilder->IsPaintingToWindow()) { + mIsScrollParent = idSetter.ShouldForceLayerForScrollParent(); + } if (idSetter.ShouldForceLayerForScrollParent() && !gfxPrefs::LayoutUseContainersForRootFrames()) { @@ -3209,7 +3284,7 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } - if (mWillBuildScrollableLayer && !gfxPrefs::LayoutUseContainersForRootFrames()) { + if (mWillBuildScrollableLayer) { aBuilder->ForceLayerForScrollParent(); } diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index cf7d666a268..98b8fb3f740 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -370,6 +370,9 @@ public: void NotifyImageVisibilityUpdate(); bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort); + void TriggerDisplayPortExpiration(); + void ResetDisplayPortExpiryTimer(); + void ScheduleSyntheticMouseMove(); static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance); @@ -468,6 +471,9 @@ public: FrameMetrics::ViewID mScrollParentID; + // Timer to remove the displayport some time after scrolling has stopped + nsCOMPtr mDisplayPortExpiryTimer; + bool mNeverHasVerticalScrollbar:1; bool mNeverHasHorizontalScrollbar:1; bool mHasVerticalScrollbar:1; @@ -509,6 +515,11 @@ public: // a scrollable layer. Used for asynchronous scrolling. bool mWillBuildScrollableLayer:1; + // If true, the scroll frame is an ancestor of other scrolling frames, so + // we shouldn't expire the displayport on this scrollframe unless those + // descendant scrollframes also have their displayports removed. + bool mIsScrollParent:1; + // Whether we are the root scroll frame that is used for containerful // scrolling with a display port. If true, the scrollable frame // shouldn't attach frame metrics to its layers because the container @@ -859,6 +870,9 @@ public: virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override { return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort); } + void TriggerDisplayPortExpiration() override { + mHelper.TriggerDisplayPortExpiration(); + } // nsIStatefulFrame NS_IMETHOD SaveState(nsPresState** aState) override { @@ -1332,6 +1346,9 @@ public: virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override { return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort); } + void TriggerDisplayPortExpiration() override { + mHelper.TriggerDisplayPortExpiration(); + } #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; diff --git a/layout/generic/nsHTMLCanvasFrame.h b/layout/generic/nsHTMLCanvasFrame.h index aada81e0410..db436548746 100644 --- a/layout/generic/nsHTMLCanvasFrame.h +++ b/layout/generic/nsHTMLCanvasFrame.h @@ -86,7 +86,8 @@ public: virtual bool IsFrameOfType(uint32_t aFlags) const override { - return nsSplittableFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced)); + return nsSplittableFrame::IsFrameOfType(aFlags & + ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing)); } #ifdef DEBUG_FRAME_DUMP diff --git a/layout/generic/nsHTMLReflowMetrics.h b/layout/generic/nsHTMLReflowMetrics.h index 94df2cc8fb9..a2a32f5de15 100644 --- a/layout/generic/nsHTMLReflowMetrics.h +++ b/layout/generic/nsHTMLReflowMetrics.h @@ -219,24 +219,29 @@ public: // width in horizontal writing modes, height in vertical ones), and BSize is // the size in the block-progression direction. nscoord ISize(mozilla::WritingMode aWritingMode) const { - CHECK_WRITING_MODE(aWritingMode); + NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode), + "mismatched writing mode"); return mISize; } nscoord BSize(mozilla::WritingMode aWritingMode) const { - CHECK_WRITING_MODE(aWritingMode); + NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode), + "mismatched writing mode"); return mBSize; } mozilla::LogicalSize Size(mozilla::WritingMode aWritingMode) const { - CHECK_WRITING_MODE(aWritingMode); + NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode), + "mismatched writing mode"); return mozilla::LogicalSize(aWritingMode, mISize, mBSize); } nscoord& ISize(mozilla::WritingMode aWritingMode) { - CHECK_WRITING_MODE(aWritingMode); + NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode), + "mismatched writing mode"); return mISize; } nscoord& BSize(mozilla::WritingMode aWritingMode) { - CHECK_WRITING_MODE(aWritingMode); + NS_ASSERTION(!aWritingMode.IsOrthogonalTo(mWritingMode), + "mismatched writing mode"); return mBSize; } diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 801144b65d4..d2a46cdcc0c 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -2925,9 +2925,14 @@ void nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics, nsReflowStatus* aStatus) const { - if (AvailableHeight() != NS_UNCONSTRAINEDSIZE && - AvailableHeight() < aMetrics.Height() && - !mFlags.mIsTopOfPage) { + const WritingMode containerWM = aMetrics.GetWritingMode(); + if (GetWritingMode().IsOrthogonalTo(containerWM)) { + // Orthogonal flows are always reflowed with an unconstrained dimension, + // so should never end up truncated (see nsHTMLReflowState::Init()). + *aStatus &= ~NS_FRAME_TRUNCATED; + } else if (AvailableBSize() != NS_UNCONSTRAINEDSIZE && + AvailableBSize() < aMetrics.BSize(containerWM) && + !mFlags.mIsTopOfPage) { *aStatus |= NS_FRAME_TRUNCATED; } else { *aStatus &= ~NS_FRAME_TRUNCATED; diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index e26436b4676..65869a5ac1b 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2137,6 +2137,11 @@ public: eExcludesIgnorableWhitespace = 1 << 14, eSupportsCSSTransforms = 1 << 15, + // A replaced element that has replaced-element sizing + // characteristics (i.e., like images or iframes), as opposed to + // inline-block sizing characteristics (like form controls). + eReplacedSizing = 1 << 16, + // These are to allow nsFrame::Init to assert that IsFrameOfType // implementations all call the base class method. They are only // meaningful in DEBUG builds. diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index 3e285a892c2..e97f953c085 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -453,6 +453,13 @@ public: * false otherwise, and doesn't touch aDisplayPort. */ virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) = 0; + + /** + * This is called when a descendant scrollframe's has its displayport expired. + * This function will check to see if this scrollframe may safely expire its + * own displayport and schedule a timer to do that if it is safe. + */ + virtual void TriggerDisplayPortExpiration() = 0; }; #endif diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 901c69596af..0a400f68416 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -112,7 +112,8 @@ public: virtual bool IsFrameOfType(uint32_t aFlags) const override { - return ImageFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced)); + return ImageFrameSuper::IsFrameOfType(aFlags & + ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing)); } #ifdef DEBUG_FRAME_DUMP diff --git a/layout/generic/nsPluginFrame.h b/layout/generic/nsPluginFrame.h index b04bcd97781..1758b84043b 100644 --- a/layout/generic/nsPluginFrame.h +++ b/layout/generic/nsPluginFrame.h @@ -91,7 +91,8 @@ public: virtual bool IsFrameOfType(uint32_t aFlags) const override { - return nsPluginFrameSuper::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced)); + return nsPluginFrameSuper::IsFrameOfType(aFlags & + ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing)); } virtual bool NeedsView() override { return true; } diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h index ddb6eb7d385..98916cfb997 100644 --- a/layout/generic/nsSubDocumentFrame.h +++ b/layout/generic/nsSubDocumentFrame.h @@ -38,7 +38,9 @@ public: virtual bool IsFrameOfType(uint32_t aFlags) const override { return nsSubDocumentFrameSuper::IsFrameOfType(aFlags & - ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); + ~(nsIFrame::eReplaced | + nsIFrame::eReplacedSizing | + nsIFrame::eReplacedContainsBlock)); } virtual void Init(nsIContent* aContent, diff --git a/layout/generic/nsVideoFrame.h b/layout/generic/nsVideoFrame.h index 9c5a9973c3f..f01f07ccd49 100644 --- a/layout/generic/nsVideoFrame.h +++ b/layout/generic/nsVideoFrame.h @@ -77,7 +77,8 @@ public: virtual bool IsFrameOfType(uint32_t aFlags) const override { - return nsSplittableFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced)); + return nsSplittableFrame::IsFrameOfType(aFlags & + ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing)); } virtual nsresult CreateAnonymousContent(nsTArray& aElements) override; diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index e78fe836b12..d0abbc89166 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -60,13 +60,13 @@ skip-if(B2G||Mulet) == 28811-2a.html 28811-2-ref.html # Initial mulet triage: pa fuzzy-if(gtkWidget,6,26200) == 28811-2b.html 28811-2-ref.html # Bug 1128229 == 40596-1a.html 40596-1-ref.html != 40596-1b.html 40596-1-ref.html -== 40596-1c.html 40596-1-ref.html +!= 40596-1c.html 40596-1-ref.html != 40596-1d.html 40596-1-ref.html -== 40596-1e.html 40596-1-ref.html +!= 40596-1e.html 40596-1-ref.html != 40596-1f.html 40596-1-ref.html -== 40596-1g.html 40596-1-ref.html +!= 40596-1g.html 40596-1-ref.html != 40596-1h.html 40596-1-ref.html -== 40596-1i.html 40596-1-ref.html +!= 40596-1i.html 40596-1-ref.html != 40596-1j.html 40596-1-ref.html == 50630-1a.html 50630-1-ref.html == 50630-1b.html 50630-1-ref.html diff --git a/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html new file mode 100644 index 00000000000..dbd50f8f4bc --- /dev/null +++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html @@ -0,0 +1,126 @@ + +References for bug 823483 + + + + + +
img, unstyled
+ + + + +
img, width: 50%
+ + + + +
img, max-width: 50%
+ + + + +
canvas, unstyled
+ + + + +
canvas, width: 50%
+ + + + +
canvas, max-width: 50%
+ + + + +
iframe, almost unstyled
+ + + + +
iframe, width: 50%
+ + + + +
iframe, max-width: 50%
+ + + + +
input type="text", unstyled
+ + + + +
input type="text", width: 50%
+ + + + +
input type="text", max-width: 50%
+ + + + +
empty input type="button", unstyled
+ + + + + +
empty input type="button", width: 50%
+ + + + + +
empty input type="button", max-width: 50%
+ + + + +
nonempty input type="button", unstyled
+ + + + +
nonempty input type="button", width: 50%
+ + + + +
nonempty input type="button", max-width: 50%
+ + diff --git a/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements.html b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements.html new file mode 100644 index 00000000000..5abfa93e782 --- /dev/null +++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements.html @@ -0,0 +1,124 @@ + +Tests for bug 823483 + + + + + +
img, unstyled
+ + + + +
img, width: 50%
+ + + + +
img, max-width: 50%
+ + + + +
canvas, unstyled
+ + + + +
canvas, width: 50%
+ + + + +
canvas, max-width: 50%
+ + + + +
iframe, almost unstyled
+ + + + +
iframe, width: 50%
+ + + + +
iframe, max-width: 50%
+ + + + +
input type="text", unstyled
+ + + + +
input type="text", width: 50%
+ + + + +
input type="text", max-width: 50%
+ + + + +
empty input type="button", unstyled
+ + + + +
empty input type="button", width: 50%
+ + + + +
empty input type="button", max-width: 50%
+ + + + +
nonempty input type="button", unstyled
+ + + + +
nonempty input type="button", width: 50%
+ + + + +
nonempty input type="button", max-width: 50%
+ + diff --git a/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases-ref.html b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases-ref.html new file mode 100644 index 00000000000..efc63f344b8 --- /dev/null +++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases-ref.html @@ -0,0 +1,88 @@ + +References for bug 823483 + + + + + + +
unstyled
+ + + + +
width: 10px
+ + + + +
width: 50%
+ + + + +
min-width: 50%
+ + + + +
max-width: 50%
+ + + + +
width: 10px; max-width: 50%
+ + + + +
width: 10px; min-width: 50%
+ + + + +
width: 150%
+ + + + +
min-width: 150%
+ + + + +
max-width: 150%
+ + + + +
width: 10px; max-width: 150%
+ + + + +
width: 10px; min-width: 150%
+ + diff --git a/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases.html b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases.html new file mode 100644 index 00000000000..9679aa04774 --- /dev/null +++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-img-cases.html @@ -0,0 +1,88 @@ + +Tests for bug 823483 + + + + + + +
unstyled
+ + + + +
width: 10px
+ + + + +
width: 50%
+ + + + +
min-width: 50%
+ + + + +
max-width: 50%
+ + + + +
width: 10px; max-width: 50%
+ + + + +
width: 10px; min-width: 50%
+ + + + +
width: 150%
+ + + + +
min-width: 150%
+ + + + +
max-width: 150%
+ + + + +
width: 10px; max-width: 150%
+ + + + +
width: 10px; min-width: 150%
+ + diff --git a/layout/reftests/css-sizing/reftest.list b/layout/reftests/css-sizing/reftest.list new file mode 100644 index 00000000000..5ffce29be3e --- /dev/null +++ b/layout/reftests/css-sizing/reftest.list @@ -0,0 +1,2 @@ +== min-intrinsic-with-percents-across-img-cases.html min-intrinsic-with-percents-across-img-cases-ref.html +== min-intrinsic-with-percents-across-elements.html min-intrinsic-with-percents-across-elements-ref.html diff --git a/layout/reftests/floats/1236745-1-ref.html b/layout/reftests/floats/1236745-1-ref.html new file mode 100644 index 00000000000..79da184857f --- /dev/null +++ b/layout/reftests/floats/1236745-1-ref.html @@ -0,0 +1,39 @@ + +Reftest, bug 1236745 + + + +
+
+
+
+
diff --git a/layout/reftests/floats/1236745-1.html b/layout/reftests/floats/1236745-1.html new file mode 100644 index 00000000000..e1264fb9a54 --- /dev/null +++ b/layout/reftests/floats/1236745-1.html @@ -0,0 +1,49 @@ + +Reftest, bug 1236745 + + + +
+
+
+
+ +
+
diff --git a/layout/reftests/floats/reftest.list b/layout/reftests/floats/reftest.list index 801074d4b87..02acdd046e8 100644 --- a/layout/reftests/floats/reftest.list +++ b/layout/reftests/floats/reftest.list @@ -20,6 +20,7 @@ fails == 345369-2.html 345369-2-ref.html == 546048-1.html 546048-1-ref.html == 775350-1.html 775350-1-ref.html == 1114329.html 1114329-ref.html +== 1236745-1.html 1236745-1-ref.html == float-in-rtl-1a.html float-in-rtl-1-ref.html == float-in-rtl-1b.html float-in-rtl-1-ref.html == float-in-rtl-1c.html float-in-rtl-1-ref.html diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 0a889acc7e2..e2f55999902 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -118,6 +118,9 @@ include text-overflow/reftest.list # css selectors include css-selectors/reftest.list +# css sizing +include css-sizing/reftest.list + # css transitions include css-transitions/reftest.list diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html index 4419f7b7d81..9249622cc46 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1-ref.html @@ -17,12 +17,12 @@ div.add { left: 10px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } div.intersect { left: 230px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html index 86c1e036a2c..bc640777e2a 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1a.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.svg), - url(blue-100x50-transparent-100X50.svg); + mask-image: url(support/blue-100x50-transparent-100x50.svg), + url(support/blue-100x50-transparent-100x50.svg); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html index dd3e1e5e495..e11c28f52e4 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1b.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.png), - url(blue-100x50-transparent-100X50.png); + mask-image: url(support/blue-100x50-transparent-100x50.png), + url(support/blue-100x50-transparent-100x50.png); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html index be1fc6d99b4..b9c1ca50464 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2-ref.html @@ -22,7 +22,7 @@ div.substract { left: 120px; - background-image: url(blue-100x50-transparent-100X50.svg); + background-image: url(support/blue-100x50-transparent-100x50.svg); } div.exclude { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html index 9c81a433221..b624178f425 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2a.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.svg), - url(transparent-100x50-blue-100X50.svg); + mask-image: url(support/blue-100x50-transparent-100x50.svg), + url(support/transparent-100x50-blue-100x50.svg); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html index 9092c1e7f04..2c6e8f41fdd 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2b.html @@ -17,8 +17,8 @@ width: 100px; height: 100px; top:10px; - mask-image: url(blue-100x50-transparent-100X50.png), - url(transparent-100x50-blue-100X50.png); + mask-image: url(support/blue-100x50-transparent-100x50.png), + url(support/transparent-100x50-blue-100x50.png); } div.add { diff --git a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png b/layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.png similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.png rename to layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.png diff --git a/layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg b/layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.svg similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/blue-100x50-transparent-100X50.svg rename to layout/reftests/w3c-css/submitted/masking/support/blue-100x50-transparent-100x50.svg diff --git a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png b/layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.png similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.png rename to layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.png diff --git a/layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg b/layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.svg similarity index 100% rename from layout/reftests/w3c-css/submitted/masking/transparent-100x50-blue-100X50.svg rename to layout/reftests/w3c-css/submitted/masking/support/transparent-100x50-blue-100x50.svg diff --git a/layout/reftests/writing-mode/1243125-1-floats-overflowing-ref.html b/layout/reftests/writing-mode/1243125-1-floats-overflowing-ref.html new file mode 100644 index 00000000000..35b2d8239a3 --- /dev/null +++ b/layout/reftests/writing-mode/1243125-1-floats-overflowing-ref.html @@ -0,0 +1,31 @@ + + + + Bug 1243125 testcase + + + + +

There should be 9 green bars

+ +
+
+
+ +
+
+
+ +
+
+
+ + diff --git a/layout/reftests/writing-mode/1243125-1-floats-overflowing.html b/layout/reftests/writing-mode/1243125-1-floats-overflowing.html new file mode 100644 index 00000000000..463396fbf24 --- /dev/null +++ b/layout/reftests/writing-mode/1243125-1-floats-overflowing.html @@ -0,0 +1,40 @@ + + + + Bug 1243125 testcase + + + + +

There should be 9 green bars

+ +
+
+
+ +
+
+
+ +
+
+
+ + diff --git a/layout/reftests/writing-mode/reftest.list b/layout/reftests/writing-mode/reftest.list index 05464d7ae4c..17fa6c85202 100644 --- a/layout/reftests/writing-mode/reftest.list +++ b/layout/reftests/writing-mode/reftest.list @@ -167,6 +167,8 @@ fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(cocoaWidget,65,69) == 1193519-sideways-l == 1216747-1.html 1216747-1-ref.html != 1216747-1.html 1216747-1-notref.html +== 1243125-1-floats-overflowing.html 1243125-1-floats-overflowing-ref.html + # Suite of tests from Gérard Talbot in bug 1079151 # Frequent Windows 7 load failed: timed out waiting for test to complete (waiting for onload scripts to complete), bug 1167155 and friends skip-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) include abspos/reftest.list diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c0cdab7efca..26d23e5c558 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2321,7 +2321,8 @@ nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aOther) const return hint; } - if (mBlendModeCount != aOther.mBlendModeCount || + if (mAttachmentCount != aOther.mAttachmentCount || + mBlendModeCount != aOther.mBlendModeCount || mClipCount != aOther.mClipCount || mCompositeCount != aOther.mCompositeCount || mMaskModeCount != aOther.mMaskModeCount || diff --git a/layout/style/test/test_initial_storage.html b/layout/style/test/test_initial_storage.html index f7c62c4eea8..f75c0d68c1e 100644 --- a/layout/style/test/test_initial_storage.html +++ b/layout/style/test/test_initial_storage.html @@ -27,6 +27,27 @@ var gDeclaration = document.getElementById("testnode").style; var gTestUnset = SpecialPowers.getBoolPref("layout.css.unset-value.enabled"); +/** + * Checks that the passed-in property-value (returned by getPropertyValue) is + * consistent with the DOM accessors that we know about for the given sproperty. + */ +function check_consistency(sproperty, valFromGetPropertyValue, messagePrefix) +{ + var sinfo = gCSSProperties[sproperty]; + is(valFromGetPropertyValue, gDeclaration[sinfo.domProp], + `(${messagePrefix}) consistency between ` + + `decl.getPropertyValue(${sproperty}) and decl.${sinfo.domProp}`); + + if (sinfo.domProp.startsWith("webkit")) { + // For webkit-prefixed DOM accessors, test with lowercase and uppercase + // first letter. + var uppercaseDomProp = "W" + sinfo.domProp.substring(1); + is(valFromGetPropertyValue, gDeclaration[uppercaseDomProp], + `(${messagePrefix}) consistency between ` + + `decl.getPropertyValue(${sproperty}) and decl.${uppercaseDomProp}`); + } +} + function test_property(property) { var info = gCSSProperties[property]; @@ -37,12 +58,9 @@ function test_property(property) keywords.forEach(function(keyword) { function check_initial(sproperty) { - var sinfo = gCSSProperties[sproperty]; var val = gDeclaration.getPropertyValue(sproperty); is(val, "", "value of '" + sproperty + "' before we do anything"); - is(val, gDeclaration[sinfo.domProp], - "(initial) consistency between decl.getPropertyValue('" + sproperty + - "') and decl." + sinfo.domProp); + check_consistency(sproperty, val, "initial"); } check_initial(property); if ("subproperties" in info) @@ -52,13 +70,10 @@ function test_property(property) gDeclaration.setProperty(property, keyword, ""); function check_set(sproperty) { - var sinfo = gCSSProperties[sproperty]; val = gDeclaration.getPropertyValue(sproperty); is(val, keyword, keyword + " reported back for property '" + sproperty + "'"); - is(val, gDeclaration[sinfo.domProp], - "(set) consistency between decl.getPropertyValue('" + sproperty + - "') and decl." + sinfo.domProp); + check_consistency(sproperty, val, "set"); } check_set(property); if ("subproperties" in info) @@ -78,12 +93,9 @@ function test_property(property) gDeclaration.removeProperty(property); function check_final(sproperty) { - var sinfo = gCSSProperties[sproperty]; var val = gDeclaration.getPropertyValue(sproperty); is(val, "", "value of '" + sproperty + "' after removal of value"); - is(val, gDeclaration[sinfo.domProp], - "(final) consistency between decl.getPropertyValue('" + sproperty + - "') and decl." + sinfo.domProp); + check_consistency(sproperty, val, "final"); } check_final(property); if ("subproperties" in info) diff --git a/media/libvpx/1237848-check-lookahead-ctx.patch b/media/libvpx/1237848-check-lookahead-ctx.patch new file mode 100644 index 00000000000..a2ebe5cd755 --- /dev/null +++ b/media/libvpx/1237848-check-lookahead-ctx.patch @@ -0,0 +1,50 @@ +# HG changeset patch +# User Gerald Squelart +# Parent 5f9ba76eb3b1fd9377bbdb4cc2f98a7e75eabdfb +Bug 1237848 - Check lookahead ctx - r=rillian + +Copied from https://chromium-review.googlesource.com/324510 + +diff --git a/media/libvpx/vp8/encoder/lookahead.c b/media/libvpx/vp8/encoder/lookahead.c +--- a/media/libvpx/vp8/encoder/lookahead.c ++++ b/media/libvpx/vp8/encoder/lookahead.c +@@ -176,16 +176,17 @@ vp8_lookahead_push(struct lookahead_ctx + + + struct lookahead_entry* + vp8_lookahead_pop(struct lookahead_ctx *ctx, + int drain) + { + struct lookahead_entry* buf = NULL; + ++ assert(ctx != NULL); + if(ctx->sz && (drain || ctx->sz == ctx->max_sz - 1)) + { + buf = pop(ctx, &ctx->read_idx); + ctx->sz--; + } + return buf; + } + +diff --git a/media/libvpx/vp9/encoder/vp9_lookahead.c b/media/libvpx/vp9/encoder/vp9_lookahead.c +--- a/media/libvpx/vp9/encoder/vp9_lookahead.c ++++ b/media/libvpx/vp9/encoder/vp9_lookahead.c +@@ -202,17 +202,17 @@ int vp9_lookahead_push(struct lookahead_ + return 0; + } + + + struct lookahead_entry *vp9_lookahead_pop(struct lookahead_ctx *ctx, + int drain) { + struct lookahead_entry *buf = NULL; + +- if (ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { ++ if (ctx && ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { + buf = pop(ctx, &ctx->read_idx); + ctx->sz--; + } + return buf; + } + + + struct lookahead_entry *vp9_lookahead_peek(struct lookahead_ctx *ctx, diff --git a/media/libvpx/update.py b/media/libvpx/update.py index 5acf21eb30f..3a36c271a05 100755 --- a/media/libvpx/update.py +++ b/media/libvpx/update.py @@ -602,6 +602,8 @@ def apply_patches(): os.system("patch -p3 < clang-cl.patch") # Bug 1224371 - Cast uint8_t to uint32_t before shift os.system("patch -p3 < cast-char-to-uint-before-shift.patch") + # Bug 1237848 - Check lookahead ctx + os.system("patch -p3 < 1237848-check-lookahead-ctx.patch") def update_readme(commit): with open('README_MOZILLA') as f: diff --git a/media/libvpx/vp8/encoder/lookahead.c b/media/libvpx/vp8/encoder/lookahead.c index ce2ce08c170..66233857433 100644 --- a/media/libvpx/vp8/encoder/lookahead.c +++ b/media/libvpx/vp8/encoder/lookahead.c @@ -181,6 +181,7 @@ vp8_lookahead_pop(struct lookahead_ctx *ctx, { struct lookahead_entry* buf = NULL; + assert(ctx != NULL); if(ctx->sz && (drain || ctx->sz == ctx->max_sz - 1)) { buf = pop(ctx, &ctx->read_idx); diff --git a/media/libvpx/vp9/encoder/vp9_lookahead.c b/media/libvpx/vp9/encoder/vp9_lookahead.c index b8e2ca88c8c..fd32a16b49e 100644 --- a/media/libvpx/vp9/encoder/vp9_lookahead.c +++ b/media/libvpx/vp9/encoder/vp9_lookahead.c @@ -207,7 +207,7 @@ struct lookahead_entry *vp9_lookahead_pop(struct lookahead_ctx *ctx, int drain) { struct lookahead_entry *buf = NULL; - if (ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { + if (ctx && ctx->sz && (drain || ctx->sz == ctx->max_sz - MAX_PRE_FRAMES)) { buf = pop(ctx, &ctx->read_idx); ctx->sz--; } diff --git a/media/mtransport/nr_socket_prsock.cpp b/media/mtransport/nr_socket_prsock.cpp index 9166ba934b7..dfeda56ceda 100644 --- a/media/mtransport/nr_socket_prsock.cpp +++ b/media/mtransport/nr_socket_prsock.cpp @@ -89,6 +89,7 @@ nrappkit copyright: #include #include #include +#include #include "nspr.h" #include "prerror.h" @@ -110,6 +111,8 @@ nrappkit copyright: #include "nsTArray.h" #include "mozilla/dom/TCPSocketBinding.h" #include "nsITCPSocketCallback.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" #if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API) // csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING @@ -164,6 +167,7 @@ extern "C" { } #include "nr_socket_prsock.h" #include "simpletokenbucket.h" +#include "test_nr_socket.h" // Implement the nsISupports ref counting namespace mozilla { diff --git a/media/mtransport/nricectx.cpp b/media/mtransport/nricectx.cpp index b8804779792..781865b64ab 100644 --- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -93,6 +93,7 @@ extern "C" { #include "nr_socket_prsock.h" #include "nrinterfaceprioritizer.h" #include "rlogringbuffer.h" +#include "test_nr_socket.h" namespace mozilla { @@ -268,6 +269,25 @@ nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server *server) const { return NS_OK; } +NrIceCtx::NrIceCtx(const std::string& name, + bool offerer, + Policy policy) + : connection_state_(ICE_CTX_INIT), + gathering_state_(ICE_CTX_GATHER_INIT), + name_(name), + offerer_(offerer), + streams_(), + ctx_(nullptr), + peer_(nullptr), + ice_handler_vtbl_(nullptr), + ice_handler_(nullptr), + trickle_(true), + policy_(policy), + nat_ (nullptr) { + // XXX: offerer_ will be used eventually; placate clang in the meantime. + (void)offerer_; +} + // Handler callbacks int NrIceCtx::select_pair(void *obj,nr_ice_media_stream *stream, int component_id, nr_ice_cand_pair **potentials, @@ -498,6 +518,42 @@ RefPtr NrIceCtx::Create(const std::string& name, } } + char* mapping_type = nullptr; + char* filtering_type = nullptr; + bool block_udp = false; + + nsresult rv; + nsCOMPtr pref_service = + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr pref_branch; + rv = pref_service->GetBranch(nullptr, getter_AddRefs(pref_branch)); + if (NS_SUCCEEDED(rv)) { + rv = pref_branch->GetCharPref( + "media.peerconnection.nat_simulator.mapping_type", + &mapping_type); + rv = pref_branch->GetCharPref( + "media.peerconnection.nat_simulator.filtering_type", + &filtering_type); + rv = pref_branch->GetBoolPref( + "media.peerconnection.nat_simulator.block_udp", + &block_udp); + } + } + + MOZ_MTLOG(ML_DEBUG, "NAT filtering type: " << filtering_type); + MOZ_MTLOG(ML_DEBUG, "NAT mapping type: " << mapping_type); + + if (mapping_type && filtering_type) { + TestNat* test_nat = new TestNat; + test_nat->filtering_type_ = TestNat::ToNatBehavior(filtering_type); + test_nat->mapping_type_ = TestNat::ToNatBehavior(mapping_type); + test_nat->block_udp_ = block_udp; + test_nat->enabled_ = true; + ctx->SetNat(test_nat); + } + // Create the handler objects ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl(); ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; @@ -522,7 +578,6 @@ RefPtr NrIceCtx::Create(const std::string& name, return nullptr; } - nsresult rv; ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) @@ -531,6 +586,17 @@ RefPtr NrIceCtx::Create(const std::string& name, return ctx; } +int NrIceCtx::SetNat(const RefPtr& aNat) { + nat_ = aNat; + nr_socket_factory *fac; + int r = nat_->create_socket_factory(&fac); + if (r) { + return r; + } + nr_ice_ctx_set_socket_factory(ctx_, fac); + return 0; +} + // ONLY USE THIS FOR TESTING. Will cause totally unpredictable and possibly very // bad effects if ICE is still live. void NrIceCtx::internal_DeinitializeGlobal() { diff --git a/media/mtransport/nricectx.h b/media/mtransport/nricectx.h index 081bc104e2e..bdf91721419 100644 --- a/media/mtransport/nricectx.h +++ b/media/mtransport/nricectx.h @@ -191,6 +191,7 @@ class NrIceProxyServer { std::string alpn_; }; +class TestNat; class NrIceCtx { public: @@ -223,6 +224,8 @@ class NrIceCtx { bool hide_non_default = false, Policy policy = ICE_POLICY_ALL); + int SetNat(const RefPtr& aNat); + // Deinitialize all ICE global state. Used only for testing. static void internal_DeinitializeGlobal(); @@ -332,21 +335,7 @@ class NrIceCtx { private: NrIceCtx(const std::string& name, bool offerer, - Policy policy) - : connection_state_(ICE_CTX_INIT), - gathering_state_(ICE_CTX_GATHER_INIT), - name_(name), - offerer_(offerer), - streams_(), - ctx_(nullptr), - peer_(nullptr), - ice_handler_vtbl_(nullptr), - ice_handler_(nullptr), - trickle_(true), - policy_(policy) { - // XXX: offerer_ will be used eventually; placate clang in the meantime. - (void)offerer_; - } + Policy policy); virtual ~NrIceCtx(); @@ -390,6 +379,7 @@ class NrIceCtx { bool trickle_; nsCOMPtr sts_target_; // The thread to run on Policy policy_; + RefPtr nat_; }; diff --git a/media/mtransport/test/ice_unittest.cpp b/media/mtransport/test/ice_unittest.cpp index c3891ee4109..1c3adb7ba36 100644 --- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -316,12 +316,9 @@ class IceTestPeer : public sigslot::has_slots<> { this, &IceTestPeer::ConnectionStateChange); - nr_socket_factory *fac; - int r = nat_->create_socket_factory(&fac); + int r = ice_ctx_->SetNat(nat_); + (void)r; MOZ_ASSERT(!r); - if (!r) { - nr_ice_ctx_set_socket_factory(ice_ctx_->ctx(), fac); - } } ~IceTestPeer() { diff --git a/media/mtransport/test/transport_unittests.cpp b/media/mtransport/test/transport_unittests.cpp index 70271de769d..2c2d0f8251c 100644 --- a/media/mtransport/test/transport_unittests.cpp +++ b/media/mtransport/test/transport_unittests.cpp @@ -91,8 +91,6 @@ class TransportLayerDummy : public TransportLayer { bool *destroyed_; }; -class TransportLayerLossy; - class Inspector { public: virtual ~Inspector() {} @@ -440,7 +438,7 @@ class TransportTestPeer : public sigslot::has_slots<> { public: TransportTestPeer(nsCOMPtr target, std::string name) : name_(name), target_(target), - received_(0), flow_(new TransportFlow(name)), + received_packets_(0),received_bytes_(0),flow_(new TransportFlow(name)), loopback_(new TransportLayerLoopback()), logging_(new TransportLayerLogging()), lossy_(new TransportLayerLossy()), @@ -724,13 +722,18 @@ class TransportTestPeer : public sigslot::has_slots<> { void PacketReceived(TransportFlow * flow, const unsigned char* data, size_t len) { std::cerr << "Received " << len << " bytes" << std::endl; - ++received_; + ++received_packets_; + received_bytes_ += len; } void SetLoss(uint32_t loss) { lossy_->SetLoss(loss); } + void SetCombinePackets(bool combine) { + loopback_->CombinePackets(combine); + } + void SetInspector(UniquePtr inspector) { lossy_->SetInspector(Move(inspector)); } @@ -768,7 +771,9 @@ class TransportTestPeer : public sigslot::has_slots<> { return state() == TransportLayer::TS_ERROR; } - size_t received() { return received_; } + size_t receivedPackets() { return received_packets_; } + + size_t receivedBytes() { return received_bytes_; } uint16_t cipherSuite() const { nsresult rv; @@ -798,7 +803,8 @@ class TransportTestPeer : public sigslot::has_slots<> { private: std::string name_; nsCOMPtr target_; - size_t received_; + size_t received_packets_; + size_t received_bytes_; RefPtr flow_; TransportLayerLoopback *loopback_; TransportLayerLogging *logging_; @@ -919,8 +925,8 @@ class TransportTest : public ::testing::Test { ASSERT_TRUE_WAIT(p2_->connected(), 10000); } - void TransferTest(size_t count) { - unsigned char buf[1000]; + void TransferTest(size_t count, size_t bytes = 1024) { + unsigned char buf[bytes]; for (size_t i= 0; i 0); } - std::cerr << "Received == " << p2_->received() << std::endl; - ASSERT_TRUE_WAIT(count == p2_->received(), 10000); + std::cerr << "Received == " << p2_->receivedPackets() << " packets" << std::endl; + ASSERT_TRUE_WAIT(count == p2_->receivedPackets(), 10000); + ASSERT_TRUE((count * sizeof(buf)) == p2_->receivedBytes()); } protected: @@ -1143,6 +1150,28 @@ TEST_F(TransportTest, TestTransfer) { TransferTest(1); } +TEST_F(TransportTest, TestTransferMaxSize) { + SetDtlsPeer(); + ConnectSocket(); + /* transportlayerdtls uses a 9216 bytes buffer - as this test uses the + * loopback implementation it does not have to take into account the extra + * bytes added by the DTLS layer below. */ + TransferTest(1, 9216); +} + +TEST_F(TransportTest, TestTransferMultiple) { + SetDtlsPeer(); + ConnectSocket(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferCombinedPackets) { + SetDtlsPeer(); + ConnectSocket(); + p2_->SetCombinePackets(true); + TransferTest(3); +} + TEST_F(TransportTest, TestConnectLoseFirst) { SetDtlsPeer(); p1_->SetLoss(0); @@ -1155,10 +1184,32 @@ TEST_F(TransportTest, TestConnectIce) { ConnectIce(); } -TEST_F(TransportTest, TestTransferIce) { +TEST_F(TransportTest, TestTransferIceMaxSize) { SetDtlsPeer(); ConnectIce(); - TransferTest(1); + /* nICEr and transportlayerdtls both use 9216 bytes buffers. But the DTLS + * layer add extra bytes to the packet, which size depends on chosen cipher + * etc. Sending more then 9216 bytes works, but on the receiving side the call + * to PR_recvfrom() will truncate any packet bigger then nICEr's buffer size + * of 9216 bytes, which then results in the DTLS layer discarding the packet. + * Therefore we leave some headroom (according to + * https://bugzilla.mozilla.org/show_bug.cgi?id=1214269#c29 256 bytes should + * be save choice) here for the DTLS bytes to make it safely into the + * receiving buffer in nICEr. */ + TransferTest(1, 8960); +} + +TEST_F(TransportTest, TestTransferIceMultiple) { + SetDtlsPeer(); + ConnectIce(); + TransferTest(3); +} + +TEST_F(TransportTest, TestTransferIceCombinedPackets) { + SetDtlsPeer(); + ConnectIce(); + p2_->SetCombinePackets(true); + TransferTest(3); } // test the default configuration against a peer that supports only diff --git a/media/mtransport/test_nr_socket.cpp b/media/mtransport/test_nr_socket.cpp index e7bf83a437c..2363bc9b0a9 100644 --- a/media/mtransport/test_nr_socket.cpp +++ b/media/mtransport/test_nr_socket.cpp @@ -137,6 +137,21 @@ static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = { test_nat_socket_factory_destroy }; +/* static */ +TestNat::NatBehavior +TestNat::ToNatBehavior(const std::string& type) { + if (!type.compare("ENDPOINT_INDEPENDENT")) { + return TestNat::ENDPOINT_INDEPENDENT; + } else if (!type.compare("ADDRESS_DEPENDENT")) { + return TestNat::ADDRESS_DEPENDENT; + } else if (!type.compare("PORT_DEPENDENT")) { + return TestNat::PORT_DEPENDENT; + } + + MOZ_ASSERT(false, "Invalid NAT behavior"); + return TestNat::ENDPOINT_INDEPENDENT; +} + bool TestNat::has_port_mappings() const { for (TestNrSocket *sock : sockets_) { if (sock->has_port_mappings()) { @@ -512,6 +527,10 @@ int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg, } if (r) { + r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for " + "internal socket: %d\n", + internal_socket_->my_addr().as_string, + r); return r; } @@ -541,6 +560,10 @@ int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg, function, line); if (r) { + r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for " + "port mapping: %d\n", + internal_socket_->my_addr().as_string, + r); return r; } } diff --git a/media/mtransport/test_nr_socket.h b/media/mtransport/test_nr_socket.h index cf34cd57c92..c975812b707 100644 --- a/media/mtransport/test_nr_socket.h +++ b/media/mtransport/test_nr_socket.h @@ -83,6 +83,10 @@ nrappkit copyright: #ifndef test_nr_socket__ #define test_nr_socket__ +extern "C" { +#include "transport_addr.h" +} + #include "nr_socket_prsock.h" extern "C" { @@ -93,6 +97,7 @@ extern "C" { #include #include #include +#include #include "mozilla/UniquePtr.h" #include "prinrval.h" @@ -152,6 +157,8 @@ class TestNat { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNat); + static NatBehavior ToNatBehavior(const std::string& type); + bool enabled_; TestNat::NatBehavior filtering_type_; TestNat::NatBehavior mapping_type_; diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c index e33049f509b..97211469f2d 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c @@ -418,7 +418,7 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) _status=0; abort: - if(_status) + if(_status && ctx) nr_ice_ctx_destroy_cb(0,0,ctx); return(_status); diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c index e19ae5a4ae9..f693d253fa9 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c @@ -46,7 +46,7 @@ static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) int r; nr_ice_stun_ctx *sc1,*sc2; nr_ice_socket *sock=cb_arg; - UCHAR buf[8192]; + UCHAR buf[9216]; char string[256]; nr_transport_addr addr; int len; @@ -61,8 +61,10 @@ static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label); /* Re-arm first! */ - if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP) + if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): rearming",sock->ctx->label); NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg); + } if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){ if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) { diff --git a/media/mtransport/transportlayerdtls.cpp b/media/mtransport/transportlayerdtls.cpp index 4c413a8f31f..10c32d39a18 100644 --- a/media/mtransport/transportlayerdtls.cpp +++ b/media/mtransport/transportlayerdtls.cpp @@ -969,26 +969,31 @@ void TransportLayerDtls::PacketReceived(TransportLayer* layer, // Now try a recv if we're open, since there might be data left if (state_ == TS_OPEN) { - unsigned char buf[2000]; + // nICEr uses a 9216 bytes buffer to allow support for jumbo frames + unsigned char buf[9216]; - int32_t rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); - if (rv > 0) { - // We have data - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); - SignalPacketReceived(this, buf, rv); - } else if (rv == 0) { - TL_SET_STATE(TS_CLOSED); - } else { - int32_t err = PR_GetError(); - - if (err == PR_WOULD_BLOCK_ERROR) { - // This gets ignored - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); + int32_t rv; + // One packet might contain several DTLS packets + do { + rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); + if (rv > 0) { + // We have data + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); + SignalPacketReceived(this, buf, rv); + } else if (rv == 0) { + TL_SET_STATE(TS_CLOSED); } else { - MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); - TL_SET_STATE(TS_ERROR); + int32_t err = PR_GetError(); + + if (err == PR_WOULD_BLOCK_ERROR) { + // This gets ignored + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); + } else { + MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); + TL_SET_STATE(TS_ERROR); + } } - } + } while (rv > 0); } } diff --git a/media/mtransport/transportlayerloopback.cpp b/media/mtransport/transportlayerloopback.cpp index a1a3d2ccdc6..11c3b4244df 100644 --- a/media/mtransport/transportlayerloopback.cpp +++ b/media/mtransport/transportlayerloopback.cpp @@ -78,13 +78,23 @@ TransportLayerLoopback::SendPacket(const unsigned char *data, size_t len) { nsresult TransportLayerLoopback::QueuePacket(const unsigned char *data, size_t len) { - MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len); MOZ_ASSERT(packets_lock_); PR_Lock(packets_lock_); - packets_.push(new QueuedPacket()); - packets_.back()->Assign(data, len); + if (combinePackets_ && !packets_.empty()) { + QueuedPacket *packet = packets_.front(); + packets_.pop(); + + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " << packet->len() << " and " << len); + packets_.push(new QueuedPacket()); + packets_.back()->Assign(packet->data(), packet->len(), + data, len); + } else { + MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len); + packets_.push(new QueuedPacket()); + packets_.back()->Assign(data, len); + } PRStatus r = PR_Unlock(packets_lock_); MOZ_ASSERT(r == PR_SUCCESS); diff --git a/media/mtransport/transportlayerloopback.h b/media/mtransport/transportlayerloopback.h index 59aac11c3a8..8ad5e28657f 100644 --- a/media/mtransport/transportlayerloopback.h +++ b/media/mtransport/transportlayerloopback.h @@ -36,7 +36,8 @@ class TransportLayerLoopback : public TransportLayer { timer_(nullptr), packets_(), packets_lock_(nullptr), - deliverer_(nullptr) {} + deliverer_(nullptr), + combinePackets_(false) {} ~TransportLayerLoopback() { while (!packets_.empty()) { @@ -67,6 +68,8 @@ class TransportLayerLoopback : public TransportLayer { } } + void CombinePackets(bool combine) { combinePackets_ = combine; } + // Overrides for TransportLayer virtual TransportResult SendPacket(const unsigned char *data, size_t len); @@ -93,6 +96,16 @@ class TransportLayerLoopback : public TransportLayer { len_ = len; } + void Assign(const unsigned char *data1, size_t len1, + const unsigned char *data2, size_t len2) { + data_ = new unsigned char[len1 + len2]; + memcpy(static_cast(data_), + static_cast(data1), len1); + memcpy(static_cast(data_ + len1), + static_cast(data2), len2); + len_ = len1 + len2; + } + const unsigned char *data() const { return data_; } size_t len() const { return len_; } @@ -133,6 +146,7 @@ class TransportLayerLoopback : public TransportLayer { std::queue packets_; PRLock *packets_lock_; RefPtr deliverer_; + bool combinePackets_; }; } // close namespace diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java index f4042e7fac2..120887b770b 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -190,7 +190,7 @@ class NativePanZoomController extends JNIObject implements PanZoomController { @Override // PanZoomController public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) { adjustScrollForSurfaceShift(aShift.x, aShift.y); - return aMetrics; + return aMetrics.offsetViewportByAndClamp(aShift.x, aShift.y); } @WrapForJNI(allowMultithread = true) diff --git a/mobile/android/tests/browser/chrome/tp5/README b/mobile/android/tests/browser/chrome/tp5/README new file mode 100644 index 00000000000..c733fb4c031 --- /dev/null +++ b/mobile/android/tests/browser/chrome/tp5/README @@ -0,0 +1 @@ +This directory contains pages and other resources downloaded from the web for the purpose of testing against pages from the real world. Pages are copied from the Talos tp5 data -- see https://wiki.mozilla.org/Buildbot/Talos/Tests#tp5. These files are not made available under an open source license. diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 80948250268..5e613f9b43f 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -245,8 +245,12 @@ Preferences::SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeO n += iter.Data()->mClosures.ShallowSizeOfExcludingThis(aMallocSizeOf); } } - // We don't measure sRootBranch and sDefaultRootBranch here because - // DMD indicates they are not significant. + if (sRootBranch) { + n += reinterpret_cast(sRootBranch)->SizeOfIncludingThis(aMallocSizeOf); + } + if (sDefaultRootBranch) { + n += reinterpret_cast(sDefaultRootBranch)->SizeOfIncludingThis(aMallocSizeOf); + } n += pref_SizeOfPrivateData(aMallocSizeOf); return n; } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e09bbb2e223..af4a08fe0ea 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -129,9 +129,6 @@ pref("dom.indexedDB.logging.profiler-marks", false); // Whether or not File Handle is enabled. pref("dom.fileHandle.enabled", true); -// Whether or not the Permissions API is enabled. -pref("dom.permissions.enabled", true); - // Whether or not selection events are enabled #ifdef NIGHTLY_BUILD pref("dom.select_events.enabled", true); @@ -570,6 +567,7 @@ pref("apz.content_response_timeout", 300); pref("apz.drag.enabled", false); pref("apz.danger_zone_x", 50); pref("apz.danger_zone_y", 100); +pref("apz.displayport_expiry_ms", 15000); pref("apz.enlarge_displayport_when_clipped", false); pref("apz.fling_accel_base_mult", "1.0"); pref("apz.fling_accel_interval_ms", 500); diff --git a/modules/libpref/nsPrefBranch.cpp b/modules/libpref/nsPrefBranch.cpp index 0f5046dfd72..6c341722e09 100644 --- a/modules/libpref/nsPrefBranch.cpp +++ b/modules/libpref/nsPrefBranch.cpp @@ -687,6 +687,15 @@ void nsPrefBranch::NotifyObserver(const char *newpref, void *data) NS_ConvertASCIItoUTF16(suffix).get()); } +size_t +nsPrefBranch::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) +{ + size_t n = aMallocSizeOf(this); + n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf); + return n; +} + void nsPrefBranch::freeObserverList(void) { // We need to prevent anyone from modifying mObservers while we're iterating diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index 31f1cca1d29..e15bd94f6a2 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -929,7 +929,9 @@ nsAtomicFileOutputStream::DoOpen() tempResult->SetFollowLinks(true); // XP_UNIX ignores SetFollowLinks(), so we have to normalize. - tempResult->Normalize(); + if (mTargetFileExists) { + tempResult->Normalize(); + } } if (NS_SUCCEEDED(rv) && mTargetFileExists) { diff --git a/netwerk/mime/nsMIMEHeaderParamImpl.cpp b/netwerk/mime/nsMIMEHeaderParamImpl.cpp index 2c42afa487b..362ff1f6abf 100644 --- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp +++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp @@ -1238,13 +1238,14 @@ nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset, if (q[1] != '?') goto badsyntax; - r = q; - for (r = q + 2; *r != '?'; r++) { + // loop-wise, keep going until we hit "?=". the inner check handles the + // nul terminator should the string terminate before we hit the right + // marker. (And the r[1] will never reach beyond the end of the string + // because *r != '?' is true if r is the nul character.) + for (r = q + 2; *r != '?' || r[1] != '='; r++) { if (*r < ' ') goto badsyntax; } - if (r[1] != '=') - goto badsyntax; - else if (r == q + 2) { + if (r == q + 2) { // it's empty, skip begin = r + 2; isLastEncodedWord = 1; diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index 1fb68c3371a..61ae865848f 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -415,7 +415,7 @@ nsHttpResponseHead::ComputeCurrentAge(uint32_t now, // // freshnessLifetime = expires_value - date_value // -// freshnessLifetime = (date_value - last_modified_value) * 0.10 +// freshnessLifetime = min(one-week,(date_value - last_modified_value) * 0.10) // // freshnessLifetime = 0 // @@ -463,6 +463,8 @@ nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const if (date2 <= date) { // this only makes sense if last-modified is actually in the past *result = (date - date2) / 10; + const uint32_t kOneWeek = 60 * 60 * 24 * 7; + *result = std::min(kOneWeek, *result); return NS_OK; } } diff --git a/security/manager/.eslintrc b/security/manager/.eslintrc index fdb997bcaa6..fcde4fe4c50 100644 --- a/security/manager/.eslintrc +++ b/security/manager/.eslintrc @@ -87,6 +87,9 @@ // No spaces between function name and parentheses "no-spaced-func": 2, + // No trailing whitespace + "no-trailing-spaces": 2, + // Error on newline where a semicolon is needed "no-unexpected-multiline": 2, diff --git a/security/manager/pki/resources/content/certManager.js b/security/manager/pki/resources/content/certManager.js index 0011be8083f..85a0491e5cb 100644 --- a/security/manager/pki/resources/content/certManager.js +++ b/security/manager/pki/resources/content/certManager.js @@ -301,8 +301,8 @@ function backupCerts() fp.appendFilters(nsIFilePicker.filterAll); var rv = fp.show(); if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) { - certdb.exportPKCS12File(null, fp.file, - selected_certs.length, selected_certs); + certdb.exportPKCS12File(null, fp.file, selected_certs.length, + selected_certs); } } @@ -404,52 +404,35 @@ function deleteCerts() var selTab = document.getElementById('certMgrTabbox').selectedItem; var selTabID = selTab.getAttribute('id'); - var t; - params.SetNumberStrings(numcerts+1); + params.SetNumberStrings(numcerts + 1); - if (selTabID == 'mine_tab') - { - params.SetString(0, selTabID); - } - else if (selTabID == "websites_tab") - { - params.SetString(0, selTabID); - } - else if (selTabID == "ca_tab") - { - params.SetString(0, selTabID); - } - else if (selTabID == "others_tab") - { - params.SetString(0, selTabID); - } - else if (selTabID == "orphan_tab") - { - params.SetString(0, selTabID); - } - else - { - return; + switch (selTabID) { + case "mine_tab": + case "websites_tab": + case "ca_tab": + case "others_tab": + case "orphan_tab": + params.SetString(0, selTabID); + break; + default: + return; } - params.SetInt(0,numcerts); - for (t=0; t=0; t--) - { + for (let t = numcerts - 1; t >= 0; t--) { treeView.deleteEntryObject(selected_index[t]); } diff --git a/security/manager/pki/resources/content/certpicker.js b/security/manager/pki/resources/content/certpicker.js index 596449d6636..dac338d1a99 100644 --- a/security/manager/pki/resources/content/certpicker.js +++ b/security/manager/pki/resources/content/certpicker.js @@ -16,22 +16,23 @@ function onLoad() itemCount = dialogParams.GetInt(0); var selIndex = dialogParams.GetInt(1); - if (selIndex < 0) + if (selIndex < 0) { selIndex = 0; - - for (var i=0; i < itemCount; i++) { - var menuItemNode = document.createElement("menuitem"); - var nick = dialogParams.GetString(i); - menuItemNode.setAttribute("value", i); - menuItemNode.setAttribute("label", nick); // this is displayed - selectElement.firstChild.appendChild(menuItemNode); - - if (selIndex == i) { - selectElement.selectedItem = menuItemNode; - } } - dialogParams.SetInt(0,0); // set cancel return value + for (let i = 0; i < itemCount; i++) { + let menuItemNode = document.createElement("menuitem"); + let nick = dialogParams.GetString(i); + menuItemNode.setAttribute("value", i); + menuItemNode.setAttribute("label", nick); // This is displayed. + selectElement.firstChild.appendChild(menuItemNode); + + if (selIndex == i) { + selectElement.selectedItem = menuItemNode; + } + } + + dialogParams.SetInt(0, 0); // Set cancel return value. setDetails(); } diff --git a/security/manager/pki/resources/content/clientauthask.js b/security/manager/pki/resources/content/clientauthask.js index 7bb73f70a43..2c5ca6d1fa0 100644 --- a/security/manager/pki/resources/content/clientauthask.js +++ b/security/manager/pki/resources/content/clientauthask.js @@ -32,10 +32,9 @@ function onLoad() if (pref) { pref = pref.getBranch(null); try { - rememberSetting = + rememberSetting = pref.getBoolPref("security.remember_cert_checkbox_default_setting"); - } - catch(e) { + } catch (e) { // pref is missing } } diff --git a/security/manager/pki/resources/content/createCertInfo.js b/security/manager/pki/resources/content/createCertInfo.js index 99fd60be0d8..e0baa47372a 100644 --- a/security/manager/pki/resources/content/createCertInfo.js +++ b/security/manager/pki/resources/content/createCertInfo.js @@ -9,21 +9,22 @@ var keygenThread; function onLoad() { keygenThread = window.arguments[0].QueryInterface(Components.interfaces.nsIKeygenThread); - + if (!keygenThread) { window.close(); return; } - + setCursor("wait"); var obs = { observe : function keygenListenerObserve(subject, topic, data) { - if (topic == "keygen-finished") + if (topic == "keygen-finished") { window.close(); + } } }; - + keygenThread.startKeyGeneration(obs); } diff --git a/security/manager/pki/resources/content/deletecert.js b/security/manager/pki/resources/content/deletecert.js index 4a94ef52ec6..12c75ee380f 100644 --- a/security/manager/pki/resources/content/deletecert.js +++ b/security/manager/pki/resources/content/deletecert.js @@ -13,15 +13,15 @@ var gParams; function setWindowName() { gParams = window.arguments[0].QueryInterface(nsIDialogParamBlock); - + var typeFlag = gParams.GetString(0); var numberOfCerts = gParams.GetInt(0); - + var bundle = document.getElementById("pippki_bundle"); var title; var confirm; var impact; - + if(typeFlag == "mine_tab") { title = bundle.getString("deleteUserCertTitle"); diff --git a/security/manager/pki/resources/content/device_manager.js b/security/manager/pki/resources/content/device_manager.js index 689a956d48c..b5e39acfa6e 100644 --- a/security/manager/pki/resources/content/device_manager.js +++ b/security/manager/pki/resources/content/device_manager.js @@ -258,11 +258,12 @@ function ClearDeviceList() tree.view.selection.clearSelection(); skip_enable_buttons = false; - // Remove the existing listed modules so that refresh doesn't - // display the module that just changed. - var device_list = document.getElementById("device_list"); - while (device_list.hasChildNodes()) - device_list.removeChild(device_list.firstChild); + // Remove the existing listed modules so that a refresh doesn't display the + // module that just changed. + let deviceList = document.getElementById("device_list"); + while (deviceList.hasChildNodes()) { + deviceList.removeChild(deviceList.firstChild); + } } @@ -356,7 +357,7 @@ function doLogin() selected_token.login(false); var tok_status = document.getElementById("tok_status"); if (selected_token.isLoggedIn()) { - tok_status.setAttribute("label", + tok_status.setAttribute("label", bundle.getString("devinfo_stat_loggedin")); } else { tok_status.setAttribute("label", @@ -378,7 +379,7 @@ function doLogout() selected_token.logoutAndDropAuthenticatedResources(); var tok_status = document.getElementById("tok_status"); if (selected_token.isLoggedIn()) { - tok_status.setAttribute("label", + tok_status.setAttribute("label", bundle.getString("devinfo_stat_loggedin")); } else { tok_status.setAttribute("label", @@ -392,8 +393,7 @@ function doLogout() // load a new device function doLoad() { - window.open("load_device.xul", "loaddevice", - "chrome,centerscreen,modal"); + window.open("load_device.xul", "loaddevice", "chrome,centerscreen,modal"); ClearDeviceList(); RefreshDeviceList(); } @@ -439,11 +439,11 @@ function onSmartCardChange() function changePassword() { getSelectedItem(); - var params = Components.classes[nsDialogParamBlock].createInstance(nsIDialogParamBlock); - params.SetString(1,selected_slot.tokenName); - window.openDialog("changepassword.xul", - "", - "chrome,centerscreen,modal", params); + let params = Components.classes[nsDialogParamBlock] + .createInstance(nsIDialogParamBlock); + params.SetString(1, selected_slot.tokenName); + window.openDialog("changepassword.xul", "", "chrome,centerscreen,modal", + params); showSlotInfo(); enableButtons(); } @@ -526,8 +526,8 @@ function toggleFIPS() return; } - //Remove the existing listed modules so that re-fresh doesn't - //display the module that just changed. + // Remove the existing listed modules so that a refresh doesn't display the + // module that just changed. ClearDeviceList(); RefreshDeviceList(); diff --git a/security/manager/pki/resources/content/downloadcert.js b/security/manager/pki/resources/content/downloadcert.js index 8f486196536..a6a7eabe5bb 100644 --- a/security/manager/pki/resources/content/downloadcert.js +++ b/security/manager/pki/resources/content/downloadcert.js @@ -14,12 +14,12 @@ function onLoad() params = window.arguments[0].QueryInterface(nsIDialogParamBlock); cert = params.objects.queryElementAt(0, nsIX509Cert); - caName = cert.commonName; - var bundle = document.getElementById("pippki_bundle"); - if (!caName.length) + caName = cert.commonName; + if (caName.length == 0) { caName = bundle.getString("unnamedCA"); + } var message2 = bundle.getFormattedString("newCAMessage1", [caName]); setText("message2", message2); diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js index 640791f898f..3e72e083174 100644 --- a/security/manager/pki/resources/content/exceptionDialog.js +++ b/security/manager/pki/resources/content/exceptionDialog.js @@ -28,7 +28,7 @@ badCertListener.prototype = { return this; throw Components.results.NS_ERROR_NO_INTERFACE; - }, + }, handle_test_result: function () { if (gSSLStatus) gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert; @@ -54,14 +54,14 @@ function initExceptionDialog() { var brandName = gBundleBrand.getString("brandShortName"); setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName])); gDialog.getButton("extra1").disabled = true; - + var args = window.arguments; if (args && args[0]) { if (args[0].location) { // We were pre-seeded with a location. document.getElementById("locationTextBox").value = args[0].location; document.getElementById('checkCertButton').disabled = false; - + if (args[0].sslStatus) { gSSLStatus = args[0].sslStatus; gCert = gSSLStatus.serverCert; @@ -77,13 +77,13 @@ function initExceptionDialog() { document.getElementById("checkCertButton").disabled = true; gChecking = true; updateCertStatus(); - + window.setTimeout(checkCert, 0); } } - + // Set out parameter to false by default - args[0].exceptionAdded = false; + args[0].exceptionAdded = false; } } @@ -92,7 +92,6 @@ function initExceptionDialog() { * the Certificate Status section with the result. */ function checkCert() { - gCert = null; gSSLStatus = null; gChecking = true; @@ -118,14 +117,15 @@ function checkCert() { } finally { gChecking = false; } - - if(req.channel && req.channel.securityInfo) { + + if (req.channel && req.channel.securityInfo) { const Ci = Components.interfaces; gSSLStatus = req.channel.securityInfo .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus; gCert = gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert; } - updateCertStatus(); + + updateCertStatus(); } /** @@ -133,21 +133,25 @@ function checkCert() { * Certificate Location fields */ function getURI() { - // Use fixup service instead of just ioservice's newURI since it's quite likely - // that the host will be supplied without a protocol prefix, resulting in malformed - // uri exceptions being thrown. - var fus = Components.classes["@mozilla.org/docshell/urifixup;1"] + // Use fixup service instead of just ioservice's newURI since it's quite + // likely that the host will be supplied without a protocol prefix, resulting + // in malformed uri exceptions being thrown. + let fus = Components.classes["@mozilla.org/docshell/urifixup;1"] .getService(Components.interfaces.nsIURIFixup); - var uri = fus.createFixupURI(document.getElementById("locationTextBox").value, 0); - - if(!uri) + let locationTextBox = document.getElementById("locationTextBox"); + let uri = fus.createFixupURI(locationTextBox.value, 0); + + if (!uri) { return null; + } - if(uri.scheme == "http") + if (uri.scheme == "http") { uri.scheme = "https"; + } - if (uri.port == -1) + if (uri.port == -1) { uri.port = 443; + } return uri; } @@ -184,8 +188,8 @@ function updateCertStatus() { var use2 = false; var use3 = false; let bucketId = gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE; - if(gCert) { - if(gBroken) { + if (gCert) { + if (gBroken) { var mms = "addExceptionDomainMismatchShort"; var mml = "addExceptionDomainMismatchLong2"; var exs = "addExceptionExpiredShort"; @@ -218,13 +222,11 @@ function updateCertStatus() { use1 = true; shortDesc = uts; longDesc = utl; - } - else if (!use2) { + } else if (!use2) { use2 = true; shortDesc2 = uts; longDesc2 = utl; - } - else { + } else { use3 = true; shortDesc3 = uts; longDesc3 = utl; @@ -281,7 +283,7 @@ function updateCertStatus() { gDialog.getButton("extra1").disabled = true; document.getElementById("permanent").disabled = true; } - + setText("statusDescription", gPKIBundle.getString(shortDesc)); setText("statusLongDescription", gPKIBundle.getString(longDesc)); @@ -303,9 +305,9 @@ function updateCertStatus() { */ function viewCertButtonClick() { gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT); - if (gCert) + if (gCert) { viewCertHelper(this, gCert); - + } } /** @@ -331,7 +333,7 @@ function addException() { flags |= overrideService.ERROR_TIME; confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME; } - + var permanentCheckbox = document.getElementById("permanent"); var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode(); if(!permanentCheckbox.checked) @@ -344,11 +346,12 @@ function addException() { gCert, flags, !shouldStorePermanently); - - var args = window.arguments; - if (args && args[0]) + + let args = window.arguments; + if (args && args[0]) { args[0].exceptionAdded = true; - + } + gDialog.acceptDialog(); } diff --git a/security/manager/pki/resources/content/password.js b/security/manager/pki/resources/content/password.js index f1c3683725d..2f602c2c944 100644 --- a/security/manager/pki/resources/content/password.js +++ b/security/manager/pki/resources/content/password.js @@ -35,11 +35,10 @@ function onLoad() // previously trying to assign unicode to the window's name. // I checked all the places where we get a password prompt and // all of them pass an argument as part of this patch. - tokenName=""; + tokenName = ""; } - - if(tokenName=="") { + if (tokenName == "") { var sectokdb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB); var tokenList = sectokdb.listTokens(); var enumElement; @@ -97,7 +96,7 @@ function process() var status = slot.status; if (status == nsIPKCS11Slot.SLOT_UNINITIALIZED || status == nsIPKCS11Slot.SLOT_READY) { - + oldpwbox.setAttribute("hidden", "true"); msgBox.setAttribute("value", bundle.getString("password_not_set")); msgBox.setAttribute("hidden", "false"); @@ -107,10 +106,9 @@ function process() } else { oldpwbox.setAttribute("inited", "true"); } - + // Select first password field document.getElementById('pw1').focus(); - } else { // Select old password field oldpwbox.setAttribute("hidden", "false"); @@ -124,7 +122,7 @@ function process() // Return value 0 means "canceled" params.SetInt(1, 0); } - + checkPasswords(); } @@ -147,23 +145,22 @@ function setPassword() var bundle = document.getElementById("pippki_bundle"); var success = false; - + if (initpw == "false" || initpw == "empty") { try { var oldpw = ""; var passok = 0; - + if (initpw == "empty") { passok = 1; } else { oldpw = oldpwbox.value; passok = token.checkPassword(oldpw); } - + if (passok) { if (initpw == "empty" && pw1.value == "") { - // This makes no sense that we arrive here, - // we reached a case that should have been prevented by checkPasswords. + // checkPasswords() should have prevented this path from being reached. } else { if (pw1.value == "") { var secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB); @@ -196,9 +193,8 @@ function setPassword() } else { token.initPassword(pw1.value); if (pw1.value == "") { - doPrompt(bundle.getString("pw_not_wanted") - + " " - + bundle.getString("pw_empty_warning")); + doPrompt(bundle.getString("pw_not_wanted") + " " + + bundle.getString("pw_empty_warning")); } success = true; } diff --git a/security/manager/pki/resources/content/pippki.js b/security/manager/pki/resources/content/pippki.js index c2719fe1c7c..967e73fad8d 100644 --- a/security/manager/pki/resources/content/pippki.js +++ b/security/manager/pki/resources/content/pippki.js @@ -54,14 +54,14 @@ function getPKCS7String(cert, chainMode) function getPEMString(cert) { var derb64 = btoa(getDERString(cert)); - // Wrap the Base64 string into lines of 64 characters, - // with CRLF line breaks (as specified in RFC 1421). + // Wrap the Base64 string into lines of 64 characters with CRLF line breaks + // (as specified in RFC 1421). var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n"); return "-----BEGIN CERTIFICATE-----\r\n" + wrapped + "\r\n-----END CERTIFICATE-----\r\n"; } - + function alertPromptService(title, message) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. diff --git a/security/manager/pki/resources/content/protectedAuth.js b/security/manager/pki/resources/content/protectedAuth.js index 1b68220fbfa..a89a0d5d445 100644 --- a/security/manager/pki/resources/content/protectedAuth.js +++ b/security/manager/pki/resources/content/protectedAuth.js @@ -4,40 +4,37 @@ function onLoad() { - protectedAuthThread = window.arguments[0].QueryInterface(Components.interfaces.nsIProtectedAuthThread); + protectedAuthThread = + window.arguments[0].QueryInterface(Components.interfaces.nsIProtectedAuthThread); - if (!protectedAuthThread) - { - window.close(); - return; - } + if (!protectedAuthThread) { + window.close(); + return; + } - try - { - var tokenName = protectedAuthThread.getTokenName(); + try { + let tokenName = protectedAuthThread.getTokenName(); - var tag = document.getElementById("tokenName"); - tag.setAttribute("value",tokenName); + let tag = document.getElementById("tokenName"); + tag.setAttribute("value", tokenName); - setCursor("wait"); - - var obs = { - observe : function protectedAuthListenerObserve(subject, topic, data) { - if (topic == "operation-completed") - window.close(); - } - }; - - protectedAuthThread.login(obs); + setCursor("wait"); - } catch (exception) - { - window.close(); - return; - } + let obs = { + observe: function protectedAuthListenerObserve(subject, topic, data) { + if (topic == "operation-completed") { + window.close(); + } + } + }; + + protectedAuthThread.login(obs); + } catch (exception) { + window.close(); + } } function onClose() { - setCursor("default"); + setCursor("default"); } diff --git a/security/manager/pki/resources/content/resetpassword.js b/security/manager/pki/resources/content/resetpassword.js index 16aeb2431ef..f3840d4d79e 100644 --- a/security/manager/pki/resources/content/resetpassword.js +++ b/security/manager/pki/resources/content/resetpassword.js @@ -54,8 +54,8 @@ function resetPassword() promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService); if (promptService && bundle) { promptService.alert(window, - bundle.getString("resetPasswordConfirmationTitle"), - bundle.getString("resetPasswordConfirmationMessage")); + bundle.getString("resetPasswordConfirmationTitle"), + bundle.getString("resetPasswordConfirmationMessage")); } return true; diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html index be8925e98f2..fcf7531ac45 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html @@ -8,11 +8,9 @@ diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html index 352ae52efc6..7060c20e470 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html @@ -20,8 +20,7 @@ document.body.background = "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"; - waitForSecurityState("broken", function() - { + waitForSecurityState("broken", function () { isSecurityState("broken", "document.body.background='http://...' changed to broken"); finish(); }); diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html index a9967228b4c..c8223a2cd52 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html @@ -23,7 +23,7 @@ self.isSecurityState("broken", "src='redirect to unsecure' changed to broken"); self.finish(); } - + iframe.src = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs"; } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html index e226fd16be0..d0c53c44a55 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html @@ -15,15 +15,13 @@ function runTest() { isSecurityState("secure"); - - window.setTimeout(function() - { + + window.setTimeout(function () { document.getElementById("buddy").innerHTML = ""; }, 1); - waitForSecurityState("broken", function() - { + waitForSecurityState("broken", function () { isSecurityState("broken", "innerHTML loading insecure changed to broken"); finish(); }); @@ -33,7 +31,7 @@ { is(document.getElementById("buddy").innerHTML, "", "innerHTML back to previous"); isSecurityState("secure"); - finish(); + finish(); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html index 43e27d414be..4264981c2af 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html @@ -15,7 +15,7 @@ function runTest() { isSecurityState("secure"); - + document.getElementById("buddy").innerHTML = ""; @@ -29,7 +29,7 @@ { is(document.getElementById("buddy").innerHTML, "", "innerHTML back to previous"); isSecurityState("secure"); - finish(); + finish(); } diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html index 5905865685e..a6ff74ae4f8 100644 --- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html @@ -82,7 +82,7 @@ } // check if the result (SECURE/INSECURE) is expected for this round/test combo - SimpleTest.is(result[0], testframes[result[1]].expected[round], + SimpleTest.is(result[0], testframes[result[1]].expected[round], "in ROUND " + round + ", test " + result[1]); testsleft[round]--; @@ -95,8 +95,8 @@ // defer this so it doesn't muck with the stack too much. if (roundsLeft == 1) setTimeout(function () { - startRound('subdom'); - }, 0); + startRound("subdom"); + }, 0); } if (roundsLeft < 1) { diff --git a/security/manager/ssl/tests/unit/test_hash_algorithms.js b/security/manager/ssl/tests/unit/test_hash_algorithms.js index 79247846ee9..31ceac91ef0 100644 --- a/security/manager/ssl/tests/unit/test_hash_algorithms.js +++ b/security/manager/ssl/tests/unit/test_hash_algorithms.js @@ -30,7 +30,7 @@ var hashes = { sha384: [ "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" - ], + ], sha512: [ "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" @@ -38,28 +38,11 @@ var hashes = { }; function hexdigest(data) { - /* - * Coment taken from bug 383390: - * - * First, |data| is the final string value produced by the cryptohash. |for (i in - * data)| uses the Mozilla JS extension that iterating over a string iterates over - * its character indexes, so that's 0..length-1 over the hash string. - * - * Returning to the left, the |charCodeAt| gets the value of the character at that - * index in the string. - * - * |slice(-2)| is equivalent to |slice(length of this string - 2)| as a convenient - * way of wrapping around to the other end of a string without doing the actual - * calculation. When provided with only one argument, slice selects to the end of - * the string, so this chomps off the last two characters of the string. - * - * The last-two-characters part clarifies the |"0" +| -- if the Unicode value is - * <10, we have a single-character hex string when we want one that's two - * characters, and unconditionally prepending a "0" solves the problem. - * - * The array comprehension just creates an array whose elements are these - * two-character strings. - */ + // |slice(-2)| chomps off the last two characters of a string. + // + // Therefore, if the Unicode value is < 10, we have a single-character hex + // string when we want one that's two characters, and unconditionally + // prepending a "0" solves the problem. return Array.from(data, (c, i) => ("0" + data.charCodeAt(i).toString(16)).slice(-2)).join(""); } diff --git a/security/manager/ssl/tests/unit/test_signed_apps.js b/security/manager/ssl/tests/unit/test_signed_apps.js index 0eaf5fc6492..2b84a009ff7 100644 --- a/security/manager/ssl/tests/unit/test_signed_apps.js +++ b/security/manager/ssl/tests/unit/test_signed_apps.js @@ -5,18 +5,18 @@ PATH=$NSS/bin:$NSS/lib:$PATH ./generate.sh cd ../../../../../.. make -C $OBJDIR/security/manager/ssl/tests - - $NSS is the path to NSS binaries and libraries built for the host platform. + + $NSS is the path to NSS binaries and libraries built for the host platform. If you get error messages about "CertUtil" on Windows, then it means that the Windows CertUtil.exe is ahead of the NSS certutil.exe in $PATH. - + Check in the generated files. These steps are not done as part of the build because we do not want to add a build-time dependency on the OpenSSL or NSS tools or libraries built for the host platform. */ // XXX from prio.h -const PR_RDWR = 0x04; +const PR_RDWR = 0x04; const PR_CREATE_FILE = 0x08; const PR_TRUNCATE = 0x20; @@ -68,7 +68,7 @@ function tamper(inFilePath, outFilePath, modifications, newEntries) { } finally { reader.close(); } - + // Any leftover modification means that we were expecting to modify an entry // in the input file that wasn't there. for(var name in modifications) { @@ -76,7 +76,7 @@ function tamper(inFilePath, outFilePath, modifications, newEntries) { throw "input file was missing expected entries: " + name; } } - + // Now, append any new entries to the end newEntries.forEach(function(newEntry) { var sis = Cc["@mozilla.org/io/string-input-stream;1"] diff --git a/services/mobileid/MobileIdentityManager.jsm b/services/mobileid/MobileIdentityManager.jsm index 77d99def065..480c601fbd7 100644 --- a/services/mobileid/MobileIdentityManager.jsm +++ b/services/mobileid/MobileIdentityManager.jsm @@ -8,6 +8,7 @@ this.EXPORTED_SYMBOLS = ["MobileIdentityManager"]; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/MobileIdentityCommon.jsm"); Cu.import("resource://gre/modules/MobileIdentityUIGlueCommon.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); @@ -51,19 +52,19 @@ XPCOMUtils.defineLazyServiceGetter(this, "appsService", "@mozilla.org/AppsService;1", "nsIAppsService"); -#ifdef MOZ_B2G_RIL -XPCOMUtils.defineLazyServiceGetter(this, "Ril", - "@mozilla.org/ril;1", - "nsIRadioInterfaceLayer"); +if (AppConstants.MOZ_B2G_RIL) { + XPCOMUtils.defineLazyServiceGetter(this, "Ril", + "@mozilla.org/ril;1", + "nsIRadioInterfaceLayer"); -XPCOMUtils.defineLazyServiceGetter(this, "IccService", - "@mozilla.org/icc/iccservice;1", - "nsIIccService"); + XPCOMUtils.defineLazyServiceGetter(this, "IccService", + "@mozilla.org/icc/iccservice;1", + "nsIIccService"); -XPCOMUtils.defineLazyServiceGetter(this, "MobileConnectionService", - "@mozilla.org/mobileconnection/mobileconnectionservice;1", - "nsIMobileConnectionService"); -#endif + XPCOMUtils.defineLazyServiceGetter(this, "MobileConnectionService", + "@mozilla.org/mobileconnection/mobileconnectionservice;1", + "nsIMobileConnectionService"); +} this.MobileIdentityManager = { @@ -107,156 +108,132 @@ this.MobileIdentityManager = { /********************************************************* * Getters ********************************************************/ -#ifdef MOZ_B2G_RIL - // We have these getters to allow mocking RIL stuff from the tests. - get ril() { - if (this._ril) { - return this._ril; - } - return Ril; - }, - - get iccService() { - if (this._iccService) { - return this._iccService; - } - return IccService; - }, - - get mobileConnectionService() { - if (this._mobileConnectionService) { - return this._mobileConnectionService; - } - return MobileConnectionService; - }, -#endif - get iccInfo() { if (this._iccInfo) { return this._iccInfo; } -#ifdef MOZ_B2G_RIL - let self = this; - let iccListener = { - notifyStkCommand: function() {}, + if (AppConstants.MOZ_B2G_RIL) { + let self = this; + let iccListener = { + notifyStkCommand: function() {}, - notifyStkSessionEnd: function() {}, + notifyStkSessionEnd: function() {}, - notifyCardStateChanged: function() {}, + notifyCardStateChanged: function() {}, - notifyIccInfoChanged: function() { - // If we receive a notification about an ICC info change, we clear - // the ICC related caches so they can be rebuilt with the new changes. + notifyIccInfoChanged: function() { + // If we receive a notification about an ICC info change, we clear + // the ICC related caches so they can be rebuilt with the new changes. - log.debug("ICC info changed observed. Clearing caches"); + log.debug("ICC info changed observed. Clearing caches"); - // We don't need to keep listening for changes until we rebuild the - // cache again. - for (let i = 0; i < self._iccInfo.length; i++) { - let icc = self.iccService.getIccByServiceId(i); - if (icc) { - icc.unregisterListener(iccListener); + // We don't need to keep listening for changes until we rebuild the + // cache again. + for (let i = 0; i < self._iccInfo.length; i++) { + let icc = self.iccService.getIccByServiceId(i); + if (icc) { + icc.unregisterListener(iccListener); + } } + + self._iccInfo = null; + self._iccIds = null; + } + }; + + // _iccInfo is a local cache containing the information about the SIM cards + // that is interesting for the Mobile ID flow. + // The index of this array does not necesarily need to match the real + // identifier of the SIM card ("clientId" or "serviceId" in RIL language). + this._iccInfo = []; + + for (let i = 0; i < this.ril.numRadioInterfaces; i++) { + let icc = this.iccService.getIccByServiceId(i); + if (!icc) { + log.warn("Tried to get the Icc instance for an invalid service ID " + i); + continue; } - self._iccInfo = null; - self._iccIds = null; - } - }; - - // _iccInfo is a local cache containing the information about the SIM cards - // that is interesting for the Mobile ID flow. - // The index of this array does not necesarily need to match the real - // identifier of the SIM card ("clientId" or "serviceId" in RIL language). - this._iccInfo = []; - - for (let i = 0; i < this.ril.numRadioInterfaces; i++) { - let icc = this.iccService.getIccByServiceId(i); - if (!icc) { - log.warn("Tried to get the Icc instance for an invalid service ID " + i); - continue; - } - - let info = icc.iccInfo; - if (!info || !info.iccid || - !info.mcc || !info.mcc.length || - !info.mnc || !info.mnc.length) { - log.warn("Absent or invalid ICC info"); - continue; - } - - // GSM SIMs may have MSISDN while CDMA SIMs may have MDN - let phoneNumber = null; - try { - if (info.iccType === "sim" || info.iccType === "usim") { - let gsmInfo = info.QueryInterface(Ci.nsIGsmIccInfo); - phoneNumber = gsmInfo.msisdn; - } else if (info.iccType === "ruim" || info.iccType === "csim") { - let cdmaInfo = info.QueryInterface(Ci.nsICdmaIccInfo); - phoneNumber = cdmaInfo.mdn; + let info = icc.iccInfo; + if (!info || !info.iccid || + !info.mcc || !info.mcc.length || + !info.mnc || !info.mnc.length) { + log.warn("Absent or invalid ICC info"); + continue; } - } catch (e) { - log.error("Failed to retrieve phoneNumber: " + e); + + // GSM SIMs may have MSISDN while CDMA SIMs may have MDN + let phoneNumber = null; + try { + if (info.iccType === "sim" || info.iccType === "usim") { + let gsmInfo = info.QueryInterface(Ci.nsIGsmIccInfo); + phoneNumber = gsmInfo.msisdn; + } else if (info.iccType === "ruim" || info.iccType === "csim") { + let cdmaInfo = info.QueryInterface(Ci.nsICdmaIccInfo); + phoneNumber = cdmaInfo.mdn; + } + } catch (e) { + log.error("Failed to retrieve phoneNumber: " + e); + } + + let connection = this.mobileConnectionService.getItemByServiceId(i); + let voice = connection && connection.voice; + let data = connection && connection.data; + let operator = null; + if (voice && + voice.network && + voice.network.shortName && + voice.network.shortName.length) { + operator = voice.network.shortName; + } else if (data && + data.network && + data.network.shortName && + data.network.shortName.length) { + operator = data.network.shortName; + } + + this._iccInfo.push({ + // Because it is possible that the _iccInfo array index doesn't match + // the real client ID, we need to store this value for later usage. + clientId: i, + iccId: info.iccid, + mcc: info.mcc, + mnc: info.mnc, + msisdn: phoneNumber, + operator: operator, + roaming: voice && voice.roaming + }); + + // We need to subscribe to ICC change notifications so we can refresh + // the cache if any change is observed. + icc.registerListener(iccListener); } - let connection = this.mobileConnectionService.getItemByServiceId(i); - let voice = connection && connection.voice; - let data = connection && connection.data; - let operator = null; - if (voice && - voice.network && - voice.network.shortName && - voice.network.shortName.length) { - operator = voice.network.shortName; - } else if (data && - data.network && - data.network.shortName && - data.network.shortName.length) { - operator = data.network.shortName; - } - - this._iccInfo.push({ - // Because it is possible that the _iccInfo array index doesn't match - // the real client ID, we need to store this value for later usage. - clientId: i, - iccId: info.iccid, - mcc: info.mcc, - mnc: info.mnc, - msisdn: phoneNumber, - operator: operator, - roaming: voice && voice.roaming - }); - - // We need to subscribe to ICC change notifications so we can refresh - // the cache if any change is observed. - icc.registerListener(iccListener); + return this._iccInfo; + } else { + return null; } - - return this._iccInfo; -#else - return null; -#endif }, get iccIds() { -#ifdef MOZ_B2G_RIL - if (this._iccIds) { + if (AppConstants.MOZ_B2G_RIL) { + if (this._iccIds) { + return this._iccIds; + } + + this._iccIds = []; + if (!this.iccInfo) { + return this._iccIds; + } + + for (let i = 0; i < this.iccInfo.length; i++) { + this._iccIds.push(this.iccInfo[i].iccId); + } + return this._iccIds; + } else { + return null; } - - this._iccIds = []; - if (!this.iccInfo) { - return this._iccIds; - } - - for (let i = 0; i < this.iccInfo.length; i++) { - this._iccIds.push(this.iccInfo[i].iccId); - } - - return this._iccIds; -#else - return null; -#endif }, get credStore() { @@ -551,8 +528,8 @@ this.MobileIdentityManager = { this.ui, this.client ); -#ifdef MOZ_B2G_RIL - } else if (aToVerify.verificationMethod.indexOf(SMS_MO_MT) != -1 && + } else if (AppConstants.MOZ_B2G_RIL && + aToVerify.verificationMethod.indexOf(SMS_MO_MT) != -1 && aToVerify.serviceId && aToVerify.verificationDetails && aToVerify.verificationDetails.moVerifier && @@ -567,7 +544,6 @@ this.MobileIdentityManager = { this.ui, this.client ); -#endif } else { return Promise.reject(ERROR_INTERNAL_CANNOT_VERIFY_SELECTION); } @@ -1068,4 +1044,40 @@ this.MobileIdentityManager = { }; +if (AppConstants.MOZ_B2G_RIL) { + // We have these getters to allow mocking RIL stuff from the tests. + Object.defineProperties(MobileIdentityManager, { + "ril": { + configurable: true, + enumerable: true, + get() { + if (this._ril) { + return this._ril; + } + return Ril; + } + }, + "iccService": { + configurable: true, + enumerable: true, + get() { + if (this._iccService) { + return this._iccService; + } + return IccService; + } + }, + "mobileConnectionService": { + configurable: true, + enumerable: true, + get() { + if (this._mobileConnectionService) { + return this._mobileConnectionService; + } + return MobileConnectionService; + } + } + }); +} + MobileIdentityManager.init(); diff --git a/services/mobileid/MobileIdentitySmsVerificationFlow.jsm b/services/mobileid/MobileIdentitySmsVerificationFlow.jsm index 2e1826a56d9..99f90706562 100644 --- a/services/mobileid/MobileIdentitySmsVerificationFlow.jsm +++ b/services/mobileid/MobileIdentitySmsVerificationFlow.jsm @@ -8,16 +8,17 @@ this.EXPORTED_SYMBOLS = ["MobileIdentitySmsVerificationFlow"]; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/MobileIdentityCommon.jsm"); Cu.import("resource://gre/modules/MobileIdentityVerificationFlow.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -#ifdef MOZ_B2G_RIL -XPCOMUtils.defineLazyServiceGetter(this, "smsService", - "@mozilla.org/sms/smsservice;1", - "nsISmsService"); -#endif +if (AppConstants.MOZ_B2G_RIL) { + XPCOMUtils.defineLazyServiceGetter(this, "smsService", + "@mozilla.org/sms/smsservice;1", + "nsISmsService"); +} this.MobileIdentitySmsVerificationFlow = function(aVerificationOptions, aUI, @@ -51,58 +52,58 @@ this.MobileIdentitySmsVerificationFlow.prototype = { // is unknown for us, so we always observe for incoming messages coming // from the given mtSender. -#ifdef MOZ_B2G_RIL - this.observedSilentNumber = this.verificationOptions.mtSender; - try { - smsService.addSilentNumber(this.observedSilentNumber); - } catch (e) { - log.warn("We are already listening for that number"); + if (AppConstants.MOZ_B2G_RIL) { + this.observedSilentNumber = this.verificationOptions.mtSender; + try { + smsService.addSilentNumber(this.observedSilentNumber); + } catch (e) { + log.warn("We are already listening for that number"); + } + + this.onSilentSms = (function(aSubject, aTopic, aData) { + log.debug("Got silent message " + aSubject.sender + " - " + aSubject.body); + // We might have observed a notification of an incoming silent message + // for other number. In that case, we just bail out. + if (aSubject.sender != this.observedSilentNumber) { + return; + } + + // We got the SMS containing the verification code. + + // If the phone number we are trying to verify is or can be an external + // phone number (meaning that it doesn't belong to any of the inserted + // SIMs) we will be receiving an human readable SMS containing a short + // verification code. In this case we need to parse the SMS body to + // extract the verification code. + // Otherwise, we just use the whole SMS body as it should contain a long + // verification code. + let verificationCode = aSubject.body; + if (this.verificationOptions.external) { + // We just take the numerical characters from the body. + verificationCode = aSubject.body.replace(/[^0-9]/g,''); + } + + log.debug("Verification code: " + verificationCode); + + this.verificationCodeDeferred.resolve(verificationCode); + }).bind(this); + + Services.obs.addObserver(this.onSilentSms, + SILENT_SMS_RECEIVED_TOPIC, + false); + log.debug("Observing messages from " + this.observedSilentNumber); } - this.onSilentSms = (function(aSubject, aTopic, aData) { - log.debug("Got silent message " + aSubject.sender + " - " + aSubject.body); - // We might have observed a notification of an incoming silent message - // for other number. In that case, we just bail out. - if (aSubject.sender != this.observedSilentNumber) { - return; - } - - // We got the SMS containing the verification code. - - // If the phone number we are trying to verify is or can be an external - // phone number (meaning that it doesn't belong to any of the inserted - // SIMs) we will be receiving an human readable SMS containing a short - // verification code. In this case we need to parse the SMS body to - // extract the verification code. - // Otherwise, we just use the whole SMS body as it should contain a long - // verification code. - let verificationCode = aSubject.body; - if (this.verificationOptions.external) { - // We just take the numerical characters from the body. - verificationCode = aSubject.body.replace(/[^0-9]/g,''); - } - - log.debug("Verification code: " + verificationCode); - - this.verificationCodeDeferred.resolve(verificationCode); - }).bind(this); - - Services.obs.addObserver(this.onSilentSms, - SILENT_SMS_RECEIVED_TOPIC, - false); - log.debug("Observing messages from " + this.observedSilentNumber); -#endif - return this.smsVerifyStrategy(); }, _cleanupStrategy: function() { -#ifdef MOZ_B2G_RIL - smsService.removeSilentNumber(this.observedSilentNumber); - Services.obs.removeObserver(this.onSilentSms, - SILENT_SMS_RECEIVED_TOPIC); - this.observedSilentNumber = null; - this.onSilentSms = null; -#endif + if (AppConstants.MOZ_B2G_RIL) { + smsService.removeSilentNumber(this.observedSilentNumber); + Services.obs.removeObserver(this.onSilentSms, + SILENT_SMS_RECEIVED_TOPIC); + this.observedSilentNumber = null; + this.onSilentSms = null; + } } }; diff --git a/services/mobileid/moz.build b/services/mobileid/moz.build index e18d282b11b..908e3f852e0 100644 --- a/services/mobileid/moz.build +++ b/services/mobileid/moz.build @@ -12,13 +12,10 @@ EXTRA_JS_MODULES += [ 'MobileIdentityClient.jsm', 'MobileIdentityCommon.jsm', 'MobileIdentityCredentialsStore.jsm', + 'MobileIdentityManager.jsm', 'MobileIdentitySmsMoMtVerificationFlow.jsm', 'MobileIdentitySmsMtVerificationFlow.jsm', + 'MobileIdentitySmsVerificationFlow.jsm', 'MobileIdentityUIGlueCommon.jsm', 'MobileIdentityVerificationFlow.jsm' ] - -EXTRA_PP_JS_MODULES += [ - 'MobileIdentityManager.jsm', - 'MobileIdentitySmsVerificationFlow.jsm' -] diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index 26ffd76dce9..5435bccdd1f 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -33,6 +33,19 @@ except ImportError: def get_default_valgrind_suppression_files(): # We are trying to locate files in the source tree. So if we # don't know where the source tree is, we must give up. + # + # When this is being run by |mach mochitest --valgrind ...|, it is + # expected that |build_obj| is not None, and so the logic below will + # select the correct suppression files. + # + # When this is run from mozharness, |build_obj| is None, and we expect + # that testing/mozharness/configs/unittests/linux_unittests.py will + # select the correct suppression files (and paths to them) and + # will specify them using the --valgrind-supp-files= flag. Hence this + # function will not get called when running from mozharness. + # + # Note: keep these Valgrind .sup file names consistent with those + # in testing/mozharness/configs/unittests/linux_unittest.py. if build_obj is None or build_obj.topsrcdir is None: return [] diff --git a/testing/mochitest/moz.build b/testing/mochitest/moz.build index ea1a8bb0da9..a65aa717a9a 100644 --- a/testing/mochitest/moz.build +++ b/testing/mochitest/moz.build @@ -29,6 +29,9 @@ TEST_HARNESS_FILES.testing.mochitest += [ '/build/mobile/remoteautomation.py', '/build/pgo/server-locations.txt', '/build/sanitizers/lsan_suppressions.txt', + '/build/valgrind/cross-architecture.sup', + '/build/valgrind/i386-redhat-linux-gnu.sup', + '/build/valgrind/x86_64-redhat-linux-gnu.sup', '/netwerk/test/httpserver/httpd.js', '/testing/mozbase/mozdevice/mozdevice/devicemanager.py', '/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py', diff --git a/testing/mozharness/configs/unittests/linux_unittest.py b/testing/mozharness/configs/unittests/linux_unittest.py index ade9385acea..6ab0aeb1960 100644 --- a/testing/mozharness/configs/unittests/linux_unittest.py +++ b/testing/mozharness/configs/unittests/linux_unittest.py @@ -9,12 +9,24 @@ XPCSHELL_NAME = "xpcshell" EXE_SUFFIX = "" DISABLE_SCREEN_SAVER = True ADJUST_MOUSE_AND_SCREEN = False + +# Note: keep these Valgrind .sup file names consistent with those +# in testing/mochitest/mochitest_options.py. +VALGRIND_SUPP_DIR = os.path.join(os.getcwd(), "build/tests/mochitest") +VALGRIND_SUPP_CROSS_ARCH = os.path.join(VALGRIND_SUPP_DIR, + "cross-architecture.sup") +VALGRIND_SUPP_ARCH = None + if platform.architecture()[0] == "64bit": TOOLTOOL_MANIFEST_PATH = "config/tooltool-manifests/linux64/releng.manifest" MINIDUMP_STACKWALK_PATH = "linux64-minidump_stackwalk" + VALGRIND_SUPP_ARCH = os.path.join(VALGRIND_SUPP_DIR, + "x86_64-redhat-linux-gnu.sup") else: TOOLTOOL_MANIFEST_PATH = "config/tooltool-manifests/linux32/releng.manifest" MINIDUMP_STACKWALK_PATH = "linux32-minidump_stackwalk" + VALGRIND_SUPP_ARCH = os.path.join(VALGRIND_SUPP_DIR, + "i386-redhat-linux-gnu.sup") ##### config = { @@ -188,6 +200,9 @@ config = { }, # local mochi suites "all_mochitest_suites": { + "valgrind-plain": ["--valgrind=/usr/bin/valgrind", + "--valgrind-supp-files=" + VALGRIND_SUPP_ARCH + + "," + VALGRIND_SUPP_CROSS_ARCH], "plain": [], "plain-chunked": ["--chunk-by-dir=4"], "mochitest-push": ["--subsuite=push"], diff --git a/testing/mozharness/mach_commands.py b/testing/mozharness/mach_commands.py index 1115cb96949..91388480c50 100644 --- a/testing/mozharness/mach_commands.py +++ b/testing/mozharness/mach_commands.py @@ -53,6 +53,11 @@ class MozharnessRunner(MozbuildObject): "--test-packages-url", self.test_packages_url] }, + "mochitest-valgrind": { + "script": "desktop_unittest.py", + "config": desktop_unittest_config + [ + "--mochitest-suite", "valgrind-plain"] + }, "mochitest": { "script": "desktop_unittest.py", "config": desktop_unittest_config + [ diff --git a/testing/mozharness/mozharness/mozilla/testing/errors.py b/testing/mozharness/mozharness/mozilla/testing/errors.py index 09e1353b6ed..d0acd29ad53 100644 --- a/testing/mozharness/mozharness/mozilla/testing/errors.py +++ b/testing/mozharness/mozharness/mozilla/testing/errors.py @@ -88,14 +88,9 @@ TinderBoxPrintRe = { "harness_error": { 'full_regex': re.compile(r"(?:TEST-UNEXPECTED-FAIL|PROCESS-CRASH) \| .* \| (application crashed|missing output line for total leaks!|negative leaks caught!|\d+ bytes leaked)"), - 'minimum_regex': re.compile(r'''(TEST-UNEXPECTED|PROCESS-CRASH)'''), + 'minimum_regex': re.compile(r'''(TEST-UNEXPECTED|PROCESS-CRASH|TEST-VALGRIND-ERROR)'''), 'retry_regex': re.compile(r'''FAIL-SHOULD-RETRY''') }, - "valgrind_error": { - 'substr': 'TEST-VALGRIND-ERROR', - 'level': ERROR, - 'explanation': 'Valgrind detected memory errors during the run' - }, } TestPassed = [ diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index c7c06998045..427edc6a089 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -13583,6 +13583,10 @@ "path": "cssom-view/elementsFromPoint.html", "url": "/cssom-view/elementsFromPoint.html" }, + { + "path": "cssom-view/negativeMargins.html", + "url": "/cssom-view/negativeMargins.html" + }, { "path": "cssom-view/scrollingElement.html", "url": "/cssom-view/scrollingElement.html" @@ -15627,6 +15631,18 @@ "path": "html/browsers/browsing-the-web/history-traversal/hashchange_event.html", "url": "/html/browsers/browsing-the-web/history-traversal/hashchange_event.html" }, + { + "path": "html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html", + "url": "/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html" + }, + { + "path": "html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html", + "url": "/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html" + }, + { + "path": "html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html", + "url": "/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html" + }, { "path": "html/browsers/browsing-the-web/history-traversal/popstate_event.html", "url": "/html/browsers/browsing-the-web/history-traversal/popstate_event.html" @@ -19515,6 +19531,10 @@ "path": "html/webappapis/scripting/events/event-handler-javascript.html", "url": "/html/webappapis/scripting/events/event-handler-javascript.html" }, + { + "path": "html/webappapis/scripting/events/event-handler-onresize.html", + "url": "/html/webappapis/scripting/events/event-handler-onresize.html" + }, { "path": "html/webappapis/scripting/events/event-handler-spec-example.html", "url": "/html/webappapis/scripting/events/event-handler-spec-example.html" @@ -31964,12 +31984,12 @@ "url": "/workers/interfaces/WorkerUtils/WindowTimers/004.html" }, { - "path": "workers/interfaces/WorkerUtils/importScripts/001.html", - "url": "/workers/interfaces/WorkerUtils/importScripts/001.html" + "path": "workers/interfaces/WorkerUtils/importScripts/001.worker.js", + "url": "/workers/interfaces/WorkerUtils/importScripts/001.worker" }, { - "path": "workers/interfaces/WorkerUtils/importScripts/002.html", - "url": "/workers/interfaces/WorkerUtils/importScripts/002.html" + "path": "workers/interfaces/WorkerUtils/importScripts/002.worker.js", + "url": "/workers/interfaces/WorkerUtils/importScripts/002.worker" }, { "path": "workers/interfaces/WorkerUtils/importScripts/003.html", @@ -32498,6 +32518,16 @@ "timeout": "long", "url": "/eventsource/shared-worker/eventsource-constructor-non-same-origin.htm" }, + { + "path": "html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html", + "timeout": "long", + "url": "/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html" + }, + { + "path": "html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html", + "timeout": "long", + "url": "/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html" + }, { "path": "html/browsers/browsing-the-web/scroll-to-fragid/007.html", "timeout": "long", @@ -33428,6 +33458,11 @@ "timeout": "long", "url": "/service-workers/cache-storage/serviceworker/cache-match.https.html" }, + { + "path": "service-workers/cache-storage/serviceworker/cache-matchAll.https.html", + "timeout": "long", + "url": "/service-workers/cache-storage/serviceworker/cache-matchAll.https.html" + }, { "path": "service-workers/cache-storage/serviceworker/cache-put.https.html", "timeout": "long", @@ -33463,6 +33498,11 @@ "timeout": "long", "url": "/service-workers/cache-storage/window/cache-match.https.html" }, + { + "path": "service-workers/cache-storage/window/cache-matchAll.https.html", + "timeout": "long", + "url": "/service-workers/cache-storage/window/cache-matchAll.https.html" + }, { "path": "service-workers/cache-storage/window/cache-put.https.html", "timeout": "long", @@ -33503,6 +33543,11 @@ "timeout": "long", "url": "/service-workers/cache-storage/worker/cache-match.https.html" }, + { + "path": "service-workers/cache-storage/worker/cache-matchAll.https.html", + "timeout": "long", + "url": "/service-workers/cache-storage/worker/cache-matchAll.https.html" + }, { "path": "service-workers/cache-storage/worker/cache-put.https.html", "timeout": "long", @@ -33713,16 +33758,7 @@ }, "local_changes": { "deleted": [], - "items": { - "testharness": { - "cssom-view/negativeMargins.html": [ - { - "path": "cssom-view/negativeMargins.html", - "url": "/cssom-view/negativeMargins.html" - } - ] - } - }, + "items": {}, "reftest_nodes": {} }, "reftest_nodes": { @@ -39835,7 +39871,7 @@ } ] }, - "rev": "f3f87e1a2cc3845819038a8a6fe435bb6092e213", + "rev": "af65262f5f3400024279c526117489f1f11d3233", "url_base": "/", "version": 2 } diff --git a/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini b/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini new file mode 100644 index 00000000000..ae72b8550f3 --- /dev/null +++ b/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini @@ -0,0 +1,6 @@ +[scroll-restoration-fragment-scrolling-cross-origin.html] + type: testharness + expected: ERROR + [Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation] + expected: TIMEOUT + diff --git a/testing/web-platform/meta/html/semantics/forms/the-input-element/checkbox.html.ini b/testing/web-platform/meta/html/semantics/forms/the-input-element/checkbox.html.ini index e1846d40560..5f97421f2bf 100644 --- a/testing/web-platform/meta/html/semantics/forms/the-input-element/checkbox.html.ini +++ b/testing/web-platform/meta/html/semantics/forms/the-input-element/checkbox.html.ini @@ -3,9 +3,6 @@ [click on mutable checkbox fires the input and change events] expected: FAIL - [canceled activation steps on unchecked checkbox] - expected: FAIL - - [canceled activation steps on unchecked checkbox (indeterminate=true in onclick)] + [click on mutable checkbox fires a click event, then an input event, then a change event] expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/forms/the-input-element/radio.html.ini b/testing/web-platform/meta/html/semantics/forms/the-input-element/radio.html.ini index b73b3614c96..0f6a91b11c5 100644 --- a/testing/web-platform/meta/html/semantics/forms/the-input-element/radio.html.ini +++ b/testing/web-platform/meta/html/semantics/forms/the-input-element/radio.html.ini @@ -3,6 +3,6 @@ [click on mutable radio fires the input event] expected: FAIL - [canceled activation steps on unchecked radio] + [click on mutable radio fires click event, then input event, then change event] expected: FAIL diff --git a/testing/web-platform/meta/mozilla-sync b/testing/web-platform/meta/mozilla-sync index 269ca479cd1..5f5462944ea 100644 --- a/testing/web-platform/meta/mozilla-sync +++ b/testing/web-platform/meta/mozilla-sync @@ -1 +1 @@ -a035c3724da7e8235c26a3b6ea567365efda3b15 \ No newline at end of file +fa876a5774a3178efb42d6ebd8c8df045e03a80b \ No newline at end of file diff --git a/testing/web-platform/meta/service-workers/cache-storage/serviceworker/cache-add.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/serviceworker/cache-add.https.html.ini new file mode 100644 index 00000000000..b67f2f79fd2 --- /dev/null +++ b/testing/web-platform/meta/service-workers/cache-storage/serviceworker/cache-add.https.html.ini @@ -0,0 +1,5 @@ +[cache-add.https.html] + type: testharness + [Cache.add with request with null body (not consumed)] + expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini new file mode 100644 index 00000000000..b67f2f79fd2 --- /dev/null +++ b/testing/web-platform/meta/service-workers/cache-storage/window/cache-add.https.html.ini @@ -0,0 +1,5 @@ +[cache-add.https.html] + type: testharness + [Cache.add with request with null body (not consumed)] + expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini new file mode 100644 index 00000000000..b67f2f79fd2 --- /dev/null +++ b/testing/web-platform/meta/service-workers/cache-storage/worker/cache-add.https.html.ini @@ -0,0 +1,5 @@ +[cache-add.https.html] + type: testharness + [Cache.add with request with null body (not consumed)] + expected: FAIL + diff --git a/testing/web-platform/tests/DOMEvents/OWNERS b/testing/web-platform/tests/DOMEvents/OWNERS new file mode 100644 index 00000000000..50802902fb8 --- /dev/null +++ b/testing/web-platform/tests/DOMEvents/OWNERS @@ -0,0 +1,3 @@ +@jdm +@zqzhang +@Ms2ger diff --git a/testing/web-platform/tests/FileAPI/OWNERS b/testing/web-platform/tests/FileAPI/OWNERS new file mode 100644 index 00000000000..03bb9336574 --- /dev/null +++ b/testing/web-platform/tests/FileAPI/OWNERS @@ -0,0 +1,6 @@ +@inexorabletash +@plehegar +@zqzhang +@zcorpan +@jdm +@Ms2ger diff --git a/testing/web-platform/tests/IndexedDB/OWNERS b/testing/web-platform/tests/IndexedDB/OWNERS new file mode 100644 index 00000000000..711894a54b3 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/OWNERS @@ -0,0 +1,8 @@ +@Velmont +@inexorabletash +@chunywang +@dumbmatter +@zqzhang +@yunxiaoxie +@zhaozihao +@foolip diff --git a/testing/web-platform/tests/WebCryptoAPI/OWNERS b/testing/web-platform/tests/WebCryptoAPI/OWNERS new file mode 100644 index 00000000000..70190999b7c --- /dev/null +++ b/testing/web-platform/tests/WebCryptoAPI/OWNERS @@ -0,0 +1,2 @@ +@Wafflespeanut +@Ms2ger diff --git a/testing/web-platform/tests/XMLHttpRequest/OWNERS b/testing/web-platform/tests/XMLHttpRequest/OWNERS new file mode 100644 index 00000000000..7a809e70f6c --- /dev/null +++ b/testing/web-platform/tests/XMLHttpRequest/OWNERS @@ -0,0 +1,13 @@ +@ecoal95 +@hallvors +@kangxu +@caitp +@Manishearth +@plehegar +@foolip +@jungkees +@ibelem +@mathiasbynens +@ronkorving +@jdm +@Ms2ger diff --git a/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm b/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm index ed53996dcec..a1bdc8e97db 100644 --- a/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm +++ b/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm @@ -43,7 +43,7 @@ } test(function() { - assert_true((new Date(client.getResponseHeader('Last-Modified'))).getTime() == (new Date(client.responseXML.lastModified)).getTime(), 'responseXML.lastModified time shoud be equal to time in response Last-Modified header') + assert_true((new Date(client.getResponseHeader('Last-Modified'))).getTime() == (new Date(client.responseXML.lastModified)).getTime(), 'responseXML.lastModified time should be equal to time in response Last-Modified header') }, 'lastModified set according to HTTP header') test(function() { diff --git a/testing/web-platform/tests/ambient-light/OWNERS b/testing/web-platform/tests/ambient-light/OWNERS new file mode 100644 index 00000000000..548c25a14da --- /dev/null +++ b/testing/web-platform/tests/ambient-light/OWNERS @@ -0,0 +1,3 @@ +@zqzhang +@Volker-E +@dontcallmedom diff --git a/testing/web-platform/tests/app-uri/OWNERS b/testing/web-platform/tests/app-uri/OWNERS new file mode 100644 index 00000000000..261dea7df72 --- /dev/null +++ b/testing/web-platform/tests/app-uri/OWNERS @@ -0,0 +1 @@ +@happyoungj diff --git a/testing/web-platform/tests/common/OWNERS b/testing/web-platform/tests/common/OWNERS new file mode 100644 index 00000000000..1da08cf4983 --- /dev/null +++ b/testing/web-platform/tests/common/OWNERS @@ -0,0 +1,4 @@ +@zqzhang +@foolip +@dontcallmedom +@deniak diff --git a/testing/web-platform/tests/conformance-checkers/OWNERS b/testing/web-platform/tests/conformance-checkers/OWNERS new file mode 100644 index 00000000000..2ac6145caa8 --- /dev/null +++ b/testing/web-platform/tests/conformance-checkers/OWNERS @@ -0,0 +1 @@ +@sideshowbarker diff --git a/testing/web-platform/tests/content-security-policy/OWNERS b/testing/web-platform/tests/content-security-policy/OWNERS new file mode 100644 index 00000000000..27348607493 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/OWNERS @@ -0,0 +1,2 @@ +@sideshowbarker +@hillbrad diff --git a/testing/web-platform/tests/cors/OWNERS b/testing/web-platform/tests/cors/OWNERS new file mode 100644 index 00000000000..c8979792445 --- /dev/null +++ b/testing/web-platform/tests/cors/OWNERS @@ -0,0 +1,5 @@ +@sideshowbarker +@zqzhang +@Velmont +@hillbrad +@jdm diff --git a/testing/web-platform/tests/cssom-view/OWNERS b/testing/web-platform/tests/cssom-view/OWNERS new file mode 100644 index 00000000000..cd9ff2eee4b --- /dev/null +++ b/testing/web-platform/tests/cssom-view/OWNERS @@ -0,0 +1 @@ +@AutomatedTester diff --git a/testing/web-platform/tests/custom-elements/OWNERS b/testing/web-platform/tests/custom-elements/OWNERS new file mode 100644 index 00000000000..4671167903f --- /dev/null +++ b/testing/web-platform/tests/custom-elements/OWNERS @@ -0,0 +1,3 @@ +@deepak-sa +@sgrekhov +@alsemenov diff --git a/testing/web-platform/tests/docs/OWNERS b/testing/web-platform/tests/docs/OWNERS new file mode 100644 index 00000000000..af3e0845c35 --- /dev/null +++ b/testing/web-platform/tests/docs/OWNERS @@ -0,0 +1,4 @@ +@sideshowbarker +@dontcallmedom +@zcorpan +@Ms2ger diff --git a/testing/web-platform/tests/dom/OWNERS b/testing/web-platform/tests/dom/OWNERS new file mode 100644 index 00000000000..fad498154e4 --- /dev/null +++ b/testing/web-platform/tests/dom/OWNERS @@ -0,0 +1,6 @@ +@ayg +@jdm +@Ms2ger +@plehegar +@zcorpan +@zqzhang diff --git a/testing/web-platform/tests/domparsing/OWNERS b/testing/web-platform/tests/domparsing/OWNERS new file mode 100644 index 00000000000..113c79d205d --- /dev/null +++ b/testing/web-platform/tests/domparsing/OWNERS @@ -0,0 +1,5 @@ +@sideshowbarker +@ChrisParis +@deniak +@jdm +@Ms2ger diff --git a/testing/web-platform/tests/domxpath/OWNERS b/testing/web-platform/tests/domxpath/OWNERS new file mode 100644 index 00000000000..7c764a42450 --- /dev/null +++ b/testing/web-platform/tests/domxpath/OWNERS @@ -0,0 +1,3 @@ +@gsnedders +@zqzhang +@deniak diff --git a/testing/web-platform/tests/editing/OWNERS b/testing/web-platform/tests/editing/OWNERS new file mode 100644 index 00000000000..ce908c45b4a --- /dev/null +++ b/testing/web-platform/tests/editing/OWNERS @@ -0,0 +1 @@ +@ayg diff --git a/testing/web-platform/tests/encoding/OWNERS b/testing/web-platform/tests/encoding/OWNERS new file mode 100644 index 00000000000..4917e26650a --- /dev/null +++ b/testing/web-platform/tests/encoding/OWNERS @@ -0,0 +1,2 @@ +@inexorabletash +@sideshowbarker diff --git a/testing/web-platform/tests/eventsource/OWNERS b/testing/web-platform/tests/eventsource/OWNERS new file mode 100644 index 00000000000..4f8d605d71c --- /dev/null +++ b/testing/web-platform/tests/eventsource/OWNERS @@ -0,0 +1,5 @@ +@zqzhang +@sideshowbarker +@deniak +@Velmont +@Yaffle diff --git a/testing/web-platform/tests/fetch/OWNERS b/testing/web-platform/tests/fetch/OWNERS new file mode 100644 index 00000000000..fbeac366e3c --- /dev/null +++ b/testing/web-platform/tests/fetch/OWNERS @@ -0,0 +1,2 @@ +@jdm +@youennf diff --git a/testing/web-platform/tests/gamepad/OWNERS b/testing/web-platform/tests/gamepad/OWNERS new file mode 100644 index 00000000000..d7fbc2625bd --- /dev/null +++ b/testing/web-platform/tests/gamepad/OWNERS @@ -0,0 +1 @@ +@luser diff --git a/testing/web-platform/tests/geolocation-API/OWNERS b/testing/web-platform/tests/geolocation-API/OWNERS new file mode 100644 index 00000000000..64e2488b4ba --- /dev/null +++ b/testing/web-platform/tests/geolocation-API/OWNERS @@ -0,0 +1,2 @@ +@zqzhang +@jdm diff --git a/testing/web-platform/tests/hr-time/OWNERS b/testing/web-platform/tests/hr-time/OWNERS new file mode 100644 index 00000000000..ffa09d9ade9 --- /dev/null +++ b/testing/web-platform/tests/hr-time/OWNERS @@ -0,0 +1,2 @@ +@plehegar +@foolip diff --git a/testing/web-platform/tests/html-imports/OWNERS b/testing/web-platform/tests/html-imports/OWNERS new file mode 100644 index 00000000000..edda46755d6 --- /dev/null +++ b/testing/web-platform/tests/html-imports/OWNERS @@ -0,0 +1 @@ +@omo diff --git a/testing/web-platform/tests/html-longdesc/OWNERS b/testing/web-platform/tests/html-longdesc/OWNERS new file mode 100644 index 00000000000..91527fa2ac9 --- /dev/null +++ b/testing/web-platform/tests/html-longdesc/OWNERS @@ -0,0 +1,2 @@ +@chaals +@cptvitamin diff --git a/testing/web-platform/tests/html-media-capture/OWNERS b/testing/web-platform/tests/html-media-capture/OWNERS new file mode 100644 index 00000000000..b45f53558b6 --- /dev/null +++ b/testing/web-platform/tests/html-media-capture/OWNERS @@ -0,0 +1,2 @@ +@haoxli +@zqzhang diff --git a/testing/web-platform/tests/html/OWNERS b/testing/web-platform/tests/html/OWNERS index 210a621c919..ef22027135b 100644 --- a/testing/web-platform/tests/html/OWNERS +++ b/testing/web-platform/tests/html/OWNERS @@ -1,5 +1,6 @@ @Ms2ger @gsnedders +@jdm @jgraham @plehegar @sideshowbarker diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html new file mode 100644 index 00000000000..6b4df1ef2fc --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html @@ -0,0 +1,8 @@ + + + Blank 1 \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html new file mode 100644 index 00000000000..def21396675 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank2.html @@ -0,0 +1,8 @@ + + + Blank 2 diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html new file mode 100644 index 00000000000..11737661d0f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/page-with-fragment.html @@ -0,0 +1,20 @@ + + + +Page with fragment + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html new file mode 100644 index 00000000000..e47cd9c3833 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic.html @@ -0,0 +1,34 @@ + +Verify existence and basic read/write function of history.scrollRestoration + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html new file mode 100644 index 00000000000..e3da59e3900 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html @@ -0,0 +1,67 @@ + + +Precedence of scroll restoration mode over fragment scrolling in cross-origin history traversal + + + + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html new file mode 100644 index 00000000000..d837b8f63f0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html @@ -0,0 +1,54 @@ + + + + + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html new file mode 100644 index 00000000000..87a337b2da3 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin.html @@ -0,0 +1,71 @@ + + +Correct behaviour of scroll restoration mode is cross origin history traversal + + + + + + + + + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html new file mode 100644 index 00000000000..46d40eedc62 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html @@ -0,0 +1,81 @@ + +Correct behaviour of scroll restoration mode in same document history traversals + + + + + + + diff --git a/testing/web-platform/tests/html/dom/interfaces.html b/testing/web-platform/tests/html/dom/interfaces.html index 8eb7df805fb..d55aca60516 100644 --- a/testing/web-platform/tests/html/dom/interfaces.html +++ b/testing/web-platform/tests/html/dom/interfaces.html @@ -2309,8 +2309,11 @@ interface BarProp { attribute boolean visible; }; +enum ScrollRestoration { "auto", "manual" }; interface History { + readonly attribute long length; + attribute ScrollRestoration scrollRestoration; readonly attribute any state; void go(optional long delta); void back(); diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html index 3e76576f76c..0de7ff329c2 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported-ref.html @@ -19,7 +19,7 @@
  • fifth item
  • sixth item
  • seventh item
  • -
  • eigth item
  • +
  • eighth item
  • ninth item
    1. first ordered item
    2. @@ -29,7 +29,7 @@
    3. fifth ordered item
    4. sixth ordered item
    5. seventh ordered item
    6. -
    7. eigth ordered item
    8. +
    9. eighth ordered item
    10. ninth ordered item
      @@ -40,6 +40,6 @@
    • fifth unordered item
    • sixth unordered item
    • seventh unordered item
    • -
    • eigth unordered item
    • +
    • eighth unordered item
    • ninth unordered item
    diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html index 0fe85bd0492..ddd9024c117 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.html @@ -9,7 +9,7 @@
  • fifth item
  • sixth item
  • seventh item
  • -
  • eigth item
  • +
  • eighth item
  • ninth item
    1. first ordered item
    2. @@ -19,7 +19,7 @@
    3. fifth ordered item
    4. sixth ordered item
    5. seventh ordered item
    6. -
    7. eigth ordered item
    8. +
    9. eighth ordered item
    10. ninth ordered item
      @@ -30,6 +30,6 @@
    • fifth unordered item
    • sixth unordered item
    • seventh unordered item
    • -
    • eigth unordered item
    • +
    • eighth unordered item
    • ninth unordered item
    diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.xhtml b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.xhtml index 69f96e894bf..7a7640e03f0 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.xhtml +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/lists/li-type-supported.xhtml @@ -12,7 +12,7 @@
  • fifth item
  • sixth item
  • seventh item
  • -
  • eigth item
  • +
  • eighth item
  • ninth item
    1. first ordered item
    2. @@ -22,7 +22,7 @@
    3. fifth ordered item
    4. sixth ordered item
    5. seventh ordered item
    6. -
    7. eigth ordered item
    8. +
    9. eighth ordered item
    10. ninth ordered item
      @@ -33,7 +33,7 @@
    • fifth unordered item
    • sixth unordered item
    • seventh unordered item
    • -
    • eigth unordered item
    • +
    • eighth unordered item
    • ninth unordered item
    diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html index 3dba394baa5..3d44678cf16 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-embed-element/embed-document.html @@ -3,13 +3,15 @@ HTML Test: The embed element represents a document - diff --git a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html index 74b48e92100..9d308bbed3c 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html +++ b/testing/web-platform/tests/html/semantics/forms/the-button-element/button-events.html @@ -23,25 +23,25 @@ var btn = document.getElementById("btn"), document.forms.fm1.onsubmit = t1.step_func(function (evt) { evt.preventDefault(); - assert_true(evt.isTrusted, "The isTrusted attribute of the submit event shoud be true."); - assert_true(evt.bubbles, "The bubbles attribute of the submit event shoud be true."); - assert_true(evt.cancelable, "The cancelable attribute of the submit event shoud be true."); + assert_true(evt.isTrusted, "The isTrusted attribute of the submit event should be true."); + assert_true(evt.bubbles, "The bubbles attribute of the submit event should be true."); + assert_true(evt.cancelable, "The cancelable attribute of the submit event should be true."); assert_true(evt instanceof Event, "The submit event is an instance of Event interface."); t1.done(); }); document.forms.fm1.onreset = t2.step_func(function (evt) { - assert_true(evt.isTrusted, "The isTrusted attribute of the reset event shoud be true."); - assert_true(evt.bubbles, "The bubbles attribute of the reset event shoud be true."); - assert_true(evt.cancelable, "The cancelable attribute of the reset event shoud be true."); + assert_true(evt.isTrusted, "The isTrusted attribute of the reset event should be true."); + assert_true(evt.bubbles, "The bubbles attribute of the reset event should be true."); + assert_true(evt.cancelable, "The cancelable attribute of the reset event should be true."); assert_true(evt instanceof Event, "The reset event is an instance of Event interface."); t2.done(); }); document.getElementById("menu").onshow = t3.step_func(function (evt) { - assert_true(evt.isTrusted, "The isTrusted attribute of the show event shoud be true."); + assert_true(evt.isTrusted, "The isTrusted attribute of the show event should be true."); assert_equals(evt.relatedTarget, menu_btn, "The relatedTarget attribute should be initialized to the related button element."); - assert_true(evt.cancelable, "The cancelable attribute of the show event shoud be true."); + assert_true(evt.cancelable, "The cancelable attribute of the show event should be true."); assert_true(evt instanceof RelatedEvent, "The show event is an instance of RelatedEvent interface."); t3.done(); }); diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html index 1b75d74300a..054d3444449 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html +++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/checkbox.html @@ -2,7 +2,8 @@ input type checkbox - + +
    @@ -19,28 +20,39 @@ checkbox4 = document.getElementById('checkbox4'), checkbox5 = document.getElementById('checkbox5'), checkbox6 = document.getElementById('checkbox6'), - c1_input_fired = false, c1_change_fired = false, - t1 = async_test("click on mutable checkbox fires the input and change events"), + c1_click_fired = false, + c1_input_fired = false, + c1_change_fired = false, + t1 = async_test("click on mutable checkbox fires a click event, then an input event, then a change event"), t2 = async_test("click on non-mutable checkbox doesn't fire the input or change event"), t3 = async_test("pre-activation steps on unchecked checkbox"), t4 = async_test("pre-activation steps on checked checkbox"), t5 = async_test("canceled activation steps on unchecked checkbox"), t6 = async_test("canceled activation steps on unchecked checkbox (indeterminate=true in onclick)"); - checkbox1.oninput= t1.step_func(function(e) { + checkbox1.onclick = t1.step_func(function () { + c1_click_fired = true; + assert_false(c1_input_fired, "click event should fire before input event"); + assert_false(c1_change_fired, "click event should fire before change event"); + }); + checkbox1.oninput = t1.step_func(function(e) { c1_input_fired = true; + assert_true(c1_click_fired, "input event should fire after click event"); + assert_false(c1_change_fired, "input event should fire before change event"); assert_true(e.bubbles, "event should bubble"); assert_true(e.isTrusted, "event should be trusted"); - assert_false(e.cancelable, "event shoud not be cancelable"); + assert_false(e.cancelable, "event should not be cancelable"); assert_true(checkbox1.checked, "checkbox is checked"); assert_false(checkbox1.indeterminate, "checkbox is not indeterminate"); }); checkbox1.onchange = t1.step_func(function(e) { c1_change_fired = true; + assert_true(c1_click_fired, "change event should fire after click event"); + assert_true(c1_input_fired, "change event should fire after input event"); assert_true(e.bubbles, "event should bubble") assert_true(e.isTrusted, "event should be trusted"); - assert_false(e.cancelable, "event shoud not be cancelable"); + assert_false(e.cancelable, "event should not be cancelable"); assert_true(checkbox1.checked, "checkbox is checked"); assert_false(checkbox1.indeterminate, "checkbox is not indeterminate"); }); @@ -83,9 +95,22 @@ checkbox5.onclick = t5.step_func(function(e) { e.preventDefault(); - assert_false(checkbox5.checked); + /* + The prevention of the click doesn't have an effect until after all the + click event handlers have been run. + */ + assert_true(checkbox5.checked); assert_false(checkbox5.indeterminate); - t5.done(); + window.setTimeout(t5.step_func(function(e) { + /* + The click event has finished being dispatched, so the checkedness and + determinateness have been toggled back by now because the event + was preventDefault-ed. + */ + assert_false(checkbox5.checked); + assert_false(checkbox5.indeterminate); + t5.done(); + }), 0); }); t5.step(function(){ @@ -97,9 +122,22 @@ checkbox6.onclick = t6.step_func(function(e) { checkbox6.indeterminate = true; e.preventDefault(); - assert_false(checkbox6.checked); - assert_false(checkbox6.indeterminate); - t6.done(); + /* + The prevention of the click doesn't have an effect until after all the + click event handlers have been run. + */ + assert_true(checkbox6.checked); + assert_true(checkbox6.indeterminate); + window.setTimeout(t6.step_func(function(e) { + /* + The click event has finished being dispatched, so the checkedness and + determinateness have been toggled back by now because the event + was preventDefault-ed. + */ + assert_false(checkbox6.checked); + assert_false(checkbox6.indeterminate); + t6.done(); + }), 0); }); t6.step(function(){ diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html index 3340239881b..9e2d47c423d 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html +++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/file-manual.html @@ -18,12 +18,12 @@ document.getElementById('file').oninput = t1.step_func_done(function(e) { assert_true(e.bubbles, "input event bubbles"); assert_true(e.isTrusted, "input event should be trusted"); - assert_false(e.cancelable, "input event shoud not be cancelable"); + assert_false(e.cancelable, "input event should not be cancelable"); }) document.getElementById('file').onchange = t2.step_func_done(function(e) { assert_true(e.bubbles, "change event bubbles"); assert_true(e.isTrusted, "change event should be trusted"); - assert_false(e.cancelable, "change event shoud not be cancelable"); + assert_false(e.cancelable, "change event should not be cancelable"); assert_true(input.files instanceof FileList); assert_equals(input.value, "C:\\fakepath\\" + input.files[0].name); }) diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html index 3c4fea2a617..9f8fe096d52 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html +++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/radio.html @@ -15,8 +15,8 @@ - - + + @@ -37,8 +37,7 @@ radio9 = document.getElementById('radio9'), radio10 = document.getElementById('radio10'), radio11 = document.getElementById('radio11'), - t1 = async_test("click on mutable radio fires the input event"), - t2 = async_test("click on mutable radio fires the change event"), + t1 = async_test("click on mutable radio fires click event, then input event, then change event"), t3 = async_test("click on non-mutable radio doesn't fire the input event"), t4 = async_test("click on non-mutable radio doesn't fire the change event"), t5 = async_test("canceled activation steps on unchecked radio"), @@ -80,18 +79,28 @@ assert_false(radio11.checked); }, "changing the name of a radio input element and setting its checkedness to true makes all the other elements' checkedness in the same radio button group be set to false"); - radio5.oninput= t1.step_func(function(e) { - input_fired = true; - assert_true(e.bubbles, "event should bubble") - assert_true(e.isTrusted, "event should be trusted"); - assert_false(e.cancelable, "event shoud not be cancelable"); + radio5.onclick = t1.step_func(function(e) { + click_fired = true; + assert_false(input_fired, "click event should fire before input event"); + assert_false(change_fired, "click event should fire before change event"); }); - radio5.onchange = t2.step_func(function(e) { + radio5.oninput = t1.step_func(function(e) { + input_fired = true; + assert_true(click_fired, "input event should fire after click event"); + assert_false(change_fired, "input event should fire before change event"); + assert_true(e.bubbles, "input event should bubble") + assert_true(e.isTrusted, "input event should be trusted"); + assert_false(e.cancelable, "input event should not be cancelable"); + }); + + radio5.onchange = t1.step_func(function(e) { change_fired = true; - assert_true(e.bubbles, "event should bubble") - assert_true(e.isTrusted, "event should be trusted"); - assert_false(e.cancelable, "event shoud not be cancelable"); + assert_true(click_fired, "change event should fire after click event"); + assert_true(input_fired, "change event should fire after input event"); + assert_true(e.bubbles, "change event should bubble") + assert_true(e.isTrusted, "change event should be trusted"); + assert_false(e.cancelable, "change event should not be cancelable"); }); radio6.oninput= t3.step_func_done(function(e) { @@ -108,11 +117,6 @@ t1.done(); }); - t2.step(function() { - assert_true(change_fired); - t2.done(); - }) - t3.step(function(){ radio6.click(); t3.done(); @@ -120,18 +124,20 @@ }); radio72.onclick = t5.step_func_done(function(e){ - assert_false(radio71.checked); - assert_true(radio72.checked); + assert_false(radio71.checked, "click on radio should uncheck other radio in same group"); + assert_true(radio72.checked, "click on radio should check that radio"); e.preventDefault(); - assert_false(radio71.checked); - assert_true(radio72.checked); + // The cancelation of the click doesn't have an effect until after all the click event handlers have been run. + assert_false(radio71.checked, "radio remains unchecked immediately after click event on other radio in same group is canceled"); + assert_true(radio72.checked, "clicked radio remains checked immediately after click event is canceled"); }); t5.step(function(){ - assert_true(radio71.checked); - assert_false(radio72.checked); + assert_true(radio71.checked, "initially checked radio should be checked"); + assert_false(radio72.checked, "other radios in same group as initially-checked radio should be unchecked"); radio72.click(); - assert_true(radio71.checked); - assert_false(radio72.checked); + // Now that the click event has been fully dispatched, its cancelation has taken effect. + assert_true(radio71.checked, "canceled click event on radio should leave the previously-checked radio checked"); + assert_false(radio72.checked, "canceled click event on previously-unchecked radio should leave that radio unchecked"); }); diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html new file mode 100644 index 00000000000..8686716e19a --- /dev/null +++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html @@ -0,0 +1,38 @@ + +HTMLBodyElement.onresize + + + + +
    + diff --git a/testing/web-platform/tests/images/OWNERS b/testing/web-platform/tests/images/OWNERS new file mode 100644 index 00000000000..8ab38aed331 --- /dev/null +++ b/testing/web-platform/tests/images/OWNERS @@ -0,0 +1,2 @@ +@zqzhang +@tagawa diff --git a/testing/web-platform/tests/media-source/OWNERS b/testing/web-platform/tests/media-source/OWNERS new file mode 100644 index 00000000000..3b36dfa39a2 --- /dev/null +++ b/testing/web-platform/tests/media-source/OWNERS @@ -0,0 +1,4 @@ +@bit +@acolwell +@shishimaru +@sideshowbarker diff --git a/testing/web-platform/tests/media/OWNERS b/testing/web-platform/tests/media/OWNERS new file mode 100644 index 00000000000..96fa9523d7b --- /dev/null +++ b/testing/web-platform/tests/media/OWNERS @@ -0,0 +1,2 @@ +@hillbrad +@foolip diff --git a/testing/web-platform/tests/mediacapture-streams/OWNERS b/testing/web-platform/tests/mediacapture-streams/OWNERS new file mode 100644 index 00000000000..03ff57c28fd --- /dev/null +++ b/testing/web-platform/tests/mediacapture-streams/OWNERS @@ -0,0 +1,3 @@ +@dontcallmedom +@alvestrand + diff --git a/testing/web-platform/tests/mixed-content/OWNERS b/testing/web-platform/tests/mixed-content/OWNERS new file mode 100644 index 00000000000..db2d613c226 --- /dev/null +++ b/testing/web-platform/tests/mixed-content/OWNERS @@ -0,0 +1 @@ +@kristijanburnik diff --git a/testing/web-platform/tests/navigation-timing/OWNERS b/testing/web-platform/tests/navigation-timing/OWNERS new file mode 100644 index 00000000000..ffa09d9ade9 --- /dev/null +++ b/testing/web-platform/tests/navigation-timing/OWNERS @@ -0,0 +1,2 @@ +@plehegar +@foolip diff --git a/testing/web-platform/tests/notifications/OWNERS b/testing/web-platform/tests/notifications/OWNERS new file mode 100644 index 00000000000..992402311a2 --- /dev/null +++ b/testing/web-platform/tests/notifications/OWNERS @@ -0,0 +1,4 @@ +@chunywang +@sideshowbarker +@xinliux +@ibelem diff --git a/testing/web-platform/tests/page-visibility/OWNERS b/testing/web-platform/tests/page-visibility/OWNERS new file mode 100644 index 00000000000..ffa09d9ade9 --- /dev/null +++ b/testing/web-platform/tests/page-visibility/OWNERS @@ -0,0 +1,2 @@ +@plehegar +@foolip diff --git a/testing/web-platform/tests/performance-timeline/OWNERS b/testing/web-platform/tests/performance-timeline/OWNERS new file mode 100644 index 00000000000..5a05f35799c --- /dev/null +++ b/testing/web-platform/tests/performance-timeline/OWNERS @@ -0,0 +1 @@ +@plehegar diff --git a/testing/web-platform/tests/pointerevents/OWNERS b/testing/web-platform/tests/pointerevents/OWNERS new file mode 100644 index 00000000000..af346a9323f --- /dev/null +++ b/testing/web-platform/tests/pointerevents/OWNERS @@ -0,0 +1,7 @@ +@bethge +@Steditor +@EvgenyAgafonchikov +@jacobrossi +@plehegar +@scottgonzalez +@staktrace diff --git a/testing/web-platform/tests/pointerlock/OWNERS b/testing/web-platform/tests/pointerlock/OWNERS new file mode 100644 index 00000000000..5a05f35799c --- /dev/null +++ b/testing/web-platform/tests/pointerlock/OWNERS @@ -0,0 +1 @@ +@plehegar diff --git a/testing/web-platform/tests/proximity/OWNERS b/testing/web-platform/tests/proximity/OWNERS new file mode 100644 index 00000000000..7fab6d626aa --- /dev/null +++ b/testing/web-platform/tests/proximity/OWNERS @@ -0,0 +1,2 @@ +@zqzhang +@dontcallmedom diff --git a/testing/web-platform/tests/quirks-mode/OWNERS b/testing/web-platform/tests/quirks-mode/OWNERS new file mode 100644 index 00000000000..63851614695 --- /dev/null +++ b/testing/web-platform/tests/quirks-mode/OWNERS @@ -0,0 +1 @@ +@zcorpan diff --git a/testing/web-platform/tests/referrer-policy/OWNERS b/testing/web-platform/tests/referrer-policy/OWNERS new file mode 100644 index 00000000000..db2d613c226 --- /dev/null +++ b/testing/web-platform/tests/referrer-policy/OWNERS @@ -0,0 +1 @@ +@kristijanburnik diff --git a/testing/web-platform/tests/resource-timing/OWNERS b/testing/web-platform/tests/resource-timing/OWNERS new file mode 100644 index 00000000000..29f407cf1e0 --- /dev/null +++ b/testing/web-platform/tests/resource-timing/OWNERS @@ -0,0 +1,3 @@ +@haoxli +@plehegar +@zqzhang diff --git a/testing/web-platform/tests/screen-orientation/OWNERS b/testing/web-platform/tests/screen-orientation/OWNERS new file mode 100644 index 00000000000..bbdded8fff9 --- /dev/null +++ b/testing/web-platform/tests/screen-orientation/OWNERS @@ -0,0 +1 @@ +@haoxli diff --git a/testing/web-platform/tests/selection/OWNERS b/testing/web-platform/tests/selection/OWNERS new file mode 100644 index 00000000000..ce908c45b4a --- /dev/null +++ b/testing/web-platform/tests/selection/OWNERS @@ -0,0 +1 @@ +@ayg diff --git a/testing/web-platform/tests/service-workers/OWNERS b/testing/web-platform/tests/service-workers/OWNERS new file mode 100644 index 00000000000..e210b8f2d4c --- /dev/null +++ b/testing/web-platform/tests/service-workers/OWNERS @@ -0,0 +1 @@ +@ehsan diff --git a/testing/web-platform/tests/service-workers/cache-storage/OWNERS b/testing/web-platform/tests/service-workers/cache-storage/OWNERS new file mode 100644 index 00000000000..2e63dd2203e --- /dev/null +++ b/testing/web-platform/tests/service-workers/cache-storage/OWNERS @@ -0,0 +1,2 @@ +@inexorabletash +@wanderview diff --git a/testing/web-platform/tests/service-workers/cache-storage/resources/test-helpers.js b/testing/web-platform/tests/service-workers/cache-storage/resources/test-helpers.js index 91110950d56..f4145e6217f 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/resources/test-helpers.js +++ b/testing/web-platform/tests/service-workers/cache-storage/resources/test-helpers.js @@ -35,3 +35,203 @@ function cache_test(test_function, description) { .then(test_function); }, description); } + +// A set of Request/Response pairs to be used with prepopulated_cache_test(). +var simple_entries = [ + { + name: 'a', + request: new Request('http://example.com/a'), + response: new Response('') + }, + + { + name: 'b', + request: new Request('http://example.com/b'), + response: new Response('') + }, + + { + name: 'a_with_query', + request: new Request('http://example.com/a?q=r'), + response: new Response('') + }, + + { + name: 'A', + request: new Request('http://example.com/A'), + response: new Response('') + }, + + { + name: 'a_https', + request: new Request('https://example.com/a'), + response: new Response('') + }, + + { + name: 'a_org', + request: new Request('http://example.org/a'), + response: new Response('') + }, + + { + name: 'cat', + request: new Request('http://example.com/cat'), + response: new Response('') + }, + + { + name: 'catmandu', + request: new Request('http://example.com/catmandu'), + response: new Response('') + }, + + { + name: 'cat_num_lives', + request: new Request('http://example.com/cat?lives=9'), + response: new Response('') + }, + + { + name: 'cat_in_the_hat', + request: new Request('http://example.com/cat/in/the/hat'), + response: new Response('') + }, + + { + name: 'non_2xx_response', + request: new Request('http://example.com/non2xx'), + response: new Response('', {status: 404, statusText: 'nope'}) + }, + + { + name: 'error_response', + request: new Request('http://example.com/error'), + response: Response.error() + }, +]; + +// A set of Request/Response pairs to be used with prepopulated_cache_test(). +// These contain a mix of test cases that use Vary headers. +var vary_entries = [ + { + name: 'vary_cookie_is_cookie', + request: new Request('http://example.com/c', + {headers: {'Cookies': 'is-for-cookie'}}), + response: new Response('', + {headers: {'Vary': 'Cookies'}}) + }, + + { + name: 'vary_cookie_is_good', + request: new Request('http://example.com/c', + {headers: {'Cookies': 'is-good-enough-for-me'}}), + response: new Response('', + {headers: {'Vary': 'Cookies'}}) + }, + + { + name: 'vary_cookie_absent', + request: new Request('http://example.com/c'), + response: new Response('', + {headers: {'Vary': 'Cookies'}}) + } +]; + +// Run |test_function| with a Cache object and a map of entries. Prior to the +// call, the Cache is populated by cache entries from |entries|. The latter is +// expected to be an Object mapping arbitrary keys to objects of the form +// {request: , response: }. There's no +// guarantee on the order in which entries will be added to the cache. +// +// |test_function| should return a Promise that can be used with promise_test. +function prepopulated_cache_test(entries, test_function, description) { + cache_test(function(cache) { + var p = Promise.resolve(); + var hash = {}; + return Promise.all(entries.map(function(entry) { + hash[entry.name] = entry; + return cache.put(entry.request.clone(), + entry.response.clone()) + .catch(function(e) { + assert_unreached( + 'Test setup failed for entry ' + entry.name + ': ' + e); + }); + })) + .then(function() { + assert_equals(Object.keys(hash).length, entries.length); + }) + .then(function() { + return test_function(cache, hash); + }); + }, description); +} + +// Helper for testing with Headers objects. Compares Headers instances +// by serializing |expected| and |actual| to arrays and comparing. +function assert_header_equals(actual, expected, description) { + assert_class_string(actual, "Headers", description); + var header; + var actual_headers = []; + var expected_headers = []; + for (header of actual) + actual_headers.push(header[0] + ": " + header[1]); + for (header of expected) + expected_headers.push(header[0] + ": " + header[1]); + assert_array_equals(actual_headers, expected_headers, + description + " Headers differ."); +} + +// Helper for testing with Response objects. Compares simple +// attributes defined on the interfaces, as well as the headers. It +// does not compare the response bodies. +function assert_response_equals(actual, expected, description) { + assert_class_string(actual, "Response", description); + ["type", "url", "status", "ok", "statusText"].forEach(function(attribute) { + assert_equals(actual[attribute], expected[attribute], + description + " Attributes differ: " + attribute + "."); + }); + assert_header_equals(actual.headers, expected.headers, description); +} + +// Assert that the two arrays |actual| and |expected| contain the same +// set of Responses as determined by assert_response_equals. The order +// is not significant. +// +// |expected| is assumed to not contain any duplicates. +function assert_response_array_equivalent(actual, expected, description) { + assert_true(Array.isArray(actual), description); + assert_equals(actual.length, expected.length, description); + expected.forEach(function(expected_element) { + // assert_response_in_array treats the first argument as being + // 'actual', and the second as being 'expected array'. We are + // switching them around because we want to be resilient + // against the |actual| array containing duplicates. + assert_response_in_array(expected_element, actual, description); + }); +} + +// Asserts that two arrays |actual| and |expected| contain the same +// set of Responses as determined by assert_response_equals(). The +// corresponding elements must occupy corresponding indices in their +// respective arrays. +function assert_response_array_equals(actual, expected, description) { + assert_true(Array.isArray(actual), description); + assert_equals(actual.length, expected.length, description); + actual.forEach(function(value, index) { + assert_response_equals(value, expected[index], + description + " : object[" + index + "]"); + }); +} + +// Equivalent to assert_in_array, but uses assert_response_equals. +function assert_response_in_array(actual, expected_array, description) { + assert_true(expected_array.some(function(element) { + try { + assert_response_equals(actual, element); + return true; + } catch (e) { + return false; + } + }), description); +} diff --git a/testing/web-platform/tests/service-workers/cache-storage/resources/testharness-helpers.js b/testing/web-platform/tests/service-workers/cache-storage/resources/testharness-helpers.js index 45667056fda..e4885727b32 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/resources/testharness-helpers.js +++ b/testing/web-platform/tests/service-workers/cache-storage/resources/testharness-helpers.js @@ -31,70 +31,3 @@ function assert_promise_rejects(promise, code, description) { } }); } - -// Helper for testing with Headers objects. Compares Headers instances -// by serializing |expected| and |actual| to arrays and comparing. -function assert_header_equals(actual, expected, description) { - assert_class_string(actual, "Headers", description); - var header, actual_headers = [], expected_headers = []; - for (header of actual) - actual_headers.push(header[0] + ": " + header[1]); - for (header of expected) - expected_headers.push(header[0] + ": " + header[1]); - assert_array_equals(actual_headers, expected_headers, - description + " Headers differ."); -} - -// Helper for testing with Response objects. Compares simple -// attributes defined on the interfaces, as well as the headers. It -// does not compare the response bodies. -function assert_response_equals(actual, expected, description) { - assert_class_string(actual, "Response", description); - ["type", "url", "status", "ok", "statusText"].forEach(function(attribute) { - assert_equals(actual[attribute], expected[attribute], - description + " Attributes differ: " + attribute + "."); - }); - assert_header_equals(actual.headers, expected.headers, description); -} - -// Assert that the two arrays |actual| and |expected| contain the same -// set of Responses as determined by assert_response_equals. The order -// is not significant. -// -// |expected| is assumed to not contain any duplicates. -function assert_response_array_equivalent(actual, expected, description) { - assert_true(Array.isArray(actual), description); - assert_equals(actual.length, expected.length, description); - expected.forEach(function(expected_element) { - // assert_response_in_array treats the first argument as being - // 'actual', and the second as being 'expected array'. We are - // switching them around because we want to be resilient - // against the |actual| array containing duplicates. - assert_response_in_array(expected_element, actual, description); - }); -} - -// Asserts that two arrays |actual| and |expected| contain the same -// set of Responses as determined by assert_response_equals(). The -// corresponding elements must occupy corresponding indices in their -// respective arrays. -function assert_response_array_equals(actual, expected, description) { - assert_true(Array.isArray(actual), description); - assert_equals(actual.length, expected.length, description); - actual.forEach(function(value, index) { - assert_response_equals(value, expected[index], - description + " : object[" + index + "]"); - }); -} - -// Equivalent to assert_in_array, but uses assert_response_equals. -function assert_response_in_array(actual, expected_array, description) { - assert_true(expected_array.some(function(element) { - try { - assert_response_equals(actual, element); - return true; - } catch (e) { - return false; - } - }), description); -} diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js index 49b8db4cd71..9fc597a4b4f 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-add.js @@ -16,6 +16,16 @@ cache_test(function(cache) { .then(function(result) { assert_equals(result, undefined, 'Cache.add should resolve with undefined on success.'); + return cache.match('../resources/simple.txt'); + }) + .then(function(response) { + assert_class_string(response, 'Response', + 'Cache.add should put a resource in the cache.'); + return response.text(); + }) + .then(function(body) { + assert_equals(body, 'a simple text file\n', + 'Cache.add should retrieve the correct body.'); }); }, 'Cache.add called with relative URL specified as a string'); @@ -35,6 +45,15 @@ cache_test(function(cache) { }); }, 'Cache.add called with Request object'); +cache_test(function(cache) { + var request = new Request('../resources/simple.txt', + {method: 'POST', body: 'This is a body.'}); + return assert_promise_rejects( + cache.add(request), + new TypeError(), + 'Cache.add should throw a TypeError for non-GET requests.'); + }, 'Cache.add called with POST request'); + cache_test(function(cache) { var request = new Request('../resources/simple.txt'); return cache.add(request) @@ -51,6 +70,17 @@ cache_test(function(cache) { }); }, 'Cache.add called twice with the same Request object'); +cache_test(function(cache) { + var request = new Request('../resources/simple.txt'); + return request.text() + .then(function() { + assert_false(request.bodyUsed); + }) + .then(function() { + return cache.add(request); + }); + }, 'Cache.add with request with null body (not consumed)'); + cache_test(function(cache) { return cache.add('this-does-not-exist-please-dont-create-it') .then(function(result) { @@ -84,19 +114,62 @@ cache_test(function(cache) { }, 'Cache.addAll with a mix of valid and undefined arguments'); cache_test(function(cache) { - // Assumes the existence of ../resources/simple.txt and ../resources/blank.html - var urls = ['../resources/simple.txt', self.location.href, '../resources/blank.html']; + return cache.addAll([]) + .then(function(result) { + assert_equals(result, undefined, + 'Cache.addAll should resolve with undefined on ' + + 'success.'); + return cache.keys(); + }) + .then(function(result) { + assert_equals(result.length, 0, + 'There should be no entry in the cache.'); + }); + }, 'Cache.addAll with an empty array'); + +cache_test(function(cache) { + // Assumes the existence of ../resources/simple.txt and + // ../resources/blank.html + var urls = ['../resources/simple.txt', + self.location.href, + '../resources/blank.html']; return cache.addAll(urls) .then(function(result) { assert_equals(result, undefined, 'Cache.addAll should resolve with undefined on ' + 'success.'); + return Promise.all( + urls.map(function(url) { return cache.match(url); })); + }) + .then(function(responses) { + assert_class_string( + responses[0], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_class_string( + responses[1], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_class_string( + responses[2], 'Response', + 'Cache.addAll should put a resource in the cache.'); + return Promise.all( + responses.map(function(response) { return response.text(); })); + }) + .then(function(bodies) { + assert_equals( + bodies[0], 'a simple text file\n', + 'Cache.add should retrieve the correct body.'); + assert_equals( + bodies[2], '\nEmpty doc\n', + 'Cache.add should retrieve the correct body.'); }); }, 'Cache.addAll with string URL arguments'); cache_test(function(cache) { - // Assumes the existence of ../resources/simple.txt and ../resources/blank.html - var urls = ['../resources/simple.txt', self.location.href, '../resources/blank.html']; + // Assumes the existence of ../resources/simple.txt and + // ../resources/blank.html + var urls = ['../resources/simple.txt', + self.location.href, + '../resources/blank.html']; var requests = urls.map(function(url) { return new Request(url); }); @@ -105,13 +178,38 @@ cache_test(function(cache) { assert_equals(result, undefined, 'Cache.addAll should resolve with undefined on ' + 'success.'); + return Promise.all( + urls.map(function(url) { return cache.match(url); })); + }) + .then(function(responses) { + assert_class_string( + responses[0], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_class_string( + responses[1], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_class_string( + responses[2], 'Response', + 'Cache.addAll should put a resource in the cache.'); + return Promise.all( + responses.map(function(response) { return response.text(); })); + }) + .then(function(bodies) { + assert_equals( + bodies[0], 'a simple text file\n', + 'Cache.add should retrieve the correct body.'); + assert_equals( + bodies[2], '\nEmpty doc\n', + 'Cache.add should retrieve the correct body.'); }); }, 'Cache.addAll with Request arguments'); cache_test(function(cache) { - // Assumes that ../resources/simple.txt and ../resources/blank.html exist. The second - // resource does not. - var urls = ['../resources/simple.txt', 'this-resource-should-not-exist', '../resources/blank.html']; + // Assumes that ../resources/simple.txt and ../resources/blank.html exist. + // The second resource does not. + var urls = ['../resources/simple.txt', + 'this-resource-should-not-exist', + '../resources/blank.html']; var requests = urls.map(function(url) { return new Request(url); }); @@ -120,6 +218,32 @@ cache_test(function(cache) { assert_equals(result, undefined, 'Cache.addAll should resolve with undefined on ' + 'success.'); + return Promise.all( + urls.map(function(url) { return cache.match(url); })); + }) + .then(function(responses) { + assert_class_string( + responses[0], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_class_string( + responses[1], 'Response', + 'Cache.addAll should put a resource in the cache.'); + assert_equals( + responses[1].status, 404, + 'Cache.addAll should put a 404 resource in the cache.'); + assert_class_string( + responses[2], 'Response', + 'Cache.addAll should put a resource in the cache.'); + return Promise.all( + responses.map(function(response) { return response.text(); })); + }) + .then(function(bodies) { + assert_equals( + bodies[0], 'a simple text file\n', + 'Cache.add should retrieve the correct body.'); + assert_equals( + bodies[2], '\nEmpty doc\n', + 'Cache.add should retrieve the correct body.'); }); }, 'Cache.addAll with a mix of succeeding and failing requests'); diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js index 02cf6cf5fb6..716bfe5a698 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-match.js @@ -4,105 +4,6 @@ if (self.importScripts) { importScripts('../resources/test-helpers.js'); } -// A set of Request/Response pairs to be used with prepopulated_cache_test(). -var simple_entries = [ - { - name: 'a', - request: new Request('http://example.com/a'), - response: new Response('') - }, - - { - name: 'b', - request: new Request('http://example.com/b'), - response: new Response('') - }, - - { - name: 'a_with_query', - request: new Request('http://example.com/a?q=r'), - response: new Response('') - }, - - { - name: 'A', - request: new Request('http://example.com/A'), - response: new Response('') - }, - - { - name: 'a_https', - request: new Request('https://example.com/a'), - response: new Response('') - }, - - { - name: 'a_org', - request: new Request('http://example.org/a'), - response: new Response('') - }, - - { - name: 'cat', - request: new Request('http://example.com/cat'), - response: new Response('') - }, - - { - name: 'catmandu', - request: new Request('http://example.com/catmandu'), - response: new Response('') - }, - - { - name: 'cat_num_lives', - request: new Request('http://example.com/cat?lives=9'), - response: new Response('') - }, - - { - name: 'cat_in_the_hat', - request: new Request('http://example.com/cat/in/the/hat'), - response: new Response('') - } -]; - -// A set of Request/Response pairs to be used with prepopulated_cache_test(). -// These contain a mix of test cases that use Vary headers. -var vary_entries = [ - { - name: 'vary_cookie_is_cookie', - request: new Request('http://example.com/c', - {headers: {'Cookies': 'is-for-cookie'}}), - response: new Response('', - {headers: {'Vary': 'Cookies'}}) - }, - - { - name: 'vary_cookie_is_good', - request: new Request('http://example.com/c', - {headers: {'Cookies': 'is-good-enough-for-me'}}), - response: new Response('', - {headers: {'Vary': 'Cookies'}}) - }, - - { - name: 'vary_cookie_absent', - request: new Request('http://example.com/c'), - response: new Response('', - {headers: {'Vary': 'Cookies'}}) - } -]; - -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll('not-present-in-the-cache') - .then(function(result) { - assert_response_array_equivalent( - result, [], - 'Cache.matchAll should resolve with an empty array on failure.'); - }); - }, 'Cache.matchAll with no matching entries'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match('not-present-in-the-cache') .then(function(result) { @@ -111,14 +12,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with no matching entries'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(entries.a.request.url) - .then(function(result) { - assert_response_array_equals(result, [entries.a.response], - 'Cache.matchAll should match by URL.'); - }); - }, 'Cache.matchAll with URL'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(entries.a.request.url) .then(function(result) { @@ -127,15 +20,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with URL'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(entries.a.request) - .then(function(result) { - assert_response_array_equals( - result, [entries.a.response], - 'Cache.matchAll should match by Request.'); - }); - }, 'Cache.matchAll with Request'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(entries.a.request) .then(function(result) { @@ -144,15 +28,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with Request'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(new Request(entries.a.request.url)) - .then(function(result) { - assert_response_array_equals( - result, [entries.a.response], - 'Cache.matchAll should match by Request.'); - }); - }, 'Cache.matchAll with new Request'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(new Request(entries.a.request.url)) .then(function(result) { @@ -161,23 +36,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with new Request'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(entries.a.request, - {ignoreSearch: true}) - .then(function(result) { - assert_response_array_equivalent( - result, - [ - entries.a.response, - entries.a_with_query.response - ], - 'Cache.matchAll with ignoreSearch should ignore the ' + - 'search parameters of cached request.'); - }); - }, - 'Cache.matchAll with ignoreSearch option (request with no search ' + - 'parameters)'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(entries.a.request, {ignoreSearch: true}) @@ -195,22 +53,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { 'Cache.match with ignoreSearch option (request with no search ' + 'parameters)'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(entries.a_with_query.request, - {ignoreSearch: true}) - .then(function(result) { - assert_response_array_equivalent( - result, - [ - entries.a.response, - entries.a_with_query.response - ], - 'Cache.matchAll with ignoreSearch should ignore the ' + - 'search parameters of request.'); - }); - }, - 'Cache.matchAll with ignoreSearch option (request with search parameter)'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(entries.a_with_query.request, {ignoreSearch: true}) @@ -227,18 +69,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }, 'Cache.match with ignoreSearch option (request with search parameter)'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll(entries.cat.request.url + '#mouse') - .then(function(result) { - assert_response_array_equivalent( - result, - [ - entries.cat.response, - ], - 'Cache.matchAll should ignore URL fragment.'); - }); - }, 'Cache.matchAll with URL containing fragment'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match(entries.cat.request.url + '#mouse') .then(function(result) { @@ -247,16 +77,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with URL containing fragment'); -prepopulated_cache_test(simple_entries, function(cache, entries) { - return cache.matchAll('http') - .then(function(result) { - assert_response_array_equivalent( - result, [], - 'Cache.matchAll should treat query as a URL and not ' + - 'just a string fragment.'); - }); - }, 'Cache.matchAll with string fragment "http" as query'); - prepopulated_cache_test(simple_entries, function(cache, entries) { return cache.match('http') .then(function(result) { @@ -267,48 +87,6 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with string fragment "http" as query'); -prepopulated_cache_test(vary_entries, function(cache, entries) { - return cache.matchAll('http://example.com/c') - .then(function(result) { - assert_response_array_equivalent( - result, - [ - entries.vary_cookie_absent.response - ], - 'Cache.matchAll should exclude matches if a vary header is ' + - 'missing in the query request, but is present in the cached ' + - 'request.'); - }) - - .then(function() { - return cache.matchAll( - new Request('http://example.com/c', - {headers: {'Cookies': 'none-of-the-above'}})); - }) - .then(function(result) { - assert_response_array_equivalent( - result, - [ - ], - 'Cache.matchAll should exclude matches if a vary header is ' + - 'missing in the cached request, but is present in the query ' + - 'request.'); - }) - - .then(function() { - return cache.matchAll( - new Request('http://example.com/c', - {headers: {'Cookies': 'is-for-cookie'}})); - }) - .then(function(result) { - assert_response_array_equivalent( - result, - [entries.vary_cookie_is_cookie.response], - 'Cache.matchAll should match the entire header if a vary header ' + - 'is present in both the query and cached requests.'); - }); - }, 'Cache.matchAll with responses containing "Vary" header'); - prepopulated_cache_test(vary_entries, function(cache, entries) { return cache.match('http://example.com/c') .then(function(result) { @@ -321,21 +99,6 @@ prepopulated_cache_test(vary_entries, function(cache, entries) { }); }, 'Cache.match with responses containing "Vary" header'); -prepopulated_cache_test(vary_entries, function(cache, entries) { - return cache.matchAll('http://example.com/c', - {ignoreVary: true}) - .then(function(result) { - assert_response_array_equivalent( - result, - [ - entries.vary_cookie_is_cookie.response, - entries.vary_cookie_is_good.response, - entries.vary_cookie_absent.response, - ], - 'Cache.matchAll should honor "ignoreVary" parameter.'); - }); - }, 'Cache.matchAll with "ignoreVary" parameter'); - cache_test(function(cache) { var request = new Request('http://example.com'); var response; @@ -397,7 +160,7 @@ cache_test(function(cache) { }, 'Cache.match invoked multiple times for the same Request/Response'); prepopulated_cache_test(simple_entries, function(cache, entries) { - var request = new Request(entries.a.request, { method: 'POST' }); + var request = new Request(entries.a.request.clone(), {method: 'POST'}); return cache.match(request) .then(function(result) { assert_equals(result, undefined, @@ -405,38 +168,26 @@ prepopulated_cache_test(simple_entries, function(cache, entries) { }); }, 'Cache.match with POST Request'); -// Helpers --- +prepopulated_cache_test(simple_entries, function(cache, entries) { + var response = entries.non_2xx_response.response; + return cache.match(entries.non_2xx_response.request.url) + .then(function(result) { + assert_response_equals( + result, entries.non_2xx_response.response, + 'Cache.match should return a Response object that has the ' + + 'same properties as a stored non-2xx response.'); + }); + }, 'Cache.match with a non-2xx Response'); -// Run |test_function| with a Cache object as its only parameter. Prior to the -// call, the Cache is populated by cache entries from |entries|. The latter is -// expected to be an Object mapping arbitrary keys to objects of the form -// {request: , response: }. There's no -// guarantee on the order in which entries will be added to the cache. -// -// |test_function| should return a Promise that can be used with promise_test. -function prepopulated_cache_test(entries, test_function, description) { - cache_test(function(cache) { - var p = Promise.resolve(); - var hash = {}; - entries.forEach(function(entry) { - p = p.then(function() { - return cache.put(entry.request.clone(), - entry.response.clone()) - .catch(function(e) { - assert_unreached('Test setup failed for entry ' + - entry.name + ': ' + e); - }); - }); - hash[entry.name] = entry; +prepopulated_cache_test(simple_entries, function(cache, entries) { + var response = entries.error_response.response; + return cache.match(entries.error_response.request.url) + .then(function(result) { + assert_response_equals( + result, entries.error_response.response, + 'Cache.match should return a Response object that has the ' + + 'same properties as a stored network error response.'); }); - p = p.then(function() { - assert_equals(Object.keys(hash).length, entries.length); - }); - - return p.then(function() { - return test_function(cache, hash); - }); - }, description); -} + }, 'Cache.match with a network error Response'); done(); diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-matchAll.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-matchAll.js new file mode 100644 index 00000000000..2bc661a5c90 --- /dev/null +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-matchAll.js @@ -0,0 +1,154 @@ +if (self.importScripts) { + importScripts('/resources/testharness.js'); + importScripts('../resources/testharness-helpers.js'); + importScripts('../resources/test-helpers.js'); +} + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll('not-present-in-the-cache') + .then(function(result) { + assert_response_array_equivalent( + result, [], + 'Cache.matchAll should resolve with an empty array on failure.'); + }); + }, 'Cache.matchAll with no matching entries'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(entries.a.request.url) + .then(function(result) { + assert_response_array_equals(result, [entries.a.response], + 'Cache.matchAll should match by URL.'); + }); + }, 'Cache.matchAll with URL'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(entries.a.request) + .then(function(result) { + assert_response_array_equals( + result, [entries.a.response], + 'Cache.matchAll should match by Request.'); + }); + }, 'Cache.matchAll with Request'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(new Request(entries.a.request.url)) + .then(function(result) { + assert_response_array_equals( + result, [entries.a.response], + 'Cache.matchAll should match by Request.'); + }); + }, 'Cache.matchAll with new Request'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(entries.a.request, + {ignoreSearch: true}) + .then(function(result) { + assert_response_array_equivalent( + result, + [ + entries.a.response, + entries.a_with_query.response + ], + 'Cache.matchAll with ignoreSearch should ignore the ' + + 'search parameters of cached request.'); + }); + }, + 'Cache.matchAll with ignoreSearch option (request with no search ' + + 'parameters)'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(entries.a_with_query.request, + {ignoreSearch: true}) + .then(function(result) { + assert_response_array_equivalent( + result, + [ + entries.a.response, + entries.a_with_query.response + ], + 'Cache.matchAll with ignoreSearch should ignore the ' + + 'search parameters of request.'); + }); + }, + 'Cache.matchAll with ignoreSearch option (request with search parameter)'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll(entries.cat.request.url + '#mouse') + .then(function(result) { + assert_response_array_equivalent( + result, + [ + entries.cat.response, + ], + 'Cache.matchAll should ignore URL fragment.'); + }); + }, 'Cache.matchAll with URL containing fragment'); + +prepopulated_cache_test(simple_entries, function(cache, entries) { + return cache.matchAll('http') + .then(function(result) { + assert_response_array_equivalent( + result, [], + 'Cache.matchAll should treat query as a URL and not ' + + 'just a string fragment.'); + }); + }, 'Cache.matchAll with string fragment "http" as query'); + +prepopulated_cache_test(vary_entries, function(cache, entries) { + return cache.matchAll('http://example.com/c') + .then(function(result) { + assert_response_array_equivalent( + result, + [ + entries.vary_cookie_absent.response + ], + 'Cache.matchAll should exclude matches if a vary header is ' + + 'missing in the query request, but is present in the cached ' + + 'request.'); + }) + + .then(function() { + return cache.matchAll( + new Request('http://example.com/c', + {headers: {'Cookies': 'none-of-the-above'}})); + }) + .then(function(result) { + assert_response_array_equivalent( + result, + [ + ], + 'Cache.matchAll should exclude matches if a vary header is ' + + 'missing in the cached request, but is present in the query ' + + 'request.'); + }) + + .then(function() { + return cache.matchAll( + new Request('http://example.com/c', + {headers: {'Cookies': 'is-for-cookie'}})); + }) + .then(function(result) { + assert_response_array_equivalent( + result, + [entries.vary_cookie_is_cookie.response], + 'Cache.matchAll should match the entire header if a vary header ' + + 'is present in both the query and cached requests.'); + }); + }, 'Cache.matchAll with responses containing "Vary" header'); + +prepopulated_cache_test(vary_entries, function(cache, entries) { + return cache.matchAll('http://example.com/c', + {ignoreVary: true}) + .then(function(result) { + assert_response_array_equivalent( + result, + [ + entries.vary_cookie_is_cookie.response, + entries.vary_cookie_is_good.response, + entries.vary_cookie_absent.response + ], + 'Cache.matchAll should honor "ignoreVary" parameter.'); + }); + }, 'Cache.matchAll with "ignoreVary" parameter'); + +done(); diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js index 1d0a5b9fa14..8dbaf9789d5 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-put.js @@ -250,16 +250,22 @@ cache_test(function(cache) { assert_true( response.bodyUsed, '[https://fetch.spec.whatwg.org/#concept-body-consume-body] ' + - 'The text() method should set "body used" flag.'); - return assert_promise_rejects( - cache.put(new Request(test_url), response), - new TypeError, - '[https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-put] ' + - 'Cache put should reject with TypeError when Response ' + - 'body is already used.'); - }); + 'The text() method should make the body disturbed.'); + var request = new Request(test_url); + return cache.put(request, response).then(() => { + assert_unreached('cache.put should be rejected'); + }, () => {}); + }); }, 'Cache.put with a used response body'); +cache_test(function(cache) { + var response = new Response(test_body); + return cache.put(new Request(test_url), response) + .then(function() { + assert_throws(new TypeError(), () => response.body.getReader()); + }); + }, 'getReader() after Cache.put'); + cache_test(function(cache) { return assert_promise_rejects( cache.put(new Request(test_url), diff --git a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js index a8d4e7e61de..594b01b5810 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js +++ b/testing/web-platform/tests/service-workers/cache-storage/script-tests/cache-storage.js @@ -106,7 +106,6 @@ promise_test(function(t) { promise_test(function(t) { var cache_name = 'cache-storage/open'; - var url = '../resources/simple.txt'; var cache; return self.caches.delete(cache_name) .then(function() { @@ -135,7 +134,7 @@ promise_test(function(t) { assert_array_equals(actual_urls, expected_urls, 'CacheStorage.open should return a new Cache ' + 'object for the same backing store.'); - }) + }); }, 'CacheStorage.open with existing cache'); promise_test(function(t) { diff --git a/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-match.https.html b/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-match.https.html index 859b1cd0554..6126568fb46 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-match.https.html +++ b/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-match.https.html @@ -1,5 +1,5 @@ -Cache.match and Cache.matchAll +Cache.match diff --git a/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-matchAll.https.html b/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-matchAll.https.html new file mode 100644 index 00000000000..878fe1209f3 --- /dev/null +++ b/testing/web-platform/tests/service-workers/cache-storage/serviceworker/cache-matchAll.https.html @@ -0,0 +1,10 @@ + +Cache.matchAll + + + + + + diff --git a/testing/web-platform/tests/service-workers/cache-storage/window/cache-match.https.html b/testing/web-platform/tests/service-workers/cache-storage/window/cache-match.https.html index 093df8db4a8..a99d700accd 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/window/cache-match.https.html +++ b/testing/web-platform/tests/service-workers/cache-storage/window/cache-match.https.html @@ -1,5 +1,5 @@ -Cache Storage: Cache.match and Cache.matchAll +Cache Storage: Cache.match diff --git a/testing/web-platform/tests/service-workers/cache-storage/window/cache-matchAll.https.html b/testing/web-platform/tests/service-workers/cache-storage/window/cache-matchAll.https.html new file mode 100644 index 00000000000..3bd92887a49 --- /dev/null +++ b/testing/web-platform/tests/service-workers/cache-storage/window/cache-matchAll.https.html @@ -0,0 +1,9 @@ + +Cache.matchAll + + + + + + + diff --git a/testing/web-platform/tests/service-workers/cache-storage/worker/cache-match.https.html b/testing/web-platform/tests/service-workers/cache-storage/worker/cache-match.https.html index b0926fce360..5304529b775 100644 --- a/testing/web-platform/tests/service-workers/cache-storage/worker/cache-match.https.html +++ b/testing/web-platform/tests/service-workers/cache-storage/worker/cache-match.https.html @@ -1,5 +1,5 @@ -Cache.match and Cache.matchAll +Cache.match diff --git a/testing/web-platform/tests/service-workers/cache-storage/worker/cache-matchAll.https.html b/testing/web-platform/tests/service-workers/cache-storage/worker/cache-matchAll.https.html new file mode 100644 index 00000000000..242ccb68799 --- /dev/null +++ b/testing/web-platform/tests/service-workers/cache-storage/worker/cache-matchAll.https.html @@ -0,0 +1,9 @@ + +Cache.matchAll + + + + + diff --git a/testing/web-platform/tests/shadow-dom/OWNERS b/testing/web-platform/tests/shadow-dom/OWNERS new file mode 100644 index 00000000000..fd2132cf82d --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/OWNERS @@ -0,0 +1,5 @@ +@kojiishi +@rniwa +@sideshowbarker +@sizuhiko +@yutak diff --git a/testing/web-platform/tests/subresource-integrity/OWNERS b/testing/web-platform/tests/subresource-integrity/OWNERS new file mode 100644 index 00000000000..8f7edaa358b --- /dev/null +++ b/testing/web-platform/tests/subresource-integrity/OWNERS @@ -0,0 +1,6 @@ +@metromoxie +@fmarier +@jonathanKingston +@mikewest +@hillbrad +@mastahyeti diff --git a/testing/web-platform/tests/svg/OWNERS b/testing/web-platform/tests/svg/OWNERS new file mode 100644 index 00000000000..aeee0aac345 --- /dev/null +++ b/testing/web-platform/tests/svg/OWNERS @@ -0,0 +1,2 @@ +@heycam +@Ms2ger diff --git a/testing/web-platform/tests/touch-events/OWNERS b/testing/web-platform/tests/touch-events/OWNERS new file mode 100644 index 00000000000..ef580b2ebe7 --- /dev/null +++ b/testing/web-platform/tests/touch-events/OWNERS @@ -0,0 +1,3 @@ +@jtangelder +@zqzhang +@cynthia diff --git a/testing/web-platform/tests/typedarrays/OWNERS b/testing/web-platform/tests/typedarrays/OWNERS new file mode 100644 index 00000000000..f02987223e3 --- /dev/null +++ b/testing/web-platform/tests/typedarrays/OWNERS @@ -0,0 +1,4 @@ +@koustuvsinha +@zqzhang +@haoxli +@Ms2ger diff --git a/testing/web-platform/tests/url/OWNERS b/testing/web-platform/tests/url/OWNERS new file mode 100644 index 00000000000..b91af6024f0 --- /dev/null +++ b/testing/web-platform/tests/url/OWNERS @@ -0,0 +1,8 @@ +@mikewest +@frewsxcv +@tomalec +@rubys +@sideshowbarker +@zcorpan +@xiaojunwu +@smola diff --git a/testing/web-platform/tests/user-timing/OWNERS b/testing/web-platform/tests/user-timing/OWNERS new file mode 100644 index 00000000000..5a05f35799c --- /dev/null +++ b/testing/web-platform/tests/user-timing/OWNERS @@ -0,0 +1 @@ +@plehegar diff --git a/testing/web-platform/tests/vibration/OWNERS b/testing/web-platform/tests/vibration/OWNERS new file mode 100644 index 00000000000..4020ed82efc --- /dev/null +++ b/testing/web-platform/tests/vibration/OWNERS @@ -0,0 +1,3 @@ +@dontcallmedom +@zqzhang +@xinliux diff --git a/testing/web-platform/tests/web-animations/OWNERS b/testing/web-platform/tests/web-animations/OWNERS new file mode 100644 index 00000000000..fd38f5e5b9b --- /dev/null +++ b/testing/web-platform/tests/web-animations/OWNERS @@ -0,0 +1 @@ +@birtles diff --git a/testing/web-platform/tests/webaudio/OWNERS b/testing/web-platform/tests/webaudio/OWNERS new file mode 100644 index 00000000000..d4865fcd085 --- /dev/null +++ b/testing/web-platform/tests/webaudio/OWNERS @@ -0,0 +1 @@ +@chrislo diff --git a/testing/web-platform/tests/webdriver/OWNERS b/testing/web-platform/tests/webdriver/OWNERS new file mode 100644 index 00000000000..45cf0102cd8 --- /dev/null +++ b/testing/web-platform/tests/webdriver/OWNERS @@ -0,0 +1,4 @@ +@andreastt +@lukeis +@AutomatedTester +@shs96c diff --git a/testing/web-platform/tests/webgl/conformance-1.0.3/conformance/programs/program-test.html b/testing/web-platform/tests/webgl/conformance-1.0.3/conformance/programs/program-test.html index 53e1e61e3e3..18759f64af3 100644 --- a/testing/web-platform/tests/webgl/conformance-1.0.3/conformance/programs/program-test.html +++ b/testing/web-platform/tests/webgl/conformance-1.0.3/conformance/programs/program-test.html @@ -228,11 +228,11 @@ function go() { checkGetAttachedShaders([fs], [], [fs], "attaching a single shader should give the expected list"); checkGetAttachedShaders([fs, vs], [], [fs, vs], "attaching some shaders should give the expected list"); - checkGetAttachedShaders([fs], [fs], [], "attaching a shader and detaching it shoud leave an empty list"); + checkGetAttachedShaders([fs], [fs], [], "attaching a shader and detaching it should leave an empty list"); checkGetAttachedShaders([fs, vs], [fs, vs], [], - "attaching some shaders and detaching them in same order shoud leave an empty list"); + "attaching some shaders and detaching them in same order should leave an empty list"); checkGetAttachedShaders([fs, vs], [vs, fs], [], - "attaching some shaders and detaching them in random order shoud leave an empty list"); + "attaching some shaders and detaching them in random order should leave an empty list"); checkGetAttachedShaders([fs, vs], [vs], [fs], "attaching and detaching some shaders should leave the difference list"); checkGetAttachedShaders([fs, vs], [fs], [vs], diff --git a/testing/web-platform/tests/webmessaging/OWNERS b/testing/web-platform/tests/webmessaging/OWNERS new file mode 100644 index 00000000000..bff9fad87de --- /dev/null +++ b/testing/web-platform/tests/webmessaging/OWNERS @@ -0,0 +1,6 @@ +@zqzhang +@sideshowbarker +@plehegar +@aogilvie +@Ms2ger +@jdm diff --git a/testing/web-platform/tests/webrtc/OWNERS b/testing/web-platform/tests/webrtc/OWNERS new file mode 100644 index 00000000000..1deb98d49c9 --- /dev/null +++ b/testing/web-platform/tests/webrtc/OWNERS @@ -0,0 +1,4 @@ +@dontcallmedom +@tidoust +@alvestrand +@phoglund diff --git a/testing/web-platform/tests/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm b/testing/web-platform/tests/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm index a6b429397b3..56330805f83 100644 --- a/testing/web-platform/tests/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm +++ b/testing/web-platform/tests/websockets/Create-Secure-valid-url-protocol-setCorrectly.htm @@ -17,7 +17,7 @@ var isOpenCalled = false; wsocket.addEventListener('open', testOpen.step_func(function (evt) { - assert_equals(wsocket.protocol, __PROTOCOL, "protocol should be set to echo"); + assert_equals(wsocket.protocol, "echo", "protocol should be set to echo"); wsocket.close(); isOpenCalled = true; testOpen.done(); diff --git a/testing/web-platform/tests/websockets/OWNERS b/testing/web-platform/tests/websockets/OWNERS new file mode 100644 index 00000000000..a34c94d717f --- /dev/null +++ b/testing/web-platform/tests/websockets/OWNERS @@ -0,0 +1,9 @@ +@kristijanburnik +@zcorpan +@plehegar +@zqzhang +@sideshowbarker +@foolip +@Jxck +@jdm +@Ms2ger diff --git a/testing/web-platform/tests/websockets/websocket.js b/testing/web-platform/tests/websockets/websocket.js index 79b7bd59c58..1563dc2175c 100644 --- a/testing/web-platform/tests/websockets/websocket.js +++ b/testing/web-platform/tests/websockets/websocket.js @@ -4,26 +4,9 @@ var __SECURE__PORT = {{ports[wss][0]}}; var __NEW__PORT = __PORT; //All ports are non-default for now var __NEW__SECURE__PORT = __SECURE__PORT; //All ports are non-default for now var __PATH = "echo"; -var __CONTROLPATH = "control"; -var __PROTOCOL = "echo"; -var __PROTOCOLS = ["echo", "chat"]; -var __REPEATED__PROTOCOLS = ["echo", "echo"]; -var __REPEATED__PROTOCOLS_CASE_INSENSITIVE = ["echo", "eCho"]; -var __URL; -var __IS__WEBSOCKET; -var __PASS = "Pass"; -var __FAIL = "Fail"; var wsocket; -var csocket; var data; -// variables for testing Close Browser/Navigate Away scenarios -var isAssociated = false; -var guid; -var dataReceived; -var closeCode; -var urlToOpen; - function IsWebSocket() { if (!window.WebSocket) { assert_true(false, "Browser does not support WebSocket"); @@ -32,92 +15,93 @@ function IsWebSocket() { function CreateWebSocketNonAbsolute() { IsWebSocket(); - __URL = __SERVER__NAME; - wsocket = new WebSocket(__URL); + var url = __SERVER__NAME; + wsocket = new WebSocket(url); } function CreateWebSocketNonWsScheme() { IsWebSocket(); - __URL = "http://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL); + var url = "http://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url); } function CreateWebSocketNonAsciiProtocol(nonAsciiProtocol) { IsWebSocket(); - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL, nonAsciiProtocol); + var url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url, nonAsciiProtocol); } function CreateWebSocketWithAsciiSep(asciiWithSep) { IsWebSocket(); - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL, asciiWithSep); + var url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url, asciiWithSep); } function CreateWebSocketWithBlockedPort(blockedPort) { IsWebSocket(); - __URL = "wss://" + __SERVER__NAME + ":" + blockedPort + "/" + __PATH; - wsocket = new WebSocket(__URL); + var url = "wss://" + __SERVER__NAME + ":" + blockedPort + "/" + __PATH; + wsocket = new WebSocket(url); } function CreateWebSocketWithSpaceInUrl(urlWithSpace) { IsWebSocket(); - __URL = "ws://" + urlWithSpace + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL); + var url = "ws://" + urlWithSpace + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url); } function CreateWebSocketWithSpaceInProtocol(protocolWithSpace) { IsWebSocket(); - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL, protocolWithSpace); + var url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url, protocolWithSpace); } function CreateWebSocketWithRepeatedProtocols() { IsWebSocket(); - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL, __REPEATED__PROTOCOLS); + var url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url, ["echo", "echo"]); } function CreateWebSocketWithRepeatedProtocolsCaseInsensitive() { IsWebSocket(); - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; - wsocket = new WebSocket(__URL, __REPEATED__PROTOCOLS_CASE_INSENSITIVE); + var url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + wsocket = new WebSocket(url, ["echo", "eCho"]); } function CreateWebSocket(isSecure, isProtocol, isProtocols) { IsWebSocket(); + var url; if (isSecure) { if (__SECURE__PORT === null) { throw new Error("wss not yet supported"); } - __URL = "wss://" + __SERVER__NAME + ":" + __SECURE__PORT + "/" + __PATH; + url = "wss://" + __SERVER__NAME + ":" + __SECURE__PORT + "/" + __PATH; } else { - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; + url = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __PATH; } if (isProtocol) { - wsocket = new WebSocket(__URL, __PROTOCOL); + wsocket = new WebSocket(url, "echo"); } else if (isProtocols) { - wsocket = new WebSocket(__URL, __PROTOCOLS); + wsocket = new WebSocket(url, ["echo", "chat"]); } else { - wsocket = new WebSocket(__URL); + wsocket = new WebSocket(url); } return wsocket; } function CreateControlWebSocket(isSecure) { IsWebSocket(); + var url; if (isSecure) { - __URL = "wss://" + __SERVER__NAME + ":" + __SECURE__PORT + "/" + __CONTROLPATH; + url = "wss://" + __SERVER__NAME + ":" + __SECURE__PORT + "/control"; } else { - __URL = "ws://" + __SERVER__NAME + ":" + __PORT + "/" + __CONTROLPATH; + url = "ws://" + __SERVER__NAME + ":" + __PORT + "/control"; } - csocket = new WebSocket(__URL); - return csocket; + return new WebSocket(url); } diff --git a/testing/web-platform/tests/webstorage/OWNERS b/testing/web-platform/tests/webstorage/OWNERS new file mode 100644 index 00000000000..3b7fb5d888f --- /dev/null +++ b/testing/web-platform/tests/webstorage/OWNERS @@ -0,0 +1,9 @@ +@siusin +@inexorabletash +@zqzhang +@chunywang +@kangxu +@plehegar +@ibelem +@jdm +@Ms2ger diff --git a/testing/web-platform/tests/workers/OWNERS b/testing/web-platform/tests/workers/OWNERS new file mode 100644 index 00000000000..d211ea968e2 --- /dev/null +++ b/testing/web-platform/tests/workers/OWNERS @@ -0,0 +1,6 @@ +@zqzhang +@chunywang +@zcorpan +@caitp +@Ms2ger +@jdm diff --git a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.html b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.html deleted file mode 100644 index 00398c78077..00000000000 --- a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.html +++ /dev/null @@ -1,26 +0,0 @@ - - -importScripts no arguments - - -
    - - diff --git a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.worker.js b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.worker.js new file mode 100644 index 00000000000..aa86c8ef177 --- /dev/null +++ b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/001.worker.js @@ -0,0 +1,7 @@ +importScripts("/resources/testharness.js"); + +test(function() { + importScripts(); +}); + +done(); diff --git a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.html b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.html deleted file mode 100644 index e7d52e9797d..00000000000 --- a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.html +++ /dev/null @@ -1,32 +0,0 @@ - - -importScripts resolving urls - - -
    - - diff --git a/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.worker.js b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.worker.js new file mode 100644 index 00000000000..2cecbcb5352 --- /dev/null +++ b/testing/web-platform/tests/workers/interfaces/WorkerUtils/importScripts/002.worker.js @@ -0,0 +1,11 @@ +importScripts("/resources/testharness.js"); + +test(function() { + var ran = false; + assert_throws("SyntaxError", function() { + importScripts('data:text/javascript,ran=true','http://foo bar'); + }); + assert_false(ran, 'first argument to importScripts ran'); +}); + +done(); diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index 0897e7e60f9..1e4b90a0bac 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -493,7 +493,7 @@ nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly, if (fm) { nsCOMPtr focusedWindow; nsresult rv = fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); - if (NS_SUCCEEDED(rv)) { + if (NS_SUCCEEDED(rv) && focusedWindow) { auto* fwPI = nsPIDOMWindowOuter::From(focusedWindow); nsCOMPtr fwTreeItem (do_QueryInterface(fwPI->GetDocShell(), &rv)); diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 21e3ebb2171..6c8e220af45 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1096,10 +1096,11 @@ GfxInfo::GetGfxDriverInfo() nsIGfxInfo::FEATURE_WEBGL_ANGLE, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_BETWEEN_INCLUSIVE, V(8,861,0,0), V(8,862,6,5000), "Radeon driver > 8.862.6.5000"); + /* This may not be needed at all */ APPEND_TO_DRIVER_BLOCKLIST2(DRIVER_OS_WINDOWS_7, (nsAString&)GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*)GfxDriverInfo::GetDeviceFamily(Bug1155608), nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, - DRIVER_LESS_THAN_OR_EQUAL, V(8,15,10,2869)); + DRIVER_LESS_THAN, V(8,15,10,2869)); /* Bug 1203199/1092166: DXVA startup crashes on some intel drivers. */ APPEND_TO_DRIVER_BLOCKLIST_RANGE(DRIVER_OS_ALL, diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 79fb2e74711..9ee8226ee34 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -1379,9 +1379,8 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() fclose(file); NS_ENSURE_SUCCESS(rv, rv); - // Cleanup if (mURLShortcut) { - SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0); + SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0); } return rv; } diff --git a/widget/windows/nsClipboard.cpp b/widget/windows/nsClipboard.cpp index 67cdee82ea8..a98f384ba82 100644 --- a/widget/windows/nsClipboard.cpp +++ b/widget/windows/nsClipboard.cpp @@ -92,8 +92,9 @@ UINT nsClipboard::GetFormat(const char* aMimeStr) { UINT format; - if (strcmp(aMimeStr, kTextMime) == 0 || - strcmp(aMimeStr, kUnicodeMime) == 0) + if (strcmp(aMimeStr, kTextMime) == 0) + format = CF_TEXT; + else if (strcmp(aMimeStr, kUnicodeMime) == 0) format = CF_UNICODETEXT; else if (strcmp(aMimeStr, kRTFMime) == 0) format = ::RegisterClipboardFormat(L"Rich Text Format"); @@ -179,7 +180,14 @@ nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDa // Do various things internal to the implementation, like map one // flavor to another or add additional flavors based on what's required // for the win32 impl. - if ( strcmp(flavorStr, kHTMLMime) == 0 ) { + if ( strcmp(flavorStr, kUnicodeMime) == 0 ) { + // if we find text/unicode, also advertise text/plain (which we will convert + // on our own in nsDataObj::GetText(). + FORMATETC textFE; + SET_FORMATETC(textFE, CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL); + dObj->AddDataFlavor(kTextMime, &textFE); + } + else if ( strcmp(flavorStr, kHTMLMime) == 0 ) { // if we find text/html, also advertise win32's html flavor (which we will convert // on our own in nsDataObj::GetText(). FORMATETC htmlFE; @@ -191,10 +199,14 @@ nsresult nsClipboard::SetupNativeDataObject(nsITransferable * aTransferable, IDa // the "file" flavors so that the win32 shell knows to create an internet // shortcut when it sees one of these beasts. FORMATETC shortcutFE; + SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) + dObj->AddDataFlavor(kURLMime, &shortcutFE); SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) dObj->AddDataFlavor(kURLMime, &shortcutFE); SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) dObj->AddDataFlavor(kURLMime, &shortcutFE); + SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLA), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) + dObj->AddDataFlavor(kURLMime, &shortcutFE); SET_FORMATETC(shortcutFE, ::RegisterClipboardFormat(CFSTR_INETURLW), 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL) dObj->AddDataFlavor(kURLMime, &shortcutFE); } @@ -607,9 +619,11 @@ nsresult nsClipboard::GetDataFromDataObject(IDataObject * aDataObject, // when directly asking for the flavor. Let's try digging around in other // flavors to help satisfy our craving for data. if ( !dataFound ) { - if ( strcmp(flavorStr, kURLMime) == 0 ) { + if ( strcmp(flavorStr, kUnicodeMime) == 0 ) + dataFound = FindUnicodeFromPlainText ( aDataObject, anIndex, &data, &dataLen ); + else if ( strcmp(flavorStr, kURLMime) == 0 ) { // drags from other windows apps expose the native - // CFSTR_INETURLW flavor + // CFSTR_INETURL{A,W} flavor dataFound = FindURLFromNativeURL ( aDataObject, anIndex, &data, &dataLen ); if ( !dataFound ) dataFound = FindURLFromLocalFile ( aDataObject, anIndex, &data, &dataLen ); @@ -759,6 +773,39 @@ nsClipboard :: FindPlatformHTML ( IDataObject* inDataObject, UINT inIndex, } +// +// FindUnicodeFromPlainText +// +// we are looking for text/unicode and we failed to find it on the clipboard first, +// try again with text/plain. If that is present, convert it to unicode. +// +bool +nsClipboard :: FindUnicodeFromPlainText ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) +{ + // we are looking for text/unicode and we failed to find it on the clipboard first, + // try again with text/plain. If that is present, convert it to unicode. + nsresult rv = GetNativeDataOffClipboard(inDataObject, inIndex, GetFormat(kTextMime), nullptr, outData, outDataLen); + if (NS_FAILED(rv) || !*outData) { + return false; + } + + const char* castedText = static_cast(*outData); + nsAutoString tmp; + rv = NS_CopyNativeToUnicode(nsDependentCSubstring(castedText, *outDataLen), tmp); + if (NS_FAILED(rv)) { + return false; + } + + // out with the old, in with the new + free(*outData); + *outData = ToNewUnicode(tmp); + *outDataLen = tmp.Length() * sizeof(char16_t); + + return true; + +} // FindUnicodeFromPlainText + + // // FindURLFromLocalFile // @@ -825,7 +872,7 @@ nsClipboard :: FindURLFromLocalFile ( IDataObject* inDataObject, UINT inIndex, v // // we are looking for a URL and couldn't find it using our internal // URL flavor, so look for it using the native URL flavor, -// CF_INETURLSTRW +// CF_INETURLSTRW (We don't handle CF_INETURLSTRA currently) // bool nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, void** outData, uint32_t* outDataLen ) @@ -846,6 +893,29 @@ nsClipboard :: FindURLFromNativeURL ( IDataObject* inDataObject, UINT inIndex, v free(tempOutData); dataFound = true; } + else { + loadResult = GetNativeDataOffClipboard(inDataObject, inIndex, ::RegisterClipboardFormat(CFSTR_INETURLA), nullptr, &tempOutData, &tempDataLen); + if ( NS_SUCCEEDED(loadResult) && tempOutData ) { + // CFSTR_INETURLA is (currently) equal to CFSTR_SHELLURL which is equal to CF_TEXT + // which is by definition ANSI encoded. + nsCString urlUnescapedA; + bool unescaped = NS_UnescapeURL(static_cast(tempOutData), tempDataLen, esc_OnlyNonASCII | esc_SkipControl, urlUnescapedA); + + nsString urlString; + if (unescaped) + NS_CopyNativeToUnicode(urlUnescapedA, urlString); + else + NS_CopyNativeToUnicode(nsDependentCString(static_cast(tempOutData), tempDataLen), urlString); + + // the internal mozilla URL format, text/x-moz-url, contains + // URL\ntitle. Since we don't actually have a title here, + // just repeat the URL to fake it. + *outData = ToNewUnicode(urlString + NS_LITERAL_STRING("\n") + urlString); + *outDataLen = NS_strlen(static_cast(*outData)) * sizeof(char16_t); + free(tempOutData); + dataFound = true; + } + } return dataFound; } // FindURLFromNativeURL @@ -942,6 +1012,16 @@ NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, *_retval = true; break; } + else { + // We haven't found the exact flavor the client asked for, but maybe we can + // still find it from something else that's on the clipboard... + if (strcmp(aFlavorList[i], kUnicodeMime) == 0) { + // client asked for unicode and it wasn't present, check if we have CF_TEXT. + // We'll handle the actual data substitution in the data object. + if (IsClipboardFormatAvailable(GetFormat(kTextMime))) + *_retval = true; + } + } } return NS_OK; diff --git a/widget/windows/nsDataObj.cpp b/widget/windows/nsDataObj.cpp index fcdae453df2..f62da18eb05 100644 --- a/widget/windows/nsDataObj.cpp +++ b/widget/windows/nsDataObj.cpp @@ -1290,7 +1290,27 @@ HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGME // by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for // CF_UNICODETEXT). DWORD allocLen = (DWORD)len; - if ( aFE.cfFormat == nsClipboard::CF_HTML ) { + if ( aFE.cfFormat == CF_TEXT ) { + // Someone is asking for text/plain; convert the unicode (assuming it's present) + // to text with the correct platform encoding. + size_t bufferSize = sizeof(char)*(len + 2); + char* plainTextData = static_cast(moz_xmalloc(bufferSize)); + char16_t* castedUnicode = reinterpret_cast(data); + int32_t plainTextLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)castedUnicode, len / 2 + 1, plainTextData, bufferSize, NULL, NULL); + // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include + // the null in the length. + free(data); + if ( plainTextLen ) { + data = plainTextData; + allocLen = plainTextLen; + } + else { + free(plainTextData); + NS_WARNING ( "Oh no, couldn't convert unicode to plain text" ); + return S_OK; + } + } + else if ( aFE.cfFormat == nsClipboard::CF_HTML ) { // Someone is asking for win32's HTML flavor. Convert our html fragment // from unicode to UTF-8 then put it into a format specified by msft. NS_ConvertUTF16toUTF8 converter ( reinterpret_cast(data) ); diff --git a/xpcom/base/Logging.cpp b/xpcom/base/Logging.cpp index 2679b9378a0..2223f1b4bfb 100644 --- a/xpcom/base/Logging.cpp +++ b/xpcom/base/Logging.cpp @@ -86,6 +86,7 @@ public: : mModulesLock("logmodules") , mModules(kInitialModuleCount) , mOutFile(nullptr) + , mMainThread(PR_GetCurrentThread()) , mAddTimestamp(false) , mIsSync(false) { @@ -176,20 +177,31 @@ public: // // Additionally we prefix the output with the abbreviated log level // and the module name. + PRThread *currentThread = PR_GetCurrentThread(); + const char *currentThreadName = (mMainThread == currentThread) + ? "Main Thread" + : PR_GetThreadName(currentThread); + + char noNameThread[40]; + if (!currentThreadName) { + snprintf_literal(noNameThread, "Unnamed thread %p", currentThread); + currentThreadName = noNameThread; + } + if (!mAddTimestamp) { fprintf_stderr(out, - "[%p]: %s/%s %s%s", - PR_GetCurrentThread(), ToLogStr(aLevel), + "[%s]: %s/%s %s%s", + currentThreadName, ToLogStr(aLevel), aName, buffToWrite, newline); } else { PRExplodedTime now; PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now); fprintf_stderr( out, - "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%p]: %s/%s %s%s", + "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%s]: %s/%s %s%s", now.tm_year, now.tm_month + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec, - PR_GetCurrentThread(), ToLogStr(aLevel), + currentThreadName, ToLogStr(aLevel), aName, buffToWrite, newline); } @@ -206,6 +218,7 @@ private: OffTheBooksMutex mModulesLock; nsClassHashtable mModules; ScopedCloseFile mOutFile; + PRThread *mMainThread; bool mAddTimestamp; bool mIsSync; }; diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp index 7d6647d39ea..08d9583020a 100644 --- a/xpcom/build/XPCOMInit.cpp +++ b/xpcom/build/XPCOMInit.cpp @@ -968,18 +968,6 @@ ShutdownXPCOM(nsIServiceManager* aServMgr) NS_ShutdownNativeCharsetUtils(); #endif -#if defined(XP_WIN) - // This exit(0) call is intended to be temporary, to get shutdown leak - // checking working on Linux. - // On Windows XP debug, there are intermittent failures in - // dom/media/tests/mochitest/test_peerConnection_basicH264Video.html - // if we don't exit early in a child process. See bug 1073310. - if (XRE_IsContentProcess() && !IsVistaOrLater()) { - NS_WARNING("Exiting child process early!"); - exit(0); - } -#endif - // Shutdown xpcom. This will release all loaders and cause others holding // a refcount to the component manager to release it. if (nsComponentManagerImpl::gComponentManager) { @@ -1054,13 +1042,13 @@ ShutdownXPCOM(nsIServiceManager* aServMgr) NS_LogTerm(); #if defined(MOZ_WIDGET_GONK) - // This exit(0) call is intended to be temporary, to get shutdown leak - // checking working on Linux. + // This _exit(0) call is intended to be temporary, to get shutdown leak + // checking working on non-B2G platforms. // On debug B2G, the child process crashes very late. Instead, just // give up so at least we exit cleanly. See bug 1071866. if (XRE_IsContentProcess()) { NS_WARNING("Exiting child process early!"); - exit(0); + _exit(0); } #endif diff --git a/xpcom/glue/nsBaseHashtable.h b/xpcom/glue/nsBaseHashtable.h index 99705b3f323..d4a708b161f 100644 --- a/xpcom/glue/nsBaseHashtable.h +++ b/xpcom/glue/nsBaseHashtable.h @@ -12,15 +12,6 @@ #include "nsTHashtable.h" #include "nsDebug.h" -// These type is returned by |EnumFunction| and controls the behavior of -// Enumerate(). The PLD/PL_D prefix is because it originated in PLDHashTable, -// but that class no longer uses it. -enum PLDHashOperator -{ - PL_DHASH_NEXT = 0, // enumerator says continue - PL_DHASH_REMOVE = 2 // enumerator says remove -}; - template class nsBaseHashtable; // forward declaration @@ -155,41 +146,6 @@ public: */ void Remove(KeyType aKey) { this->RemoveEntry(aKey); } - /** - * function type provided by the application for enumeration. - * @param aKey the key being enumerated - * @param aData Reference to data being enumerated, may be altered. e.g. for - * nsInterfaceHashtable this is an nsCOMPtr reference... - * @parm aUserArg passed unchanged from Enumerate - * @return bitflag combination of - * @link PLDHashOperator::PL_DHASH_REMOVE @endlink or - * @link PLDHashOperator::PL_DHASH_NEXT PL_DHASH_NEXT @endlink - */ - typedef PLDHashOperator (*EnumFunction)(KeyType aKey, - DataType& aData, - void* aUserArg); - - /** - * enumerate entries in the hashtable, allowing changes. - * WARNING: this function is deprecated. Please use Iterator and/or - * MutatingIterator instead. - * @param aEnumFunc enumeration callback - * @param aUserArg passed unchanged to the EnumFunction - */ - uint32_t Enumerate(EnumFunction aEnumFunc, void* aUserArg) - { - uint32_t n = 0; - for (auto iter = this->mTable.Iter(); !iter.Done(); iter.Next()) { - auto entry = static_cast(iter.Get()); - PLDHashOperator op = aEnumFunc(entry->GetKey(), entry->mData, aUserArg); - n++; - if (op & PL_DHASH_REMOVE) { - iter.Remove(); - } - } - return n; - } - // This is an iterator that also allows entry removal. Example usage: // // for (auto iter = table.Iter(); !iter.Done(); iter.Next()) { diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp index f4853bee136..b44fd21a821 100644 --- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp +++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_x86_64_unix.cpp @@ -47,9 +47,9 @@ static void invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s, uint64_t * gpregs, double * fpregs) { - uint32_t nr_gpr = 1; // skip one GP register for 'that' - uint32_t nr_fpr = 0; - uint64_t value; + uint32_t nr_gpr = 1u; // skip one GP register for 'that' + uint32_t nr_fpr = 0u; + uint64_t value = 0u; for (uint32_t i = 0; i < paramCount; i++, s++) { if (s->IsPtrData()) diff --git a/xpcom/tests/TestHashtables.cpp b/xpcom/tests/TestHashtables.cpp index b72fe45985a..9dd4fecd0ce 100644 --- a/xpcom/tests/TestHashtables.cpp +++ b/xpcom/tests/TestHashtables.cpp @@ -170,19 +170,6 @@ testTHashtable(nsTHashtable& hash, uint32_t numEntries) { } } -PLDHashOperator -nsDEnum(const uint32_t& aKey, const char*& aData, void* userArg) { - printf(" enumerated %u = \"%s\"\n", aKey, aData); - return PL_DHASH_NEXT; -} - -PLDHashOperator -nsCEnum(const nsACString& aKey, nsAutoPtr& aData, void* userArg) { - printf(" enumerated \"%s\" = %c\n", - PromiseFlatCString(aKey).get(), aData->GetChar()); - return PL_DHASH_NEXT; -} - // // all this nsIFoo stuff was copied wholesale from TestCOMPtr.cpp // @@ -326,25 +313,6 @@ CreateIFoo( IFoo** result ) return NS_OK; } -PLDHashOperator -nsIEnum(const uint32_t& aKey, nsCOMPtr& aData, void* userArg) { - nsAutoCString str; - aData->GetString(str); - - printf(" enumerated %u = \"%s\"\n", aKey, str.get()); - return PL_DHASH_NEXT; -} - -PLDHashOperator -nsIEnum2(nsISupports* aKey, uint32_t& aData, void* userArg) { - nsAutoCString str; - nsCOMPtr foo = do_QueryInterface(aKey); - foo->GetString(str); - - printf(" enumerated \"%s\" = %u\n", str.get(), aData); - return PL_DHASH_NEXT; -} - } // namespace TestHashtables using namespace TestHashtables; @@ -369,7 +337,7 @@ main(void) { printf("Check enumeration..."); count = nsTIterPrint(EntityToUnicode); - if (count) { + if (count != 0) { printf("entries remain in table!\n"); exit (8); } @@ -384,7 +352,7 @@ main(void) { printf("Check enumeration..."); count = nsTIterPrint(EntityToUnicode); - if (count) { + if (count != 0) { printf("entries remain in table!\n"); exit (9); } @@ -445,8 +413,12 @@ main(void) { printf("OK\n"); printf("Checking count..."); - count = UniToEntity.Enumerate(nsDEnum, nullptr); - if (count) { + count = 0; + for (auto iter = UniToEntity.Iter(); !iter.Done(); iter.Next()) { + printf(" enumerated %u = \"%s\"\n", iter.Key(), iter.Data()); + count++; + } + if (count != 0) { printf(" Clear did not remove all entries.\n"); exit (15); } @@ -510,8 +482,13 @@ main(void) { printf(" Clearing OK\n"); printf("Checking count..."); - count = EntToUniClass.Enumerate(nsCEnum, nullptr); - if (count) { + count = 0; + for (auto iter = EntToUniClass.Iter(); !iter.Done(); iter.Next()) { + printf(" enumerated \"%s\" = %c\n", + PromiseFlatCString(iter.Key()).get(), iter.Data()->GetChar()); + count++; + } + if (count != 0) { printf(" Clear did not remove all entries.\n"); exit (21); } @@ -584,8 +561,15 @@ main(void) { printf(" Clearing OK\n"); printf("Checking count..."); - count = EntToUniClass2.Enumerate(nsIEnum2, nullptr); - if (count) { + count = 0; + for (auto iter = EntToUniClass2.Iter(); !iter.Done(); iter.Next()) { + nsAutoCString s; + nsCOMPtr foo = do_QueryInterface(iter.Key()); + foo->GetString(s); + printf(" enumerated \"%s\" = %u\n", s.get(), iter.Data()); + count++; + } + if (count != 0) { printf(" Clear did not remove all entries.\n"); exit (27); } @@ -656,8 +640,14 @@ main(void) { printf(" Clearing OK\n"); printf("Checking count..."); - count = UniToEntClass2.Enumerate(nsIEnum, nullptr); - if (count) { + count = 0; + for (auto iter = UniToEntClass2.Iter(); !iter.Done(); iter.Next()) { + nsAutoCString s; + iter.Data()->GetString(s); + printf(" enumerated %u = \"%s\"\n", iter.Key(), s.get()); + count++; + } + if (count != 0) { printf(" Clear did not remove all entries.\n"); exit (33); } diff --git a/xpcom/tests/gtest/TestPLDHash.cpp b/xpcom/tests/gtest/TestPLDHash.cpp index e5c82734485..6d7150088cd 100644 --- a/xpcom/tests/gtest/TestPLDHash.cpp +++ b/xpcom/tests/gtest/TestPLDHash.cpp @@ -345,8 +345,8 @@ TEST(PLDHashTableTest, GrowToMaxCapacity) numInserted++; } - // We stop when the element count is 96.875% of PL_DHASH_MAX_SIZE (see - // MaxLoadOnGrowthFailure()). + // We stop when the element count is 96.875% of PLDHashTable::kMaxCapacity + // (see MaxLoadOnGrowthFailure()). if (numInserted != PLDHashTable::kMaxCapacity - (PLDHashTable::kMaxCapacity >> 5)) { delete t; diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 1df052b7f6e..2fcd6ad512c 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -36,6 +36,7 @@ #include "nsXPCOMPrivate.h" #include "mozilla/ChaosMode.h" #include "mozilla/TimeStamp.h" +#include "mozilla/unused.h" #include "nsThreadSyncDispatch.h" #include "LeakRefPtr.h" @@ -238,6 +239,15 @@ private: struct nsThreadShutdownContext { + nsThreadShutdownContext() + { + MOZ_COUNT_CTOR(nsThreadShutdownContext); + } + ~nsThreadShutdownContext() + { + MOZ_COUNT_DTOR(nsThreadShutdownContext); + } + // NB: This will be the last reference. RefPtr terminatingThread; nsThread* joiningThread; @@ -505,6 +515,19 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) nsThread::~nsThread() { + NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(), + "shouldn't be waiting on other threads to shutdown"); +#ifdef DEBUG + // We deliberately leak these so they can be tracked by the leak checker. + // If you're having nsThreadShutdownContext leaks, you can set: + // XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext + // during a test run and that will at least tell you what thread is + // requesting shutdown on another, which can be helpful for diagnosing + // the leak. + for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) { + Unused << mRequestedShutdownContexts[i].forget(); + } +#endif } nsresult