diff --git a/.gitignore b/.gitignore index 61261431867e..959052720949 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,7 @@ testing/web-platform/products/ mobile/android/gradle/.gradle # XCode project cruft +/*.xcodeproj/ embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata diff --git a/.hgignore b/.hgignore index d005d38a5bb1..626d51b96af4 100644 --- a/.hgignore +++ b/.hgignore @@ -107,6 +107,7 @@ GPATH ^mobile/android/gradle/.gradle # XCode project cruft +^[^/]*\.xcodeproj/ ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 95b04a7a601c..29551353af32 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1336,6 +1336,13 @@ HyperTextAccessible::SetSelectionRange(int32_t aStartPos, int32_t aEndPos) domSel->RemoveRange(domSel->GetRangeAt(idx)); SetSelectionBoundsAt(0, aStartPos, aEndPos); + // Make sure it is visible + domSel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, + nsIPresShell::ScrollAxis(), + nsIPresShell::ScrollAxis(), + dom::Selection::SCROLL_FOR_CARET_MOVE | + dom::Selection::SCROLL_OVERFLOW_HIDDEN); + // When selection is done, move the focus to the selection if accessible is // not focusable. That happens when selection is set within hypertext // accessible. diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index e2e44b245599..59d1cde3e248 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2002,17 +2002,6 @@ if (AppConstants.platform == "macosx") { }; } - -/* Legacy global init functions */ -var BrowserStartup = gBrowserInit.onLoad.bind(gBrowserInit); -var BrowserShutdown = gBrowserInit.onUnload.bind(gBrowserInit); - -if (AppConstants.platform == "macosx") { - var nonBrowserWindowStartup = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit); - var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup.bind(gBrowserInit); - var nonBrowserWindowShutdown = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit); -} - function HandleAppCommandEvent(evt) { switch (evt.command) { case "Back": diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js index c881cc639977..1722bb76aef5 100644 --- a/browser/base/content/test/general/browser_bug553455.js +++ b/browser/base/content/test/general/browser_bug553455.js @@ -25,6 +25,10 @@ const CHROMEROOT = croot; var gApp = document.getElementById("bundle_brand").getString("brandShortName"); var gVersion = Services.appinfo.version; +function waitForTick() { + return new Promise(resolve => executeSoon(resolve)); +} + function getObserverTopic(aNotificationId) { let topic = aNotificationId; if (topic == "xpinstall-disabled") @@ -69,6 +73,7 @@ async function waitForProgressNotification(aPanelOpen = false, aExpectedCount = await observerPromise; await panelEventPromise; + await waitForTick(); info("Saw a notification"); ok(PopupNotifications.isPanelOpen, "Panel should be open"); @@ -114,6 +119,7 @@ async function waitForNotification(aId, aExpectedCount = 1) { await observerPromise; await panelEventPromise; + await waitForTick(); info("Saw a " + aId + " notification"); ok(PopupNotifications.isPanelOpen, "Panel should be open"); diff --git a/browser/base/content/test/general/browser_tab_close_dependent_window.js b/browser/base/content/test/general/browser_tab_close_dependent_window.js index bc6ed293589a..7d45c5fcb85c 100644 --- a/browser/base/content/test/general/browser_tab_close_dependent_window.js +++ b/browser/base/content/test/general/browser_tab_close_dependent_window.js @@ -15,8 +15,11 @@ add_task(async function closing_tab_with_dependents_should_close_window() { let openedTab = (await depTabOpened).target; info("Got opened tab"); + let otherTabClosePromise = BrowserTestUtils.tabRemoved(openedTab); let windowClosedPromise = BrowserTestUtils.windowClosed(win); await BrowserTestUtils.removeTab(tab); + info("Wait for other tab to close, this shouldn't time out"); + await otherTabClosePromise; is(Cu.isDeadWrapper(openedTab) || openedTab.linkedBrowser == null, true, "Opened tab should also have closed"); info("If we timeout now, the window failed to close - that shouldn't happen!"); await windowClosedPromise; diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 5977574fe89c..f39862640939 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1933,8 +1933,11 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. let identityIcon = document.getElementById("identity-icon"); let identityRect = this.DOMWindowUtils.getBoundsWithoutFlushing(identityIcon); - this.siteIconStart = popupDirection == "rtl" ? identityRect.right - : identityRect.left; + if (popupDirection == "rtl") { + this.siteIconStart = documentRect.right - identityRect.right; + } else { + this.siteIconStart = identityRect.left; + } } else { // Reset the alignment so that the site icons are positioned // according to whatever's in the CSS. @@ -1989,10 +1992,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. deckIndex = 1; if (this.siteIconStart) { - let rect = this.DOMWindowUtils.getBoundsWithoutFlushing(window.document.documentElement); - let padding = popupDirection == "rtl" ? rect.right - this.siteIconStart - : this.siteIconStart; - this.searchSuggestionsNotification.style.paddingInlineStart = padding + "px"; + this.searchSuggestionsNotification.style.paddingInlineStart = + this.siteIconStart + "px"; } else { this.searchSuggestionsNotification.style.removeProperty("padding-inline-start"); } diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index f3010a62859f..b93e2cf9dd94 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -266,6 +266,12 @@ font-size: 1.25em; } +/* Ensure diacritics and other edge-of-font-box glyphs do not get clipped, + * even in non-Latin scripts. */ +html|input.urlbar-input { + line-height: 1.745em; +} + #urlbar[focused="true"], .searchbar-textbox[focused="true"] { border-color: -moz-mac-focusring; diff --git a/devtools/server/actors/highlighters/shapes.js b/devtools/server/actors/highlighters/shapes.js index 7d239528e699..682bf779be3a 100644 --- a/devtools/server/actors/highlighters/shapes.js +++ b/devtools/server/actors/highlighters/shapes.js @@ -6,7 +6,8 @@ const { CanvasFrameAnonymousContentHelper, getCSSStyleRules, createSVGNode, createNode, getComputedStyle } = require("./utils/markup"); -const { setIgnoreLayoutChanges, getCurrentZoom } = require("devtools/shared/layout/utils"); +const { setIgnoreLayoutChanges, getCurrentZoom, + getAdjustedQuads } = require("devtools/shared/layout/utils"); const { AutoRefreshHighlighter } = require("./auto-refresh"); const { getDistance, @@ -176,6 +177,27 @@ class ShapesHighlighter extends AutoRefreshHighlighter { }; } + get frameDimensions() { + // In an iframe, we get the node's quads relative to the frame, + // instead of the parent document. + let dims = getAdjustedQuads(this.currentNode.ownerGlobal, + this.currentNode, this.referenceBox)[0].bounds; + let zoom = getCurrentZoom(this.win); + + if (this.currentNode.getBBox && + getComputedStyle(this.currentNode).stroke !== "none" && !this.useStrokeBox) { + dims = getObjectBoundingBox(dims.top, dims.left, + dims.width, dims.height, this.currentNode); + } + + return { + top: dims.top / zoom, + left: dims.left / zoom, + width: dims.width / zoom, + height: dims.height / zoom + }; + } + handleEvent(event, id) { // No event handling if the highlighter is hidden if (this.areShapesHidden()) { @@ -702,7 +724,10 @@ class ShapesHighlighter extends AutoRefreshHighlighter { * in percentages relative to the element. */ convertPageCoordsToPercent(pageX, pageY) { - let { top, left, width, height } = this.zoomAdjustedDimensions; + // If the current node is in an iframe, we get dimensions relative to the frame. + let dims = (this.highlighterEnv.window.document === this.currentNode.ownerDocument) ? + this.zoomAdjustedDimensions : this.frameDimensions; + let { top, left, width, height } = dims; pageX -= left; pageY -= top; let percentX = pageX * 100 / width; diff --git a/dom/base/crashtests/1324500.html b/dom/base/crashtests/1324500.html new file mode 100644 index 000000000000..83072c9bd151 --- /dev/null +++ b/dom/base/crashtests/1324500.html @@ -0,0 +1,4 @@ + + diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 0a58e6bc6376..521692ca92de 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -205,6 +205,7 @@ load 1230422.html load 1251361.html load 1304437.html pref(dom.IntersectionObserver.enabled,true) load 1324209.html +load 1324500.html pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html pref(dom.IntersectionObserver.enabled,true) load 1332939.html diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index a05f6cf6d463..465dbac36a9c 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -32,8 +32,6 @@ using mozilla::dom::Element; AutoTArray, 4>* nsDOMMutationObserver::sScheduledMutationObservers = nullptr; -nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr; - uint32_t nsDOMMutationObserver::sMutationLevel = 0; uint64_t nsDOMMutationObserver::sCount = 0; @@ -599,10 +597,32 @@ nsDOMMutationObserver::ScheduleForRun() RescheduleForRun(); } +class MutationObserverMicroTask final : public MicroTaskRunnable +{ +public: + virtual void Run(AutoSlowOperation& aAso) override + { + nsDOMMutationObserver::HandleMutations(aAso); + } + + virtual bool Suppressed() override + { + return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed(); + } +}; + void nsDOMMutationObserver::RescheduleForRun() { if (!sScheduledMutationObservers) { + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (!ccjs) { + return; + } + + RefPtr momt = + new MutationObserverMicroTask(); + ccjs->DispatchMicroTaskRunnable(momt.forget()); sScheduledMutationObservers = new AutoTArray, 4>; } @@ -864,37 +884,9 @@ nsDOMMutationObserver::HandleMutation() mCallback->Call(this, mutations, *this); } -class AsyncMutationHandler : public mozilla::Runnable -{ -public: - AsyncMutationHandler() : mozilla::Runnable("AsyncMutationHandler") {} - NS_IMETHOD Run() override - { - nsDOMMutationObserver::HandleMutations(); - return NS_OK; - } -}; - void -nsDOMMutationObserver::HandleMutationsInternal() +nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso) { - if (!nsContentUtils::IsSafeToRunScript()) { - nsContentUtils::AddScriptRunner(new AsyncMutationHandler()); - return; - } - static RefPtr sCurrentObserver; - if (sCurrentObserver && !sCurrentObserver->Suppressed()) { - // In normal cases sScheduledMutationObservers will be handled - // after previous mutations are handled. But in case some - // callback calls a sync API, which spins the eventloop, we need to still - // process other mutations happening during that sync call. - // This does *not* catch all cases, but should work for stuff running - // in separate tabs. - return; - } - - mozilla::AutoSlowOperation aso; - nsTArray >* suppressedObservers = nullptr; while (sScheduledMutationObservers) { @@ -902,20 +894,21 @@ nsDOMMutationObserver::HandleMutationsInternal() sScheduledMutationObservers; sScheduledMutationObservers = nullptr; for (uint32_t i = 0; i < observers->Length(); ++i) { - sCurrentObserver = static_cast((*observers)[i]); - if (!sCurrentObserver->Suppressed()) { - sCurrentObserver->HandleMutation(); + RefPtr currentObserver = + static_cast((*observers)[i]); + if (!currentObserver->Suppressed()) { + currentObserver->HandleMutation(); } else { if (!suppressedObservers) { suppressedObservers = new nsTArray >; } - if (!suppressedObservers->Contains(sCurrentObserver)) { - suppressedObservers->AppendElement(sCurrentObserver); + if (!suppressedObservers->Contains(currentObserver)) { + suppressedObservers->AppendElement(currentObserver); } } } delete observers; - aso.CheckForInterrupt(); + aAso.CheckForInterrupt(); } if (suppressedObservers) { @@ -926,7 +919,6 @@ nsDOMMutationObserver::HandleMutationsInternal() delete suppressedObservers; suppressedObservers = nullptr; } - sCurrentObserver = nullptr; } nsDOMMutationRecord* diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index 7dd911b0fb41..cc73942daec0 100644 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -575,13 +575,29 @@ public: } // static methods - static void HandleMutations() + static void HandleMutations(mozilla::AutoSlowOperation& aAso) { if (sScheduledMutationObservers) { - HandleMutationsInternal(); + HandleMutationsInternal(aAso); } } + static bool AllScheduledMutationObserversAreSuppressed() + { + if (sScheduledMutationObservers) { + uint32_t len = sScheduledMutationObservers->Length(); + if (len > 0) { + for (uint32_t i = 0; i < len; ++i) { + if (!(*sScheduledMutationObservers)[i]->Suppressed()) { + return false; + } + } + return true; + } + } + return false; + } + static void EnterMutationHandling(); static void LeaveMutationHandling(); @@ -613,7 +629,7 @@ protected: return mOwner && nsGlobalWindow::Cast(mOwner)->IsInSyncOperation(); } - static void HandleMutationsInternal(); + static void HandleMutationsInternal(mozilla::AutoSlowOperation& aAso); static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver, uint32_t aMutationLevel); @@ -641,7 +657,6 @@ protected: static uint64_t sCount; static AutoTArray, 4>* sScheduledMutationObservers; - static nsDOMMutationObserver* sCurrentObserver; static uint32_t sMutationLevel; static AutoTArray, 4>, 4>* diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 5f04a19b569e..8ab778f6e36d 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -576,8 +576,6 @@ DumpString(const nsAString &str) #define JS_OPTIONS_DOT_STR "javascript.options." -static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR; - nsJSContext::nsJSContext(bool aGCOnDestruction, nsIScriptGlobalObject* aGlobalObject) : mWindowProxy(nullptr) diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index e52858b18fe7..106b45a8a264 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -21,9 +21,6 @@ #include "nsJSUtils.h" #include "WorkerPrivate.h" -static const char kSetIntervalStr[] = "setInterval"; -static const char kSetTimeoutStr[] = "setTimeout"; - using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::workers; diff --git a/dom/base/test/test_mutationobservers.html b/dom/base/test/test_mutationobservers.html index b99994234bbc..8eeea9bdc240 100644 --- a/dom/base/test/test_mutationobservers.html +++ b/dom/base/test/test_mutationobservers.html @@ -362,7 +362,7 @@ function testChildList5() { is(records[5].previousSibling, c3, ""); is(records[5].nextSibling, c5, ""); observer.disconnect(); - then(testAdoptNode); + then(testNestedMutations); m = null; }); m.observe(div, { childList: true, subtree: true }); @@ -375,6 +375,37 @@ function testChildList5() { div.appendChild(emptyDF); // empty document shouldn't cause mutation records } +function testNestedMutations() { + div.textContent = null; + div.appendChild(document.createTextNode("foo")); + var m2WasCalled = false; + m = new M(function(records, observer) { + is(records[0].type, "characterData", "Should have got characterData"); + observer.disconnect(); + m = null; + m3 = new M(function(records, observer) { + ok(m2WasCalled, "m2 should have been called before m3!"); + is(records[0].type, "characterData", "Should have got characterData"); + observer.disconnect(); + then(testAdoptNode); + m3 = null; + }); + m3.observe(div, { characterData: true, subtree: true}); + div.firstChild.data = "foo"; + }); + m2 = new M(function(records, observer) { + m2WasCalled = true; + is(records[0].type, "characterData", "Should have got characterData"); + observer.disconnect(); + m2 = null; + }); + m2.observe(div, { characterData: true, subtree: true}); + div.appendChild(document.createTextNode("foo")); + m.observe(div, { characterData: true, subtree: true }); + + div.firstChild.data = "bar"; +} + function testAdoptNode() { var d1 = document.implementation.createHTMLDocument(null); var d2 = document.implementation.createHTMLDocument(null); diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index 97ca7f0c696e..50d4355c21d1 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -345,18 +345,19 @@ template<> class Optional { public: - Optional() : mPassed(false) {} + Optional() + : mStr(nullptr) + {} bool WasPassed() const { - return mPassed; + return !!mStr; } void operator=(const nsAString* str) { MOZ_ASSERT(str); mStr = str; - mPassed = true; } // If this code ever goes away, remove the comment pointing to it in the @@ -365,7 +366,6 @@ public: { MOZ_ASSERT(str); mStr = reinterpret_cast(str); - mPassed = true; } const nsAString& Value() const @@ -379,7 +379,6 @@ private: Optional(const Optional& other) = delete; const Optional &operator=(const Optional &other) = delete; - bool mPassed; const nsAString* mStr; }; @@ -388,8 +387,9 @@ class NonNull { public: NonNull() + : ptr(nullptr) #ifdef DEBUG - : inited(false) + , inited(false) #endif {} diff --git a/dom/bindings/FakeString.h b/dom/bindings/FakeString.h index d49e43df09b9..a8db1917c50d 100644 --- a/dom/bindings/FakeString.h +++ b/dom/bindings/FakeString.h @@ -19,6 +19,8 @@ namespace binding_detail { // for small strings and a nsStringBuffer for longer strings. struct FakeString { FakeString() : + mData(nsString::char_traits::sEmptyBuffer), + mLength(0), mDataFlags(nsString::DataFlags::TERMINATED), mClassFlags(nsString::ClassFlags(0)) { diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 4d36cdee11f3..e5fee86cfb25 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -2084,17 +2084,7 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId, rv = state->GetIsNull(6, &nullPadding); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } -#ifdef NIGHTLY_BUILD - bool shouldUpdateTo26 = false; - if (nullPadding && aSavedResponseOut->mValue.type() == ResponseType::Opaque) { - // XXXtt: This should be removed in the future (e.g. Nightly 58) by - // bug 1398167. - shouldUpdateTo26 = true; - aSavedResponseOut->mValue.paddingSize() = 0; - } else if (nullPadding) { -#else if (nullPadding) { -#endif // NIGHTLY_BUILD MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() != ResponseType::Opaque); aSavedResponseOut->mValue.paddingSize() = @@ -2113,20 +2103,6 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId, rv = state->GetBlobAsUTF8String(7, aSavedResponseOut->mValue.channelInfo().securityInfo()); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } -#ifdef NIGHTLY_BUILD - if (shouldUpdateTo26) { - // XXXtt: This is a quick fix for not updating properly in Nightly 57. - // Note: This should be removed in the future (e.g. Nightly 58) by - // bug 1398167. - rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE entries SET response_padding_size = 0 " - "WHERE response_type = 4 " // opaque response - "AND response_padding_size IS NULL" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - } -#endif // NIGHTLY_BUILD - rv = aConn->CreateStatement(NS_LITERAL_CSTRING( "SELECT " "name, " diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index a59913540eb8..5cc371b8d444 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -389,19 +389,6 @@ WebGLContext::ValidateStencilParamsForDrawCall() return true; } -static inline int32_t -FloorPOT(int32_t x) -{ - MOZ_ASSERT(x > 0); - int32_t pot = 1; - while (pot < 0x40000000) { - if (x < pot*2) - break; - pot *= 2; - } - return pot; -} - bool WebGLContext::InitAndValidateGL(FailureReason* const out_failReason) { diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index b6b245d4ef02..7c24772f0abc 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -936,21 +936,6 @@ DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, return errorScope.GetError(); } -static inline GLenum -DoCopyTexImage2D(gl::GLContext* gl, TexImageTarget target, GLint level, - GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height) -{ - const GLint border = 0; - - gl::GLContext::LocalErrorScope errorScope(*gl); - - MOZ_ASSERT(!IsTarget3D(target)); - gl->fCopyTexImage2D(target.get(), level, internalFormat, x, y, width, height, - border); - - return errorScope.GetError(); -} - static inline GLenum DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width, diff --git a/dom/canvas/crashtests/1305850.html b/dom/canvas/crashtests/1305850.html new file mode 100644 index 000000000000..7f79905161f0 --- /dev/null +++ b/dom/canvas/crashtests/1305850.html @@ -0,0 +1,11 @@ + + + + diff --git a/dom/canvas/crashtests/989628.html b/dom/canvas/crashtests/989628.html new file mode 100644 index 000000000000..99735a947a18 --- /dev/null +++ b/dom/canvas/crashtests/989628.html @@ -0,0 +1,9 @@ + diff --git a/dom/canvas/crashtests/crashtests.list b/dom/canvas/crashtests/crashtests.list index ce515bfae155..f77b82e3da23 100644 --- a/dom/canvas/crashtests/crashtests.list +++ b/dom/canvas/crashtests/crashtests.list @@ -19,6 +19,7 @@ load 802926-1.html load 896047-1.html load 916128-1.html load 934939-1.html +load 989628.html load 1099143-1.html load 1161277-1.html load 1183363.html @@ -38,10 +39,11 @@ load 1290628-1.html load 1283113-1.html load 1286458-1.html load 1296410-1.html +load 1298576-1.html load 1299062-1.html load 1305085-1.html load 1305312-1.html -load 1298576-1.html +load 1305850.html load 1334366-1.html load 1334647-1.html load 1357092.html diff --git a/dom/canvas/test/webgl-mochitest/mochitest.ini b/dom/canvas/test/webgl-mochitest/mochitest.ini index ee29a55e7203..cbaa9300b8d1 100644 --- a/dom/canvas/test/webgl-mochitest/mochitest.ini +++ b/dom/canvas/test/webgl-mochitest/mochitest.ini @@ -75,6 +75,7 @@ fail-if = (os == 'win' && os_version == '5.1') skip-if = android_version == '18' #Android 4.3 aws only; bug 1030942 [test_noprog_draw.html] [test_pixel_pack_buffer.html] +skip-if = os == "win" && os_version == "10.0" # Bug 1302199 [test_privileged_exts.html] [test_renderer_strings.html] [test_sab_with_webgl.html] diff --git a/dom/encoding/TextDecoder.cpp b/dom/encoding/TextDecoder.cpp index 70f93d8055da..c7d43988bf01 100644 --- a/dom/encoding/TextDecoder.cpp +++ b/dom/encoding/TextDecoder.cpp @@ -14,8 +14,6 @@ namespace mozilla { namespace dom { -static const char16_t kReplacementChar = static_cast(0xFFFD); - void TextDecoder::Init(const nsAString& aLabel, const bool aFatal, ErrorResult& aRv) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index b341bbfc6dad..d1709303259a 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -4277,7 +4277,8 @@ EventStateManager::GeneratePointerEnterExit(EventMessage aMessage, /* static */ void EventStateManager::UpdateLastRefPointOfMouseEvent(WidgetMouseEvent* aMouseEvent) { - if (aMouseEvent->mMessage != eMouseMove) { + if (aMouseEvent->mMessage != eMouseMove && + aMouseEvent->mMessage != ePointerMove) { return; } @@ -4310,10 +4311,15 @@ EventStateManager::ResetPointerToWindowCenterWhilePointerLocked( WidgetMouseEvent* aMouseEvent) { MOZ_ASSERT(sIsPointerLocked); - if (aMouseEvent->mMessage != eMouseMove || !aMouseEvent->mWidget) { + if ((aMouseEvent->mMessage != eMouseMove && + aMouseEvent->mMessage != ePointerMove) || !aMouseEvent->mWidget) { return; } + // We generate pointermove from mousemove event, so only synthesize native + // mouse move and update sSynthCenteringPoint by mousemove event. + bool updateSynthCenteringPoint = aMouseEvent->mMessage == eMouseMove; + // The pointer is locked. If the pointer is not located at the center of // the window, dispatch a synthetic mousemove to return the pointer there. // Doing this between "real" pointer moves gives the impression that the @@ -4323,7 +4329,7 @@ EventStateManager::ResetPointerToWindowCenterWhilePointerLocked( LayoutDeviceIntPoint center = GetWindowClientRectCenter(aMouseEvent->mWidget); - if (aMouseEvent->mRefPoint != center) { + if (aMouseEvent->mRefPoint != center && updateSynthCenteringPoint) { // Mouse move doesn't finish at the center of the window. Dispatch a // synthetic native mouse event to move the pointer back to the center // of the window, to faciliate more movement. But first, record that @@ -4338,7 +4344,9 @@ EventStateManager::ResetPointerToWindowCenterWhilePointerLocked( aMouseEvent->StopPropagation(); // Clear sSynthCenteringPoint so we don't cancel other events // targeted at the center. - sSynthCenteringPoint = kInvalidRefPoint; + if (updateSynthCenteringPoint) { + sSynthCenteringPoint = kInvalidRefPoint; + } } } diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp index ffdcd22fa4ab..fbd6d24f8f21 100644 --- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -124,7 +124,7 @@ UIEvent::GetMovementPoint() } if (!mEvent || !mEvent->AsGUIEvent()->mWidget || - (mEvent->mMessage != eMouseMove)) { + (mEvent->mMessage != eMouseMove && mEvent->mMessage != ePointerMove)) { // Pointer Lock spec defines that movementX/Y must be zero for all mouse // events except mousemove. return nsIntPoint(0, 0); diff --git a/dom/events/test/pointerevents/pointerlock/mochitest.ini b/dom/events/test/pointerevents/pointerlock/mochitest.ini index cac0bba6fecc..d7794bfc5b22 100644 --- a/dom/events/test/pointerevents/pointerlock/mochitest.ini +++ b/dom/events/test/pointerevents/pointerlock/mochitest.ini @@ -6,6 +6,10 @@ support-files = ../pointerevent_styles.css ../pointerevent_support.js +[test_pointerevent_movementxy-manual.html] + support-files = + pointerevent_movementxy-manual.html + ./resources/pointerevent_movementxy-iframe.html [test_pointerevent_pointerlock_after_pointercapture-manual.html] support-files = pointerevent_pointerlock_after_pointercapture-manual.html disabled = disabled # We don't allow pointer lock in mousemove handlers. diff --git a/dom/events/test/pointerevents/pointerlock/pointerevent_movementxy-manual.html b/dom/events/test/pointerevents/pointerlock/pointerevent_movementxy-manual.html new file mode 100644 index 000000000000..5b0edd3c61da --- /dev/null +++ b/dom/events/test/pointerevents/pointerlock/pointerevent_movementxy-manual.html @@ -0,0 +1,99 @@ + + + + Pointer Events properties tests + + + + + + + + + + +

Pointer Events movementX/Y attribute test

+

+

+ Test Description: This test checks the properties of pointer events that do not support hover. +
    +
  1. Press down on the black square.
  2. +
  3. Move your pointer slowly along a straight line to the red square.
  4. +
  5. Release the pointer when you are over the red square.
  6. +
+ + Test passes if the proper behavior of the events is observed. +

+
+
+
+ +
+
+ + + diff --git a/dom/events/test/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html b/dom/events/test/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html new file mode 100644 index 000000000000..627af3b61cad --- /dev/null +++ b/dom/events/test/pointerevents/pointerlock/resources/pointerevent_movementxy-iframe.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dom/events/test/pointerevents/pointerlock/test_pointerevent_movementxy-manual.html b/dom/events/test/pointerevents/pointerlock/test_pointerevent_movementxy-manual.html new file mode 100644 index 000000000000..270b12c0f1a1 --- /dev/null +++ b/dom/events/test/pointerevents/pointerlock/test_pointerevent_movementxy-manual.html @@ -0,0 +1,53 @@ + + + + + + Test for Bug 1399740 + + + + + + + + + diff --git a/dom/fetch/InternalRequest.cpp b/dom/fetch/InternalRequest.cpp index d1a0d30c3127..9fe13d36e50a 100644 --- a/dom/fetch/InternalRequest.cpp +++ b/dom/fetch/InternalRequest.cpp @@ -147,6 +147,7 @@ InternalRequest::InternalRequest(const InternalRequest& aOther) : mMethod(aOther.mMethod) , mURLList(aOther.mURLList) , mHeaders(new InternalHeaders(*aOther.mHeaders)) + , mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE) , mContentPolicyType(aOther.mContentPolicyType) , mReferrer(aOther.mReferrer) , mReferrerPolicy(aOther.mReferrerPolicy) diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 09e14a5dcb97..9b08aa04b099 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -227,9 +227,6 @@ const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000; {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \ } -#define PROGRESS_STR "progress" -static const uint32_t kProgressEventInterval = 50; // ms - // An helper class for the dispatching of the 'change' event. // This class is used when the FilePicker finished its task (or when files and // directories are set by some chrome/test only method). diff --git a/dom/html/nsTextEditorState.cpp b/dom/html/nsTextEditorState.cpp index cc3055643857..df5b20304d2c 100644 --- a/dom/html/nsTextEditorState.cpp +++ b/dom/html/nsTextEditorState.cpp @@ -53,8 +53,6 @@ using namespace mozilla; using namespace mozilla::dom; using mozilla::layers::ScrollInputMethod; -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); - class MOZ_STACK_CLASS ValueSetter { public: diff --git a/dom/media/CubebUtils.cpp b/dom/media/CubebUtils.cpp index 9200be1928a1..1e9756a1ce03 100644 --- a/dom/media/CubebUtils.cpp +++ b/dom/media/CubebUtils.cpp @@ -189,8 +189,6 @@ cubeb_channel_layout sPreferredChannelLayout; } // namespace -extern LazyLogModule gAudioStreamLog; - static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100; // Consevative default that can work on all platforms. static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024; diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 4936718287b9..4422eabbe6b6 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -104,12 +104,6 @@ static constexpr auto AMPLE_AUDIO_THRESHOLD = TimeUnit::FromMicroseconds(AMPLE_A // which is at or after the current playback position. static const uint32_t LOW_VIDEO_FRAMES = 2; -// Threshold that used to check if we are low on decoded video. -// If the last video frame's end time |mDecodedVideoEndTime| is more than -// |LOW_VIDEO_THRESHOLD*mPlaybackRate| after the current clock in -// Advanceframe(), the video decode is lagging, and we skip to next keyframe. -static constexpr auto LOW_VIDEO_THRESHOLD = TimeUnit::FromMicroseconds(60000); - // Arbitrary "frame duration" when playing only audio. static const int AUDIO_DURATION_USECS = 40000; diff --git a/dom/media/MediaDeviceInfo.cpp b/dom/media/MediaDeviceInfo.cpp index 327d8202918e..4b5a0270902b 100644 --- a/dom/media/MediaDeviceInfo.cpp +++ b/dom/media/MediaDeviceInfo.cpp @@ -60,8 +60,6 @@ void MediaDeviceInfo::GetLabel(nsString& retval) } MediaDeviceKind Kind(); -void GetLabel(nsString& retval); -void GetGroupId(nsString& retval); } // namespace dom } // namespace mozilla diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index b4864ab70d39..4350301b26e6 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -1435,11 +1435,6 @@ MediaRecorder::SetOptions(const MediaRecorderOptions& aInitDict) } } -static char const *const gWebMAudioEncoderCodecs[2] = { - "opus", - // no VP9 yet - nullptr, -}; static char const *const gWebMVideoEncoderCodecs[4] = { "opus", "vp8", diff --git a/dom/media/StreamTracks.cpp b/dom/media/StreamTracks.cpp index d44dc7a965b2..32194781d665 100644 --- a/dom/media/StreamTracks.cpp +++ b/dom/media/StreamTracks.cpp @@ -9,10 +9,11 @@ namespace mozilla { +#ifdef DEBUG + extern LazyLogModule gMediaStreamGraphLog; #define STREAM_LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg) -#ifdef DEBUG void StreamTracks::DumpTrackInfo() const { diff --git a/dom/media/encoder/TrackEncoder.cpp b/dom/media/encoder/TrackEncoder.cpp index e281901696c2..ca4d3fb040a2 100644 --- a/dom/media/encoder/TrackEncoder.cpp +++ b/dom/media/encoder/TrackEncoder.cpp @@ -23,7 +23,6 @@ static const int DEFAULT_CHANNELS = 1; static const int DEFAULT_SAMPLING_RATE = 16000; static const int DEFAULT_FRAME_WIDTH = 640; static const int DEFAULT_FRAME_HEIGHT = 480; -static const int DEFAULT_TRACK_RATE = USECS_PER_S; // 1 second threshold if the audio encoder cannot be initialized. static const int AUDIO_INIT_FAILED_DURATION = 1; // 30 second threshold if the video encoder cannot be initialized. diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index 7a391ec83f4b..1990f2927a1c 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -69,18 +69,18 @@ skip-if = toolkit == 'android' # Not supported on android [test_DurationUpdated_mp4.html] skip-if = toolkit == 'android' # Not supported on android [test_EndedEvent.html] -skip-if = android_version == '22' # bug 1358640 +skip-if = android_version == '22' || toolkit == 'android' # bug 1358640, bug 1401090 [test_EndOfStream.html] [test_EndOfStream_mp4.html] skip-if = toolkit == 'android' # Not supported on android [test_Eviction_mp4.html] skip-if = android_version == '15' # Not supported on Android(Bug 1358271) [test_FrameSelection.html] -skip-if = android_version == '22' # bug 1341519 +skip-if = android_version == '22' || toolkit == 'android' # bug 1341519, bug 1401090 [test_FrameSelection_mp4.html] skip-if = toolkit == 'android' # Not supported on android [test_HaveMetadataUnbufferedSeek.html] -skip-if = android_version == '22' # bug 1342247 +skip-if = android_version == '22' || toolkit == 'android' # bug 1342247, bug 1401090 [test_HaveMetadataUnbufferedSeek_mp4.html] skip-if = toolkit == 'android' # Not supported on android [test_LiveSeekable.html] diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index b983b7a506e6..f3392c4e5112 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -49,7 +49,6 @@ namespace mozilla { -extern already_AddRefed CreateAgnosticDecoderModule(); extern already_AddRefed CreateBlankDecoderModule(); extern already_AddRefed CreateNullDecoderModule(); diff --git a/dom/media/test/crashtests/1180881.html b/dom/media/test/crashtests/1180881.html new file mode 100644 index 000000000000..d5bf2f564258 --- /dev/null +++ b/dom/media/test/crashtests/1180881.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dom/media/test/crashtests/1180881.webm b/dom/media/test/crashtests/1180881.webm new file mode 100644 index 000000000000..2fb2be7a7f7d Binary files /dev/null and b/dom/media/test/crashtests/1180881.webm differ diff --git a/dom/media/test/crashtests/1197935.html b/dom/media/test/crashtests/1197935.html new file mode 100644 index 000000000000..dd8ad0382d15 --- /dev/null +++ b/dom/media/test/crashtests/1197935.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dom/media/test/crashtests/1197935.mp4 b/dom/media/test/crashtests/1197935.mp4 new file mode 100644 index 000000000000..f00de7562785 Binary files /dev/null and b/dom/media/test/crashtests/1197935.mp4 differ diff --git a/dom/media/test/crashtests/1270303.html b/dom/media/test/crashtests/1270303.html new file mode 100644 index 000000000000..23608bb2b8a8 --- /dev/null +++ b/dom/media/test/crashtests/1270303.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dom/media/test/crashtests/1270303.webm b/dom/media/test/crashtests/1270303.webm new file mode 100644 index 000000000000..c9b0003d6b64 Binary files /dev/null and b/dom/media/test/crashtests/1270303.webm differ diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index b93bc63e0e6b..446b529814e0 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -75,12 +75,15 @@ skip-if(Android) test-pref(media.navigator.permission.disabled,true) load 102845 load 1041466.html load 1045650.html load 1080986.html +load 1180881.html +load 1197935.html load 1122218.html load 1127188.html load 1157994.html load 1158427.html load 1185176.html load 1185192.html +load 1270303.html load 1304948.html load 1319486.html load 1368490.html diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp index d0d87bf74892..70eebfc0a2fa 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.cpp +++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp @@ -43,8 +43,6 @@ using namespace mozilla; using namespace mozilla::plugins::parent; using namespace mozilla::layers; -static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID); - NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback) nsNPAPIPluginInstance::nsNPAPIPluginInstance() diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 481c927fa3ea..c0d68c959182 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -615,17 +615,6 @@ PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect) return IPC_OK(); } -static inline NPRect -IntRectToNPRect(const gfx::IntRect& rect) -{ - NPRect r; - r.left = rect.x; - r.top = rect.y; - r.right = rect.x + rect.width; - r.bottom = rect.y + rect.height; - return r; -} - mozilla::ipc::IPCResult PluginInstanceParent::RecvRevokeCurrentDirectSurface() { diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 38beaa13030e..f6517a3101ae 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -3838,6 +3838,13 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType, return nullptr; } +#if defined(NIGHTLY_BUILD) + { + MutexAutoLock autoLock(mQuotaMutex); + MOZ_DIAGNOSTIC_ASSERT(mTemporaryStorageInitialized); + } +#endif + nsString path; nsresult rv = aFile->GetPath(path); NS_ENSURE_SUCCESS(rv, nullptr); @@ -3923,6 +3930,13 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType, { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); +#if defined(NIGHTLY_BUILD) + if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT){ + MutexAutoLock autoLock(mQuotaMutex); + MOZ_DIAGNOSTIC_ASSERT(mTemporaryStorageInitialized); + } +#endif + if (aFileSizeOut) { *aFileSizeOut = 0; } @@ -5242,7 +5256,14 @@ QuotaManager::EnsureOriginIsInitializedInternal( NS_ENSURE_SUCCESS(rv, rv); } +#if defined(NIGHTLY_BUILD) + { + MutexAutoLock autoLock(mQuotaMutex); mTemporaryStorageInitialized = true; + } +#else + mTemporaryStorageInitialized = true; +#endif CheckTemporaryStorageLimits(); } @@ -5328,7 +5349,16 @@ QuotaManager::ResetOrClearCompleted() AssertIsOnIOThread(); mInitializedOrigins.Clear(); - mTemporaryStorageInitialized = false; + +#if defined(NIGHTLY_BUILD) + { + MutexAutoLock autoLock(mQuotaMutex); + mTemporaryStorageInitialized = false; + } +#else + mTemporaryStorageInitialized = false; +#endif + mStorageInitialized = false; ReleaseIOThreadObjects(); diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 04fcae847878..4952a181df03 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -224,14 +224,6 @@ nsCSPContext::permitsInternal(CSPDirective aDir, nsAutoString violatedDirective; for (uint32_t p = 0; p < mPolicies.Length(); p++) { - - // According to the W3C CSP spec, frame-ancestors checks are ignored for - // report-only policies (when "monitoring"). - if (aDir == nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE && - mPolicies[p]->getReportOnlyFlag()) { - continue; - } - if (!mPolicies[p]->permits(aDir, aContentLocation, aNonce, diff --git a/dom/security/test/csp/file_frame_ancestors_ro.html b/dom/security/test/csp/file_frame_ancestors_ro.html new file mode 100644 index 000000000000..ff5ae9cf9f83 --- /dev/null +++ b/dom/security/test/csp/file_frame_ancestors_ro.html @@ -0,0 +1 @@ +Child Document diff --git a/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ b/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ new file mode 100644 index 000000000000..d018af3a96c5 --- /dev/null +++ b/dom/security/test/csp/file_frame_ancestors_ro.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: frame-ancestors 'none'; report-uri http://mochi.test:8888/foo.sjs diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index 2f33dd33c0bf..4ced8d2bd2ac 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -90,6 +90,8 @@ support-files = file_bug941404.html file_bug941404_xhr.html file_bug941404_xhr.html^headers^ + file_frame_ancestors_ro.html + file_frame_ancestors_ro.html^headers^ file_hash_source.html file_dual_header_testserver.sjs file_hash_source.html^headers^ @@ -246,6 +248,7 @@ skip-if = toolkit == 'android' # Times out, not sure why (bug 1008445) [test_bug910139.html] [test_bug909029.html] [test_bug1229639.html] +[test_frame_ancestors_ro.html] [test_policyuri_regression_from_multipolicy.html] [test_nonce_source.html] [test_bug941404.html] diff --git a/dom/security/test/csp/test_frame_ancestors_ro.html b/dom/security/test/csp/test_frame_ancestors_ro.html new file mode 100644 index 000000000000..90f68e25ebd2 --- /dev/null +++ b/dom/security/test/csp/test_frame_ancestors_ro.html @@ -0,0 +1,69 @@ + + + + Test for frame-ancestors support in Content-Security-Policy-Report-Only + + + + + + + + diff --git a/dom/storage/LocalStorage.cpp b/dom/storage/LocalStorage.cpp index 7e581eeec669..762f62c37ae6 100644 --- a/dom/storage/LocalStorage.cpp +++ b/dom/storage/LocalStorage.cpp @@ -244,9 +244,6 @@ LocalStorage::ApplyEvent(StorageEvent* aStorageEvent) mCache->SetItem(this, key, value, old, LocalStorageCache::E10sPropagated); } -static const char kPermissionType[] = "cookie"; -static const char kStorageEnabled[] = "dom.storage.enabled"; - bool LocalStorage::PrincipalEquals(nsIPrincipal* aPrincipal) { diff --git a/dom/storage/Storage.cpp b/dom/storage/Storage.cpp index c051d7a3ff54..06b7baa531ce 100644 --- a/dom/storage/Storage.cpp +++ b/dom/storage/Storage.cpp @@ -14,6 +14,8 @@ namespace mozilla { namespace dom { +static const char kStorageEnabled[] = "dom.storage.enabled"; + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mWindow, mPrincipal) NS_IMPL_CYCLE_COLLECTING_ADDREF(Storage) diff --git a/dom/xslt/xpath/XPathEvaluator.cpp b/dom/xslt/xpath/XPathEvaluator.cpp index 7216b8b3107f..628ba461278a 100644 --- a/dom/xslt/xpath/XPathEvaluator.cpp +++ b/dom/xslt/xpath/XPathEvaluator.cpp @@ -24,11 +24,6 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/XPathNSResolverBinding.h" -extern nsresult -TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID, - nsAtom *aName, nsISupports *aState, - FunctionCall **aFunction); - namespace mozilla { namespace dom { diff --git a/editor/libeditor/DeleteRangeTransaction.cpp b/editor/libeditor/DeleteRangeTransaction.cpp index 77ad5efcf0a1..604fe0aeef09 100644 --- a/editor/libeditor/DeleteRangeTransaction.cpp +++ b/editor/libeditor/DeleteRangeTransaction.cpp @@ -64,8 +64,10 @@ DeleteRangeTransaction::DoTransaction() if (startContainer == endContainer) { // the selection begins and ends in the same node + nsIContent* startChild = rangeToDelete->GetChildAtStartOffset(); nsresult rv = - CreateTxnsToDeleteBetween(startContainer, startOffset, endOffset); + CreateTxnsToDeleteBetween(startContainer, startOffset, + startChild, endOffset); NS_ENSURE_SUCCESS(rv, rv); } else { // the selection ends in a different node from where it started. delete @@ -122,6 +124,7 @@ DeleteRangeTransaction::GetTxnDescription(nsAString& aString) nsresult DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, int32_t aStartOffset, + nsIContent* aChildAtStartOffset, int32_t aEndOffset) { if (NS_WARN_IF(!mEditorBase)) { @@ -153,7 +156,7 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, return NS_OK; } - nsCOMPtr child = aNode->GetChildAt(aStartOffset); + nsIContent* child = aChildAtStartOffset; for (int32_t i = aStartOffset; i < aEndOffset; ++i) { // Even if we detect invalid range, we should ignore it for removing // specified range's nodes as far as possible. diff --git a/editor/libeditor/DeleteRangeTransaction.h b/editor/libeditor/DeleteRangeTransaction.h index 19d6479aba59..de6bb410caaf 100644 --- a/editor/libeditor/DeleteRangeTransaction.h +++ b/editor/libeditor/DeleteRangeTransaction.h @@ -52,6 +52,7 @@ public: protected: nsresult CreateTxnsToDeleteBetween(nsINode* aNode, int32_t aStartOffset, + nsIContent* aChildAtStartOffset, int32_t aEndOffset); nsresult CreateTxnsToDeleteNodesBetween(nsRange* aRangeToDelete); diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 7f5b8b6c964d..1c2bffab0c29 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -2391,13 +2391,14 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset) { nsCOMPtr node = do_QueryInterface(aNode); - FindBetterInsertionPoint(node, aOffset); + FindBetterInsertionPoint(node, aOffset, nullptr); aNode = do_QueryInterface(node); } void EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset) + int32_t& aOffset, + nsCOMPtr* aSelChild) { if (aNode->IsNodeOfType(nsINode::eTEXT)) { // There is no "better" insertion point. @@ -2424,6 +2425,9 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { aNode = node->GetFirstChild(); aOffset = 0; + if (aSelChild) { + *aSelChild = nullptr; + } return; } @@ -2439,6 +2443,9 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); aNode = child; aOffset = static_cast(aNode->Length()); + if (aSelChild) { + *aSelChild = nullptr; + } return; } } else { @@ -2450,6 +2457,9 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); aNode = child; aOffset = static_cast(aNode->Length()); + if (aSelChild) { + *aSelChild = nullptr; + } return; } child = child->GetPreviousSibling(); @@ -2467,10 +2477,16 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, NS_ENSURE_TRUE_VOID(node->Length() <= INT32_MAX); aNode = node->GetPreviousSibling(); aOffset = static_cast(aNode->Length()); + if (aSelChild) { + *aSelChild = nullptr; + } return; } if (node->GetParentNode() && node->GetParentNode() == root) { + if (aSelChild) { + *aSelChild = node->AsContent(); + } aNode = node->GetParentNode(); aOffset = 0; return; @@ -2481,6 +2497,7 @@ EditorBase::FindBetterInsertionPoint(nsCOMPtr& aNode, nsresult EditorBase::InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc) { @@ -2502,15 +2519,17 @@ EditorBase::InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr node = *aInOutNode; int32_t offset = *aInOutOffset; + nsCOMPtr child = *aInOutChildAtOffset; + + MOZ_ASSERT(node->GetChildAt(offset) == *aInOutChildAtOffset); // In some cases, the node may be the anonymous div elemnt or a mozBR // element. Let's try to look for better insertion point in the nearest // text node if there is. - FindBetterInsertionPoint(node, offset); + FindBetterInsertionPoint(node, offset, address_of(child)); // If a neighboring text node already exists, use that if (!node->IsNodeOfType(nsINode::eTEXT)) { - nsIContent* child = node->GetChildAt(offset); if (offset && child && child->GetPreviousSibling() && child->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) { node = child->GetPreviousSibling(); @@ -2566,6 +2585,7 @@ EditorBase::InsertTextImpl(const nsAString& aStringToInsert, *aInOutNode = node; *aInOutOffset = offset; + *aInOutChildAtOffset = nullptr; return NS_OK; } @@ -3278,6 +3298,7 @@ EditorBase::GetLengthOfDOMNode(nsIDOMNode* aNode, nsIContent* EditorBase::GetPriorNode(nsINode* aParentNode, int32_t aOffset, + nsINode* aChildAtOffset, bool aEditableNode, bool aNoBlockCrossing) { @@ -3294,8 +3315,8 @@ EditorBase::GetPriorNode(nsINode* aParentNode, } // else look before the child at 'aOffset' - if (nsIContent* child = aParentNode->GetChildAt(aOffset)) { - return GetPriorNode(child, aEditableNode, aNoBlockCrossing); + if (aChildAtOffset) { + return GetPriorNode(aChildAtOffset, aEditableNode, aNoBlockCrossing); } // unless there isn't one, in which case we are at the end of the node @@ -3312,6 +3333,7 @@ EditorBase::GetPriorNode(nsINode* aParentNode, nsIContent* EditorBase::GetNextNode(nsINode* aParentNode, int32_t aOffset, + nsINode* aChildAtOffset, bool aEditableNode, bool aNoBlockCrossing) { @@ -3326,15 +3348,16 @@ EditorBase::GetNextNode(nsINode* aParentNode, } // look at the child at 'aOffset' - nsIContent* child = aParentNode->GetChildAt(aOffset); - if (child) { - if (aNoBlockCrossing && IsBlockNode(child)) { - return child; + if (aChildAtOffset) { + if (aNoBlockCrossing && IsBlockNode(aChildAtOffset)) { + MOZ_ASSERT(aChildAtOffset->IsContent()); + return aChildAtOffset->AsContent(); } - nsIContent* resultNode = GetLeftmostChild(child, aNoBlockCrossing); + nsIContent* resultNode = GetLeftmostChild(aChildAtOffset, aNoBlockCrossing); if (!resultNode) { - return child; + MOZ_ASSERT(aChildAtOffset->IsContent()); + return aChildAtOffset->AsContent(); } if (!IsDescendantOfEditorRoot(resultNode)) { @@ -4078,8 +4101,12 @@ EditorBase::JoinNodeDeep(nsIContent& aLeftNode, // Get new left and right nodes, and begin anew parentNode = rightNodeToJoin; - leftNodeToJoin = parentNode->GetChildAt(length - 1); rightNodeToJoin = parentNode->GetChildAt(length); + if (rightNodeToJoin) { + leftNodeToJoin = rightNodeToJoin->GetPreviousSibling(); + } else { + leftNodeToJoin = nullptr; + } // Skip over non-editable nodes while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) { @@ -4540,6 +4567,7 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete, return nullptr; } + nsIContent* child = aRangeToDelete->GetChildAtStartOffset(); int32_t offset = aRangeToDelete->StartOffset(); // determine if the insertion point is at the beginning, middle, or end of @@ -4652,9 +4680,9 @@ EditorBase::CreateTxnForDeleteRange(nsRange* aRangeToDelete, // node to find out nsCOMPtr selectedNode; if (aAction == ePrevious) { - selectedNode = GetPriorNode(node, offset, true); + selectedNode = GetPriorNode(node, offset, child, true); } else if (aAction == eNext) { - selectedNode = GetNextNode(node, offset, true); + selectedNode = GetNextNode(node, offset, child, true); } while (selectedNode && @@ -4958,7 +4986,7 @@ EditorBase::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget) NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE); nsCOMPtr startNode = firstRange->GetStartContainer(); int32_t startOffset = firstRange->StartOffset(); - FindBetterInsertionPoint(startNode, startOffset); + FindBetterInsertionPoint(startNode, startOffset, nullptr); Text* textNode = startNode->GetAsText(); MOZ_ASSERT(textNode, "There must be text node if mIMETextLength is larger than 0"); diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 37fd5e254697..5270cd4f28ab 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -287,6 +287,7 @@ public: virtual nsresult InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc); nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, @@ -708,6 +709,7 @@ public: */ nsIContent* GetPriorNode(nsINode* aParentNode, int32_t aOffset, + nsINode* aChildAtOffset, bool aEditableNode, bool aNoBlockCrossing = false); @@ -730,6 +732,7 @@ public: */ nsIContent* GetNextNode(nsINode* aParentNode, int32_t aOffset, + nsINode* aChildAtOffset, bool aEditableNode, bool aNoBlockCrossing = false); @@ -1220,11 +1223,23 @@ public: /** * FindBetterInsertionPoint() tries to look for better insertion point which * is typically the nearest text node and offset in it. + * + * @param aNode in/out param, on input set to the node to use to start the search, + * on output set to the node found as the better insertion point. + * @param aOffset in/out param, on input set to the offset to use to start the + * search, on putput set to the offset found as the better insertion + * point. + * @param aSelChild in/out param, on input, can be set to nullptr if the caller + * doesn't want to pass this in, or set to a pointer to an nsCOMPtr + * pointing to the child at the input node and offset, and on output + * the method will make it point to the child at the output node and + * offset returned in aNode and aOffset. */ void FindBetterInsertionPoint(nsCOMPtr& aNode, int32_t& aOffset); void FindBetterInsertionPoint(nsCOMPtr& aNode, - int32_t& aOffset); + int32_t& aOffset, + nsCOMPtr* aSelChild); /** * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp index c268731b7812..939de24bce0b 100644 --- a/editor/libeditor/HTMLEditRules.cpp +++ b/editor/libeditor/HTMLEditRules.cpp @@ -854,6 +854,7 @@ HTMLEditRules::GetAlignment(bool* aMixed, selection->GetRangeAt(0)->GetStartContainer()); OwningNonNull parent = *selection->GetRangeAt(0)->GetStartContainer(); + nsIContent* child = selection->GetRangeAt(0)->GetChildAtStartOffset(); int32_t offset = selection->GetRangeAt(0)->StartOffset(); // Is the selection collapsed? @@ -865,7 +866,7 @@ HTMLEditRules::GetAlignment(bool* aMixed, nodeToExamine = parent; } else if (parent->IsHTMLElement(nsGkAtoms::html) && offset == rootOffset) { // If we have selected the body, let's look at the first editable node - nodeToExamine = htmlEditor->GetNextNode(parent, offset, true); + nodeToExamine = htmlEditor->GetNextNode(parent, offset, child, true); } else { nsTArray> arrayOfRanges; GetPromotedRanges(selection, arrayOfRanges, EditAction::align); @@ -1219,11 +1220,13 @@ HTMLEditRules::WillInsert(Selection& aSelection, aSelection.GetRangeAt(0)->GetStartContainer()); OwningNonNull selNode = *aSelection.GetRangeAt(0)->GetStartContainer(); + nsIContent* selChild = aSelection.GetRangeAt(0)->GetChildAtStartOffset(); int32_t selOffset = aSelection.GetRangeAt(0)->StartOffset(); // Get prior node nsCOMPtr priorNode = htmlEditor->GetPriorHTMLNode(selNode, - selOffset); + selOffset, + selChild); if (priorNode && TextEditUtils::IsMozBR(priorNode)) { nsCOMPtr block1 = htmlEditor->GetBlock(selNode); nsCOMPtr block2 = htmlEditor->GetBlockNodeParent(priorNode); @@ -1296,6 +1299,8 @@ HTMLEditRules::WillInsertText(EditAction aAction, NS_ENSURE_STATE(mHTMLEditor); NS_ENSURE_STATE(aSelection->GetRangeAt(0)); nsCOMPtr selNode = aSelection->GetRangeAt(0)->GetStartContainer(); + nsCOMPtr selChild = + aSelection->GetRangeAt(0)->GetChildAtStartOffset(); int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset(); NS_ENSURE_STATE(selNode); @@ -1320,13 +1325,15 @@ HTMLEditRules::WillInsertText(EditAction aAction, } if (inString->IsEmpty()) { rv = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), + address_of(selChild), &selOffset, doc); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { WSRunObject wsObj(mHTMLEditor, selNode, selOffset); - rv = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc); + rv = wsObj.InsertText(*inString, address_of(selNode), + address_of(selChild), &selOffset, doc); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1398,6 +1405,7 @@ HTMLEditRules::WillInsertText(EditAction aAction, } else { NS_ENSURE_STATE(mHTMLEditor); rv = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), + address_of(selChild), &curOffset, doc); NS_ENSURE_SUCCESS(rv, rv); } @@ -1430,7 +1438,8 @@ HTMLEditRules::WillInsertText(EditAction aAction, // is it a tab? if (subStr.Equals(tabStr)) { rv = - wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc); + wsObj.InsertText(spacesStr, address_of(curNode), + address_of(selChild), &curOffset, doc); NS_ENSURE_SUCCESS(rv, rv); pos++; } @@ -1441,8 +1450,10 @@ HTMLEditRules::WillInsertText(EditAction aAction, nsIEditor::eNone); NS_ENSURE_TRUE(br, NS_ERROR_FAILURE); pos++; + selChild = br->GetNextSibling();; } else { - rv = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc); + rv = wsObj.InsertText(subStr, address_of(curNode), + address_of(selChild), &curOffset, doc); NS_ENSURE_SUCCESS(rv, rv); } } @@ -1533,6 +1544,7 @@ HTMLEditRules::WillInsertBreak(Selection& aSelection, aSelection.GetRangeAt(0)->GetStartContainer(), NS_ERROR_FAILURE); OwningNonNull node = *aSelection.GetRangeAt(0)->GetStartContainer(); + nsIContent* child = aSelection.GetRangeAt(0)->GetChildAtStartOffset(); int32_t offset = aSelection.GetRangeAt(0)->StartOffset(); // Do nothing if the node is read-only @@ -1585,6 +1597,7 @@ HTMLEditRules::WillInsertBreak(Selection& aSelection, return NS_ERROR_FAILURE; } node = *aSelection.GetRangeAt(0)->GetStartContainer(); + child = aSelection.GetRangeAt(0)->GetChildAtStartOffset(); offset = aSelection.GetRangeAt(0)->StartOffset(); blockParent = mHTMLEditor->GetBlock(node); @@ -1638,8 +1651,8 @@ HTMLEditRules::WillInsertBreak(Selection& aSelection, blockParent->IsAnyOfHTMLElements(nsGkAtoms::p, nsGkAtoms::div))) { // Paragraphs: special rules to look for
s nsresult rv = - ReturnInParagraph(&aSelection, GetAsDOMNode(blockParent), - GetAsDOMNode(node), offset, aCancel, aHandled); + ReturnInParagraph(&aSelection, blockParent, node, + offset, child, aCancel, aHandled); NS_ENSURE_SUCCESS(rv, rv); // Fall through, we may not have handled it in ReturnInParagraph() } @@ -3596,6 +3609,7 @@ HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsAtom& blockType) aSelection.GetRangeAt(0)->GetStartContainer()); OwningNonNull container = *aSelection.GetRangeAt(0)->GetStartContainer(); + nsIContent* child = aSelection.GetRangeAt(0)->GetChildAtStartOffset(); int32_t offset = aSelection.GetRangeAt(0)->StartOffset(); if (&blockType == nsGkAtoms::normal || @@ -3608,7 +3622,7 @@ HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsAtom& blockType) // Otherwise it gets pushed into a following block after the split, // which is visually bad. nsCOMPtr brNode = - htmlEditor->GetNextHTMLNode(container, offset); + htmlEditor->GetNextHTMLNode(container, offset, child); if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) { rv = htmlEditor->DeleteNode(brNode); NS_ENSURE_SUCCESS(rv, rv); @@ -3631,7 +3645,7 @@ HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsAtom& blockType) } else { // We are making a block. Consume a br, if needed. nsCOMPtr brNode = - htmlEditor->GetNextHTMLNode(container, offset, true); + htmlEditor->GetNextHTMLNode(container, offset, child, true); if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) { rv = htmlEditor->DeleteNode(brNode); NS_ENSURE_SUCCESS(rv, rv); @@ -4738,7 +4752,7 @@ HTMLEditRules::WillAlign(Selection& aSelection, // Consume a trailing br, if any. This is to keep an alignment from // creating extra lines, if possible. nsCOMPtr brContent = - htmlEditor->GetNextHTMLNode(parent, offset); + htmlEditor->GetNextHTMLNode(parent, offset, child); if (brContent && TextEditUtils::IsBreak(brContent)) { // Making use of html structure... if next node after where we are // putting our div is not a block, then the br we found is in same block @@ -5042,8 +5056,9 @@ HTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode, if (aAction == nsIEditor::eNext || aAction == nsIEditor::eNextWord || aAction == nsIEditor::eToEndOfLine) { // Move to the start of the next node, if any - nsCOMPtr nextNode = htmlEditor->GetNextNode(blockParent, - offset + 1, true); + nsINode* child = emptyBlock->GetNextSibling(); + nsCOMPtr nextNode = + htmlEditor->GetNextNode(blockParent, offset + 1, child, true); if (nextNode) { EditorDOMPoint pt = GetGoodSelPointForNode(*nextNode, aAction); nsresult rv = aSelection->Collapse(pt.node, pt.offset); @@ -5059,6 +5074,7 @@ HTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode, // Move to the end of the previous node nsCOMPtr priorNode = htmlEditor->GetPriorNode(blockParent, offset, + emptyBlock, true); if (priorNode) { EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction); @@ -5318,6 +5334,8 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) if (NS_WARN_IF(!endNode)) { return NS_ERROR_FAILURE; } + nsIContent* startChild = range->GetChildAtStartOffset(); + nsIContent* endChild = range->GetChildAtEndOffset(); uint32_t startOffset = range->StartOffset(); uint32_t endOffset = range->EndOffset(); @@ -5355,7 +5373,8 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) } else if (wsEndObj.mStartReason == WSType::thisBlock) { // endpoint is just after start of this block nsINode* child = - htmlEditor->GetPriorHTMLNode(endNode, static_cast(endOffset)); + htmlEditor->GetPriorHTMLNode(endNode, static_cast(endOffset), + endChild); if (child) { int32_t offset = -1; newEndNode = EditorBase::GetNodeLocation(child, &offset); @@ -5397,7 +5416,8 @@ HTMLEditRules::NormalizeSelection(Selection* inSelection) // startpoint is just before end of this block nsINode* child = htmlEditor->GetNextHTMLNode(startNode, - static_cast(startOffset)); + static_cast(startOffset), + startChild); if (child) { int32_t offset = -1; newStartNode = EditorBase::GetNodeLocation(child, &offset); @@ -5493,6 +5513,7 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, } nsCOMPtr node = &aNode; + nsINode* child = node->GetChildAt(aOffset); int32_t offset = aOffset; // else not a text section. In this case we want to see if we should grab @@ -5505,27 +5526,29 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, return EditorDOMPoint(node, offset); } offset = node->GetParentNode()->IndexOf(node); + child = node; node = node->GetParentNode(); } // look back through any further inline nodes that aren't across a
// from us, and that are enclosed in the same block. nsCOMPtr priorNode = - htmlEditor->GetPriorHTMLNode(node, offset, true); + htmlEditor->GetPriorHTMLNode(node, offset, child, true); while (priorNode && priorNode->GetParentNode() && !htmlEditor->IsVisibleBRElement(priorNode) && !IsBlockNode(*priorNode)) { offset = priorNode->GetParentNode()->IndexOf(priorNode); + child = node; node = priorNode->GetParentNode(); - priorNode = htmlEditor->GetPriorHTMLNode(node, offset, true); + priorNode = htmlEditor->GetPriorHTMLNode(node, offset, child, true); } // finding the real start for this point. look up the tree for as long as // we are the first node in the container, and as long as we haven't hit // the body node. nsCOMPtr nearNode = - htmlEditor->GetPriorHTMLNode(node, offset, true); + htmlEditor->GetPriorHTMLNode(node, offset, child, true); while (!nearNode && !node->IsHTMLElement(nsGkAtoms::body) && node->GetParentNode()) { // some cutoffs are here: we don't need to also include them in the @@ -5554,9 +5577,10 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, break; } + child = node; node = parent; offset = parentOffset; - nearNode = htmlEditor->GetPriorHTMLNode(node, offset, true); + nearNode = htmlEditor->GetPriorHTMLNode(node, offset, child, true); } return EditorDOMPoint(node, offset); } @@ -5570,16 +5594,18 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, } // want to be after the text node offset = 1 + node->GetParentNode()->IndexOf(node); + child = node; node = node->GetParentNode(); } // look ahead through any further inline nodes that aren't across a
from // us, and that are enclosed in the same block. nsCOMPtr nextNode = - htmlEditor->GetNextHTMLNode(node, offset, true); + htmlEditor->GetNextHTMLNode(node, offset, child, true); while (nextNode && !IsBlockNode(*nextNode) && nextNode->GetParentNode()) { offset = 1 + nextNode->GetParentNode()->IndexOf(nextNode); + child = nextNode->GetNextSibling(); node = nextNode->GetParentNode(); if (htmlEditor->IsVisibleBRElement(nextNode)) { break; @@ -5602,14 +5628,14 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, } } } - nextNode = htmlEditor->GetNextHTMLNode(node, offset, true); + nextNode = htmlEditor->GetNextHTMLNode(node, offset, child, true); } // finding the real end for this point. look up the tree for as long as we // are the last node in the container, and as long as we haven't hit the body // node. nsCOMPtr nearNode = - htmlEditor->GetNextHTMLNode(node, offset, true); + htmlEditor->GetNextHTMLNode(node, offset, child, true); while (!nearNode && !node->IsHTMLElement(nsGkAtoms::body) && node->GetParentNode()) { int32_t parentOffset = node->GetParentNode()->IndexOf(node); @@ -5623,10 +5649,11 @@ HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, break; } + child = node->GetNextSibling(); node = parent; // we want to be AFTER nearNode offset = parentOffset + 1; - nearNode = htmlEditor->GetNextHTMLNode(node, offset, true); + nearNode = htmlEditor->GetNextHTMLNode(node, offset, child, true); } return EditorDOMPoint(node, offset); } @@ -6440,9 +6467,10 @@ HTMLEditRules::ReturnInHeader(Selection& aSelection, */ nsresult HTMLEditRules::ReturnInParagraph(Selection* aSelection, - nsIDOMNode* aPara, - nsIDOMNode* aNode, + nsINode* aPara, + nsINode* aNode, int32_t aOffset, + nsIContent* aChildAtOffset, bool* aCancel, bool* aHandled) { @@ -6462,7 +6490,7 @@ HTMLEditRules::ReturnInParagraph(Selection* aSelection, bool newBRneeded = false; bool newSelNode = false; nsCOMPtr sibling; - nsCOMPtr selNode = aNode; + nsCOMPtr selNode = GetAsDOMNode(aNode); int32_t selOffset = aOffset; NS_ENSURE_STATE(mHTMLEditor); @@ -6500,7 +6528,7 @@ HTMLEditRules::ReturnInParagraph(Selection* aSelection, return NS_ERROR_UNEXPECTED; } nsresult rv = - mHTMLEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp)); + mHTMLEditor->SplitNode(selNode, aOffset, getter_AddRefs(tmp)); NS_ENSURE_SUCCESS(rv, rv); selNode = tmp; } @@ -6513,13 +6541,13 @@ HTMLEditRules::ReturnInParagraph(Selection* aSelection, // is there a BR prior to it? nsCOMPtr nearNode; NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetPriorHTMLNode(node, aOffset); + nearNode = mHTMLEditor->GetPriorHTMLNode(node, aOffset, aChildAtOffset); NS_ENSURE_STATE(mHTMLEditor); if (!nearNode || !mHTMLEditor->IsVisibleBRElement(nearNode) || TextEditUtils::HasMozAttr(GetAsDOMNode(nearNode))) { // is there a BR after it? NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetNextHTMLNode(node, aOffset); + nearNode = mHTMLEditor->GetNextHTMLNode(node, aOffset, aChildAtOffset); NS_ENSURE_STATE(mHTMLEditor); if (!nearNode || !mHTMLEditor->IsVisibleBRElement(nearNode) || TextEditUtils::HasMozAttr(GetAsDOMNode(nearNode))) { @@ -6546,7 +6574,8 @@ HTMLEditRules::ReturnInParagraph(Selection* aSelection, } } *aHandled = true; - return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &selOffset); + return SplitParagraph(GetAsDOMNode(aPara), sibling, aSelection, + address_of(selNode), &selOffset); } /** @@ -7489,7 +7518,7 @@ HTMLEditRules::CheckInterlinePosition(Selection& aSelection) // special-case first so that we don't accidentally fall through into one of // the other conditionals. nsCOMPtr node = - htmlEditor->GetPriorHTMLNode(selNode, selOffset, true); + htmlEditor->GetPriorHTMLNode(selNode, selOffset, child, true); if (node && node->IsHTMLElement(nsGkAtoms::br)) { aSelection.SetInterlinePosition(true); return; @@ -7537,12 +7566,14 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); NS_ENSURE_SUCCESS(rv, rv); + nsINode* child = aSelection->GetRangeAt(0)->GetChildAtStartOffset(); temp = selNode; // are we in an editable node? NS_ENSURE_STATE(mHTMLEditor); while (!mHTMLEditor->IsEditable(selNode)) { // scan up the tree until we find an editable place to be + child = temp; selNode = EditorBase::GetNodeLocation(temp, &selOffset); NS_ENSURE_TRUE(selNode, NS_ERROR_FAILURE); temp = selNode; @@ -7589,7 +7620,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, NS_ENSURE_STATE(mHTMLEditor); nsCOMPtr nearNode = - mHTMLEditor->GetPriorHTMLNode(selNode, selOffset); + mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, child); if (nearNode) { // is nearNode also a descendant of same block? NS_ENSURE_STATE(mHTMLEditor); @@ -7628,7 +7659,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, // we aren't in a textnode: are we adjacent to text or a break or an image? NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, true); + nearNode = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, child, true); if (nearNode && (TextEditUtils::IsBreak(nearNode) || EditorBase::IsTextNode(nearNode) || HTMLEditUtils::IsImage(nearNode) || @@ -7637,7 +7668,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, return NS_OK; } NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, true); + nearNode = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, child, true); if (nearNode && (TextEditUtils::IsBreak(nearNode) || EditorBase::IsTextNode(nearNode) || nearNode->IsAnyOfHTMLElements(nsGkAtoms::img, @@ -7647,7 +7678,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, // look for a nearby text node. // prefer the correct direction. - rv = FindNearSelectableNode(selNode, selOffset, aAction, + rv = FindNearSelectableNode(selNode, selOffset, child, aAction, address_of(nearNode)); NS_ENSURE_SUCCESS(rv, rv); @@ -7666,6 +7697,7 @@ HTMLEditRules::AdjustSelection(Selection* aSelection, nsresult HTMLEditRules::FindNearSelectableNode(nsINode* aSelNode, int32_t aSelOffset, + nsINode* aChildAtOffset, nsIEditor::EDirection& aDirection, nsCOMPtr* outSelectableNode) { @@ -7675,13 +7707,15 @@ HTMLEditRules::FindNearSelectableNode(nsINode* aSelNode, nsCOMPtr nearNode, curNode; if (aDirection == nsIEditor::ePrevious) { NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset); + nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, + aChildAtOffset); if (NS_WARN_IF(!nearNode)) { return NS_ERROR_FAILURE; } } else { NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset); + nearNode = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, + aChildAtOffset); if (NS_WARN_IF(!nearNode)) { return NS_ERROR_FAILURE; } @@ -7697,13 +7731,15 @@ HTMLEditRules::FindNearSelectableNode(nsINode* aSelNode, if (aDirection == nsIEditor::ePrevious) { NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset); + nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, + aChildAtOffset); if (NS_WARN_IF(!nearNode)) { return NS_ERROR_FAILURE; } } else { NS_ENSURE_STATE(mHTMLEditor); - nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset); + nearNode = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, + aChildAtOffset); if (NS_WARN_IF(!nearNode)) { return NS_ERROR_FAILURE; } diff --git a/editor/libeditor/HTMLEditRules.h b/editor/libeditor/HTMLEditRules.h index 261f69ac916f..70854943fc06 100644 --- a/editor/libeditor/HTMLEditRules.h +++ b/editor/libeditor/HTMLEditRules.h @@ -289,9 +289,10 @@ protected: nsAtom& DefaultParagraphSeparator(); nsresult ReturnInHeader(Selection& aSelection, Element& aHeader, nsINode& aNode, int32_t aOffset); - nsresult ReturnInParagraph(Selection* aSelection, nsIDOMNode* aHeader, - nsIDOMNode* aTextNode, int32_t aOffset, - bool* aCancel, bool* aHandled); + nsresult ReturnInParagraph(Selection* aSelection, nsINode* aHeader, + nsINode* aTextNode, int32_t aOffset, + nsIContent* aChildAtOffset, bool* aCancel, + bool* aHandled); nsresult SplitParagraph(nsIDOMNode* aPara, nsIContent* aBRNode, Selection* aSelection, @@ -399,6 +400,7 @@ protected: nsIEditor::EDirection aAction); nsresult FindNearSelectableNode(nsINode* aSelNode, int32_t aSelOffset, + nsINode* aChildAtOffset, nsIEditor::EDirection& aDirection, nsCOMPtr* outSelectableNode); /** diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index ccba1f32c91d..0c2369212708 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -3118,6 +3118,7 @@ HTMLEditor::DeleteText(nsGenericDOMDataNode& aCharData, nsresult HTMLEditor::InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc) { @@ -3126,8 +3127,9 @@ HTMLEditor::InsertTextImpl(const nsAString& aStringToInsert, return NS_ERROR_FAILURE; } - return EditorBase::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, - aDoc); + return EditorBase::InsertTextImpl(aStringToInsert, aInOutNode, + aInOutChildAtOffset, + aInOutOffset, aDoc); } void @@ -3892,6 +3894,7 @@ HTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode, nsIContent* HTMLEditor::GetPriorHTMLNode(nsINode* aParent, int32_t aOffset, + nsINode* aChildAtOffset, bool aNoBlockCrossing) { MOZ_ASSERT(aParent); @@ -3900,7 +3903,7 @@ HTMLEditor::GetPriorHTMLNode(nsINode* aParent, return nullptr; } - return GetPriorNode(aParent, aOffset, true, aNoBlockCrossing); + return GetPriorNode(aParent, aOffset, aChildAtOffset, true, aNoBlockCrossing); } /** @@ -3942,9 +3945,11 @@ HTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode, nsIContent* HTMLEditor::GetNextHTMLNode(nsINode* aParent, int32_t aOffset, + nsINode* aChildAtOffset, bool aNoBlockCrossing) { - nsIContent* content = GetNextNode(aParent, aOffset, true, aNoBlockCrossing); + nsIContent* content = GetNextNode(aParent, aOffset, aChildAtOffset, + true, aNoBlockCrossing); if (content && !IsDescendantOfEditorRoot(content)) { return nullptr; } diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index 1ca4fe568a7e..9878ace579b9 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -313,6 +313,7 @@ public: uint32_t aLength); virtual nsresult InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc) override; NS_IMETHOD_(bool) IsModifiableNode(nsIDOMNode* aNode) override; @@ -779,12 +780,14 @@ protected: nsresult GetPriorHTMLNode(nsIDOMNode* inNode, nsCOMPtr* outNode, bool bNoBlockCrossing = false); nsIContent* GetPriorHTMLNode(nsINode* aParent, int32_t aOffset, + nsINode* aChildAtOffset, bool aNoBlockCrossing = false); nsIContent* GetNextHTMLNode(nsINode* aNode, bool aNoBlockCrossing = false); nsresult GetNextHTMLNode(nsIDOMNode* inNode, nsCOMPtr* outNode, bool bNoBlockCrossing = false); nsIContent* GetNextHTMLNode(nsINode* aParent, int32_t aOffset, + nsINode* aChildAtOffset, bool aNoBlockCrossing = false); bool IsFirstEditableChild(nsINode* aNode); diff --git a/editor/libeditor/TextEditRules.cpp b/editor/libeditor/TextEditRules.cpp index d4a5858aea96..e15da5557a37 100644 --- a/editor/libeditor/TextEditRules.cpp +++ b/editor/libeditor/TextEditRules.cpp @@ -726,6 +726,8 @@ TextEditRules::WillInsertText(EditAction aAction, // get the (collapsed) selection location NS_ENSURE_STATE(aSelection->GetRangeAt(0)); nsCOMPtr selNode = aSelection->GetRangeAt(0)->GetStartContainer(); + nsCOMPtr selChild = + aSelection->GetRangeAt(0)->GetChildAtStartOffset(); int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset(); NS_ENSURE_STATE(selNode); @@ -744,7 +746,8 @@ TextEditRules::WillInsertText(EditAction aAction, if (aAction == EditAction::insertIMEText) { NS_ENSURE_STATE(mTextEditor); // Find better insertion point to insert text. - mTextEditor->FindBetterInsertionPoint(selNode, selOffset); + mTextEditor->FindBetterInsertionPoint(selNode, selOffset, + address_of(selChild)); // If there is one or more IME selections, its minimum offset should be // the insertion point. int32_t IMESelectionOffset = @@ -753,7 +756,7 @@ TextEditRules::WillInsertText(EditAction aAction, selOffset = IMESelectionOffset; } rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode), - &selOffset, doc); + address_of(selChild), &selOffset, doc); NS_ENSURE_SUCCESS(rv, rv); } else { // aAction == EditAction::insertText; find where we are @@ -765,7 +768,7 @@ TextEditRules::WillInsertText(EditAction aAction, AutoTransactionsConserveSelection dontChangeMySelection(mTextEditor); rv = mTextEditor->InsertTextImpl(*outString, address_of(curNode), - &curOffset, doc); + address_of(selChild), &curOffset, doc); NS_ENSURE_SUCCESS(rv, rv); if (curNode) { diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index ca6efa16c483..746ba85420e4 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -551,7 +551,7 @@ TextEditor::ExtendSelectionForDelete(Selection* aSelection, NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); // node might be anonymous DIV, so we find better text node - FindBetterInsertionPoint(node, offset); + FindBetterInsertionPoint(node, offset, nullptr); if (IsTextNode(node)) { const nsTextFragment* data = node->GetAsText()->GetText(); @@ -704,6 +704,7 @@ TextEditor::InsertLineBreak() // get the (collapsed) selection location NS_ENSURE_STATE(selection->GetRangeAt(0)); nsCOMPtr selNode = selection->GetRangeAt(0)->GetStartContainer(); + nsCOMPtr selChild = selection->GetRangeAt(0)->GetChildAtStartOffset(); int32_t selOffset = selection->GetRangeAt(0)->StartOffset(); NS_ENSURE_STATE(selNode); @@ -722,7 +723,7 @@ TextEditor::InsertLineBreak() // insert a linefeed character rv = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode), - &selOffset, doc); + address_of(selChild), &selOffset, doc); if (!selNode) { rv = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called } diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp index 7cc76b68b5e9..61ad3e4933c8 100644 --- a/editor/libeditor/WSRunObject.cpp +++ b/editor/libeditor/WSRunObject.cpp @@ -234,6 +234,7 @@ WSRunObject::InsertBreak(nsCOMPtr* aInOutParent, nsresult WSRunObject::InsertText(const nsAString& aStringToInsert, nsCOMPtr* aInOutParent, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc) { @@ -356,7 +357,8 @@ WSRunObject::InsertText(const nsAString& aStringToInsert, } // Ready, aim, fire! - mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutOffset, aDoc); + mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutChildAtOffset, + aInOutOffset, aDoc); return NS_OK; } diff --git a/editor/libeditor/WSRunObject.h b/editor/libeditor/WSRunObject.h index cd851ad9596c..d5d848455c46 100644 --- a/editor/libeditor/WSRunObject.h +++ b/editor/libeditor/WSRunObject.h @@ -224,6 +224,7 @@ public: // trailingws before {aInOutParent,aInOutOffset} needs to be removed. nsresult InsertText(const nsAString& aStringToInsert, nsCOMPtr* aInOutNode, + nsCOMPtr* aInOutChildAtOffset, int32_t* aInOutOffset, nsIDocument* aDoc); diff --git a/editor/libeditor/crashtests/1388075.html b/editor/libeditor/crashtests/1388075.html new file mode 100644 index 000000000000..057c97c4b3ba --- /dev/null +++ b/editor/libeditor/crashtests/1388075.html @@ -0,0 +1,23 @@ + + + + + diff --git a/editor/libeditor/crashtests/crashtests.list b/editor/libeditor/crashtests/crashtests.list index 147a40d17e65..8ada7b4685f7 100644 --- a/editor/libeditor/crashtests/crashtests.list +++ b/editor/libeditor/crashtests/crashtests.list @@ -80,6 +80,7 @@ load 1366176.html load 1375131.html load 1381541.html load 1383755.html +load 1388075.html load 1402469.html load 1402904.html load 1405747.html diff --git a/gfx/layers/CompositorTypes.h b/gfx/layers/CompositorTypes.h index 0765c751c943..5ee176b7a9ba 100644 --- a/gfx/layers/CompositorTypes.h +++ b/gfx/layers/CompositorTypes.h @@ -174,7 +174,6 @@ struct TextureFactoryIdentifier bool mSupportsTextureBlitting; bool mSupportsPartialUploads; bool mSupportsComponentAlpha; - bool mSupportsBackdropCopyForComponentAlpha; bool mUsingAdvancedLayers; SyncHandle mSyncHandle; @@ -193,7 +192,6 @@ struct TextureFactoryIdentifier , mSupportsTextureBlitting(aSupportsTextureBlitting) , mSupportsPartialUploads(aSupportsPartialUploads) , mSupportsComponentAlpha(aSupportsComponentAlpha) - , mSupportsBackdropCopyForComponentAlpha(true) , mUsingAdvancedLayers(false) , mSyncHandle(aSyncHandle) {} diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 0c42a26e68c2..6d79276fcb60 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1352,8 +1352,7 @@ ContainerLayer::DefaultComputeSupportsComponentAlphaChildren(bool* aNeedsSurface if (HasOpaqueAncestorLayer(this) && GetEffectiveTransform().Is2D(&transform) && !gfx::ThebesMatrix(transform).HasNonIntegerTranslation() && - blendMode == gfx::CompositionOp::OP_OVER && - Manager()->SupportsBackdropCopyForComponentAlpha()) + blendMode == gfx::CompositionOp::OP_OVER) { mSupportsComponentAlphaChildren = true; needsSurfaceCopy = true; diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 57040a373691..924713ac7b68 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -360,12 +360,6 @@ public: */ virtual bool BlendingRequiresIntermediateSurface() { return false; } - /** - * Returns true if this LayerManager supports component alpha layers in - * situations that require a copy of the backdrop. - */ - virtual bool SupportsBackdropCopyForComponentAlpha() { return true; } - /** * CONSTRUCTION PHASE ONLY * Set the root layer. The root layer is initially null. If there is diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 4d10e219feda..5ceb7734c37c 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -825,13 +825,6 @@ ClientLayerManager::AreComponentAlphaLayersEnabled() LayerManager::AreComponentAlphaLayersEnabled(); } -bool -ClientLayerManager::SupportsBackdropCopyForComponentAlpha() -{ - const TextureFactoryIdentifier& ident = AsShadowForwarder()->GetTextureFactoryIdentifier(); - return ident.mSupportsBackdropCopyForComponentAlpha; -} - void ClientLayerManager::SetIsFirstPaint() { diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h index 1be187fbe7bd..b810e8a08549 100644 --- a/gfx/layers/client/ClientLayerManager.h +++ b/gfx/layers/client/ClientLayerManager.h @@ -201,7 +201,6 @@ public: const mozilla::TimeStamp& aCompositeEnd) override; virtual bool AreComponentAlphaLayersEnabled() override; - virtual bool SupportsBackdropCopyForComponentAlpha() override; // Log APZ test data for the current paint. We supply the paint sequence // number ourselves, and take care of calling APZTestData::StartNewPaint() diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index 9419f4b2da36..3e43b2391097 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -29,6 +29,7 @@ #include "mozilla/gfx/GPUProcessManager.h" #include "mozilla/gfx/Logging.h" #include "mozilla/mozalloc.h" // for operator new, etc +#include "mozilla/Telemetry.h" #include "nsAutoPtr.h" #include "nsDebug.h" // for NS_RUNTIMEABORT #include "nsIObserver.h" // for nsIObserver @@ -94,6 +95,8 @@ CompositorBridgeChild::CompositorBridgeChild(CompositorManagerChild *aManager) , mOutstandingAsyncPaints(0) , mOutstandingAsyncEndTransaction(false) , mIsWaitingForPaint(false) + , mSlowFlushCount(0) + , mTotalFlushCount(0) { MOZ_ASSERT(NS_IsMainThread()); } @@ -1154,13 +1157,35 @@ CompositorBridgeChild::FlushAsyncPaints() { MOZ_ASSERT(NS_IsMainThread()); - MonitorAutoLock lock(mPaintLock); - while (mIsWaitingForPaint) { - lock.Wait(); + Maybe start; + if (XRE_IsContentProcess() && gfx::gfxVars::UseOMTP()) { + start = Some(TimeStamp::Now()); } - // It's now safe to free any TextureClients that were used during painting. - mTextureClientsForAsyncPaint.Clear(); + { + MonitorAutoLock lock(mPaintLock); + while (mIsWaitingForPaint) { + lock.Wait(); + } + + // It's now safe to free any TextureClients that were used during painting. + mTextureClientsForAsyncPaint.Clear(); + } + + if (start) { + float ms = (TimeStamp::Now() - start.value()).ToMilliseconds(); + + // Anything above 200us gets recorded. + if (ms >= 0.2) { + mSlowFlushCount++; + Telemetry::Accumulate(Telemetry::GFX_OMTP_PAINT_WAIT_TIME, int32_t(ms)); + } + mTotalFlushCount++; + + double ratio = double(mSlowFlushCount) / double(mTotalFlushCount); + Telemetry::ScalarSet(Telemetry::ScalarID::GFX_OMTP_PAINT_WAIT_RATIO, + uint32_t(ratio * 100 * 100)); + } } void diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h index 1d1e01bf443f..6c32c9bbbc2a 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.h +++ b/gfx/layers/ipc/CompositorBridgeChild.h @@ -388,6 +388,9 @@ private: // paint thread completes. This is R/W on both the main and paint threads, and // must be accessed within the paint lock. bool mIsWaitingForPaint; + + uintptr_t mSlowFlushCount; + uintptr_t mTotalFlushCount; }; } // namespace layers diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h index ede6f2955fd5..27f7cc7a7582 100644 --- a/gfx/layers/ipc/LayersMessageUtils.h +++ b/gfx/layers/ipc/LayersMessageUtils.h @@ -307,7 +307,6 @@ struct ParamTraits WriteParam(aMsg, aParam.mSupportsTextureBlitting); WriteParam(aMsg, aParam.mSupportsPartialUploads); WriteParam(aMsg, aParam.mSupportsComponentAlpha); - WriteParam(aMsg, aParam.mSupportsBackdropCopyForComponentAlpha); WriteParam(aMsg, aParam.mUsingAdvancedLayers); WriteParam(aMsg, aParam.mSyncHandle); } @@ -321,7 +320,6 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) && ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) && ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) && - ReadParam(aMsg, aIter, &aResult->mSupportsBackdropCopyForComponentAlpha) && ReadParam(aMsg, aIter, &aResult->mUsingAdvancedLayers) && ReadParam(aMsg, aIter, &aResult->mSyncHandle); return result; diff --git a/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp b/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp index c2d3a6662610..14d2563ba4ff 100644 --- a/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp +++ b/gfx/layers/mlgpu/ContainerLayerMLGPU.cpp @@ -21,7 +21,8 @@ using namespace gfx; ContainerLayerMLGPU::ContainerLayerMLGPU(LayerManagerMLGPU* aManager) : ContainerLayer(aManager, nullptr), LayerMLGPU(aManager), - mInvalidateEntireSurface(false) + mInvalidateEntireSurface(false), + mSurfaceCopyNeeded(false) { } @@ -35,6 +36,8 @@ ContainerLayerMLGPU::~ContainerLayerMLGPU() bool ContainerLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder) { + mView = nullptr; + if (!UseIntermediateSurface()) { // Set this so we invalidate the entire cached render target (if any) // if our container uses an intermediate surface again later. @@ -67,6 +70,19 @@ ContainerLayerMLGPU::OnPrepareToRender(FrameBuilder* aBuilder) mRenderTarget = nullptr; } + // Note that if a surface copy is needed, we always redraw the + // whole surface (on-demand). This is a rare case - the old + // Compositor already does this - and it saves us having to + // do much more complicated invalidation. + bool surfaceCopyNeeded = false; + DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded); + if (surfaceCopyNeeded != mSurfaceCopyNeeded || + surfaceCopyNeeded) + { + mInvalidateEntireSurface = true; + } + mSurfaceCopyNeeded = surfaceCopyNeeded; + gfx::IntRect viewport(gfx::IntPoint(0, 0), mTargetSize); if (!mRenderTarget || !gfxPrefs::AdvancedLayersUseInvalidation() || diff --git a/gfx/layers/mlgpu/ContainerLayerMLGPU.h b/gfx/layers/mlgpu/ContainerLayerMLGPU.h index 2b303ef4c837..67921604156d 100644 --- a/gfx/layers/mlgpu/ContainerLayerMLGPU.h +++ b/gfx/layers/mlgpu/ContainerLayerMLGPU.h @@ -13,6 +13,7 @@ namespace mozilla { namespace layers { class MLGDevice; +class RenderViewMLGPU; class ContainerLayerMLGPU final : public ContainerLayer , public LayerMLGPU @@ -53,6 +54,17 @@ public: mInvalidRect.SetEmpty(); } bool IsContentOpaque() override; + bool NeedsSurfaceCopy() const { + return mSurfaceCopyNeeded; + } + + RenderViewMLGPU* GetRenderView() const { + return mView; + } + void SetRenderView(RenderViewMLGPU* aView) { + MOZ_ASSERT(!mView); + mView = aView; + } protected: bool OnPrepareToRender(FrameBuilder* aBuilder) override; @@ -71,6 +83,11 @@ private: // is in layer coordinates. gfx::IntRect mInvalidRect; bool mInvalidateEntireSurface; + bool mSurfaceCopyNeeded; + + // This is only valid for intermediate surfaces while an instance of + // FrameBuilder is live. + RenderViewMLGPU* mView; }; } // namespace layers diff --git a/gfx/layers/mlgpu/LayerManagerMLGPU.cpp b/gfx/layers/mlgpu/LayerManagerMLGPU.cpp index 132745901aed..aa37fbd9d805 100644 --- a/gfx/layers/mlgpu/LayerManagerMLGPU.cpp +++ b/gfx/layers/mlgpu/LayerManagerMLGPU.cpp @@ -183,7 +183,6 @@ LayerManagerMLGPU::GetTextureFactoryIdentifier() if (mDevice) { ident = mDevice->GetTextureFactoryIdentifier(); } - ident.mSupportsBackdropCopyForComponentAlpha = SupportsBackdropCopyForComponentAlpha(); ident.mUsingAdvancedLayers = true; return ident; } @@ -525,12 +524,6 @@ LayerManagerMLGPU::BlendingRequiresIntermediateSurface() return true; } -bool -LayerManagerMLGPU::SupportsBackdropCopyForComponentAlpha() -{ - return false; -} - void LayerManagerMLGPU::EndTransaction(DrawPaintedLayerCallback aCallback, void* aCallbackData, diff --git a/gfx/layers/mlgpu/LayerManagerMLGPU.h b/gfx/layers/mlgpu/LayerManagerMLGPU.h index cdc752cabced..dd043b612232 100644 --- a/gfx/layers/mlgpu/LayerManagerMLGPU.h +++ b/gfx/layers/mlgpu/LayerManagerMLGPU.h @@ -50,7 +50,6 @@ public: bool AreComponentAlphaLayersEnabled() override; bool BlendingRequiresIntermediateSurface() override; - bool SupportsBackdropCopyForComponentAlpha() override; // HostLayerManager methods void ForcePresent() override; diff --git a/gfx/layers/mlgpu/RenderPassMLGPU.cpp b/gfx/layers/mlgpu/RenderPassMLGPU.cpp index ef09faae452f..0b18ae598fd9 100644 --- a/gfx/layers/mlgpu/RenderPassMLGPU.cpp +++ b/gfx/layers/mlgpu/RenderPassMLGPU.cpp @@ -305,6 +305,11 @@ ShaderRenderPass::ExecuteRendering() return; } + // Change the blend state if needed. + if (Maybe blendState = GetBlendState()) { + mDevice->SetBlendState(blendState.value()); + } + mDevice->SetPSConstantBuffer(0, &mPSBuffer0); if (MaskOperation* mask = GetMask()) { mDevice->SetPSTexture(kMaskLayerTextureSlot, mask->GetTexture()); @@ -994,5 +999,48 @@ RenderViewPass::SetupPipeline() mDevice->SetSamplerMode(kDefaultSamplerSlot, SamplerMode::LinearClamp); } +void +RenderViewPass::ExecuteRendering() +{ + if (mAssignedLayer->NeedsSurfaceCopy()) { + RenderWithBackdropCopy(); + return; + } + + TexturedRenderPass::ExecuteRendering(); +} + +void +RenderViewPass::RenderWithBackdropCopy() +{ + MOZ_ASSERT(mAssignedLayer->NeedsSurfaceCopy()); + + DebugOnly transform2d; + const Matrix4x4& transform = mAssignedLayer->GetEffectiveTransform(); + MOZ_ASSERT(transform.Is2D(&transform2d) && + !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation()); + + IntRect visible = mAssignedLayer->GetShadowVisibleRegion().GetBounds().ToUnknownRect(); + visible += IntPoint::Truncate(transform._41, transform._42); + visible -= mParentView->GetTargetOffset(); + + RefPtr dest = mAssignedLayer->GetRenderTarget()->GetTexture(); + RefPtr source = mParentView->GetRenderTarget()->GetTexture(); + + // Clamp the rect so that we don't read pixels outside the source texture, or + // write pixels outside the destination texture. + visible = visible.Intersect(IntRect(IntPoint(0, 0), source->GetSize())); + visible = visible.Intersect(IntRect(visible.TopLeft(), dest->GetSize())); + + mDevice->CopyTexture(dest, IntPoint(0, 0), source, visible); + + RenderViewMLGPU* childView = mAssignedLayer->GetRenderView(); + childView->RenderAfterBackdropCopy(); + + mParentView->RestoreDeviceState(); + + TexturedRenderPass::ExecuteRendering(); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/mlgpu/RenderPassMLGPU.h b/gfx/layers/mlgpu/RenderPassMLGPU.h index 0909b83391a3..29ffd7232d8c 100644 --- a/gfx/layers/mlgpu/RenderPassMLGPU.h +++ b/gfx/layers/mlgpu/RenderPassMLGPU.h @@ -464,8 +464,10 @@ private: bool AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo) override; void SetupPipeline() override; bool OnPrepareBuffers() override; + void ExecuteRendering() override; float GetOpacity() const override; bool PrepareBlendState(); + void RenderWithBackdropCopy(); private: ConstantBufferSection mBlendConstants; diff --git a/gfx/layers/mlgpu/RenderViewMLGPU.cpp b/gfx/layers/mlgpu/RenderViewMLGPU.cpp index d8e0adbba5bb..ed44fd8b869d 100644 --- a/gfx/layers/mlgpu/RenderViewMLGPU.cpp +++ b/gfx/layers/mlgpu/RenderViewMLGPU.cpp @@ -62,6 +62,8 @@ RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder, this, aContainer->GetLayer(), Stringify(mInvalidBounds).c_str()); + + mContainer->SetRenderView(this); } RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder, RenderViewMLGPU* aParent) @@ -72,6 +74,7 @@ RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder, RenderViewMLGPU* aParen mFinishedBuilding(false), mCurrentLayerBufferIndex(kInvalidResourceIndex), mCurrentMaskRectBufferIndex(kInvalidResourceIndex), + mCurrentDepthMode(MLGDepthTestMode::Disabled), mNextSortIndex(1), mUseDepthBuffer(gfxPrefs::AdvancedLayersEnableDepthBuffer()), mDepthBufferNeedsClear(false) @@ -111,11 +114,23 @@ RenderViewMLGPU::AddChild(RenderViewMLGPU* aParent) void RenderViewMLGPU::Render() { - // We render tiles front-to-back, depth-first, to minimize render target switching. + // We render views depth-first to minimize render target switching. for (const auto& child : mChildren) { child->Render(); } + // If the view requires a surface copy (of its backdrop), then we delay + // rendering it until it is added to a batch. + if (mContainer && mContainer->NeedsSurfaceCopy()) { + return; + } + ExecuteRendering(); +} + +void +RenderViewMLGPU::RenderAfterBackdropCopy() +{ + MOZ_ASSERT(mContainer && mContainer->NeedsSurfaceCopy()); ExecuteRendering(); } @@ -385,26 +400,19 @@ RenderViewMLGPU::ExecuteRendering() if (!mTarget) { return; } - - // Note: we unbind slot 0 (which is where the render target could have been - // bound on a previous frame). Otherwise we trigger D3D11_DEVICE_PSSETSHADERRESOURCES_HAZARD. - mDevice->UnsetPSTexture(0); - mDevice->SetRenderTarget(mTarget); - mDevice->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize())); - mDevice->SetScissorRect(Some(mInvalidBounds)); - if (!mWorldConstants.IsValid()) { gfxWarning() << "Failed to allocate constant buffer for world transform"; return; } - mDevice->SetVSConstantBuffer(kWorldConstantBufferSlot, &mWorldConstants); + + SetDeviceState(); // If using the depth buffer, clear it (if needed) and enable writes. if (mUseDepthBuffer) { if (mDepthBufferNeedsClear) { mDevice->ClearDepthBuffer(mTarget); } - mDevice->SetDepthTestMode(MLGDepthTestMode::Write); + SetDepthTestMode(MLGDepthTestMode::Write); } // Opaque items, rendered front-to-back. @@ -415,7 +423,7 @@ RenderViewMLGPU::ExecuteRendering() if (mUseDepthBuffer) { // From now on we might be rendering transparent pixels, so we disable // writing to the z-buffer. - mDevice->SetDepthTestMode(MLGDepthTestMode::ReadOnly); + SetDepthTestMode(MLGDepthTestMode::ReadOnly); } // Clear any pixels that are not occluded, and therefore might require @@ -466,14 +474,37 @@ RenderViewMLGPU::ExecutePass(RenderPassMLGPU* aPass) mDevice->SetVSConstantBuffer(kMaskBufferSlot, §ion); } - // Change the blend state if needed. - if (Maybe blendState = aPass->GetBlendState()) { - mDevice->SetBlendState(blendState.value()); - } - aPass->ExecuteRendering(); } +void +RenderViewMLGPU::SetDeviceState() +{ + // Note: we unbind slot 0 (which is where the render target could have been + // bound on a previous frame). Otherwise we trigger D3D11_DEVICE_PSSETSHADERRESOURCES_HAZARD. + mDevice->UnsetPSTexture(0); + mDevice->SetRenderTarget(mTarget); + mDevice->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize())); + mDevice->SetScissorRect(Some(mInvalidBounds)); + mDevice->SetVSConstantBuffer(kWorldConstantBufferSlot, &mWorldConstants); +} + +void +RenderViewMLGPU::SetDepthTestMode(MLGDepthTestMode aMode) +{ + mDevice->SetDepthTestMode(aMode); + mCurrentDepthMode = aMode; +} + +void +RenderViewMLGPU::RestoreDeviceState() +{ + SetDeviceState(); + mDevice->SetDepthTestMode(mCurrentDepthMode); + mCurrentLayerBufferIndex = kInvalidResourceIndex; + mCurrentMaskRectBufferIndex = kInvalidResourceIndex; +} + int32_t RenderViewMLGPU::PrepareDepthBuffer() { @@ -514,6 +545,11 @@ RenderViewMLGPU::PrepareDepthBuffer() void RenderViewMLGPU::PrepareClears() { + // We don't do any clearing if we're copying from a source backdrop. + if (mContainer && mContainer->NeedsSurfaceCopy()) { + return; + } + // Get the list of rects to clear. If using the depth buffer, we don't // care if it's accurate since the GPU will do occlusion testing for us. // If not using the depth buffer, we subtract out the occluded region. diff --git a/gfx/layers/mlgpu/RenderViewMLGPU.h b/gfx/layers/mlgpu/RenderViewMLGPU.h index 2f3212adf0ef..a1effa9c5dba 100644 --- a/gfx/layers/mlgpu/RenderViewMLGPU.h +++ b/gfx/layers/mlgpu/RenderViewMLGPU.h @@ -52,6 +52,11 @@ public: return mUseDepthBuffer; } + // Render after having previously delayed rendering due to the view + // requiring a backdrop copy. + void RenderAfterBackdropCopy(); + void RestoreDeviceState(); + // The size and render target cannot be read until the view has finished // building, since we try to right-size the render target to the visible // region. @@ -72,6 +77,8 @@ private: void AddItemBackToFront(LayerMLGPU* aLayer, ItemInfo& aItem); void PrepareClears(); + void SetDeviceState(); + void SetDepthTestMode(MLGDepthTestMode aMode); void ExecutePass(RenderPassMLGPU* aPass); @@ -124,6 +131,9 @@ private: size_t mCurrentLayerBufferIndex; size_t mCurrentMaskRectBufferIndex; + // This state is saved locally so it can be restored in RestoreDeviceState. + MLGDepthTestMode mCurrentDepthMode; + // Depth-buffer tracking. int32_t mNextSortIndex; bool mUseDepthBuffer; diff --git a/gfx/layers/wr/StackingContextHelper.cpp b/gfx/layers/wr/StackingContextHelper.cpp index 8396b2f1d5b3..821e80bbf351 100644 --- a/gfx/layers/wr/StackingContextHelper.cpp +++ b/gfx/layers/wr/StackingContextHelper.cpp @@ -79,11 +79,5 @@ StackingContextHelper::ToRelativeLayoutRect(const LayoutDeviceRect& aRect) const PixelCastJustification::WebRenderHasUnitResolution) - mOrigin)); } -wr::LayoutPoint -StackingContextHelper::ToRelativeLayoutPoint(const LayerPoint& aPoint) const -{ - return wr::ToLayoutPoint(aPoint - mOrigin); -} - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/StackingContextHelper.h b/gfx/layers/wr/StackingContextHelper.h index 45f7734e4863..445e9c7cb842 100644 --- a/gfx/layers/wr/StackingContextHelper.h +++ b/gfx/layers/wr/StackingContextHelper.h @@ -62,7 +62,11 @@ public: wr::LayoutRect ToRelativeLayoutRect(const LayerRect& aRect) const; wr::LayoutRect ToRelativeLayoutRect(const LayoutDeviceRect& aRect) const; // Same but for points - wr::LayoutPoint ToRelativeLayoutPoint(const LayerPoint& aPoint) const; + wr::LayoutPoint ToRelativeLayoutPoint(const LayerPoint& aPoint) const + { + return wr::ToLayoutPoint(aPoint - mOrigin); + } + // Export the inherited scale gfx::Size GetInheritedScale() const { return mScale; } diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 1cb2632685b8..9a5b29b0ac3b 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1370,37 +1370,38 @@ js::IdToStringOrSymbol(JSContext* cx, HandleId id, MutableHandleValue result) return true; } -/* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */ +// ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f +// 19.1.2.10.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type ) bool js::GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags) { - // Steps 1-2. + // Step 1. RootedObject obj(cx, ToObject(cx, args.get(0))); if (!obj) return false; - // Steps 3-10. + // Steps 2-4. AutoIdVector keys(cx); if (!GetPropertyKeys(cx, obj, flags, &keys)) return false; - // Step 11. - AutoValueVector vals(cx); - if (!vals.resize(keys.length())) + // Step 5 (Inlined CreateArrayFromList). + RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, keys.length())); + if (!array) return false; + array->ensureDenseInitializedLength(cx, 0, keys.length()); + + RootedValue val(cx); for (size_t i = 0, len = keys.length(); i < len; i++) { MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS); MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY)); - if (!IdToStringOrSymbol(cx, keys[i], vals[i])) + if (!IdToStringOrSymbol(cx, keys[i], &val)) return false; + array->initDenseElement(i, val); } - JSObject* aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin()); - if (!aobj) - return false; - - args.rval().setObject(*aobj); + args.rval().setObject(*array); return true; } diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index d37d5f0717e3..7ce9e6ff5f1d 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -1734,9 +1734,9 @@ class ASTSerializer return StringValue(atom ? atom : cx->names().empty); } - BinaryOperator binop(ParseNodeKind kind, JSOp op); - UnaryOperator unop(ParseNodeKind kind, JSOp op); - AssignmentOperator aop(JSOp op); + BinaryOperator binop(ParseNodeKind kind); + UnaryOperator unop(ParseNodeKind kind); + AssignmentOperator aop(ParseNodeKind kind); bool statements(ParseNode* pn, NodeVector& elts); bool expressions(ParseNode* pn, NodeVector& elts); @@ -1843,34 +1843,34 @@ class ASTSerializer } /* anonymous namespace */ AssignmentOperator -ASTSerializer::aop(JSOp op) +ASTSerializer::aop(ParseNodeKind kind) { - switch (op) { - case JSOP_NOP: + switch (kind) { + case PNK_ASSIGN: return AOP_ASSIGN; - case JSOP_ADD: + case PNK_ADDASSIGN: return AOP_PLUS; - case JSOP_SUB: + case PNK_SUBASSIGN: return AOP_MINUS; - case JSOP_MUL: + case PNK_MULASSIGN: return AOP_STAR; - case JSOP_DIV: + case PNK_DIVASSIGN: return AOP_DIV; - case JSOP_MOD: + case PNK_MODASSIGN: return AOP_MOD; - case JSOP_POW: + case PNK_POWASSIGN: return AOP_POW; - case JSOP_LSH: + case PNK_LSHASSIGN: return AOP_LSH; - case JSOP_RSH: + case PNK_RSHASSIGN: return AOP_RSH; - case JSOP_URSH: + case PNK_URSHASSIGN: return AOP_URSH; - case JSOP_BITOR: + case PNK_BITORASSIGN: return AOP_BITOR; - case JSOP_BITXOR: + case PNK_BITXORASSIGN: return AOP_BITXOR; - case JSOP_BITAND: + case PNK_BITANDASSIGN: return AOP_BITAND; default: return AOP_ERR; @@ -1878,7 +1878,7 @@ ASTSerializer::aop(JSOp op) } UnaryOperator -ASTSerializer::unop(ParseNodeKind kind, JSOp op) +ASTSerializer::unop(ParseNodeKind kind) { if (IsDeleteKind(kind)) return UNOP_DELETE; @@ -1886,19 +1886,18 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op) if (IsTypeofKind(kind)) return UNOP_TYPEOF; - if (kind == PNK_AWAIT) + switch (kind) { + case PNK_AWAIT: return UNOP_AWAIT; - - switch (op) { - case JSOP_NEG: + case PNK_NEG: return UNOP_NEG; - case JSOP_POS: + case PNK_POS: return UNOP_POS; - case JSOP_NOT: + case PNK_NOT: return UNOP_NOT; - case JSOP_BITNOT: + case PNK_BITNOT: return UNOP_BITNOT; - case JSOP_VOID: + case PNK_VOID: return UNOP_VOID; default: return UNOP_ERR; @@ -1906,7 +1905,7 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op) } BinaryOperator -ASTSerializer::binop(ParseNodeKind kind, JSOp op) +ASTSerializer::binop(ParseNodeKind kind) { switch (kind) { case PNK_LSH: @@ -2632,7 +2631,7 @@ ASTSerializer::leftAssociate(ParseNode* pn, MutableHandleValue dst) if (!builder.logicalExpression(lor, left, right, &subpos, &left)) return false; } else { - BinaryOperator op = binop(pn->getKind(), pn->getOp()); + BinaryOperator op = binop(pn->getKind()); LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); if (!builder.binaryExpression(op, left, right, &subpos, &left)) @@ -2676,7 +2675,7 @@ ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst) TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end); - BinaryOperator op = binop(pn->getKind(), pn->getOp()); + BinaryOperator op = binop(pn->getKind()); LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); if (!builder.binaryExpression(op, left, right, &subpos, &right)) @@ -2887,7 +2886,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); - AssignmentOperator op = aop(pn->getOp()); + AssignmentOperator op = aop(pn->getKind()); LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT); RootedValue lhs(cx), rhs(cx); @@ -2936,7 +2935,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_NEG: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); - UnaryOperator op = unop(pn->getKind(), pn->getOp()); + UnaryOperator op = unop(pn->getKind()); LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT); RootedValue expr(cx); @@ -3486,10 +3485,8 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& ParseNode* pnstart = pnbody->pn_head; // Skip over initial yield in generator. - if (pnstart && pnstart->isKind(PNK_INITIALYIELD)) { - MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD); + if (pnstart && pnstart->isKind(PNK_INITIALYIELD)) pnstart = pnstart->pn_next; - } // Async arrow with expression body is converted into STATEMENTLIST // to insert initial yield. diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index dba4585b7ed9..c026273e3de0 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -13,6 +13,8 @@ #include "mozilla/Unused.h" #include +#include +#include #include "jsapi.h" #include "jscntxt.h" @@ -4737,6 +4739,118 @@ DisableForEach(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +GetTimeZone(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() != 0) { + ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); + return false; + } + + auto getTimeZone = [](std::time_t* now) -> const char* { + std::tm local{}; +#if defined(_WIN32) + _tzset(); + if (localtime_s(&local, now) == 0) + return _tzname[local.tm_isdst > 0]; +#else + tzset(); +#if defined(HAVE_LOCALTIME_R) + if (localtime_r(now, &local)) { +#else + std::tm* localtm = std::localtime(now); + if (localtm) { + *local = *localtm; +#endif /* HAVE_LOCALTIME_R */ + +#if defined(HAVE_TM_ZONE_TM_GMTOFF) + return local.tm_zone; +#else + return tzname[local.tm_isdst > 0]; +#endif /* HAVE_TM_ZONE_TM_GMTOFF */ + } +#endif /* _WIN32 */ + return nullptr; + }; + + std::time_t now = std::time(nullptr); + if (now != static_cast(-1)) { + if (const char* tz = getTimeZone(&now)) { + JSString* str = JS_NewStringCopyZ(cx, tz); + if (!str) + return false; + args.rval().setString(str); + return true; + } + } + + args.rval().setUndefined(); + return true; +} + +static bool +SetTimeZone(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() != 1) { + ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); + return false; + } + + if (!args[0].isString() && !args[0].isUndefined()) { + ReportUsageErrorASCII(cx, callee, "First argument should be a string or undefined"); + return false; + } + + auto setTimeZone = [](const char* value) { +#if defined(_WIN32) + return _putenv_s("TZ", value) == 0; +#else + return setenv("TZ", value, true) == 0; +#endif /* _WIN32 */ + }; + + auto unsetTimeZone = []() { +#if defined(_WIN32) + return _putenv_s("TZ", "") == 0; +#else + return unsetenv("TZ") == 0; +#endif /* _WIN32 */ + }; + + if (args[0].isString() && !args[0].toString()->empty()) { + JSAutoByteString timeZone; + if (!timeZone.encodeLatin1(cx, args[0].toString())) + return false; + + if (!setTimeZone(timeZone.ptr())) { + JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable"); + return false; + } + } else { + if (!unsetTimeZone()) { + JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable"); + return false; + } + } + +#if defined(_WIN32) + _tzset(); +#else + tzset(); +#endif /* _WIN32 */ + + JS::ResetTimeZone(); + + args.rval().setUndefined(); + return true; +} + #if defined(FUZZING) && defined(__AFL_COMPILER) static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) @@ -5414,6 +5528,10 @@ gc::ZealModeHelpText), "isLegacyIterator(value)", " Returns whether the value is considered is a legacy iterator.\n"), + JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0, +"getTimeZone()", +" Get the current time zone.\n"), + JS_FS_HELP_END }; @@ -5432,6 +5550,12 @@ static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = { "getErrorNotes(error)", " Returns an array of error notes."), + JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0, +"setTimeZone(tzname)", +" Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n" +" An empty string or undefined resets the time zone to its default value.\n" +" NOTE: The input string is not validated and will be passed verbatim to setenv()."), + JS_FS_HELP_END }; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f8c30f0da2af..ed368684a4ea 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -6148,6 +6148,7 @@ bool BytecodeEmitter::emitDeclarationList(ParseNode* declList) { MOZ_ASSERT(declList->isArity(PN_LIST)); + MOZ_ASSERT(declList->isOp(JSOP_NOP)); ParseNode* next; for (ParseNode* decl = declList->pn_head; decl; decl = next) { @@ -6226,9 +6227,32 @@ EmitAssignmentRhs(BytecodeEmitter* bce, ParseNode* rhs, uint8_t offset) return true; } -bool -BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) +static inline JSOp +CompoundAssignmentParseNodeKindToJSOp(ParseNodeKind pnk) { + switch (pnk) { + case PNK_ASSIGN: return JSOP_NOP; + case PNK_ADDASSIGN: return JSOP_ADD; + case PNK_SUBASSIGN: return JSOP_SUB; + case PNK_BITORASSIGN: return JSOP_BITOR; + case PNK_BITXORASSIGN: return JSOP_BITXOR; + case PNK_BITANDASSIGN: return JSOP_BITAND; + case PNK_LSHASSIGN: return JSOP_LSH; + case PNK_RSHASSIGN: return JSOP_RSH; + case PNK_URSHASSIGN: return JSOP_URSH; + case PNK_MULASSIGN: return JSOP_MUL; + case PNK_DIVASSIGN: return JSOP_DIV; + case PNK_MODASSIGN: return JSOP_MOD; + case PNK_POWASSIGN: return JSOP_POW; + default: MOZ_CRASH("unexpected compound assignment op"); + } +} + +bool +BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rhs) +{ + JSOp op = CompoundAssignmentParseNodeKindToJSOp(pnk); + // Name assignments are handled separately because choosing ops and when // to emit BINDNAME is involved and should avoid duplication. if (lhs->isKind(PNK_NAME)) { @@ -6254,8 +6278,10 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs) } // Emit the compound assignment op if there is one. - if (op != JSOP_NOP && !bce->emit1(op)) - return false; + if (op != JSOP_NOP) { + if (!bce->emit1(op)) + return false; + } return true; }; @@ -6454,7 +6480,7 @@ ParseNode::getConstantValue(JSContext* cx, AllowConstantObjects allowObjects, count = pn_count - 1; pn = pn_head->pn_next; } else { - MOZ_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); + MOZ_ASSERT(!(pn_xflags & PNX_NONCONST)); count = pn_count; pn = pn_head; } @@ -6485,7 +6511,6 @@ ParseNode::getConstantValue(JSContext* cx, AllowConstantObjects allowObjects, return true; } case PNK_OBJECT: { - MOZ_ASSERT(isOp(JSOP_NEWINIT)); MOZ_ASSERT(!(pn_xflags & PNX_NONCONST)); if (allowObjects == DontAllowObjects) { @@ -7137,7 +7162,7 @@ BytecodeEmitter::emitInitializeForInOrOfTarget(ParseNode* forHead) // initialization is just assigning the iteration value to a target // expression. if (!parser.isDeclarationList(target)) - return emitAssignment(target, JSOP_NOP, nullptr); // ... ITERVAL + return emitAssignment(target, PNK_ASSIGN, nullptr); // ... ITERVAL // Otherwise, per-loop initialization is (possibly) declaration // initialization. If the declaration is a lexical declaration, it must be @@ -7862,7 +7887,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn) // Notice: Comprehension for-of doesn't perform IteratorClose, since it's // not in the spec. - if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER VALUE + if (!emitAssignment(loopVariableName, PNK_ASSIGN, nullptr)) // ITER VALUE return false; // Remove VALUE from the stack to release it. @@ -7986,7 +8011,7 @@ BytecodeEmitter::emitComprehensionForIn(ParseNode* pn) // Emit code to assign the enumeration value to the left hand side, but // also leave it on the stack. - if (!emitAssignment(forHead->pn_kid2, JSOP_NOP, nullptr)) + if (!emitAssignment(forHead->pn_kid2, PNK_ASSIGN, nullptr)) return false; /* The stack should be balanced around the assignment opcode sequence. */ @@ -8688,7 +8713,7 @@ bool BytecodeEmitter::emitYield(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); - MOZ_ASSERT(pn->getOp() == JSOP_YIELD); + MOZ_ASSERT(pn->isKind(PNK_YIELD)); bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); if (needsIteratorResult) { @@ -8738,7 +8763,7 @@ bool BytecodeEmitter::emitAwait(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); - MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); + MOZ_ASSERT(pn->isKind(PNK_AWAIT)); if (!emitTree(pn->pn_kid)) return false; @@ -9675,6 +9700,41 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs return true; } +static const JSOp ParseNodeKindToJSOp[] = { + JSOP_OR, + JSOP_AND, + JSOP_BITOR, + JSOP_BITXOR, + JSOP_BITAND, + JSOP_STRICTEQ, + JSOP_EQ, + JSOP_STRICTNE, + JSOP_NE, + JSOP_LT, + JSOP_LE, + JSOP_GT, + JSOP_GE, + JSOP_INSTANCEOF, + JSOP_IN, + JSOP_LSH, + JSOP_RSH, + JSOP_URSH, + JSOP_ADD, + JSOP_SUB, + JSOP_MUL, + JSOP_DIV, + JSOP_MOD, + JSOP_POW +}; + +static inline JSOp +BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk) +{ + MOZ_ASSERT(pnk >= PNK_BINOP_FIRST); + MOZ_ASSERT(pnk <= PNK_BINOP_LAST); + return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST]; +} + bool BytecodeEmitter::emitRightAssociative(ParseNode* pn) { @@ -9702,7 +9762,7 @@ BytecodeEmitter::emitLeftAssociative(ParseNode* pn) // Left-associative operator chain. if (!emitTree(pn->pn_head)) return false; - JSOp op = pn->getOp(); + JSOp op = BinaryOpParseNodeKindToJSOp(pn->getKind()); ParseNode* nextExpr = pn->pn_head->pn_next; do { if (!emitTree(nextExpr)) @@ -9717,6 +9777,7 @@ bool BytecodeEmitter::emitLogical(ParseNode* pn) { MOZ_ASSERT(pn->isArity(PN_LIST)); + MOZ_ASSERT(pn->isKind(PNK_OR) || pn->isKind(PNK_AND)); /* * JSOP_OR converts the operand on the stack to boolean, leaves the original @@ -9734,7 +9795,7 @@ BytecodeEmitter::emitLogical(ParseNode* pn) ParseNode* pn2 = pn->pn_head; if (!emitTree(pn2)) return false; - JSOp op = pn->getOp(); + JSOp op = pn->isKind(PNK_OR) ? JSOP_OR : JSOP_AND; JumpList jump; if (!emitJump(op, &jump)) return false; @@ -10237,20 +10298,28 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) return true; } +static inline JSOp +UnaryOpParseNodeKindToJSOp(ParseNodeKind pnk) +{ + switch (pnk) { + case PNK_THROW: return JSOP_THROW; + case PNK_VOID: return JSOP_VOID; + case PNK_NOT: return JSOP_NOT; + case PNK_BITNOT: return JSOP_BITNOT; + case PNK_POS: return JSOP_POS; + case PNK_NEG: return JSOP_NEG; + default: MOZ_CRASH("unexpected unary op"); + } +} + bool BytecodeEmitter::emitUnary(ParseNode* pn) { if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; - - /* Unary op, including unary +/-. */ - JSOp op = pn->getOp(); - ParseNode* pn2 = pn->pn_kid; - - if (!emitTree(pn2)) + if (!emitTree(pn->pn_kid)) return false; - - return emit1(op); + return emit1(UnaryOpParseNodeKindToJSOp(pn->getKind())); } bool @@ -10973,7 +11042,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: case PNK_DIVASSIGN: case PNK_MODASSIGN: case PNK_POWASSIGN: - if (!emitAssignment(pn->pn_left, pn->getOp(), pn->pn_right)) + if (!emitAssignment(pn->pn_left, pn->getKind(), pn->pn_right)) return false; break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index fe374cbd6eeb..b155ddcc6592 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -759,7 +759,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitCallSiteObject(ParseNode* pn); MOZ_MUST_USE bool emitTemplateString(ParseNode* pn); - MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs); + MOZ_MUST_USE bool emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rhs); MOZ_MUST_USE bool emitReturn(ParseNode* pn); MOZ_MUST_USE bool emitStatement(ParseNode* pn); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 91a72bf52e28..c8ac063263e9 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -97,7 +97,7 @@ class FullParseHandler ParseNode* newComputedName(ParseNode* expr, uint32_t begin, uint32_t end) { TokenPos pos(begin, end); - return new_(PNK_COMPUTED_NAME, JSOP_NOP, pos, expr); + return new_(PNK_COMPUTED_NAME, pos, expr); } ParseNode* newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) { @@ -181,69 +181,69 @@ class FullParseHandler ParseNode* newDelete(uint32_t begin, ParseNode* expr) { if (expr->isKind(PNK_NAME)) { expr->setOp(JSOP_DELNAME); - return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr); + return newUnary(PNK_DELETENAME, begin, expr); } if (expr->isKind(PNK_DOT)) - return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr); + return newUnary(PNK_DELETEPROP, begin, expr); if (expr->isKind(PNK_ELEM)) - return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr); + return newUnary(PNK_DELETEELEM, begin, expr); - return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr); + return newUnary(PNK_DELETEEXPR, begin, expr); } ParseNode* newTypeof(uint32_t begin, ParseNode* kid) { + return newUnary(kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR, begin, kid); + } + + ParseNode* newUnary(ParseNodeKind kind, uint32_t begin, ParseNode* kid) { TokenPos pos(begin, kid->pn_pos.end); - ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR; - return new_(kind, JSOP_NOP, pos, kid); - } - - ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { - return new_(kind, op, pos); - } - - ParseNode* newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, ParseNode* kid) { - TokenPos pos(begin, kid ? kid->pn_pos.end : begin + 1); - return new_(kind, op, pos, kid); + return new_(kind, pos, kid); } ParseNode* newUpdate(ParseNodeKind kind, uint32_t begin, ParseNode* kid) { TokenPos pos(begin, kid->pn_pos.end); - return new_(kind, JSOP_NOP, pos, kid); + return new_(kind, pos, kid); } ParseNode* newSpread(uint32_t begin, ParseNode* kid) { TokenPos pos(begin, kid->pn_pos.end); - return new_(PNK_SPREAD, JSOP_NOP, pos, kid); + return new_(PNK_SPREAD, pos, kid); } ParseNode* newArrayPush(uint32_t begin, ParseNode* kid) { TokenPos pos(begin, kid->pn_pos.end); - return new_(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, pos, kid); + return new_(PNK_ARRAYPUSH, pos, kid); } + private: ParseNode* newBinary(ParseNodeKind kind, ParseNode* left, ParseNode* right, JSOp op = JSOP_NOP) { TokenPos pos(left->pn_pos.begin, right->pn_pos.end); return new_(kind, op, pos, left, right); } - ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right, - ParseContext* pc, JSOp op = JSOP_NOP) - { - return ParseNode::appendOrCreateList(kind, op, left, right, this, pc); - } - ParseNode* newTernary(ParseNodeKind kind, - ParseNode* first, ParseNode* second, ParseNode* third, - JSOp op = JSOP_NOP) + public: + ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right, + ParseContext* pc) { - return new_(kind, op, first, second, third); + return ParseNode::appendOrCreateList(kind, left, right, this, pc); } // Expressions + ParseNode* newGeneratorComprehension(ParseNode* genfn, const TokenPos& pos) { + MOZ_ASSERT(pos.begin <= genfn->pn_pos.begin); + MOZ_ASSERT(genfn->pn_pos.end <= pos.end); + ParseNode* result = new_(PNK_GENEXP, JSOP_CALL, pos); + if (!result) + return null(); + result->append(genfn); + return result; + } + ParseNode* newArrayComprehension(ParseNode* body, const TokenPos& pos) { MOZ_ASSERT(pos.begin <= body->pn_pos.begin); MOZ_ASSERT(body->pn_pos.end <= pos.end); @@ -255,11 +255,7 @@ class FullParseHandler } ParseNode* newArrayLiteral(uint32_t begin) { - ParseNode* literal = new_(PNK_ARRAY, TokenPos(begin, begin + 1)); - // Later in this stack: remove dependency on this opcode. - if (literal) - literal->setOp(JSOP_NEWINIT); - return literal; + return new_(PNK_ARRAY, TokenPos(begin, begin + 1)); } MOZ_MUST_USE bool addElision(ParseNode* literal, const TokenPos& pos) { @@ -287,19 +283,19 @@ class FullParseHandler } ParseNode* newCall(const TokenPos& pos) { - return newList(PNK_CALL, pos, JSOP_CALL); + return new_(PNK_CALL, JSOP_CALL, pos); + } + + ParseNode* newSuperCall(ParseNode* callee) { + return new_(PNK_SUPERCALL, JSOP_SUPERCALL, callee); } ParseNode* newTaggedTemplate(const TokenPos& pos) { - return newList(PNK_TAGGED_TEMPLATE, pos, JSOP_CALL); + return new_(PNK_TAGGED_TEMPLATE, JSOP_CALL, pos); } ParseNode* newObjectLiteral(uint32_t begin) { - ParseNode* literal = new_(PNK_OBJECT, TokenPos(begin, begin + 1)); - // Later in this stack: remove dependency on this opcode. - if (literal) - literal->setOp(JSOP_NEWINIT); - return literal; + return new_(PNK_OBJECT, TokenPos(begin, begin + 1)); } ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock, @@ -320,7 +316,7 @@ class FullParseHandler return new_(PNK_POSHOLDER, pos); } ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) { - return new_(PNK_SUPERBASE, JSOP_NOP, pos, thisName); + return new_(PNK_SUPERBASE, pos, thisName); } MOZ_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) { @@ -328,7 +324,7 @@ class FullParseHandler // singleton objects will have Object.prototype as their [[Prototype]]. setListFlag(literal, PNX_NONCONST); - ParseNode* mutation = newUnary(PNK_MUTATEPROTO, JSOP_NOP, begin, expr); + ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr); if (!mutation) return false; literal->append(mutation); @@ -378,7 +374,7 @@ class FullParseHandler } MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, - JSOp op) + AccessorType atype) { MOZ_ASSERT(literal->isArity(PN_LIST)); MOZ_ASSERT(key->isKind(PNK_NUMBER) || @@ -387,7 +383,7 @@ class FullParseHandler key->isKind(PNK_COMPUTED_NAME)); literal->pn_xflags |= PNX_NONCONST; - ParseNode* propdef = newBinary(PNK_COLON, key, fn, op); + ParseNode* propdef = newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype)); if (!propdef) return false; literal->append(propdef); @@ -395,7 +391,7 @@ class FullParseHandler } MOZ_MUST_USE bool addClassMethodDefinition(ParseNode* methodList, ParseNode* key, ParseNode* fn, - JSOp op, bool isStatic) + AccessorType atype, bool isStatic) { MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST)); MOZ_ASSERT(key->isKind(PNK_NUMBER) || @@ -403,7 +399,7 @@ class FullParseHandler key->isKind(PNK_STRING) || key->isKind(PNK_COMPUTED_NAME)); - ParseNode* classMethod = new_(key, fn, op, isStatic); + ParseNode* classMethod = new_(key, fn, AccessorTypeToJSOp(atype), isStatic); if (!classMethod) return false; methodList->append(classMethod); @@ -412,22 +408,22 @@ class FullParseHandler ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) { TokenPos pos(begin, begin + 1); - return new_(PNK_INITIALYIELD, JSOP_INITIALYIELD, pos, gen); + return new_(PNK_INITIALYIELD, pos, gen); } ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_(PNK_YIELD, JSOP_YIELD, pos, value); + return new_(PNK_YIELD, pos, value); } ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value->pn_pos.end); - return new_(PNK_YIELD_STAR, JSOP_NOP, pos, value); + return new_(PNK_YIELD_STAR, pos, value); } ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_(PNK_AWAIT, JSOP_AWAIT, pos, value); + return new_(PNK_AWAIT, pos, value); } // Statements @@ -500,7 +496,7 @@ class FullParseHandler } ParseNode* newEmptyStatement(const TokenPos& pos) { - return new_(PNK_SEMI, JSOP_NOP, pos, (ParseNode*) nullptr); + return new_(PNK_SEMI, pos, nullptr); } ParseNode* newImportDeclaration(ParseNode* importSpecSet, @@ -513,8 +509,12 @@ class FullParseHandler return pn; } + ParseNode* newImportSpec(ParseNode* importNameNode, ParseNode* bindingName) { + return newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName); + } + ParseNode* newExportDeclaration(ParseNode* kid, const TokenPos& pos) { - return new_(PNK_EXPORT, JSOP_NOP, pos, kid); + return new_(PNK_EXPORT, pos, kid); } ParseNode* newExportFromDeclaration(uint32_t begin, ParseNode* exportSpecSet, @@ -532,15 +532,23 @@ class FullParseHandler return new_(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid, maybeBinding); } + ParseNode* newExportSpec(ParseNode* bindingName, ParseNode* exportName) { + return newBinary(PNK_EXPORT_SPEC, bindingName, exportName); + } + + ParseNode* newExportBatchSpec(const TokenPos& pos) { + return new_(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos); + } + ParseNode* newExprStatement(ParseNode* expr, uint32_t end) { MOZ_ASSERT(expr->pn_pos.end <= end); - return new_(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr); + return new_(PNK_SEMI, TokenPos(expr->pn_pos.begin, end), expr); } ParseNode* newIfStatement(uint32_t begin, ParseNode* cond, ParseNode* thenBranch, ParseNode* elseBranch) { - ParseNode* pn = new_(PNK_IF, JSOP_NOP, cond, thenBranch, elseBranch); + ParseNode* pn = new_(PNK_IF, cond, thenBranch, elseBranch); if (!pn) return null(); pn->pn_pos.begin = begin; @@ -590,14 +598,14 @@ class FullParseHandler ParseNode* newForHead(ParseNode* init, ParseNode* test, ParseNode* update, const TokenPos& pos) { - return new_(PNK_FORHEAD, JSOP_NOP, init, test, update, pos); + return new_(PNK_FORHEAD, init, test, update, pos); } ParseNode* newForInOrOfHead(ParseNodeKind kind, ParseNode* target, ParseNode* iteratedExpr, const TokenPos& pos) { MOZ_ASSERT(kind == PNK_FORIN || kind == PNK_FOROF); - return new_(kind, JSOP_NOP, target, nullptr, iteratedExpr, pos); + return new_(kind, target, nullptr, iteratedExpr, pos); } ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) { @@ -619,11 +627,11 @@ class FullParseHandler ParseNode* newReturnStatement(ParseNode* expr, const TokenPos& pos) { MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos)); - return new_(PNK_RETURN, JSOP_RETURN, pos, expr); + return new_(PNK_RETURN, pos, expr); } ParseNode* newExpressionBody(ParseNode* expr) { - return new_(PNK_RETURN, JSOP_RETURN, expr->pn_pos, expr); + return new_(PNK_RETURN, expr->pn_pos, expr); } ParseNode* newWithStatement(uint32_t begin, ParseNode* expr, ParseNode* body) { @@ -637,13 +645,13 @@ class FullParseHandler ParseNode* newThrowStatement(ParseNode* expr, const TokenPos& pos) { MOZ_ASSERT(pos.encloses(expr->pn_pos)); - return new_(PNK_THROW, JSOP_THROW, pos, expr); + return new_(PNK_THROW, pos, expr); } ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchList, ParseNode* finallyBlock) { TokenPos pos(begin, (finallyBlock ? finallyBlock : catchList)->pn_pos.end); - return new_(PNK_TRY, JSOP_NOP, body, catchList, finallyBlock, pos); + return new_(PNK_TRY, body, catchList, finallyBlock, pos); } ParseNode* newDebuggerStatement(const TokenPos& pos) { @@ -717,7 +725,7 @@ class FullParseHandler } Node newNewExpression(uint32_t begin, ParseNode* ctor) { - ParseNode* newExpr = newList(PNK_NEW, begin, JSOP_NEW); + ParseNode* newExpr = new_(PNK_NEW, JSOP_NEW, TokenPos(begin, begin + 1)); if (!newExpr) return nullptr; @@ -725,10 +733,8 @@ class FullParseHandler return newExpr; } - ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs, - JSOp op) - { - return newBinary(kind, lhs, rhs, op); + ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) { + return newBinary(kind, lhs, rhs); } bool isUnparenthesizedYieldExpression(ParseNode* node) { @@ -800,28 +806,28 @@ class FullParseHandler return kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST; } - ParseNode* newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) { + ParseNode* newList(ParseNodeKind kind, const TokenPos& pos) { MOZ_ASSERT(!isDeclarationKind(kind)); - return new_(kind, op, pos); + return new_(kind, JSOP_NOP, pos); } private: - ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { - return newList(kind, TokenPos(begin, begin + 1), op); + ParseNode* newList(ParseNodeKind kind, uint32_t begin) { + return newList(kind, TokenPos(begin, begin + 1)); } template - ParseNode* newList(ParseNodeKind kind, const T& begin, JSOp op = JSOP_NOP) = delete; + ParseNode* newList(ParseNodeKind kind, const T& begin) = delete; public: - ParseNode* newList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { + ParseNode* newList(ParseNodeKind kind, ParseNode* kid) { MOZ_ASSERT(!isDeclarationKind(kind)); - return new_(kind, op, kid); + return new_(kind, JSOP_NOP, kid); } - ParseNode* newDeclarationList(ParseNodeKind kind, const TokenPos& pos, JSOp op) { + ParseNode* newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { MOZ_ASSERT(isDeclarationKind(kind)); - return new_(kind, op, pos); + return new_(kind, JSOP_NOP, pos); } bool isDeclarationList(ParseNode* node) { @@ -839,7 +845,7 @@ class FullParseHandler } ParseNode* newCommaExpressionList(ParseNode* kid) { - return newList(PNK_COMMA, kid, JSOP_NOP); + return new_(PNK_COMMA, JSOP_NOP, kid); } void addList(ParseNode* list, ParseNode* kid) { @@ -943,7 +949,7 @@ FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody) { - ParseNode* catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody); + ParseNode* catchpn = new_(PNK_CATCH, catchName, catchGuard, catchBody); if (!catchpn) return false; catchList->append(lexicalScope); @@ -956,7 +962,7 @@ FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue) { ParseNode* arg = funcpn->pn_body->last(); - ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue, JSOP_NOP); + ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue); if (!pn) return false; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 48c9dae6e71c..7d228a1ca39e 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -571,7 +571,7 @@ ParseNodeAllocator::allocNode() } ParseNode* -ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right, +ParseNode::appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right, FullParseHandler* handler, ParseContext* pc) { // The asm.js specification is written in ECMAScript grammar terms that @@ -593,9 +593,7 @@ ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, Pars // processed with a left fold because (+) is left-associative. // if (left->isKind(kind) && - left->isOp(op) && - (CodeSpec[op].format & JOF_LEFTASSOC || - (kind == PNK_POW && !left->pn_parens))) + (kind == PNK_POW ? !left->pn_parens : left->isBinaryOperation())) { ListNode* list = &left->as(); @@ -606,7 +604,7 @@ ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, Pars } } - ParseNode* list = handler->new_(kind, op, left); + ParseNode* list = handler->new_(kind, JSOP_NOP, left); if (!list) return nullptr; diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 15d92560bdcf..2eb3e7d6144d 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -267,7 +267,7 @@ IsTypeofKind(ParseNodeKind kind) * PNK_FORHEAD ternary pn_kid1: init expr before first ';' or nullptr * pn_kid2: cond expr before second ';' or nullptr * pn_kid3: update expr after second ';' or nullptr - * PNK_THROW unary pn_op: JSOP_THROW, pn_kid: exception + * PNK_THROW unary pn_kid: exception * PNK_TRY ternary pn_kid1: try block * pn_kid2: null or PNK_CATCHLIST list * pn_kid3: null or finally block @@ -347,8 +347,8 @@ IsTypeofKind(ParseNodeKind kind) * PNK_STAR, * PNK_DIV, * PNK_MOD, - * PNK_POW (**) is right-associative, but forms a list - * nonetheless. Special hacks everywhere. + * PNK_POW (**) is right-associative, but still forms a list. + * See comments in ParseNode::appendOrCreateList. * * PNK_POS, unary pn_kid: UNARY expr * PNK_NEG @@ -427,8 +427,7 @@ IsTypeofKind(ParseNodeKind kind) * pn_head: list of 1 element, which is block * enclosing for loop(s) and optionally * if-guarded PNK_ARRAYPUSH - * PNK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP - * pn_kid: array comprehension expression + * PNK_ARRAYPUSH unary pn_kid: array comprehension expression * PNK_NOP nullary */ enum ParseNodeArity @@ -614,7 +613,7 @@ class ParseNode * |right| to it and return |left|. Otherwise return a [left, right] list. */ static ParseNode* - appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right, + appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right, FullParseHandler* handler, ParseContext* pc); inline PropertyName* name() const; @@ -849,8 +848,8 @@ struct NullaryNode : public ParseNode struct UnaryNode : public ParseNode { - UnaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* kid) - : ParseNode(kind, op, PN_UNARY, pos) + UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid) + : ParseNode(kind, JSOP_NOP, PN_UNARY, pos) { pn_kid = kid; } @@ -883,8 +882,8 @@ struct BinaryNode : public ParseNode struct TernaryNode : public ParseNode { - TernaryNode(ParseNodeKind kind, JSOp op, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3) - : ParseNode(kind, op, PN_TERNARY, + TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3) + : ParseNode(kind, JSOP_NOP, PN_TERNARY, TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin, (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end)) { @@ -893,9 +892,9 @@ struct TernaryNode : public ParseNode pn_kid3 = kid3; } - TernaryNode(ParseNodeKind kind, JSOp op, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3, + TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3, const TokenPos& pos) - : ParseNode(kind, op, PN_TERNARY, pos) + : ParseNode(kind, JSOP_NOP, PN_TERNARY, pos) { pn_kid1 = kid1; pn_kid2 = kid2; @@ -1143,7 +1142,7 @@ class ThisLiteral : public UnaryNode { public: ThisLiteral(const TokenPos& pos, ParseNode* thisName) - : UnaryNode(PNK_THIS, JSOP_NOP, pos, thisName) + : UnaryNode(PNK_THIS, pos, thisName) { } }; @@ -1320,7 +1319,7 @@ struct ClassNames : public BinaryNode { struct ClassNode : public TernaryNode { ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock, const TokenPos& pos) - : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos) + : TernaryNode(PNK_CLASS, names, heritage, methodsOrBlock, pos) { MOZ_ASSERT_IF(names, names->is()); MOZ_ASSERT(methodsOrBlock->is() || @@ -1390,7 +1389,6 @@ ParseNode::isConstant() return true; case PNK_ARRAY: case PNK_OBJECT: - MOZ_ASSERT(isOp(JSOP_NEWINIT)); return !(pn_xflags & PNX_NONCONST); default: return false; @@ -1426,6 +1424,27 @@ enum ParseReportKind ParseStrictError }; +enum class AccessorType { + None, + Getter, + Setter +}; + +inline JSOp +AccessorTypeToJSOp(AccessorType atype) +{ + switch (atype) { + case AccessorType::None: + return JSOP_INITPROP; + case AccessorType::Getter: + return JSOP_INITPROP_GETTER; + case AccessorType::Setter: + return JSOP_INITPROP_SETTER; + default: + MOZ_CRASH("unexpected accessor type"); + } +} + enum FunctionSyntaxKind { Expression, diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0db67cafcc90..19eac8274f21 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4439,7 +4439,7 @@ Parser::bindingInitializer(Node lhs, DeclarationKind kind, handler.checkAndSetIsDirectRHSAnonFunction(rhs); - Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs); if (!assign) return null(); @@ -4834,7 +4834,7 @@ Parser::declarationPattern(Node decl, DeclarationKind declK tokenStream.addModifierException(TokenStream::OperandIsNone); } - return handler.newBinary(PNK_ASSIGN, pattern, init); + return handler.newAssignment(PNK_ASSIGN, pattern, init); } template @@ -5015,26 +5015,22 @@ Parser::declarationList(YieldHandling yieldHandling, { MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST); - JSOp op; DeclarationKind declKind; switch (kind) { case PNK_VAR: - op = JSOP_DEFVAR; declKind = DeclarationKind::Var; break; case PNK_CONST: - op = JSOP_DEFCONST; declKind = DeclarationKind::Const; break; case PNK_LET: - op = JSOP_DEFLET; declKind = DeclarationKind::Let; break; default: MOZ_CRASH("Unknown declaration kind"); } - Node decl = handler.newDeclarationList(kind, pos(), op); + Node decl = handler.newDeclarationList(kind, pos()); if (!decl) return null(); @@ -5156,7 +5152,7 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, if (!importNameNode) return false; - Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName); + Node importSpec = handler.newImportSpec(importNameNode, bindingName); if (!importSpec) return false; @@ -5202,7 +5198,7 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, // environment. pc->varScope().lookupDeclaredName(bindingName)->value()->setClosedOver(); - Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingNameNode); + Node importSpec = handler.newImportSpec(importName, bindingNameNode); if (!importSpec) return false; @@ -5269,7 +5265,7 @@ Parser::importDeclaration() if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) return null(); - Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); + Node importSpec = handler.newImportSpec(importName, bindingName); if (!importSpec) return null(); @@ -5494,7 +5490,7 @@ Parser::exportBatch(uint32_t begin) // Handle the form |export *| by adding a special export batch // specifier to the list. - Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); + Node exportSpec = handler.newExportBatchSpec(pos()); if (!exportSpec) return null(); @@ -5575,7 +5571,7 @@ Parser::exportClause(uint32_t begin) if (!checkExportedNameForClause(exportName)) return null(); - Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); + Node exportSpec = handler.newExportSpec(bindingName, exportName); if (!exportSpec) return null(); @@ -7214,16 +7210,16 @@ Parser::debuggerStatement() return handler.newDebuggerStatement(p); } -static JSOp -JSOpFromPropertyType(PropertyType propType) +static AccessorType +ToAccessorType(PropertyType propType) { switch (propType) { case PropertyType::Getter: case PropertyType::GetterNoExpressionClosure: - return JSOP_INITPROP_GETTER; + return AccessorType::Getter; case PropertyType::Setter: case PropertyType::SetterNoExpressionClosure: - return JSOP_INITPROP_SETTER; + return AccessorType::Setter; case PropertyType::Normal: case PropertyType::Method: case PropertyType::GeneratorMethod: @@ -7231,7 +7227,7 @@ JSOpFromPropertyType(PropertyType propType) case PropertyType::AsyncGeneratorMethod: case PropertyType::Constructor: case PropertyType::DerivedConstructor: - return JSOP_INITPROP; + return AccessorType::None; default: MOZ_CRASH("unexpected property type"); } @@ -7408,8 +7404,8 @@ Parser::classDefinition(YieldHandling yieldHandling, handler.checkAndSetIsDirectRHSAnonFunction(fn); - JSOp op = JSOpFromPropertyType(propType); - if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic)) + AccessorType atype = ToAccessorType(propType); + if (!handler.addClassMethodDefinition(classMethods, propName, fn, atype, isStatic)) return null(); } @@ -7993,41 +7989,6 @@ Parser::expr(InHandling inHandling, YieldHandling yieldHand return seq; } -static const JSOp ParseNodeKindToJSOp[] = { - JSOP_OR, - JSOP_AND, - JSOP_BITOR, - JSOP_BITXOR, - JSOP_BITAND, - JSOP_STRICTEQ, - JSOP_EQ, - JSOP_STRICTNE, - JSOP_NE, - JSOP_LT, - JSOP_LE, - JSOP_GT, - JSOP_GE, - JSOP_INSTANCEOF, - JSOP_IN, - JSOP_LSH, - JSOP_RSH, - JSOP_URSH, - JSOP_ADD, - JSOP_SUB, - JSOP_MUL, - JSOP_DIV, - JSOP_MOD, - JSOP_POW -}; - -static inline JSOp -BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk) -{ - MOZ_ASSERT(pnk >= PNK_BINOP_FIRST); - MOZ_ASSERT(pnk <= PNK_BINOP_LAST); - return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST]; -} - static ParseNodeKind BinaryOpTokenKindToParseNodeKind(TokenKind tok) { @@ -8134,8 +8095,7 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldH while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { depth--; ParseNodeKind combiningPnk = kindStack[depth]; - JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk); - pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp); + pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc); if (!pn) return pn; } @@ -8290,21 +8250,20 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yie } ParseNodeKind kind; - JSOp op; switch (tokenStream.currentToken().type) { - case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break; - case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break; - case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break; - case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break; - case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break; - case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break; - case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break; - case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break; - case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break; - case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break; - case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break; - case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break; - case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break; + case TOK_ASSIGN: kind = PNK_ASSIGN; break; + case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; break; + case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; break; + case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; break; + case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; break; + case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; break; + case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; break; + case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; break; + case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; break; + case TOK_MULASSIGN: kind = PNK_MULASSIGN; break; + case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; break; + case TOK_MODASSIGN: kind = PNK_MODASSIGN; break; + case TOK_POWASSIGN: kind = PNK_POWASSIGN; break; case TOK_ARROW: { @@ -8446,7 +8405,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yie if (kind == PNK_ASSIGN) handler.checkAndSetIsDirectRHSAnonFunction(rhs); - return handler.newAssignment(kind, lhs, rhs, op); + return handler.newAssignment(kind, lhs, rhs); } template @@ -8506,13 +8465,13 @@ Parser::checkIncDecOperand(Node operand, uint32_t operandOf template typename ParseHandler::Node -Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, +Parser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin) { Node kid = unaryExpr(yieldHandling, TripledotProhibited); if (!kid) return null(); - return handler.newUnary(kind, op, begin, kid); + return handler.newUnary(kind, begin, kid); } template @@ -8531,15 +8490,15 @@ Parser::unaryExpr(YieldHandling yieldHandling, uint32_t begin = pos().begin; switch (tt) { case TOK_VOID: - return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin); + return unaryOpExpr(yieldHandling, PNK_VOID, begin); case TOK_NOT: - return unaryOpExpr(yieldHandling, PNK_NOT, JSOP_NOT, begin); + return unaryOpExpr(yieldHandling, PNK_NOT, begin); case TOK_BITNOT: - return unaryOpExpr(yieldHandling, PNK_BITNOT, JSOP_BITNOT, begin); + return unaryOpExpr(yieldHandling, PNK_BITNOT, begin); case TOK_ADD: - return unaryOpExpr(yieldHandling, PNK_POS, JSOP_POS, begin); + return unaryOpExpr(yieldHandling, PNK_POS, begin); case TOK_SUB: - return unaryOpExpr(yieldHandling, PNK_NEG, JSOP_NEG, begin); + return unaryOpExpr(yieldHandling, PNK_NEG, begin); case TOK_TYPEOF: { // The |typeof| operator is specially parsed to distinguish its @@ -8930,13 +8889,7 @@ Parser::generatorComprehension(uint32_t begin) if (!genfn) return null(); - Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL); - if (!result) - return null(); - handler.setBeginPosition(result, begin); - handler.setEndPosition(result, pos().end); - - return result; + return handler.newGeneratorComprehension(genfn, TokenPos(begin, pos().end)); } template @@ -9141,7 +9094,7 @@ Parser::memberExpr(YieldHandling yieldHandling, return null(); } - nextMember = handler.newList(PNK_SUPERCALL, lhs, JSOP_SUPERCALL); + nextMember = handler.newSuperCall(lhs); if (!nextMember) return null(); @@ -10099,7 +10052,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, handler.checkAndSetIsDirectRHSAnonFunction(rhs); - Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs); if (!propExpr) return null(); @@ -10123,8 +10076,8 @@ Parser::objectLiteral(YieldHandling yieldHandling, handler.checkAndSetIsDirectRHSAnonFunction(fn); - JSOp op = JSOpFromPropertyType(propType); - if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) + AccessorType atype = ToAccessorType(propType); + if (!handler.addObjectMethodDefinition(literal, propName, fn, atype)) return null(); if (possibleError) { diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 8c7ee3e7594d..283153906dda 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -781,7 +781,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionBodyType type); - Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin); + Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin); Node condition(InHandling inHandling, YieldHandling yieldHandling); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 460a58f5cc4b..27206f6befff 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -237,11 +237,7 @@ class SyntaxParseHandler return NodeUnparenthesizedUnary; } - Node newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { - return NodeGeneric; - } - - Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) { + Node newUnary(ParseNodeKind kind, uint32_t begin, Node kid) { return NodeUnparenthesizedUnary; } @@ -257,20 +253,13 @@ class SyntaxParseHandler return NodeGeneric; } - Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) { - return NodeGeneric; - } - Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, - ParseContext* pc, JSOp op = JSOP_NOP) { - return NodeGeneric; - } - - Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) { + Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) { return NodeGeneric; } // Expressions + Node newGeneratorComprehension(Node genfn, const TokenPos& pos) { return NodeGeneric; } Node newArrayComprehension(Node body, const TokenPos& pos) { return NodeGeneric; } Node newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; } MOZ_MUST_USE bool addElision(Node literal, const TokenPos& pos) { return true; } @@ -278,6 +267,7 @@ class SyntaxParseHandler void addArrayElement(Node literal, Node element) { } Node newCall(const TokenPos& pos) { return NodeFunctionCall; } + Node newSuperCall(Node callee) { return NodeGeneric; } Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; } Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } @@ -293,8 +283,8 @@ class SyntaxParseHandler MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; } MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } - MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } - MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } + MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, AccessorType atype) { return true; } + MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, AccessorType atype, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; } Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; } Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; } @@ -317,6 +307,12 @@ class SyntaxParseHandler Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) { return NodeGeneric; } + Node newExportSpec(Node bindingName, Node exportName) { + return NodeGeneric; + } + Node newExportBatchSpec(const TokenPos& pos) { + return NodeGeneric; + } Node newSetThis(Node thisName, Node value) { return value; } @@ -410,27 +406,18 @@ class SyntaxParseHandler return ts.currentToken().pos.begin; } - Node newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) { + Node newList(ParseNodeKind kind, const TokenPos& pos) { MOZ_ASSERT(kind != PNK_VAR); MOZ_ASSERT(kind != PNK_LET); MOZ_ASSERT(kind != PNK_CONST); return NodeGeneric; } - private: - Node newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { - return newList(kind, TokenPos(begin, begin + 1), op); + Node newList(ParseNodeKind kind, Node kid) { + return newList(kind, TokenPos()); } - template - Node newList(ParseNodeKind kind, const T& begin, JSOp op = JSOP_NOP) = delete; - - public: - Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) { - return newList(kind, TokenPos(), op); - } - - Node newDeclarationList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) { + Node newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { if (kind == PNK_VAR) return NodeVarDeclaration; MOZ_ASSERT(kind == PNK_LET || kind == PNK_CONST); @@ -462,7 +449,7 @@ class SyntaxParseHandler } Node newCatchList(const TokenPos& pos) { - return newList(PNK_CATCHLIST, pos, JSOP_NOP); + return NodeGeneric; } Node newCommaExpressionList(Node kid) { @@ -480,18 +467,11 @@ class SyntaxParseHandler } Node newNewExpression(uint32_t begin, Node ctor) { - Node newExpr = newList(PNK_NEW, begin, JSOP_NEW); - if (!newExpr) - return newExpr; - - addList(newExpr, ctor); - return newExpr; + return NodeGeneric; } - Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs, JSOp op) { - if (kind == PNK_ASSIGN) - return NodeUnparenthesizedAssignment; - return newBinary(kind, lhs, rhs, op); + Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs) { + return kind == PNK_ASSIGN ? NodeUnparenthesizedAssignment : NodeGeneric; } bool isUnparenthesizedCommaExpression(Node node) { diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index 4b1ff3ea607b..651f0bbe72c2 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -428,7 +428,7 @@ GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind, // Trigger an incremental slice if needed. if (checkThresholds) - maybeAllocTriggerZoneGC(zone, lock); + maybeAllocTriggerGC(zone, lock); return arena; } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 814ec5d1389f..b048c72a6120 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -167,14 +167,14 @@ class GCSchedulingTunables * * Fraction of threshold.gcBytes() which triggers an incremental GC. */ - UnprotectedData zoneAllocThresholdFactor_; + UnprotectedData allocThresholdFactor_; /* * JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT * * The same except when doing so would interrupt an already running GC. */ - UnprotectedData zoneAllocThresholdFactorAvoidInterrupt_; + UnprotectedData allocThresholdFactorAvoidInterrupt_; /* * Number of bytes to allocate between incremental slices in GCs triggered @@ -251,8 +251,8 @@ class GCSchedulingTunables size_t gcMaxBytes() const { return gcMaxBytes_; } size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; } size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; } - float zoneAllocThresholdFactor() const { return zoneAllocThresholdFactor_; } - float zoneAllocThresholdFactorAvoidInterrupt() const { return zoneAllocThresholdFactorAvoidInterrupt_; } + float allocThresholdFactor() const { return allocThresholdFactor_; } + float allocThresholdFactorAvoidInterrupt() const { return allocThresholdFactorAvoidInterrupt_; } size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; } bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; } uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; } @@ -745,7 +745,7 @@ class GCRuntime uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock); MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason); - void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock); + void maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock); // The return value indicates if we were able to do the GC. bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason, size_t usedBytes, size_t thresholdBytes); diff --git a/js/src/jit-test/tests/basic/bug1405820.js b/js/src/jit-test/tests/basic/bug1405820.js new file mode 100644 index 000000000000..58594d77a524 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1405820.js @@ -0,0 +1,5 @@ +// |jit-test| error: Error + +var g = newGlobal(); +g.f = setJitCompilerOption; +g.eval("clone(f)()(9)") diff --git a/js/src/jit-test/tests/basic/testTypedArrayInit.js b/js/src/jit-test/tests/basic/testTypedArrayInit.js index 1d85017d1ceb..34bffa39393e 100644 --- a/js/src/jit-test/tests/basic/testTypedArrayInit.js +++ b/js/src/jit-test/tests/basic/testTypedArrayInit.js @@ -11,7 +11,9 @@ function f() { Float32Array, Float64Array ]) { - for (var len of [ 3, 30, 300, 3000, 30000 ]) { + for (var len of [ 3, 30, 300, 3000 ]) { + // TODO: disabled before follow-up in bug 1406041 + //for (var len of [ 3, 30, 300, 3000, 30000 ]) { var arr = new ctor(len); for (var i = 0; i < arr.length; i++) assertEq(arr[i], 0, "index " + i + " of " + ctor.name + " len " + len); diff --git a/js/src/jit-test/tests/debug/bug1385843.js b/js/src/jit-test/tests/debug/bug1385843.js new file mode 100644 index 000000000000..f7d4810e69b1 --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1385843.js @@ -0,0 +1,22 @@ +var g = newGlobal(); +g.parent = this; +g.count = 0; +g.eval("(" + function() { + var dbg = new Debugger(parent); + dbg.onEnterFrame = function(frame) { + if (count === 5) + dbg.onEnterFrame = undefined; + count++; + var ex = frame.eval("this").throw.unsafeDereference(); + assertEq(ex.message.includes("uninitialized"), true); + assertEq(ex.message.includes("Foo2"), true); + } +} + ")()"); +class Foo1 {}; +class Foo2 extends Foo1 { + constructor() { + super(); + } +}; +new Foo2(); +assertEq(g.count, 6); diff --git a/js/src/jit-test/tests/wasm/regress/onlyjsiter-while-wasm.js b/js/src/jit-test/tests/wasm/regress/onlyjsiter-while-wasm.js new file mode 100644 index 000000000000..590f237e6959 --- /dev/null +++ b/js/src/jit-test/tests/wasm/regress/onlyjsiter-while-wasm.js @@ -0,0 +1,14 @@ +if (typeof evalInCooperativeThread === 'undefined') + quit(); + +try { + evalInCooperativeThread(` + var { f } = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(\` + (module + (func $f (export "f") call $f) + ) + \`))).exports; + gczeal(9); + f(); + `); +} catch(e) {} diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 4ff3cd3a7811..fbb3606ef98d 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -368,19 +368,10 @@ jit::CanEnterBaselineMethod(JSContext* cx, RunState& state) { if (state.isInvoke()) { InvokeState& invoke = *state.asInvoke(); - if (invoke.args().length() > BASELINE_MAX_ARGS_LENGTH) { JitSpew(JitSpew_BaselineAbort, "Too many arguments (%u)", invoke.args().length()); return Method_CantCompile; } - - if (!state.maybeCreateThisForConstructor(cx)) { - if (cx->isThrowingOutOfMemory()) { - cx->recoverFromOutOfMemory(); - return Method_Skipped; - } - return Method_Error; - } } else { if (state.asExecute()->isDebuggerEval()) { JitSpew(JitSpew_BaselineAbort, "debugger frame"); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ec4099fa1808..64494313aa2a 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2238,7 +2238,8 @@ IonCompile(JSContext* cx, JSScript* script, if (!CreateMIRRootList(*builder)) return AbortReason::Alloc; - if (!StartOffThreadIonCompile(cx, builder)) { + AutoLockHelperThreadState lock; + if (!StartOffThreadIonCompile(cx, builder, lock)) { JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation."); builder->graphSpewer().endFunction(); return AbortReason::Alloc; @@ -2521,14 +2522,6 @@ jit::CanEnter(JSContext* cx, RunState& state) ForbidCompilation(cx, script); return Method_CantCompile; } - - if (!state.maybeCreateThisForConstructor(cx)) { - if (cx->isThrowingOutOfMemory()) { - cx->recoverFromOutOfMemory(); - return Method_Skipped; - } - return Method_Error; - } } // If --ion-eager is used, compile with Baseline first, so that we @@ -2539,11 +2532,8 @@ jit::CanEnter(JSContext* cx, RunState& state) return status; } - // Skip if the script is being compiled off thread or can't be - // Ion-compiled (again). MaybeCreateThisForConstructor could have - // started an Ion compilation or marked the script as uncompilable. - if (script->isIonCompilingOffThread() || !script->canIonCompile()) - return Method_Skipped; + MOZ_ASSERT(!script->isIonCompilingOffThread()); + MOZ_ASSERT(script->canIonCompile()); // Attempt compilation. Returns Method_Compiled if already compiled. MethodStatus status = Compile(cx, script, nullptr, nullptr); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 7a9933b4be4a..4bc0c6d35ec5 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -628,14 +628,8 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa JSScript* script = JSFunction::getOrCreateScript(cx, fun); if (!script || !script->ensureHasTypes(cx)) return false; - if (fun->isBoundFunction() || script->isDerivedClassConstructor()) { - rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL)); - } else { - JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject); - if (!thisObj) - return false; - rval.set(ObjectValue(*thisObj)); - } + if (!js::CreateThis(cx, fun, script, newTarget, GenericObject, rval)) + return false; } } diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp index 245e41e49f16..68b722666b71 100644 --- a/js/src/jit/arm/Simulator-arm.cpp +++ b/js/src/jit/arm/Simulator-arm.cpp @@ -1596,7 +1596,7 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes) void* pc = reinterpret_cast(get_pc()); uint8_t* fp = reinterpret_cast(get_register(r11)); - const wasm::CodeSegment* segment = act->compartment()->wasm.lookupCodeSegment(pc); + const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc); if (!segment) return false; diff --git a/js/src/jit/mips32/Simulator-mips32.cpp b/js/src/jit/mips32/Simulator-mips32.cpp index 155ab96d579e..e202f771c7bc 100644 --- a/js/src/jit/mips32/Simulator-mips32.cpp +++ b/js/src/jit/mips32/Simulator-mips32.cpp @@ -1641,7 +1641,7 @@ Simulator::handleWasmInterrupt() void* fp = (void*)getRegister(Register::fp); JitActivation* activation = TlsContext.get()->activation()->asJit(); - const wasm::CodeSegment* segment = activation->compartment()->wasm.lookupCodeSegment(pc); + const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc); if (!segment || !segment->containsCodePC(pc)) return; @@ -1671,7 +1671,7 @@ Simulator::handleWasmFault(int32_t addr, unsigned numBytes) void* pc = reinterpret_cast(get_pc()); uint8_t* fp = reinterpret_cast(getRegister(Register::fp)); - const wasm::CodeSegment* segment = act->compartment()->wasm.lookupCodeSegment(pc); + const wasm::CodeSegment* segment = wasm::LookupCodeSegment(pc); if (!segment) return false; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index d9156419f906..9fe82e77cdf7 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -847,7 +847,7 @@ ReleaseAssertObjectHasNoWrappers(JSContext* cx, HandleObject target) RootedValue origv(cx, ObjectValue(*target)); for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { - if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) + if (c->lookupWrapper(origv)) MOZ_CRASH("wrapper found for target object"); } } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 8c754b5fb859..0ba8102aec54 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1436,6 +1436,9 @@ JS_RefreshCrossCompartmentWrappers(JSContext* cx, JS::Handle obj); * enter/leave calls on the context. Furthermore, only the return value of a * JS_EnterCompartment call may be passed as the 'oldCompartment' argument of * the corresponding JS_LeaveCompartment call. + * + * Entering a compartment roots the compartment and its global object for the + * lifetime of the JSAutoCompartment. */ class MOZ_RAII JS_PUBLIC_API(JSAutoCompartment) @@ -1464,7 +1467,11 @@ class MOZ_RAII JS_PUBLIC_API(JSAutoNullableCompartment) MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; -/** NB: This API is infallible; a nullptr return value does not indicate error. */ +/** NB: This API is infallible; a nullptr return value does not indicate error. + * + * Entering a compartment roots the compartment and its global object until the + * matching JS_LeaveCompartment() call. + */ extern JS_PUBLIC_API(JSCompartment*) JS_EnterCompartment(JSContext* cx, JSObject* target); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 4b8e6119bd3b..0125589dbf1f 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -491,14 +491,6 @@ js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format, void js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg) { - const char* usageStr = "usage"; - PropertyName* usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName(); - RootedId id(cx, NameToId(usageAtom)); - DebugOnly shape = static_cast(callee->as().lookup(cx, id)); - MOZ_ASSERT(!shape->configurable()); - MOZ_ASSERT(!shape->writable()); - MOZ_ASSERT(shape->hasDefaultGetter()); - RootedValue usage(cx); if (!JS_GetProperty(cx, callee, "usage", &usage)) return; diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 576c27ef2a62..c2e1d28c9b95 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -55,9 +55,7 @@ class CompartmentChecker void check(JSCompartment* c) { if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) { - if (!compartment) - compartment = c; - else if (c != compartment) + if (c != compartment) fail(compartment, c); } } diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 76f93686ed89..5e5b50edb363 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -483,7 +483,14 @@ LocalTime(double t) static double UTC(double t) { - return t - AdjustTime(t - DateTimeInfo::localTZA()); + // Following the ES2017 specification creates undesirable results at DST + // transitions. For example when transitioning from PST to PDT, + // |new Date(2016,2,13,2,0,0).toTimeString()| returns the string value + // "01:00:00 GMT-0800 (PST)" instead of "03:00:00 GMT-0700 (PDT)". Follow + // V8 and subtract one hour before computing the offset. + // Spec bug: https://bugs.ecmascript.org/show_bug.cgi?id=4007 + + return t - AdjustTime(t - DateTimeInfo::localTZA() - msPerHour); } /* ES5 15.9.1.10. */ @@ -2584,7 +2591,7 @@ date_toJSON(JSContext* cx, unsigned argc, Value* vp) /* Interface to PRMJTime date struct. */ static PRMJTime -ToPRMJTime(double localTime) +ToPRMJTime(double localTime, double utcTime) { double year = YearFromTime(localTime); @@ -2598,9 +2605,7 @@ ToPRMJTime(double localTime) prtm.tm_wday = int8_t(WeekDay(localTime)); prtm.tm_year = year; prtm.tm_yday = int16_t(DayWithinYear(localTime, year)); - - // XXX: DaylightSavingTA expects utc-time argument. - prtm.tm_isdst = (DaylightSavingTA(localTime) != 0); + prtm.tm_isdst = (DaylightSavingTA(utcTime) != 0); return prtm; } @@ -2647,7 +2652,7 @@ FormatDate(JSContext* cx, double utcTime, FormatSpec format, MutableHandleValue */ /* get a time zone string from the OS to include as a comment. */ - PRMJTime prtm = ToPRMJTime(utcTime); + PRMJTime prtm = ToPRMJTime(localTime, utcTime); size_t tzlen = PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &prtm); if (tzlen != 0) { /* @@ -2727,7 +2732,7 @@ ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, Mutabl strcpy(buf, js_NaN_date_str); } else { double localTime = LocalTime(utcTime); - PRMJTime prtm = ToPRMJTime(localTime); + PRMJTime prtm = ToPRMJTime(localTime, utcTime); /* Let PRMJTime format it. */ size_t result_len = PRMJ_FormatTime(buf, sizeof buf, format, &prtm); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index d25cb798ba9d..15221f73936b 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -80,13 +80,17 @@ fun_enumerate(JSContext* cx, HandleObject obj) return false; } - id = NameToId(cx->names().length); - if (!HasOwnProperty(cx, obj, id, &found)) - return false; + if (!obj->as().hasResolvedLength()) { + id = NameToId(cx->names().length); + if (!HasOwnProperty(cx, obj, id, &found)) + return false; + } - id = NameToId(cx->names().name); - if (!HasOwnProperty(cx, obj, id, &found)) - return false; + if (!obj->as().hasResolvedName()) { + id = NameToId(cx->names().name); + if (!HasOwnProperty(cx, obj, id, &found)) + return false; + } return true; } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 7c7492e4a90b..e76d8fa48032 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -280,11 +280,14 @@ namespace TuningDefaults { /* JSGC_ALLOCATION_THRESHOLD */ static const size_t GCZoneAllocThresholdBase = 30 * 1024 * 1024; + /* JSGC_MAX_MALLOC_BYTES */ + static const size_t MaxMallocBytes = 128 * 1024 * 1024; + /* JSGC_ALLOCATION_THRESHOLD_FACTOR */ - static const float ZoneAllocThresholdFactor = 0.9f; + static const float AllocThresholdFactor = 0.9f; /* JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT */ - static const float ZoneAllocThresholdFactorAvoidInterrupt = 0.9f; + static const float AllocThresholdFactorAvoidInterrupt = 0.9f; /* no parameter */ static const size_t ZoneAllocDelayBytes = 1024 * 1024; @@ -323,8 +326,7 @@ namespace TuningDefaults { static const uint32_t MaxEmptyChunkCount = 30; /* JSGC_SLICE_TIME_BUDGET */ - static const int64_t DefaultTimeBudget = - SliceBudget::UnlimitedTimeBudget; + static const int64_t DefaultTimeBudget = SliceBudget::UnlimitedTimeBudget; /* JSGC_MODE */ static const JSGCMode Mode = JSGC_MODE_INCREMENTAL; @@ -1159,13 +1161,9 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) { AutoLockGC lock(rt); - /* - * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes - * for default backward API compatibility. - */ MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock)); MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_NURSERY_BYTES, maxNurseryBytes, lock)); - setMaxMallocBytes(maxbytes, lock); + setMaxMallocBytes(TuningDefaults::MaxMallocBytes, lock); const char* size = getenv("JSGC_MARK_STACK_LIMIT"); if (size) @@ -1348,14 +1346,14 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL float newFactor = value / 100.0; if (newFactor <= 0.1 || newFactor > 1.0) return false; - zoneAllocThresholdFactor_ = newFactor; + allocThresholdFactor_ = newFactor; break; } case JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT: { float newFactor = value / 100.0; if (newFactor <= 0.1 || newFactor > 1.0) return false; - zoneAllocThresholdFactorAvoidInterrupt_ = newFactor; + allocThresholdFactorAvoidInterrupt_ = newFactor; break; } case JSGC_MIN_EMPTY_CHUNK_COUNT: @@ -1414,9 +1412,8 @@ GCSchedulingTunables::GCSchedulingTunables() : gcMaxBytes_(0), gcMaxNurseryBytes_(0), gcZoneAllocThresholdBase_(TuningDefaults::GCZoneAllocThresholdBase), - zoneAllocThresholdFactor_(TuningDefaults::ZoneAllocThresholdFactor), - zoneAllocThresholdFactorAvoidInterrupt_( - TuningDefaults::ZoneAllocThresholdFactorAvoidInterrupt), + allocThresholdFactor_(TuningDefaults::AllocThresholdFactor), + allocThresholdFactorAvoidInterrupt_(TuningDefaults::AllocThresholdFactorAvoidInterrupt), zoneAllocDelayBytes_(TuningDefaults::ZoneAllocDelayBytes), dynamicHeapGrowthEnabled_(TuningDefaults::DynamicHeapGrowthEnabled), highFrequencyThresholdUsec_(TuningDefaults::HighFrequencyThresholdUsec), @@ -1436,7 +1433,7 @@ GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) { switch (key) { case JSGC_MAX_MALLOC_BYTES: - setMaxMallocBytes(0xffffffff, lock); + setMaxMallocBytes(TuningDefaults::MaxMallocBytes, lock); break; case JSGC_SLICE_TIME_BUDGET: defaultTimeBudget_ = TuningDefaults::DefaultTimeBudget; @@ -1503,11 +1500,10 @@ GCSchedulingTunables::resetParameter(JSGCParamKey key, const AutoLockGC& lock) gcZoneAllocThresholdBase_ = TuningDefaults::GCZoneAllocThresholdBase; break; case JSGC_ALLOCATION_THRESHOLD_FACTOR: - zoneAllocThresholdFactor_ = TuningDefaults::ZoneAllocThresholdFactor; + allocThresholdFactor_ = TuningDefaults::AllocThresholdFactor; break; case JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT: - zoneAllocThresholdFactorAvoidInterrupt_ = - TuningDefaults::ZoneAllocThresholdFactorAvoidInterrupt; + allocThresholdFactorAvoidInterrupt_ = TuningDefaults::AllocThresholdFactorAvoidInterrupt; break; case JSGC_MIN_EMPTY_CHUNK_COUNT: setMinEmptyChunkCount(TuningDefaults::MinEmptyChunkCount); @@ -1570,9 +1566,9 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) case JSGC_ALLOCATION_THRESHOLD: return tunables.gcZoneAllocThresholdBase() / 1024 / 1024; case JSGC_ALLOCATION_THRESHOLD_FACTOR: - return uint32_t(tunables.zoneAllocThresholdFactor() * 100); + return uint32_t(tunables.allocThresholdFactor() * 100); case JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT: - return uint32_t(tunables.zoneAllocThresholdFactorAvoidInterrupt() * 100); + return uint32_t(tunables.allocThresholdFactorAvoidInterrupt() * 100); case JSGC_MIN_EMPTY_CHUNK_COUNT: return tunables.minEmptyChunkCount(lock); case JSGC_MAX_EMPTY_CHUNK_COUNT: @@ -3166,56 +3162,75 @@ GCRuntime::triggerGC(JS::gcreason::Reason reason) } void -GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock) +GCRuntime::maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock) { - size_t usedBytes = zone->usage.gcBytes(); - size_t thresholdBytes = zone->threshold.gcTriggerBytes(); + MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting()); if (!CurrentThreadCanAccessRuntime(rt)) { - /* Zones in use by a helper thread can't be collected. */ + // Zones in use by a helper thread can't be collected. MOZ_ASSERT(zone->usedByHelperThread() || zone->isAtomsZone()); return; } + // Check GC bytes triggers. + + size_t usedBytes = zone->usage.gcBytes(); + size_t thresholdBytes = zone->threshold.gcTriggerBytes(); + if (usedBytes >= thresholdBytes) { - /* - * The threshold has been surpassed, immediately trigger a GC, - * which will be done non-incrementally. - */ + // The threshold has been surpassed, immediately trigger a GC, which + // will be done non-incrementally. triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, thresholdBytes); - } else { - bool wouldInterruptCollection; - size_t igcThresholdBytes; - float zoneAllocThresholdFactor; + return; + } - wouldInterruptCollection = isIncrementalGCInProgress() && - !zone->isCollecting(); - zoneAllocThresholdFactor = wouldInterruptCollection ? - tunables.zoneAllocThresholdFactorAvoidInterrupt() : - tunables.zoneAllocThresholdFactor(); + bool wouldInterruptCollection = isIncrementalGCInProgress() && !zone->isCollecting(); + float zoneGCThresholdFactor = + wouldInterruptCollection ? tunables.allocThresholdFactorAvoidInterrupt() + : tunables.allocThresholdFactor(); - igcThresholdBytes = thresholdBytes * zoneAllocThresholdFactor; + size_t igcThresholdBytes = thresholdBytes * zoneGCThresholdFactor; - if (usedBytes >= igcThresholdBytes) { - // Reduce the delay to the start of the next incremental slice. - if (zone->gcDelayBytes < ArenaSize) - zone->gcDelayBytes = 0; - else - zone->gcDelayBytes -= ArenaSize; + if (usedBytes >= igcThresholdBytes) { + // Reduce the delay to the start of the next incremental slice. + if (zone->gcDelayBytes < ArenaSize) + zone->gcDelayBytes = 0; + else + zone->gcDelayBytes -= ArenaSize; - if (!zone->gcDelayBytes) { - // Start or continue an in progress incremental GC. We do this - // to try to avoid performing non-incremental GCs on zones - // which allocate a lot of data, even when incremental slices - // can't be triggered via scheduling in the event loop. - triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, igcThresholdBytes); + if (!zone->gcDelayBytes) { + // Start or continue an in progress incremental GC. We do this + // to try to avoid performing non-incremental GCs on zones + // which allocate a lot of data, even when incremental slices + // can't be triggered via scheduling in the event loop. + triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, igcThresholdBytes); - // Delay the next slice until a certain amount of allocation - // has been performed. - zone->gcDelayBytes = tunables.zoneAllocDelayBytes(); - } + // Delay the next slice until a certain amount of allocation + // has been performed. + zone->gcDelayBytes = tunables.zoneAllocDelayBytes(); + return; } } + + // Check malloc bytes triggers. + + wouldInterruptCollection = isIncrementalGCInProgress() && !isFull; + float fullGCThresholdFactor = + wouldInterruptCollection ? tunables.allocThresholdFactorAvoidInterrupt() + : tunables.allocThresholdFactor(); + + size_t mallocBytes = mallocCounter.bytes(); + size_t mallocThesholdBytes = mallocCounter.maxBytes() * fullGCThresholdFactor; + if (mallocBytes > mallocThesholdBytes) { + stats().recordTrigger(mallocBytes, mallocThesholdBytes); + MOZ_ALWAYS_TRUE(triggerGC(JS::gcreason::TOO_MUCH_MALLOC)); + return; + } + + mallocBytes = zone->GCMallocBytes(); + mallocThesholdBytes = zone->GCMaxMallocBytes() * zoneGCThresholdFactor; + if (mallocBytes > mallocThesholdBytes) + triggerZoneGC(zone, JS::gcreason::TOO_MUCH_MALLOC, mallocBytes, mallocThesholdBytes); } bool @@ -7538,7 +7553,7 @@ GCRuntime::minorGC(JS::gcreason::Reason reason, gcstats::PhaseKind phase) { AutoLockGC lock(rt); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - maybeAllocTriggerZoneGC(zone, lock); + maybeAllocTriggerGC(zone, lock); } } @@ -7739,7 +7754,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) rt->gc.mergeCompartments(source, target); AutoLockGC lock(rt); - rt->gc.maybeAllocTriggerZoneGC(target->zone(), lock); + rt->gc.maybeAllocTriggerGC(target->zone(), lock); } void diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index fcf6bfcbdf39..30b2aff64c94 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -841,6 +841,31 @@ IsConstructor(const Value& v) return v.isObject() && v.toObject().isConstructor(); } +MOZ_ALWAYS_INLINE bool +CreateThis(JSContext* cx, HandleObject callee, JSScript* calleeScript, HandleObject newTarget, + NewObjectKind newKind, MutableHandleValue thisv) +{ + if (callee->isBoundFunction()) { + thisv.setMagic(JS_UNINITIALIZED_LEXICAL); + return true; + } + + if (calleeScript->isDerivedClassConstructor()) { + MOZ_ASSERT(callee->as().isClassConstructor()); + thisv.setMagic(JS_UNINITIALIZED_LEXICAL); + return true; + } + + MOZ_ASSERT(thisv.isMagic(JS_IS_CONSTRUCTING)); + + JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind); + if (!obj) + return false; + + thisv.setObject(*obj); + return true; +} + } /* namespace js */ MOZ_ALWAYS_INLINE bool diff --git a/js/src/jsscriptinlines.h b/js/src/jsscriptinlines.h index 616d28f98acb..628138dc0cf4 100644 --- a/js/src/jsscriptinlines.h +++ b/js/src/jsscriptinlines.h @@ -182,7 +182,7 @@ JSScript::setBaselineScript(JSRuntime* maybeRuntime, js::jit::BaselineScript* ba { if (hasBaselineScript()) js::jit::BaselineScript::writeBarrierPre(zone(), baseline); - MOZ_ASSERT(!hasIonScript()); + MOZ_ASSERT(!ion || ion == ION_DISABLED_SCRIPT); baseline = baselineScript; resetWarmUpResetCounter(); updateBaselineOrIonRaw(maybeRuntime); diff --git a/js/src/moz.build b/js/src/moz.build index 32e5dd465a1f..a8d780ac9a2c 100755 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -379,6 +379,7 @@ UNIFIED_SOURCES += [ 'wasm/WasmIonCompile.cpp', 'wasm/WasmJS.cpp', 'wasm/WasmModule.cpp', + 'wasm/WasmProcess.cpp', 'wasm/WasmSignalHandlers.cpp', 'wasm/WasmStubs.cpp', 'wasm/WasmTable.cpp', diff --git a/js/src/tests/ecma_6/Date/browser.js b/js/src/tests/ecma_6/Date/browser.js index e69de29bb2d1..5665e7ed448b 100644 --- a/js/src/tests/ecma_6/Date/browser.js +++ b/js/src/tests/ecma_6/Date/browser.js @@ -0,0 +1,3 @@ +if (typeof setTimeZone === "undefined") { + var setTimeZone = SpecialPowers.Cu.getJSTestingFunctions().setTimeZone; +} diff --git a/js/src/tests/ecma_6/Date/time-zone-pst.js b/js/src/tests/ecma_6/Date/time-zone-pst.js new file mode 100644 index 000000000000..ae890ea3ab4f --- /dev/null +++ b/js/src/tests/ecma_6/Date/time-zone-pst.js @@ -0,0 +1,134 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Note: The default time zone is set to PST8PDT for all jstests (when run in the shell). + +assertEq(/^(PST|PDT)$/.test(getTimeZone()), true); + +const msPerMinute = 60 * 1000; +const msPerHour = 60 * 60 * 1000; + +const Month = { + January: 0, + February: 1, + March: 2, + April: 3, + May: 4, + June: 5, + July: 6, + August: 7, + September: 8, + October: 9, + November: 10, + December: 11, +}; + +// PDT -> PST, using milliseconds from epoch. +{ + let midnight = new Date(2016, Month.November, 6, 0, 0, 0, 0); + let midnightUTC = Date.UTC(2016, Month.November, 6, 0, 0, 0, 0); + + // Ensure midnight time is correct. + assertEq(midnightUTC - midnight.getTime(), -7 * msPerHour); + + let tests = [ + { offset: 0 * 60, date: "Sun Nov 06 2016", time: "00:00:00 GMT-0700 (PDT)" }, + { offset: 0 * 60 + 30, date: "Sun Nov 06 2016", time: "00:30:00 GMT-0700 (PDT)" }, + { offset: 1 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0700 (PDT)" }, + { offset: 1 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0700 (PDT)" }, + { offset: 2 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0800 (PST)" }, + { offset: 2 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0800 (PST)" }, + { offset: 3 * 60, date: "Sun Nov 06 2016", time: "02:00:00 GMT-0800 (PST)" }, + { offset: 3 * 60 + 30, date: "Sun Nov 06 2016", time: "02:30:00 GMT-0800 (PST)" }, + { offset: 4 * 60, date: "Sun Nov 06 2016", time: "03:00:00 GMT-0800 (PST)" }, + { offset: 4 * 60 + 30, date: "Sun Nov 06 2016", time: "03:30:00 GMT-0800 (PST)" }, + ]; + + for (let {offset, date, time} of tests) { + let dt = new Date(midnight.getTime() + offset * msPerMinute); + assertEq(dt.toString(), `${date} ${time}`); + assertEq(dt.toDateString(), date); + assertEq(dt.toTimeString(), time); + } +} + + +// PDT -> PST, using local date-time. +{ + let tests = [ + { offset: 0 * 60, date: "Sun Nov 06 2016", time: "00:00:00 GMT-0700 (PDT)" }, + { offset: 0 * 60 + 30, date: "Sun Nov 06 2016", time: "00:30:00 GMT-0700 (PDT)" }, + { offset: 1 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0700 (PDT)" }, + { offset: 1 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0700 (PDT)" }, + { offset: 2 * 60, date: "Sun Nov 06 2016", time: "02:00:00 GMT-0800 (PST)" }, + { offset: 2 * 60 + 30, date: "Sun Nov 06 2016", time: "02:30:00 GMT-0800 (PST)" }, + { offset: 3 * 60, date: "Sun Nov 06 2016", time: "03:00:00 GMT-0800 (PST)" }, + { offset: 3 * 60 + 30, date: "Sun Nov 06 2016", time: "03:30:00 GMT-0800 (PST)" }, + { offset: 4 * 60, date: "Sun Nov 06 2016", time: "04:00:00 GMT-0800 (PST)" }, + { offset: 4 * 60 + 30, date: "Sun Nov 06 2016", time: "04:30:00 GMT-0800 (PST)" }, + ]; + + for (let {offset, date, time} of tests) { + let dt = new Date(2016, Month.November, 6, (offset / 60)|0, (offset % 60), 0, 0); + assertEq(dt.toString(), `${date} ${time}`); + assertEq(dt.toDateString(), date); + assertEq(dt.toTimeString(), time); + } +} + + +// PST -> PDT, using milliseconds from epoch. +{ + let midnight = new Date(2016, Month.March, 13, 0, 0, 0, 0); + let midnightUTC = Date.UTC(2016, Month.March, 13, 0, 0, 0, 0); + + // Ensure midnight time is correct. + assertEq(midnightUTC - midnight.getTime(), -8 * msPerHour); + + let tests = [ + { offset: 0 * 60, date: "Sun Mar 13 2016", time: "00:00:00 GMT-0800 (PST)" }, + { offset: 0 * 60 + 30, date: "Sun Mar 13 2016", time: "00:30:00 GMT-0800 (PST)" }, + { offset: 1 * 60, date: "Sun Mar 13 2016", time: "01:00:00 GMT-0800 (PST)" }, + { offset: 1 * 60 + 30, date: "Sun Mar 13 2016", time: "01:30:00 GMT-0800 (PST)" }, + { offset: 2 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" }, + { offset: 2 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" }, + { offset: 3 * 60, date: "Sun Mar 13 2016", time: "04:00:00 GMT-0700 (PDT)" }, + { offset: 3 * 60 + 30, date: "Sun Mar 13 2016", time: "04:30:00 GMT-0700 (PDT)" }, + { offset: 4 * 60, date: "Sun Mar 13 2016", time: "05:00:00 GMT-0700 (PDT)" }, + { offset: 4 * 60 + 30, date: "Sun Mar 13 2016", time: "05:30:00 GMT-0700 (PDT)" }, + ]; + + for (let {offset, date, time} of tests) { + let dt = new Date(midnight.getTime() + offset * msPerMinute); + assertEq(dt.toString(), `${date} ${time}`); + assertEq(dt.toDateString(), date); + assertEq(dt.toTimeString(), time); + } +} + + +// PST -> PDT, using local date-time. +{ + let tests = [ + { offset: 0 * 60, date: "Sun Mar 13 2016", time: "00:00:00 GMT-0800 (PST)" }, + { offset: 0 * 60 + 30, date: "Sun Mar 13 2016", time: "00:30:00 GMT-0800 (PST)" }, + { offset: 1 * 60, date: "Sun Mar 13 2016", time: "01:00:00 GMT-0800 (PST)" }, + { offset: 1 * 60 + 30, date: "Sun Mar 13 2016", time: "01:30:00 GMT-0800 (PST)" }, + { offset: 2 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" }, + { offset: 2 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" }, + { offset: 3 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" }, + { offset: 3 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" }, + { offset: 4 * 60, date: "Sun Mar 13 2016", time: "04:00:00 GMT-0700 (PDT)" }, + { offset: 4 * 60 + 30, date: "Sun Mar 13 2016", time: "04:30:00 GMT-0700 (PDT)" }, + ]; + + for (let {offset, date, time} of tests) { + let dt = new Date(2016, Month.March, 13, (offset / 60)|0, (offset % 60), 0, 0); + assertEq(dt.toString(), `${date} ${time}`); + assertEq(dt.toDateString(), date); + assertEq(dt.toTimeString(), time); + } +} + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Date/time-zones-pedantic.js b/js/src/tests/ecma_6/Date/time-zones-pedantic.js new file mode 100644 index 000000000000..a1aca65b70e2 --- /dev/null +++ b/js/src/tests/ecma_6/Date/time-zones-pedantic.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(xulRuntime.OS=="WINNT"||xulRuntime.OS=="Darwin") -- Skip on OS X in addition to Windows + +// Contains the tests from "time-zones.js" which fail on OS X. + +const msPerHour = 60 * 60 * 1000; + +const Month = { + January: 0, + February: 1, + March: 2, + April: 3, + May: 4, + June: 5, + July: 6, + August: 7, + September: 8, + October: 9, + November: 10, + December: 11, +}; + +function inTimeZone(tzname, fn) { + setTimeZone(tzname); + try { + fn(); + } finally { + setTimeZone(undefined); + } +} + +const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|"); +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|"); +const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`; +const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`; +const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`); + +function assertDateTime(date, expected) { + let actual = date.toString(); + assertEq(dateTimeRE.test(expected), true, `${expected}`); + assertEq(dateTimeRE.test(actual), true, `${actual}`); + + let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected); + let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual); + + assertEq(actualDateTime, expectedDateTime); + + // The time zone identifier is optional, so only compare its value if it's + // present in |actual| and |expected|. + if (expectedTimeZone !== undefined && actualTimeZone !== undefined) { + assertEq(actualTimeZone, expectedTimeZone); + } +} + +// bug 637244 +inTimeZone("Asia/Novosibirsk", () => { + let dt1 = new Date(1984, Month.April, 1, -1); + assertDateTime(dt1, "Sat Mar 31 1984 23:00:00 GMT+0700 (NOVT)"); + + let dt2 = new Date(1984, Month.April, 1); + assertDateTime(dt2, "Sun Apr 01 1984 01:00:00 GMT+0800 (NOVST)"); +}); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Date/time-zones-posix.js b/js/src/tests/ecma_6/Date/time-zones-posix.js new file mode 100644 index 000000000000..f194dd34effa --- /dev/null +++ b/js/src/tests/ecma_6/Date/time-zones-posix.js @@ -0,0 +1,198 @@ +// |reftest| skip-if(xulRuntime.OS=="WINNT"&&!xulRuntime.shell) -- Windows browser in automation doesn't pick up new time zones correctly + +// Repeats the test from "time-zones.js", but uses POSIX instead of IANA names +// for the time zones. This allows to run these tests on Windows, too. + +// From bug 1330149: +// +// Windows only supports a very limited set of IANA time zone names for the TZ +// environment variable. +// +// TZ format supported by Windows: "TZ=tzn[+|-]hh[:mm[:ss]][dzn]". +// +// Complete list of all IANA time zone ids matching that format. +// +// From tzdata's "northamerica" file: +// EST5EDT +// CST6CDT +// MST7MDT +// PST8PDT +// +// From tzdata's "backward" file: +// GMT+0 +// GMT-0 +// GMT0 + +// Perform the following replacements: +// America/New_York -> EST5EDT +// America/Chicago -> CST6CDT +// America/Denver -> MST7MDT +// America/Los_Angeles -> PST8PDT +// +// And remove any tests not matching one of the four time zones from above. + +const msPerHour = 60 * 60 * 1000; + +const Month = { + January: 0, + February: 1, + March: 2, + April: 3, + May: 4, + June: 5, + July: 6, + August: 7, + September: 8, + October: 9, + November: 10, + December: 11, +}; + +function inTimeZone(tzname, fn) { + setTimeZone(tzname); + try { + fn(); + } finally { + setTimeZone("PST8PDT"); + } +} + +const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|"); +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|"); +const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`; +const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`; +const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`); + +function assertDateTime(date, expected) { + let actual = date.toString(); + assertEq(dateTimeRE.test(expected), true, `${expected}`); + assertEq(dateTimeRE.test(actual), true, `${actual}`); + + let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected); + let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual); + + assertEq(actualDateTime, expectedDateTime); + + // The time zone identifier is optional, so only compare its value if it's + // present in |actual| and |expected|. + if (expectedTimeZone !== undefined && actualTimeZone !== undefined) { + assertEq(actualTimeZone, expectedTimeZone); + } +} + +// bug 294908 +inTimeZone("EST5EDT", () => { + let dt = new Date(2003, Month.April, 6, 2, 30, 00); + assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)"); +}); + +// bug 610183 +inTimeZone("PST8PDT", () => { + let dt = new Date(2014, Month.November, 2, 1, 47, 42); + assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)"); +}); + +// bug 629465 +inTimeZone("MST7MDT", () => { + let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour); + assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)"); + + let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour); + assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)"); + + let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour); + assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)"); +}); + +// bug 742427 +inTimeZone("EST5EDT", () => { + let dt = new Date(2009, Month.March, 8, 1, 0, 0); + assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)"); + dt.setHours(dt.getHours() + 1); + assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)"); +}); +inTimeZone("MST7MDT", () => { + let dt = new Date(2009, Month.March, 8, 1, 0, 0); + assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)"); + dt.setHours(dt.getHours() + 1); + assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)"); +}); +inTimeZone("EST5EDT", () => { + let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour); + assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)"); + + let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour); + assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)"); + + let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour); + assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)"); +}); + +// bug 802627 +inTimeZone("EST5EDT", () => { + let dt = new Date(0); + assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)"); +}); + +// bug 879261 +inTimeZone("EST5EDT", () => { + let dt1 = new Date(1362891600000); + assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)"); + + let dt2 = new Date(dt1.setHours(dt1.getHours() + 24)); + assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)"); +}); +inTimeZone("PST8PDT", () => { + let dt1 = new Date(2014, Month.January, 1); + assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)"); + + let dt2 = new Date(2014, Month.August, 1); + assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)"); +}); +inTimeZone("EST5EDT", () => { + let dt1 = new Date(2016, Month.October, 14, 3, 5, 9); + assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)"); + + let dt2 = new Date(2016, Month.January, 9, 23, 26, 40); + assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)"); +}); + +// bug 1084547 +inTimeZone("EST5EDT", () => { + let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00")); + assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)"); + + dt.setMilliseconds(0); + assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)"); +}); + +// bug 1303306 +inTimeZone("EST5EDT", () => { + let dt = new Date(2016, Month.September, 15, 16, 14, 48); + assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)"); +}); + +// bug 1317364 +inTimeZone("PST8PDT", () => { + let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0); + assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)"); + + let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500); + assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)"); + + let dt3 = new Date(dt2.getTime()); + dt3.setMonth(dt2.getMonth() + 2); + dt3.setDate(dt2.getDate() + 7 + 1); + dt3.setHours(dt2.getHours() + 2); + + assertEq(dt3.getHours(), 3); +}); + +// bug 1355272 +inTimeZone("PST8PDT", () => { + let dt = new Date(2017, Month.April, 10, 17, 25, 07); + assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)"); +}); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Date/time-zones.js b/js/src/tests/ecma_6/Date/time-zones.js new file mode 100644 index 000000000000..1fb01e483ca4 --- /dev/null +++ b/js/src/tests/ecma_6/Date/time-zones.js @@ -0,0 +1,310 @@ +// |reftest| skip-if(xulRuntime.OS=="WINNT") -- Windows doesn't accept IANA names for the TZ env variable + +const msPerHour = 60 * 60 * 1000; + +const Month = { + January: 0, + February: 1, + March: 2, + April: 3, + May: 4, + June: 5, + July: 6, + August: 7, + September: 8, + October: 9, + November: 10, + December: 11, +}; + +function inTimeZone(tzname, fn) { + setTimeZone(tzname); + try { + fn(); + } finally { + setTimeZone(undefined); + } +} + +const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|"); +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|"); +const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`; +const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`; +const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`); + +function assertDateTime(date, expected) { + let actual = date.toString(); + assertEq(dateTimeRE.test(expected), true, `${expected}`); + assertEq(dateTimeRE.test(actual), true, `${actual}`); + + let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected); + let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual); + + assertEq(actualDateTime, expectedDateTime); + + // The time zone identifier is optional, so only compare its value if it's + // present in |actual| and |expected|. + if (expectedTimeZone !== undefined && actualTimeZone !== undefined) { + assertEq(actualTimeZone, expectedTimeZone); + } +} + +// bug 158328 +inTimeZone("Europe/London", () => { + let dt1 = new Date(2002, Month.July, 19, 16, 10, 55); + assertDateTime(dt1, "Fri Jul 19 2002 16:10:55 GMT+0100 (BST)"); + + let dt2 = new Date(2009, Month.December, 24, 13, 44, 52); + assertDateTime(dt2, "Thu Dec 24 2009 13:44:52 GMT+0000 (GMT)"); +}); + +// bug 294908 +inTimeZone("America/New_York", () => { + let dt = new Date(2003, Month.April, 6, 2, 30, 00); + assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)"); +}); + +// bug 610183 +inTimeZone("America/Los_Angeles", () => { + let dt = new Date(2014, Month.November, 2, 1, 47, 42); + assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)"); +}); + +// bug 629465 +inTimeZone("America/Denver", () => { + let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour); + assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)"); + + let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour); + assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)"); + + let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour); + assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)"); +}); + +// bug 637244 +inTimeZone("Europe/Helsinki", () => { + let dt1 = new Date(2016, Month.March, 27, 2, 59); + assertDateTime(dt1, "Sun Mar 27 2016 02:59:00 GMT+0200 (EET)"); + + let dt2 = new Date(2016, Month.March, 27, 3, 0); + assertDateTime(dt2, "Sun Mar 27 2016 04:00:00 GMT+0300 (EEST)"); +}); + +// bug 718175 +inTimeZone("Europe/London", () => { + let dt = new Date(0); + assertEq(dt.getHours(), 1); +}); + +// bug 719274 +inTimeZone("Pacific/Auckland", () => { + let dt = new Date(2012, Month.January, 19, 12, 54, 27); + assertDateTime(dt, "Thu Jan 19 2012 12:54:27 GMT+1300 (NZDT)"); +}); + +// bug 742427 +inTimeZone("Europe/Paris", () => { + let dt1 = new Date(2009, Month.March, 29, 1, 0, 0); + assertDateTime(dt1, "Sun Mar 29 2009 01:00:00 GMT+0100 (CET)"); + dt1.setHours(dt1.getHours() + 1); + assertDateTime(dt1, "Sun Mar 29 2009 03:00:00 GMT+0200 (CEST)"); + + let dt2 = new Date(2010, Month.March, 29, 1, 0, 0); + assertDateTime(dt2, "Mon Mar 29 2010 01:00:00 GMT+0200 (CEST)"); + dt2.setHours(dt2.getHours() + 1); + assertDateTime(dt2, "Mon Mar 29 2010 02:00:00 GMT+0200 (CEST)"); +}); +inTimeZone("America/New_York", () => { + let dt = new Date(2009, Month.March, 8, 1, 0, 0); + assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)"); + dt.setHours(dt.getHours() + 1); + assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)"); +}); +inTimeZone("America/Denver", () => { + let dt = new Date(2009, Month.March, 8, 1, 0, 0); + assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)"); + dt.setHours(dt.getHours() + 1); + assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)"); +}); +inTimeZone("America/New_York", () => { + let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour); + assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)"); + + let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour); + assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)"); + + let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour); + assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)"); +}); +inTimeZone("Europe/Paris", () => { + let dt1 = new Date(Date.UTC(2008, Month.March, 30, 0, 0, 0) - 1 * msPerHour); + assertDateTime(dt1, "Sun Mar 30 2008 00:00:00 GMT+0100 (CET)"); + + let dt2 = new Date(Date.UTC(2008, Month.March, 30, 1, 0, 0) - 1 * msPerHour); + assertDateTime(dt2, "Sun Mar 30 2008 01:00:00 GMT+0100 (CET)"); + + let dt3 = new Date(Date.UTC(2008, Month.March, 30, 3, 0, 0) - 2 * msPerHour); + assertDateTime(dt3, "Sun Mar 30 2008 03:00:00 GMT+0200 (CEST)"); + + let dt4 = new Date(Date.UTC(2008, Month.March, 30, 4, 0, 0) - 2 * msPerHour); + assertDateTime(dt4, "Sun Mar 30 2008 04:00:00 GMT+0200 (CEST)"); +}); + +// bug 802627 +inTimeZone("America/New_York", () => { + let dt = new Date(0); + assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)"); +}); + +// bug 819820 +inTimeZone("Europe/London", () => { + let dt1 = new Date(Date.UTC(2012, Month.October, 28, 0, 59, 59)); + assertDateTime(dt1, "Sun Oct 28 2012 01:59:59 GMT+0100 (BST)"); + + let dt2 = new Date(Date.UTC(2012, Month.October, 28, 1, 0, 0)); + assertDateTime(dt2, "Sun Oct 28 2012 01:00:00 GMT+0000 (GMT)"); + + let dt3 = new Date(Date.UTC(2012, Month.October, 28, 1, 59, 59)); + assertDateTime(dt3, "Sun Oct 28 2012 01:59:59 GMT+0000 (GMT)"); + + let dt4 = new Date(Date.UTC(2012, Month.October, 28, 2, 0, 0)); + assertDateTime(dt4, "Sun Oct 28 2012 02:00:00 GMT+0000 (GMT)"); +}); + +// bug 879261 +inTimeZone("America/New_York", () => { + let dt1 = new Date(1362891600000); + assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)"); + + let dt2 = new Date(dt1.setHours(dt1.getHours() + 24)); + assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)"); +}); +inTimeZone("America/Los_Angeles", () => { + let dt1 = new Date(2014, Month.January, 1); + assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)"); + + let dt2 = new Date(2014, Month.August, 1); + assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)"); +}); +inTimeZone("America/New_York", () => { + let dt1 = new Date(2016, Month.October, 14, 3, 5, 9); + assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)"); + + let dt2 = new Date(2016, Month.January, 9, 23, 26, 40); + assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)"); +}); + +// bug 994086 +inTimeZone("Europe/Vienna", () => { + let dt1 = new Date(2014, Month.March, 30, 2, 0); + assertDateTime(dt1, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)"); + + let dt2 = new Date(2014, Month.March, 30, 3, 0); + assertDateTime(dt2, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)"); + + let dt3 = new Date(2014, Month.March, 30, 4, 0); + assertDateTime(dt3, "Sun Mar 30 2014 04:00:00 GMT+0200 (CEST)"); +}); + +// bug 1084434 +inTimeZone("America/Sao_Paulo", () => { + let dt = new Date(2014, Month.October, 19); + assertEq(dt.getDate(), 19); + assertEq(dt.getHours(), 1); + assertDateTime(dt, "Sun Oct 19 2014 01:00:00 GMT-0200 (BRST)"); +}); + +// bug 1084547 +inTimeZone("America/New_York", () => { + let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00")); + assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)"); + + dt.setMilliseconds(0); + assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)"); +}); + +// bug 1118690 +inTimeZone("Europe/London", () => { + let dt = new Date(1965, Month.January, 1); + assertEq(dt.getFullYear(), 1965); +}); + +// bug 1155096 +inTimeZone("Europe/Moscow", () => { + let dt1 = new Date(1981, Month.March, 32); + assertEq(dt1.getDate(), 1); + + let dt2 = new Date(1982, Month.March, 32); + assertEq(dt2.getDate(), 1); + + let dt3 = new Date(1983, Month.March, 32); + assertEq(dt3.getDate(), 1); + + let dt4 = new Date(1984, Month.March, 32); + assertEq(dt4.getDate(), 1); +}); + +// bug 1284507 +inTimeZone("Atlantic/Azores", () => { + let dt1 = new Date(2017, Month.March, 25, 0, 0, 0); + assertDateTime(dt1, "Sat Mar 25 2017 00:00:00 GMT-0100 (AZOT)"); + + let dt2 = new Date(2016, Month.October, 30, 0, 0, 0); + assertDateTime(dt2, "Sun Oct 30 2016 00:00:00 GMT+0000 (AZOST)"); + + let dt3 = new Date(2016, Month.October, 30, 23, 0, 0); + assertDateTime(dt3, "Sun Oct 30 2016 23:00:00 GMT-0100 (AZOT)"); +}); + +// bug 1303306 +inTimeZone("America/New_York", () => { + let dt = new Date(2016, Month.September, 15, 16, 14, 48); + assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)"); +}); + +// bug 1317364 +inTimeZone("America/Los_Angeles", () => { + let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0); + assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)"); + + let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500); + assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)"); + + let dt3 = new Date(dt2.getTime()); + dt3.setMonth(dt2.getMonth() + 2); + dt3.setDate(dt2.getDate() + 7 + 1); + dt3.setHours(dt2.getHours() + 2); + + assertEq(dt3.getHours(), 3); +}); + +// bug 1335818 +inTimeZone("Asia/Jerusalem", () => { + let dt1 = new Date(2013, Month.March, 22, 1, 0, 0, 0); + assertDateTime(dt1, "Fri Mar 22 2013 01:00:00 GMT+0200 (IST)"); + + let dt2 = new Date(2013, Month.March, 22, 2, 0, 0, 0); + assertDateTime(dt2, "Fri Mar 22 2013 02:00:00 GMT+0200 (IST)"); + + let dt3 = new Date(2013, Month.March, 22, 3, 0, 0, 0); + assertDateTime(dt3, "Fri Mar 22 2013 03:00:00 GMT+0200 (IST)"); + + let dt4 = new Date(2013, Month.March, 29, 1, 0, 0, 0); + assertDateTime(dt4, "Fri Mar 29 2013 01:00:00 GMT+0200 (IST)"); + + let dt5 = new Date(2013, Month.March, 29, 2, 0, 0, 0); + assertDateTime(dt5, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)"); + + let dt6 = new Date(2013, Month.March, 29, 3, 0, 0, 0); + assertDateTime(dt6, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)"); +}); + +// bug 1355272 +inTimeZone("America/Los_Angeles", () => { + let dt = new Date(2017, Month.April, 10, 17, 25, 07); + assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)"); +}); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/test262/built-ins/Atomics/wait/did-timeout.js b/js/src/tests/test262/built-ins/Atomics/wait/did-timeout.js index d946e8a680c0..dcfa09c9fb09 100644 --- a/js/src/tests/test262/built-ins/Atomics/wait/did-timeout.js +++ b/js/src/tests/test262/built-ins/Atomics/wait/did-timeout.js @@ -2,6 +2,7 @@ // This code is governed by the BSD license found in the LICENSE file. /*--- +esid: sec-atomics.wait description: > Test that Atomics.wait returns the right result when it timed out and that the time to time out is reasonable. @@ -14,7 +15,7 @@ $262.agent.receiveBroadcast(function (sab, id) { var ia = new Int32Array(sab); var then = Date.now(); $262.agent.report(Atomics.wait(ia, 0, 0, 500)); // Timeout 500ms - $262.agent.report(Date.now() - then); + $262.agent.report(Date.now() - then); // Actual time can be more than 500ms $262.agent.leaving(); }) `); @@ -23,12 +24,12 @@ var ia = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)); $262.agent.broadcast(ia.buffer); assert.sameValue(getReport(), "timed-out"); -assert.sameValue(Math.abs((getReport()|0) - 500) < $ATOMICS_MAX_TIME_EPSILON, true); +assert.sameValue((getReport()|0) >= 500 - $ATOMICS_MAX_TIME_EPSILON, true); function getReport() { var r; while ((r = $262.agent.getReport()) == null) - $262.agent.sleep(100); + $262.agent.sleep(100); return r; } diff --git a/js/src/tests/test262/built-ins/Atomics/wait/no-spurious-wakeup.js b/js/src/tests/test262/built-ins/Atomics/wait/no-spurious-wakeup.js index 6add8587f96c..9febe0bfcff9 100644 --- a/js/src/tests/test262/built-ins/Atomics/wait/no-spurious-wakeup.js +++ b/js/src/tests/test262/built-ins/Atomics/wait/no-spurious-wakeup.js @@ -2,6 +2,7 @@ // This code is governed by the BSD license found in the LICENSE file. /*--- +esid: sec-atomics.wait description: > Test that Atomics.wait actually waits and does not spuriously wake up when the memory value is changed. @@ -14,7 +15,7 @@ $262.agent.receiveBroadcast(function (sab, id) { var ia = new Int32Array(sab); var then = Date.now(); Atomics.wait(ia, 0, 0); - var diff = Date.now() - then; // Should be about 1000 ms + var diff = Date.now() - then; // Should be about 1000 ms but can be more $262.agent.report(diff); $262.agent.leaving(); }) @@ -24,10 +25,10 @@ var ia = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)); $262.agent.broadcast(ia.buffer); $262.agent.sleep(500); // Give the agent a chance to wait -Atomics.store(ia, 0, 1); // Change the value, should not wake the agent +Atomics.store(ia, 0, 1); // Change the value, should not wake the agent $262.agent.sleep(500); // Wait some more so that we can tell -Atomics.wake(ia, 0); // Really wake it up -assert.sameValue(Math.abs((getReport()|0) - 1000) < $ATOMICS_MAX_TIME_EPSILON, true); +Atomics.wake(ia, 0); // Really wake it up +assert.sameValue((getReport()|0) >= 1000 - $ATOMICS_MAX_TIME_EPSILON, true); function getReport() { var r; diff --git a/js/src/vm/DateTime.cpp b/js/src/vm/DateTime.cpp index 3225165e5eb4..bc35dd5dd081 100644 --- a/js/src/vm/DateTime.cpp +++ b/js/src/vm/DateTime.cpp @@ -90,8 +90,11 @@ UTCToLocalStandardOffsetSeconds() currentNoDST = currentMaybeWithDST; } else { // If |local| respected DST, we need a time broken down into components - // ignoring DST. Turn off DST in the broken-down time. - local.tm_isdst = 0; + // ignoring DST. Turn off DST in the broken-down time. Create a fresh + // copy of |local|, because mktime() will reset tm_isdst = 1 and will + // adjust tm_hour and tm_hour accordingly. + struct tm localNoDST = local; + localNoDST.tm_isdst = 0; // Compute a |time_t t| corresponding to the broken-down time with DST // off. This has boundary-condition issues (for about the duration of @@ -99,7 +102,7 @@ UTCToLocalStandardOffsetSeconds() // zone. But 1) errors will be transient; 2) locations rarely change // time zone; and 3) in the absence of an API that provides the time // zone offset directly, this may be the best we can do. - currentNoDST = mktime(&local); + currentNoDST = mktime(&localNoDST); if (currentNoDST == time_t(-1)) return 0; } @@ -177,6 +180,8 @@ js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds) if (!ComputeLocalTime(static_cast(utcSeconds), &tm)) return 0; + // NB: The offset isn't computed correctly when the standard local offset + // at |utcSeconds| is different from |utcToLocalStandardOffsetSeconds|. int32_t dayoff = int32_t((utcSeconds + utcToLocalStandardOffsetSeconds) % SecondsPerDay); int32_t tmoff = tm.tm_sec + (tm.tm_min * SecondsPerMinute) + (tm.tm_hour * SecondsPerHour); @@ -184,6 +189,8 @@ js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds) if (diff < 0) diff += SecondsPerDay; + else if (uint32_t(diff) >= SecondsPerDay) + diff -= SecondsPerDay; return diff * msPerSecond; } diff --git a/js/src/vm/GeckoProfiler-inl.h b/js/src/vm/GeckoProfiler-inl.h index b7b660d40c4d..9454fa4ba4e6 100644 --- a/js/src/vm/GeckoProfiler-inl.h +++ b/js/src/vm/GeckoProfiler-inl.h @@ -47,6 +47,42 @@ class MOZ_RAII AutoSuppressProfilerSampling MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +MOZ_ALWAYS_INLINE +GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx, + JSScript* script + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) + : profiler_(&cx->geckoProfiler()) +{ + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (MOZ_LIKELY(!profiler_->installed())) { + profiler_ = nullptr; + return; + } +#ifdef DEBUG + spBefore_ = profiler_->stackPointer(); +#endif + + // We want to push a CPP frame so the profiler can correctly order JS and native stacks. + // Only the sp value is important. + profiler_->pseudoStack_->pushCppFrame( + /* label = */ "", /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0, + ProfileEntry::Kind::CPP_MARKER_FOR_JS, ProfileEntry::Category::OTHER); + + profiler_->pseudoStack_->pushJsFrame( + "js::RunScript", /* dynamicString = */ nullptr, script, script->code()); +} + +MOZ_ALWAYS_INLINE +GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker() +{ + if (MOZ_LIKELY(profiler_ == nullptr)) + return; + + profiler_->pseudoStack_->pop(); // the JS frame + profiler_->pseudoStack_->pop(); // the BEGIN_PSEUDO_JS frame + MOZ_ASSERT(spBefore_ == profiler_->stackPointer()); +} + MOZ_ALWAYS_INLINE AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSContext* cx, const char* label, ProfileEntry::Category category diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp index 73c0e3166b21..e0a453a33ee7 100644 --- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -376,38 +376,6 @@ ProfileEntry::trace(JSTracer* trc) } } -GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx, - JSScript* script - MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : profiler(&cx->geckoProfiler()) -{ - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (!profiler->installed()) { - profiler = nullptr; - return; - } - spBefore_ = profiler->stackPointer(); - - // We want to push a CPP frame so the profiler can correctly order JS and native stacks. - // Only the sp value is important. - profiler->pseudoStack_->pushCppFrame( - /* label = */ "", /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0, - ProfileEntry::Kind::CPP_MARKER_FOR_JS, ProfileEntry::Category::OTHER); - - profiler->pseudoStack_->pushJsFrame( - "js::RunScript", /* dynamicString = */ nullptr, script, script->code()); -} - -GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker() -{ - if (profiler == nullptr) - return; - - profiler->pseudoStack_->pop(); // the JS frame - profiler->pseudoStack_->pop(); // the BEGIN_PSEUDO_JS frame - MOZ_ASSERT(spBefore_ == profiler->stackPointer()); -} - GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSContext* cx, bool hasProfilerFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : profiler(&cx->geckoProfiler()) diff --git a/js/src/vm/GeckoProfiler.h b/js/src/vm/GeckoProfiler.h index 9f0feacf04a5..75378f4dc1f0 100644 --- a/js/src/vm/GeckoProfiler.h +++ b/js/src/vm/GeckoProfiler.h @@ -209,14 +209,17 @@ GeckoProfilerRuntime::stringsReset() class MOZ_RAII GeckoProfilerEntryMarker { public: - explicit GeckoProfilerEntryMarker(JSContext* cx, - JSScript* script - MOZ_GUARD_OBJECT_NOTIFIER_PARAM); - ~GeckoProfilerEntryMarker(); + explicit MOZ_ALWAYS_INLINE + GeckoProfilerEntryMarker(JSContext* cx, + JSScript* script + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + MOZ_ALWAYS_INLINE ~GeckoProfilerEntryMarker(); private: - GeckoProfilerThread* profiler; - mozilla::DebugOnly spBefore_; + GeckoProfilerThread* profiler_; +#ifdef DEBUG + uint32_t spBefore_; +#endif MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 9cb7eacf36ab..ee1fbc199d13 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -180,10 +180,9 @@ js::CancelOffThreadWasmTier2Generator() } bool -js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder) +js::StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder, + const AutoLockHelperThreadState& lock) { - AutoLockHelperThreadState lock; - if (!HelperThreadState().ionWorklist(lock).append(builder)) return false; @@ -1232,26 +1231,35 @@ GlobalHelperThreadState::canStartWasmCompile(const AutoLockHelperThreadState& lo if (wasmWorklist(lock, mode).empty()) return false; - // For Tier1 and Once compilation, honor the maximum allowed threads to - // compile wasm jobs at once, to avoid oversaturating the machine. - // - // For Tier2 compilation we need to allow other things to happen too, so for - // now we only allow one thread. - // - // TODO: We should investigate more intelligent strategies, see bug 1380033. - // + // Parallel compilation and background compilation should be disabled on + // unicore systems. + + MOZ_RELEASE_ASSERT(cpuCount > 1); + // If Tier2 is very backlogged we must give priority to it, since the Tier2 // queue holds onto Tier1 tasks. Indeed if Tier2 is backlogged we will // devote more resources to Tier2 and not start any Tier1 work at all. bool tier2oversubscribed = wasmTier2GeneratorWorklist(lock).length() > 20; + // For Tier1 and Once compilation, honor the maximum allowed threads to + // compile wasm jobs at once, to avoid oversaturating the machine. + // + // For Tier2 compilation we need to allow other things to happen too, so we + // do not allow all logical cores to be used for background work; instead we + // wish to use a fraction of the physical cores. We can't directly compute + // the physical cores from the logical cores, but 1/3 of the logical cores + // is a safe estimate for the number of physical cores available for + // background work. + + size_t physCoresAvailable = size_t(ceil(cpuCount / 3.0)); + size_t threads; if (mode == wasm::CompileMode::Tier2) { if (tier2oversubscribed) threads = maxWasmCompilationThreads(); else - threads = 1; + threads = physCoresAvailable; } else { if (tier2oversubscribed) threads = 0; diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h index 6311afb8c4ea..0b7887a6c8e1 100644 --- a/js/src/vm/HelperThreads.h +++ b/js/src/vm/HelperThreads.h @@ -484,7 +484,8 @@ StartOffThreadPromiseHelperTask(JSContext* cx, UniquePtr task * generated and read everything needed from the VM state. */ bool -StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder); +StartOffThreadIonCompile(JSContext* cx, jit::IonBuilder* builder, + const AutoLockHelperThreadState& lock); /* * Schedule deletion of Ion compilation data. @@ -539,12 +540,6 @@ CancelOffThreadIonCompilesUsingNurseryPointers(JSRuntime* runtime) CancelOffThreadIonCompile(CompilationSelector(CompilationsUsingNursery{runtime}), true); } -inline void -CancelOffThreadIonCompile() -{ - CancelOffThreadIonCompile(CompilationSelector(AllCompilations()), false); -} - #ifdef DEBUG bool HasOffThreadIonCompile(JSCompartment* comp); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 9355586e137a..dab4e4cecfce 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -334,30 +334,18 @@ js::ValueToCallable(JSContext* cx, HandleValue v, int numToSkip, MaybeConstruct return nullptr; } -bool -RunState::maybeCreateThisForConstructor(JSContext* cx) +static bool +MaybeCreateThisForConstructor(JSContext* cx, JSScript* calleeScript, const CallArgs& args, + bool createSingleton) { - if (isInvoke()) { - InvokeState& invoke = *asInvoke(); - if (invoke.constructing() && invoke.args().thisv().isPrimitive()) { - RootedObject callee(cx, &invoke.args().callee()); - if (callee->isBoundFunction()) { - invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL)); - } else if (script()->isDerivedClassConstructor()) { - MOZ_ASSERT(callee->as().isClassConstructor()); - invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL)); - } else { - MOZ_ASSERT(invoke.args().thisv().isMagic(JS_IS_CONSTRUCTING)); - RootedObject newTarget(cx, &invoke.args().newTarget().toObject()); - NewObjectKind newKind = invoke.createSingleton() ? SingletonObject : GenericObject; - JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind); - if (!obj) - return false; - invoke.args().setThis(ObjectValue(*obj)); - } - } - } - return true; + if (args.thisv().isObject()) + return true; + + RootedObject callee(cx, &args.callee()); + RootedObject newTarget(cx, &args.newTarget().toObject()); + NewObjectKind newKind = createSingleton ? SingletonObject : GenericObject; + + return CreateThis(cx, callee, calleeScript, newTarget, newKind, args.mutableThisv()); } static MOZ_NEVER_INLINE bool @@ -375,6 +363,15 @@ ExecuteState::pushInterpreterFrame(JSContext* cx) return cx->interpreterStack().pushExecuteFrame(cx, script_, newTargetValue_, envChain_, evalInFrame_); } + +InterpreterFrame* +RunState::pushInterpreterFrame(JSContext* cx) +{ + if (isInvoke()) + return asInvoke()->pushInterpreterFrame(cx); + return asExecute()->pushInterpreterFrame(cx); +} + // MSVC with PGO inlines a lot of functions in RunScript, resulting in large // stack frames and stack overflow issues, see bug 1167883. Turn off PGO to // avoid this. @@ -493,11 +490,15 @@ js::InternalCallOrConstruct(JSContext* cx, const CallArgs& args, MaybeConstruct // Check to see if createSingleton flag should be set for this frame. if (construct) { + bool createSingleton = false; jsbytecode* pc; if (JSScript* script = cx->currentScript(&pc)) { if (ObjectGroup::useSingletonForNewObject(cx, script, pc)) - state.setCreateSingleton(); + createSingleton = true; } + + if (!MaybeCreateThisForConstructor(cx, state.script(), args, createSingleton)) + return false; } bool ok = RunScript(cx, state); @@ -3088,45 +3089,48 @@ CASE(JSOP_FUNCALL) if (!funScript) goto error; - bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); + bool createSingleton = false; + if (construct) { + createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); + + if (!MaybeCreateThisForConstructor(cx, funScript, args, createSingleton)) + goto error; + } TypeMonitorCall(cx, args, construct); - mozilla::Maybe state; - state.emplace(cx, args, construct); + { + InvokeState state(cx, args, construct); - if (createSingleton) - state->setCreateSingleton(); + if (!createSingleton && jit::IsIonEnabled(cx)) { + jit::MethodStatus status = jit::CanEnter(cx, state); + if (status == jit::Method_Error) + goto error; + if (status == jit::Method_Compiled) { + jit::JitExecStatus exec = jit::IonCannon(cx, state); + interpReturnOK = !IsErrorStatus(exec); + if (interpReturnOK) + CHECK_BRANCH(); + REGS.sp = args.spAfterCall(); + goto jit_return; + } + } - if (!createSingleton && jit::IsIonEnabled(cx)) { - jit::MethodStatus status = jit::CanEnter(cx, state.ref()); - if (status == jit::Method_Error) - goto error; - if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::IonCannon(cx, state.ref()); - interpReturnOK = !IsErrorStatus(exec); - if (interpReturnOK) - CHECK_BRANCH(); - REGS.sp = args.spAfterCall(); - goto jit_return; + if (jit::IsBaselineEnabled(cx)) { + jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state); + if (status == jit::Method_Error) + goto error; + if (status == jit::Method_Compiled) { + jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state); + interpReturnOK = !IsErrorStatus(exec); + if (interpReturnOK) + CHECK_BRANCH(); + REGS.sp = args.spAfterCall(); + goto jit_return; + } } } - if (jit::IsBaselineEnabled(cx)) { - jit::MethodStatus status = jit::CanEnterBaselineMethod(cx, state.ref()); - if (status == jit::Method_Error) - goto error; - if (status == jit::Method_Compiled) { - jit::JitExecStatus exec = jit::EnterBaselineMethod(cx, state.ref()); - interpReturnOK = !IsErrorStatus(exec); - if (interpReturnOK) - CHECK_BRANCH(); - REGS.sp = args.spAfterCall(); - goto jit_return; - } - } - - state.reset(); funScript = fun->nonLazyScript(); if (!activation.pushInlineFrame(args, funScript, construct)) @@ -5183,6 +5187,8 @@ js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame) Scope* startingScope; if (frame.isDebuggerEvalFrame()) { AbstractFramePtr evalInFramePrev = frame.asInterpreterFrame()->evalInFramePrev(); + while (evalInFramePrev.isDebuggerEvalFrame()) + evalInFramePrev = evalInFramePrev.asInterpreterFrame()->evalInFramePrev(); startingScope = evalInFramePrev.script()->bodyScope(); } else { MOZ_ASSERT(frame.isEvalFrame()); diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index f86670c99cce..5697df8145c0 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -230,10 +230,8 @@ class RunState JS::HandleScript script() const { return script_; } - virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx) = 0; - virtual void setReturnValue(const Value& v) = 0; - - bool maybeCreateThisForConstructor(JSContext* cx); + InterpreterFrame* pushInterpreterFrame(JSContext* cx); + inline void setReturnValue(const Value& v); private: RunState(const RunState& other) = delete; @@ -268,9 +266,9 @@ class ExecuteState : public RunState JSObject* environmentChain() const { return envChain_; } bool isDebuggerEval() const { return !!evalInFrame_; } - virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); + InterpreterFrame* pushInterpreterFrame(JSContext* cx); - virtual void setReturnValue(const Value& v) { + void setReturnValue(const Value& v) { if (result_) *result_ = v; } @@ -281,29 +279,33 @@ class InvokeState final : public RunState { const CallArgs& args_; MaybeConstruct construct_; - bool createSingleton_; public: InvokeState(JSContext* cx, const CallArgs& args, MaybeConstruct construct) : RunState(cx, Invoke, args.callee().as().nonLazyScript()), args_(args), - construct_(construct), - createSingleton_(false) + construct_(construct) { } - bool createSingleton() const { return createSingleton_; } - void setCreateSingleton() { createSingleton_ = true; } - bool constructing() const { return construct_; } const CallArgs& args() const { return args_; } - virtual InterpreterFrame* pushInterpreterFrame(JSContext* cx); + InterpreterFrame* pushInterpreterFrame(JSContext* cx); - virtual void setReturnValue(const Value& v) { + void setReturnValue(const Value& v) { args_.rval().set(v); } }; +inline void +RunState::setReturnValue(const Value& v) +{ + if (isInvoke()) + asInvoke()->setReturnValue(v); + else + asExecute()->setReturnValue(v); +} + extern bool RunScript(JSContext* cx, RunState& state); diff --git a/js/src/vm/MutexIDs.h b/js/src/vm/MutexIDs.h index ecf0d7096308..ace766026e25 100644 --- a/js/src/vm/MutexIDs.h +++ b/js/src/vm/MutexIDs.h @@ -47,6 +47,7 @@ _(WasmModuleTieringLock, 500) \ _(WasmCompileTaskState, 500) \ \ + _(WasmCodeSegmentMap, 600) \ _(TraceLoggerGraphState, 600) \ _(VTuneLock, 600) diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 3cf55cb1761c..2d8bbd879eec 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -244,25 +244,8 @@ InterpreterFrame::prologue(JSContext* cx) if (callee().needsFunctionEnvironmentObjects() && !initFunctionEnvironmentObjects(cx)) return false; - if (isConstructing()) { - if (callee().isBoundFunction()) { - thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL); - } else if (script->isDerivedClassConstructor()) { - MOZ_ASSERT(callee().isClassConstructor()); - thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL); - } else if (thisArgument().isObject()) { - // Nothing to do. Correctly set. - } else { - MOZ_ASSERT(thisArgument().isMagic(JS_IS_CONSTRUCTING)); - RootedObject callee(cx, &this->callee()); - RootedObject newTarget(cx, &this->newTarget().toObject()); - JSObject* obj = CreateThisForFunction(cx, callee, newTarget, - createSingleton() ? SingletonObject : GenericObject); - if (!obj) - return false; - thisArgument() = ObjectValue(*obj); - } - } + MOZ_ASSERT_IF(isConstructing(), + thisArgument().isObject() || thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL)); return probes::EnterScript(cx, script, script->functionNonDelazifying(), this); } @@ -1730,7 +1713,7 @@ jit::JitActivation::startWasmInterrupt(const JS::ProfilingFrameIterator::Registe MOZ_ALWAYS_TRUE(wasm::StartUnwinding(*this, state, &unwindState, &ignoredUnwound)); void* pc = unwindState.pc; - MOZ_ASSERT(compartment()->wasm.lookupCode(pc)->lookupRange(pc)->isFunction()); + MOZ_ASSERT(wasm::LookupCode(pc)->lookupRange(pc)->isFunction()); cx_->runtime()->startWasmInterrupt(state.pc, pc); setWasmExitFP(unwindState.fp); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 8dd94e9a6b19..8dbbda2914e3 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1821,7 +1821,7 @@ class OnlyJSJitFrameIter : public JitFrameIter { void settle() { while (!done() && !isJSJit()) - ++(*this); + JitFrameIter::operator++(); } public: diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index ea873abf0786..8429b3e0f6f7 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -6331,43 +6331,43 @@ CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type) Op stmt; if (lhsType.isSigned() && rhsType.isSigned()) { - switch (comp->getOp()) { - case JSOP_EQ: stmt = Op::I32Eq; break; - case JSOP_NE: stmt = Op::I32Ne; break; - case JSOP_LT: stmt = Op::I32LtS; break; - case JSOP_LE: stmt = Op::I32LeS; break; - case JSOP_GT: stmt = Op::I32GtS; break; - case JSOP_GE: stmt = Op::I32GeS; break; + switch (comp->getKind()) { + case PNK_EQ: stmt = Op::I32Eq; break; + case PNK_NE: stmt = Op::I32Ne; break; + case PNK_LT: stmt = Op::I32LtS; break; + case PNK_LE: stmt = Op::I32LeS; break; + case PNK_GT: stmt = Op::I32GtS; break; + case PNK_GE: stmt = Op::I32GeS; break; default: MOZ_CRASH("unexpected comparison op"); } } else if (lhsType.isUnsigned() && rhsType.isUnsigned()) { - switch (comp->getOp()) { - case JSOP_EQ: stmt = Op::I32Eq; break; - case JSOP_NE: stmt = Op::I32Ne; break; - case JSOP_LT: stmt = Op::I32LtU; break; - case JSOP_LE: stmt = Op::I32LeU; break; - case JSOP_GT: stmt = Op::I32GtU; break; - case JSOP_GE: stmt = Op::I32GeU; break; + switch (comp->getKind()) { + case PNK_EQ: stmt = Op::I32Eq; break; + case PNK_NE: stmt = Op::I32Ne; break; + case PNK_LT: stmt = Op::I32LtU; break; + case PNK_LE: stmt = Op::I32LeU; break; + case PNK_GT: stmt = Op::I32GtU; break; + case PNK_GE: stmt = Op::I32GeU; break; default: MOZ_CRASH("unexpected comparison op"); } } else if (lhsType.isDouble()) { - switch (comp->getOp()) { - case JSOP_EQ: stmt = Op::F64Eq; break; - case JSOP_NE: stmt = Op::F64Ne; break; - case JSOP_LT: stmt = Op::F64Lt; break; - case JSOP_LE: stmt = Op::F64Le; break; - case JSOP_GT: stmt = Op::F64Gt; break; - case JSOP_GE: stmt = Op::F64Ge; break; + switch (comp->getKind()) { + case PNK_EQ: stmt = Op::F64Eq; break; + case PNK_NE: stmt = Op::F64Ne; break; + case PNK_LT: stmt = Op::F64Lt; break; + case PNK_LE: stmt = Op::F64Le; break; + case PNK_GT: stmt = Op::F64Gt; break; + case PNK_GE: stmt = Op::F64Ge; break; default: MOZ_CRASH("unexpected comparison op"); } } else if (lhsType.isFloat()) { - switch (comp->getOp()) { - case JSOP_EQ: stmt = Op::F32Eq; break; - case JSOP_NE: stmt = Op::F32Ne; break; - case JSOP_LT: stmt = Op::F32Lt; break; - case JSOP_LE: stmt = Op::F32Le; break; - case JSOP_GT: stmt = Op::F32Gt; break; - case JSOP_GE: stmt = Op::F32Ge; break; + switch (comp->getKind()) { + case PNK_EQ: stmt = Op::F32Eq; break; + case PNK_NE: stmt = Op::F32Ne; break; + case PNK_LT: stmt = Op::F32Lt; break; + case PNK_LE: stmt = Op::F32Le; break; + case PNK_GT: stmt = Op::F32Gt; break; + case PNK_GE: stmt = Op::F32Ge; break; default: MOZ_CRASH("unexpected comparison op"); } } else { diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index 6199035e061b..a01a72696707 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -27,6 +27,7 @@ #endif #include "vtune/VTuneWrapper.h" #include "wasm/WasmModule.h" +#include "wasm/WasmProcess.h" #include "wasm/WasmSerialize.h" #include "jit/MacroAssembler-inl.h" @@ -284,11 +285,21 @@ CodeSegment::initialize(Tier tier, if (!ExecutableAllocator::makeExecutable(bytes_.get(), RoundupCodeLength(codeLength))) return false; + if (!RegisterCodeSegment(this)) + return false; + registered_ = true; + SendCodeRangesToProfiler(*this, bytecode.bytes, metadata); return true; } +CodeSegment::~CodeSegment() +{ + if (registered_) + UnregisterCodeSegment(this); +} + size_t CodeSegment::serializedSize() const { diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index 6d77fd24395c..5dd61933603f 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -80,6 +80,8 @@ class CodeSegment uint8_t* outOfBoundsCode_; uint8_t* unalignedAccessCode_; + bool registered_; + bool initialize(Tier tier, UniqueCodeBytes bytes, uint32_t codeLength, @@ -103,9 +105,12 @@ class CodeSegment length_(0), interruptCode_(nullptr), outOfBoundsCode_(nullptr), - unalignedAccessCode_(nullptr) + unalignedAccessCode_(nullptr), + registered_(false) {} + ~CodeSegment(); + static UniqueCodeSegment create(Tier tier, jit::MacroAssembler& masm, const ShareableBytes& bytecode, diff --git a/js/src/wasm/WasmCompartment.cpp b/js/src/wasm/WasmCompartment.cpp index ae4963fd6840..98f45fe7b2e5 100644 --- a/js/src/wasm/WasmCompartment.cpp +++ b/js/src/wasm/WasmCompartment.cpp @@ -28,13 +28,11 @@ using namespace js; using namespace wasm; Compartment::Compartment(Zone* zone) - : mutatingInstances_(false) {} Compartment::~Compartment() { MOZ_ASSERT(instances_.empty()); - MOZ_ASSERT(!mutatingInstances_); } struct InstanceComparator @@ -62,20 +60,6 @@ struct InstanceComparator } }; -struct CodeSegmentPC -{ - const void* pc; - - explicit CodeSegmentPC(const void* pc) : pc(pc) {} - int operator()(const CodeSegment* cs) const { - if (cs->containsCodePC(pc)) - return 0; - if (pc < cs->base()) - return -1; - return 1; - } -}; - bool Compartment::registerInstance(JSContext* cx, HandleWasmInstanceObject instanceObj) { @@ -84,33 +68,16 @@ Compartment::registerInstance(JSContext* cx, HandleWasmInstanceObject instanceOb instance.ensureProfilingLabels(cx->runtime()->geckoProfiler().enabled()); - if (instance.debugEnabled() && - instance.compartment()->debuggerObservesAllExecution()) - { + if (instance.debugEnabled() && instance.compartment()->debuggerObservesAllExecution()) instance.ensureEnterFrameTrapsState(cx, true); - } size_t index; if (BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index)) MOZ_CRASH("duplicate registration"); - { - AutoMutateInstances guard(*this); - if (!instances_.insert(instances_.begin() + index, &instance)) { - ReportOutOfMemory(cx); - return false; - } - - const Code& code = instance.code(); - for (auto t : code.tiers()) { - const CodeSegment& cs = code.segment(t); - BinarySearchIf(codeSegments_, 0, codeSegments_.length(), CodeSegmentPC(cs.base()), - &index); - if (!codeSegments_.insert(codeSegments_.begin() + index, &cs)) { - ReportOutOfMemory(cx); - return false; - } - } + if (!instances_.insert(instances_.begin() + index, &instance)) { + ReportOutOfMemory(cx); + return false; } Debugger::onNewWasmInstance(cx, instanceObj); @@ -123,39 +90,7 @@ Compartment::unregisterInstance(Instance& instance) size_t index; if (!BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index)) return; - - AutoMutateInstances guard(*this); instances_.erase(instances_.begin() + index); - const Code& code = instance.code(); - for (auto t : code.tiers()) { - MOZ_ALWAYS_TRUE(BinarySearchIf(codeSegments_, 0, codeSegments_.length(), - CodeSegmentPC(code.segment(t).base()), &index)); - codeSegments_.erase(codeSegments_.begin() + index); - } -} - -const CodeSegment* -Compartment::lookupCodeSegment(const void* pc) const -{ - // lookupCodeSegment() can be called asynchronously from the interrupt signal - // handler. In that case, the signal handler is just asking whether the pc - // is in wasm code. If instances_ is being mutated then we can't be - // executing wasm code so returning nullptr is fine. - if (mutatingInstances_) - return nullptr; - - size_t index; - if (!BinarySearchIf(codeSegments_, 0, codeSegments_.length(), CodeSegmentPC(pc), &index)) - return nullptr; - - return codeSegments_[index]; -} - -const Code* -Compartment::lookupCode(const void* pc) const -{ - const CodeSegment* found = lookupCodeSegment(pc); - return found ? found->code() : nullptr; } void diff --git a/js/src/wasm/WasmCompartment.h b/js/src/wasm/WasmCompartment.h index 5cd1746d3b67..a098a957f6b0 100644 --- a/js/src/wasm/WasmCompartment.h +++ b/js/src/wasm/WasmCompartment.h @@ -27,8 +27,6 @@ namespace wasm { class CodeSegment; typedef Vector InstanceVector; -typedef Vector CodeSegmentVector; - // wasm::Compartment lives in JSCompartment and contains the wasm-related // per-compartment state. wasm::Compartment tracks every live instance in the // compartment and must be notified, via registerInstance(), of any new @@ -37,20 +35,6 @@ typedef Vector CodeSegmentVector; class Compartment { InstanceVector instances_; - CodeSegmentVector codeSegments_; - volatile bool mutatingInstances_; - - struct AutoMutateInstances { - Compartment &c; - explicit AutoMutateInstances(Compartment& c) : c(c) { - MOZ_ASSERT(!c.mutatingInstances_); - c.mutatingInstances_ = true; - } - ~AutoMutateInstances() { - MOZ_ASSERT(c.mutatingInstances_); - c.mutatingInstances_ = false; - } - }; public: explicit Compartment(Zone* zone); @@ -72,12 +56,6 @@ class Compartment const InstanceVector& instances() const { return instances_; } - // These methods return the wasm::CodeSegment (resp. wasm::Code) containing - // the given pc, if any exist in the compartment. - - const CodeSegment* lookupCodeSegment(const void* pc) const; - const Code* lookupCode(const void* pc) const; - // Ensure all Instances in this JSCompartment have profiling labels created. void ensureProfilingLabels(bool profilingEnabled); diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp index bbecea9fd8b0..520e9df9bf80 100644 --- a/js/src/wasm/WasmFrameIter.cpp +++ b/js/src/wasm/WasmFrameIter.cpp @@ -62,7 +62,7 @@ WasmFrameIter::WasmFrameIter(JitActivation* activation, wasm::Frame* fp) // instead. code_ = &fp_->tls->instance->code(); - MOZ_ASSERT(code_ == activation->compartment()->wasm.lookupCode(activation->wasmUnwindPC())); + MOZ_ASSERT(code_ == LookupCode(activation->wasmUnwindPC())); codeRange_ = code_->lookupRange(activation->wasmUnwindPC()); MOZ_ASSERT(codeRange_->kind() == CodeRange::Function); @@ -127,7 +127,7 @@ WasmFrameIter::popFrame() void* returnAddress = prevFP->returnAddress; code_ = &fp_->tls->instance->code(); - MOZ_ASSERT(code_ == activation_->compartment()->wasm.lookupCode(returnAddress)); + MOZ_ASSERT(code_ == LookupCode(returnAddress)); codeRange_ = code_->lookupRange(returnAddress); MOZ_ASSERT(codeRange_->kind() == CodeRange::Function); @@ -596,7 +596,7 @@ static inline void AssertMatchesCallSite(const JitActivation& activation, void* callerPC, Frame* callerFP) { #ifdef DEBUG - const Code* code = activation.compartment()->wasm.lookupCode(callerPC); + const Code* code = LookupCode(callerPC); MOZ_ASSERT(code); const CodeRange* callerCodeRange = code->lookupRange(callerPC); @@ -626,7 +626,7 @@ ProfilingFrameIterator::initFromExitFP(const Frame* fp) stackAddress_ = (void*)fp; - code_ = activation_->compartment()->wasm.lookupCode(pc); + code_ = LookupCode(pc); MOZ_ASSERT(code_); codeRange_ = code_->lookupRange(pc); @@ -686,7 +686,7 @@ js::wasm::StartUnwinding(const JitActivation& activation, const RegisterState& r uint8_t* codeBase; const Code* code = nullptr; - const CodeSegment* codeSegment = activation.compartment()->wasm.lookupCodeSegment(pc); + const CodeSegment* codeSegment = LookupCodeSegment(pc); if (codeSegment) { code = codeSegment->code(); codeRange = code->lookupRange(pc); @@ -900,7 +900,7 @@ ProfilingFrameIterator::operator++() } code_ = &callerFP_->tls->instance->code(); - MOZ_ASSERT(code_ == activation_->compartment()->wasm.lookupCode(callerPC_)); + MOZ_ASSERT(code_ == LookupCode(callerPC_)); codeRange_ = code_->lookupRange(callerPC_); MOZ_ASSERT(codeRange_); @@ -1118,13 +1118,7 @@ wasm::LookupFaultingInstance(const CodeSegment& codeSegment, void* pc, void* fp) bool wasm::InCompiledCode(void* pc) { - JSContext* cx = TlsContext.get(); - if (!cx) - return false; - - MOZ_RELEASE_ASSERT(!cx->handlingSegFault); - - if (cx->compartment()->wasm.lookupCodeSegment(pc)) + if (LookupCodeSegment(pc)) return true; const CodeRange* codeRange; diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index e1c5784be50f..fdeabdbcd2e0 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -22,6 +22,7 @@ #include "gc/Barrier.h" #include "wasm/WasmCode.h" #include "wasm/WasmDebug.h" +#include "wasm/WasmProcess.h" #include "wasm/WasmTable.h" namespace js { diff --git a/js/src/wasm/WasmProcess.cpp b/js/src/wasm/WasmProcess.cpp new file mode 100644 index 000000000000..90ee03c01c4f --- /dev/null +++ b/js/src/wasm/WasmProcess.cpp @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmProcess.h" + +#include "mozilla/BinarySearch.h" + +#include "vm/MutexIDs.h" +#include "wasm/WasmCode.h" + +using namespace js; +using namespace wasm; + +using mozilla::BinarySearchIf; + +// Per-process map from values of program-counter (pc) to CodeSegments. +// +// Whenever a new CodeSegment is ready to use, it has to be registered so that +// we can have fast lookups from pc to CodeSegments in numerous places. Since +// wasm compilation may be tiered, and the second tier doesn't have access to +// any JSContext/JSCompartment/etc lying around, we have to use a process-wide +// map instead. + +typedef Vector CodeSegmentVector; + +class ProcessCodeSegmentMap +{ + // Since writes (insertions or removals) can happen on any background + // thread at the same time, we need a lock here. + + Mutex mutatorsMutex_; + + CodeSegmentVector segments1_; + CodeSegmentVector segments2_; + + // Because of sampling/interruptions/stack iteration in general, the + // thread running wasm might need to know to which CodeSegment the + // current PC belongs, during a call to lookup(). A lookup is a + // read-only operation, and we don't want to take a lock then + // (otherwise, we could have a deadlock situation if an async lookup + // happened on a given thread that was holding mutatorsMutex_ while getting + // interrupted/sampled). Since the writer could be modifying the data that + // is getting looked up, the writer functions use spin-locks to know if + // there are any observers (i.e. calls to lookup()) of the atomic data. + + Atomic observers_; + + // Except during swapAndWait(), there are no lookup() observers of the + // vector pointed to by mutableCodeSegments_ + + CodeSegmentVector* mutableCodeSegments_; + Atomic readonlyCodeSegments_; + + struct CodeSegmentPC + { + const void* pc; + explicit CodeSegmentPC(const void* pc) : pc(pc) {} + int operator()(const CodeSegment* cs) const { + if (cs->containsCodePC(pc)) + return 0; + if (pc < cs->base()) + return -1; + return 1; + } + }; + + void swapAndWait() { + // Both vectors are consistent for look up at this point, although their + // contents are different: there is no way for the looked up PC to be + // in the code segment that is getting registered, because the code + // segment is not even fully created yet. + + // If a lookup happens before this instruction, then the + // soon-to-become-former read-only pointer is used during the lookup, + // which is valid. + + mutableCodeSegments_ = const_cast( + readonlyCodeSegments_.exchange(mutableCodeSegments_) + ); + + // If a lookup happens after this instruction, then the updated vector + // is used, which is valid: + // - in case of insertion, it means the new vector contains more data, + // but it's fine since the code segment is getting registered and thus + // isn't even fully created yet, so the code can't be running. + // - in case of removal, it means the new vector contains one less + // entry, but it's fine since unregistering means the code segment + // isn't used by any live instance anymore, thus PC can't be in the + // to-be-removed code segment's range. + + // A lookup could have happened on any of the two vectors. Wait for + // observers to be done using any vector before mutating. + + while (observers_); + } + + public: + ProcessCodeSegmentMap() + : mutatorsMutex_(mutexid::WasmCodeSegmentMap), + observers_(0), + mutableCodeSegments_(&segments1_), + readonlyCodeSegments_(&segments2_) + { + } + + ~ProcessCodeSegmentMap() + { + MOZ_ASSERT(segments1_.empty()); + MOZ_ASSERT(segments2_.empty()); + } + + bool insert(const CodeSegment* cs) { + LockGuard lock(mutatorsMutex_); + + size_t index; + MOZ_ALWAYS_FALSE(BinarySearchIf(*mutableCodeSegments_, 0, mutableCodeSegments_->length(), + CodeSegmentPC(cs->base()), &index)); + + if (!mutableCodeSegments_->insert(mutableCodeSegments_->begin() + index, cs)) + return false; + + swapAndWait(); + +#ifdef DEBUG + size_t otherIndex; + MOZ_ALWAYS_FALSE(BinarySearchIf(*mutableCodeSegments_, 0, mutableCodeSegments_->length(), + CodeSegmentPC(cs->base()), &otherIndex)); + MOZ_ASSERT(index == otherIndex); +#endif + + // Although we could simply revert the insertion in the read-only + // vector, it is simpler to just crash and given that each CodeSegment + // consumes multiple pages, it is unlikely this insert() would OOM in + // practice + AutoEnterOOMUnsafeRegion oom; + if (!mutableCodeSegments_->insert(mutableCodeSegments_->begin() + index, cs)) + oom.crash("when inserting a CodeSegment in the process-wide map"); + + return true; + } + + void remove(const CodeSegment* cs) { + LockGuard lock(mutatorsMutex_); + + size_t index; + MOZ_ALWAYS_TRUE(BinarySearchIf(*mutableCodeSegments_, 0, mutableCodeSegments_->length(), + CodeSegmentPC(cs->base()), &index)); + + mutableCodeSegments_->erase(mutableCodeSegments_->begin() + index); + + swapAndWait(); + +#ifdef DEBUG + size_t otherIndex; + MOZ_ALWAYS_TRUE(BinarySearchIf(*mutableCodeSegments_, 0, mutableCodeSegments_->length(), + CodeSegmentPC(cs->base()), &otherIndex)); + MOZ_ASSERT(index == otherIndex); +#endif + + mutableCodeSegments_->erase(mutableCodeSegments_->begin() + index); + } + + const CodeSegment* lookup(const void* pc) { + auto decObserver = mozilla::MakeScopeExit([&] { + observers_--; + }); + observers_++; + + // Once atomically-read, the readonly vector is valid as long as + // observers_ has been incremented (see swapAndWait()). + const CodeSegmentVector* readonly = readonlyCodeSegments_; + + size_t index; + if (!BinarySearchIf(*readonly, 0, readonly->length(), CodeSegmentPC(pc), &index)) + return nullptr; + + // It is fine returning a raw CodeSegment*, because we assume we are + // looking up a live PC in code which is on the stack, keeping the + // CodeSegment alive. + + return (*readonly)[index]; + } +}; + +static ProcessCodeSegmentMap processCodeSegmentMap; + +bool +wasm::RegisterCodeSegment(const CodeSegment* cs) +{ + return processCodeSegmentMap.insert(cs); +} + +void +wasm::UnregisterCodeSegment(const CodeSegment* cs) +{ + processCodeSegmentMap.remove(cs); +} + +const CodeSegment* +wasm::LookupCodeSegment(const void* pc) +{ + return processCodeSegmentMap.lookup(pc); +} + +const Code* +wasm::LookupCode(const void* pc) +{ + const CodeSegment* found = LookupCodeSegment(pc); + return found ? found->code() : nullptr; +} diff --git a/js/src/wasm/WasmProcess.h b/js/src/wasm/WasmProcess.h new file mode 100644 index 000000000000..648d8558608d --- /dev/null +++ b/js/src/wasm/WasmProcess.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_process_h +#define wasm_process_h + +namespace js { +namespace wasm { + +class CodeSegment; +class Code; + +// These methods return the wasm::CodeSegment (resp. wasm::Code) containing +// the given pc, if any exist in the process. These methods do not take a lock, +// and thus are safe to use in a profiling or async interrupt context. + +const CodeSegment* +LookupCodeSegment(const void* pc); + +const Code* +LookupCode(const void* pc); + +// These methods allow to (un)register CodeSegments so they can be looked up +// via pc in the methods described above. + +bool +RegisterCodeSegment(const CodeSegment* cs); + +void +UnregisterCodeSegment(const CodeSegment* cs); + +} // namespace wasm +} // namespace js + +#endif // wasm_process_h diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index 55b4c9299037..4dda8457c002 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -990,7 +990,7 @@ HandleFault(PEXCEPTION_POINTERS exception) return false; JitActivation* activation = cx->activation()->asJit(); - const CodeSegment* codeSegment = activation->compartment()->wasm.lookupCodeSegment(pc); + const CodeSegment* codeSegment = LookupCodeSegment(pc); if (!codeSegment) return false; @@ -1125,7 +1125,7 @@ HandleMachException(JSContext* cx, const ExceptionRequest& request) return false; JitActivation* activation = cx->activation()->asJit(); - const CodeSegment* codeSegment = activation->compartment()->wasm.lookupCodeSegment(pc); + const CodeSegment* codeSegment = LookupCodeSegment(pc); if (!codeSegment) return false; @@ -1336,7 +1336,7 @@ HandleFault(int signum, siginfo_t* info, void* ctx) return false; JitActivation* activation = cx->activation()->asJit(); - const CodeSegment* segment = activation->compartment()->wasm.lookupCodeSegment(pc); + const CodeSegment* segment = LookupCodeSegment(pc); if (!segment) return false; @@ -1445,7 +1445,7 @@ wasm::InInterruptibleCode(JSContext* cx, uint8_t* pc, const CodeSegment** cs) if (!cx->compartment()) return false; - *cs = cx->compartment()->wasm.lookupCodeSegment(pc); + *cs = LookupCodeSegment(pc); if (!*cs) return false; diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 19d3e551d846..4c6371d996bb 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -6292,12 +6292,10 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState static void AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent) { - NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame), - "property should only be set on first continuation/ib-sibling"); - // FIXME(emilio): Remove this property, and use the frame of the generated // content itself to tear the content down? It should be quite simpler. + aOwnerFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aOwnerFrame); nsIFrame::ContentArray* value = aOwnerFrame->GetProperty(nsIFrame::GenConProperty()); if (!value) { @@ -6557,7 +6555,9 @@ AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager, // frames to find the first one that is either a ::after frame for an // ancestor of aChild or a frame that is for a node later in the // document than aChild and return that in aAfterFrame. - if (aParentFrame->GetProperty(nsIFrame::GenConProperty()) || + nsIFrame* afterBeforeOwnerFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParentFrame); + if (afterBeforeOwnerFrame->GetProperty(nsIFrame::GenConProperty()) || nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(), CSSPseudoElementType::after, aParentFrame->PresContext()) || diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 743507a5faf7..f6be108b4964 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -361,8 +361,6 @@ nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult); nsresult NS_NewDomSelection(nsISelection** aResult); already_AddRefed NS_NewContentViewer(); -nsresult NS_NewGenRegularIterator(nsIContentIterator** aResult); -nsresult NS_NewGenSubtreeIterator(nsIContentIterator** aInstancePtrResult); nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult); nsresult NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult); nsresult NS_NewTextEncoder(nsIDocumentEncoder** aResult); diff --git a/layout/generic/crashtests/1145931.html b/layout/generic/crashtests/1145931.html new file mode 100644 index 000000000000..8be7bc1a3ea9 --- /dev/null +++ b/layout/generic/crashtests/1145931.html @@ -0,0 +1,16 @@ + + + + + + + +
+ diff --git a/layout/generic/crashtests/1350372.html b/layout/generic/crashtests/1350372.html new file mode 100644 index 000000000000..58858b3518e8 --- /dev/null +++ b/layout/generic/crashtests/1350372.html @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/layout/generic/crashtests/1381134-2.html b/layout/generic/crashtests/1381134-2.html new file mode 100644 index 000000000000..d3ac73507a14 --- /dev/null +++ b/layout/generic/crashtests/1381134-2.html @@ -0,0 +1,45 @@ + + + + + + + + +
+
+ + + +
+
+ + diff --git a/layout/generic/crashtests/1381134.html b/layout/generic/crashtests/1381134.html new file mode 100644 index 000000000000..a45fa04ecb74 --- /dev/null +++ b/layout/generic/crashtests/1381134.html @@ -0,0 +1,45 @@ + + + + + + + + +
+
+ + + +
+
+ + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index e2d29bfc2228..d161a35fa3ac 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -591,6 +591,7 @@ load 1137723-1.html load 1137723-2.html load 1140268-1.html load 1145768.html +load 1145931.html load 1146103.html load 1146107.html load 1146114.html @@ -656,10 +657,13 @@ load 1308876-1.html load 1316649.html load 1349650.html asserts-if(browserIsRemote,0-5) load 1349816-1.html # bug 1350352 +load 1350372.html load 1364361-1.html load 1367413-1.html load 1368617-1.html load 1373586.html +load 1381134.html +load 1381134-2.html load 1401420-1.html load 1401709.html load 1401807.html diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 43450fb63e34..bbca214fb234 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -5674,6 +5674,7 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, params.ascent = gfxFloat(mAscent) / appUnitsPerDevUnit; params.style = decorationStyle; params.vertical = verticalRun; + params.sidewaysLeft = mTextRun->IsSidewaysLeft(); params.offset = underlineOffset / appUnitsPerDevUnit; params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; @@ -5718,14 +5719,12 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, frameBStart = GetSize().width; ascent = -ascent; } - // The decoration-line offsets need to be reversed for sideways-lr mode, - // so we will multiply the values from metrics by this factor. - gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; nsCSSRendering::DecorationRectParams params; params.lineSize = Size(gfxWidth, 0); params.ascent = ascent; params.vertical = verticalDec; + params.sidewaysLeft = mTextRun->IsSidewaysLeft(); nscoord topOrLeft(nscoord_MAX), bottomOrRight(nscoord_MIN); typedef gfxFont::Metrics Metrics; @@ -5747,7 +5746,7 @@ nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, useVerticalMetrics); params.lineSize.height = metrics.*lineSize; - params.offset = decorationOffsetDir * metrics.*lineOffset; + params.offset = metrics.*lineOffset; const nsRect decorationRect = nsCSSRendering::GetTextDecorationRect(aPresContext, params) + (verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0) @@ -5931,7 +5930,6 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks, bool aVertical, - gfxFloat aDecorationOffsetDir, uint8_t aDecoration) { PaintDecorationLineParams params; @@ -5946,6 +5944,7 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, params.decorationType = DecorationType::Selection; params.callbacks = aCallbacks; params.vertical = aVertical; + params.sidewaysLeft = mTextRun->IsSidewaysLeft(); params.descentLimit = ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(), aFontMetrics); @@ -6054,7 +6053,6 @@ nsTextFrame::DrawSelectionDecorations(gfxContext* aContext, NS_WARNING("Requested selection decorations when there aren't any"); return; } - params.offset *= aDecorationOffsetDir; params.lineSize.height *= relativeSize; params.icoordInFrame = (aVertical ? params.pt.y - aPt.y : params.pt.x - aPt.x) + aICoordInFrame; @@ -6618,7 +6616,6 @@ nsTextFrame::PaintTextSelectionDecorations( } else { pt.y = (aParams.textBaselinePt.y - mAscent) / app; } - gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; SelectionType nextSelectionType; TextRangeStyle selectedStyle; @@ -6646,7 +6643,7 @@ nsTextFrame::PaintTextSelectionDecorations( aParams.context, aParams.dirtyRect, aSelectionType, *aParams.textPaintStyle, selectedStyle, pt, xInFrame, width, mAscent / app, decorationMetrics, aParams.callbacks, - verticalRun, decorationOffsetDir, kDecoration); + verticalRun, kDecoration); } iterator.UpdateWithAdvance(advance); ++selectionIndex; @@ -7253,10 +7250,6 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, nscoord inflationMinFontSize = nsLayoutUtils::InflationMinFontSizeFor(this); - // The decoration-line offsets need to be reversed for sideways-lr mode, - // so we will multiply the values from metrics by this factor. - gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0; - PaintDecorationLineParams params; params.context = aParams.context; params.dirtyRect = aParams.dirtyRect; @@ -7269,6 +7262,7 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, params.lineSize = Size(measure / app, 0); params.ascent = ascent; params.vertical = verticalDec; + params.sidewaysLeft = mTextRun->IsSidewaysLeft(); // The matrix of the context may have been altered for text-combine- // upright. However, we want to draw decoration lines unscaled, thus @@ -7303,7 +7297,7 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, bCoord = (frameBStart - dec.mBaselineOffset) / app; params.color = dec.mColor; - params.offset = decorationOffsetDir * metrics.*lineOffset; + params.offset = metrics.*lineOffset; params.style = dec.mStyle; PaintDecorationLine(params); }; @@ -7579,6 +7573,9 @@ nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext, ComputeDescentLimitForSelectionUnderline(aPresContext, metrics); params.vertical = verticalRun; + EnsureTextRun(nsTextFrame::eInflated); + params.sidewaysLeft = mTextRun ? mTextRun->IsSidewaysLeft() : false; + UniquePtr details = GetSelectionDetails(); for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) { if (sd->mStart == sd->mEnd || diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 5c450473adc6..f74037fb23db 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -833,7 +833,6 @@ protected: const gfxFont::Metrics& aFontMetrics, DrawPathCallbacks* aCallbacks, bool aVertical, - gfxFloat aDecorationOffsetDir, uint8_t aDecoration); struct PaintDecorationLineParams; diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index b1ecc40a84e8..269474464a16 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -4022,7 +4022,7 @@ nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame, DrawTarget& aDrawTarget, Float& ptICoord = aParams.vertical ? pt.y : pt.x; Float& ptBCoord = aParams.vertical ? pt.x : pt.y; if (aParams.vertical) { - ptBCoord += adv + lineThickness / 2.0; + ptBCoord += adv; } Float iCoordLimit = ptICoord + rectISize + lineThickness; @@ -4222,7 +4222,24 @@ nsCSSRendering::GetTextDecorationRectInternal(const Point& aPt, } gfxFloat baseline = floor(bCoord + aParams.ascent + 0.5); + + // Calculate adjusted offset based on writing-mode/orientation and thickness + // of decoration line. The input value aParams.offset is the nominal position + // (offset from baseline) where we would draw a single, infinitely-thin line; + // but for a wavy or double line, we'll need to move the bounding rect of the + // decoration outwards from the baseline so that an underline remains below + // the glyphs, and an overline above them, despite the increased block-dir + // extent of the decoration. + // + // So adjustments by r.Height() are used to make the wider line styles (wavy + // and double) "grow" in the appropriate direction compared to the basic + // single line. + // + // Note that at this point, the decoration rect is being calculated in line- + // relative coordinates, where 'x' is line-rightwards, and 'y' is line- + // upwards. We'll swap them to be physical coords at the end. gfxFloat offset = 0.0; + switch (aParams.decoration) { case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE: offset = aParams.offset; @@ -4238,24 +4255,46 @@ nsCSSRendering::GetTextDecorationRectInternal(const Point& aPt, } } break; + case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE: + // For overline, we adjust the offset by lineThickness (the thickness of + // a single decoration line) because empirically it looks better to draw + // the overline just inside rather than outside the font's ascent, which + // is what nsTextFrame passes as aParams.offset (as fonts don't provide + // an explicit overline-offset). offset = aParams.offset - lineThickness + r.Height(); break; + case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: { + // To maintain a consistent mid-point for line-through decorations, + // we adjust the offset by half of the decoration rect's height. gfxFloat extra = floor(r.Height() / 2.0 + 0.5); extra = std::max(extra, lineThickness); offset = aParams.offset - lineThickness + extra; break; } + default: NS_ERROR("Invalid decoration value!"); } + // Convert line-relative coordinate system (x = line-right, y = line-up) + // to physical coords, and move the decoration rect to the calculated + // offset from baseline. if (aParams.vertical) { - r.y = baseline + floor(offset + 0.5); Swap(r.x, r.y); Swap(r.width, r.height); + // line-upwards in vertical mode = physical-right, so we /add/ offset + // to baseline. Except in sideways-lr mode, where line-upwards will be + // physical leftwards. + if (aParams.sidewaysLeft) { + r.x = baseline - floor(offset + 0.5); + } else { + r.x = baseline + floor(offset - r.Width() + 0.5); + } } else { + // line-upwards in horizontal mode = physical-up, but our physical coord + // system works downwards, so we /subtract/ offset from baseline. r.y = baseline - floor(offset + 0.5); } diff --git a/layout/painting/nsCSSRendering.h b/layout/painting/nsCSSRendering.h index c429c5f7d399..d7a52f4611eb 100644 --- a/layout/painting/nsCSSRendering.h +++ b/layout/painting/nsCSSRendering.h @@ -583,6 +583,7 @@ struct nsCSSRendering { // NS_STYLE_TEXT_DECORATION_STYLE_*. uint8_t style = NS_STYLE_TEXT_DECORATION_STYLE_NONE; bool vertical = false; + bool sidewaysLeft = false; }; struct PaintDecorationLineParams : DecorationRectParams diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 73e9bc03088d..66d747da8f56 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -878,11 +878,6 @@ public: mBuilder->mCurrentReferenceFrame = aFrame; mBuilder->mCurrentOffsetToReferenceFrame = aOffset; } - // Return the previous frame's animated geometry root, whether or not the - // current frame is an immediate descendant. - const nsIFrame* GetPrevAnimatedGeometryRoot() const { - return mPrevAnimatedGeometryRoot; - } bool IsAnimatedGeometryRoot() const { return mCurrentAGRState == AGR_YES; } @@ -908,7 +903,6 @@ public: AGRState mCurrentAGRState; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; - nsIFrame* mPrevAnimatedGeometryRoot; nsDisplayLayerEventRegions* mPrevLayerEventRegions; nsPoint mPrevOffset; nsRect mPrevDirtyRect; diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index 08023fd8d687..23f4c6d7a9f0 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -249,7 +249,7 @@ fails-if(webrender) == grid-fragmentation-dyn2-020.html grid-fragmentation-020-r != grid-fragmentation-dyn1-021.html grid-fragmentation-021-ref.html # bug 1251799 == grid-fragmentation-dyn2-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn3-021.html grid-fragmentation-021-ref.html -asserts(1-10) == grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.html # assertion related to bug 1251799 ? +== grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.html == grid-fragmentation-dyn5-021.html grid-fragmentation-021-ref.html fails-if(webrender) == grid-fragmentation-dyn2-022.html grid-fragmentation-007-ref.html fails-if(webrender) == grid-fragmentation-dyn1-023.html grid-fragmentation-023-ref.html diff --git a/layout/reftests/text-decoration/reftest.list b/layout/reftests/text-decoration/reftest.list index b156363516e4..0d57a49b9ffd 100644 --- a/layout/reftests/text-decoration/reftest.list +++ b/layout/reftests/text-decoration/reftest.list @@ -110,3 +110,5 @@ fuzzy-if(skiaContent,4,2) == underline-select-1.html underline-select-1-ref.html == 1133392.html 1133392-ref.html != 1159729-offset-adjustment.html 1159729-offset-adjustment-notref.html == emphasis-style-dynamic.html emphasis-style-dynamic-ref.html +== vertical-mode-decorations-1.html vertical-mode-decorations-1-ref.html +== vertical-mode-decorations-2.html vertical-mode-decorations-2-ref.html diff --git a/layout/reftests/text-decoration/vertical-mode-decorations-1-ref.html b/layout/reftests/text-decoration/vertical-mode-decorations-1-ref.html new file mode 100644 index 000000000000..e2078fdc393e --- /dev/null +++ b/layout/reftests/text-decoration/vertical-mode-decorations-1-ref.html @@ -0,0 +1,15 @@ + + + + + + +
+ hello hello hello +
diff --git a/layout/reftests/text-decoration/vertical-mode-decorations-1.html b/layout/reftests/text-decoration/vertical-mode-decorations-1.html new file mode 100644 index 000000000000..2c14b80b1328 --- /dev/null +++ b/layout/reftests/text-decoration/vertical-mode-decorations-1.html @@ -0,0 +1,15 @@ + + + + + + +
+ hello hello hello +
diff --git a/layout/reftests/text-decoration/vertical-mode-decorations-2-ref.html b/layout/reftests/text-decoration/vertical-mode-decorations-2-ref.html new file mode 100644 index 000000000000..a410fa3bc6c5 --- /dev/null +++ b/layout/reftests/text-decoration/vertical-mode-decorations-2-ref.html @@ -0,0 +1,15 @@ + + + + + + +
+ hello hello hello +
diff --git a/layout/reftests/text-decoration/vertical-mode-decorations-2.html b/layout/reftests/text-decoration/vertical-mode-decorations-2.html new file mode 100644 index 000000000000..72bdfca99939 --- /dev/null +++ b/layout/reftests/text-decoration/vertical-mode-decorations-2.html @@ -0,0 +1,15 @@ + + + + + + +
+ hello hello hello +
diff --git a/layout/style/crashtests/1344210.html b/layout/style/crashtests/1344210.html new file mode 100644 index 000000000000..f6b39eb082a1 --- /dev/null +++ b/layout/style/crashtests/1344210.html @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index 37752a4b99fd..d57f5e067ab4 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -166,6 +166,7 @@ load 1331272.html HTTP load 1333001-1.html pref(dom.animations-api.core.enabled,true) load 1340344.html load 1342316-1.html +load 1344210.html load 1356601-1.html load 1364139-1.html load 1370793-1.xhtml diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index d13399c14d09..b7b5e951d626 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -3996,6 +3996,13 @@ SVGTextFrame::ConvertTextElementCharIndexToAddressableIndex( uint32_t SVGTextFrame::GetNumberOfChars(nsIContent* aContent) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return 0; + } + UpdateGlyphPositioning(); uint32_t n = 0; @@ -4040,6 +4047,13 @@ nsresult SVGTextFrame::SelectSubString(nsIContent* aContent, uint32_t charnum, uint32_t nchars) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return NS_ERROR_FAILURE; + } + UpdateGlyphPositioning(); // Convert charnum/nchars from addressable characters relative to @@ -4271,6 +4285,13 @@ int32_t SVGTextFrame::GetCharNumAtPosition(nsIContent* aContent, mozilla::nsISVGPoint* aPoint) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return -1; + } + UpdateGlyphPositioning(); nsPresContext* context = PresContext(); @@ -4304,6 +4325,13 @@ SVGTextFrame::GetStartPositionOfChar(nsIContent* aContent, uint32_t aCharNum, mozilla::nsISVGPoint** aResult) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return NS_ERROR_FAILURE; + } + UpdateGlyphPositioning(); CharIterator it(this, CharIterator::eAddressable, aContent); @@ -4329,6 +4357,13 @@ SVGTextFrame::GetEndPositionOfChar(nsIContent* aContent, uint32_t aCharNum, mozilla::nsISVGPoint** aResult) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return NS_ERROR_FAILURE; + } + UpdateGlyphPositioning(); CharIterator it(this, CharIterator::eAddressable, aContent); @@ -4366,6 +4401,13 @@ SVGTextFrame::GetExtentOfChar(nsIContent* aContent, uint32_t aCharNum, dom::SVGIRect** aResult) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return NS_ERROR_FAILURE; + } + UpdateGlyphPositioning(); CharIterator it(this, CharIterator::eAddressable, aContent); @@ -4426,6 +4468,13 @@ SVGTextFrame::GetRotationOfChar(nsIContent* aContent, uint32_t aCharNum, float* aResult) { + nsIFrame* kid = PrincipalChildList().FirstChild(); + if (NS_SUBTREE_DIRTY(kid)) { + // We're never reflowed if we're under a non-SVG element that is + // never reflowed (such as the HTML 'caption' element). + return NS_ERROR_FAILURE; + } + UpdateGlyphPositioning(); CharIterator it(this, CharIterator::eAddressable, aContent); diff --git a/layout/svg/crashtests/1322852.html b/layout/svg/crashtests/1322852.html new file mode 100644 index 000000000000..728ea5c7a9a4 --- /dev/null +++ b/layout/svg/crashtests/1322852.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index f98f8a3be05c..1c5c06d839a1 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -200,6 +200,7 @@ load 1209525-1.svg load 1223281-1.svg load 1322537-1.html load 1322537-2.html +load 1322852.html load 1348564.svg load conditional-outer-svg-nondirty-reflow-assert.xhtml load extref-test-1.xhtml diff --git a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h index f451abee9206..c168f0284a46 100755 --- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h +++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h @@ -11,7 +11,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/RefCounted.h" #include "mozilla/UniquePtr.h" -#include "mozilla/utils.h" +#include "double-conversion/utils.h" // for DISALLOW_COPY_AND_ASSIGN #include "CodecConfig.h" #include "VideoTypes.h" #include "MediaConduitErrors.h" diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index 7dfc2b2426f0..94bf90044182 100644 --- a/mfbt/Atomics.h +++ b/mfbt/Atomics.h @@ -558,6 +558,11 @@ private: Atomic(Atomic& aOther) = delete; }; +// If you want to atomically swap two atomic values, use exchange(). +template +void +Swap(Atomic&, Atomic&) = delete; + } // namespace mozilla #endif /* mozilla_Atomics_h */ diff --git a/mfbt/decimal/moz-decimal-utils.h b/mfbt/decimal/moz-decimal-utils.h index cd2289b6a36d..ca8439ebdb2e 100644 --- a/mfbt/decimal/moz-decimal-utils.h +++ b/mfbt/decimal/moz-decimal-utils.h @@ -10,7 +10,7 @@ // of Decimal.cpp under the Mozilla source without blink core dependencies. Do // not include it into any file other than Decimal.cpp. -#include "../double-conversion/source/double-conversion.h" +#include "../double-conversion/double-conversion/double-conversion.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" #include "mozilla/FloatingPoint.h" diff --git a/mfbt/double-conversion.h b/mfbt/double-conversion.h new file mode 100644 index 000000000000..10eae12d8544 --- /dev/null +++ b/mfbt/double-conversion.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A temporary header providing double-conversion functionality until we change + * our code to use the double-conversion header directly. + */ + +#ifndef mozilla_double_conversion_h +#define mozilla_double_conversion_h + +#include "double-conversion/double-conversion.h" + +#endif /* mozilla_double_conversion_h */ diff --git a/mfbt/double-conversion/GIT-INFO b/mfbt/double-conversion/GIT-INFO index 37079955ced8..8d2a9e771803 100644 --- a/mfbt/double-conversion/GIT-INFO +++ b/mfbt/double-conversion/GIT-INFO @@ -1,9 +1,9 @@ -commit d8d4e668ee1e6e10b728f0671a89b07d7c4d45be -Merge: 79fb300 48b5e70 -Author: Florian Loitsch -Date: Wed Nov 23 19:14:38 2016 +0100 +commit fe9b384793c4e79bd32133dc9053f27b75a5eeae +Merge: 2a257b7 1d5a688 +Author: Florian Loitsch +Date: Fri Sep 15 11:32:00 2017 +0200 - Merge pull request #39 from uburuntu/master + Merge pull request #52 from uburuntu/master - Some small fixes: compilation, null ptr derefence and const qualifiers + Some refactorings: remove unused static, replace deprecated headers, init member in constructor diff --git a/mfbt/double-conversion/ToPrecision-exponential.patch b/mfbt/double-conversion/ToPrecision-exponential.patch index 01f7a4fad04a..d02273f46923 100644 --- a/mfbt/double-conversion/ToPrecision-exponential.patch +++ b/mfbt/double-conversion/ToPrecision-exponential.patch @@ -1,9 +1,12 @@ -1e7bf0c636b8cca54dd83456a0f8fa219343e2a1 Bug 608195 - part 2 - extend ToPrecision to tell use whether exponential notation was used -diff --git a/mfbt/double-conversion/source/double-conversion.cc b/mfbt/double-conversion/source/double-conversion.cc -index febba6c..394b6a0 100644 ---- a/mfbt/double-conversion/source/double-conversion.cc -+++ b/mfbt/double-conversion/source/double-conversion.cc -@@ -283,7 +283,9 @@ bool DoubleToStringConverter::ToExponential( +diff --git a/mfbt/double-conversion/double-conversion/double-conversion.cc b/mfbt/double-conversion/double-conversion/double-conversion.cc +--- a/mfbt/double-conversion/double-conversion/double-conversion.cc ++++ b/mfbt/double-conversion/double-conversion/double-conversion.cc +@@ -279,17 +279,19 @@ bool DoubleToStringConverter::ToExponent + exponent, + result_builder); + return true; + } + bool DoubleToStringConverter::ToPrecision(double value, int precision, @@ -13,7 +16,17 @@ index febba6c..394b6a0 100644 if (Double(value).IsSpecial()) { return HandleSpecialValues(value, result_builder); } -@@ -325,6 +327,7 @@ bool DoubleToStringConverter::ToPrecision(double value, + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + +@@ -321,16 +323,17 @@ bool DoubleToStringConverter::ToPrecisio + max_trailing_padding_zeroes_in_precision_mode_)) { + // Fill buffer to contain 'precision' digits. + // Usually the buffer is already at the correct length, but 'DoubleToAscii' + // is allowed to return less characters. + for (int i = decimal_rep_length; i < precision; ++i) { decimal_rep[i] = '0'; } @@ -21,11 +34,20 @@ index febba6c..394b6a0 100644 CreateExponentialRepresentation(decimal_rep, precision, exponent, -diff --git a/mfbt/double-conversion/source/double-conversion.h b/mfbt/double-conversion/source/double-conversion.h -index 0900ba0..957575c 100644 ---- a/mfbt/double-conversion/source/double-conversion.h -+++ b/mfbt/double-conversion/source/double-conversion.h -@@ -270,6 +270,7 @@ class DoubleToStringConverter { + result_builder); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + Max(0, precision - decimal_point), + result_builder); +diff --git a/mfbt/double-conversion/double-conversion/double-conversion.h b/mfbt/double-conversion/double-conversion/double-conversion.h +--- a/mfbt/double-conversion/double-conversion/double-conversion.h ++++ b/mfbt/double-conversion/double-conversion/double-conversion.h +@@ -265,16 +265,17 @@ class DoubleToStringConverter { + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits + // The last condition implies that the result will never contain more than + // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the // exponent character, the exponent's sign, and at most 3 exponent digits). MFBT_API bool ToPrecision(double value, int precision, @@ -33,3 +55,8 @@ index 0900ba0..957575c 100644 StringBuilder* result_builder) const; enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, + // Same as SHORTEST, but for single-precision floats. diff --git a/mfbt/double-conversion/add-mfbt-api-markers.patch b/mfbt/double-conversion/add-mfbt-api-markers.patch index 082a601d3a96..7e4c53feb3bf 100644 --- a/mfbt/double-conversion/add-mfbt-api-markers.patch +++ b/mfbt/double-conversion/add-mfbt-api-markers.patch @@ -1,16 +1,30 @@ -diff --git a/mfbt/double-conversion/source/double-conversion.h b/mfbt/double-conversion/source/double-conversion.h -index f98edae..c62b16b 100644 ---- a/mfbt/double-conversion/source/double-conversion.h -+++ b/mfbt/double-conversion/source/double-conversion.h -@@ -28,6 +28,7 @@ +diff --git a/mfbt/double-conversion/double-conversion/double-conversion.h b/mfbt/double-conversion/double-conversion/double-conversion.h +--- a/mfbt/double-conversion/double-conversion/double-conversion.h ++++ b/mfbt/double-conversion/double-conversion/double-conversion.h +@@ -23,16 +23,17 @@ + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ #define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ +#include "mozilla/Types.h" - #include "utils.h" + #include namespace double_conversion { -@@ -129,7 +130,7 @@ class DoubleToStringConverter { + + class DoubleToStringConverter { + public: + // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint + // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the +@@ -124,17 +125,17 @@ class DoubleToStringConverter { + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); } // Returns a converter following the EcmaScript specification. @@ -19,7 +33,17 @@ index f98edae..c62b16b 100644 // Computes the shortest string of digits that correctly represent the input // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high -@@ -197,7 +198,7 @@ class DoubleToStringConverter { + // (see constructor) it then either returns a decimal representation, or an + // exponential representation. + // Example with decimal_in_shortest_low = -6, + // decimal_in_shortest_high = 21, + // EMIT_POSITIVE_EXPONENT_SIGN activated, and +@@ -192,17 +193,17 @@ class DoubleToStringConverter { + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'value' > 10^kMaxFixedDigitsBeforePoint, or + // - 'requested_digits' > kMaxFixedDigitsAfterPoint. // The last two conditions imply that the result will never contain more than // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters // (one additional character for the sign, and one for the decimal point). @@ -28,7 +52,17 @@ index f98edae..c62b16b 100644 int requested_digits, StringBuilder* result_builder) const; -@@ -229,7 +230,7 @@ class DoubleToStringConverter { + // Computes a representation in exponential format with requested_digits + // after the decimal point. The last emitted digit is rounded. + // If requested_digits equals -1, then the shortest exponential representation + // is computed. + // +@@ -224,17 +225,17 @@ class DoubleToStringConverter { + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - 'requested_digits' > kMaxExponentialDigits. + // The last condition implies that the result will never contain more than // kMaxExponentialDigits + 8 characters (the sign, the digit before the // decimal point, the decimal point, the exponent character, the // exponent's sign, and at most 3 exponent digits). @@ -37,7 +71,17 @@ index f98edae..c62b16b 100644 int requested_digits, StringBuilder* result_builder) const; -@@ -267,7 +268,7 @@ class DoubleToStringConverter { + // Computes 'precision' leading digits of the given 'value' and returns them + // either in exponential or decimal format, depending on + // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the + // constructor). + // The last computed digit is rounded. +@@ -262,17 +263,17 @@ class DoubleToStringConverter { + // except for the following cases: + // - the input value is special and no infinity_symbol or nan_symbol has + // been provided to the constructor, + // - precision < kMinPericisionDigits + // - precision > kMaxPrecisionDigits // The last condition implies that the result will never contain more than // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the // exponent character, the exponent's sign, and at most 3 exponent digits). @@ -46,7 +90,17 @@ index f98edae..c62b16b 100644 int precision, StringBuilder* result_builder) const; -@@ -292,7 +293,7 @@ class DoubleToStringConverter { + enum DtoaMode { + // Produce the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate + // but correct) 0.3. + SHORTEST, +@@ -287,17 +288,17 @@ class DoubleToStringConverter { + }; + + // The maximal number of digits that are needed to emit a double in base 10. + // A higher precision can be achieved by using more digits, but the shortest + // accurate representation of any double will never use more digits than // kBase10MaximalLength. // Note that DoubleToAscii null-terminates its input. So the given buffer // should be at least kBase10MaximalLength + 1 characters long. @@ -55,7 +109,17 @@ index f98edae..c62b16b 100644 // Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or // -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v' -@@ -332,7 +333,7 @@ class DoubleToStringConverter { + // after it has been casted to a single-precision float. That is, in this + // mode static_cast(v) must not be NaN, +Infinity or -Infinity. + // + // The result should be interpreted as buffer * 10^(point-length). + // +@@ -327,44 +328,44 @@ class DoubleToStringConverter { + // DoubleToAscii expects the given buffer to be big enough to hold all + // digits and a terminating null-character. In SHORTEST-mode it expects a + // buffer of at least kBase10MaximalLength + 1. In all other modes the + // requested_digits parameter and the padding-zeroes limit the size of the + // output. Don't forget the decimal point, the exponent character and the // terminating null-character when computing the maximal output size. // The given length is only used in debug mode to ensure the buffer is big // enough. @@ -64,7 +128,10 @@ index f98edae..c62b16b 100644 DtoaMode mode, int requested_digits, char* buffer, -@@ -343,7 +344,7 @@ class DoubleToStringConverter { + int buffer_length, + bool* sign, + int* length, + int* point); private: // Implementation for ToShortest and ToShortestSingle. @@ -73,7 +140,7 @@ index f98edae..c62b16b 100644 StringBuilder* result_builder, DtoaMode mode) const; -@@ -351,15 +352,15 @@ class DoubleToStringConverter { + // If the value is a special value (NaN or Infinity) constructs the // corresponding string using the configured infinity/nan-symbol. // If either of them is NULL or the value is not special then the // function returns false. @@ -92,3 +159,8 @@ index f98edae..c62b16b 100644 int length, int decimal_point, int digits_after_point, + StringBuilder* result_builder) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; diff --git a/mfbt/double-conversion/source/LICENSE b/mfbt/double-conversion/double-conversion/LICENSE similarity index 100% rename from mfbt/double-conversion/source/LICENSE rename to mfbt/double-conversion/double-conversion/LICENSE diff --git a/mfbt/double-conversion/source/README.md b/mfbt/double-conversion/double-conversion/README.md similarity index 100% rename from mfbt/double-conversion/source/README.md rename to mfbt/double-conversion/double-conversion/README.md diff --git a/mfbt/double-conversion/source/bignum-dtoa.cc b/mfbt/double-conversion/double-conversion/bignum-dtoa.cc similarity index 99% rename from mfbt/double-conversion/source/bignum-dtoa.cc rename to mfbt/double-conversion/double-conversion/bignum-dtoa.cc index f1ad7a5ae8dd..526f96edf545 100644 --- a/mfbt/double-conversion/source/bignum-dtoa.cc +++ b/mfbt/double-conversion/double-conversion/bignum-dtoa.cc @@ -25,12 +25,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include -#include "bignum-dtoa.h" +#include -#include "bignum.h" -#include "ieee.h" +#include +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/bignum-dtoa.h b/mfbt/double-conversion/double-conversion/bignum-dtoa.h similarity index 99% rename from mfbt/double-conversion/source/bignum-dtoa.h rename to mfbt/double-conversion/double-conversion/bignum-dtoa.h index 34b961992d67..9d15ce3dce97 100644 --- a/mfbt/double-conversion/source/bignum-dtoa.h +++ b/mfbt/double-conversion/double-conversion/bignum-dtoa.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_ #define DOUBLE_CONVERSION_BIGNUM_DTOA_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/bignum.cc b/mfbt/double-conversion/double-conversion/bignum.cc similarity index 98% rename from mfbt/double-conversion/source/bignum.cc rename to mfbt/double-conversion/double-conversion/bignum.cc index 8892de8f2a3a..a7bc86d0c0e1 100644 --- a/mfbt/double-conversion/source/bignum.cc +++ b/mfbt/double-conversion/double-conversion/bignum.cc @@ -25,13 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "bignum.h" -#include "utils.h" +#include +#include namespace double_conversion { Bignum::Bignum() - : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + : bigits_buffer_(), bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { for (int i = 0; i < kBigitCapacity; ++i) { bigits_[i] = 0; } @@ -445,26 +445,27 @@ void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { mask >>= 2; uint64_t this_value = base; - bool delayed_multipliciation = false; + bool delayed_multiplication = false; const uint64_t max_32bits = 0xFFFFFFFF; while (mask != 0 && this_value <= max_32bits) { this_value = this_value * this_value; // Verify that there is enough space in this_value to perform the // multiplication. The first bit_size bits must be 0. if ((power_exponent & mask) != 0) { + ASSERT(bit_size > 0); uint64_t base_bits_mask = ~((static_cast(1) << (64 - bit_size)) - 1); bool high_bits_zero = (this_value & base_bits_mask) == 0; if (high_bits_zero) { this_value *= base; } else { - delayed_multipliciation = true; + delayed_multiplication = true; } } mask >>= 1; } AssignUInt64(this_value); - if (delayed_multipliciation) { + if (delayed_multiplication) { MultiplyByUInt32(base); } diff --git a/mfbt/double-conversion/source/bignum.h b/mfbt/double-conversion/double-conversion/bignum.h similarity index 99% rename from mfbt/double-conversion/source/bignum.h rename to mfbt/double-conversion/double-conversion/bignum.h index c385f2237bae..4ff34e2693b3 100644 --- a/mfbt/double-conversion/source/bignum.h +++ b/mfbt/double-conversion/double-conversion/bignum.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_BIGNUM_H_ #define DOUBLE_CONVERSION_BIGNUM_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/cached-powers.cc b/mfbt/double-conversion/double-conversion/cached-powers.cc similarity index 98% rename from mfbt/double-conversion/source/cached-powers.cc rename to mfbt/double-conversion/double-conversion/cached-powers.cc index 2b43f0641277..6f771e9c73d9 100644 --- a/mfbt/double-conversion/source/cached-powers.cc +++ b/mfbt/double-conversion/double-conversion/cached-powers.cc @@ -25,13 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include -#include +#include +#include +#include -#include "utils.h" +#include -#include "cached-powers.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/cached-powers.h b/mfbt/double-conversion/double-conversion/cached-powers.h similarity index 98% rename from mfbt/double-conversion/source/cached-powers.h rename to mfbt/double-conversion/double-conversion/cached-powers.h index 61a50614cf10..eabff4a15afd 100644 --- a/mfbt/double-conversion/source/cached-powers.h +++ b/mfbt/double-conversion/double-conversion/cached-powers.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_ #define DOUBLE_CONVERSION_CACHED_POWERS_H_ -#include "diy-fp.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/diy-fp.cc b/mfbt/double-conversion/double-conversion/diy-fp.cc similarity index 97% rename from mfbt/double-conversion/source/diy-fp.cc rename to mfbt/double-conversion/double-conversion/diy-fp.cc index ddd1891b168a..82b0d08af4ef 100644 --- a/mfbt/double-conversion/source/diy-fp.cc +++ b/mfbt/double-conversion/double-conversion/diy-fp.cc @@ -26,8 +26,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "diy-fp.h" -#include "utils.h" +#include +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/diy-fp.h b/mfbt/double-conversion/double-conversion/diy-fp.h similarity index 99% rename from mfbt/double-conversion/source/diy-fp.h rename to mfbt/double-conversion/double-conversion/diy-fp.h index 2edf34674ee2..e2011d43e5aa 100644 --- a/mfbt/double-conversion/source/diy-fp.h +++ b/mfbt/double-conversion/double-conversion/diy-fp.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_DIY_FP_H_ #define DOUBLE_CONVERSION_DIY_FP_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/double-conversion.cc b/mfbt/double-conversion/double-conversion/double-conversion.cc similarity index 94% rename from mfbt/double-conversion/source/double-conversion.cc rename to mfbt/double-conversion/double-conversion/double-conversion.cc index dee8b1760ea1..9a6bb0a72f06 100644 --- a/mfbt/double-conversion/source/double-conversion.cc +++ b/mfbt/double-conversion/double-conversion/double-conversion.cc @@ -25,17 +25,18 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include +#include +#include +#include -#include "double-conversion.h" +#include -#include "bignum-dtoa.h" -#include "fast-dtoa.h" -#include "fixed-dtoa.h" -#include "ieee.h" -#include "strtod.h" -#include "utils.h" +#include +#include +#include +#include +#include +#include namespace double_conversion { @@ -417,21 +418,55 @@ void DoubleToStringConverter::DoubleToAscii(double v, } -// Consumes the given substring from the iterator. -// Returns false, if the substring does not match. -template -static bool ConsumeSubString(Iterator* current, - Iterator end, - const char* substring) { - ASSERT(**current == *substring); +namespace { + +inline char ToLower(char ch) { + static const std::ctype& cType = + std::use_facet >(std::locale::classic()); + return cType.tolower(ch); +} + +inline char Pass(char ch) { + return ch; +} + +template +static inline bool ConsumeSubStringImpl(Iterator* current, + Iterator end, + const char* substring, + Converter converter) { + ASSERT(converter(**current) == *substring); for (substring++; *substring != '\0'; substring++) { ++*current; - if (*current == end || **current != *substring) return false; + if (*current == end || converter(**current) != *substring) { + return false; + } } ++*current; return true; } +// Consumes the given substring from the iterator. +// Returns false, if the substring does not match. +template +static bool ConsumeSubString(Iterator* current, + Iterator end, + const char* substring, + bool allow_case_insensibility) { + if (allow_case_insensibility) { + return ConsumeSubStringImpl(current, end, substring, ToLower); + } else { + return ConsumeSubStringImpl(current, end, substring, Pass); + } +} + +// Consumes first character of the str is equal to ch +inline bool ConsumeFirstCharacter(char ch, + const char* str, + bool case_insensibility) { + return case_insensibility ? ToLower(ch) == str[0] : ch == str[0]; +} +} // namespace // Maximum number of significant digits in decimal representation. // The longest possible double in decimal representation is @@ -632,7 +667,6 @@ static double RadixStringToIeee(Iterator* current, return Double(DiyFp(number, exponent)).value(); } - template double StringToDoubleConverter::StringToIeee( Iterator input, @@ -648,6 +682,8 @@ double StringToDoubleConverter::StringToIeee( const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + const bool allow_case_insensibility = (flags_ & ALLOW_CASE_INSENSIBILITY) != 0; + // To make sure that iterator dereferencing is valid the following // convention is used: @@ -697,8 +733,8 @@ double StringToDoubleConverter::StringToIeee( } if (infinity_symbol_ != NULL) { - if (*current == infinity_symbol_[0]) { - if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + if (ConsumeFirstCharacter(*current, infinity_symbol_, allow_case_insensibility)) { + if (!ConsumeSubString(¤t, end, infinity_symbol_, allow_case_insensibility)) { return junk_string_value_; } @@ -716,8 +752,8 @@ double StringToDoubleConverter::StringToIeee( } if (nan_symbol_ != NULL) { - if (*current == nan_symbol_[0]) { - if (!ConsumeSubString(¤t, end, nan_symbol_)) { + if (ConsumeFirstCharacter(*current, nan_symbol_, allow_case_insensibility)) { + if (!ConsumeSubString(¤t, end, nan_symbol_, allow_case_insensibility)) { return junk_string_value_; } diff --git a/mfbt/double-conversion/source/double-conversion.h b/mfbt/double-conversion/double-conversion/double-conversion.h similarity index 99% rename from mfbt/double-conversion/source/double-conversion.h rename to mfbt/double-conversion/double-conversion/double-conversion.h index 577013a0430c..401087fb278e 100644 --- a/mfbt/double-conversion/source/double-conversion.h +++ b/mfbt/double-conversion/double-conversion/double-conversion.h @@ -29,7 +29,7 @@ #define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_ #include "mozilla/Types.h" -#include "utils.h" +#include namespace double_conversion { @@ -391,7 +391,8 @@ class StringToDoubleConverter { ALLOW_TRAILING_JUNK = 4, ALLOW_LEADING_SPACES = 8, ALLOW_TRAILING_SPACES = 16, - ALLOW_SPACES_AFTER_SIGN = 32 + ALLOW_SPACES_AFTER_SIGN = 32, + ALLOW_CASE_INSENSIBILITY = 64, }; // Flags should be a bit-or combination of the possible Flags-enum. @@ -423,6 +424,8 @@ class StringToDoubleConverter { // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign. // Ex: StringToDouble("- 123.2") -> -123.2. // StringToDouble("+ 123.2") -> 123.2 + // - ALLOW_CASE_INSENSIBILITY: ignore case of characters for special values: + // infinity and nan. // // empty_string_value is returned when an empty string is given as input. // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string diff --git a/mfbt/double-conversion/source/fast-dtoa.cc b/mfbt/double-conversion/double-conversion/fast-dtoa.cc similarity index 99% rename from mfbt/double-conversion/source/fast-dtoa.cc rename to mfbt/double-conversion/double-conversion/fast-dtoa.cc index 61350383a95e..e5c222291f5e 100644 --- a/mfbt/double-conversion/source/fast-dtoa.cc +++ b/mfbt/double-conversion/double-conversion/fast-dtoa.cc @@ -25,11 +25,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "fast-dtoa.h" +#include -#include "cached-powers.h" -#include "diy-fp.h" -#include "ieee.h" +#include +#include +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/fast-dtoa.h b/mfbt/double-conversion/double-conversion/fast-dtoa.h similarity index 99% rename from mfbt/double-conversion/source/fast-dtoa.h rename to mfbt/double-conversion/double-conversion/fast-dtoa.h index 5f1e8eee5e56..ac4317c04dd4 100644 --- a/mfbt/double-conversion/source/fast-dtoa.h +++ b/mfbt/double-conversion/double-conversion/fast-dtoa.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_FAST_DTOA_H_ #define DOUBLE_CONVERSION_FAST_DTOA_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/fixed-dtoa.cc b/mfbt/double-conversion/double-conversion/fixed-dtoa.cc similarity index 98% rename from mfbt/double-conversion/source/fixed-dtoa.cc rename to mfbt/double-conversion/double-conversion/fixed-dtoa.cc index 78378c513961..8c111aca6425 100644 --- a/mfbt/double-conversion/source/fixed-dtoa.cc +++ b/mfbt/double-conversion/double-conversion/fixed-dtoa.cc @@ -25,10 +25,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include -#include "fixed-dtoa.h" -#include "ieee.h" +#include +#include namespace double_conversion { @@ -259,7 +259,8 @@ static void FillFractionals(uint64_t fractionals, int exponent, fractionals -= static_cast(digit) << point; } // If the first bit after the point is set we have to round up. - if (((fractionals >> (point - 1)) & 1) == 1) { + ASSERT(fractionals == 0 || point - 1 >= 0); + if ((fractionals != 0) && ((fractionals >> (point - 1)) & 1) == 1) { RoundUp(buffer, length, decimal_point); } } else { // We need 128 bits. diff --git a/mfbt/double-conversion/source/fixed-dtoa.h b/mfbt/double-conversion/double-conversion/fixed-dtoa.h similarity index 98% rename from mfbt/double-conversion/source/fixed-dtoa.h rename to mfbt/double-conversion/double-conversion/fixed-dtoa.h index 3bdd08e21f59..a9436fc9f617 100644 --- a/mfbt/double-conversion/source/fixed-dtoa.h +++ b/mfbt/double-conversion/double-conversion/fixed-dtoa.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_ #define DOUBLE_CONVERSION_FIXED_DTOA_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/ieee.h b/mfbt/double-conversion/double-conversion/ieee.h similarity index 99% rename from mfbt/double-conversion/source/ieee.h rename to mfbt/double-conversion/double-conversion/ieee.h index b14cf4f7172a..1b705d2ee23c 100644 --- a/mfbt/double-conversion/source/ieee.h +++ b/mfbt/double-conversion/double-conversion/ieee.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_DOUBLE_H_ #define DOUBLE_CONVERSION_DOUBLE_H_ -#include "diy-fp.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/strtod.cc b/mfbt/double-conversion/double-conversion/strtod.cc similarity index 99% rename from mfbt/double-conversion/source/strtod.cc rename to mfbt/double-conversion/double-conversion/strtod.cc index 17abcbb2a557..3a59b699d302 100644 --- a/mfbt/double-conversion/source/strtod.cc +++ b/mfbt/double-conversion/double-conversion/strtod.cc @@ -25,13 +25,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include +#include +#include -#include "strtod.h" -#include "bignum.h" -#include "cached-powers.h" -#include "ieee.h" +#include +#include +#include +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/strtod.h b/mfbt/double-conversion/double-conversion/strtod.h similarity index 98% rename from mfbt/double-conversion/source/strtod.h rename to mfbt/double-conversion/double-conversion/strtod.h index ed0293b8f54a..322651621fb2 100644 --- a/mfbt/double-conversion/source/strtod.h +++ b/mfbt/double-conversion/double-conversion/strtod.h @@ -28,7 +28,7 @@ #ifndef DOUBLE_CONVERSION_STRTOD_H_ #define DOUBLE_CONVERSION_STRTOD_H_ -#include "utils.h" +#include namespace double_conversion { diff --git a/mfbt/double-conversion/source/utils.h b/mfbt/double-conversion/double-conversion/utils.h similarity index 99% rename from mfbt/double-conversion/source/utils.h rename to mfbt/double-conversion/double-conversion/utils.h index e5c787652a6a..b287c99986ae 100644 --- a/mfbt/double-conversion/source/utils.h +++ b/mfbt/double-conversion/double-conversion/utils.h @@ -28,8 +28,8 @@ #ifndef DOUBLE_CONVERSION_UTILS_H_ #define DOUBLE_CONVERSION_UTILS_H_ -#include -#include +#include +#include #include "mozilla/Assertions.h" #ifndef ASSERT diff --git a/mfbt/double-conversion/update.sh b/mfbt/double-conversion/update.sh index 0a54ca3e7a01..a9956f3ef5c0 100755 --- a/mfbt/double-conversion/update.sh +++ b/mfbt/double-conversion/update.sh @@ -16,7 +16,7 @@ LOCAL_PATCHES="$LOCAL_PATCHES use-mozilla-assertions.patch" LOCAL_PATCHES="$LOCAL_PATCHES ToPrecision-exponential.patch" TMPDIR=`mktemp --directory` -LOCAL_CLONE="$TMPDIR/double-conversion" +LOCAL_CLONE="$TMPDIR/new-double-conversion" git clone https://github.com/google/double-conversion.git "$LOCAL_CLONE" @@ -26,8 +26,8 @@ if [ "$1" != "" ]; then fi # First clear out everything already present. -DEST=./source -mv "$DEST" "$TMPDIR"/ +DEST=./double-conversion +mv "$DEST" "$TMPDIR"/old-double-conversion mkdir "$DEST" # Copy over critical files. diff --git a/mfbt/double-conversion/use-StandardInteger.patch b/mfbt/double-conversion/use-StandardInteger.patch index 909aa579a729..04912b40df5e 100644 --- a/mfbt/double-conversion/use-StandardInteger.patch +++ b/mfbt/double-conversion/use-StandardInteger.patch @@ -1,6 +1,6 @@ -diff --git a/mfbt/double-conversion/source/utils.h b/mfbt/double-conversion/source/utils.h ---- a/mfbt/double-conversion/source/utils.h -+++ b/mfbt/double-conversion/source/utils.h +diff --git a/mfbt/double-conversion/double-conversion/utils.h b/mfbt/double-conversion/double-conversion/utils.h +--- a/mfbt/double-conversion/double-conversion/utils.h ++++ b/mfbt/double-conversion/double-conversion/utils.h @@ -93,34 +93,18 @@ inline void abort_noreturn() { abort(); #endif diff --git a/mfbt/double-conversion/use-mozilla-assertions.patch b/mfbt/double-conversion/use-mozilla-assertions.patch index 99f85e89ba8b..a0a0b0d1c920 100644 --- a/mfbt/double-conversion/use-mozilla-assertions.patch +++ b/mfbt/double-conversion/use-mozilla-assertions.patch @@ -1,16 +1,16 @@ -diff --git a/mfbt/double-conversion/source/utils.h b/mfbt/double-conversion/source/utils.h ---- a/mfbt/double-conversion/source/utils.h -+++ b/mfbt/double-conversion/source/utils.h +diff --git a/mfbt/double-conversion/double-conversion/utils.h b/mfbt/double-conversion/double-conversion/utils.h +--- a/mfbt/double-conversion/double-conversion/utils.h ++++ b/mfbt/double-conversion/double-conversion/utils.h @@ -26,38 +26,38 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef DOUBLE_CONVERSION_UTILS_H_ #define DOUBLE_CONVERSION_UTILS_H_ - #include - #include + #include + #include --#include +-#include +#include "mozilla/Assertions.h" #ifndef ASSERT #define ASSERT(condition) \ diff --git a/mfbt/moz.build b/mfbt/moz.build index e97991b81f34..ef0f1952935b 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -34,8 +34,7 @@ EXPORTS.mozilla = [ 'DebugOnly.h', 'decimal/Decimal.h', 'DefineEnum.h', - 'double-conversion/source/double-conversion.h', - 'double-conversion/source/utils.h', + 'double-conversion.h', 'DoublyLinkedList.h', 'EndianUtils.h', 'EnumeratedArray.h', @@ -106,6 +105,15 @@ EXPORTS.mozilla = [ 'XorShift128PlusRNG.h', ] +EXPORTS["double-conversion"] = [ + 'double-conversion/double-conversion/double-conversion.h', + 'double-conversion/double-conversion/utils.h', +] + +LOCAL_INCLUDES += [ + '/mfbt/double-conversion', +] + if CONFIG['OS_ARCH'] == 'WINNT': EXPORTS.mozilla += [ 'WindowsVersion.h', diff --git a/mfbt/objs.mozbuild b/mfbt/objs.mozbuild index 6630a6e42443..1fda2db29bd9 100644 --- a/mfbt/objs.mozbuild +++ b/mfbt/objs.mozbuild @@ -7,14 +7,14 @@ mfbt_src_lcppsrcs = [ 'Assertions.cpp', 'ChaosMode.cpp', - 'double-conversion/source/bignum-dtoa.cc', - 'double-conversion/source/bignum.cc', - 'double-conversion/source/cached-powers.cc', - 'double-conversion/source/diy-fp.cc', - 'double-conversion/source/double-conversion.cc', - 'double-conversion/source/fast-dtoa.cc', - 'double-conversion/source/fixed-dtoa.cc', - 'double-conversion/source/strtod.cc', + 'double-conversion/double-conversion/bignum-dtoa.cc', + 'double-conversion/double-conversion/bignum.cc', + 'double-conversion/double-conversion/cached-powers.cc', + 'double-conversion/double-conversion/diy-fp.cc', + 'double-conversion/double-conversion/double-conversion.cc', + 'double-conversion/double-conversion/fast-dtoa.cc', + 'double-conversion/double-conversion/fixed-dtoa.cc', + 'double-conversion/double-conversion/strtod.cc', 'FloatingPoint.cpp', 'HashFunctions.cpp', 'JSONWriter.cpp', diff --git a/mfbt/staticruntime/moz.build b/mfbt/staticruntime/moz.build index 71d2ad79da58..534b0e8d5f34 100644 --- a/mfbt/staticruntime/moz.build +++ b/mfbt/staticruntime/moz.build @@ -6,6 +6,10 @@ Library('mfbt_staticruntime') +LOCAL_INCLUDES += [ + '/mfbt/double-conversion', +] + include('../objs.mozbuild') UNIFIED_SOURCES += mfbt_src_cppsrcs diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 96da1e47c11d..ad83b69705c2 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1457,11 +1457,7 @@ pref("javascript.options.discardSystemSource", false); // js/src/jsgc.cpp. They're documented in js/src/jsapi.h. // JSGC_MAX_MALLOC_BYTES -// This preference limits the malloc memory that javascript objects may use. -// If you want to change these values for your device, -// please find Bug 417052 comment 17 and Bug 456721 -// Comment 32 and Bug 613551. -// Override the shell's default of 0xffffffff +// How much malloc memory can be allocated before triggering a GC, in MB. pref("javascript.options.mem.high_water_mark", 128); // JSGC_MAX_BYTES diff --git a/netwerk/base/RequestContextService.cpp b/netwerk/base/RequestContextService.cpp index 0cfacef66e02..e6e40a0ecafa 100644 --- a/netwerk/base/RequestContextService.cpp +++ b/netwerk/base/RequestContextService.cpp @@ -396,8 +396,8 @@ RequestContext::CancelTailedRequest(nsIRequestTailUnblockCallback * aRequest) mUntailTimer = nullptr; } - // Must drop to stop tailing requests - mUntailAt = TimeStamp(); + // Must drop to allow re-engage of the timer + mTimerScheduledAt = TimeStamp(); } return NS_OK; diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 8639435a1b5a..322216ebde34 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -1431,7 +1431,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) "HttpChannelParent getting OnStartRequest from a different HttpBaseChannel instance"); // Send down any permissions which are relevant to this URL if we are - // performing a document load. We can't do that is mIPCClosed is set. + // performing a document load. We can't do that if mIPCClosed is set. if (!mIPCClosed) { PContentParent* pcp = Manager()->Manager(); MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed"); diff --git a/netwerk/protocol/http/InterceptedHttpChannel.cpp b/netwerk/protocol/http/InterceptedHttpChannel.cpp index a9520d92c370..59859b46030b 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.cpp +++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp @@ -86,6 +86,76 @@ InterceptedHttpChannel::SetupReplacementChannel(nsIURI *aURI, return NS_OK; } +void +InterceptedHttpChannel::AsyncOpenInternal() +{ + // If an error occurs in this file we must ensure mListener callbacks are + // invoked in some way. We either Cancel() or ResetInterception below + // depending on which path we take. + nsresult rv = NS_OK; + + // We should have pre-set the AsyncOpen time based on the original channel if + // timings are enabled. + if (mTimingEnabled) { + MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull()); + } + + mIsPending = true; + mResponseCouldBeSynthesized = true; + + if (mLoadGroup) { + mLoadGroup->AddRequest(this, nullptr); + } + + // If we already have a synthesized body then we are pre-synthesized. + // This can happen for two reasons: + // 1. We have a pre-synthesized redirect in e10s mode. In this case + // we should follow the redirect. + // 2. We are handling a "fake" redirect for an opaque response. Here + // we should just process the synthetic body. + if (mBodyReader) { + // If we fail in this path, then cancel the channel. We don't want + // to ResetInterception() after a synthetic result has already been + // produced by the ServiceWorker. + auto autoCancel = MakeScopeExit([&] { + if (NS_FAILED(rv)) { + Cancel(rv); + } + }); + + if (ShouldRedirect()) { + rv = FollowSyntheticRedirect(); + return; + } + + rv = StartPump(); + return; + } + + // If we fail the initial interception, then attempt to ResetInterception + // to fall back to network. We only cancel if the reset fails. + auto autoReset = MakeScopeExit([&] { + if (NS_FAILED(rv)) { + rv = ResetInterception(); + if (NS_WARN_IF(NS_FAILED(rv))) { + Cancel(rv); + } + } + }); + + // Otherwise we need to trigger a FetchEvent in a ServiceWorker. + nsCOMPtr controller; + GetCallback(controller); + + if (NS_WARN_IF(!controller)) { + rv = NS_ERROR_DOM_INVALID_STATE_ERR; + return; + } + + rv = controller->ChannelIntercepted(this); + NS_ENSURE_SUCCESS_VOID(rv); +} + bool InterceptedHttpChannel::ShouldRedirect() const { @@ -385,7 +455,25 @@ InterceptedHttpChannel::CreateForSynthesis(const nsHttpResponseHead* aHead, NS_IMETHODIMP InterceptedHttpChannel::Cancel(nsresult aStatus) { - return CancelInterception(aStatus); + // Note: This class has been designed to send all error results through + // Cancel(). Don't add calls directly to AsyncAbort() or + // DoNotifyListener(). Instead call Cancel(). + + if (mCanceled) { + return NS_OK; + } + mCanceled = true; + + MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus)); + if (NS_SUCCEEDED(mStatus)) { + mStatus = aStatus; + } + + if (mPump) { + return mPump->Cancel(mStatus); + } + + return AsyncAbort(mStatus); } NS_IMETHODIMP @@ -429,51 +517,11 @@ InterceptedHttpChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aCo return mStatus; } - // We should have pre-set the AsyncOpen time based on the original channel if - // timings are enabled. - if (mTimingEnabled) { - MOZ_DIAGNOSTIC_ASSERT(!mAsyncOpenTime.IsNull()); - } - - mIsPending = true; + // After this point we should try to return NS_OK and notify the listener + // of the result. mListener = aListener; - mResponseCouldBeSynthesized = true; - - if (mLoadGroup) { - mLoadGroup->AddRequest(this, nullptr); - } - - // If we already have a synthesized body then we are pre-synthesized. - // This can happen for two reasons: - // 1. We have a pre-synthesized redirect in e10s mode. In this case - // we should follow the redirect. - // 2. We are handling a "fake" redirect for an opaque response. Here - // we should just process the synthetic body. - if (mBodyReader) { - if (ShouldRedirect()) { - return FollowSyntheticRedirect(); - } - - return StartPump(); - } - - // Otherwise we need to trigger a FetchEvent in a ServiceWorker. - nsCOMPtr controller; - GetCallback(controller); - - if (NS_WARN_IF(!controller)) { - Cancel(NS_ERROR_FAILURE); - DoNotifyListener(); - return NS_ERROR_FAILURE; - } - - nsresult rv = controller->ChannelIntercepted(this); - if (NS_WARN_IF(NS_FAILED(rv))) { - Cancel(rv); - DoNotifyListener(); - return rv; - } + AsyncOpenInternal(); return NS_OK; } @@ -484,8 +532,7 @@ InterceptedHttpChannel::AsyncOpen2(nsIStreamListener* aListener) nsCOMPtr listener(aListener); nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener); if (NS_WARN_IF(NS_FAILED(rv))) { - mStatus = rv; - DoNotifyListener(); + Cancel(rv); return rv; } return AsyncOpen(listener, nullptr); @@ -697,21 +744,7 @@ InterceptedHttpChannel::FinishSynthesizedResponse(const nsACString& aFinalURLSpe NS_IMETHODIMP InterceptedHttpChannel::CancelInterception(nsresult aStatus) { - if (mCanceled) { - return NS_OK; - } - mCanceled = true; - - MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(aStatus)); - if (NS_SUCCEEDED(mStatus)) { - mStatus = aStatus; - } - - if (mPump) { - return mPump->Cancel(mStatus); - } - - return AsyncAbort(mStatus); + return Cancel(aStatus); } NS_IMETHODIMP diff --git a/netwerk/protocol/http/InterceptedHttpChannel.h b/netwerk/protocol/http/InterceptedHttpChannel.h index 944dda8f83fb..6d188bc27c4d 100644 --- a/netwerk/protocol/http/InterceptedHttpChannel.h +++ b/netwerk/protocol/http/InterceptedHttpChannel.h @@ -104,6 +104,9 @@ private: bool aPreserveMethod, uint32_t aRedirectFlags) override; + void + AsyncOpenInternal(); + bool ShouldRedirect() const; diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 1a468409c99e..cf8dd43a9fe0 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -107,6 +107,7 @@ private: public: NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { + // Note: MainThread if (strcmp(aTopic, "xpcom-will-shutdown") == 0) { LOG(("Shutting down SCTP")); if (sctp_initialized) { @@ -295,12 +296,6 @@ DataChannelConnection::Destroy() MOZ_ASSERT(mSTS); ASSERT_WEBRTC(NS_IsMainThread()); - // Must do this in Destroy() since we may then delete this object. - // Do this before dispatching to create a consistent ordering of calls to - // the SCTP stack. - usrsctp_deregister_address(static_cast(this)); - LOG(("Deregistered %p from the SCTP stack.", static_cast(this))); - // Finish Destroy on STS thread to avoid bug 876167 - once that's fixed, // the usrsctp_close() calls can move back here (and just proxy the // disconnect_all()) @@ -327,6 +322,9 @@ void DataChannelConnection::DestroyOnSTS(struct socket *aMasterSocket, if (aMasterSocket) usrsctp_close(aMasterSocket); + usrsctp_deregister_address(static_cast(this)); + LOG(("Deregistered %p from the SCTP stack.", static_cast(this))); + disconnect_all(); } diff --git a/netwerk/test/unit/test_synthesized_response.js b/netwerk/test/unit/test_synthesized_response.js index 5668ba5020b1..0693abf460fc 100644 --- a/netwerk/test/unit/test_synthesized_response.js +++ b/netwerk/test/unit/test_synthesized_response.js @@ -238,6 +238,16 @@ add_test(function() { CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)); }); +// Ensure that nsIInterceptedChannel.channelIntercepted() can return an error. +// In this case we should automatically ResetInterception() and complete the +// network request. +add_test(function() { + var chan = make_channel(URL + '/body', null, function(chan) { + throw('boom'); + }); + chan.asyncOpen2(new ChannelListener(handle_remote_response, null)); +}); + add_test(function() { httpServer.stop(run_next_test); }); diff --git a/taskcluster/ci/test/test-sets.yml b/taskcluster/ci/test/test-sets.yml index 581847f45e28..ecedf65fc9cd 100644 --- a/taskcluster/ci/test/test-sets.yml +++ b/taskcluster/ci/test/test-sets.yml @@ -172,6 +172,7 @@ windows-tests: - mochitest-webgl - reftest - reftest-no-accel + - test-verify - web-platform-tests - web-platform-tests-reftests - xpcshell @@ -227,6 +228,7 @@ macosx64-tests: - mochitest-media - mochitest-webgl - reftest + - test-verify - web-platform-tests - web-platform-tests-reftests - xpcshell diff --git a/taskcluster/ci/test/tests.yml b/taskcluster/ci/test/tests.yml index 233ab1b6b33b..765fb606b051 100644 --- a/taskcluster/ci/test/tests.yml +++ b/taskcluster/ci/test/tests.yml @@ -1898,7 +1898,9 @@ test-verify: default: built-projects tier: by-test-platform: - windows10-64-asan.*: 3 + macosx.*: 3 + windows.*: 3 + # windows10-64-asan.*: 3 default: 2 mozharness: script: desktop_unittest.py diff --git a/taskcluster/taskgraph/transforms/job/mozharness_test.py b/taskcluster/taskgraph/transforms/job/mozharness_test.py index 3ecfb6cc9590..56e165b19525 100644 --- a/taskcluster/taskgraph/transforms/job/mozharness_test.py +++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py @@ -248,6 +248,8 @@ def mozharness_test_on_generic_worker(config, job, taskdesc): env = worker.setdefault('env', {}) env['MOZ_AUTOMATION'] = '1' + env['GECKO_HEAD_REPOSITORY'] = config.params['head_repository'] + env['GECKO_HEAD_REV'] = config.params['head_rev'] # this list will get cleaned up / reduced / removed in bug 1354088 if is_macosx: diff --git a/testing/firefox-ui/tests/functional/sessionstore/manifest.ini b/testing/firefox-ui/tests/functional/sessionstore/manifest.ini index ea51e9e4238e..59e83f444059 100644 --- a/testing/firefox-ui/tests/functional/sessionstore/manifest.ini +++ b/testing/firefox-ui/tests/functional/sessionstore/manifest.ini @@ -2,4 +2,5 @@ tags = local [test_tabbar_session_restore_button.py] +skip-if = os == "mac" # Bug 1396803 [test_restore_windows_after_restart_and_quit.py] diff --git a/testing/marionette/atom.js b/testing/marionette/atom.js index 694d4dde7756..0a803cf1204a 100644 --- a/testing/marionette/atom.js +++ b/testing/marionette/atom.js @@ -235,59 +235,6 @@ L(a);var d=[];for(var e=M(c);e;e=M(c))d.push(e);this.snapshotLength=a.l;this.inv Z.NUMBER_TYPE=1;Z.STRING_TYPE=2;Z.BOOLEAN_TYPE=3;Z.UNORDERED_NODE_ITERATOR_TYPE=4;Z.ORDERED_NODE_ITERATOR_TYPE=5;Z.UNORDERED_NODE_SNAPSHOT_TYPE=6;Z.ORDERED_NODE_SNAPSHOT_TYPE=7;Z.ANY_UNORDERED_NODE_TYPE=8;Z.FIRST_ORDERED_NODE_TYPE=9;function lb(a){this.lookupNamespaceURI=ka(a)} aa("wgxpath.install",function(a,b){a=a||h;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=Z,c.evaluate=function(a,b,c,g){return(new kb(a,c)).evaluate(b,g)},c.createExpression=function(a,b){return new kb(a,b)},c.createNSResolver=function(a){return new lb(a)}});var mb="BUTTON INPUT OPTGROUP OPTION SELECT TEXTAREA".split(" ");function nb(a){return C(mb,function(b){return I(a,b)})?a.disabled?!1:a.parentNode&&1==a.parentNode.nodeType&&I(a,"OPTGROUP")||I(a,"OPTION")?nb(a.parentNode):!ua(a,function(a){var b=a.parentNode;if(b&&I(b,"FIELDSET")&&b.disabled){if(!I(a,"LEGEND"))return!0;for(;a=void 0!==a.previousElementSibling?a.previousElementSibling:pa(a.previousSibling);)if(I(a,"LEGEND"))return!0}return!1}):!0};aa("_",nb);; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);} -// https://github.com/SeleniumHQ/selenium/blob/master/javascript/atoms/domcore.js#L198 -atom.isElementSelected = function(element, window){return function(){var f=this;function k(a){return"string"==typeof a}function aa(a,b){a=a.split(".");var c=f;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b} -function ba(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; -else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function ca(a,b,c){return a.call.apply(a.bind,arguments)}function da(a,b,c){if(!a)throw Error();if(2b||a.indexOf("Error",b)!=b)a+="Error";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||""}n(fa,Error);var q="unknown error",p={15:"element not selectable",11:"element not visible"};p[31]=q;p[30]=q;p[24]="invalid cookie domain";p[29]="invalid element coordinates";p[12]="invalid element state"; -p[32]="invalid selector";p[51]="invalid selector";p[52]="invalid selector";p[17]="javascript error";p[405]="unsupported operation";p[34]="move target out of bounds";p[27]="no such alert";p[7]="no such element";p[8]="no such frame";p[23]="no such window";p[28]="script timeout";p[33]="session not created";p[10]="stale element reference";p[21]="timeout";p[25]="unable to set cookie";p[26]="unexpected alert open";p[13]=q;p[9]="unknown command";fa.prototype.toString=function(){return this.name+": "+this.message}; -function r(a,b,c){this.a=a;this.b=b||1;this.i=c||1};function ga(a){this.b=a;this.a=0}function ha(a){a=a.match(ia);for(var b=0;b]=|\s+|./g,ja=/^\s/;function t(a,b){return a.b[a.a+(b||0)]}ga.prototype.next=function(){return this.b[this.a++]};function u(a){return a.b.length<=a.a};var v;a:{var ka=f.navigator;if(ka){var la=ka.userAgent;if(la){v=la;break a}}v=""};function x(a,b){this.j=a;this.c=void 0!==b?b:null;this.b=null;switch(a){case "comment":this.b=8;break;case "text":this.b=3;break;case "processing-instruction":this.b=7;break;case "node":break;default:throw Error("Unexpected argument");}}function ma(a){return"comment"==a||"text"==a||"processing-instruction"==a||"node"==a}x.prototype.a=function(a){return null===this.b||this.b==a.nodeType};x.prototype.i=function(){return this.j}; -x.prototype.toString=function(){var a="Kind Test: "+this.j;null===this.c||(a+=y(this.c));return a};function z(a,b){this.o=a.toLowerCase();a="*"==this.o?"*":"http://www.w3.org/1999/xhtml";this.b=b?b.toLowerCase():a}z.prototype.a=function(a){var b=a.nodeType;if(1!=b&&2!=b)return!1;b=void 0!==a.localName?a.localName:a.nodeName;return"*"!=this.o&&this.o!=b.toLowerCase()?!1:"*"==this.b?!0:this.b==(a.namespaceURI?a.namespaceURI.toLowerCase():"http://www.w3.org/1999/xhtml")};z.prototype.i=function(){return this.o}; -z.prototype.toString=function(){return"Name Test: "+("http://www.w3.org/1999/xhtml"==this.b?"":this.b+":")+this.o};function na(a){switch(a.nodeType){case 1:return ea(oa,a);case 9:return na(a.documentElement);case 11:case 10:case 6:case 12:return pa;default:return a.parentNode?na(a.parentNode):pa}}function pa(){return null}function oa(a,b){if(a.prefix==b)return a.namespaceURI||"http://www.w3.org/1999/xhtml";var c=a.getAttributeNode("xmlns:"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?oa(a.parentNode,b):null};function A(a,b){for(var c=a.length,d=k(a)?a.split(""):a,e=0;e=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var sa=-1!=v.indexOf("Macintosh"),ta=-1!=v.indexOf("Windows");function E(a,b){this.w={};this.m=[];this.a=0;var c=arguments.length;if(1",4,2,function(a,b,c){return Q(function(a,b){return a>b},a,b,c)});R("<=",4,2,function(a,b,c){return Q(function(a,b){return a<=b},a,b,c)});R(">=",4,2,function(a,b,c){return Q(function(a,b){return a>=b},a,b,c)});var Pa=R("=",3,2,function(a,b,c){return Q(function(a,b){return a==b},a,b,c,!0)});R("!=",3,2,function(a,b,c){return Q(function(a,b){return a!=b},a,b,c,!0)});R("and",2,2,function(a,b,c){return O(a,c)&&O(b,c)});R("or",1,2,function(a,b,c){return O(a,c)||O(b,c)});function Sa(a,b){if(b.a.length&&4!=a.l)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");K.call(this,a.l);this.c=a;this.j=b;this.h=a.h;this.b=a.b}n(Sa,K);Sa.prototype.a=function(a){a=this.c.a(a);return Ta(this.j,a)};Sa.prototype.toString=function(){var a="Filter:"+y(this.c);return a+=y(this.j)};function Ua(a,b){if(b.lengtha.F)throw Error("Function "+a.o+" expects at most "+a.F+" arguments, "+b.length+" given");a.K&&A(b,function(b,d){if(4!=b.l)throw Error("Argument "+d+" to function "+a.o+" is not of type Nodeset: "+b);});K.call(this,a.l);this.C=a;this.c=b;Na(this,a.h||D(b,function(a){return a.h}));Oa(this,a.J&&!b.length||a.I&&!!b.length||D(b,function(a){return a.b}))}n(Ua,K); -Ua.prototype.a=function(a){return this.C.u.apply(null,qa(a,this.c))};Ua.prototype.toString=function(){var a="Function: "+this.C;if(this.c.length)var b=C(this.c,function(a,b){return a+y(b)},"Arguments:"),a=a+y(b);return a};function Va(a,b,c,d,e,g,h,m,w){this.o=a;this.l=b;this.h=c;this.J=d;this.I=e;this.u=g;this.G=h;this.F=void 0!==m?m:h;this.K=!!w}Va.prototype.toString=function(){return this.o};var Wa={}; -function S(a,b,c,d,e,g,h,m){if(Wa.hasOwnProperty(a))throw Error("Function already created: "+a+".");Wa[a]=new Va(a,b,c,d,!1,e,g,h,m)}S("boolean",2,!1,!1,function(a,b){return O(b,a)},1);S("ceiling",1,!1,!1,function(a,b){return Math.ceil(L(b,a))},1);S("concat",3,!1,!1,function(a,b){return C(ra(arguments,1),function(b,d){return b+M(d,a)},"")},2,null);S("contains",2,!1,!1,function(a,b,c){b=M(b,a);a=M(c,a);return-1!=b.indexOf(a)},2);S("count",1,!1,!1,function(a,b){return b.a(a).s},1,1,!0); -S("false",2,!1,!1,function(){return!1},0);S("floor",1,!1,!1,function(a,b){return Math.floor(L(b,a))},1);S("id",4,!1,!1,function(a,b){var c=a.a,d=9==c.nodeType?c:c.ownerDocument;a=M(b,a).split(/\s+/);var e=[];A(a,function(a){a=d.getElementById(a);var b;if(!(b=!a)){a:if(k(e))b=k(a)&&1==a.length?e.indexOf(a,0):-1;else{for(b=0;ba.length)throw Error("Unclosed literal string");return new Xa(a)} -function tb(a){var b=[];if(bb(t(a.a))){var c=a.a.next();var d=t(a.a);if("/"==c&&(u(a.a)||"."!=d&&".."!=d&&"@"!=d&&"*"!=d&&!/(?![0-9])[\w]/.test(d)))return new T;d=new T;W(a,"Missing next location step.");c=ub(a,c);b.push(c)}else{a:{c=t(a.a);d=c.charAt(0);switch(d){case "$":throw Error("Variable reference not allowed in HTML XPath");case "(":a.a.next();c=ob(a);W(a,'unclosed "("');qb(a,")");break;case '"':case "'":c=sb(a);break;default:if(isNaN(+c))if(!ma(c)&&/(?![0-9])[\w]/.test(d)&&"("==t(a.a,1)){c= -a.a.next();c=Wa[c]||null;a.a.next();for(d=[];")"!=t(a.a);){W(a,"Missing function argument list.");d.push(ob(a));if(","!=t(a.a))break;a.a.next()}W(a,"Unclosed function argument list.");rb(a);c=new Ua(c,d)}else{c=null;break a}else c=new Ya(+a.a.next())}"["==t(a.a)&&(d=new eb(vb(a)),c=new Sa(c,d))}if(c)if(bb(t(a.a)))d=c;else return c;else c=ub(a,"/"),d=new ab,b.push(c)}for(;bb(t(a.a));)c=a.a.next(),W(a,"Missing next location step."),c=ub(a,c),b.push(c);return new Za(d,b)} -function ub(a,b){if("/"!=b&&"//"!=b)throw Error('Step op should be "/" or "//"');if("."==t(a.a)){var c=new U(mb,new x("node"));a.a.next();return c}if(".."==t(a.a))return c=new U(lb,new x("node")),a.a.next(),c;if("@"==t(a.a)){var d=$a;a.a.next();W(a,"Missing attribute name")}else if("::"==t(a.a,1)){if(!/(?![0-9])[\w]/.test(t(a.a).charAt(0)))throw Error("Bad token: "+a.a.next());var e=a.a.next();d=kb[e]||null;if(!d)throw Error("No axis with name: "+e);a.a.next();W(a,"Missing node name")}else d=hb;e= -t(a.a);if(/(?![0-9])[\w\*]/.test(e.charAt(0)))if("("==t(a.a,1)){if(!ma(e))throw Error("Invalid node type: "+e);e=a.a.next();if(!ma(e))throw Error("Invalid type name: "+e);qb(a,"(");W(a,"Bad nodetype");var g=t(a.a).charAt(0),h=null;if('"'==g||"'"==g)h=sb(a);W(a,"Bad nodetype");rb(a);e=new x(e,h)}else if(e=a.a.next(),g=e.indexOf(":"),-1==g)e=new z(e);else{var h=e.substring(0,g);if("*"==h)var m="*";else if(m=a.b(h),!m)throw Error("Namespace prefix not declared: "+h);e=e.substr(g+1);e=new z(e,m)}else throw Error("Bad token: "+ -a.a.next());a=new eb(vb(a),d.A);return c||new U(d,e,a,"//"==b)}function vb(a){for(var b=[];"["==t(a.a);){a.a.next();W(a,"Missing predicate expression.");var c=ob(a);b.push(c);W(a,"Unclosed predicate expression.");qb(a,"]")}return b}function pb(a){if("-"==t(a.a))return a.a.next(),new fb(pb(a));var b=tb(a);if("|"!=t(a.a))a=b;else{for(b=[b];"|"==a.a.next();)W(a,"Missing next union location path."),b.push(tb(a));a.a.a--;a=new gb(b)}return a};function wb(a,b){if(!a.length)throw Error("Empty XPath expression.");a=ha(a);if(u(a))throw Error("Invalid XPath expression.");b?"function"==ba(b)||(b=l(b.lookupNamespaceURI,b)):b=function(){return null};var c=ob(new nb(a,b));if(!u(a))throw Error("Bad token: "+a.next());this.evaluate=function(a,b){a=c.a(new r(a));return new X(a,b)}} -function X(a,b){if(!b)if(a instanceof I)b=4;else if("string"==typeof a)b=2;else if("number"==typeof a)b=1;else if("boolean"==typeof a)b=3;else throw Error("Unexpected evaluation result.");if(2!=b&&1!=b&&3!=b&&!(a instanceof I))throw Error("value could not be converted to the specified type");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof I?La(a):""+a;break;case 1:this.numberValue=a instanceof I?+La(a):+a;break;case 3:this.booleanValue=a instanceof I?0=d.length?null:d[g++]};this.snapshotItem=function(a){if(6!=b&&7!=b)throw Error("snapshotItem called with wrong result type");return a>=d.length||0>a?null:d[a]}} -X.ANY_TYPE=0;X.NUMBER_TYPE=1;X.STRING_TYPE=2;X.BOOLEAN_TYPE=3;X.UNORDERED_NODE_ITERATOR_TYPE=4;X.ORDERED_NODE_ITERATOR_TYPE=5;X.UNORDERED_NODE_SNAPSHOT_TYPE=6;X.ORDERED_NODE_SNAPSHOT_TYPE=7;X.ANY_UNORDERED_NODE_TYPE=8;X.FIRST_ORDERED_NODE_TYPE=9;function xb(a){this.lookupNamespaceURI=na(a)} -aa("wgxpath.install",function(a,b){a=a||f;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=X,c.evaluate=function(a,b,c,h){return(new wb(a,c)).evaluate(b,h)},c.createExpression=function(a,b){return new wb(a,b)},c.createNSResolver=function(a){return new xb(a)}});ya&&ya&&xa(3.6);var yb={};function Y(a,b,c){var d=typeof a;("object"==d&&null!=a||"function"==d)&&(a=a.f);a=new zb(a);!b||b in yb&&!c||(yb[b]={key:a,shift:!1},c&&(yb[c]={key:a,shift:!0}));return a}function zb(a){this.code=a}Y(8);Y(9);Y(13);var Ab=Y(16),Bb=Y(17),Cb=Y(18);Y(19);Y(20);Y(27);Y(32," ");Y(33);Y(34);Y(35);Y(36);Y(37);Y(38);Y(39);Y(40);Y(44);Y(45);Y(46);Y(48,"0",")");Y(49,"1","!");Y(50,"2","@");Y(51,"3","#");Y(52,"4","$");Y(53,"5","%");Y(54,"6","^");Y(55,"7","&");Y(56,"8","*");Y(57,"9","(");Y(65,"a","A"); -Y(66,"b","B");Y(67,"c","C");Y(68,"d","D");Y(69,"e","E");Y(70,"f","F");Y(71,"g","G");Y(72,"h","H");Y(73,"i","I");Y(74,"j","J");Y(75,"k","K");Y(76,"l","L");Y(77,"m","M");Y(78,"n","N");Y(79,"o","O");Y(80,"p","P");Y(81,"q","Q");Y(82,"r","R");Y(83,"s","S");Y(84,"t","T");Y(85,"u","U");Y(86,"v","V");Y(87,"w","W");Y(88,"x","X");Y(89,"y","Y");Y(90,"z","Z");var Db=Y(ta?{f:91,g:91}:sa?{f:224,g:91}:{f:0,g:91});Y(ta?{f:92,g:92}:sa?{f:224,g:93}:{f:0,g:92});Y(ta?{f:93,g:93}:sa?{f:0,g:0}:{f:93,g:null}); -Y({f:96,g:96},"0");Y({f:97,g:97},"1");Y({f:98,g:98},"2");Y({f:99,g:99},"3");Y({f:100,g:100},"4");Y({f:101,g:101},"5");Y({f:102,g:102},"6");Y({f:103,g:103},"7");Y({f:104,g:104},"8");Y({f:105,g:105},"9");Y({f:106,g:106},"*");Y({f:107,g:107},"+");Y({f:109,g:109},"-");Y({f:110,g:110},".");Y({f:111,g:111},"/");Y(144);Y(112);Y(113);Y(114);Y(115);Y(116);Y(117);Y(118);Y(119);Y(120);Y(121);Y(122);Y(123);Y({f:107,g:187},"=","+");Y(108,",");Y({f:109,g:189},"-","_");Y(188,",","<");Y(190,".",">");Y(191,"/","?"); -Y(192,"`","~");Y(219,"[","{");Y(220,"\\","|");Y(221,"]","}");Y({f:59,g:186},";",":");Y(222,"'",'"');var Z=new E;Z.set(1,Ab);Z.set(2,Bb);Z.set(4,Cb);Z.set(8,Db);(function(a){var b=new E;A(ua(a),function(c){b.set(a.get(c).code,c)});return b})(Z);ya&&wa(12);aa("_",function(a){if(Ga(a)){if(!Ga(a))throw new fa(15,"Element is not selectable");var b="selected",c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";a=!!a[b]}else a=!1;return a});; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);} - // https://github.com/SeleniumHQ/selenium/blob/master/javascript/atoms/dom.js#L435 atom.isElementDisplayed = function(element, window){return function(){var aa=this;function h(a){return void 0!==a}function l(a){return"string"==typeof a}function ba(a,b){a=a.split(".");var c=aa;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)!a.length&&h(b)?c[d]=b:c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}} function ca(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; diff --git a/testing/marionette/element.js b/testing/marionette/element.js index 06b4068cc6b5..9b7a82f981ed 100644 --- a/testing/marionette/element.js +++ b/testing/marionette/element.js @@ -22,6 +22,25 @@ this.EXPORTED_SYMBOLS = ["element"]; const XMLNS = "http://www.w3.org/1999/xhtml"; const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +/** XUL elements that support checked property. */ +const XUL_CHECKED_ELS = new Set([ + "button", + "checkbox", + "listitem", + "toolbarbutton", +]); + +/** XUL elements that support selected property. */ +const XUL_SELECTED_ELS = new Set([ + "listitem", + "menu", + "menuitem", + "menuseparator", + "radio", + "richlistitem", + "tab", +]); + const uuidGen = Cc["@mozilla.org/uuid-generator;1"] .getService(Ci.nsIUUIDGenerator); @@ -668,6 +687,46 @@ element.isStale = function(el, window = undefined) { return !el.isConnected; }; +/** + * Determine if el is selected or not. + * + * This operation only makes sense on + * <input type=checkbox>, + * <input type=radio>, + * and >option> elements. + * + * @param {(DOMElement|XULElement)} el + * Element to test if selected. + * + * @return {boolean} + * True if element is selected, false otherwise. + */ +element.isSelected = function(el) { + if (!el) { + return false; + } + + if (element.isXULElement(el)) { + if (XUL_CHECKED_ELS.has(el.tagName)) { + return el.checked; + } else if (XUL_SELECTED_ELS.has(el.tagName)) { + return el.selected; + } + + // TODO(ato): Use element.isDOMElement when bug 1400256 lands + } else if (typeof el == "object" && + "nodeType" in el && + el.nodeType == el.ELEMENT_NODE) { + if (el.localName == "input" && ["checkbox", "radio"].includes(el.type)) { + return el.checked; + } else if (el.localName == "option") { + return el.selected; + } + } + + return false; +}; + /** * This function generates a pair of coordinates relative to the viewport * given a target element and coordinates relative to that element's diff --git a/testing/marionette/interaction.js b/testing/marionette/interaction.js index 824896fc71f2..17e4dae62610 100644 --- a/testing/marionette/interaction.js +++ b/testing/marionette/interaction.js @@ -57,25 +57,6 @@ const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([ "TREE", ]); -/** XUL elements that support checked property. */ -const CHECKED_PROPERTY_SUPPORTED_XUL = new Set([ - "BUTTON", - "CHECKBOX", - "LISTITEM", - "TOOLBARBUTTON", -]); - -/** XUL elements that support selected property. */ -const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([ - "LISTITEM", - "MENU", - "MENUITEM", - "MENUSEPARATOR", - "RADIO", - "RICHLISTITEM", - "TAB", -]); - /** * Common form controls that user can change the value property * interactively. @@ -478,34 +459,25 @@ interaction.isElementEnabled = function(el, strict = false) { }; /** - * Determines if the referenced element is selected or not. + * Determines if the referenced element is selected or not, with + * an additional accessibility check if strict is true. * - * This operation only makes sense on input elements of the Checkbox- - * and Radio Button states, or option elements. + * This operation only makes sense on input elements of the checkbox- + * and radio button states, and option elements. * - * @param {DOMElement|XULElement} el + * @param {(DOMElement|XULElement)} el * Element to test if is selected. * @param {boolean=} [strict=false] strict * Enforce strict accessibility tests. * * @return {boolean} * True if element is selected, false otherwise. + * + * @throws {ElementNotAccessibleError} + * If el is not accessible when strict is true. */ interaction.isElementSelected = function(el, strict = false) { - let selected = true; - let win = getWindow(el); - - if (element.isXULElement(el)) { - let tagName = el.tagName.toUpperCase(); - if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) { - selected = el.checked; - } - if (SELECTED_PROPERTY_SUPPORTED_XUL.has(tagName)) { - selected = el.selected; - } - } else { - selected = atom.isElementSelected(el, win); - } + let selected = element.isSelected(el); let a11y = accessibility.get(strict); return a11y.getAccessible(el).then(acc => { diff --git a/testing/marionette/test_element.js b/testing/marionette/test_element.js index f565bd8c0212..8253bce913ed 100644 --- a/testing/marionette/test_element.js +++ b/testing/marionette/test_element.js @@ -6,8 +6,28 @@ const {utils: Cu} = Components; Cu.import("chrome://marionette/content/element.js"); -let el = { - getBoundingClientRect: function() { +class DOMElement { + constructor(tagName, attrs = {}) { + this.tagName = tagName; + this.localName = tagName; + + for (let attr in attrs) { + this[attr] = attrs[attr]; + } + + if (this.localName == "option") { + this.selected = false; + } + + if (this.localName == "input" && ["checkbox", "radio"].includes(this.type)) { + this.checked = false; + } + } + + get nodeType() { return 1; } + get ELEMENT_NODE() { return 1; } + + getBoundingClientRect() { return { top: 0, left: 0, @@ -17,28 +37,59 @@ let el = { } }; +const domEl = new DOMElement("p"); + +add_test(function test_isSelected() { + let checkbox = new DOMElement("input", {type: "checkbox"}); + ok(!element.isSelected(checkbox)); + checkbox.checked = true; + ok(element.isSelected(checkbox)); + + // selected is not a property of + checkbox.selected = true; + checkbox.checked = false; + ok(!element.isSelected(checkbox)); + + let option = new DOMElement("option"); + ok(!element.isSelected(option)); + option.selected = true; + ok(element.isSelected(option)); + + // checked is not a property of