diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml
index c48c8a7c38e0..74298a847fce 100644
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml
index a10becb547fa..17ac2244564f 100644
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 68e3e4076485..3caeb388dbe8 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 0545c5713601..2d389e0ae947 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,10 +17,10 @@
-
+
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 4eb62fb93c10..13ad7488a4e4 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml
index 17395660e8dd..af1ab43bb82c 100644
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 68e3e4076485..3caeb388dbe8 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml
index 927bb82b89be..a6343a12b764 100644
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 6d1ef0823b8a..800f35be2a8e 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
{
"git": {
- "git_revision": "39b121515ab8a8c3ea07f26d3ba1dd792e90217c",
+ "git_revision": "4b09b8824e3c68d8f5208a53f9ae3a8917dc9560",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
- "revision": "545d4c6c1224394885c502aa863f5fc95268a3f2",
+ "revision": "74ba42b610e520caa4e67264d52ef3c30d3193ae",
"repo_path": "integration/gaia-central"
}
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index 8a44b7e8b6b3..5e90049cd3f0 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,10 +17,10 @@
-
+
-
+
diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml
index 2047450b9cda..1efc205e7f46 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js
index d885463a1831..71ba6f3fb991 100644
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1186,7 +1186,7 @@ nsContextMenu.prototype = {
// Save URL of clicked-on frame.
saveFrame: function () {
- saveDocument(this.target.ownerDocument);
+ saveBrowser(this.browser, false, this.frameOuterWindowID);
},
// Helper function to wait for appropriate MIME-type headers and
diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini
index a73788904159..0a33f5249cc7 100644
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -55,6 +55,8 @@ support-files =
file_mixedContentFromOnunload.html
file_mixedContentFromOnunload_test1.html
file_mixedContentFromOnunload_test2.html
+ file_mixedContentFramesOnHttp.html
+ file_mixedPassiveContent.html
file_bug970276_popup1.html
file_bug970276_popup2.html
file_bug970276_favicon1.ico
@@ -271,6 +273,9 @@ tags = mcb
tags = mcb
skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
[browser_mixedContentFromOnunload.js]
+tags = mcb
+[browser_mixedContentFramesOnHttp.js]
+tags = mcb
[browser_bug970746.js]
[browser_bug1015721.js]
skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe version of synthesizeWheel
@@ -488,6 +493,7 @@ skip-if = buildapp == 'mulet'
skip-if = e10s # Bug 1094240 - has findbar-related failures
[browser_registerProtocolHandler_notification.js]
[browser_no_mcb_on_http_site.js]
+tags = mcb
[browser_bug1104165-switchtab-decodeuri.js]
[browser_bug1003461-switchtab-override.js]
[browser_bug1024133-switchtab-override-keynav.js]
diff --git a/browser/base/content/test/general/browser_mixedContentFramesOnHttp.js b/browser/base/content/test/general/browser_mixedContentFramesOnHttp.js
new file mode 100644
index 000000000000..ea1419b19d5c
--- /dev/null
+++ b/browser/base/content/test/general/browser_mixedContentFramesOnHttp.js
@@ -0,0 +1,52 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Test for Bug 1182551 -
+ *
+ * This test has a top level HTTP page with an HTTPS iframe. The HTTPS iframe
+ * includes an HTTP image. We check that the top level security state is
+ * STATE_IS_INSECURE. The mixed content from the iframe shouldn't "upgrade"
+ * the HTTP top level page to broken HTTPS.
+ */
+
+const gHttpTestRoot = "http://example.com/browser/browser/base/content/test/general/";
+
+let gTestBrowser = null;
+
+function SecStateTestsCompleted() {
+ gBrowser.removeCurrentTab();
+ window.focus();
+ finish();
+}
+
+function test() {
+ waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["security.mixed_content.block_active_content", true],
+ ["security.mixed_content.block_display_content", false]
+ ]}, SecStateTests);
+}
+
+function SecStateTests() {
+ let url = gHttpTestRoot + "file_mixedContentFramesOnHttp.html";
+ gBrowser.selectedTab = gBrowser.addTab();
+ gTestBrowser = gBrowser.selectedBrowser;
+ whenLoaded(gTestBrowser, SecStateTest1);
+ gTestBrowser.contentWindow.location = url;
+}
+
+// The http page loads an https frame with an http image.
+function SecStateTest1() {
+ // check security state is insecure
+ isSecurityState("insecure");
+
+ SecStateTestsCompleted();
+}
+
+function whenLoaded(aElement, aCallback) {
+ aElement.addEventListener("load", function onLoad() {
+ aElement.removeEventListener("load", onLoad, true);
+ executeSoon(aCallback);
+ }, true);
+}
diff --git a/browser/base/content/test/general/browser_mixedContentFromOnunload.js b/browser/base/content/test/general/browser_mixedContentFromOnunload.js
index 2aa0f9358124..4efda362c22b 100644
--- a/browser/base/content/test/general/browser_mixedContentFromOnunload.js
+++ b/browser/base/content/test/general/browser_mixedContentFromOnunload.js
@@ -70,35 +70,6 @@ function SecStateTest2B() {
SecStateTestsCompleted();
}
-// Compares the security state of the page with what is expected
-function isSecurityState(expectedState) {
- let ui = gTestBrowser.securityUI;
- if (!ui) {
- ok(false, "No security UI to get the security state");
- return;
- }
-
- const wpl = Components.interfaces.nsIWebProgressListener;
-
- // determine the security state
- let isSecure = ui.state & wpl.STATE_IS_SECURE;
- let isBroken = ui.state & wpl.STATE_IS_BROKEN;
- let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
-
- let actualState;
- if (isSecure && !(isBroken || isInsecure)) {
- actualState = "secure";
- } else if (isBroken && !(isSecure || isInsecure)) {
- actualState = "broken";
- } else if (isInsecure && !(isSecure || isBroken)) {
- actualState = "insecure";
- } else {
- actualState = "unknown";
- }
-
- is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
-}
-
function whenLoaded(aElement, aCallback) {
aElement.addEventListener("load", function onLoad() {
aElement.removeEventListener("load", onLoad, true);
diff --git a/browser/base/content/test/general/file_mixedContentFramesOnHttp.html b/browser/base/content/test/general/file_mixedContentFramesOnHttp.html
new file mode 100644
index 000000000000..3bd16aea51fa
--- /dev/null
+++ b/browser/base/content/test/general/file_mixedContentFramesOnHttp.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Test for Bug 1182551
+
+
+ Test for Bug 1182551. This is an HTTP top level page. We include an HTTPS iframe that loads mixed passive content.
+
+
+
diff --git a/browser/base/content/test/general/file_mixedPassiveContent.html b/browser/base/content/test/general/file_mixedPassiveContent.html
new file mode 100644
index 000000000000..a60ac94e8bda
--- /dev/null
+++ b/browser/base/content/test/general/file_mixedPassiveContent.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ HTTPS page with HTTP image
+
+
+
+
+
diff --git a/browser/base/content/test/general/head.js b/browser/base/content/test/general/head.js
index effe154f25eb..654fee2c4ed5 100644
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -961,3 +961,32 @@ function promiseNewSearchEngine(basename) {
});
});
}
+
+// Compares the security state of the page with what is expected
+function isSecurityState(expectedState) {
+ let ui = gTestBrowser.securityUI;
+ if (!ui) {
+ ok(false, "No security UI to get the security state");
+ return;
+ }
+
+ const wpl = Components.interfaces.nsIWebProgressListener;
+
+ // determine the security state
+ let isSecure = ui.state & wpl.STATE_IS_SECURE;
+ let isBroken = ui.state & wpl.STATE_IS_BROKEN;
+ let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
+
+ let actualState;
+ if (isSecure && !(isBroken || isInsecure)) {
+ actualState = "secure";
+ } else if (isBroken && !(isSecure || isInsecure)) {
+ actualState = "broken";
+ } else if (isInsecure && !(isSecure || isBroken)) {
+ actualState = "insecure";
+ } else {
+ actualState = "unknown";
+ }
+
+ is(expectedState, actualState, "Expected state " + expectedState + " and the actual state is " + actualState + ".");
+}
diff --git a/browser/base/content/test/newtab/browser.ini b/browser/base/content/test/newtab/browser.ini
index b2a13608568a..047f1ed20b70 100644
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -7,6 +7,7 @@ support-files =
[browser_newtab_block.js]
[browser_newtab_bug721442.js]
[browser_newtab_bug722273.js]
+skip-if = true # Bug 1119906
[browser_newtab_bug723102.js]
[browser_newtab_bug723121.js]
[browser_newtab_bug725996.js]
diff --git a/browser/devtools/styleinspector/test/browser.ini b/browser/devtools/styleinspector/test/browser.ini
index 7d69fb8ca86c..afee1c15850f 100644
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -198,7 +198,7 @@ skip-if = e10s || os == 'linux' # Bug 1111546 (e10s), bug 1093431 (linux)
[browser_styleinspector_tooltip-multiple-background-images.js]
[browser_styleinspector_tooltip-shorthand-fontfamily.js]
[browser_styleinspector_tooltip-size.js]
-skip-if = e10s # Bug 1111546
+skip-if = e10s || os == 'linux' # Bug 1111546 (e10s), bug 1093431 (linux)
[browser_styleinspector_transform-highlighter-01.js]
[browser_styleinspector_transform-highlighter-02.js]
[browser_styleinspector_transform-highlighter-03.js]
diff --git a/build/annotationProcessors/CodeGenerator.java b/build/annotationProcessors/CodeGenerator.java
index b5a21d67f89b..0bba34e3af0a 100644
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -48,7 +48,7 @@ public class CodeGenerator {
" \"" + cls.getName().replace('.', '/') + "\";\n" +
"\n" +
"protected:\n" +
- " using Class::Class;\n" +
+ " " + unqualifiedName + "(jobject instance) : Class(instance) {}\n" +
"\n");
cpp.append(
diff --git a/dom/apps/tests/file_test_widget.js b/dom/apps/tests/file_test_widget.js
index b2a42c2b0e78..0fc86bdcc587 100644
--- a/dom/apps/tests/file_test_widget.js
+++ b/dom/apps/tests/file_test_widget.js
@@ -170,7 +170,9 @@ function checkIsWidgetScript(testMozbrowserEvent) {
request.onerror = onError;
if (testMozbrowserEvent) {
- content.window.open("about:blank"); /* test mozbrowseropenwindow */
+ var win = content.window.open("about:blank"); /* test mozbrowseropenwindow */
+ /*Close new window to avoid mochitest "unable to restore focus" failures.*/
+ win.close();
content.window.scrollTo(4000, 4000); /* test mozbrowser(async)scroll */
}
}
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 4153df458607..f1f3cb144c86 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3849,6 +3849,30 @@ nsContentUtils::MatchElementId(nsIContent *aContent, const nsAString& aId)
return MatchElementId(aContent, id);
}
+/* static */
+nsIDocument*
+nsContentUtils::GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
+ uint64_t aOuterWindowId)
+{
+ if (!aDocument || !aOuterWindowId) {
+ return nullptr;
+ }
+
+ nsCOMPtr window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId);
+ if (!window) {
+ return nullptr;
+ }
+
+ nsCOMPtr foundDoc = window->GetDoc();
+ if (nsContentUtils::ContentIsCrossDocDescendantOf(foundDoc, aDocument)) {
+ // Note that ContentIsCrossDocDescendantOf will return true if
+ // foundDoc == aDocument.
+ return foundDoc;
+ }
+
+ return nullptr;
+}
+
// Convert the string from the given encoding to Unicode.
/* static */
nsresult
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 9c541a75716c..519c2a5117ea 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -325,6 +325,23 @@ public:
*/
static uint16_t ReverseDocumentPosition(uint16_t aDocumentPosition);
+ /**
+ * Returns a subdocument for aDocument with a particular outer window ID.
+ *
+ * @param aDocument
+ * The document whose subdocuments will be searched.
+ * @param aOuterWindowID
+ * The outer window ID for the subdocument to be found. This must
+ * be a value greater than 0.
+ * @return nsIDocument*
+ * A pointer to the found nsIDocument. nullptr if the subdocument
+ * cannot be found, or if either aDocument or aOuterWindowId were
+ * invalid. If the outer window ID belongs to aDocument itself, this
+ * will return a pointer to aDocument.
+ */
+ static nsIDocument* GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
+ uint64_t aOuterWindowId);
+
static uint32_t CopyNewlineNormalizedUnicodeTo(const nsAString& aSource,
uint32_t aSrcOffset,
char16_t* aDest,
diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp
index aa037e2bace8..7265c4df0276 100644
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2885,18 +2885,31 @@ nsFrameLoader::InitializeBrowserAPI()
}
NS_IMETHODIMP
-nsFrameLoader::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
+nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
+ nsIWebBrowserPersistDocumentReceiver* aRecv)
{
+ if (!aRecv) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
if (mRemoteBrowser) {
- return mRemoteBrowser->StartPersistence(aRecv);
+ return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
}
- if (mDocShell) {
- nsCOMPtr doc = do_GetInterface(mDocShell);
- NS_ENSURE_STATE(doc);
+
+ nsCOMPtr rootDoc = do_GetInterface(mDocShell);
+ nsCOMPtr foundDoc;
+ if (aOuterWindowID) {
+ foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
+ } else {
+ foundDoc = rootDoc;
+ }
+
+ if (!foundDoc) {
+ aRecv->OnError(NS_ERROR_NO_CONTENT);
+ } else {
nsCOMPtr pdoc =
- new mozilla::WebBrowserPersistLocalDocument(doc);
+ new mozilla::WebBrowserPersistLocalDocument(foundDoc);
aRecv->OnDocumentReady(pdoc);
- return NS_OK;
}
- return NS_ERROR_NO_CONTENT;
+ return NS_OK;
}
diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp
index ba1989d21ceb..c90df55949db 100644
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -117,7 +117,7 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
}
event->SetTrusted(true);
// Dispatch the event.
- *aStatus = nsEventStatus_eConsumeNoDefault;
+ // We don't initialize aStatus here, as our callers have already done so.
nsresult rv =
EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
static_cast(event),
@@ -177,7 +177,7 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
return BrowserElementParent::OPEN_WINDOW_CANCELLED;
}
- nsEventStatus status;
+ nsEventStatus status = nsEventStatus_eIgnore;
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),
diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl
index df1dab6916e6..6df1a10d1baf 100644
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -121,7 +121,7 @@ both:
*/
PRenderFrame();
- PWebBrowserPersistDocument();
+ PWebBrowserPersistDocument(uint64_t aOuterWindowID);
parent:
/**
@@ -524,6 +524,7 @@ parent:
child:
NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
+
parent:
/**
diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp
index 395238f1b3ae..8b45f60e7d6e 100644
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3118,16 +3118,28 @@ TabChildGlobal::GetGlobalJSObject()
}
PWebBrowserPersistDocumentChild*
-TabChild::AllocPWebBrowserPersistDocumentChild()
+TabChild::AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentChild();
}
bool
-TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor)
+TabChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
+ const uint64_t& aOuterWindowID)
{
- nsCOMPtr doc = GetDocument();
- static_cast(aActor)->Start(doc);
+ nsCOMPtr rootDoc = GetDocument();
+ nsCOMPtr foundDoc;
+ if (aOuterWindowID) {
+ foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
+ } else {
+ foundDoc = rootDoc;
+ }
+
+ if (!foundDoc) {
+ aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
+ } else {
+ static_cast(aActor)->Start(foundDoc);
+ }
return true;
}
diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h
index b323266ff0c3..521b93c30a0e 100644
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -495,8 +495,9 @@ public:
virtual ScreenIntSize GetInnerSize() override;
- virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild() override;
- virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor) override;
+ virtual PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild(const uint64_t& aOuterWindowID) override;
+ virtual bool RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
+ const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override;
protected:
diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp
index 0ac0cee51d76..39cd0dfec770 100644
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3368,7 +3368,7 @@ TabParent::AsyncPanZoomEnabled() const
}
PWebBrowserPersistDocumentParent*
-TabParent::AllocPWebBrowserPersistDocumentParent()
+TabParent::AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentParent();
}
@@ -3381,11 +3381,12 @@ TabParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentPar
}
NS_IMETHODIMP
-TabParent::StartPersistence(nsIWebBrowserPersistDocumentReceiver* aRecv)
+TabParent::StartPersistence(uint64_t aOuterWindowID,
+ nsIWebBrowserPersistDocumentReceiver* aRecv)
{
auto* actor = new WebBrowserPersistDocumentParent();
actor->SetOnReady(aRecv);
- return SendPWebBrowserPersistDocumentConstructor(actor)
+ return SendPWebBrowserPersistDocumentConstructor(actor, aOuterWindowID)
? NS_OK : NS_ERROR_FAILURE;
// (The actor will be destroyed on constructor failure.)
}
diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h
index 4791e899dda8..5ccdecbff86a 100644
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -435,7 +435,7 @@ public:
int32_t& aDragAreaX, int32_t& aDragAreaY);
layout::RenderFrameParent* GetRenderFrame();
- virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent() override;
+ virtual PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent(const uint64_t& aOuterWindowID) override;
virtual bool DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
protected:
diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp
index e024f4c53311..6f5b3688e2ea 100644
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -565,6 +565,15 @@ nsJSChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP
nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
+#ifdef DEBUG
+ {
+ nsCOMPtr loadInfo = nsIChannel::GetLoadInfo();
+ MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 ||
+ loadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+ }
+#endif
+
NS_ENSURE_ARG(aListener);
// First make sure that we have a usable inner window; we'll want to make
diff --git a/dom/media/MediaEventSource.h b/dom/media/MediaEventSource.h
index be13898031ca..8ef2742738e9 100644
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -47,6 +47,15 @@ private:
Atomic mRevoked;
};
+enum class ListenerMode : int8_t {
+ // Allow at most one listener. Move will be used when possible
+ // to pass the event data to save copy.
+ Exclusive,
+ // This is the default. Event data will always be copied when passed
+ // to the listeners.
+ NonExclusive
+};
+
namespace detail {
/**
@@ -111,7 +120,7 @@ private:
} // namespace detail
-template class MediaEventSource;
+template class MediaEventSource;
/**
* Not thread-safe since this is not meant to be shared and therefore only
@@ -120,7 +129,7 @@ template class MediaEventSource;
* listener from an event source.
*/
class MediaEventListener {
- template
+ template
friend class MediaEventSource;
public:
@@ -154,7 +163,7 @@ private:
/**
* A generic and thread-safe class to implement the observer pattern.
*/
-template
+template
class MediaEventSource {
static_assert(!IsReference::value, "Ref-type not supported!");
typedef typename detail::EventTypeTraits::ArgType ArgType;
@@ -246,6 +255,7 @@ class MediaEventSource {
MediaEventListener
ConnectInternal(Target* aTarget, const Function& aFunction) {
MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(Mode == ListenerMode::NonExclusive || mListeners.IsEmpty());
auto l = mListeners.AppendElement();
l->reset(new ListenerImpl(aTarget, aFunction));
return MediaEventListener((*l)->Token());
@@ -346,8 +356,8 @@ private:
* and event publisher. Mostly used as a member variable to publish events
* to the listeners.
*/
-template
-class MediaEventProducer : public MediaEventSource {
+template
+class MediaEventProducer : public MediaEventSource {
public:
void Notify(const EventType& aEvent) {
this->NotifyInternal(aEvent);
diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp
index aaa3d08af5a4..bc49534e41da 100644
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -805,7 +805,6 @@ MediaFormatReader::NotifyError(TrackType aTrack)
LOGV("%s Decoding error", TrackTypeToStr(aTrack));
auto& decoder = GetDecoderData(aTrack);
decoder.mError = true;
- decoder.mNeedDraining = true;
ScheduleUpdate(aTrack);
}
@@ -1144,7 +1143,7 @@ MediaFormatReader::Update(TrackType aTrack)
} else if (decoder.mDemuxEOS) {
decoder.RejectPromise(END_OF_STREAM, __func__);
}
- } else if (decoder.mError && !decoder.mDecoder) {
+ } else if (decoder.mError) {
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mWaitingForData) {
diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp
index c3b096929d93..70c7932d6958 100644
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -1008,13 +1008,13 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
mOffset = aOffset;
+ // Don't report close of the channel because the channel is not closed for
+ // download ended, but for internal changes in the read position.
+ mIgnoreClose = true;
+
+ // Don't create a new channel if we are still suspended. The channel will
+ // be recreated when we are resumed.
if (mSuspendCount > 0) {
- // Close the existing channel to force the channel to be recreated at
- // the correct offset upon resume.
- if (mChannel) {
- mIgnoreClose = true;
- CloseChannel();
- }
return NS_OK;
}
diff --git a/dom/media/eme/MediaKeySession.cpp b/dom/media/eme/MediaKeySession.cpp
index fb979757f495..0e32dcb15b7e 100644
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -16,7 +16,7 @@
#include "mozilla/Move.h"
#include "nsContentUtils.h"
#include "mozilla/EMEUtils.h"
-#include "mozilla/Base64.h"
+#include "GMPUtils.h"
#include "nsPrintfCString.h"
namespace mozilla {
@@ -143,14 +143,7 @@ MediaKeySession::UpdateKeyStatusMap()
nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
this, NS_ConvertUTF16toUTF8(mSessionId).get()));
for (const CDMCaps::KeyStatus& status : keyStatuses) {
- nsAutoCString base64KeyId;
- nsDependentCSubstring rawKeyId(reinterpret_cast(status.mId.Elements()),
- status.mId.Length());
- nsresult rv = Base64Encode(rawKeyId, base64KeyId);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- continue;
- }
- message.Append(nsPrintfCString(" (%s,%s)", base64KeyId.get(),
+ message.Append(nsPrintfCString(" (%s,%s)", ToBase64(status.mId).get(),
MediaKeyStatusValues::strings[status.mStatus].value));
}
message.Append(" }");
@@ -197,17 +190,9 @@ MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
}
// Convert initData to base64 for easier logging.
- // Note: UpdateSession() Move()s the data out of the array, so we have
+ // Note: CreateSession() Move()s the data out of the array, so we have
// to copy it here.
- nsAutoCString base64InitData;
- if (EME_LOG_ENABLED()) {
- nsDependentCSubstring rawInitData(reinterpret_cast(data.Elements()),
- data.Length());
- if (NS_FAILED(Base64Encode(rawInitData, base64InitData))) {
- NS_WARNING("Failed to base64 encode initData for logging");
- }
- }
-
+ nsAutoCString base64InitData(ToBase64(data));
PromiseId pid = mKeys->StorePromise(promise);
mKeys->GetCDMProxy()->CreateSession(Token(),
mSessionType,
@@ -296,14 +281,7 @@ MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResu
// Convert response to base64 for easier logging.
// Note: UpdateSession() Move()s the data out of the array, so we have
// to copy it here.
- nsAutoCString base64Response;
- if (EME_LOG_ENABLED()) {
- nsDependentCSubstring rawResponse(reinterpret_cast(data.Elements()),
- data.Length());
- if (NS_FAILED(Base64Encode(rawResponse, base64Response))) {
- NS_WARNING("Failed to base64 encode response for logging");
- }
- }
+ nsAutoCString base64Response(ToBase64(data));
PromiseId pid = mKeys->StorePromise(promise);
mKeys->GetCDMProxy()->UpdateSession(mSessionId,
@@ -400,16 +378,10 @@ MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
const nsTArray& aMessage)
{
if (EME_LOG_ENABLED()) {
- nsAutoCString base64MsgData;
- nsDependentCSubstring rawMsgData(reinterpret_cast(aMessage.Elements()),
- aMessage.Length());
- if (NS_FAILED(Base64Encode(rawMsgData, base64MsgData))) {
- NS_WARNING("Failed to base64 encode message for logging");
- }
EME_LOG("MediaKeySession[%p,'%s'] DispatchKeyMessage() type=%s message(base64)='%s'",
this, NS_ConvertUTF16toUTF8(mSessionId).get(),
MediaKeyMessageTypeValues::strings[uint32_t(aMessageType)].value,
- base64MsgData.get());
+ ToBase64(aMessage).get());
}
nsRefPtr event(
diff --git a/dom/media/gmp-plugin/gmp-test-output-protection.h b/dom/media/gmp-plugin/gmp-test-output-protection.h
index 54b4fa739174..57855ca71f91 100644
--- a/dom/media/gmp-plugin/gmp-test-output-protection.h
+++ b/dom/media/gmp-plugin/gmp-test-output-protection.h
@@ -35,13 +35,6 @@ static BOOL CALLBACK EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc,
failureMsgs->push_back("FAIL GetMonitorInfoA call failed");
}
- DISPLAY_DEVICEA dd;
- ZeroMemory(&dd, sizeof(dd));
- dd.cb = sizeof(dd);
- if (!EnumDisplayDevicesA(miex.szDevice, 0, &dd, 1)) {
- failureMsgs->push_back("FAIL EnumDisplayDevicesA call failed");
- }
-
ULONG numVideoOutputs = 0;
IOPMVideoOutput** opmVideoOutputArray = nullptr;
HRESULT hr = sOPMGetVideoOutputsFromHMONITORProc(hMonitor,
@@ -57,6 +50,13 @@ static BOOL CALLBACK EnumDisplayMonitorsCallback(HMONITOR hMonitor, HDC hdc,
return true;
}
+ DISPLAY_DEVICEA dd;
+ ZeroMemory(&dd, sizeof(dd));
+ dd.cb = sizeof(dd);
+ if (!EnumDisplayDevicesA(miex.szDevice, 0, &dd, 1)) {
+ failureMsgs->push_back("FAIL EnumDisplayDevicesA call failed");
+ }
+
for (ULONG i = 0; i < numVideoOutputs; ++i) {
OPM_RANDOM_NUMBER opmRandomNumber;
BYTE* certificate = nullptr;
diff --git a/dom/media/gmp/GMPAudioDecoderParent.cpp b/dom/media/gmp/GMPAudioDecoderParent.cpp
index 72facefc3fbf..b5e4d4db9631 100644
--- a/dom/media/gmp/GMPAudioDecoderParent.cpp
+++ b/dom/media/gmp/GMPAudioDecoderParent.cpp
@@ -19,6 +19,7 @@ namespace mozilla {
extern PRLogModuleInfo* GetGMPLog();
+#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
@@ -48,6 +49,8 @@ GMPAudioDecoderParent::InitDecode(GMPAudioCodecType aCodecType,
nsTArray& aExtraData,
GMPAudioDecoderCallbackProxy* aCallback)
{
+ LOGD(("GMPAudioDecoderParent[%p]::InitDecode()", this));
+
if (mIsOpen) {
NS_WARNING("Trying to re-init an in-use GMP audio decoder!");
return NS_ERROR_FAILURE;
@@ -78,6 +81,8 @@ GMPAudioDecoderParent::InitDecode(GMPAudioCodecType aCodecType,
nsresult
GMPAudioDecoderParent::Decode(GMPAudioSamplesImpl& aEncodedSamples)
{
+ LOGV(("GMPAudioDecoderParent[%p]::Decode() timestamp=%lld",
+ this, aEncodedSamples.TimeStamp()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
@@ -100,6 +105,8 @@ GMPAudioDecoderParent::Decode(GMPAudioSamplesImpl& aEncodedSamples)
nsresult
GMPAudioDecoderParent::Reset()
{
+ LOGD(("GMPAudioDecoderParent[%p]::Reset()", this));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
return NS_ERROR_FAILURE;
@@ -120,6 +127,8 @@ GMPAudioDecoderParent::Reset()
nsresult
GMPAudioDecoderParent::Drain()
{
+ LOGD(("GMPAudioDecoderParent[%p]::Drain()", this));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP Audio decoder!");
return NS_ERROR_FAILURE;
@@ -141,7 +150,7 @@ GMPAudioDecoderParent::Drain()
nsresult
GMPAudioDecoderParent::Close()
{
- LOGD(("%s: %p", __FUNCTION__, this));
+ LOGD(("GMPAudioDecoderParent[%p]::Close()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
// Ensure if we've received a Close while waiting for a ResetComplete
@@ -166,7 +175,7 @@ GMPAudioDecoderParent::Close()
nsresult
GMPAudioDecoderParent::Shutdown()
{
- LOGD(("%s: %p", __FUNCTION__, this));
+ LOGD(("GMPAudioDecoderParent[%p]::Shutdown()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
if (mShuttingDown) {
@@ -197,6 +206,8 @@ GMPAudioDecoderParent::Shutdown()
void
GMPAudioDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
{
+ LOGD(("GMPAudioDecoderParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
+
mIsOpen = false;
mActorDestroyed = true;
@@ -220,6 +231,9 @@ GMPAudioDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
{
+ LOGV(("GMPAudioDecoderParent[%p]::RecvDecoded() timestamp=%lld",
+ this, aDecoded.mTimeStamp()));
+
if (!mCallback) {
return false;
}
@@ -235,6 +249,8 @@ GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
bool
GMPAudioDecoderParent::RecvInputDataExhausted()
{
+ LOGV(("GMPAudioDecoderParent[%p]::RecvInputDataExhausted()", this));
+
if (!mCallback) {
return false;
}
@@ -248,6 +264,8 @@ GMPAudioDecoderParent::RecvInputDataExhausted()
bool
GMPAudioDecoderParent::RecvDrainComplete()
{
+ LOGD(("GMPAudioDecoderParent[%p]::RecvDrainComplete()", this));
+
if (!mCallback) {
return false;
}
@@ -266,6 +284,8 @@ GMPAudioDecoderParent::RecvDrainComplete()
bool
GMPAudioDecoderParent::RecvResetComplete()
{
+ LOGD(("GMPAudioDecoderParent[%p]::RecvResetComplete()", this));
+
if (!mCallback) {
return false;
}
@@ -284,6 +304,8 @@ GMPAudioDecoderParent::RecvResetComplete()
bool
GMPAudioDecoderParent::RecvError(const GMPErr& aError)
{
+ LOGD(("GMPAudioDecoderParent[%p]::RecvError(error=%d)", this, aError));
+
if (!mCallback) {
return false;
}
@@ -302,6 +324,8 @@ GMPAudioDecoderParent::RecvError(const GMPErr& aError)
bool
GMPAudioDecoderParent::RecvShutdown()
{
+ LOGD(("GMPAudioDecoderParent[%p]::RecvShutdown()", this));
+
Shutdown();
return true;
}
@@ -309,6 +333,8 @@ GMPAudioDecoderParent::RecvShutdown()
bool
GMPAudioDecoderParent::Recv__delete__()
{
+ LOGD(("GMPAudioDecoderParent[%p]::Recv__delete__()", this));
+
if (mPlugin) {
// Ignore any return code. It is OK for this to fail without killing the process.
mPlugin->AudioDecoderDestroyed(this);
@@ -321,6 +347,8 @@ GMPAudioDecoderParent::Recv__delete__()
void
GMPAudioDecoderParent::UnblockResetAndDrain()
{
+ LOGD(("GMPAudioDecoderParent[%p]::UnblockResetAndDrain()", this));
+
if (!mCallback) {
MOZ_ASSERT(!mIsAwaitingResetComplete);
MOZ_ASSERT(!mIsAwaitingDrainComplete);
diff --git a/dom/media/gmp/GMPDecryptorParent.cpp b/dom/media/gmp/GMPDecryptorParent.cpp
index 6c127461821e..501aec124102 100644
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -9,6 +9,17 @@
#include "mozilla/unused.h"
namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+extern PRLogModuleInfo* GetGMPLog();
+
+#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
+#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
+#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
+
namespace gmp {
GMPDecryptorParent::GMPDecryptorParent(GMPContentParent* aPlugin)
@@ -32,6 +43,8 @@ GMPDecryptorParent::~GMPDecryptorParent()
nsresult
GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback)
{
+ LOGD(("GMPDecryptorParent[%p]::Init()", this));
+
if (mIsOpen) {
NS_WARNING("Trying to re-use an in-use GMP decrypter!");
return NS_ERROR_FAILURE;
@@ -51,6 +64,9 @@ GMPDecryptorParent::CreateSession(uint32_t aCreateSessionToken,
const nsTArray& aInitData,
GMPSessionType aSessionType)
{
+ LOGD(("GMPDecryptorParent[%p]::CreateSession(token=%u, promiseId=%u, aInitData='%s')",
+ this, aCreateSessionToken, aPromiseId, ToBase64(aInitData).get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -64,6 +80,8 @@ void
GMPDecryptorParent::LoadSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
+ LOGD(("GMPDecryptorParent[%p]::LoadSession(sessionId='%s', promiseId=%u)",
+ this, aSessionId.get(), aPromiseId));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -78,6 +96,9 @@ GMPDecryptorParent::UpdateSession(uint32_t aPromiseId,
const nsCString& aSessionId,
const nsTArray& aResponse)
{
+ LOGD(("GMPDecryptorParent[%p]::UpdateSession(sessionId='%s', promiseId=%u response='%s')",
+ this, aSessionId.get(), aPromiseId, ToBase64(aResponse).get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -91,6 +112,9 @@ void
GMPDecryptorParent::CloseSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
+ LOGD(("GMPDecryptorParent[%p]::CloseSession(sessionId='%s', promiseId=%u)",
+ this, aSessionId.get(), aPromiseId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -104,6 +128,9 @@ void
GMPDecryptorParent::RemoveSession(uint32_t aPromiseId,
const nsCString& aSessionId)
{
+ LOGD(("GMPDecryptorParent[%p]::RemoveSession(sessionId='%s', promiseId=%u)",
+ this, aSessionId.get(), aPromiseId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -117,6 +144,9 @@ void
GMPDecryptorParent::SetServerCertificate(uint32_t aPromiseId,
const nsTArray& aServerCert)
{
+ LOGD(("GMPDecryptorParent[%p]::SetServerCertificate(promiseId=%u)",
+ this, aPromiseId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -131,6 +161,8 @@ GMPDecryptorParent::Decrypt(uint32_t aId,
const CryptoSample& aCrypto,
const nsTArray& aBuffer)
{
+ LOGV(("GMPDecryptorParent[%p]::Decrypt(id=%d)", this, aId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return;
@@ -152,6 +184,9 @@ bool
GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
const nsCString& aSessionId)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvSetSessionId(token=%u, sessionId='%s')",
+ this, aCreateSessionId, aSessionId.get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -164,6 +199,9 @@ bool
GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
const bool& aSuccess)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvResolveLoadSessionPromise(promiseId=%u)",
+ this, aPromiseId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -175,6 +213,9 @@ GMPDecryptorParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
bool
GMPDecryptorParent::RecvResolvePromise(const uint32_t& aPromiseId)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvResolvePromise(promiseId=%u)",
+ this, aPromiseId));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -206,6 +247,9 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId,
const GMPDOMException& aException,
const nsCString& aMessage)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')",
+ this, aException, aMessage.get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -219,6 +263,9 @@ GMPDecryptorParent::RecvSessionMessage(const nsCString& aSessionId,
const GMPSessionMessageType& aMessageType,
nsTArray&& aMessage)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvSessionMessage(sessionId='%s', type=%d, msg='%s')",
+ this, aSessionId.get(), aMessageType, ToBase64(aMessage).get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -231,6 +278,9 @@ bool
GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
const double& aExpiryTime)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvExpirationChange(sessionId='%s', expiry=%lf)",
+ this, aSessionId.get(), aExpiryTime));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -242,6 +292,9 @@ GMPDecryptorParent::RecvExpirationChange(const nsCString& aSessionId,
bool
GMPDecryptorParent::RecvSessionClosed(const nsCString& aSessionId)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvSessionClosed(sessionId='%s')",
+ this, aSessionId.get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -256,6 +309,10 @@ GMPDecryptorParent::RecvSessionError(const nsCString& aSessionId,
const uint32_t& aSystemCode,
const nsCString& aMessage)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvSessionError(sessionId='%s', exception=%d, sysCode=%d, msg='%s')",
+ this, aSessionId.get(),
+ aException, aSystemCode, aMessage.get()));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -272,6 +329,9 @@ GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
InfallibleTArray&& aKeyId,
const GMPMediaKeyStatus& aStatus)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvKeyStatusChanged(sessionId='%s', keyId=%s, status=%d)",
+ this, aSessionId.get(), ToBase64(aKeyId).get(), aStatus));
+
if (mIsOpen) {
mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
}
@@ -281,6 +341,8 @@ GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
bool
GMPDecryptorParent::RecvSetCaps(const uint64_t& aCaps)
{
+ LOGD(("GMPDecryptorParent[%p]::RecvSetCaps(caps=0x%llx)", this, aCaps));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -294,6 +356,9 @@ GMPDecryptorParent::RecvDecrypted(const uint32_t& aId,
const GMPErr& aErr,
InfallibleTArray&& aBuffer)
{
+ LOGV(("GMPDecryptorParent[%p]::RecvDecrypted(id=%d, err=%d)",
+ this, aId, aErr));
+
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
return false;
@@ -305,6 +370,8 @@ GMPDecryptorParent::RecvDecrypted(const uint32_t& aId,
bool
GMPDecryptorParent::RecvShutdown()
{
+ LOGD(("GMPDecryptorParent[%p]::RecvShutdown()", this));
+
Shutdown();
return true;
}
@@ -313,7 +380,9 @@ GMPDecryptorParent::RecvShutdown()
void
GMPDecryptorParent::Close()
{
+ LOGD(("GMPDecryptorParent[%p]::Close()", this));
MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
+
// Consumer is done with us; we can shut down. No more callbacks should
// be made to mCallback. Note: do this before Shutdown()!
mCallback = nullptr;
@@ -328,6 +397,7 @@ GMPDecryptorParent::Close()
void
GMPDecryptorParent::Shutdown()
{
+ LOGD(("GMPDecryptorParent[%p]::Shutdown()", this));
MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
if (mShuttingDown) {
@@ -351,6 +421,8 @@ GMPDecryptorParent::Shutdown()
void
GMPDecryptorParent::ActorDestroy(ActorDestroyReason aWhy)
{
+ LOGD(("GMPDecryptorParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
+
mIsOpen = false;
mActorDestroyed = true;
if (mCallback) {
@@ -367,6 +439,8 @@ GMPDecryptorParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPDecryptorParent::Recv__delete__()
{
+ LOGD(("GMPDecryptorParent[%p]::Recv__delete__()", this));
+
if (mPlugin) {
mPlugin->DecryptorDestroyed(this);
mPlugin = nullptr;
diff --git a/dom/media/gmp/GMPStorageParent.cpp b/dom/media/gmp/GMPStorageParent.cpp
index e93fef042328..c4f04ede0863 100644
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -30,11 +30,6 @@ extern PRLogModuleInfo* GetGMPLog();
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
-#ifdef __CLASS__
-#undef __CLASS__
-#endif
-#define __CLASS__ "GMPStorageParent"
-
namespace gmp {
// We store the records in files in the profile dir.
@@ -165,7 +160,7 @@ public:
{
MOZ_ASSERT(!IsOpen(aRecordName));
nsresult rv;
- Record* record = nullptr;
+ Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
// New file.
nsAutoString filename;
@@ -254,7 +249,7 @@ public:
return GMPClosedErr;
}
- Record* record = nullptr;
+ Record* record = nullptr;
mRecords.Get(aRecordName, &record);
MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this.
@@ -581,6 +576,8 @@ GMPStorageParent::GMPStorageParent(const nsCString& aNodeId,
nsresult
GMPStorageParent::Init()
{
+ LOGD(("GMPStorageParent[%p]::Init()", this));
+
if (NS_WARN_IF(mNodeId.IsEmpty())) {
return NS_ERROR_FAILURE;
}
@@ -611,6 +608,9 @@ GMPStorageParent::Init()
bool
GMPStorageParent::RecvOpen(const nsCString& aRecordName)
{
+ LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s')",
+ this, aRecordName.get()));
+
if (mShutdown) {
return false;
}
@@ -618,23 +618,30 @@ GMPStorageParent::RecvOpen(const nsCString& aRecordName)
if (mNodeId.EqualsLiteral("null")) {
// Refuse to open storage if the page is opened from local disk,
// or shared across origin.
- NS_WARNING("Refusing to open storage for null NodeId");
+ LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; null nodeId",
+ this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
if (aRecordName.IsEmpty()) {
+ LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; record name empty",
+ this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
if (mStorage->IsOpen(aRecordName)) {
+ LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') failed; record in use",
+ this, aRecordName.get()));
unused << SendOpenComplete(aRecordName, GMPRecordInUse);
return true;
}
auto err = mStorage->Open(aRecordName);
MOZ_ASSERT(GMP_FAILED(err) || mStorage->IsOpen(aRecordName));
+ LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s') complete; rv=%d",
+ this, aRecordName.get(), err));
unused << SendOpenComplete(aRecordName, err);
return true;
@@ -643,7 +650,8 @@ GMPStorageParent::RecvOpen(const nsCString& aRecordName)
bool
GMPStorageParent::RecvRead(const nsCString& aRecordName)
{
- LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
+ LOGD(("GMPStorageParent[%p]::RecvRead(record='%s')",
+ this, aRecordName.get()));
if (mShutdown) {
return false;
@@ -651,9 +659,14 @@ GMPStorageParent::RecvRead(const nsCString& aRecordName)
nsTArray data;
if (!mStorage->IsOpen(aRecordName)) {
+ LOGD(("GMPStorageParent[%p]::RecvRead(record='%s') failed; record not open",
+ this, aRecordName.get()));
unused << SendReadComplete(aRecordName, GMPClosedErr, data);
} else {
- unused << SendReadComplete(aRecordName, mStorage->Read(aRecordName, data), data);
+ GMPErr rv = mStorage->Read(aRecordName, data);
+ LOGD(("GMPStorageParent[%p]::RecvRead(record='%s') read %d bytes rv=%d",
+ this, aRecordName.get(), data.Length(), rv));
+ unused << SendReadComplete(aRecordName, rv, data);
}
return true;
@@ -663,23 +676,32 @@ bool
GMPStorageParent::RecvWrite(const nsCString& aRecordName,
InfallibleTArray&& aBytes)
{
- LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
+ LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') %d bytes",
+ this, aRecordName.get(), aBytes.Length()));
if (mShutdown) {
return false;
}
if (!mStorage->IsOpen(aRecordName)) {
+ LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') failed record not open",
+ this, aRecordName.get()));
unused << SendWriteComplete(aRecordName, GMPClosedErr);
return true;
}
if (aBytes.Length() > GMP_MAX_RECORD_SIZE) {
+ LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') failed record too big",
+ this, aRecordName.get()));
unused << SendWriteComplete(aRecordName, GMPQuotaExceededErr);
return true;
}
- unused << SendWriteComplete(aRecordName, mStorage->Write(aRecordName, aBytes));
+ GMPErr rv = mStorage->Write(aRecordName, aBytes);
+ LOGD(("GMPStorageParent[%p]::RecvWrite(record='%s') write complete rv=%d",
+ this, aRecordName.get(), rv));
+
+ unused << SendWriteComplete(aRecordName, rv);
return true;
}
@@ -687,14 +709,16 @@ GMPStorageParent::RecvWrite(const nsCString& aRecordName,
bool
GMPStorageParent::RecvGetRecordNames()
{
- LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
-
if (mShutdown) {
return true;
}
nsTArray recordNames;
GMPErr status = mStorage->GetRecordNames(recordNames);
+
+ LOGD(("GMPStorageParent[%p]::RecvGetRecordNames() status=%d numRecords=%d",
+ this, status, recordNames.Length()));
+
unused << SendRecordNames(recordNames, status);
return true;
@@ -703,7 +727,8 @@ GMPStorageParent::RecvGetRecordNames()
bool
GMPStorageParent::RecvClose(const nsCString& aRecordName)
{
- LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
+ LOGD(("GMPStorageParent[%p]::RecvClose(record='%s')",
+ this, aRecordName.get()));
if (mShutdown) {
return true;
@@ -717,14 +742,14 @@ GMPStorageParent::RecvClose(const nsCString& aRecordName)
void
GMPStorageParent::ActorDestroy(ActorDestroyReason aWhy)
{
- LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+ LOGD(("GMPStorageParent[%p]::ActorDestroy(reason=%d)", this, aWhy));
Shutdown();
}
void
GMPStorageParent::Shutdown()
{
- LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+ LOGD(("GMPStorageParent[%p]::Shutdown()", this));
if (mShutdown) {
return;
diff --git a/dom/media/gmp/GMPUtils.cpp b/dom/media/gmp/GMPUtils.cpp
index bb9b621d535d..44732d05076f 100644
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -10,6 +10,7 @@
#include "nsCOMPtr.h"
#include "nsLiteralString.h"
#include "nsCRTGlue.h"
+#include "mozilla/Base64.h"
namespace mozilla {
@@ -50,4 +51,17 @@ SplitAt(const char* aDelims,
}
}
+nsCString
+ToBase64(const nsTArray& aBytes)
+{
+ nsAutoCString base64;
+ nsDependentCSubstring raw(reinterpret_cast(aBytes.Elements()),
+ aBytes.Length());
+ nsresult rv = Base64Encode(raw, base64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_LITERAL_CSTRING("[Base64EncodeFailed]");
+ }
+ return base64;
+}
+
} // namespace mozilla
diff --git a/dom/media/gmp/GMPUtils.h b/dom/media/gmp/GMPUtils.h
index bb0a22e21dd7..6f50a6d7a7ce 100644
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -34,6 +34,9 @@ SplitAt(const char* aDelims,
const nsACString& aInput,
nsTArray& aOutTokens);
+nsCString
+ToBase64(const nsTArray& aBytes);
+
} // namespace mozilla
#endif
diff --git a/dom/media/gmp/GMPVideoDecoderParent.cpp b/dom/media/gmp/GMPVideoDecoderParent.cpp
index 68e633f8701a..0209ecd4fd5d 100644
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -23,6 +23,7 @@ namespace mozilla {
extern PRLogModuleInfo* GetGMPLog();
+#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
@@ -67,7 +68,7 @@ GMPVideoDecoderParent::Host()
void
GMPVideoDecoderParent::Close()
{
- LOGD(("%s: %p", __FUNCTION__, this));
+ LOGD(("GMPVideoDecoderParent[%p]::Close()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
// Ensure if we've received a Close while waiting for a ResetComplete
@@ -92,6 +93,8 @@ GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
GMPVideoDecoderCallbackProxy* aCallback,
int32_t aCoreCount)
{
+ LOGD(("GMPVideoDecoderParent[%p]::InitDecode()", this));
+
if (mActorDestroyed) {
NS_WARNING("Trying to use a destroyed GMP video decoder!");
return NS_ERROR_FAILURE;
@@ -123,6 +126,10 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr aInputFrame,
const nsTArray& aCodecSpecificInfo,
int64_t aRenderTimeMs)
{
+ LOGV(("GMPVideoDecoderParent[%p]::Decode() timestamp=%lld keyframe=%d",
+ this, aInputFrame->TimeStamp(),
+ aInputFrame->FrameType() == kGMPKeyFrame));
+
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@@ -158,6 +165,8 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr aInputFrame,
nsresult
GMPVideoDecoderParent::Reset()
{
+ LOGD(("GMPVideoDecoderParent[%p]::Reset()", this));
+
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@@ -178,6 +187,8 @@ GMPVideoDecoderParent::Reset()
nsresult
GMPVideoDecoderParent::Drain()
{
+ LOGD(("GMPVideoDecoderParent[%p]::Drain()", this));
+
if (!mIsOpen) {
NS_WARNING("Trying to use an dead GMP video decoder");
return NS_ERROR_FAILURE;
@@ -211,7 +222,7 @@ GMPVideoDecoderParent::GetDisplayName() const
nsresult
GMPVideoDecoderParent::Shutdown()
{
- LOGD(("%s: %p", __FUNCTION__, this));
+ LOGD(("GMPVideoDecoderParent[%p]::Shutdown()", this));
MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
if (mShuttingDown) {
@@ -242,6 +253,8 @@ GMPVideoDecoderParent::Shutdown()
void
GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
{
+ LOGD(("GMPVideoDecoderParent[%p]::ActorDestroy reason=%d", this, aWhy));
+
mIsOpen = false;
mActorDestroyed = true;
mVideoHost.DoneWithAPI();
@@ -267,12 +280,17 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
bool
GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
{
+ LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld",
+ this, aDecodedFrame.mTimestamp()));
+
if (!mCallback) {
return false;
}
if (!GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame)) {
- LOG(LogLevel::Error, ("%s: Decoded frame corrupt, ignoring", __FUNCTION__));
+ LOG(LogLevel::Error,
+ ("GMPVideoDecoderParent[%p]::RecvDecoded() "
+ "timestamp=%lld decoded frame corrupt, ignoring"));
return false;
}
auto f = new GMPVideoi420FrameImpl(aDecodedFrame, &mVideoHost);
@@ -312,6 +330,8 @@ GMPVideoDecoderParent::RecvReceivedDecodedFrame(const uint64_t& aPictureId)
bool
GMPVideoDecoderParent::RecvInputDataExhausted()
{
+ LOGV(("GMPVideoDecoderParent[%p]::RecvInputDataExhausted()", this));
+
if (!mCallback) {
return false;
}
@@ -325,6 +345,8 @@ GMPVideoDecoderParent::RecvInputDataExhausted()
bool
GMPVideoDecoderParent::RecvDrainComplete()
{
+ LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete()", this));
+
if (!mCallback) {
return false;
}
@@ -343,6 +365,8 @@ GMPVideoDecoderParent::RecvDrainComplete()
bool
GMPVideoDecoderParent::RecvResetComplete()
{
+ LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this));
+
if (!mCallback) {
return false;
}
@@ -361,6 +385,8 @@ GMPVideoDecoderParent::RecvResetComplete()
bool
GMPVideoDecoderParent::RecvError(const GMPErr& aError)
{
+ LOGD(("GMPVideoDecoderParent[%p]::RecvError(error=%d)", this, aError));
+
if (!mCallback) {
return false;
}
@@ -379,6 +405,8 @@ GMPVideoDecoderParent::RecvError(const GMPErr& aError)
bool
GMPVideoDecoderParent::RecvShutdown()
{
+ LOGD(("GMPVideoDecoderParent[%p]::RecvShutdown()", this));
+
Shutdown();
return true;
}
@@ -415,6 +443,8 @@ GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize,
bool
GMPVideoDecoderParent::Recv__delete__()
{
+ LOGD(("GMPVideoDecoderParent[%p]::Recv__delete__()", this));
+
if (mPlugin) {
// Ignore any return code. It is OK for this to fail without killing the process.
mPlugin->VideoDecoderDestroyed(this);
@@ -427,6 +457,8 @@ GMPVideoDecoderParent::Recv__delete__()
void
GMPVideoDecoderParent::UnblockResetAndDrain()
{
+ LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain()", this));
+
if (!mCallback) {
MOZ_ASSERT(!mIsAwaitingResetComplete);
MOZ_ASSERT(!mIsAwaitingDrainComplete);
diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp
index a2268d315db6..1cdf847f2beb 100644
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -308,14 +308,10 @@ MediaSource::EndOfStream(const Optional& aError, Er
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
- // TODO: If media element has a readyState of:
- // HAVE_NOTHING -> run resource fetch algorithm
- // > HAVE_NOTHING -> run "interrupted" steps of resource fetch
+ mDecoder->NetworkError();
break;
case MediaSourceEndOfStreamError::Decode:
- // TODO: If media element has a readyState of:
- // HAVE_NOTHING -> run "unsupported" steps of resource fetch
- // > HAVE_NOTHING -> run "corrupted" steps of resource fetch
+ mDecoder->DecodeError();
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp
index 7976c8d8aefc..679e730c2224 100644
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -550,6 +550,15 @@ SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult&
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
+
+ // If the HTMLMediaElement.error attribute is not null, then throw an
+ // InvalidStateError exception and abort these steps.
+ if (!mMediaSource->GetDecoder() ||
+ mMediaSource->GetDecoder()->IsEndedOrShutdown()) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return nullptr;
+ }
+
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
}
diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp
index 61dc83fd99a4..26d4a1b0a2db 100644
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -978,6 +978,13 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult)
// 6. Set first initialization segment received flag to true.
mFirstInitializationSegmentReceived = true;
} else {
+ // Check that audio configuration hasn't changed as this is something
+ // we do not support yet (bug 1185827).
+ if (mAudioTracks.mNumTracks &&
+ (info.mAudio.mChannels != mAudioTracks.mInfo->GetAsAudioInfo()->mChannels ||
+ info.mAudio.mRate != mAudioTracks.mInfo->GetAsAudioInfo()->mRate)) {
+ RejectAppend(NS_ERROR_FAILURE, __func__);
+ }
mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
}
diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp
index a90aeea8a6e6..28e9b17112d7 100644
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -393,6 +393,10 @@ nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
#define HANDLE_DECODER_ERROR() \
if (NS_FAILED(res)) { \
NS_WARNING("exiting decoder loop due to exception"); \
+ if (mDraining) { \
+ ENVOKE_CALLBACK(DrainComplete); \
+ mDraining = false; \
+ } \
ENVOKE_CALLBACK(Error); \
break; \
}
diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp
index 901191d49d01..5f6c47dddc93 100644
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -55,8 +55,8 @@ bool nsMixedContentBlocker::sBlockMixedDisplay = false;
class nsMixedContentEvent : public nsRunnable
{
public:
- nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType)
- : mContext(aContext), mType(aType)
+ nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType, bool aRootHasSecureConnection)
+ : mContext(aContext), mType(aType), mRootHasSecureConnection(aRootHasSecureConnection)
{}
NS_IMETHOD Run()
@@ -85,6 +85,21 @@ public:
nsCOMPtr rootDoc = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
+ // Get eventSink and the current security state from the docShell
+ nsCOMPtr eventSink = do_QueryInterface(docShell);
+ NS_ASSERTION(eventSink, "No eventSink from docShell.");
+ nsCOMPtr rootShell = do_GetInterface(sameTypeRoot);
+ NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
+ uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
+ nsCOMPtr securityUI;
+ rootShell->GetSecurityUI(getter_AddRefs(securityUI));
+ // If there is no securityUI, document doesn't have a security state to
+ // update. But we still want to set the document flags, so we don't return
+ // early.
+ nsresult stateRV;
+ if (securityUI) {
+ stateRV = securityUI->GetState(&state);
+ }
if (mType == eMixedScript) {
// See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
@@ -94,18 +109,26 @@ public:
rootDoc->SetHasMixedActiveContentLoaded(true);
// Update the security UI in the tab with the allowed mixed active content
- nsCOMPtr eventSink = do_QueryInterface(docShell);
- if (eventSink) {
- // If mixed display content is loaded, make sure to include that in the state.
- if (rootDoc->GetHasMixedDisplayContentLoaded()) {
- eventSink->OnSecurityChange(mContext,
- (nsIWebProgressListener::STATE_IS_BROKEN |
- nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
- nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ if (securityUI) {
+ // Bug 1182551 - before changing the security state to broken, check
+ // that the root is actually secure.
+ if (mRootHasSecureConnection) {
+ // If mixed display content is loaded, make sure to include that in the state.
+ if (rootDoc->GetHasMixedDisplayContentLoaded()) {
+ eventSink->OnSecurityChange(mContext,
+ (nsIWebProgressListener::STATE_IS_BROKEN |
+ nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
+ nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ } else {
+ eventSink->OnSecurityChange(mContext,
+ (nsIWebProgressListener::STATE_IS_BROKEN |
+ nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ }
} else {
- eventSink->OnSecurityChange(mContext,
- (nsIWebProgressListener::STATE_IS_BROKEN |
- nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ // root not secure, mixed active content loaded in an https subframe
+ if (NS_SUCCEEDED(stateRV)) {
+ eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ }
}
}
@@ -117,17 +140,25 @@ public:
rootDoc->SetHasMixedDisplayContentLoaded(true);
// Update the security UI in the tab with the allowed mixed display content.
- nsCOMPtr eventSink = do_QueryInterface(docShell);
- if (eventSink) {
+ if (securityUI) {
+ // Bug 1182551 - before changing the security state to broken, check
+ // that the root is actually secure.
+ if (mRootHasSecureConnection) {
// If mixed active content is loaded, make sure to include that in the state.
- if (rootDoc->GetHasMixedActiveContentLoaded()) {
- eventSink->OnSecurityChange(mContext,
- (nsIWebProgressListener::STATE_IS_BROKEN |
- nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
- nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ if (rootDoc->GetHasMixedActiveContentLoaded()) {
+ eventSink->OnSecurityChange(mContext,
+ (nsIWebProgressListener::STATE_IS_BROKEN |
+ nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
+ nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ } else {
+ eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
+ nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ }
} else {
- eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
- nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ // root not secure, mixed display content loaded in an https subframe
+ if (NS_SUCCEEDED(stateRV)) {
+ eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ }
}
}
}
@@ -141,6 +172,9 @@ private:
// The type of mixed content detected, e.g. active or display
const MixedContentTypes mType;
+
+ // Indicates whether the top level load is https or not.
+ bool mRootHasSecureConnection;
};
@@ -673,7 +707,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
NS_ASSERTION(eventSink, "No eventSink from docShell.");
nsCOMPtr rootShell = do_GetInterface(sameTypeRoot);
NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
- uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN;
+ uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
nsCOMPtr securityUI;
rootShell->GetSecurityUI(getter_AddRefs(securityUI));
// If there is no securityUI, document doesn't have a security state.
@@ -682,7 +716,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
*aDecision = nsIContentPolicy::ACCEPT;
return NS_OK;
}
- nsresult stateRV = securityUI->GetState(&State);
+ nsresult stateRV = securityUI->GetState(&state);
// If the content is display content, and the pref says display content should be blocked, block it.
if (sBlockMixedDisplay && classification == eMixedDisplay) {
@@ -712,7 +746,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// User has overriden the pref and the root is not https;
// mixed display content was allowed on an https subframe.
if (NS_SUCCEEDED(stateRV)) {
- eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
}
}
} else {
@@ -720,7 +754,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
rootDoc->SetHasMixedDisplayContentBlocked(true);
- eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
+ eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
}
}
return NS_OK;
@@ -755,7 +789,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// User has already overriden the pref and the root is not https;
// mixed active content was allowed on an https subframe.
if (NS_SUCCEEDED(stateRV)) {
- eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
+ eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
}
return NS_OK;
}
@@ -772,7 +806,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// The user has not overriden the pref, so make sure they still have an option by calling eventSink
// which will invoke the doorhanger
if (NS_SUCCEEDED(stateRV)) {
- eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
+ eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
}
return NS_OK;
}
@@ -786,7 +820,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// Fire the event from a script runner as it is unsafe to run script
// from within ShouldLoad
nsContentUtils::AddScriptRunner(
- new nsMixedContentEvent(aRequestingContext, classification));
+ new nsMixedContentEvent(aRequestingContext, classification, rootHasSecureConnection));
*aDecision = ACCEPT;
return NS_OK;
}
diff --git a/dom/security/test/mixedcontentblocker/mochitest.ini b/dom/security/test/mixedcontentblocker/mochitest.ini
index f3a16e3e6dcf..f2595e856978 100644
--- a/dom/security/test/mixedcontentblocker/mochitest.ini
+++ b/dom/security/test/mixedcontentblocker/mochitest.ini
@@ -1,4 +1,5 @@
[DEFAULT]
+tags = mcb
support-files =
file_bug803225_test_mailto.html
file_frameNavigation.html
diff --git a/dom/xbl/nsXBLPrototypeBinding.cpp b/dom/xbl/nsXBLPrototypeBinding.cpp
index 94244d73dc88..8a060152214b 100644
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -901,7 +901,8 @@ nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
for (; interfaceCount > 0; interfaceCount--) {
nsIID iid;
- aStream->ReadID(&iid);
+ rv = aStream->ReadID(&iid);
+ NS_ENSURE_SUCCESS(rv, rv);
mInterfaceTable.Put(iid, mBinding);
}
diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp
index 77a5b4553d19..12397a69dcca 100644
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -240,9 +240,6 @@ NS_NewXULDocument(nsIXULDocument** result)
return NS_ERROR_NULL_POINTER;
XULDocument* doc = new XULDocument();
- if (! doc)
- return NS_ERROR_OUT_OF_MEMORY;
-
NS_ADDREF(doc);
nsresult rv;
@@ -449,8 +446,6 @@ XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
// the interesting work happens below XULDocument::EndLoad, from
// the call there to mCurrentPrototype->NotifyLoadDone().
*aDocListener = new CachedChromeStreamListener(this, loaded);
- if (! *aDocListener)
- return NS_ERROR_OUT_OF_MEMORY;
}
else {
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
@@ -1635,9 +1630,6 @@ XULDocument::AddElementToDocumentPre(Element* aElement)
// later.
if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
- if (! hookup)
- return NS_ERROR_OUT_OF_MEMORY;
-
rv = AddForwardReference(hookup);
if (NS_FAILED(rv)) return rv;
}
@@ -1668,9 +1660,6 @@ XULDocument::AddElementToDocumentPost(Element* aElement)
}
else {
TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
- if (! hookup)
- return NS_ERROR_OUT_OF_MEMORY;
-
rv = AddForwardReference(hookup);
if (NS_FAILED(rv))
return rv;
@@ -1875,7 +1864,6 @@ XULDocument::Init()
// Create our command dispatcher and hook it up.
mCommandDispatcher = new nsXULCommandDispatcher(this);
- NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
if (gRefCnt++ == 0) {
// ensure that the XUL prototype cache is instantiated successfully,
@@ -2007,7 +1995,6 @@ XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
// Create a XUL content sink, a parser, and kick off a load for
// the overlay.
nsRefPtr sink = new XULContentSinkImpl();
- if (!sink) return NS_ERROR_OUT_OF_MEMORY;
rv = sink->Init(this, mCurrentPrototype);
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
@@ -2195,9 +2182,6 @@ XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
nsIContent* aElement)
{
Entry* entry = new Entry;
- if (! entry)
- return NS_ERROR_OUT_OF_MEMORY;
-
entry->mPrototype = aPrototype;
entry->mElement = aElement;
NS_IF_ADDREF(entry->mElement);
@@ -2652,9 +2636,6 @@ XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
// and will let us recover from a missing overlay.
ParserObserver* parserObserver =
new ParserObserver(this, mCurrentPrototype);
- if (! parserObserver)
- return NS_ERROR_OUT_OF_MEMORY;
-
NS_ADDREF(parserObserver);
parser->Parse(aURI, parserObserver);
NS_RELEASE(parserObserver);
@@ -3632,8 +3613,6 @@ XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
OverlayForwardReference* fwdref =
new OverlayForwardReference(this, element);
- if (! fwdref)
- return NS_ERROR_OUT_OF_MEMORY;
// transferring ownership to ya...
rv = AddForwardReference(fwdref);
diff --git a/dom/xul/nsXULCommandDispatcher.cpp b/dom/xul/nsXULCommandDispatcher.cpp
index e91ab667669a..da104d2d25bc 100644
--- a/dom/xul/nsXULCommandDispatcher.cpp
+++ b/dom/xul/nsXULCommandDispatcher.cpp
@@ -310,11 +310,7 @@ nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
#endif
// If we get here, this is a new updater. Append it to the list.
- updater = new Updater(aElement, aEvents, aTargets);
- if (! updater)
- return NS_ERROR_OUT_OF_MEMORY;
-
- *link = updater;
+ *link = new Updater(aElement, aEvents, aTargets);
return NS_OK;
}
diff --git a/dom/xul/nsXULControllers.cpp b/dom/xul/nsXULControllers.cpp
index 57869faf29b4..6b795b29a68a 100644
--- a/dom/xul/nsXULControllers.cpp
+++ b/dom/xul/nsXULControllers.cpp
@@ -52,9 +52,6 @@ NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult)
return NS_ERROR_NO_AGGREGATION;
nsXULControllers* controllers = new nsXULControllers();
- if (! controllers)
- return NS_ERROR_OUT_OF_MEMORY;
-
nsresult rv;
NS_ADDREF(controllers);
rv = controllers->QueryInterface(aIID, aResult);
@@ -121,7 +118,6 @@ NS_IMETHODIMP
nsXULControllers::InsertControllerAt(uint32_t aIndex, nsIController *controller)
{
nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller);
- if (!controllerData) return NS_ERROR_OUT_OF_MEMORY;
#ifdef DEBUG
nsXULControllerData** inserted =
#endif
@@ -165,7 +161,6 @@ nsXULControllers::AppendController(nsIController *controller)
{
// This assigns controller IDs starting at 1 so we can use 0 to test if an ID was obtained
nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller);
- if (!controllerData) return NS_ERROR_OUT_OF_MEMORY;
#ifdef DEBUG
nsXULControllerData** appended =
diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp
index 04a019c68551..3abc01943be6 100644
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -2278,16 +2278,15 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
// Read Node Info
uint32_t number = 0;
nsresult rv = aStream->Read32(&number);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
if (!mNodeInfo) {
return NS_ERROR_UNEXPECTED;
}
// Read Attributes
- nsresult tmp = aStream->Read32(&number);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->Read32(&number);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mNumAttributes = int32_t(number);
if (mNumAttributes > 0) {
@@ -2298,10 +2297,8 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
nsAutoString attributeValue;
for (uint32_t i = 0; i < mNumAttributes; ++i) {
- tmp = aStream->Read32(&number);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->Read32(&number);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
if (!ni) {
return NS_ERROR_UNEXPECTED;
@@ -2309,31 +2306,25 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
mAttributes[i].mName.SetTo(ni);
- tmp = aStream->ReadString(attributeValue);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = SetAttrAt(i, attributeValue, aDocumentURI);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->ReadString(attributeValue);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+ rv = SetAttrAt(i, attributeValue, aDocumentURI);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
}
}
- tmp = aStream->Read32(&number);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->Read32(&number);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
uint32_t numChildren = int32_t(number);
if (numChildren > 0) {
- mChildren.SetCapacity(numChildren);
+ if (!mChildren.SetCapacity(numChildren, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
for (uint32_t i = 0; i < numChildren; i++) {
- tmp = aStream->Read32(&number);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->Read32(&number);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
Type childType = (Type)number;
nsRefPtr child;
@@ -2341,64 +2332,43 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
switch (childType) {
case eType_Element:
child = new nsXULPrototypeElement();
- child->mType = childType;
-
- tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
- aNodeInfos);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
+ aNodeInfos);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_Text:
child = new nsXULPrototypeText();
- child->mType = childType;
-
- tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
- aNodeInfos);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
+ aNodeInfos);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_PI:
child = new nsXULPrototypePI();
- child->mType = childType;
-
- tmp = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
- aNodeInfos);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
+ aNodeInfos);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
break;
case eType_Script: {
// language version/options obtained during deserialization.
- nsXULPrototypeScript* script = new nsXULPrototypeScript(0, 0);
- child = script;
- child->mType = childType;
+ nsRefPtr script = new nsXULPrototypeScript(0, 0);
- tmp = aStream->ReadBoolean(&script->mOutOfLine);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- if (! script->mOutOfLine) {
- tmp = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
- aNodeInfos);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = aStream->ReadBoolean(&script->mOutOfLine);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+ if (!script->mOutOfLine) {
+ rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
+ aNodeInfos);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
} else {
nsCOMPtr supports;
- tmp = aStream->ReadObject(true, getter_AddRefs(supports));
+ rv = aStream->ReadObject(true, getter_AddRefs(supports));
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
script->mSrcURI = do_QueryInterface(supports);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
- tmp = script->DeserializeOutOfLine(aStream, aProtoDoc);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
+ if (NS_WARN_IF(NS_FAILED(rv))) return rv;
}
- // If we failed to deserialize, consider deleting 'script'?
+
+ child = script.forget();
break;
}
default:
@@ -2407,6 +2377,7 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
}
MOZ_ASSERT(child, "Don't append null to mChildren");
+ MOZ_ASSERT(child->mType == childType);
mChildren.AppendElement(child);
// Oh dear. Something failed during the deserialization.
@@ -2417,7 +2388,7 @@ nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
// death. So, let's just fail now, and propagate that failure
// upward so that the ChromeProtocolHandler knows it can't use
// a cached chrome channel for this.
- if (NS_FAILED(rv))
+ if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
}
}
@@ -2617,13 +2588,16 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
nsIURI* aDocumentURI,
const nsTArray> *aNodeInfos)
{
+ nsresult rv;
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
!mScriptObject,
"prototype script not well-initialized when deserializing?!");
// Read basic prototype data
- aStream->Read32(&mLineNo);
- aStream->Read32(&mLangVersion);
+ rv = aStream->Read32(&mLineNo);
+ if (NS_FAILED(rv)) return rv;
+ rv = aStream->Read32(&mLangVersion);
+ if (NS_FAILED(rv)) return rv;
AutoSafeJSContext cx;
JS::Rooted global(cx, xpc::CompilationScope());
@@ -2631,8 +2605,8 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
JSAutoCompartment ac(cx, global);
JS::Rooted newScriptObject(cx);
- nsresult rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
- newScriptObject.address());
+ rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
+ newScriptObject.address());
NS_ENSURE_SUCCESS(rv, rv);
Set(newScriptObject);
return NS_OK;
@@ -2862,11 +2836,11 @@ nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
nsIURI* aDocumentURI,
const nsTArray> *aNodeInfos)
{
- nsresult rv;
-
- rv = aStream->ReadString(mValue);
-
- return rv;
+ nsresult rv = aStream->ReadString(mValue);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
//----------------------------------------------------------------------
@@ -2905,10 +2879,9 @@ nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream,
nsresult rv;
rv = aStream->ReadString(mTarget);
- nsresult tmp = aStream->ReadString(mData);
- if (NS_FAILED(tmp)) {
- rv = tmp;
- }
+ if (NS_FAILED(rv)) return rv;
+ rv = aStream->ReadString(mData);
+ if (NS_FAILED(rv)) return rv;
return rv;
}
diff --git a/dom/xul/nsXULPrototypeDocument.cpp b/dom/xul/nsXULPrototypeDocument.cpp
index 5e086958154f..1b4b411f1f21 100644
--- a/dom/xul/nsXULPrototypeDocument.cpp
+++ b/dom/xul/nsXULPrototypeDocument.cpp
@@ -54,8 +54,6 @@ nsresult
nsXULPrototypeDocument::Init()
{
mNodeInfoManager = new nsNodeInfoManager();
- NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
-
return mNodeInfoManager->Init(nullptr);
}
@@ -149,8 +147,6 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
mNodeInfoManager->SetDocumentPrincipal(principal);
mRoot = new nsXULPrototypeElement();
- if (! mRoot)
- return NS_ERROR_OUT_OF_MEMORY;
// mozilla::dom::NodeInfo table
nsTArray> nodeInfos;
@@ -208,10 +204,6 @@ nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream)
if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
nsRefPtr pi = new nsXULPrototypePI();
- if (! pi) {
- rv = NS_ERROR_OUT_OF_MEMORY;
- break;
- }
tmp = pi->Deserialize(aStream, this, mURI, &nodeInfos);
if (NS_FAILED(tmp)) {
diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
index c384671d6929..51edbda5c4af 100644
--- a/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -163,24 +163,34 @@ NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetContentDisposition(nsAString& aCD)
{
nsCOMPtr window = mDocument->GetDefaultView();
- NS_ENSURE_STATE(window);
+ if (NS_WARN_IF(!window)) {
+ aCD.SetIsVoid(true);
+ return NS_OK;
+ }
nsCOMPtr utils = do_GetInterface(window);
- NS_ENSURE_STATE(utils);
- return utils->GetDocumentMetadata(
+ if (NS_WARN_IF(!utils)) {
+ aCD.SetIsVoid(true);
+ return NS_OK;
+ }
+ nsresult rv = utils->GetDocumentMetadata(
NS_LITERAL_STRING("content-disposition"), aCD);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aCD.SetIsVoid(true);
+ }
+ return NS_OK;
}
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
{
- nsCOMPtr history;
- nsresult rv = GetHistory(getter_AddRefs(history));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_STATE(history);
+ nsCOMPtr history = GetHistory();
+ if (!history) {
+ *aKey = 0;
+ return NS_OK;
+ }
nsCOMPtr abstractKey;
- rv = history->GetCacheKey(getter_AddRefs(abstractKey));
- NS_ENSURE_SUCCESS(rv, rv);
- if (!abstractKey) {
+ nsresult rv = history->GetCacheKey(getter_AddRefs(abstractKey));
+ if (NS_WARN_IF(NS_FAILED(rv)) || !abstractKey) {
*aKey = 0;
return NS_OK;
}
@@ -195,29 +205,37 @@ WebBrowserPersistLocalDocument::GetCacheKey(uint32_t* aKey)
NS_IMETHODIMP
WebBrowserPersistLocalDocument::GetPostData(nsIInputStream** aStream)
{
- nsCOMPtr history;
- nsresult rv = GetHistory(getter_AddRefs(history));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_STATE(history);
+ nsCOMPtr history = GetHistory();
+ if (!history) {
+ *aStream = nullptr;
+ return NS_OK;
+ }
return history->GetPostData(aStream);
}
-nsresult
-WebBrowserPersistLocalDocument::GetHistory(nsISHEntry** aHistory)
+already_AddRefed
+WebBrowserPersistLocalDocument::GetHistory()
{
nsCOMPtr window = mDocument->GetDefaultView();
- NS_ENSURE_STATE(window);
+ if (NS_WARN_IF(!window)) {
+ return nullptr;
+ }
nsCOMPtr webNav = do_GetInterface(window);
- NS_ENSURE_STATE(webNav);
+ if (NS_WARN_IF(!webNav)) {
+ return nullptr;
+ }
nsCOMPtr desc = do_QueryInterface(webNav);
- NS_ENSURE_STATE(desc);
+ if (NS_WARN_IF(!desc)) {
+ return nullptr;
+ }
nsCOMPtr curDesc;
nsresult rv = desc->GetCurrentDescriptor(getter_AddRefs(curDesc));
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_STATE(curDesc);
+ // This can fail if, e.g., the document is a Print Preview.
+ if (NS_FAILED(rv) || NS_WARN_IF(!curDesc)) {
+ return nullptr;
+ }
nsCOMPtr history = do_QueryInterface(curDesc);
- history.forget(aHistory);
- return NS_OK;
+ return history.forget();
}
const nsCString&
@@ -328,7 +346,10 @@ ResourceReader::OnWalkSubframe(nsIDOMNode* aNode)
NS_ENSURE_STATE(loader);
++mOutstandingDocuments;
- nsresult rv = loader->StartPersistence(this);
+ // Pass in 0 as the outer window ID so that we start
+ // persisting the root of this subframe, and not some other
+ // subframe child of this subframe.
+ nsresult rv = loader->StartPersistence(0, this);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_NO_CONTENT) {
// Just ignore frames with no content document.
diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.h b/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.h
index 23176ef306fe..9110d6c35553 100644
--- a/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.h
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistLocalDocument.h
@@ -41,7 +41,7 @@ private:
nsresult GetDocEncoder(const nsACString& aContentType,
uint32_t aEncoderFlags,
nsIDocumentEncoder** aEncoder);
- nsresult GetHistory(nsISHEntry** aHistory);
+ already_AddRefed GetHistory();
virtual ~WebBrowserPersistLocalDocument();
};
diff --git a/embedding/components/webbrowserpersist/WebBrowserPersistResourcesChild.cpp b/embedding/components/webbrowserpersist/WebBrowserPersistResourcesChild.cpp
index 443f26f4faef..fed58f73fa0d 100644
--- a/embedding/components/webbrowserpersist/WebBrowserPersistResourcesChild.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistResourcesChild.cpp
@@ -37,7 +37,11 @@ WebBrowserPersistResourcesChild::VisitDocument(nsIWebBrowserPersistDocument* aDo
{
auto* subActor = new WebBrowserPersistDocumentChild();
dom::PBrowserChild* grandManager = Manager()->Manager();
- if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor)) {
+ // As a consequence of how PWebBrowserPersistDocumentConstructor can be
+ // sent by both the parent and the child, we must pass the outerWindowID
+ // argument here. We pass 0, though note that this argument is actually
+ // just ignored when passed up to the parent from the child.
+ if (!grandManager->SendPWebBrowserPersistDocumentConstructor(subActor, 0)) {
// NOTE: subActor is freed at this point.
return NS_ERROR_FAILURE;
}
diff --git a/embedding/components/webbrowserpersist/nsIWebBrowserPersistable.idl b/embedding/components/webbrowserpersist/nsIWebBrowserPersistable.idl
index f57b383b830c..39ea6b33b034 100644
--- a/embedding/components/webbrowserpersist/nsIWebBrowserPersistable.idl
+++ b/embedding/components/webbrowserpersist/nsIWebBrowserPersistable.idl
@@ -22,9 +22,20 @@ interface nsIWebBrowserPersistDocumentReceiver;
* @see nsIWebBrowserPersistDocumentReceiver
* @see nsIWebBrowserPersistDocument
* @see nsIWebBrowserPersist
+ *
+ * @param aOuterWindowID
+ * The outer window ID of the subframe we'd like to persist.
+ * If set at 0, nsIWebBrowserPersistable will attempt to persist
+ * the top-level document. If the outer window ID is for a subframe
+ * that does not exist, or is not held beneath the nsIWebBrowserPersistable,
+ * aRecv's onError method will be called with NS_ERROR_NO_CONTENT.
+ * @param aRecv
+ * The nsIWebBrowserPersistDocumentReceiver is a callback that
+ * will be fired once the document is ready for persisting.
*/
-[scriptable, function, uuid(24d0dc9e-b970-4cca-898f-cbba03abaa73)]
+[scriptable, uuid(f4c3fa8e-83e9-49f8-ac6f-951fc7541fe4)]
interface nsIWebBrowserPersistable : nsISupports
{
- void startPersistence(in nsIWebBrowserPersistDocumentReceiver aRecv);
+ void startPersistence(in unsigned long long aOuterWindowID,
+ in nsIWebBrowserPersistDocumentReceiver aRecv);
};
diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build
index 21eb5a9e3f3b..e2618bc89f39 100644
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -37,6 +37,7 @@ EXPORTS.mozilla.gfx += [
'ScaleFactor.h',
'ScaleFactors2D.h',
'SourceSurfaceCairo.h',
+ 'StackArray.h',
'Tools.h',
'Types.h',
'UserData.h',
diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp
index d38fdf2d54cd..536623a8bb01 100644
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -281,6 +281,9 @@ ImageHost::Composite(LayerComposite* aLayer,
TimedImage* img = &mImages[imageIndex];
// Make sure the front buffer has a compositor
img->mFrontBuffer->SetCompositor(GetCompositor());
+ if (img->mTextureSource) {
+ img->mTextureSource->SetCompositor(GetCompositor());
+ }
{
AutoLockCompositableHost autoLock(this);
diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp
index 278234a0e885..87eac4d19c3c 100644
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -18,6 +18,7 @@
#include "gfxPrefs.h"
#include "gfxCrashReporterUtils.h"
#include "gfxVR.h"
+#include "mozilla/gfx/StackArray.h"
#include "mozilla/EnumeratedArray.h"
@@ -1091,23 +1092,20 @@ CompositorD3D11::EndFrame()
DXGI_PRESENT_PARAMETERS params;
PodZero(¶ms);
params.DirtyRectsCount = mInvalidRegion.GetNumRects();
- std::vector rects;
- rects.reserve(params.DirtyRectsCount);
+ StackArray rects(params.DirtyRectsCount);
nsIntRegionRectIterator iter(mInvalidRegion);
const IntRect* r;
uint32_t i = 0;
while ((r = iter.Next()) != nullptr) {
- RECT rect;
- rect.left = r->x;
- rect.top = r->y;
- rect.bottom = r->YMost();
- rect.right = r->XMost();
-
- rects.push_back(rect);
+ rects[i].left = r->x;
+ rects[i].top = r->y;
+ rects[i].bottom = r->YMost();
+ rects[i].right = r->XMost();
+ i++;
}
- params.pDirtyRects = &rects.front();
+ params.pDirtyRects = rects.data();
chain->Present1(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0, ¶ms);
} else {
mSwapChain->Present(presentInterval, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
index db4f7bbdb42e..bea27ebd1765 100644
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -136,6 +136,9 @@ void
MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
{
mCompositor = static_cast(aCompositor);
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
}
gl::GLContext*
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp
index ea8b66a62221..e8f7d56409d1 100644
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -335,6 +335,9 @@ GLTextureSource::SetCompositor(Compositor* aCompositor)
{
MOZ_ASSERT(aCompositor);
mCompositor = static_cast(aCompositor);
+ if (mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
}
bool
diff --git a/gfx/src/DriverCrashGuard.cpp b/gfx/src/DriverCrashGuard.cpp
index 448fb1e300ba..a4384e113a65 100644
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -157,8 +157,6 @@ DriverCrashGuard::GetGuardFile()
void
DriverCrashGuard::ActivateGuard()
{
- MOZ_ASSERT(XRE_IsParentProcess());
-
mGuardActivated = true;
#ifdef MOZ_CRASHREPORTER
diff --git a/image/DecodePool.cpp b/image/DecodePool.cpp
index 09ed7e965f05..209b481bf069 100644
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -451,7 +451,10 @@ DecodePool::Decode(Decoder* aDecoder)
nsresult rv = aDecoder->Decode();
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
- if (aDecoder->HasProgress()) {
+ // If this isn't a metadata decode, notify for the progress we've made so
+ // far. It's important that metadata decode results are delivered
+ // atomically, so for those decodes we wait until NotifyDecodeComplete.
+ if (aDecoder->HasProgress() && !aDecoder->IsMetadataDecode()) {
NotifyProgress(aDecoder);
}
// The decoder will ensure that a new worker gets enqueued to continue
diff --git a/image/Decoder.cpp b/image/Decoder.cpp
index 1d95d65f86e4..d663dcb4c27d 100644
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -37,10 +37,8 @@ Decoder::Decoder(RasterImage* aImage)
, mMetadataDecode(false)
, mSendPartialInvalidations(false)
, mImageIsTransient(false)
- , mImageIsLocked(false)
, mFirstFrameDecode(false)
, mInFrame(false)
- , mIsAnimated(false)
, mDataDone(false)
, mDecodeDone(false)
, mDataError(false)
@@ -237,7 +235,7 @@ Decoder::CompleteDecode()
// If this image wasn't animated and isn't a transient image, mark its frame
// as optimizable. We don't support optimizing animated images and
// optimizing transient images isn't worth it.
- if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
+ if (!HasAnimation() && !mImageIsTransient && mCurrentFrame) {
mCurrentFrame->SetOptimizable();
}
}
@@ -260,7 +258,12 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
if (aFrameNum + 1 == mFrameCount) {
- PostFrameStart();
+ // If we're past the first frame, PostIsAnimated() should've been called.
+ MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
+
+ // Update our state to reflect the new frame
+ MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
+ mInFrame = true;
}
} else {
PostDataError();
@@ -406,19 +409,11 @@ Decoder::PostHasTransparency()
}
void
-Decoder::PostFrameStart()
+Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
{
- // We shouldn't already be mid-frame
- MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
-
- // Update our state to reflect the new frame
- mInFrame = true;
-
- // If we just became animated, record that fact.
- if (mFrameCount > 1) {
- mIsAnimated = true;
- mProgress |= FLAG_IS_ANIMATED;
- }
+ mProgress |= FLAG_IS_ANIMATED;
+ mImageMetadata.SetHasAnimation();
+ mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
}
void
@@ -442,7 +437,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
// If we're not sending partial invalidations, then we send an invalidation
// here when the first frame is complete.
- if (!mSendPartialInvalidations && !mIsAnimated) {
+ if (!mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect,
gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
}
@@ -459,7 +454,7 @@ Decoder::PostInvalidation(const nsIntRect& aRect,
// Record this invalidation, unless we're not sending partial invalidations
// or we're past the first frame.
- if (mSendPartialInvalidations && !mIsAnimated) {
+ if (mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect, aRect);
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
}
diff --git a/image/Decoder.h b/image/Decoder.h
index 9ec512c9e497..7e8f7624f0d6 100644
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -181,20 +181,6 @@ public:
mImageIsTransient = aIsTransient;
}
- /**
- * Set whether the image is locked for the lifetime of this decoder. We lock
- * the image during our initial decode to ensure that we don't evict any
- * surfaces before we realize that the image is animated.
- */
- void SetImageIsLocked()
- {
- MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
- mImageIsLocked = true;
- }
-
- bool ImageIsLocked() const { return mImageIsLocked; }
-
-
/**
* Set whether we should stop decoding after the first frame.
*/
@@ -225,7 +211,7 @@ public:
}
// Did we discover that the image we're decoding is animated?
- bool HasAnimation() const { return mIsAnimated; }
+ bool HasAnimation() const { return mImageMetadata.HasAnimation(); }
// Error tracking
bool HasError() const { return HasDataError() || HasDecoderError(); }
@@ -344,9 +330,11 @@ protected:
// actual contents of the frame and give a more accurate result.
void PostHasTransparency();
- // Called by decoders when they begin a frame. Informs the image, sends
- // notifications, and does internal book-keeping.
- void PostFrameStart();
+ // Called by decoders if they determine that the image is animated.
+ //
+ // @param aTimeout The time for which the first frame should be shown before
+ // we advance to the next frame.
+ void PostIsAnimated(int32_t aFirstFrameTimeout);
// Called by decoders when they end a frame. Informs the image, sends
// notifications, and does internal book-keeping.
@@ -451,10 +439,8 @@ private:
bool mMetadataDecode : 1;
bool mSendPartialInvalidations : 1;
bool mImageIsTransient : 1;
- bool mImageIsLocked : 1;
bool mFirstFrameDecode : 1;
bool mInFrame : 1;
- bool mIsAnimated : 1;
bool mDataDone : 1;
bool mDecodeDone : 1;
bool mDataError : 1;
diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp
index 932a2c9e3647..6aecfa1fe64b 100644
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -113,8 +113,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
int aSampleSize,
const IntSize& aResolution,
bool aIsRedecode,
- bool aImageIsTransient,
- bool aImageIsLocked)
+ bool aImageIsTransient)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
@@ -131,10 +130,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
decoder->SetResolution(aResolution);
decoder->SetSendPartialInvalidations(!aIsRedecode);
decoder->SetImageIsTransient(aImageIsTransient);
-
- if (aImageIsLocked) {
- decoder->SetImageIsLocked();
- }
+ decoder->SetIsFirstFrameDecode();
// Set a target size for downscale-during-decode if applicable.
if (aTargetSize) {
@@ -152,6 +148,39 @@ DecoderFactory::CreateDecoder(DecoderType aType,
return decoder.forget();
}
+/* static */ already_AddRefed
+DecoderFactory::CreateAnimationDecoder(DecoderType aType,
+ RasterImage* aImage,
+ SourceBuffer* aSourceBuffer,
+ uint32_t aFlags,
+ const IntSize& aResolution)
+{
+ if (aType == DecoderType::UNKNOWN) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG,
+ "Calling CreateAnimationDecoder for non-animating DecoderType");
+
+ nsRefPtr decoder =
+ GetDecoder(aType, aImage, /* aIsRedecode = */ true);
+ MOZ_ASSERT(decoder, "Should have a decoder now");
+
+ // Initialize the decoder.
+ decoder->SetMetadataDecode(false);
+ decoder->SetIterator(aSourceBuffer->Iterator());
+ decoder->SetFlags(aFlags);
+ decoder->SetResolution(aResolution);
+ decoder->SetSendPartialInvalidations(false);
+
+ decoder->Init();
+ if (NS_FAILED(decoder->GetDecoderError())) {
+ return nullptr;
+ }
+
+ return decoder.forget();
+}
+
/* static */ already_AddRefed
DecoderFactory::CreateMetadataDecoder(DecoderType aType,
RasterImage* aImage,
diff --git a/image/DecoderFactory.h b/image/DecoderFactory.h
index 358c47212aff..01a5e78ca1ae 100644
--- a/image/DecoderFactory.h
+++ b/image/DecoderFactory.h
@@ -38,12 +38,13 @@ public:
static DecoderType GetDecoderType(const char* aMimeType);
/**
- * Creates and initializes a decoder of type @aType. The decoder will send
- * notifications to @aImage.
+ * Creates and initializes a decoder for non-animated images of type @aType.
+ * (If the image *is* animated, only the first frame will be decoded.) The
+ * decoder will send notifications to @aImage.
*
- * XXX(seth): @aIsRedecode, @aImageIsTransient, and @aImageIsLocked should
- * really be part of @aFlags. This requires changes to the way that decoder
- * flags work, though. See bug 1185800.
+ * XXX(seth): @aIsRedecode and @aImageIsTransient should really be part of
+ * @aFlags. This requires changes to the way that decoder flags work, though.
+ * See bug 1185800.
*
* @param aType Which type of decoder to create - JPEG, PNG, etc.
* @param aImage The image will own the decoder and which should receive
@@ -62,9 +63,6 @@ public:
* empty rect if none).
* @param aIsRedecode Specify 'true' if this image has been decoded before.
* @param aImageIsTransient Specify 'true' if this image is transient.
- * @param aImageIsLocked Specify 'true' if this image is locked for the
- * lifetime of this decoder, and should be unlocked
- * when the decoder finishes.
*/
static already_AddRefed
CreateDecoder(DecoderType aType,
@@ -75,8 +73,28 @@ public:
int aSampleSize,
const gfx::IntSize& aResolution,
bool aIsRedecode,
- bool aImageIsTransient,
- bool aImageIsLocked);
+ bool aImageIsTransient);
+
+ /**
+ * Creates and initializes a decoder for animated images of type @aType.
+ * The decoder will send notifications to @aImage.
+ *
+ * @param aType Which type of decoder to create - JPEG, PNG, etc.
+ * @param aImage The image will own the decoder and which should receive
+ * notifications as decoding progresses.
+ * @param aSourceBuffer The SourceBuffer which the decoder will read its data
+ * from.
+ * @param aFlags Flags specifying what type of output the decoder should
+ * produce; see GetDecodeFlags() in RasterImage.h.
+ * @param aResolution The resolution requested using #-moz-resolution (or an
+ * empty rect if none).
+ */
+ static already_AddRefed
+ CreateAnimationDecoder(DecoderType aType,
+ RasterImage* aImage,
+ SourceBuffer* aSourceBuffer,
+ uint32_t aFlags,
+ const gfx::IntSize& aResolution);
/**
* Creates and initializes a metadata decoder of type @aType. This decoder
diff --git a/image/FrameAnimator.cpp b/image/FrameAnimator.cpp
index 6fdaa15731ac..316e2e115192 100644
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -291,14 +291,19 @@ FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
int32_t
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
{
+ int32_t rawTimeout = 0;
+
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
- if (!frame) {
+ if (frame) {
+ AnimationData data = frame->GetAnimationData();
+ rawTimeout = data.mRawTimeout;
+ } else if (aFrameNum == 0) {
+ rawTimeout = mFirstFrameTimeout;
+ } else {
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
return 100;
}
- AnimationData data = frame->GetAnimationData();
-
// Ensure a minimal time between updates so we don't throttle the UI thread.
// consider 0 == unspecified and make it fast but not too fast. Unless we
// have a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug
@@ -312,11 +317,11 @@ FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
// It seems that there are broken tools out there that set a 0ms or 10ms
// timeout when they really want a "default" one. So munge values in that
// range.
- if (data.mRawTimeout >= 0 && data.mRawTimeout <= 10) {
+ if (rawTimeout >= 0 && rawTimeout <= 10) {
return 100;
}
- return data.mRawTimeout;
+ return rawTimeout;
}
static void
diff --git a/image/FrameAnimator.h b/image/FrameAnimator.h
index a404d01c714f..95a2a13b910b 100644
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -33,6 +33,7 @@ public:
, mLoopRemainingCount(-1)
, mLastCompositedFrameIndex(-1)
, mLoopCount(-1)
+ , mFirstFrameTimeout(0)
, mAnimationMode(aAnimationMode)
, mDoneDecoding(false)
{ }
@@ -148,6 +149,12 @@ public:
void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
int32_t LoopCount() const { return mLoopCount; }
+ /*
+ * Set the timeout for the first frame. This is used to allow animation
+ * scheduling even before a full decode runs for this image.
+ */
+ void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
+
/**
* Collect an accounting of the memory occupied by the compositing surfaces we
* use during animation playback. All of the actual animation frames are
@@ -277,6 +284,9 @@ private: // data
//! The total number of loops for the image.
int32_t mLoopCount;
+ //! The timeout for the first frame of this image.
+ int32_t mFirstFrameTimeout;
+
//! The animation mode of this image. Constants defined in imgIContainer.
uint16_t mAnimationMode;
diff --git a/image/ImageMetadata.cpp b/image/ImageMetadata.cpp
deleted file mode 100644
index 236228520a3e..000000000000
--- a/image/ImageMetadata.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ImageMetadata.h"
-
-#include "RasterImage.h"
-#include "nsComponentManagerUtils.h"
-#include "nsISupportsPrimitives.h"
-#include "nsXPCOMCID.h"
-
-namespace mozilla {
-namespace image {
-
-nsresult
-ImageMetadata::SetOnImage(RasterImage* aImage)
-{
- nsresult rv = NS_OK;
-
- if (mHotspotX != -1 && mHotspotY != -1) {
- nsCOMPtr intwrapx =
- do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
- nsCOMPtr intwrapy =
- do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
- intwrapx->SetData(mHotspotX);
- intwrapy->SetData(mHotspotY);
- aImage->Set("hotspotX", intwrapx);
- aImage->Set("hotspotY", intwrapy);
- }
-
- aImage->SetLoopCount(mLoopCount);
-
- if (HasSize()) {
- MOZ_ASSERT(HasOrientation(), "Should have orientation");
- rv = aImage->SetSize(GetWidth(), GetHeight(), GetOrientation());
- }
-
- return rv;
-}
-
-} // namespace image
-} // namespace mozilla
diff --git a/image/ImageMetadata.h b/image/ImageMetadata.h
index c257612e0213..defa56af4c00 100644
--- a/image/ImageMetadata.h
+++ b/image/ImageMetadata.h
@@ -22,23 +22,26 @@ class ImageMetadata
{
public:
ImageMetadata()
- : mHotspotX(-1)
- , mHotspotY(-1)
- , mLoopCount(-1)
+ : mLoopCount(-1)
+ , mFirstFrameTimeout(0)
+ , mHasAnimation(false)
{ }
- // Set the metadata this object represents on an image.
- nsresult SetOnImage(RasterImage* aImage);
-
- void SetHotspot(uint16_t hotspotx, uint16_t hotspoty)
+ void SetHotspot(uint16_t aHotspotX, uint16_t aHotspotY)
{
- mHotspotX = hotspotx;
- mHotspotY = hotspoty;
+ mHotspot = Some(gfx::IntPoint(aHotspotX, aHotspotY));
}
+ gfx::IntPoint GetHotspot() const { return *mHotspot; }
+ bool HasHotspot() const { return mHotspot.isSome(); }
+
void SetLoopCount(int32_t loopcount)
{
mLoopCount = loopcount;
}
+ int32_t GetLoopCount() const { return mLoopCount; }
+
+ void SetFirstFrameTimeout(int32_t aTimeout) { mFirstFrameTimeout = aTimeout; }
+ int32_t GetFirstFrameTimeout() const { return mFirstFrameTimeout; }
void SetSize(int32_t width, int32_t height, Orientation orientation)
{
@@ -47,25 +50,28 @@ public:
mOrientation.emplace(orientation);
}
}
-
+ nsIntSize GetSize() const { return *mSize; }
+ Orientation GetOrientation() const { return *mOrientation; }
bool HasSize() const { return mSize.isSome(); }
bool HasOrientation() const { return mOrientation.isSome(); }
- int32_t GetWidth() const { return mSize->width; }
- int32_t GetHeight() const { return mSize->height; }
- nsIntSize GetSize() const { return *mSize; }
- Orientation GetOrientation() const { return *mOrientation; }
+ void SetHasAnimation() { mHasAnimation = true; }
+ bool HasAnimation() const { return mHasAnimation; }
private:
- // The hotspot found on cursors, or -1 if none was found.
- int32_t mHotspotX;
- int32_t mHotspotY;
+ /// The hotspot found on cursors, if present.
+ Maybe mHotspot;
- // The loop count for animated images, or -1 for infinite loop.
+ /// The loop count for animated images, or -1 for infinite loop.
int32_t mLoopCount;
+ /// The timeout of an animated image's first frame.
+ int32_t mFirstFrameTimeout;
+
Maybe mSize;
Maybe mOrientation;
+
+ bool mHasAnimation : 1;
};
} // namespace image
diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp
index da062530a912..22168ab9961e 100644
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -24,6 +24,7 @@
#include "nsIConsoleService.h"
#include "nsIInputStream.h"
#include "nsIScriptError.h"
+#include "nsISupportsPrimitives.h"
#include "nsPresContext.h"
#include "SourceBuffer.h"
#include "SurfaceCache.h"
@@ -478,7 +479,7 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
// We don't have a copy of this frame, and there's no decoder working on
// one. (Or we're sync decoding and the existing decoder hasn't even started
// yet.) Trigger decoding so it'll be available next time.
- MOZ_ASSERT(!mAnim, "Animated frames should be locked");
+ MOZ_ASSERT(!mAnim || GetNumFrames() < 1, "Animated frames should be locked");
Decode(requestedSize, aFlags);
@@ -529,7 +530,7 @@ RasterImage::GetRequestedFrameIndex(uint32_t aWhichFrame) const
IntRect
RasterImage::GetFirstFrameRect()
{
- if (mAnim) {
+ if (mAnim && mHasBeenDecoded) {
return mAnim->GetFirstFrameRefreshArea();
}
@@ -582,7 +583,10 @@ RasterImage::GetAnimated(bool* aAnimated)
}
// Otherwise, we need to have been decoded to know for sure, since if we were
- // decoded at least once mAnim would have been created for animated images
+ // decoded at least once mAnim would have been created for animated images.
+ // This is true even though we check for animation during the metadata decode,
+ // because we may still discover animation only during the full decode for
+ // corrupt images.
if (!mHasBeenDecoded) {
return NS_ERROR_NOT_AVAILABLE;
}
@@ -921,21 +925,9 @@ RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
mFrameCount = aNewFrameCount;
if (aNewFrameCount == 2) {
- // We're becoming animated, so initialize animation stuff.
- MOZ_ASSERT(!mAnim, "Already have animation state?");
- mAnim = MakeUnique(this, mSize, mAnimationMode);
-
- // We don't support discarding animated images (See bug 414259).
- // Lock the image and throw away the key.
- //
- // Note that this is inefficient, since we could get rid of the source
- // data too. However, doing this is actually hard, because we're probably
- // mid-decode, and thus we're decoding out of the source buffer. Since
- // we're going to fix this anyway later, and since we didn't kill the
- // source data in the old world either, locking is acceptable for the
- // moment.
- LockImage();
+ MOZ_ASSERT(mAnim, "Should already have animation state");
+ // We may be able to start animating.
if (mPendingAnimation && ShouldAnimate()) {
StartAnimation();
}
@@ -947,7 +939,8 @@ RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
}
nsresult
-RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
+RasterImage::SetMetadata(const ImageMetadata& aMetadata,
+ bool aFromMetadataDecode)
{
MOZ_ASSERT(NS_IsMainThread());
@@ -955,26 +948,64 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation)
return NS_ERROR_FAILURE;
}
- // Ensure that we have positive values
- // XXX - Why isn't the size unsigned? Should this be changed?
- if ((aWidth < 0) || (aHeight < 0)) {
- return NS_ERROR_INVALID_ARG;
+ if (aMetadata.HasSize()) {
+ IntSize size = aMetadata.GetSize();
+ if (size.width < 0 || size.height < 0) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MOZ_ASSERT(aMetadata.HasOrientation());
+ Orientation orientation = aMetadata.GetOrientation();
+
+ // If we already have a size, check the new size against the old one.
+ if (mHasSize && (size != mSize || orientation != mOrientation)) {
+ NS_WARNING("Image changed size or orientation on redecode! "
+ "This should not happen!");
+ DoError();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Set the size and flag that we have it.
+ mSize = size;
+ mOrientation = orientation;
+ mHasSize = true;
}
- // if we already have a size, check the new size against the old one
- if (mHasSize &&
- ((aWidth != mSize.width) ||
- (aHeight != mSize.height) ||
- (aOrientation != mOrientation))) {
- NS_WARNING("Image changed size on redecode! This should not happen!");
- DoError();
- return NS_ERROR_UNEXPECTED;
+ if (mHasSize && aMetadata.HasAnimation() && !mAnim) {
+ // We're becoming animated, so initialize animation stuff.
+ mAnim = MakeUnique(this, mSize, mAnimationMode);
+
+ // We don't support discarding animated images (See bug 414259).
+ // Lock the image and throw away the key.
+ LockImage();
+
+ if (!aFromMetadataDecode) {
+ // The metadata decode reported that this image isn't animated, but we
+ // discovered that it actually was during the full decode. This is a
+ // rare failure that only occurs for corrupt images. To recover, we need
+ // to discard all existing surfaces and redecode.
+ RecoverFromLossOfFrames(mSize, DECODE_FLAGS_DEFAULT);
+ }
}
- // Set the size and flag that we have it
- mSize.SizeTo(aWidth, aHeight);
- mOrientation = aOrientation;
- mHasSize = true;
+ if (mAnim) {
+ mAnim->SetLoopCount(aMetadata.GetLoopCount());
+ mAnim->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
+ }
+
+ if (aMetadata.HasHotspot()) {
+ IntPoint hotspot = aMetadata.GetHotspot();
+
+ nsCOMPtr intwrapx =
+ do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
+ nsCOMPtr intwrapy =
+ do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
+ intwrapx->SetData(hotspot.x);
+ intwrapy->SetData(hotspot.y);
+
+ Set("hotspotX", intwrapx);
+ Set("hotspotY", intwrapy);
+ }
return NS_OK;
}
@@ -999,10 +1030,9 @@ RasterImage::StartAnimation()
MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
- // If we don't have mAnim yet, then we're not ready to animate. Setting
- // mPendingAnimation will cause us to start animating as soon as we have a
- // second frame, which causes mAnim to be constructed.
- mPendingAnimation = !mAnim;
+ // If we're not ready to animate, then set mPendingAnimation, which will cause
+ // us to start animating if and when we do become ready.
+ mPendingAnimation = !mAnim || GetNumFrames() < 2;
if (mPendingAnimation) {
return NS_OK;
}
@@ -1091,19 +1121,6 @@ RasterImage::GetFrameIndex(uint32_t aWhichFrame)
: mAnim->GetCurrentAnimationFrameIndex();
}
-void
-RasterImage::SetLoopCount(int32_t aLoopCount)
-{
- if (mError) {
- return;
- }
-
- // No need to set this if we're not an animation.
- if (mAnim) {
- mAnim->SetLoopCount(aLoopCount);
- }
-}
-
NS_IMETHODIMP_(IntRect)
RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
{
@@ -1391,20 +1408,19 @@ RasterImage::Decode(const IntSize& aSize, uint32_t aFlags)
Maybe targetSize = mSize != aSize ? Some(aSize) : Nothing();
- bool imageIsLocked = false;
- if (!mHasBeenDecoded) {
- // Lock the image while we're decoding, so that it doesn't get evicted from
- // the SurfaceCache before we have a chance to realize that it's animated.
- // The corresponding unlock happens in FinalizeDecoder.
- LockImage();
- imageIsLocked = true;
- }
-
// Create a decoder.
- nsRefPtr decoder =
- DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer, targetSize,
- aFlags, mRequestedSampleSize, mRequestedResolution,
- mHasBeenDecoded, mTransient, imageIsLocked);
+ nsRefPtr decoder;
+ if (mAnim) {
+ decoder = DecoderFactory::CreateAnimationDecoder(mDecoderType, this,
+ mSourceBuffer, aFlags,
+ mRequestedResolution);
+ } else {
+ decoder = DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer,
+ targetSize, aFlags,
+ mRequestedSampleSize,
+ mRequestedResolution,
+ mHasBeenDecoded, mTransient);
+ }
// Make sure DecoderFactory was able to create a decoder successfully.
if (!decoder) {
@@ -1483,6 +1499,11 @@ RasterImage::RecoverFromLossOfFrames(const IntSize& aSize, uint32_t aFlags)
// Discard all existing frames, since they're probably all now invalid.
SurfaceCache::RemoveImage(ImageKey(this));
+ // Relock the image if it's supposed to be locked.
+ if (mLockCount > 0) {
+ SurfaceCache::LockImage(ImageKey(this));
+ }
+
// Animated images require some special handling, because we normally require
// that they never be discarded.
if (mAnim) {
@@ -1948,7 +1969,8 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
// Record all the metadata the decoder gathered about this image.
- nsresult rv = aDecoder->GetImageMetadata().SetOnImage(this);
+ nsresult rv = SetMetadata(aDecoder->GetImageMetadata(),
+ aDecoder->IsMetadataDecode());
if (NS_FAILED(rv)) {
aDecoder->PostResizeError();
}
@@ -1959,17 +1981,8 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
if (aDecoder->GetDecodeTotallyDone() && !mError) {
// Flag that we've been decoded before.
mHasBeenDecoded = true;
-
- if (aDecoder->HasAnimation()) {
- if (mAnim) {
- mAnim->SetDoneDecoding(true);
- } else {
- // The OnAddedFrame event that will create mAnim is still in the event
- // queue. Wait for it.
- nsCOMPtr runnable =
- NS_NewRunnableMethod(this, &RasterImage::MarkAnimationDecoded);
- NS_DispatchToMainThread(runnable);
- }
+ if (mAnim) {
+ mAnim->SetDoneDecoding(true);
}
}
@@ -2017,11 +2030,6 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
}
- if (aDecoder->ImageIsLocked()) {
- // Unlock the image, balancing the LockImage call we made in CreateDecoder.
- UnlockImage();
- }
-
// If we were a metadata decode and a full decode was requested, do it.
if (done && wasMetadata && mWantFullDecode) {
mWantFullDecode = false;
@@ -2029,17 +2037,6 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
}
}
-void
-RasterImage::MarkAnimationDecoded()
-{
- MOZ_ASSERT(mAnim, "Should have an animation now");
- if (!mAnim) {
- return;
- }
-
- mAnim->SetDoneDecoding(true);
-}
-
void
RasterImage::ReportDecoderError(Decoder* aDecoder)
{
diff --git a/image/RasterImage.h b/image/RasterImage.h
index 13a1392b2974..4d591e52166d 100644
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -132,6 +132,7 @@ namespace image {
class Decoder;
class FrameAnimator;
+class ImageMetadata;
class SourceBuffer;
/**
@@ -188,18 +189,6 @@ public:
void OnAddedFrame(uint32_t aNewFrameCount, const nsIntRect& aNewRefreshArea);
- /** Sets the size and inherent orientation of the container. This should only
- * be called by the decoder. This function may be called multiple times, but
- * will throw an error if subsequent calls do not match the first.
- */
- nsresult SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation);
-
- /**
- * Number of times to loop the image.
- * @note -1 means forever.
- */
- void SetLoopCount(int32_t aLoopCount);
-
/**
* Sends the provided progress notifications to ProgressTracker.
*
@@ -222,8 +211,7 @@ public:
*/
void FinalizeDecoder(Decoder* aDecoder);
- // Helper methods for FinalizeDecoder.
- void MarkAnimationDecoded();
+ // Helper method for FinalizeDecoder.
void ReportDecoderError(Decoder* aDecoder);
@@ -339,6 +327,19 @@ private:
*/
NS_IMETHOD DecodeMetadata(uint32_t aFlags);
+ /**
+ * Sets the size, inherent orientation, animation metadata, and other
+ * information about the image gathered during decoding.
+ *
+ * This function may be called multiple times, but will throw an error if
+ * subsequent calls do not match the first.
+ *
+ * @param aMetadata The metadata to set on this image.
+ * @param aFromMetadataDecode True if this metadata came from a metadata
+ * decode; false if it came from a full decode.
+ */
+ nsresult SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
+
/**
* In catastrophic circumstances like a GPU driver crash, we may lose our
* frames even if they're locked. RecoverFromLossOfFrames discards all
diff --git a/image/decoders/icon/mac/nsIconChannelCocoa.mm b/image/decoders/icon/mac/nsIconChannelCocoa.mm
index 77e0c31bb020..c65effe7eb28 100644
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -225,6 +225,10 @@ NS_IMETHODIMP
nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
nsISupports* ctxt)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
nsCOMPtr inStream;
nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
NS_ENSURE_SUCCESS(rv, rv);
diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp
index f180aa73b4e2..367d46beae46 100644
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -239,6 +239,10 @@ NS_IMETHODIMP
nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
nsISupports* ctxt)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
nsCOMPtr inStream;
nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
if (NS_FAILED(rv)) {
diff --git a/image/decoders/nsGIFDecoder2.cpp b/image/decoders/nsGIFDecoder2.cpp
index 898436e11814..a3a4ab86ae9c 100644
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -857,6 +857,11 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
}
mGIFStruct.delay_time = GETINT16(q + 1) * 10;
+
+ if (mGIFStruct.delay_time > 0) {
+ PostIsAnimated(mGIFStruct.delay_time);
+ }
+
GETN(1, gif_consume_block);
break;
@@ -921,11 +926,20 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
break;
case gif_image_header: {
- if (mGIFStruct.images_decoded > 0 && IsFirstFrameDecode()) {
- // We're about to get a second frame, but we only want the first. Stop
- // decoding now.
- mGIFStruct.state = gif_done;
- break;
+ if (mGIFStruct.images_decoded == 1) {
+ if (!HasAnimation()) {
+ // We should've already called PostIsAnimated(); this must be a
+ // corrupt animated image with a first frame timeout of zero. Signal
+ // that we're animated now, before the first-frame decode early exit
+ // below, so that RasterImage can detect that this happened.
+ PostIsAnimated(/* aFirstFrameTimeout = */ 0);
+ }
+ if (IsFirstFrameDecode()) {
+ // We're about to get a second frame, but we only want the first. Stop
+ // decoding now.
+ mGIFStruct.state = gif_done;
+ break;
+ }
}
// Get image offsets, with respect to the screen origin
diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp
index 643a09b54a2f..8d0fc6511728 100644
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -378,8 +378,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
}
if (!HasSize() && mContainedDecoder->HasSize()) {
- PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
- mContainedDecoder->GetImageMetadata().GetHeight());
+ nsIntSize size = mContainedDecoder->GetSize();
+ PostSize(size.width, size.height);
}
mPos += aCount;
@@ -479,8 +479,8 @@ nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
return;
}
- PostSize(mContainedDecoder->GetImageMetadata().GetWidth(),
- mContainedDecoder->GetImageMetadata().GetHeight());
+ nsIntSize size = mContainedDecoder->GetSize();
+ PostSize(size.width, size.height);
// We have the size. If we're doing a metadata decode, we're done.
if (IsMetadataDecode()) {
diff --git a/image/decoders/nsPNGDecoder.cpp b/image/decoders/nsPNGDecoder.cpp
index 9e15fab88b2c..742229b64dcb 100644
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -56,32 +56,33 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameInfo()
{ }
#ifdef PNG_APNG_SUPPORTED
+
+int32_t GetNextFrameDelay(png_structp aPNG, png_infop aInfo)
+{
+ // Delay, in seconds, is delayNum / delayDen.
+ png_uint_16 delayNum = png_get_next_frame_delay_num(aPNG, aInfo);
+ png_uint_16 delayDen = png_get_next_frame_delay_den(aPNG, aInfo);
+
+ if (delayNum == 0) {
+ return 0; // SetFrameTimeout() will set to a minimum.
+ }
+
+ if (delayDen == 0) {
+ delayDen = 100; // So says the APNG spec.
+ }
+
+ // Need to cast delay_num to float to have a proper division and
+ // the result to int to avoid a compiler warning.
+ return static_cast(static_cast(delayNum) * 1000 / delayDen);
+}
+
nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
: mDispose(DisposalMethod::KEEP)
, mBlend(BlendMethod::OVER)
, mTimeout(0)
{
- png_uint_16 delay_num, delay_den;
- // delay, in seconds is delay_num/delay_den
- png_byte dispose_op;
- png_byte blend_op;
- delay_num = png_get_next_frame_delay_num(aPNG, aInfo);
- delay_den = png_get_next_frame_delay_den(aPNG, aInfo);
- dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
- blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
-
- if (delay_num == 0) {
- mTimeout = 0; // SetFrameTimeout() will set to a minimum
- } else {
- if (delay_den == 0) {
- delay_den = 100; // so says the APNG spec
- }
-
- // Need to cast delay_num to float to have a proper division and
- // the result to int to avoid compiler warning
- mTimeout = static_cast(static_cast(delay_num) *
- 1000 / delay_den);
- }
+ png_byte dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
+ png_byte blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) {
mDispose = DisposalMethod::RESTORE_PREVIOUS;
@@ -96,6 +97,8 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
} else {
mBlend = BlendMethod::OVER;
}
+
+ mTimeout = GetNextFrameDelay(aPNG, aInfo);
}
#endif
@@ -595,18 +598,25 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
png_longjmp(decoder->mPNG, 1); // invalid number of channels
}
+#ifdef PNG_APNG_SUPPORTED
+ bool isAnimated = png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL);
+ if (isAnimated) {
+ decoder->PostIsAnimated(GetNextFrameDelay(png_ptr, info_ptr));
+ }
+#endif
+
if (decoder->IsMetadataDecode()) {
decoder->CheckForTransparency(decoder->format,
IntRect(0, 0, width, height));
- // We have the size and transparency information we're looking for, so we
- // don't need to decode any further.
+ // We have the metadata we're looking for, so we don't need to decode any
+ // further.
decoder->mSuccessfulEarlyFinish = true;
png_longjmp(decoder->mPNG, 1);
}
#ifdef PNG_APNG_SUPPORTED
- if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
+ if (isAnimated) {
png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
nullptr);
}
diff --git a/image/moz.build b/image/moz.build
index 866e9801498d..1a3c93464ddc 100644
--- a/image/moz.build
+++ b/image/moz.build
@@ -59,7 +59,6 @@ UNIFIED_SOURCES += [
'Image.cpp',
'ImageCacheKey.cpp',
'ImageFactory.cpp',
- 'ImageMetadata.cpp',
'ImageOps.cpp',
'ImageWrapper.cpp',
'imgFrame.cpp',
diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp
index 548dc11b135a..40946a981528 100644
--- a/image/test/gtest/Common.cpp
+++ b/image/test/gtest/Common.cpp
@@ -15,6 +15,7 @@
#include "nsIProperties.h"
#include "nsNetUtil.h"
#include "mozilla/nsRefPtr.h"
+#include "nsStreamUtils.h"
#include "nsString.h"
namespace mozilla {
@@ -71,6 +72,15 @@ LoadFile(const char* aRelativePath)
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file);
ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
+ // Ensure the resulting input stream is buffered.
+ if (!NS_InputStreamIsBuffered(inputStream)) {
+ nsCOMPtr bufStream;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
+ inputStream, 1024);
+ ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
+ inputStream = bufStream;
+ }
+
return inputStream.forget();
}
@@ -144,13 +154,14 @@ ImageTestCase GreenICOTestCase()
ImageTestCase GreenFirstFrameAnimatedGIFTestCase()
{
- return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100));
+ return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100),
+ TEST_CASE_IS_ANIMATED);
}
ImageTestCase GreenFirstFrameAnimatedPNGTestCase()
{
return ImageTestCase("first-frame-green.png", "image/png", IntSize(100, 100),
- TEST_CASE_IS_TRANSPARENT);
+ TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED);
}
ImageTestCase CorruptTestCase()
@@ -198,4 +209,13 @@ ImageTestCase RLE8BMPTestCase()
TEST_CASE_IS_TRANSPARENT);
}
+ImageTestCase NoFrameDelayGIFTestCase()
+{
+ // This is an invalid (or at least, questionably valid) GIF that's animated
+ // even though it specifies a frame delay of zero. It's animated, but it's not
+ // marked TEST_CASE_IS_ANIMATED because the metadata decoder can't detect that
+ // it's animated.
+ return ImageTestCase("no-frame-delay.gif", "image/gif", IntSize(100, 100));
+}
+
} // namespace mozilla
diff --git a/image/test/gtest/Common.h b/image/test/gtest/Common.h
index f88824bd614e..6864c09a2b32 100644
--- a/image/test/gtest/Common.h
+++ b/image/test/gtest/Common.h
@@ -21,8 +21,9 @@ enum TestCaseFlags
{
TEST_CASE_DEFAULT_FLAGS = 0,
TEST_CASE_IS_FUZZY = 1 << 0,
- TEST_CASE_IS_TRANSPARENT = 1 << 1,
- TEST_CASE_HAS_ERROR = 1 << 2
+ TEST_CASE_HAS_ERROR = 1 << 1,
+ TEST_CASE_IS_TRANSPARENT = 1 << 2,
+ TEST_CASE_IS_ANIMATED = 1 << 3,
};
struct ImageTestCase
@@ -97,6 +98,7 @@ ImageTestCase CorruptTestCase();
ImageTestCase TransparentPNGTestCase();
ImageTestCase TransparentGIFTestCase();
ImageTestCase FirstFramePaddingGIFTestCase();
+ImageTestCase NoFrameDelayGIFTestCase();
ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase();
ImageTestCase RLE4BMPTestCase();
diff --git a/image/test/gtest/TestMetadata.cpp b/image/test/gtest/TestMetadata.cpp
index 9ebe6d0bf925..a01833104e53 100644
--- a/image/test/gtest/TestMetadata.cpp
+++ b/image/test/gtest/TestMetadata.cpp
@@ -10,6 +10,7 @@
#include "decoders/nsBMPDecoder.h"
#include "imgIContainer.h"
#include "imgITools.h"
+#include "ImageFactory.h"
#include "mozilla/gfx/2D.h"
#include "nsComponentManagerUtils.h"
#include "nsCOMPtr.h"
@@ -37,26 +38,22 @@ TEST(ImageMetadata, ImageModuleAvailable)
EXPECT_TRUE(imgTools != nullptr);
}
+enum class BMPAlpha
+{
+ DISABLED,
+ ENABLED
+};
+
static void
-CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
+CheckMetadata(const ImageTestCase& aTestCase,
+ BMPAlpha aBMPAlpha = BMPAlpha::DISABLED)
{
nsCOMPtr inputStream = LoadFile(aTestCase.mPath);
ASSERT_TRUE(inputStream != nullptr);
- // Prepare the input stream.
- nsresult rv;
- if (!NS_InputStreamIsBuffered(inputStream)) {
- nsCOMPtr bufStream;
- rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
- inputStream, 1024);
- if (NS_SUCCEEDED(rv)) {
- inputStream = bufStream;
- }
- }
-
// Figure out how much data we have.
uint64_t length;
- rv = inputStream->Available(&length);
+ nsresult rv = inputStream->Available(&length);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Write the data into a SourceBuffer.
@@ -73,7 +70,7 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, sourceBuffer);
ASSERT_TRUE(decoder != nullptr);
- if (aEnableBMPAlpha) {
+ if (aBMPAlpha == BMPAlpha::ENABLED) {
static_cast(decoder.get())->SetUseAlphaData(true);
}
@@ -84,7 +81,8 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
// (which would indicate that it decoded past the header of the image).
Progress metadataProgress = decoder->TakeProgress();
EXPECT_TRUE(0 == (metadataProgress & ~(FLAG_SIZE_AVAILABLE |
- FLAG_HAS_TRANSPARENCY)));
+ FLAG_HAS_TRANSPARENCY |
+ FLAG_IS_ANIMATED)));
// If the test case is corrupt, assert what we can and return early.
if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
@@ -102,18 +100,21 @@ CheckMetadata(const ImageTestCase& aTestCase, bool aEnableBMPAlpha = false)
EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
- bool expectTransparency = aEnableBMPAlpha
+ bool expectTransparency = aBMPAlpha == BMPAlpha::ENABLED
? true
: bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
+ EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
+ bool(metadataProgress & FLAG_IS_ANIMATED));
+
// Create a full decoder, so we can compare the result.
decoder =
DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer,
imgIContainer::DECODE_FLAGS_DEFAULT);
ASSERT_TRUE(decoder != nullptr);
- if (aEnableBMPAlpha) {
+ if (aBMPAlpha == BMPAlpha::ENABLED) {
static_cast(decoder.get())->SetUseAlphaData(true);
}
@@ -164,17 +165,90 @@ TEST(ImageMetadata, FirstFramePaddingGIF)
TEST(ImageMetadata, TransparentBMPWithBMPAlphaOff)
{
- CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(),
- /* aEnableBMPAlpha = */ false);
+ CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(), BMPAlpha::ENABLED);
}
TEST(ImageMetadata, TransparentBMPWithBMPAlphaOn)
{
- CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(),
- /* aEnableBMPAlpha = */ true);
+ CheckMetadata(TransparentBMPWhenBMPAlphaEnabledTestCase(), BMPAlpha::ENABLED);
}
TEST(ImageMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
TEST(ImageMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
TEST(ImageMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
+
+TEST(ImageMetadata, NoFrameDelayGIF)
+{
+ CheckMetadata(NoFrameDelayGIFTestCase());
+}
+
+TEST(ImageMetadata, NoFrameDelayGIFFullDecode)
+{
+ ImageTestCase testCase = NoFrameDelayGIFTestCase();
+
+ // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
+ // this test case is animated, because it has a zero frame delay for the first
+ // frame. This test verifies that when we do a full decode, we detect the
+ // animation at that point and successfully decode all the frames.
+
+ // Create an image.
+ nsRefPtr image =
+ ImageFactory::CreateAnonymousImage(nsAutoCString(testCase.mMimeType));
+ ASSERT_TRUE(!image->HasError());
+
+ nsCOMPtr inputStream = LoadFile(testCase.mPath);
+ ASSERT_TRUE(inputStream != nullptr);
+
+ // Figure out how much data we have.
+ uint64_t length;
+ nsresult rv = inputStream->Available(&length);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Write the data into the image.
+ rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
+ static_cast(length));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Let the image know we've sent all the data.
+ rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsRefPtr tracker = image->GetProgressTracker();
+ tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+ // Use GetFrame() to force a sync decode of the image.
+ nsRefPtr surface =
+ image->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE);
+
+ // Ensure that the image's metadata meets our expectations.
+ IntSize imageSize(0, 0);
+ rv = image->GetWidth(&imageSize.width);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+ rv = image->GetHeight(&imageSize.height);
+ EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+ EXPECT_EQ(testCase.mSize.width, imageSize.width);
+ EXPECT_EQ(testCase.mSize.height, imageSize.height);
+
+ Progress imageProgress = tracker->GetProgress();
+
+ EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
+ EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
+
+ // Ensure that we decoded both frames of the image.
+ LookupResult firstFrameLookupResult =
+ SurfaceCache::Lookup(ImageKey(image.get()),
+ RasterSurfaceKey(imageSize,
+ imgIContainer::DECODE_FLAGS_DEFAULT,
+ /* aFrameNum = */ 0));
+ EXPECT_EQ(MatchType::EXACT, firstFrameLookupResult.Type());
+
+ LookupResult secondFrameLookupResult =
+ SurfaceCache::Lookup(ImageKey(image.get()),
+ RasterSurfaceKey(imageSize,
+ imgIContainer::DECODE_FLAGS_DEFAULT,
+ /* aFrameNum = */ 1));
+ EXPECT_EQ(MatchType::EXACT, secondFrameLookupResult.Type());
+}
diff --git a/image/test/gtest/moz.build b/image/test/gtest/moz.build
index edb595faf44c..45f9f96197fb 100644
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -24,6 +24,7 @@ TEST_HARNESS_FILES.gtest += [
'green.ico',
'green.jpg',
'green.png',
+ 'no-frame-delay.gif',
'rle4.bmp',
'rle8.bmp',
'transparent.bmp',
diff --git a/image/test/gtest/no-frame-delay.gif b/image/test/gtest/no-frame-delay.gif
new file mode 100644
index 000000000000..1c50b67431fb
Binary files /dev/null and b/image/test/gtest/no-frame-delay.gif differ
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
index 32d9280b17cd..5ccb45ac4ff0 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -361,23 +361,6 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-/* ES5 15.2.4.3. */
-static bool
-obj_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
-{
- JS_CHECK_RECURSION(cx, return false);
-
- CallArgs args = CallArgsFromVp(argc, vp);
-
- /* Step 1. */
- RootedObject obj(cx, ToObject(cx, args.thisv()));
- if (!obj)
- return false;
-
- /* Steps 2-4. */
- RootedId id(cx, NameToId(cx->names().toString));
- return obj->callMethod(cx, id, 0, nullptr, args.rval());
-}
bool
js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
@@ -999,7 +982,7 @@ static const JSFunctionSpec object_methods[] = {
JS_FN(js_toSource_str, obj_toSource, 0,0),
#endif
JS_FN(js_toString_str, obj_toString, 0,0),
- JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
+ JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0,JSPROP_DEFINE_LATE),
JS_FN(js_valueOf_str, obj_valueOf, 0,0),
#if JS_HAS_OBJ_WATCHPOINT
JS_FN(js_watch_str, obj_watch, 2,0),
diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js
index f2316b89c665..f4957ec46e9c 100644
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -51,6 +51,15 @@ function ObjectIsExtensible(obj) {
return IsObject(obj) && std_Reflect_isExtensible(obj);
}
+/* ES2015 19.1.3.5 Object.prototype.toLocaleString */
+function Object_toLocaleString() {
+ // Step 1.
+ var O = this;
+
+ // Step 2.
+ return O.toString();
+}
+
function ObjectDefineSetter(name, setter) {
var object;
if (this === null || this === undefined)
diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp
index 6584662ed3f6..c336b957f773 100644
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1922,7 +1922,7 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
uint32_t index;
if (IdIsIndex(id, &index)) {
if (!receiver.isObject() || obj != &receiver.toObject())
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
if (index >= uint32_t(typedObj->length())) {
JS_ReportErrorNumber(cx, GetErrorMessage,
@@ -1948,7 +1948,7 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
break;
if (!receiver.isObject() || obj != &receiver.toObject())
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
size_t offset = descr->fieldOffset(fieldIndex);
Rooted fieldType(cx, &descr->fieldDescr(fieldIndex));
diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
index bb049f44dd4b..86e917cad62e 100644
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -463,90 +463,6 @@ FoldType(ExclusiveContext* cx, ParseNode* pn, ParseNodeKind kind)
return true;
}
-/*
- * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
- * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
- * a successful call to this function.
- */
-static bool
-FoldBinaryNumeric(ExclusiveContext* cx, JSOp op, ParseNode* pn1, ParseNode* pn2,
- ParseNode* pn)
-{
- double d, d2;
- int32_t i, j;
-
- MOZ_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
- d = pn1->pn_dval;
- d2 = pn2->pn_dval;
- switch (op) {
- case JSOP_LSH:
- case JSOP_RSH:
- i = ToInt32(d);
- j = ToInt32(d2);
- j &= 31;
- d = int32_t((op == JSOP_LSH) ? uint32_t(i) << j : i >> j);
- break;
-
- case JSOP_URSH:
- j = ToInt32(d2);
- j &= 31;
- d = ToUint32(d) >> j;
- break;
-
- case JSOP_ADD:
- d += d2;
- break;
-
- case JSOP_SUB:
- d -= d2;
- break;
-
- case JSOP_MUL:
- d *= d2;
- break;
-
- case JSOP_DIV:
- if (d2 == 0) {
-#if defined(XP_WIN)
- /* XXX MSVC miscompiles such that (NaN == 0) */
- if (IsNaN(d2))
- d = GenericNaN();
- else
-#endif
- if (d == 0 || IsNaN(d))
- d = GenericNaN();
- else if (IsNegative(d) != IsNegative(d2))
- d = NegativeInfinity();
- else
- d = PositiveInfinity();
- } else {
- d /= d2;
- }
- break;
-
- case JSOP_MOD:
- if (d2 == 0) {
- d = GenericNaN();
- } else {
- d = js_fmod(d, d2);
- }
- break;
-
- case JSOP_POW:
- d = ecmaPow(d, d2);
- break;
-
- default:;
- }
-
- /* Take care to allow pn1 or pn2 to alias pn. */
- pn->setKind(PNK_NUMBER);
- pn->setOp(JSOP_DOUBLE);
- pn->setArity(PN_NULLARY);
- pn->pn_dval = d;
- return true;
-}
-
// Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode,
// *pn, in its place.
//
@@ -561,6 +477,19 @@ ReplaceNode(ParseNode** pnp, ParseNode* pn)
*pnp = pn;
}
+static bool
+IsEffectless(ParseNode* node)
+{
+ return node->isKind(PNK_TRUE) ||
+ node->isKind(PNK_FALSE) ||
+ node->isKind(PNK_STRING) ||
+ node->isKind(PNK_TEMPLATE_STRING) ||
+ node->isKind(PNK_NUMBER) ||
+ node->isKind(PNK_NULL) ||
+ node->isKind(PNK_FUNCTION) ||
+ node->isKind(PNK_GENEXP);
+}
+
enum Truthiness { Truthy, Falsy, Unknown };
static Truthiness
@@ -571,6 +500,7 @@ Boolish(ParseNode* pn)
return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy;
case PNK_STRING:
+ case PNK_TEMPLATE_STRING:
return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
case PNK_TRUE:
@@ -582,36 +512,58 @@ Boolish(ParseNode* pn)
case PNK_NULL:
return Falsy;
+ case PNK_VOID: {
+ // |void | evaluates to |undefined| which isn't truthy. But the
+ // sense of this method requires that the expression be literally
+ // replaceable with true/false: not the case if the nested expression
+ // is effectful, might throw, &c. Walk past the |void| (and nested
+ // |void| expressions, for good measure) and check that the nested
+ // expression doesn't break this requirement before indicating falsity.
+ do {
+ pn = pn->pn_kid;
+ } while (pn->isKind(PNK_VOID));
+
+ return IsEffectless(pn) ? Falsy : Unknown;
+ }
+
default:
return Unknown;
}
}
-// Expressions that appear in a few specific places are treated specially
-// during constant folding. This enum tells where a parse node appears.
-enum class SyntacticContext : int {
- // pn is an expression, and it appears in a context where only its side
- // effects and truthiness matter: the condition of an if statement,
- // conditional expression, while loop, or for(;;) loop; or an operand of &&
- // or || in such a context.
- Condition,
-
- // pn is the operand of the 'delete' keyword.
- Delete,
-
- // Any other syntactic context.
- Other
-};
-
-static SyntacticContext
-condIf(const ParseNode* pn, ParseNodeKind kind)
-{
- return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other;
-}
+static bool
+Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda);
static bool
-Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda,
- SyntacticContext sc);
+FoldCondition(ExclusiveContext* cx, ParseNode** nodePtr, Parser& parser,
+ bool inGenexpLambda)
+{
+ // Conditions fold like any other expression...
+ if (!Fold(cx, nodePtr, parser, inGenexpLambda))
+ return false;
+
+ // ...but then they sometimes can be further folded to constants.
+ ParseNode* node = *nodePtr;
+ Truthiness t = Boolish(node);
+ if (t != Unknown) {
+ // We can turn function nodes into constant nodes here, but mutating
+ // function nodes is tricky --- in particular, mutating a function node
+ // that appears on a method list corrupts the method list. However,
+ // methods are M's in statements of the form 'this.foo = M;', which we
+ // never fold, so we're okay.
+ parser.prepareNodeForMutation(node);
+ if (t == Truthy) {
+ node->setKind(PNK_TRUE);
+ node->setOp(JSOP_TRUE);
+ } else {
+ node->setKind(PNK_FALSE);
+ node->setOp(JSOP_FALSE);
+ }
+ node->setArity(PN_NULLARY);
+ }
+
+ return true;
+}
static bool
FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser& parser,
@@ -621,7 +573,7 @@ FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser&
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
// Constant-fold the entire |typeof| if given a constant with known type.
@@ -649,36 +601,6 @@ FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser&
return true;
}
-static bool
-FoldVoid(ExclusiveContext* cx, ParseNode* node, Parser& parser,
- bool inGenexpLambda, SyntacticContext sc)
-{
- MOZ_ASSERT(node->isKind(PNK_VOID));
- MOZ_ASSERT(node->isArity(PN_UNARY));
-
- ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
-
- if (sc == SyntacticContext::Condition) {
- if (expr->isKind(PNK_TRUE) ||
- expr->isKind(PNK_FALSE) ||
- expr->isKind(PNK_STRING) ||
- expr->isKind(PNK_TEMPLATE_STRING) ||
- expr->isKind(PNK_NUMBER) ||
- expr->isKind(PNK_NULL) ||
- expr->isKind(PNK_FUNCTION))
- {
- parser.prepareNodeForMutation(node);
- node->setKind(PNK_FALSE);
- node->setArity(PN_NULLARY);
- node->setOp(JSOP_FALSE);
- }
- }
-
- return true;
-}
-
static bool
FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, Parser& parser,
bool inGenexpLambda)
@@ -687,19 +609,12 @@ FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, Parser&
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
- // Expression deletion evaluates the expression, then evaluates to
- // true. For trivial expressions, eliminate the expression evaluation.
- if (expr->isKind(PNK_TRUE) ||
- expr->isKind(PNK_FALSE) ||
- expr->isKind(PNK_STRING) ||
- expr->isKind(PNK_TEMPLATE_STRING) ||
- expr->isKind(PNK_NUMBER) ||
- expr->isKind(PNK_NULL) ||
- expr->isKind(PNK_FUNCTION))
- {
+ // Expression deletion evaluates the expression, then evaluates to true.
+ // For effectless expressions, eliminate the expression evaluation.
+ if (IsEffectless(expr)) {
parser.prepareNodeForMutation(node);
node->setKind(PNK_TRUE);
node->setArity(PN_NULLARY);
@@ -718,7 +633,7 @@ FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parserpn_kid->isKind(PNK_ELEM) || node->pn_kid->isKind(PNK_SUPERELEM));
ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
// If we're deleting an element, but constant-folding converted our
@@ -749,7 +664,7 @@ FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, ParsergetKind();
#endif
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
MOZ_ASSERT(expr->isKind(oldKind),
@@ -766,7 +681,7 @@ FoldNot(ExclusiveContext* cx, ParseNode* node, Parser& parser,
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Condition))
+ if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
return false;
if (expr->isKind(PNK_NUMBER)) {
@@ -802,7 +717,7 @@ FoldUnaryArithmetic(ExclusiveContext* cx, ParseNode* node, ParserisArity(PN_UNARY));
ParseNode*& expr = node->pn_kid;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
if (expr->isKind(PNK_NUMBER) || expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
@@ -840,7 +755,7 @@ FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parserpn_kid;
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser::PermitAssignmentToFunctionCalls));
- if (!Fold(cx, &target, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &target, parser, inGenexpLambda))
return false;
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser::PermitAssignmentToFunctionCalls));
@@ -850,7 +765,7 @@ FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parser& parser,
- bool inGenexpLambda, SyntacticContext sc)
+ bool inGenexpLambda)
{
ParseNode* node = *nodePtr;
@@ -860,8 +775,7 @@ FoldAndOr(ExclusiveContext* cx, ParseNode** nodePtr, Parser& p
bool isOrNode = node->isKind(PNK_OR);
ParseNode** elem = &node->pn_head;
do {
- // Pass |sc| through to propagate conditionality.
- if (!Fold(cx, elem, parser, inGenexpLambda, sc))
+ if (!Fold(cx, elem, parser, inGenexpLambda))
return false;
Truthiness t = Boolish(*elem);
@@ -950,11 +864,11 @@ FoldConditional(ExclusiveContext* cx, ParseNode** nodePtr, ParserisArity(PN_TERNARY));
ParseNode*& expr = node->pn_kid1;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Condition))
+ if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
return false;
ParseNode*& ifTruthy = node->pn_kid2;
- if (!Fold(cx, &ifTruthy, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &ifTruthy, parser, inGenexpLambda))
return false;
ParseNode*& ifFalsy = node->pn_kid3;
@@ -969,7 +883,7 @@ FoldConditional(ExclusiveContext* cx, ParseNode** nodePtr, ParserisKind(PNK_CONDITIONAL)) {
nextNode = &ifFalsy;
} else {
- if (!Fold(cx, &ifFalsy, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &ifFalsy, parser, inGenexpLambda))
return false;
}
@@ -1041,11 +955,11 @@ FoldIf(ExclusiveContext* cx, ParseNode** nodePtr, Parser& pars
MOZ_ASSERT(node->isArity(PN_TERNARY));
ParseNode*& expr = node->pn_kid1;
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Condition))
+ if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
return false;
ParseNode*& consequent = node->pn_kid2;
- if (!Fold(cx, &consequent, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &consequent, parser, inGenexpLambda))
return false;
ParseNode*& alternative = node->pn_kid3;
@@ -1058,11 +972,8 @@ FoldIf(ExclusiveContext* cx, ParseNode** nodePtr, Parser& pars
if (alternative->isKind(PNK_IF)) {
nextNode = &alternative;
} else {
- if (!Fold(cx, &alternative, parser, inGenexpLambda,
- SyntacticContext::Other))
- {
+ if (!Fold(cx, &alternative, parser, inGenexpLambda))
return false;
- }
}
}
@@ -1154,16 +1065,55 @@ FoldFunction(ExclusiveContext* cx, ParseNode* node, Parser& pa
// Note: pn_body is null for lazily-parsed functions.
if (ParseNode*& functionBody = node->pn_body) {
- if (!Fold(cx, &functionBody, parser, node->pn_funbox->inGenexpLambda,
- SyntacticContext::Other))
- {
+ if (!Fold(cx, &functionBody, parser, node->pn_funbox->inGenexpLambda))
return false;
- }
}
return true;
}
+static double
+ComputeBinary(ParseNodeKind kind, double left, double right)
+{
+ if (kind == PNK_ADD)
+ return left + right;
+
+ if (kind == PNK_SUB)
+ return left - right;
+
+ if (kind == PNK_STAR)
+ return left * right;
+
+ if (kind == PNK_MOD)
+ return right == 0 ? GenericNaN() : js_fmod(left, right);
+
+ if (kind == PNK_URSH)
+ return ToUint32(left) >> (ToUint32(right) & 31);
+
+ if (kind == PNK_DIV) {
+ if (right == 0) {
+#if defined(XP_WIN)
+ /* XXX MSVC miscompiles such that (NaN == 0) */
+ if (IsNaN(right))
+ return GenericNaN();
+#endif
+ if (left == 0 || IsNaN(left))
+ return GenericNaN();
+ if (IsNegative(left) != IsNegative(right))
+ return NegativeInfinity();
+ return PositiveInfinity();
+ }
+
+ return left / right;
+ }
+
+ MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
+
+ int32_t i = ToInt32(left);
+ uint32_t j = ToUint32(right) & 31;
+ return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
+}
+
static bool
FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser& parser,
bool inGenexpLambda)
@@ -1181,7 +1131,7 @@ FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parserpn_head;
for (; *listp; listp = &(*listp)->pn_next) {
- if (!Fold(cx, listp, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, listp, parser, inGenexpLambda))
return false;
if (!FoldType(cx, *listp, PNK_NUMBER))
@@ -1196,22 +1146,26 @@ FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, ParsergetOp();
ParseNode* elem = node->pn_head;
ParseNode* next = elem->pn_next;
if (elem->isKind(PNK_NUMBER)) {
+ ParseNodeKind kind = node->getKind();
while (true) {
if (!next || !next->isKind(PNK_NUMBER))
break;
- ParseNode* afterNext = next->pn_next;
- if (!FoldBinaryNumeric(cx, op, elem, next, elem))
- return false;
+ double d = ComputeBinary(kind, elem->pn_dval, next->pn_dval);
+ ParseNode* afterNext = next->pn_next;
parser.freeTree(next);
next = afterNext;
elem->pn_next = next;
+ elem->setKind(PNK_NUMBER);
+ elem->setOp(JSOP_DOUBLE);
+ elem->setArity(PN_NULLARY);
+ elem->pn_dval = d;
+
node->pn_count--;
}
@@ -1243,7 +1197,7 @@ FoldExponentiation(ExclusiveContext* cx, ParseNode* node, Parserpn_head;
for (; *listp; listp = &(*listp)->pn_next) {
- if (!Fold(cx, listp, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, listp, parser, inGenexpLambda))
return false;
if (!FoldType(cx, *listp, PNK_NUMBER))
@@ -1284,7 +1238,7 @@ FoldList(ExclusiveContext* cx, ParseNode* list, Parser& parser
ParseNode** elem = &list->pn_head;
for (; *elem; elem = &(*elem)->pn_next) {
- if (!Fold(cx, elem, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, elem, parser, inGenexpLambda))
return false;
}
@@ -1304,7 +1258,7 @@ FoldReturn(ExclusiveContext* cx, ParseNode* node, Parser& pars
MOZ_ASSERT(node->isArity(PN_BINARY));
if (ParseNode*& expr = node->pn_left) {
- if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
return false;
}
@@ -1327,16 +1281,16 @@ FoldTry(ExclusiveContext* cx, ParseNode* node, Parser& parser,
MOZ_ASSERT(node->isArity(PN_TERNARY));
ParseNode*& statements = node->pn_kid1;
- if (!Fold(cx, &statements, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &statements, parser, inGenexpLambda))
return false;
if (ParseNode*& catchList = node->pn_kid2) {
- if (!Fold(cx, &catchList, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &catchList, parser, inGenexpLambda))
return false;
}
if (ParseNode*& finally = node->pn_kid3) {
- if (!Fold(cx, &finally, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &finally, parser, inGenexpLambda))
return false;
}
@@ -1351,16 +1305,16 @@ FoldCatch(ExclusiveContext* cx, ParseNode* node, Parser& parse
MOZ_ASSERT(node->isArity(PN_TERNARY));
ParseNode*& declPattern = node->pn_kid1;
- if (!Fold(cx, &declPattern, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &declPattern, parser, inGenexpLambda))
return false;
if (ParseNode*& cond = node->pn_kid2) {
- if (!Fold(cx, &cond, parser, inGenexpLambda, SyntacticContext::Condition))
+ if (!FoldCondition(cx, &cond, parser, inGenexpLambda))
return false;
}
if (ParseNode*& statements = node->pn_kid3) {
- if (!Fold(cx, &statements, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &statements, parser, inGenexpLambda))
return false;
}
@@ -1375,28 +1329,369 @@ FoldClass(ExclusiveContext* cx, ParseNode* node, Parser& parse
MOZ_ASSERT(node->isArity(PN_TERNARY));
if (ParseNode*& classNames = node->pn_kid1) {
- if (!Fold(cx, &classNames, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &classNames, parser, inGenexpLambda))
return false;
}
if (ParseNode*& heritage = node->pn_kid2) {
- if (!Fold(cx, &heritage, parser, inGenexpLambda, SyntacticContext::Other))
+ if (!Fold(cx, &heritage, parser, inGenexpLambda))
return false;
}
ParseNode*& body = node->pn_kid3;
- return Fold(cx, &body, parser, inGenexpLambda, SyntacticContext::Other);
+ return Fold(cx, &body, parser, inGenexpLambda);
+}
+
+static bool
+FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser& parser,
+ bool inGenexpLambda)
+{
+ ParseNode* node = *nodePtr;
+
+ MOZ_ASSERT(node->isKind(PNK_ELEM));
+ MOZ_ASSERT(node->isArity(PN_BINARY));
+
+ ParseNode*& expr = node->pn_left;
+ if (!Fold(cx, &expr, parser, inGenexpLambda))
+ return false;
+
+ ParseNode*& key = node->pn_right;
+ if (!Fold(cx, &key, parser, inGenexpLambda))
+ return false;
+
+ PropertyName* name = nullptr;
+ if (key->isKind(PNK_STRING)) {
+ JSAtom* atom = key->pn_atom;
+ uint32_t index;
+
+ if (atom->isIndex(&index)) {
+ // Optimization 1: We have something like expr["100"]. This is
+ // equivalent to expr[100] which is faster.
+ key->setKind(PNK_NUMBER);
+ key->setOp(JSOP_DOUBLE);
+ key->pn_dval = index;
+ } else {
+ name = atom->asPropertyName();
+ }
+ } else if (key->isKind(PNK_NUMBER)) {
+ double number = key->pn_dval;
+ if (number != ToUint32(number)) {
+ // Optimization 2: We have something like expr[3.14]. The number
+ // isn't an array index, so it converts to a string ("3.14"),
+ // enabling optimization 3 below.
+ JSAtom* atom = ToAtom(cx, DoubleValue(number));
+ if (!atom)
+ return false;
+ name = atom->asPropertyName();
+ }
+ }
+
+ // If we don't have a name, we can't optimize to getprop.
+ if (!name)
+ return true;
+
+ // Also don't optimize if the name doesn't map directly to its id for TI's
+ // purposes.
+ if (NameToId(name) != IdToTypeId(NameToId(name)))
+ return true;
+
+ // Optimization 3: We have expr["foo"] where foo is not an index. Convert
+ // to a property access (like expr.foo) that optimizes better downstream.
+ // Don't bother with this for names that TI considers to be indexes, to
+ // simplify downstream analysis.
+ ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
+ if (!dottedAccess)
+ return false;
+ dottedAccess->setInParens(node->isInParens());
+ ReplaceNode(nodePtr, dottedAccess);
+
+ // If we've replaced |expr["prop"]| with |expr.prop|, we can now free the
+ // |"prop"| and |expr["prop"]| nodes -- but not the |expr| node that we're
+ // now using as a sub-node of |dottedAccess|. Munge |expr["prop"]| into a
+ // node with |"prop"| as its only child, that'll pass AST sanity-checking
+ // assertions during freeing, then free it.
+ node->setKind(PNK_TYPEOFEXPR);
+ node->setArity(PN_UNARY);
+ node->pn_kid = key;
+ parser.freeTree(node);
+
+ return true;
+}
+
+static bool
+FoldAdd(ExclusiveContext* cx, ParseNode** nodePtr, Parser& parser,
+ bool inGenexpLambda)
+{
+ ParseNode* node = *nodePtr;
+
+ MOZ_ASSERT(node->isKind(PNK_ADD));
+ MOZ_ASSERT(node->isArity(PN_LIST));
+ MOZ_ASSERT(node->pn_count >= 2);
+
+ // Generically fold all operands first.
+ if (!FoldList(cx, node, parser, inGenexpLambda))
+ return false;
+
+ // Fold leading numeric operands together:
+ //
+ // (1 + 2 + x) becomes (3 + x)
+ //
+ // Don't go past the leading operands: additions after a string are
+ // string concatenations, not additions: ("1" + 2 + 3 === "123").
+ ParseNode* current = node->pn_head;
+ ParseNode* next = current->pn_next;
+ if (current->isKind(PNK_NUMBER)) {
+ do {
+ if (!next->isKind(PNK_NUMBER))
+ break;
+
+ current->pn_dval += next->pn_dval;
+ current->pn_next = next->pn_next;
+ parser.freeTree(next);
+ next = current->pn_next;
+
+ MOZ_ASSERT(node->pn_count > 1);
+ node->pn_count--;
+ } while (next);
+ }
+
+ // If any operands remain, attempt string concatenation folding.
+ do {
+ // If no operands remain, we're done.
+ if (!next)
+ break;
+
+ // (number + string) is string concatenation *only* at the start of
+ // the list: (x + 1 + "2" !== x + "12") when x is a number.
+ if (current->isKind(PNK_NUMBER) && next->isKind(PNK_STRING)) {
+ if (!FoldType(cx, current, PNK_STRING))
+ return false;
+ next = current->pn_next;
+ }
+
+ // The first string forces all subsequent additions to be
+ // string concatenations.
+ do {
+ if (current->isKind(PNK_STRING))
+ break;
+
+ current = next;
+ next = next->pn_next;
+ } while (next);
+
+ // If there's nothing left to fold, we're done.
+ if (!next)
+ break;
+
+ RootedString combination(cx);
+ RootedString tmp(cx);
+ do {
+ // Create a rope of the current string and all succeeding
+ // constants that we can convert to strings, then atomize it
+ // and replace them all with that fresh string.
+ MOZ_ASSERT(current->isKind(PNK_STRING));
+
+ combination = current->pn_atom;
+
+ do {
+ // Try folding the next operand to a string.
+ if (!FoldType(cx, next, PNK_STRING))
+ return false;
+
+ // Stop glomming once folding doesn't produce a string.
+ if (!next->isKind(PNK_STRING))
+ break;
+
+ // Add this string to the combination and remove the node.
+ tmp = next->pn_atom;
+ combination = ConcatStrings(cx, combination, tmp);
+ if (!combination)
+ return false;
+
+ current->pn_next = next->pn_next;
+ parser.freeTree(next);
+ next = current->pn_next;
+
+ MOZ_ASSERT(node->pn_count > 1);
+ node->pn_count--;
+ } while (next);
+
+ // Replace |current|'s string with the entire combination.
+ MOZ_ASSERT(current->isKind(PNK_STRING));
+ combination = AtomizeString(cx, combination);
+ if (!combination)
+ return false;
+ current->pn_atom = &combination->asAtom();
+
+
+ // If we're out of nodes, we're done.
+ if (!next)
+ break;
+
+ current = next;
+ next = current->pn_next;
+
+ // If we're out of nodes *after* the non-foldable-to-string
+ // node, we're done.
+ if (!next)
+ break;
+
+ // Otherwise find the next node foldable to a string, and loop.
+ do {
+ current = next;
+ next = current->pn_next;
+
+ if (!FoldType(cx, current, PNK_STRING))
+ return false;
+ next = current->pn_next;
+ } while (!current->isKind(PNK_STRING) && next);
+ } while (next);
+ } while (false);
+
+ MOZ_ASSERT(!next, "must have considered all nodes here");
+ MOZ_ASSERT(!current->pn_next, "current node must be the last node");
+
+ node->pn_tail = ¤t->pn_next;
+ node->checkListConsistency();
+
+ if (node->pn_count == 1) {
+ // We reduced the list to a constant. Replace the PNK_ADD node
+ // with that constant.
+ ReplaceNode(nodePtr, current);
+
+ // Free the old node to aggressively verify nothing uses it.
+ node->setKind(PNK_TRUE);
+ node->setArity(PN_NULLARY);
+ node->setOp(JSOP_TRUE);
+ parser.freeTree(node);
+ }
+
+ return true;
+}
+
+static bool
+FoldCall(ExclusiveContext* cx, ParseNode* node, Parser& parser,
+ bool inGenexpLambda)
+{
+ MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_TAGGED_TEMPLATE));
+ MOZ_ASSERT(node->isArity(PN_LIST));
+
+ // Don't fold a parenthesized callable component in an invocation, as this
+ // might cause a different |this| value to be used, changing semantics:
+ //
+ // var prop = "global";
+ // var obj = { prop: "obj", f: function() { return this.prop; } };
+ // assertEq((true ? obj.f : null)(), "global");
+ // assertEq(obj.f(), "obj");
+ // assertEq((true ? obj.f : null)``, "global");
+ // assertEq(obj.f``, "obj");
+ //
+ // See bug 537673 and bug 1182373.
+ ParseNode** listp = &node->pn_head;
+ if ((*listp)->isInParens())
+ listp = &(*listp)->pn_next;
+
+ for (; *listp; listp = &(*listp)->pn_next) {
+ if (!Fold(cx, listp, parser, inGenexpLambda))
+ return false;
+ }
+
+ // If the last node in the list was replaced, pn_tail points into the wrong node.
+ node->pn_tail = listp;
+
+ node->checkListConsistency();
+ return true;
+}
+
+static bool
+FoldForInOrOf(ExclusiveContext* cx, ParseNode* node, Parser& parser,
+ bool inGenexpLambda)
+{
+ MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
+ MOZ_ASSERT(node->isArity(PN_TERNARY));
+
+ if (ParseNode*& decl = node->pn_kid1) {
+ if (!Fold(cx, &decl, parser, inGenexpLambda))
+ return false;
+ }
+
+ return Fold(cx, &node->pn_kid2, parser, inGenexpLambda) &&
+ Fold(cx, &node->pn_kid3, parser, inGenexpLambda);
+}
+
+static bool
+FoldForHead(ExclusiveContext* cx, ParseNode* node, Parser& parser,
+ bool inGenexpLambda)
+{
+ MOZ_ASSERT(node->isKind(PNK_FORHEAD));
+ MOZ_ASSERT(node->isArity(PN_TERNARY));
+
+ if (ParseNode*& init = node->pn_kid1) {
+ if (!Fold(cx, &init, parser, inGenexpLambda))
+ return false;
+ }
+
+ if (ParseNode*& test = node->pn_kid2) {
+ if (!FoldCondition(cx, &test, parser, inGenexpLambda))
+ return false;
+
+ if (test->isKind(PNK_TRUE)) {
+ parser.freeTree(test);
+ test = nullptr;
+ }
+ }
+
+ if (ParseNode*& update = node->pn_kid3) {
+ if (!Fold(cx, &update, parser, inGenexpLambda))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+FoldDottedProperty(ExclusiveContext* cx, ParseNode* node, Parser& parser,
+ bool inGenexpLambda)
+{
+ MOZ_ASSERT(node->isKind(PNK_DOT));
+ MOZ_ASSERT(node->isArity(PN_NAME));
+
+ // Iterate through a long chain of dotted property accesses to find the
+ // most-nested non-dotted property node, then fold that.
+ ParseNode** nested = &node->pn_expr;
+ while ((*nested)->isKind(PNK_DOT)) {
+ MOZ_ASSERT((*nested)->isArity(PN_NAME));
+ nested = &(*nested)->pn_expr;
+ }
+
+ return Fold(cx, nested, parser, inGenexpLambda);
+}
+
+static bool
+FoldName(ExclusiveContext* cx, ParseNode* node, Parser& parser,
+ bool inGenexpLambda)
+{
+ MOZ_ASSERT(node->isKind(PNK_NAME));
+ MOZ_ASSERT(node->isArity(PN_NAME));
+
+ // Name nodes that are used, are in use-definition lists. Such nodes store
+ // name analysis information and contain nothing foldable.
+ if (node->isUsed())
+ return true;
+
+ // Other names might have a foldable expression in pn_expr.
+ if (!node->pn_expr)
+ return true;
+
+ return Fold(cx, &node->pn_expr, parser, inGenexpLambda);
}
bool
-Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda,
- SyntacticContext sc)
+Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda)
{
JS_CHECK_RECURSION(cx, return false);
ParseNode* pn = *pnp;
- ParseNode* pn1 = nullptr;
- ParseNode* pn2 = nullptr;
switch (pn->getKind()) {
case PNK_NEWTARGET:
@@ -1419,7 +1714,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_SUPERPROP:
case PNK_FRESHENBLOCK:
MOZ_ASSERT(pn->isArity(PN_NULLARY));
- goto afterFolding;
+ return true;
case PNK_TYPEOFNAME:
MOZ_ASSERT(pn->isArity(PN_UNARY));
@@ -1430,9 +1725,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_TYPEOFEXPR:
return FoldTypeOfExpr(cx, pn, parser, inGenexpLambda);
- case PNK_VOID:
- return FoldVoid(cx, pn, parser, inGenexpLambda, sc);
-
case PNK_DELETENAME: {
MOZ_ASSERT(pn->isArity(PN_UNARY));
MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
@@ -1476,18 +1768,21 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_COMPUTED_NAME:
case PNK_SPREAD:
case PNK_SUPERELEM:
+ case PNK_EXPORT:
+ case PNK_EXPORT_DEFAULT:
+ case PNK_VOID:
MOZ_ASSERT(pn->isArity(PN_UNARY));
- return Fold(cx, &pn->pn_kid, parser, inGenexpLambda, SyntacticContext::Other);
+ return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
case PNK_SEMI:
MOZ_ASSERT(pn->isArity(PN_UNARY));
if (ParseNode*& expr = pn->pn_kid)
- return Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other);
+ return Fold(cx, &expr, parser, inGenexpLambda);
return true;
case PNK_AND:
case PNK_OR:
- return FoldAndOr(cx, pnp, parser, inGenexpLambda, sc);
+ return FoldAndOr(cx, pnp, parser, inGenexpLambda);
case PNK_FUNCTION:
return FoldFunction(cx, pn, parser, inGenexpLambda);
@@ -1520,6 +1815,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_INSTANCEOF:
case PNK_IN:
case PNK_COMMA:
+ case PNK_NEW:
case PNK_ARRAY:
case PNK_OBJECT:
case PNK_ARRAYCOMP:
@@ -1535,13 +1831,14 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_CALLSITEOBJ:
case PNK_EXPORT_SPEC_LIST:
case PNK_IMPORT_SPEC_LIST:
+ case PNK_GENEXP:
return FoldList(cx, pn, parser, inGenexpLambda);
case PNK_YIELD_STAR:
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
MOZ_ASSERT(!pn->pn_right->isAssigned());
- return Fold(cx, &pn->pn_left, parser, inGenexpLambda, SyntacticContext::Other);
+ return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_YIELD:
MOZ_ASSERT(pn->isArity(PN_BINARY));
@@ -1551,7 +1848,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
if (!pn->pn_left)
return true;
- return Fold(cx, &pn->pn_left, parser, inGenexpLambda, SyntacticContext::Other);
+ return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_RETURN:
return FoldReturn(cx, pn, parser, inGenexpLambda);
@@ -1565,369 +1862,102 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo
case PNK_CLASS:
return FoldClass(cx, pn, parser, inGenexpLambda);
- case PNK_EXPORT:
+ case PNK_ELEM:
+ return FoldElement(cx, pnp, parser, inGenexpLambda);
+
+ case PNK_ADD:
+ return FoldAdd(cx, pnp, parser, inGenexpLambda);
+
+ case PNK_CALL:
+ case PNK_TAGGED_TEMPLATE:
+ return FoldCall(cx, pn, parser, inGenexpLambda);
+
+ case PNK_SWITCH:
+ case PNK_CASE:
+ case PNK_COLON:
case PNK_ASSIGN:
case PNK_ADDASSIGN:
case PNK_SUBASSIGN:
case PNK_BITORASSIGN:
- case PNK_BITXORASSIGN:
case PNK_BITANDASSIGN:
+ case PNK_BITXORASSIGN:
case PNK_LSHASSIGN:
case PNK_RSHASSIGN:
case PNK_URSHASSIGN:
- case PNK_MULASSIGN:
case PNK_DIVASSIGN:
case PNK_MODASSIGN:
+ case PNK_MULASSIGN:
case PNK_POWASSIGN:
- case PNK_ELEM:
- case PNK_COLON:
- case PNK_CASE:
+ case PNK_IMPORT:
+ case PNK_EXPORT_FROM:
case PNK_SHORTHAND:
- case PNK_DOWHILE:
- case PNK_WHILE:
- case PNK_SWITCH:
case PNK_LETBLOCK:
case PNK_FOR:
case PNK_CLASSMETHOD:
- case PNK_WITH:
+ case PNK_IMPORT_SPEC:
+ case PNK_EXPORT_SPEC:
+ MOZ_ASSERT(pn->isArity(PN_BINARY));
+ return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
+ Fold(cx, &pn->pn_right, parser, inGenexpLambda);
+
case PNK_CLASSNAMES:
+ MOZ_ASSERT(pn->isArity(PN_BINARY));
+ if (ParseNode*& outerBinding = pn->pn_left) {
+ if (!Fold(cx, &outerBinding, parser, inGenexpLambda))
+ return false;
+ }
+ return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
+
+ case PNK_DOWHILE:
+ MOZ_ASSERT(pn->isArity(PN_BINARY));
+ return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
+ FoldCondition(cx, &pn->pn_right, parser, inGenexpLambda);
+
+ case PNK_WHILE:
+ MOZ_ASSERT(pn->isArity(PN_BINARY));
+ return FoldCondition(cx, &pn->pn_left, parser, inGenexpLambda) &&
+ Fold(cx, &pn->pn_right, parser, inGenexpLambda);
+
case PNK_DEFAULT:
- case PNK_IMPORT:
- case PNK_EXPORT_FROM:
- case PNK_EXPORT_DEFAULT:
+ MOZ_ASSERT(pn->isArity(PN_BINARY));
+ MOZ_ASSERT(!pn->pn_left);
+ MOZ_ASSERT(pn->pn_right->isKind(PNK_STATEMENTLIST));
+ return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
+
+ case PNK_WITH:
+ MOZ_ASSERT(pn->isArity(PN_BINARY_OBJ));
+ return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
+ Fold(cx, &pn->pn_right, parser, inGenexpLambda);
+
case PNK_FORIN:
case PNK_FOROF:
+ return FoldForInOrOf(cx, pn, parser, inGenexpLambda);
+
case PNK_FORHEAD:
- case PNK_ADD:
- case PNK_NEW:
- case PNK_CALL:
- case PNK_GENEXP:
- case PNK_TAGGED_TEMPLATE:
+ return FoldForHead(cx, pn, parser, inGenexpLambda);
+
case PNK_LABEL:
+ MOZ_ASSERT(pn->isArity(PN_NAME));
+ return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
+
case PNK_DOT:
+ return FoldDottedProperty(cx, pn, parser, inGenexpLambda);
+
case PNK_LEXICALSCOPE:
+ MOZ_ASSERT(pn->isArity(PN_NAME));
+ if (!pn->pn_expr)
+ return true;
+ return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
+
case PNK_NAME:
- case PNK_EXPORT_SPEC:
- case PNK_IMPORT_SPEC:
- MOZ_ASSERT(!pn->isArity(PN_CODE), "only functions are code nodes");
- break; // for now
+ return FoldName(cx, pn, parser, inGenexpLambda);
case PNK_LIMIT: // invalid sentinel value
MOZ_CRASH("invalid node kind");
}
- // First, recursively fold constants on the children of this node.
- switch (pn->getArity()) {
- case PN_CODE:
- MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
- MOZ_CRASH("should have been handled above");
-
- case PN_LIST:
- {
- // Don't fold a parenthesized call expression. See bug 537673.
- ParseNode** listp = &pn->pn_head;
- if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE)) && (*listp)->isInParens())
- listp = &(*listp)->pn_next;
-
- for (; *listp; listp = &(*listp)->pn_next) {
- if (!Fold(cx, listp, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
- }
-
- // If the last node in the list was replaced, pn_tail points into the wrong node.
- pn->pn_tail = listp;
-
- // Save the list head in pn1 for later use.
- pn1 = pn->pn_head;
- pn2 = nullptr;
- break;
- }
-
- case PN_TERNARY:
- MOZ_ASSERT(!pn->isKind(PNK_CONDITIONAL),
- "should be skipping this above");
- MOZ_ASSERT(!pn->isKind(PNK_IF),
- "should be skipping this above");
- /* Any kid may be null (e.g. for (;;)). */
- if (pn->pn_kid1) {
- if (!Fold(cx, &pn->pn_kid1, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
- }
- pn1 = pn->pn_kid1;
-
- if (pn->pn_kid2) {
- if (!Fold(cx, &pn->pn_kid2, parser, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
- return false;
- if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) {
- parser.freeTree(pn->pn_kid2);
- pn->pn_kid2 = nullptr;
- }
- }
- pn2 = pn->pn_kid2;
-
- if (pn->pn_kid3) {
- if (!Fold(cx, &pn->pn_kid3, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
- }
- break;
-
- case PN_BINARY:
- case PN_BINARY_OBJ:
- /* First kid may be null (for default case in switch). */
- if (pn->pn_left) {
- if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda, condIf(pn, PNK_WHILE)))
- return false;
- }
- /* Second kid may be null (for return in non-generator). */
- if (pn->pn_right) {
- if (!Fold(cx, &pn->pn_right, parser, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
- return false;
- }
- pn1 = pn->pn_left;
- pn2 = pn->pn_right;
- break;
-
- case PN_UNARY:
- MOZ_ASSERT(!IsDeleteKind(pn->getKind()),
- "should have been handled above");
- if (pn->pn_kid) {
- if (!Fold(cx, &pn->pn_kid, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
- }
- pn1 = pn->pn_kid;
- break;
-
- case PN_NAME:
- /*
- * Skip pn1 down along a chain of dotted member expressions to avoid
- * excessive recursion. Our only goal here is to fold constants (if
- * any) in the primary expression operand to the left of the first
- * dot in the chain.
- */
- if (!pn->isUsed()) {
- ParseNode** lhsp = &pn->pn_expr;
- while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed())
- lhsp = &(*lhsp)->pn_expr;
- if (*lhsp && !Fold(cx, lhsp, parser, inGenexpLambda, SyntacticContext::Other))
- return false;
- pn1 = *lhsp;
- }
- break;
-
- case PN_NULLARY:
- break;
- }
-
- // The immediate child of a PNK_DELETE* node should not be replaced
- // with node indicating a different syntactic form; |delete x| is not
- // the same as |delete (true && x)|. See bug 888002.
- //
- // pn is the immediate child in question. Its descendants were already
- // constant-folded above, so we're done.
- if (sc == SyntacticContext::Delete)
- return true;
-
- switch (pn->getKind()) {
- case PNK_ADD: {
- MOZ_ASSERT(pn->isArity(PN_LIST));
-
- bool folded = false;
-
- pn2 = pn1->pn_next;
- if (pn1->isKind(PNK_NUMBER)) {
- // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
- // Note that we can only do this the front of the list:
- // (x + 1 + 2 !== x + 3) when x is a string.
- while (pn2 && pn2->isKind(PNK_NUMBER)) {
- pn1->pn_dval += pn2->pn_dval;
- pn1->pn_next = pn2->pn_next;
- parser.freeTree(pn2);
- pn2 = pn1->pn_next;
- pn->pn_count--;
- folded = true;
- }
- }
-
- // Now search for adjacent pairs of literals to fold for string
- // concatenation.
- //
- // isStringConcat is true if we know the operation we're looking at
- // will be string concatenation at runtime. As soon as we see a
- // string, we know that every addition to the right of it will be
- // string concatenation, even if both operands are numbers:
- // ("s" + x + 1 + 2 === "s" + x + "12").
- //
- bool isStringConcat = false;
- RootedString foldedStr(cx);
-
- // (number + string) is definitely concatenation, but only at the
- // front of the list: (x + 1 + "2" !== x + "12") when x is a
- // number.
- if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING))
- isStringConcat = true;
-
- while (pn2) {
- isStringConcat = isStringConcat || pn1->isKind(PNK_STRING);
-
- if (isStringConcat &&
- (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) &&
- (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER)))
- {
- // Fold string concatenation of literals.
- if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING))
- return false;
- if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING))
- return false;
- if (!foldedStr)
- foldedStr = pn1->pn_atom;
- RootedString right(cx, pn2->pn_atom);
- foldedStr = ConcatStrings(cx, foldedStr, right);
- if (!foldedStr)
- return false;
- pn1->pn_next = pn2->pn_next;
- parser.freeTree(pn2);
- pn2 = pn1->pn_next;
- pn->pn_count--;
- folded = true;
- } else {
- if (foldedStr) {
- // Convert the rope of folded strings into an Atom.
- pn1->pn_atom = AtomizeString(cx, foldedStr);
- if (!pn1->pn_atom)
- return false;
- foldedStr = nullptr;
- }
- pn1 = pn2;
- pn2 = pn2->pn_next;
- }
- }
-
- if (foldedStr) {
- // Convert the rope of folded strings into an Atom.
- pn1->pn_atom = AtomizeString(cx, foldedStr);
- if (!pn1->pn_atom)
- return false;
- }
-
- if (folded) {
- if (pn->pn_count == 1) {
- // We reduced the list to one constant. There is no
- // addition anymore. Replace the PNK_ADD node with the
- // single PNK_STRING or PNK_NUMBER node.
- ReplaceNode(pnp, pn1);
- pn = pn1;
- } else if (!pn2) {
- pn->pn_tail = &pn1->pn_next;
- }
- }
-
- break;
- }
-
- case PNK_TYPEOFNAME:
- case PNK_TYPEOFEXPR:
- case PNK_VOID:
- case PNK_NOT:
- case PNK_BITNOT:
- case PNK_POS:
- case PNK_NEG:
- case PNK_CONDITIONAL:
- case PNK_IF:
- case PNK_AND:
- case PNK_OR:
- case PNK_SUB:
- case PNK_STAR:
- case PNK_LSH:
- case PNK_RSH:
- case PNK_URSH:
- case PNK_DIV:
- case PNK_MOD:
- case PNK_POW:
- MOZ_CRASH("should have been fully handled above");
-
- case PNK_ELEM: {
- // An indexed expression, pn1[pn2]. A few cases can be improved.
- PropertyName* name = nullptr;
- if (pn2->isKind(PNK_STRING)) {
- JSAtom* atom = pn2->pn_atom;
- uint32_t index;
-
- if (atom->isIndex(&index)) {
- // Optimization 1: We have something like pn1["100"]. This is
- // equivalent to pn1[100] which is faster.
- pn2->setKind(PNK_NUMBER);
- pn2->setOp(JSOP_DOUBLE);
- pn2->pn_dval = index;
- } else {
- name = atom->asPropertyName();
- }
- } else if (pn2->isKind(PNK_NUMBER)) {
- double number = pn2->pn_dval;
- if (number != ToUint32(number)) {
- // Optimization 2: We have something like pn1[3.14]. The number
- // is not an array index. This is equivalent to pn1["3.14"]
- // which enables optimization 3 below.
- JSAtom* atom = ToAtom(cx, DoubleValue(number));
- if (!atom)
- return false;
- name = atom->asPropertyName();
- }
- }
-
- if (name && NameToId(name) == IdToTypeId(NameToId(name))) {
- // Optimization 3: We have pn1["foo"] where foo is not an index.
- // Convert to a property access (like pn1.foo) which we optimize
- // better downstream. Don't bother with this for names which TI
- // considers to be indexes, to simplify downstream analysis.
- ParseNode* expr = parser.handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
- if (!expr)
- return false;
- expr->setInParens(pn->isInParens());
- ReplaceNode(pnp, expr);
-
- // Supposing we're replacing |obj["prop"]| with |obj.prop|, we now
- // can free the |"prop"| node and |obj["prop"]| nodes -- but not
- // the |obj| node now a sub-node of |expr|. Mutate |pn| into a
- // node with |"prop"| as its child so that our |pn| doesn't have a
- // necessarily-weird structure (say, by nulling out |pn->pn_left|
- // only) that would fail AST sanity assertions performed by
- // |parser.freeTree(pn)|.
- pn->setKind(PNK_TYPEOFEXPR);
- pn->setArity(PN_UNARY);
- pn->pn_kid = pn2;
- parser.freeTree(pn);
-
- pn = expr;
- }
- break;
- }
-
- default:;
- }
-
- afterFolding:
- if (sc == SyntacticContext::Condition) {
- Truthiness t = Boolish(pn);
- if (t != Unknown) {
- /*
- * We can turn function nodes into constant nodes here, but mutating function
- * nodes is tricky --- in particular, mutating a function node that appears on
- * a method list corrupts the method list. However, methods are M's in
- * statements of the form 'this.foo = M;', which we never fold, so we're okay.
- */
- parser.prepareNodeForMutation(pn);
- if (t == Truthy) {
- pn->setKind(PNK_TRUE);
- pn->setOp(JSOP_TRUE);
- } else {
- pn->setKind(PNK_FALSE);
- pn->setOp(JSOP_FALSE);
- }
- pn->setArity(PN_NULLARY);
- }
- }
-
- return true;
+ MOZ_CRASH("shouldn't reach here");
+ return false;
}
bool
@@ -1938,5 +1968,5 @@ frontend::FoldConstants(ExclusiveContext* cx, ParseNode** pnp, Parserpc->useAsmOrInsideUseAsm())
return true;
- return Fold(cx, pnp, *parser, false, SyntacticContext::Other);
+ return Fold(cx, pnp, *parser, false);
}
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 19bbd98bafc8..b346e01df48c 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5705,7 +5705,9 @@ Parser::yieldExpression(InHandling inHandling)
case TOK_COMMA:
// No value.
exprNode = null();
- tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ tokenStream.addModifierException((tt == TOK_EOL || tt == TOK_EOF)
+ ? TokenStream::NoneIsOperandYieldEOL
+ : TokenStream::NoneIsOperand);
break;
case TOK_MUL:
kind = PNK_YIELD_STAR;
@@ -5768,7 +5770,9 @@ Parser::yieldExpression(InHandling inHandling)
case TOK_COMMA:
// No value.
exprNode = null();
- tokenStream.addModifierException(TokenStream::NoneIsOperand);
+ tokenStream.addModifierException((tt == TOK_EOL || tt == TOK_EOF)
+ ? TokenStream::NoneIsOperandYieldEOL
+ : TokenStream::NoneIsOperand);
break;
default:
exprNode = assignExpr(inHandling, YieldIsKeyword);
diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp
index ae6e97a5e5da..0c21ba6f7fa0 100644
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1650,7 +1650,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
// occurs and then the token is re-gotten (or peeked, etc.), we can assert
// that both gets have used the same modifiers.
tp->modifier = modifier;
- tp->modifierExceptions = NoException;
+ tp->modifierException = NoException;
#endif
MOZ_ASSERT(IsTokenSane(tp));
*ttp = tp->type;
diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h
index c9bf570b1e5a..75a62c560fa6 100644
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -117,6 +117,29 @@ struct Token
// ^ TemplateTail context
TemplateTail,
};
+ enum ModifierException
+ {
+ NoException,
+
+ // If an yield expression operand is omitted and yield expression is
+ // followed by non-EOL, the next token is already gotten with Operand,
+ // but we expect operator (None).
+ NoneIsOperand,
+
+ // If an yield expression operand is omitted and yield expression is
+ // followed by EOL, the next token is already gotten with Operand, and
+ // we expect Operand in next statement, but MatchOrInsertSemicolon
+ // after expression statement expects operator (None).
+ NoneIsOperandYieldEOL,
+
+ // If a semicolon is inserted automatically, the next token is already
+ // gotten with None, but we expect Operand.
+ OperandIsNone,
+
+ // If name of method definition is `get` or `set`, the next token is
+ // already gotten with KeywordIsName, but we expect None.
+ NoneIsKeywordIsName,
+ };
friend class TokenStream;
public:
@@ -136,7 +159,7 @@ struct Token
} u;
#ifdef DEBUG
Modifier modifier; // Modifier used to get this token
- uint8_t modifierExceptions; // Bitwise OR of modifier exceptions
+ ModifierException modifierException; // Exception for this modifier
#endif
// This constructor is necessary only for MSVC 2013 and how it compiles the
@@ -411,37 +434,47 @@ class MOZ_STACK_CLASS TokenStream
static MOZ_CONSTEXPR_VAR Modifier KeywordIsName = Token::KeywordIsName;
static MOZ_CONSTEXPR_VAR Modifier TemplateTail = Token::TemplateTail;
- enum ModifierException
- {
- NoException = 0x00,
-
- // If a semicolon is inserted automatically, the next token is already
- // gotten with None, but we expect Operand.
- NoneIsOperand = 0x01,
-
- // If an yield expression operand is omitted, the next token is already
- // gotten with Operand, but we expect operator (None).
- OperandIsNone = 0x02,
-
- // If name of method definition is `get` or `set`, the next token is
- // already gotten with KeywordIsName, but we expect None.
- NoneIsKeywordIsName = 0x04,
- };
+ typedef Token::ModifierException ModifierException;
+ static MOZ_CONSTEXPR_VAR ModifierException NoException = Token::NoException;
+ static MOZ_CONSTEXPR_VAR ModifierException NoneIsOperand = Token::NoneIsOperand;
+ static MOZ_CONSTEXPR_VAR ModifierException NoneIsOperandYieldEOL = Token::NoneIsOperandYieldEOL;
+ static MOZ_CONSTEXPR_VAR ModifierException OperandIsNone = Token::OperandIsNone;
+ static MOZ_CONSTEXPR_VAR ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName;
void addModifierException(ModifierException modifierException) {
#ifdef DEBUG
const Token& next = nextToken();
+ if (next.modifierException == NoneIsOperand ||
+ next.modifierException == NoneIsOperandYieldEOL)
+ {
+ // Token after yield expression without operand already has
+ // NoneIsOperand or NoneIsOperandYieldEOL exception.
+ MOZ_ASSERT(modifierException == OperandIsNone);
+ if (next.modifierException == NoneIsOperand)
+ MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
+ "next token requires contextual specifier to be parsed unambiguously");
+ else
+ MOZ_ASSERT(next.type != TOK_DIV,
+ "next token requires contextual specifier to be parsed unambiguously");
+
+ // Do not update modifierException.
+ return;
+ }
+
+ MOZ_ASSERT(next.modifierException == NoException);
switch (modifierException) {
case NoneIsOperand:
MOZ_ASSERT(next.modifier == Operand);
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
break;
+ case NoneIsOperandYieldEOL:
+ MOZ_ASSERT(next.modifier == Operand);
+ MOZ_ASSERT(next.type != TOK_DIV,
+ "next token requires contextual specifier to be parsed unambiguously");
+ break;
case OperandIsNone:
- // Non-Operand token after yield/continue/break already has
- // NoneIsOperand exception.
- MOZ_ASSERT(next.modifier == None ||
- ((next.modifierExceptions & NoneIsOperand) && next.modifier == Operand));
+ MOZ_ASSERT(next.modifier == None);
MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
"next token requires contextual specifier to be parsed unambiguously");
break;
@@ -452,7 +485,7 @@ class MOZ_STACK_CLASS TokenStream
default:
MOZ_CRASH("unexpected modifier exception");
}
- tokens[(cursor + 1) & ntokensMask].modifierExceptions |= modifierException;
+ tokens[(cursor + 1) & ntokensMask].modifierException = modifierException;
#endif
}
@@ -473,19 +506,21 @@ class MOZ_STACK_CLASS TokenStream
if (modifier == lookaheadToken.modifier)
return;
- if (lookaheadToken.modifierExceptions & OperandIsNone) {
+ if (lookaheadToken.modifierException == OperandIsNone) {
// getToken(Operand) permissibly following getToken().
if (modifier == Operand && lookaheadToken.modifier == None)
return;
}
- if (lookaheadToken.modifierExceptions & NoneIsOperand) {
+ if (lookaheadToken.modifierException == NoneIsOperand ||
+ lookaheadToken.modifierException == NoneIsOperandYieldEOL)
+ {
// getToken() permissibly following getToken(Operand).
if (modifier == None && lookaheadToken.modifier == Operand)
return;
}
- if (lookaheadToken.modifierExceptions & NoneIsKeywordIsName) {
+ if (lookaheadToken.modifierException == NoneIsKeywordIsName) {
// getToken() permissibly following getToken(KeywordIsName).
if (modifier == None && lookaheadToken.modifier == KeywordIsName)
return;
diff --git a/js/src/jit-test/tests/basic/bug1172503-2.js b/js/src/jit-test/tests/basic/bug1172503-2.js
new file mode 100644
index 000000000000..7f1ded8dc357
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1172503-2.js
@@ -0,0 +1,10 @@
+var n = 0;
+this.__proto__ = new Proxy({}, {
+ has: function () {
+ if (++n === 2)
+ return false;
+ a = 0;
+ }
+});
+a = 0;
+assertEq(a, 0);
diff --git a/js/src/jit-test/tests/basic/bug1172503.js b/js/src/jit-test/tests/basic/bug1172503.js
new file mode 100644
index 000000000000..5fda66ffe332
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1172503.js
@@ -0,0 +1,9 @@
+// |jit-test| error: m is not defined
+this.__proto__ = Proxy.create({
+ has:function(){
+ try {
+ aa0 = Function(undefined);
+ } catch (aa) {}
+ }
+});
+m();
diff --git a/js/src/jit-test/tests/basic/bug1189744.js b/js/src/jit-test/tests/basic/bug1189744.js
new file mode 100644
index 000000000000..6d202e702ca1
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1189744.js
@@ -0,0 +1,11 @@
+var obj;
+for (var i = 0; i < 100; i++)
+ obj = {a: 7, b: 13, c: 42, d: 0};
+
+Object.defineProperty(obj, "x", {
+ get: function () { return 3; }
+});
+obj.__ob__ = 17;
+
+Object.defineProperty(obj, "c", {value: 8, writable: true});
+assertEq(obj.__ob__, 17);
diff --git a/js/src/jit-test/tests/gc/oomInWeakMap.js b/js/src/jit-test/tests/gc/oomInWeakMap.js
new file mode 100644
index 000000000000..df9867fc647e
--- /dev/null
+++ b/js/src/jit-test/tests/gc/oomInWeakMap.js
@@ -0,0 +1,7 @@
+// |jit-test| --no-ggc; allow-unhandlable-oom; --no-threads
+
+load(libdir + 'oomTest.js');
+oomTest(function () {
+ eval(`var wm = new WeakMap();
+ wm.set({}, 'FOO').get(false);`);
+});
diff --git a/js/src/jit-test/tests/ion/dce-with-rinstructions.js b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
index 3226fb641ce6..126b2dabdfad 100644
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -475,7 +475,8 @@ function rpow_number(i) {
var x = Math.pow(i, 3.14159);
if (uceFault_pow_number(i) || uceFault_pow_number(i))
assertEq(x, Math.pow(99, 3.14159));
- assertRecoveredOnBailout(x, true);
+ // POW recovery temporarily disabled. See bug 1188586.
+ assertRecoveredOnBailout(x, false);
return i;
}
diff --git a/js/src/jit-test/tests/parser/yield-without-operand.js b/js/src/jit-test/tests/parser/yield-without-operand.js
index 64a3c23e5a3f..56f041a7306e 100644
--- a/js/src/jit-test/tests/parser/yield-without-operand.js
+++ b/js/src/jit-test/tests/parser/yield-without-operand.js
@@ -6,8 +6,14 @@ assertNoWarning(() => Function("yield"), SyntaxError,
"yield followed by EOF is fine");
assertNoWarning(() => Function("yield;"), SyntaxError,
"yield followed by semicolon is fine");
-assertNoWarning(() => Function("yield\n print('ok');"), SyntaxError,
+assertNoWarning(() => Function("yield\n"), SyntaxError,
"yield followed by newline is fine");
+assertNoWarning(() => Function("yield\n print('ok');"), SyntaxError,
+ "yield followed by newline and statement is fine");
+assertNoWarning(() => Function("yield\n /x/;"), SyntaxError,
+ "yield followed by newline and regexp is fine");
+assertThrowsInstanceOf(() => Function("yield\n /"), SyntaxError,
+ "yield followed by newline and slash is fine");
assertNoWarning(() => eval("(function () { yield; })"), SyntaxError,
"yield followed by semicolon in eval code is fine");
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 8024c94044f5..bb0eab23b19b 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5949,7 +5949,8 @@ class MPow
}
bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
- return true;
+ // Temporarily disable recovery to relieve fuzzer pressure. See bug 1188586.
+ return false;
}
ALLOW_CLONE(MPow)
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 1312c4a132bc..f0cc12a21d75 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1021,11 +1021,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len
return false;
if (!hole && !v.isNullOrUndefined()) {
if (Locale) {
- JSObject* robj = ToObject(cx, v);
- if (!robj)
+ RootedValue fun(cx);
+ if (!GetProperty(cx, v, cx->names().toLocaleString, &fun))
return false;
- RootedId id(cx, NameToId(cx->names().toLocaleString));
- if (!robj->callMethod(cx, id, 0, nullptr, &v))
+ if (!Invoke(cx, v, fun, 0, nullptr, &v))
return false;
}
if (!ValueToStringBuffer(cx, v, sb))
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index 53f903ad3f78..85c6691101d5 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2586,8 +2586,10 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
} else {
// This is either a straight-up data property or (rarely) a
// property with a JSGetterOp/JSSetterOp. The latter must be
- // reported to the caller as a plain data property, so don't
- // populate desc.getter/setter, and mask away the SHARED bit.
+ // reported to the caller as a plain data property, so clear
+ // desc.getter/setter, and mask away the SHARED bit.
+ desc.setGetter(nullptr);
+ desc.setSetter(nullptr);
desc.attributesRef() &= ~JSPROP_SHARED;
if (IsImplicitDenseOrTypedArrayElement(shape)) {
diff --git a/js/src/jsobj.h b/js/src/jsobj.h
index 2f9f794f58f9..acffeca0b38d 100644
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1004,7 +1004,8 @@ GetObjectClassName(JSContext* cx, HandleObject obj);
*/
/*
- * If obj a WindowProxy, return its current inner Window. Otherwise return obj.
+ * If obj is a WindowProxy, return its current inner Window. Otherwise return
+ * obj. This function can't fail and never returns nullptr.
*
* GetInnerObject is called when we need a scope chain; you never want a
* WindowProxy on a scope chain.
@@ -1027,6 +1028,7 @@ GetInnerObject(JSObject* obj)
/*
* If obj is a Window object, return the WindowProxy. Otherwise return obj.
+ * This function can't fail; it never sets an exception or returns nullptr.
*
* This must be called before passing an object to script, if the object might
* be a Window. (But usually those cases involve scope objects, and for those,
diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp
index ddd8c4e5a22c..a90ae99fa2df 100644
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -355,14 +355,15 @@ SetWeakMapEntryInternal(JSContext* cx, Handle mapObj,
{
ObjectValueMap* map = mapObj->getMap();
if (!map) {
- map = cx->new_(cx, mapObj.get());
- if (!map)
+ AutoInitGCManagedObject newMap(
+ cx->make_unique(cx, mapObj.get()));
+ if (!newMap)
return false;
- if (!map->init()) {
- js_delete(map);
+ if (!newMap->init()) {
JS_ReportOutOfMemory(cx);
return false;
}
+ map = newMap.release();
mapObj->setPrivate(map);
}
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
index 33f7a4b36d93..9cbac49a296b 100644
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -88,17 +88,6 @@ js::assertEnteredPolicy(JSContext* cx, JSObject* proxy, jsid id,
}
#endif
-#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \
- JS_BEGIN_MACRO \
- RootedObject proto(cx); \
- if (!GetPrototype(cx, proxy, &proto)) \
- return false; \
- if (!proto) \
- return true; \
- assertSameCompartment(cx, proxy, proto); \
- return protoCall; \
- JS_END_MACRO \
-
bool
Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
MutableHandle desc)
@@ -109,13 +98,12 @@ Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
if (!policy.allowed())
return policy.returnValue();
- if (!handler->hasPrototype())
- return handler->getPropertyDescriptor(cx, proxy, id, desc);
- if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
- return false;
- if (desc.object())
- return true;
- INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc));
+
+ // Special case. See the comment on BaseProxyHandler::mHasPrototype.
+ if (handler->hasPrototype())
+ return handler->BaseProxyHandler::getPropertyDescriptor(cx, proxy, id, desc);
+
+ return handler->getPropertyDescriptor(cx, proxy, id, desc);
}
bool
@@ -241,16 +229,23 @@ Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
if (!policy.allowed())
return policy.returnValue();
- if (!handler->hasPrototype())
- return handler->has(cx, proxy, id, bp);
- if (!handler->hasOwn(cx, proxy, id, bp))
- return false;
- if (*bp)
- return true;
- bool Bp;
- INVOKE_ON_PROTOTYPE(cx, handler, proxy,
- JS_HasPropertyById(cx, proto, id, &Bp) &&
- ((*bp = Bp) || true));
+
+ if (handler->hasPrototype()) {
+ if (!handler->hasOwn(cx, proxy, id, bp))
+ return false;
+ if (*bp)
+ return true;
+
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return true;
+
+ return HasProperty(cx, proto, id, bp);
+ }
+
+ return handler->has(cx, proxy, id, bp);
}
bool
@@ -265,8 +260,18 @@ Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
return handler->hasOwn(cx, proxy, id, bp);
}
+static Value
+OuterizeValue(JSContext* cx, HandleValue v)
+{
+ if (v.isObject()) {
+ RootedObject obj(cx, &v.toObject());
+ return ObjectValue(*GetOuterObject(cx, obj));
+ }
+ return v;
+}
+
bool
-Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver_, HandleId id,
MutableHandleValue vp)
{
JS_CHECK_RECURSION(cx, return false);
@@ -275,16 +280,26 @@ Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id
AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
if (!policy.allowed())
return policy.returnValue();
- bool own;
- if (!handler->hasPrototype()) {
- own = true;
- } else {
+
+ // Outerize the receiver. Proxy handlers shouldn't have to know about
+ // the Window/WindowProxy distinction.
+ RootedObject receiver(cx, GetOuterObject(cx, receiver_));
+
+ if (handler->hasPrototype()) {
+ bool own;
if (!handler->hasOwn(cx, proxy, id, &own))
return false;
+ if (!own) {
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return true;
+ return GetProperty(cx, proto, receiver, id, vp);
+ }
}
- if (own)
- return handler->get(cx, proxy, receiver, id, vp);
- INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetProperty(cx, proto, receiver, id, vp));
+
+ return handler->get(cx, proxy, receiver, id, vp);
}
bool
@@ -307,7 +322,7 @@ Proxy::callProp(JSContext* cx, HandleObject proxy, HandleObject receiver, Handle
}
bool
-Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver,
+Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
ObjectOpResult& result)
{
JS_CHECK_RECURSION(cx, return false);
@@ -319,6 +334,10 @@ Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, Handle
return result.succeed();
}
+ // Outerize the receiver. Proxy handlers shouldn't have to know about
+ // the Window/WindowProxy distinction.
+ RootedValue receiver(cx, OuterizeValue(cx, receiver_));
+
// Special case. See the comment on BaseProxyHandler::mHasPrototype.
if (handler->hasPrototype())
return handler->BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
@@ -343,33 +362,35 @@ Proxy::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
JS_CHECK_RECURSION(cx, return false);
const BaseProxyHandler* handler = proxy->as().handler();
objp.set(nullptr); // default result if we refuse to perform this action
- if (!handler->hasPrototype()) {
- AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
- BaseProxyHandler::ENUMERATE, true);
- // If the policy denies access but wants us to return true, we need
- // to hand a valid (empty) iterator object to the caller.
- if (!policy.allowed()) {
- return policy.returnValue() &&
- NewEmptyPropertyIterator(cx, 0, objp);
- }
- return handler->enumerate(cx, proxy, objp);
+
+ if (handler->hasPrototype()) {
+ AutoIdVector props(cx);
+ if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
+ return false;
+
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, proxy, &proto))
+ return false;
+ if (!proto)
+ return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
+ assertSameCompartment(cx, proxy, proto);
+
+ AutoIdVector protoProps(cx);
+ return GetPropertyKeys(cx, proto, 0, &protoProps) &&
+ AppendUnique(cx, props, protoProps) &&
+ EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
}
- AutoIdVector props(cx);
- if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
- return false;
+ AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
+ BaseProxyHandler::ENUMERATE, true);
- RootedObject proto(cx);
- if (!GetPrototype(cx, proxy, &proto))
- return false;
- if (!proto)
- return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
- assertSameCompartment(cx, proxy, proto);
-
- AutoIdVector protoProps(cx);
- return GetPropertyKeys(cx, proto, 0, &protoProps) &&
- AppendUnique(cx, props, protoProps) &&
- EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
+ // If the policy denies access but wants us to return true, we need
+ // to hand a valid (empty) iterator object to the caller.
+ if (!policy.allowed()) {
+ return policy.returnValue() &&
+ NewEmptyPropertyIterator(cx, 0, objp);
+ }
+ return handler->enumerate(cx, proxy, objp);
}
bool
diff --git a/js/src/tests/ecma_6/Array/toLocaleString.js b/js/src/tests/ecma_6/Array/toLocaleString.js
new file mode 100644
index 000000000000..5bc19a839f22
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/toLocaleString.js
@@ -0,0 +1,16 @@
+"use strict";
+
+Object.defineProperty(String.prototype, "toLocaleString", {
+ get() {
+ // Congratulations! You probably fixed primitive-this getters.
+ // Change "object" to "string".
+ assertEq(typeof this, "object");
+
+ return function() { return typeof this; };
+ }
+})
+
+assertEq(["test"].toLocaleString(), "string");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Object/toLocaleString.js b/js/src/tests/ecma_6/Object/toLocaleString.js
new file mode 100644
index 000000000000..bc5a96ea0ab0
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/toLocaleString.js
@@ -0,0 +1,15 @@
+"use strict";
+
+Object.defineProperty(String.prototype, "toString", {
+ get() {
+ // Congratulations! You probably fixed primitive-this getters.
+ // Change "object" to "string".
+ assertEq(typeof this, "object");
+
+ return function() { return typeof this; };
+ }
+})
+assertEq(Object.prototype.toLocaleString.call("test"), "string");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_6/Proxy/global-receiver.js b/js/src/tests/ecma_6/Proxy/global-receiver.js
new file mode 100644
index 000000000000..48db39b23710
--- /dev/null
+++ b/js/src/tests/ecma_6/Proxy/global-receiver.js
@@ -0,0 +1,29 @@
+// The global object can be the receiver passed to the get and set traps of a Proxy.
+
+var global = this;
+var proto = Object.getPrototypeOf(global);
+var gets = 0, sets = 0;
+Object.setPrototypeOf(global, new Proxy(proto, {
+ has(t, id) {
+ return id === "bareword" || Reflect.has(t, id);
+ },
+ get(t, id, r) {
+ gets++;
+ assertEq(r, global);
+ return Reflect.get(t, id, r);
+ },
+ set(t, id, v, r) {
+ sets++;
+ assertEq(r, global);
+ return Reflect.set(t, id, v, r);
+ }
+}));
+
+assertEq(bareword, undefined);
+assertEq(gets, 1);
+
+bareword = 12;
+assertEq(sets, 1);
+assertEq(global.bareword, 12);
+
+reportCompare(0, 0);
diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp
index bcf691cad204..b143ac843fef 100644
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2055,7 +2055,7 @@ NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape
*/
bool
js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiverValue, bool objHasOwn, ObjectOpResult& result)
+ HandleValue receiverValue, ObjectOpResult& result)
{
// Step 5.b.
if (!receiverValue.isObject())
@@ -2063,25 +2063,7 @@ js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleVa
RootedObject receiver(cx, &receiverValue.toObject());
bool existing;
- if (receiver == obj) {
- // Steps 5.c-e.ii.
- // The common case. The caller has necessarily done a property lookup
- // on obj and passed us the answer as objHasOwn.
- // We also know that the property is a data property and writable
- // if it exists.
-#ifdef DEBUG
- // Check that objHasOwn is correct. This could fail if receiver or a
- // native object on its prototype chain has a nondeterministic resolve
- // hook. We shouldn't have any that are quite that badly behaved.
- Rooted desc(cx);
- if (!GetOwnPropertyDescriptor(cx, receiver, id, &desc))
- return false;
- MOZ_ASSERT(!!desc.object() == objHasOwn);
- MOZ_ASSERT_IF(desc.object(), desc.isDataDescriptor());
- MOZ_ASSERT_IF(desc.object(), desc.writable());
-#endif
- existing = objHasOwn;
- } else {
+ {
// Steps 5.c-d.
Rooted desc(cx);
if (!GetOwnPropertyDescriptor(cx, receiver, id, &desc))
@@ -2156,7 +2138,7 @@ js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue
RootedObject proto(cx, obj->getProto());
if (proto)
return SetProperty(cx, proto, id, v, receiver, result);
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
/*
@@ -2178,7 +2160,7 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handl
return false;
}
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
/*
@@ -2239,7 +2221,7 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa
return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), v, result);
// Steps 5.b-f.
- return SetPropertyByDefining(cx, obj, id, v, receiver, obj == pobj, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
// Step 5 for all other properties.
@@ -2277,7 +2259,7 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa
// Shadow pobj[id] by defining a new data property receiver[id].
// Delegate everything to SetPropertyByDefining.
- return SetPropertyByDefining(cx, obj, id, v, receiver, obj == pobj, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
// Steps 6-11.
diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h
index 25d065add92d..7c69472fb2b1 100644
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1312,7 +1312,7 @@ NativeGetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, MutableH
bool
SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
- HandleValue receiver, bool objHasOwn, ObjectOpResult& result);
+ HandleValue receiver, ObjectOpResult& result);
bool
SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp
index 13cb4443be36..6460e3556993 100644
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -818,7 +818,7 @@ UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id
return SetProperty(cx, obj, id, v, receiver, result);
}
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) {
@@ -1485,7 +1485,7 @@ UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id
return SetProperty(cx, obj, id, v, receiver, result);
}
- return SetPropertyByDefining(cx, obj, id, v, receiver, false, result);
+ return SetPropertyByDefining(cx, obj, id, v, receiver, result);
}
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
diff --git a/layout/style/crashtests/1146101-1.html b/layout/style/crashtests/1146101-1.html
new file mode 100644
index 000000000000..e3f8f2aa3f59
--- /dev/null
+++ b/layout/style/crashtests/1146101-1.html
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list
index 3028fd765886..444071f15209 100644
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -112,6 +112,7 @@ load 1066089-1.html
load 1074651-1.html
pref(dom.webcomponents.enabled,true) load 1089463-1.html
pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
+pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
load 1153693-1.html
load 1161320-1.html
pref(dom.animations-api.core.enabled,true) load 1161320-2.html
diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp
index eeffff82eb6e..3a027ca3a6d2 100644
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -933,6 +933,10 @@ nsJARChannel::OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput,
NS_IMETHODIMP
nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetEnforceSecurity(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
NS_ENSURE_ARG_POINTER(listener);
diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp
index cde055f4be0b..8568b5f968a2 100644
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -626,6 +626,10 @@ nsBaseChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP
nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
diff --git a/netwerk/base/nsBaseChannel.h b/netwerk/base/nsBaseChannel.h
index 717ed60b6337..56b56bc4c1ad 100644
--- a/netwerk/base/nsBaseChannel.h
+++ b/netwerk/base/nsBaseChannel.h
@@ -269,7 +269,6 @@ private:
nsCOMPtr mProgressSink;
nsCOMPtr mOriginalURI;
nsCOMPtr mOwner;
- nsCOMPtr mLoadInfo;
nsCOMPtr mSecurityInfo;
nsCOMPtr mRedirectChannel;
nsCString mContentType;
@@ -285,6 +284,7 @@ private:
protected:
nsCOMPtr mURI;
nsCOMPtr mLoadGroup;
+ nsCOMPtr mLoadInfo;
nsCOMPtr mCallbacks;
nsCOMPtr mListener;
nsCOMPtr mListenerContext;
diff --git a/netwerk/protocol/app/AppProtocolHandler.cpp b/netwerk/protocol/app/AppProtocolHandler.cpp
index 94b41ae5d86c..48f892b1a0e3 100644
--- a/netwerk/protocol/app/AppProtocolHandler.cpp
+++ b/netwerk/protocol/app/AppProtocolHandler.cpp
@@ -111,6 +111,10 @@ DummyChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP DummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
mListener = aListener;
mListenerContext = aContext;
mPending = true;
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index 4ab6c4a42dad..17a239650147 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1488,6 +1488,10 @@ HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
NS_IMETHODIMP
HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get()));
if (mCanceled)
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index 79ceb97d9c84..0f011e49c640 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4953,6 +4953,10 @@ nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
NS_IMETHODIMP
nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
NS_ENSURE_ARG_POINTER(listener);
diff --git a/netwerk/protocol/rtsp/RtspChannelChild.cpp b/netwerk/protocol/rtsp/RtspChannelChild.cpp
index 08e7d58975cf..f6c8f7904efa 100644
--- a/netwerk/protocol/rtsp/RtspChannelChild.cpp
+++ b/netwerk/protocol/rtsp/RtspChannelChild.cpp
@@ -107,6 +107,10 @@ private:
NS_IMETHODIMP
RtspChannelChild::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
// Precondition checks.
MOZ_ASSERT(aListener);
nsCOMPtr uri = nsBaseChannel::URI();
diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
index 1edb63c54e2d..8824e9b1215f 100644
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -258,6 +258,15 @@ nsViewSourceChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP
nsViewSourceChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
+#ifdef DEBUG
+ {
+ nsCOMPtr loadInfo = mChannel->GetLoadInfo();
+ MOZ_ASSERT(!loadInfo || loadInfo->GetSecurityMode() == 0 ||
+ loadInfo->GetEnforceSecurity(),
+ "security flags in loadInfo but asyncOpen2() not called");
+ }
+#endif
+
NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
mListener = aListener;
diff --git a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
index f041c11dbcd7..ca16a2779fac 100644
--- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
+++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
@@ -626,6 +626,10 @@ GetTabChild(nsIChannel* aChannel)
NS_IMETHODIMP
WyciwygChannelChild::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
LOG(("WyciwygChannelChild::AsyncOpen [this=%p]\n", this));
// The only places creating wyciwyg: channels should be
diff --git a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
index 0e27a1397e68..51d838d870f7 100644
--- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
+++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
@@ -431,6 +431,10 @@ nsWyciwygChannel::Open2(nsIInputStream** aStream)
NS_IMETHODIMP
nsWyciwygChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
{
+ MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone(),
+ "security flags in loadInfo but asyncOpen2() not called");
+
LOG(("nsWyciwygChannel::AsyncOpen [this=%p]\n", this));
MOZ_ASSERT(mMode == NONE, "nsWyciwygChannel already open");
diff --git a/security/manager/ssl/nsSecureBrowserUIImpl.cpp b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
index 3db3e709a6ff..a9db36ccdb6c 100644
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -263,18 +263,24 @@ nsSecureBrowserUIImpl::MapInternalToExternalState(uint32_t* aState, lockIconStat
*aState |= nsIWebProgressListener::STATE_IDENTITY_EV_TOPLEVEL;
}
}
- // * If so, the state should be broken; overriding the previous state
- // set by the lock parameter.
+ // * If so, the state should be broken or insecure; overriding the previous
+ // state set by the lock parameter.
+ uint32_t tempState = STATE_IS_BROKEN;
+ if (lock == lis_no_security) {
+ // this is to ensure that http: pages with mixed content in nested
+ // iframes don't get marked as broken instead of insecure
+ tempState = STATE_IS_INSECURE;
+ }
if (docShell->GetHasMixedActiveContentLoaded() &&
docShell->GetHasMixedDisplayContentLoaded()) {
- *aState = STATE_IS_BROKEN |
+ *aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
} else if (docShell->GetHasMixedActiveContentLoaded()) {
- *aState = STATE_IS_BROKEN |
+ *aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
} else if (docShell->GetHasMixedDisplayContentLoaded()) {
- *aState = STATE_IS_BROKEN |
+ *aState = tempState |
nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
}
diff --git a/testing/config/mozharness/b2g_emulator_config.py b/testing/config/mozharness/b2g_emulator_config.py
index a3ec67493d4b..cd98e74aecb3 100644
--- a/testing/config/mozharness/b2g_emulator_config.py
+++ b/testing/config/mozharness/b2g_emulator_config.py
@@ -12,7 +12,6 @@ config = {
"--addEnv",
"LD_LIBRARY_PATH=/vendor/lib:/system/lib:/system/b2g",
"--with-b2g-emulator=%(b2gpath)s",
- "--skip-manifest=b2g_cppunittest_manifest.txt",
"."
],
"run_filename": "remotecppunittests.py",
diff --git a/testing/mach_commands.py b/testing/mach_commands.py
index 9502ff95d2bf..85ecc7678964 100644
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -299,9 +299,10 @@ class MachCommands(MachCommandBase):
if len(params['test_files']) == 0:
testdir = os.path.join(self.distdir, 'cppunittests')
- tests = cppunittests.extract_unittests_from_args([testdir], mozinfo.info)
+ manifest = os.path.join(self.topsrcdir, 'testing', 'cppunittest.ini')
+ tests = cppunittests.extract_unittests_from_args([testdir], mozinfo.info, manifest)
else:
- tests = cppunittests.extract_unittests_from_args(params['test_files'], mozinfo.info)
+ tests = cppunittests.extract_unittests_from_args(params['test_files'], mozinfo.info, None)
# See if we have crash symbols
symbols_path = os.path.join(self.distdir, 'crashreporter-symbols')
diff --git a/testing/mozharness/scripts/spidermonkey_build.py b/testing/mozharness/scripts/spidermonkey_build.py
index b05f45aea18b..99bf5b55bb07 100755
--- a/testing/mozharness/scripts/spidermonkey_build.py
+++ b/testing/mozharness/scripts/spidermonkey_build.py
@@ -349,6 +349,14 @@ class SpidermonkeyBuild(MockMixin,
def checkout_tools(self):
dirs = self.query_abs_dirs()
+
+ # If running from within a directory also passed as the --source dir,
+ # this has the danger of clobbering