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.
+
+ - Press down on the black square.
+ - Move your pointer slowly along a straight line to the red square.
+ - Release the pointer when you are over the red square.
+
+
+ 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 @@
+