diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index b3d6c8220b3..4114b63a2f5 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1816,6 +1816,7 @@ pref("print.enable_e10s_testing", true); #ifdef NIGHTLY_BUILD // Enable e10s add-on interposition by default. pref("extensions.interposition.enabled", true); +pref("extensions.interposition.prefetching", true); #endif pref("browser.defaultbrowser.notificationbar", false); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index b9ae9481ffd..40c05d14306 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -838,6 +838,7 @@ nsDocShell::nsDocShell(): mAllowKeywordFixup(false), mIsOffScreenBrowser(false), mIsActive(true), + mIsPrerendered(false), mIsAppTab(false), mUseGlobalHistory(false), mInPrivateBrowsing(false), @@ -3386,6 +3387,11 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent) { SetIsActive(value); } + if (NS_SUCCEEDED(parentAsDocShell->GetIsPrerendered(&value))) { + if (value) { + SetIsPrerendered(true); + } + } if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { value = false; } @@ -6052,6 +6058,22 @@ nsDocShell::GetIsActive(bool *aIsActive) return NS_OK; } +NS_IMETHODIMP +nsDocShell::SetIsPrerendered(bool aPrerendered) +{ + MOZ_ASSERT(!aPrerendered || !mIsPrerendered, + "SetIsPrerendered(true) called on already prerendered docshell"); + mIsPrerendered = aPrerendered; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShell::GetIsPrerendered(bool *aIsPrerendered) +{ + *aIsPrerendered = mIsPrerendered; + return NS_OK; +} + NS_IMETHODIMP nsDocShell::SetIsAppTab(bool aIsAppTab) { @@ -8553,8 +8575,8 @@ nsDocShell::RestoreFromHistory() // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning // that the child inherits our state. Among other things, this means - // that the child inherits our mIsActive and mInPrivateBrowsing, which - // is what we want. + // that the child inherits our mIsActive, mIsPrerendered and mInPrivateBrowsing, + // which is what we want. AddChild(childItem); childShell->SetAllowPlugins(allowPlugins); diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 837b9fc331f..3132bf8010b 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -877,6 +877,7 @@ protected: bool mAllowKeywordFixup; bool mIsOffScreenBrowser; bool mIsActive; + bool mIsPrerendered; bool mIsAppTab; bool mUseGlobalHistory; bool mInPrivateBrowsing; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index c50c98378d8..ba7be7c76e2 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -54,7 +54,7 @@ interface nsITabParent; typedef unsigned long nsLoadFlags; -[scriptable, builtinclass, uuid(c2756385-bc54-417b-9ae4-c5a40053a2a3)] +[scriptable, builtinclass, uuid(fef3bae1-6673-4c49-9f5a-fcc075926730)] interface nsIDocShell : nsIDocShellTreeItem { /** @@ -619,6 +619,13 @@ interface nsIDocShell : nsIDocShellTreeItem */ attribute boolean isActive; + /** + * Puts the docshell in prerendering mode. noscript because we want only + * native code to be able to put a docshell in prerendering. + */ + [noscript] void SetIsPrerendered(in boolean prerendered); + readonly attribute boolean isPrerendered; + /** * The ID of the docshell in the session history. */ diff --git a/dom/animation/AnimationTimeline.cpp b/dom/animation/AnimationTimeline.cpp index b20cee214dc..9d29ed1c803 100644 --- a/dom/animation/AnimationTimeline.cpp +++ b/dom/animation/AnimationTimeline.cpp @@ -60,14 +60,8 @@ AnimationTimeline::FastForward(const TimeStamp& aTimeStamp) return; } - // Bug 1113413: If the refresh driver has just been restored from test - // control it's possible that aTimeStamp could be before the most recent - // refresh. - if (refreshDriver && - aTimeStamp < refreshDriver->MostRecentRefresh()) { - mFastForwardTime = refreshDriver->MostRecentRefresh(); - return; - } + MOZ_ASSERT(!refreshDriver || aTimeStamp >= refreshDriver->MostRecentRefresh(), + "aTimeStamp must be >= the refresh driver time"); // FIXME: For all animations attached to this timeline, we should mark // their target elements as needing restyling. Otherwise, tasks that run diff --git a/dom/animation/test/css-transitions/test_animation-player-ready.html b/dom/animation/test/css-transitions/test_animation-player-ready.html index be4bafe5121..7cdd9b70bb5 100644 --- a/dom/animation/test/css-transitions/test_animation-player-ready.html +++ b/dom/animation/test/css-transitions/test_animation-player-ready.html @@ -67,4 +67,36 @@ async_test(function(t) { }, 'ready promise is rejected when a transition is cancelled by updating' + ' transition-property'); +async_test(function(t) { + var div = addDiv(t); + + // Set up pending transition + div.style.marginLeft = '0px'; + window.getComputedStyle(div).marginLeft; + div.style.transition = 'margin-left 100s'; + div.style.marginLeft = '100px'; + window.getComputedStyle(div).marginLeft; + + var player = div.getAnimationPlayers()[0]; + assert_equals(player.playState, 'pending', 'Player is initially pending'); + + // Set up listeners on ready promise + player.ready.then(t.step_func(function() { + assert_unreached('ready promise was fulfilled'); + })).catch(t.step_func(function(err) { + assert_equals(err.name, 'AbortError', + 'ready promise is rejected with AbortError'); + assert_equals(player.playState, 'idle', + 'Player is idle after transition was cancelled'); + })).then(t.step_func(function() { + t.done(); + })); + + // Now update the transition to animate to something not-interpolable + div.style.marginLeft = 'auto'; + window.getComputedStyle(div).marginLeft; + +}, 'ready promise is rejected when a transition is cancelled by changing' + + ' the transition property to something not interpolable'); + diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index 4d1f4a1e3fe..e5573282480 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -7,7 +7,7 @@ skip-if = buildapp == 'mulet' [css-animations/test_animations-dynamic-changes.html] [css-animations/test_animation-effect-name.html] [css-animations/test_animation-pausing.html] -skip-if = os == "mac" && os_version == "10.8" # disabled until bug 1112480 lands +skip-if = os == "win" || (os == "mac" && os_version == "10.8") # disabled until bug 1112480 lands [css-animations/test_animation-player-playstate.html] [css-animations/test_animation-player-ready.html] [css-animations/test_animation-target.html] diff --git a/dom/apps/PermissionsTable.jsm b/dom/apps/PermissionsTable.jsm index 0f8d46078cd..a2033c68134 100644 --- a/dom/apps/PermissionsTable.jsm +++ b/dom/apps/PermissionsTable.jsm @@ -407,7 +407,13 @@ this.PermissionsTable = { geolocation: { privileged: PROMPT_ACTION, certified: ALLOW_ACTION }, - "nfc": { + "audio-capture:3gpp": { + app: DENY_ACTION, + trusted: DENY_ACTION, + privileged: ALLOW_ACTION, + certified: ALLOW_ACTION + }, + "nfc": { app: DENY_ACTION, trusted: DENY_ACTION, privileged: ALLOW_ACTION, diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index a910ed21f18..cf650088c31 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -201,11 +201,7 @@ EventSource::Init(nsISupports* aOwner, // The conditional here is historical and not necessarily sane. if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) { - const char *filename; - if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) { - mScriptFile.AssignASCII(filename); - } - + nsJSUtils::GetCallingLocation(cx, mScriptFile, &mScriptLine); mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx); } diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index 31b5d462078..e450ab05ce4 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -83,8 +83,11 @@ WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx, return true; } - JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, aProxy)); - if (HasPropertyOnPrototype(aCx, aProxy, aId)) { + bool hasOnPrototype; + if (!HasPropertyOnPrototype(aCx, aProxy, aId, &hasOnPrototype)) { + return false; + } + if (hasOnPrototype) { return true; } @@ -94,6 +97,7 @@ WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx, } // Grab the DOM window. + JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, aProxy)); nsGlobalWindow* win = xpc::WindowOrNull(global); if (win->Length() > 0) { nsCOMPtr childWin = win->GetChildWindow(str); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4f9d7417665..953b11cf75f 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3364,12 +3364,7 @@ nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText, if (!aLineNumber) { JSContext *cx = GetCurrentJSContext(); if (cx) { - const char* filename; - uint32_t lineno; - if (nsJSUtils::GetCallingLocation(cx, &filename, &lineno)) { - spec = filename; - aLineNumber = lineno; - } + nsJSUtils::GetCallingLocation(cx, spec, &aLineNumber); } } if (spec.IsEmpty() && aURI) diff --git a/dom/base/nsElementFrameLoaderOwner.cpp b/dom/base/nsElementFrameLoaderOwner.cpp index e61fcfaf1f5..8d49e2d46bb 100644 --- a/dom/base/nsElementFrameLoaderOwner.cpp +++ b/dom/base/nsElementFrameLoaderOwner.cpp @@ -112,6 +112,9 @@ nsElementFrameLoaderOwner::EnsureFrameLoader() // Strangely enough, this method doesn't actually ensure that the // frameloader exists. It's more of a best-effort kind of thing. mFrameLoader = nsFrameLoader::Create(thisElement, mNetworkCreated); + if (mIsPrerendered) { + mFrameLoader->SetIsPrerendered(); + } } NS_IMETHODIMP @@ -135,6 +138,14 @@ nsElementFrameLoaderOwner::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner) return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsElementFrameLoaderOwner::SetIsPrerendered() +{ + MOZ_ASSERT(!mFrameLoader, "Please call SetIsPrerendered before frameLoader is created"); + mIsPrerendered = true; + return NS_OK; +} + nsresult nsElementFrameLoaderOwner::LoadSrc() { diff --git a/dom/base/nsElementFrameLoaderOwner.h b/dom/base/nsElementFrameLoaderOwner.h index f00125a5d58..fbdd796d9ed 100644 --- a/dom/base/nsElementFrameLoaderOwner.h +++ b/dom/base/nsElementFrameLoaderOwner.h @@ -33,6 +33,7 @@ class nsElementFrameLoaderOwner : public nsIFrameLoaderOwner public: explicit nsElementFrameLoaderOwner(mozilla::dom::FromParser aFromParser) : mNetworkCreated(aFromParser == mozilla::dom::FROM_PARSER_NETWORK) + , mIsPrerendered(false) , mBrowserFrameListenersRegistered(false) , mFrameLoaderCreationDisallowed(false) { @@ -70,6 +71,7 @@ protected: */ bool mNetworkCreated; + bool mIsPrerendered; bool mBrowserFrameListenersRegistered; bool mFrameLoaderCreationDisallowed; }; diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index ef7204a76af..6fea812ac9b 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -154,6 +154,7 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated) : mOwnerContent(aOwner) , mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID) , mDetachedSubdocViews(nullptr) + , mIsPrerendered(false) , mDepthTooGreat(false) , mIsTopLevelContent(false) , mDestroyCalled(false) @@ -295,6 +296,15 @@ nsFrameLoader::LoadURI(nsIURI* aURI) return rv; } +NS_IMETHODIMP +nsFrameLoader::SetIsPrerendered() +{ + MOZ_ASSERT(!mDocShell, "Please call SetIsPrerendered before docShell is created"); + mIsPrerendered = true; + + return NS_OK; +} + nsresult nsFrameLoader::ReallyStartLoading() { @@ -1613,6 +1623,11 @@ nsFrameLoader::MaybeCreateDocShell() mDocShell = do_CreateInstance("@mozilla.org/docshell;1"); NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); + if (mIsPrerendered) { + nsresult rv = mDocShell->SetIsPrerendered(true); + NS_ENSURE_SUCCESS(rv,rv); + } + // Apply sandbox flags even if our owner is not an iframe, as this copies // flags from our owning content's owning document. uint32_t sandboxFlags = 0; diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index 530b9af76d3..a8a0a7baebe 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -331,6 +331,7 @@ private: // a reframe, so that we know not to restore the presentation. nsCOMPtr mContainerDocWhileDetached; + bool mIsPrerendered : 1; bool mDepthTooGreat : 1; bool mIsTopLevelContent : 1; bool mDestroyCalled : 1; diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 4fc582b520b..c8ef7123a5b 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -951,6 +951,7 @@ GK_ATOM(precedingSibling, "preceding-sibling") GK_ATOM(predicate, "predicate") GK_ATOM(prefix, "prefix") GK_ATOM(preload, "preload") +GK_ATOM(prerendered, "prerendered") GK_ATOM(preserve, "preserve") GK_ATOM(preserveSpace, "preserve-space") GK_ATOM(preventdefault, "preventdefault") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 7bb08d31ed1..e28fbb7bb56 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1118,6 +1118,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mCanSkipCCGeneration(0), mVRDevicesInitialized(false) { + AssertIsOnMainThread(); + nsLayoutStatics::AddRef(); // Initialize the PRCList (this). @@ -1218,10 +1220,23 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) } } +#ifdef DEBUG + +/* static */ +void +nsGlobalWindow::AssertIsOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +#endif // DEBUG + /* static */ void nsGlobalWindow::Init() { + AssertIsOnMainThread(); + CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); NS_ASSERTION(gEntropyCollector, "gEntropyCollector should have been initialized!"); @@ -1245,6 +1260,8 @@ DisconnectEventTargetObjects(nsPtrHashKey* aKey, nsGlobalWindow::~nsGlobalWindow() { + AssertIsOnMainThread(); + mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); mEventTargetObjects.Clear(); @@ -1363,6 +1380,8 @@ nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject) void nsGlobalWindow::ShutDown() { + AssertIsOnMainThread(); + if (gDumpFile && gDumpFile != stdout) { fclose(gDumpFile); } diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 20c245c071c..777c7b59ed3 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -334,6 +334,14 @@ public: typedef mozilla::TimeDuration TimeDuration; typedef nsDataHashtable WindowByIdTable; + static void + AssertIsOnMainThread() +#ifdef DEBUG + ; +#else + { } +#endif + // public methods nsPIDOMWindow* GetPrivateParent(); @@ -670,6 +678,8 @@ public: } static nsGlobalWindow* GetOuterWindowWithId(uint64_t aWindowID) { + AssertIsOnMainThread(); + if (!sWindowsById) { return nullptr; } @@ -679,6 +689,8 @@ public: } static nsGlobalWindow* GetInnerWindowWithId(uint64_t aInnerWindowID) { + AssertIsOnMainThread(); + if (!sWindowsById) { return nullptr; } @@ -688,6 +700,8 @@ public: } static WindowByIdTable* GetWindowsTable() { + AssertIsOnMainThread(); + return sWindowsById; } diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl index beff6e1e66c..b60a8ff20f4 100644 --- a/dom/base/nsIFrameLoader.idl +++ b/dom/base/nsIFrameLoader.idl @@ -16,7 +16,7 @@ interface nsIDOMElement; interface nsITabParent; interface nsILoadContext; -[scriptable, builtinclass, uuid(7600aa92-88dc-491c-896d-0564159b6a66)] +[scriptable, builtinclass, uuid(28b6b043-46ec-412f-9be9-db22938b0d6d)] interface nsIFrameLoader : nsISupports { /** @@ -49,6 +49,11 @@ interface nsIFrameLoader : nsISupports */ void loadURI(in nsIURI aURI); + /** + * Puts the frameloader in prerendering mode. + */ + void setIsPrerendered(); + /** * Destroy the frame loader and everything inside it. This will * clear the weak owner content reference. @@ -192,7 +197,7 @@ class nsFrameLoader; native alreadyAddRefed_nsFrameLoader(already_AddRefed); -[scriptable, uuid(5879040e-83e9-40e3-b2bb-5ddf43b76e47)] +[scriptable, uuid(c4abebcf-55f3-47d4-af15-151311971255)] interface nsIFrameLoaderOwner : nsISupports { /** @@ -201,6 +206,11 @@ interface nsIFrameLoaderOwner : nsISupports readonly attribute nsIFrameLoader frameLoader; [noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader(); + /** + * Puts the FrameLoaderOwner in prerendering mode. + */ + void setIsPrerendered(); + /** * Swap frame loaders with the given nsIFrameLoaderOwner. This may * only be posible in a very limited range of circumstances, or diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index ed38513f36d..5ab5f051a08 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -173,11 +173,8 @@ CheckCSPForEval(JSContext* aCx, nsGlobalWindow* aWindow, ErrorResult& aError) // Get the calling location. uint32_t lineNum = 0; - const char *fileName; nsAutoString fileNameString; - if (nsJSUtils::GetCallingLocation(aCx, &fileName, &lineNum)) { - AppendUTF8toUTF16(fileName, fileNameString); - } else { + if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum)) { fileNameString.AssignLiteral("unknown"); } @@ -233,10 +230,7 @@ nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx, } // Get the calling location. - const char *filename; - if (nsJSUtils::GetCallingLocation(aCx, &filename, &mLineNo)) { - mFileName.Assign(filename); - } + nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo); } nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler() @@ -353,10 +347,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval, AssignJSFlatString(mExpr, expr); // Get the calling location. - const char *filename; - if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) { - mFileName.Assign(filename); - } + nsJSUtils::GetCallingLocation(cx, mFileName, &mLineNo); } else if (funobj) { *aAllowEval = true; diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 2edde34a4f2..6b9c2340ad6 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -34,19 +34,28 @@ using namespace mozilla::dom; bool -nsJSUtils::GetCallingLocation(JSContext* aContext, const char* *aFilename, +nsJSUtils::GetCallingLocation(JSContext* aContext, nsACString& aFilename, uint32_t* aLineno) { JS::AutoFilename filename; - unsigned lineno = 0; - - if (!JS::DescribeScriptedCaller(aContext, &filename, &lineno)) { + if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno)) { return false; } - *aFilename = filename.get(); - *aLineno = lineno; + aFilename.Assign(filename.get()); + return true; +} +bool +nsJSUtils::GetCallingLocation(JSContext* aContext, nsAString& aFilename, + uint32_t* aLineno) +{ + JS::AutoFilename filename; + if (!JS::DescribeScriptedCaller(aContext, &filename, aLineno)) { + return false; + } + + aFilename.Assign(NS_ConvertUTF8toUTF16(filename.get())); return true; } diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index 0074254b1db..257ad344bd1 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -32,7 +32,9 @@ class Element; class nsJSUtils { public: - static bool GetCallingLocation(JSContext* aContext, const char* *aFilename, + static bool GetCallingLocation(JSContext* aContext, nsACString& aFilename, + uint32_t* aLineno); + static bool GetCallingLocation(JSContext* aContext, nsAString& aFilename, uint32_t* aLineno); static nsIScriptGlobalObject *GetStaticScriptGlobal(JSObject* aObj); diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp index 0c104de4199..b425a95ce5c 100644 --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -324,6 +324,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, // if aDeep is true, deal with aNode's children (and recurse into their // attributes and children). + nsAutoScriptBlocker scriptBlocker; AutoJSContext cx; nsresult rv; diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index a1c4068aa92..93cdbc8e458 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -1199,6 +1199,12 @@ nsObjectLoadingContent::GetFrameLoader() return loader.forget(); } +NS_IMETHODIMP +nsObjectLoadingContent::SetIsPrerendered() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader) { diff --git a/dom/base/test/TestCSPParser.cpp b/dom/base/test/TestCSPParser.cpp index 745c975a52b..27ed7849ca3 100644 --- a/dom/base/test/TestCSPParser.cpp +++ b/dom/base/test/TestCSPParser.cpp @@ -366,6 +366,10 @@ nsresult TestPaths() { "connect-src http://www.example.com/foo;sessionid=12,34" }, { "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@", "connect-src http://test.com/pathincludingaz19-._~!$&'()*+=:@" }, + { "script-src http://www.example.com:88/.js", + "script-src http://www.example.com:88/.js" }, + { "script-src https://foo.com/_abc/abc_/_/_a_b_c_", + "script-src https://foo.com/_abc/abc_/_/_a_b_c_" } }; uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest); @@ -490,8 +494,6 @@ nsresult TestPoliciesWithInvalidSrc() { "script-src 'none'" }, { "script-src http://www.example.com:88//path-1", "script-src 'none'" }, - { "script-src http://www.example.com:88/.js", - "script-src 'none'" }, { "script-src http://www.example.com:88.js", "script-src 'none'" }, { "script-src http://www.example.com:*.js", diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index 7b5f7b8ad0c..c31707c0428 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -92,7 +92,10 @@ public: protected: JS::Rooted mGlobalJSObject; JSContext* mCx; - mutable nsISupports* mGlobalObject; + mutable nsISupports* MOZ_UNSAFE_REF("Valid because GlobalObject is a stack " + "class, and mGlobalObject points to the " + "global, so it won't be destroyed as long " + "as GlobalObject lives on the stack") mGlobalObject; }; // Class for representing optional arguments. diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 01707809e30..69b0e6686b4 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1613,7 +1613,7 @@ NativePropertyHooks sWorkerNativePropertyHooks = { bool GetPropertyOnPrototype(JSContext* cx, JS::Handle proxy, JS::Handle id, bool* found, - JS::Value* vp) + JS::MutableHandle vp) { JS::Rooted proto(cx); if (!js::GetObjectProto(cx, proxy, &proto)) { @@ -1624,33 +1624,31 @@ GetPropertyOnPrototype(JSContext* cx, JS::Handle proxy, return true; } - bool hasProp; - if (!JS_HasPropertyById(cx, proto, id, &hasProp)) { + if (!JS_HasPropertyById(cx, proto, id, found)) { return false; } - *found = hasProp; - if (!hasProp || !vp) { + if (!*found) { return true; } - JS::Rooted value(cx); - if (!JS_ForwardGetPropertyTo(cx, proto, id, proxy, &value)) { - return false; - } - - *vp = value; - return true; + return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp); } bool HasPropertyOnPrototype(JSContext* cx, JS::Handle proxy, - JS::Handle id) + JS::Handle id, bool* has) { - bool found; - // We ignore an error from GetPropertyOnPrototype. We pass nullptr - // for vp so that GetPropertyOnPrototype won't actually do a get. - return !GetPropertyOnPrototype(cx, proxy, id, &found, nullptr) || found; + JS::Rooted proto(cx); + if (!js::GetObjectProto(cx, proxy, &proto)) { + return false; + } + if (!proto) { + *has = false; + return true; + } + + return JS_HasPropertyById(cx, proto, id, has); } bool @@ -1670,7 +1668,16 @@ AppendNamedPropertyIds(JSContext* cx, JS::Handle proxy, return false; } - if (shadowPrototypeProperties || !HasPropertyOnPrototype(cx, proxy, id)) { + bool shouldAppend = shadowPrototypeProperties; + if (!shouldAppend) { + bool has; + if (!HasPropertyOnPrototype(cx, proxy, id, &has)) { + return false; + } + shouldAppend = !has; + } + + if (shouldAppend) { if (!props.append(id)) { return false; } diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 7a228bfa07e..f886b76eaf5 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1810,16 +1810,15 @@ ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); bool ThrowConstructorWithoutNew(JSContext* cx, const char* name); -// vp is allowed to be null; in that case no get will be attempted, -// and *found will simply indicate whether the property exists. bool GetPropertyOnPrototype(JSContext* cx, JS::Handle proxy, JS::Handle id, bool* found, - JS::Value* vp); + JS::MutableHandle vp); +// bool HasPropertyOnPrototype(JSContext* cx, JS::Handle proxy, - JS::Handle id); + JS::Handle id, bool* has); // Append the property names in "names" to "props". If diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index efaf50e581a..d5d677f52a1 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -10104,15 +10104,41 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): "return true;\n" % (readonly, enumerable)) templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()', 'obj': 'proxy', 'successCode': fillDescriptor} - condition = "!HasPropertyOnPrototype(cx, proxy, id)" + + computeCondition = dedent(""" + bool hasOnProto; + if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { + return false; + } + callNamedGetter = !hasOnProto; + """) if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): - condition = "(!isXray || %s)" % condition - condition = "!ignoreNamedProps && " + condition + computeCondition = fill(""" + if (!isXray) { + callNamedGetter = true; + } else { + $*{hasOnProto} + } + """, + hasOnProto=computeCondition) + + outerCondition = "!ignoreNamedProps" if self.descriptor.supportsIndexedProperties(): - condition = "!IsArrayIndex(index) && " + condition - namedGet = (CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues), - condition).define() + - "\n") + outerCondition = "!IsArrayIndex(index) && " + outerCondition + + namedGet = fill(""" + bool callNamedGetter = false; + if (${outerCondition}) { + $*{computeCondition} + } + if (callNamedGetter) { + $*{namedGetCode} + } + """, + outerCondition=outerCondition, + computeCondition=computeCondition, + namedGetCode=CGProxyNamedGetter(self.descriptor, templateValues).define()) + namedGet += "\n" else: namedGet = "" @@ -10345,8 +10371,16 @@ class CGDOMJSProxyHandler_delete(ClassMethod): """, namedBody=namedBody) if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): - delete = CGIfWrapper(CGGeneric(delete), - "!HasPropertyOnPrototype(cx, proxy, id)").define() + delete = fill(""" + bool hasOnProto; + if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { + return false; + } + if (!hasOnProto) { + $*{delete} + } + """, + delete=delete) delete += dedent(""" @@ -10485,8 +10519,17 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod): """, presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define()) if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): - named = CGIfWrapper(CGGeneric(named + "return true;\n"), - "!HasPropertyOnPrototype(cx, proxy, id)").define() + named = fill(""" + bool hasOnProto; + if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) { + return false; + } + if (!hasOnProto) { + $*{protoLacksProperty} + return true; + } + """, + protoLacksProperty=named) named += "*bp = false;\n" else: named += "\n" @@ -10593,7 +10636,7 @@ class CGDOMJSProxyHandler_get(ClassMethod): getOnPrototype = dedent(""" bool foundOnPrototype; - if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) { + if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp)) { return false; } diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 80ca3fe1c88..518b9ec91c3 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -146,15 +146,22 @@ static const double THRESHOLD_LOW_PLAYBACKRATE_AUDIO = 0.5; // start playing afterward, so we need to stay alive. // 4) If autoplay could start playback in this element (if we got enough data), // then we need to stay alive. -// 5) if the element is currently loading and not suspended, -// script might be waiting for progress events or a 'suspend' event, -// so we need to stay alive. If we're already suspended then (all other -// conditions being met) it's OK to just disappear without firing any more -// events, since we have the freedom to remain suspended indefinitely. Note +// 5) if the element is currently loading, not suspended, and its source is +// not a MediaSource, then script might be waiting for progress events or a +// 'stalled' or 'suspend' event, so we need to stay alive. +// If we're already suspended then (all other conditions being met), +// it's OK to just disappear without firing any more events, +// since we have the freedom to remain suspended indefinitely. Note // that we could use this 'suspended' loophole to garbage-collect a suspended // element in case 4 even if it had 'autoplay' set, but we choose not to. // If someone throws away all references to a loading 'autoplay' element // sound should still eventually play. +// 6) If the source is a MediaSource, most loading events will not fire unless +// appendBuffer() is called on a SourceBuffer, in which case something is +// already referencing the SourceBuffer, which keeps the associated media +// element alive. Further, a MediaSource will never time out the resource +// fetch, and so should not keep the media element alive if it is +// unreferenced. A pending 'stalled' event keeps the media element alive. // // Media elements owned by inactive documents (i.e. documents not contained in any // document viewer) should never hold a self-reference because none of the @@ -684,7 +691,7 @@ void HTMLMediaElement::AbortExistingLoads() DispatchAsyncEvent(NS_LITERAL_STRING("emptied")); } - // We may have changed mPaused, mAutoplaying, mNetworkState and other + // We may have changed mPaused, mAutoplaying, and other // things which can affect AddRemoveSelfReference AddRemoveSelfReference(); @@ -699,7 +706,6 @@ void HTMLMediaElement::NoSupportedMediaSourceError() mError = new MediaError(this, nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE); DispatchAsyncEvent(NS_LITERAL_STRING("error")); - // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called ChangeDelayLoadStatus(false); } @@ -814,7 +820,6 @@ void HTMLMediaElement::SelectResource() // The media element has neither a src attribute nor any source // element children, abort the load. ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); - // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called ChangeDelayLoadStatus(false); return; } @@ -822,8 +827,6 @@ void HTMLMediaElement::SelectResource() ChangeDelayLoadStatus(true); ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); - // Load event was delayed, and still is, so no need to call - // AddRemoveSelfReference, since it must still be held DispatchAsyncEvent(NS_LITERAL_STRING("loadstart")); // Delay setting mIsRunningSeletResource until after UpdatePreloadAction @@ -2133,7 +2136,6 @@ HTMLMediaElement::ResetConnectionState() FireTimeUpdate(false); DispatchAsyncEvent(NS_LITERAL_STRING("ended")); ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); - AddRemoveSelfReference(); ChangeDelayLoadStatus(false); ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); } @@ -2858,7 +2860,6 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) DispatchAsyncEvent(NS_LITERAL_STRING("durationchange")); DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata")); ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); - AddRemoveSelfReference(); // FirstFrameLoaded() will be called when the stream has current data. } @@ -3010,7 +3011,6 @@ void HTMLMediaElement::Error(uint16_t aErrorCode) } else { ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); } - AddRemoveSelfReference(); ChangeDelayLoadStatus(false); } @@ -3084,7 +3084,6 @@ void HTMLMediaElement::DownloadSuspended() } if (mBegun) { ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE); - AddRemoveSelfReference(); } } @@ -3092,7 +3091,6 @@ void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading) { if (mBegun || aForceNetworkLoading) { ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING); - AddRemoveSelfReference(); } } @@ -3145,6 +3143,8 @@ void HTMLMediaElement::CheckProgress(bool aHaveNewProgress) // is more progress. StopProgress(); } + + AddRemoveSelfReference(); } /* static */ @@ -3358,6 +3358,9 @@ void HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState) // Fire 'suspend' event when entering NETWORK_IDLE and no error presented. DispatchAsyncEvent(NS_LITERAL_STRING("suspend")); } + + // Changing mNetworkState affects AddRemoveSelfReference(). + AddRemoveSelfReference(); } bool HTMLMediaElement::CanActivateAutoplay() @@ -3628,7 +3631,8 @@ void HTMLMediaElement::AddRemoveSelfReference() (!mPaused && mSrcStream && !mSrcStream->IsFinished()) || (mDecoder && mDecoder->IsSeeking()) || CanActivateAutoplay() || - mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING); + (mMediaSource ? mProgressTimer : + mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING)); if (needSelfReference != mHasSelfReference) { mHasSelfReference = needSelfReference; diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index ce2fe2639ed..3c5a28f1425 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -168,16 +168,7 @@ IDBRequest::CaptureCaller(nsAString& aFilename, uint32_t* aLineNo) MOZ_ASSERT(aLineNo); ThreadsafeAutoJSContext cx; - - const char* filename = nullptr; - uint32_t lineNo = 0; - if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) { - *aLineNo = 0; - return; - } - - aFilename.Assign(NS_ConvertUTF8toUTF16(filename)); - *aLineNo = lineNo; + nsJSUtils::GetCallingLocation(cx, aFilename, aLineNo); } void diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index f616a69fa06..d2725d914c4 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -20,6 +20,7 @@ #include "mozilla/dom/VideoStreamTrack.h" #include "nsError.h" #include "nsIDocument.h" +#include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsMimeTypes.h" #include "nsProxyRelease.h" @@ -545,6 +546,33 @@ private: } } + bool Check3gppPermission() + { + nsCOMPtr doc = mRecorder->GetOwner()->GetExtantDoc(); + if (!doc) { + return false; + } + + uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED; + doc->NodePrincipal()->GetAppStatus(&appStatus); + + // Certified applications can always assign AUDIO_3GPP + if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { + return true; + } + + nsCOMPtr pm = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); + + if (!pm) { + return false; + } + + uint32_t perm = nsIPermissionManager::DENY_ACTION; + pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), "audio-capture:3gpp", &perm); + return perm == nsIPermissionManager::ALLOW_ACTION; + } + void InitEncoder(uint8_t aTrackTypes) { LOG(PR_LOG_DEBUG, ("Session.InitEncoder %p", this)); @@ -553,14 +581,8 @@ private: // Allocate encoder and bind with union stream. // At this stage, the API doesn't allow UA to choose the output mimeType format. - nsCOMPtr doc = mRecorder->GetOwner()->GetExtantDoc(); - uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED; - if (doc) { - doc->NodePrincipal()->GetAppStatus(&appStatus); - } - // Only allow certificated application can assign AUDIO_3GPP - if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED && - mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP)) { + // Make sure the application has permission to assign AUDIO_3GPP + if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) { mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP), aTrackTypes); } else { mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes); diff --git a/dom/media/MediaRecorder.h b/dom/media/MediaRecorder.h index fbfb3595a5f..dad6fdfe306 100644 --- a/dom/media/MediaRecorder.h +++ b/dom/media/MediaRecorder.h @@ -148,6 +148,8 @@ private: // Register MediaRecorder into Document to listen the activity changes. void RegisterActivityObserver(); void UnRegisterActivityObserver(); + + bool Check3gppPermission(); }; } diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index a41fe9eb254..5e6c08d37ed 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -312,9 +312,12 @@ MediaSource::Attach(MediaSourceDecoder* aDecoder) MOZ_ASSERT(NS_IsMainThread()); MSE_DEBUG("MediaSource(%p)::Attach(aDecoder=%p) owner=%p", this, aDecoder, aDecoder->GetOwner()); MOZ_ASSERT(aDecoder); + MOZ_ASSERT(aDecoder->GetOwner()); if (mReadyState != MediaSourceReadyState::Closed) { return false; } + MOZ_ASSERT(!mMediaElement); + mMediaElement = aDecoder->GetOwner()->GetMediaElement(); MOZ_ASSERT(!mDecoder); mDecoder = aDecoder; mDecoder->AttachMediaSource(this); @@ -335,6 +338,7 @@ MediaSource::Detach() } mDecoder->DetachMediaSource(); mDecoder = nullptr; + mMediaElement = nullptr; mFirstSourceBufferInitialized = false; SetReadyState(MediaSourceReadyState::Closed); if (mActiveSourceBuffers) { @@ -487,6 +491,7 @@ MediaSource::WrapObject(JSContext* aCx) } NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper, + mMediaElement, mSourceBuffers, mActiveSourceBuffers) NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper) diff --git a/dom/media/mediasource/MediaSource.h b/dom/media/mediasource/MediaSource.h index bba3e4c38c3..f2f1d6d70c4 100644 --- a/dom/media/mediasource/MediaSource.h +++ b/dom/media/mediasource/MediaSource.h @@ -128,6 +128,9 @@ private: nsRefPtr mActiveSourceBuffers; nsRefPtr mDecoder; + // Ensures the media element remains alive to dispatch progress and + // durationchanged events. + nsRefPtr mMediaElement; nsRefPtr mPrincipal; diff --git a/dom/media/tests/mochitest/dataChannel.js b/dom/media/tests/mochitest/dataChannel.js new file mode 100644 index 00000000000..b0259db7878 --- /dev/null +++ b/dom/media/tests/mochitest/dataChannel.js @@ -0,0 +1,230 @@ +/* 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/. */ + +function addInitialDataChannel(chain) { + chain.insertBefore('PC_LOCAL_CREATE_OFFER', [ + ['PC_LOCAL_CREATE_DATA_CHANNEL', + function (test) { + var channel = test.pcLocal.createDataChannel({}); + + is(channel.binaryType, "blob", channel + " is of binary type 'blob'"); + is(channel.readyState, "connecting", channel + " is in state: 'connecting'"); + + is(test.pcLocal.signalingState, STABLE, + "Create datachannel does not change signaling state"); + + test.next(); + } + ] + ]); + chain.insertAfter('PC_REMOTE_CREATE_ANSWER', [ + [ + 'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK', + function (test) { + test.waitForInitialDataChannel(test.pcLocal, function () { + ok(true, test.pcLocal + " dataChannels[0] switched to 'open'"); + }, + // At this point a timeout failure will be of no value + null); + test.next(); + } + ], + [ + 'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK', + function (test) { + test.waitForInitialDataChannel(test.pcRemote, function () { + ok(true, test.pcRemote + " dataChannels[0] switched to 'open'"); + }, + // At this point a timeout failure will be of no value + null); + test.next(); + } + ] + ]); + chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', [ + [ + 'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE', + function (test) { + test.waitForInitialDataChannel(test.pcLocal, function() { + test.next(); + }, function() { + ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'"); + //TODO: use stopAndExit() once bug 1019323 has landed + unexpectedEventAndFinish(this, 'timeout') + // to prevent test framework timeouts + test.next(); + }); + } + ], + [ + 'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE', + function (test) { + test.waitForInitialDataChannel(test.pcRemote, function() { + test.next(); + }, function() { + ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'"); + //TODO: use stopAndExit() once bug 1019323 has landed + unexpectedEventAndFinish(this, 'timeout'); + // to prevent test framework timeouts + test.next(); + }); + } + ] + ]); + chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS'); + chain.append([ + [ + 'SEND_MESSAGE', + function (test) { + var message = "Lorem ipsum dolor sit amet"; + + test.send(message, function (channel, data) { + is(data, message, "Message correctly transmitted from pcLocal to pcRemote."); + + test.next(); + }); + } + ], + [ + 'SEND_BLOB', + function (test) { + var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."]; + var blob = new Blob(contents, { "type" : "text/plain" }); + + test.send(blob, function (channel, data) { + ok(data instanceof Blob, "Received data is of instance Blob"); + is(data.size, blob.size, "Received data has the correct size."); + + getBlobContent(data, function (recv_contents) { + is(recv_contents, contents, "Received data has the correct content."); + + test.next(); + }); + }); + } + ], + [ + 'CREATE_SECOND_DATA_CHANNEL', + function (test) { + test.createDataChannel({ }, function (sourceChannel, targetChannel) { + is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'"); + is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); + + is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'"); + is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); + + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL', + function (test) { + var channels = test.pcRemote.dataChannels; + var message = "Lorem ipsum dolor sit amet"; + + test.send(message, function (channel, data) { + is(channels.indexOf(channel), channels.length - 1, "Last channel used"); + is(data, message, "Received message has the correct content."); + + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_FIRST_CHANNEL', + function (test) { + var message = "Message through 1st channel"; + var options = { + sourceChannel: test.pcLocal.dataChannels[0], + targetChannel: test.pcRemote.dataChannels[0] + }; + + test.send(message, function (channel, data) { + is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used"); + is(data, message, "Received message has the correct content."); + + test.next(); + }, options); + } + ], + [ + 'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL', + function (test) { + var message = "Return a message also through 1st channel"; + var options = { + sourceChannel: test.pcRemote.dataChannels[0], + targetChannel: test.pcLocal.dataChannels[0] + }; + + test.send(message, function (channel, data) { + is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used"); + is(data, message, "Return message has the correct content."); + + test.next(); + }, options); + } + ], + [ + 'CREATE_NEGOTIATED_DATA_CHANNEL', + function (test) { + var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false, + maxRetransmits:500}; + test.createDataChannel(options, function (sourceChannel2, targetChannel2) { + is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'"); + is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); + + is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'"); + is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); + + if (options.id != undefined) { + is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id); + } + else { + options.id = sourceChannel2.id; + } + var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime); + is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol); + is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable); + /* + These aren't exposed by IDL yet + is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered); + is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" + + sourceChannel2.maxRetransmits); + is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" + + sourceChannel2.maxRetransmitTime); + */ + + is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id); + is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol); + is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable); + /* + These aren't exposed by IDL yet + is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered); + is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" + + targetChannel2.maxRetransmits); + is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" + + targetChannel2.maxRetransmitTime); + */ + + test.next(); + }); + } + ], + [ + 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2', + function (test) { + var channels = test.pcRemote.dataChannels; + var message = "Lorem ipsum dolor sit amet"; + + test.send(message, function (channel, data) { + is(channels.indexOf(channel), channels.length - 1, "Last channel used"); + is(data, message, "Received message has the correct content."); + + test.next(); + }); + } + ] + ]); +} diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 45c3b87c7ad..587fe0d9124 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -4,6 +4,7 @@ skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' support-files = head.js constraints.js + dataChannel.js mediaStreamPlayback.js nonTrickleIce.js pc.js diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index 9a6cba86179..3777a58b4cd 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -584,7 +584,7 @@ function PeerConnectionTest(options) { * @param {Function} onSuccess * Callback to execute when the peer connection has been closed successfully */ -PeerConnectionTest.prototype.close = function PCT_close(onSuccess) { +PeerConnectionTest.prototype.closePC = function PCT_closePC(onSuccess) { info("Closing peer connections"); var self = this; @@ -655,6 +655,316 @@ PeerConnectionTest.prototype.close = function PCT_close(onSuccess) { closeEverything(); }; +/** + * Close the open data channels, followed by the underlying peer connection + * + * @param {Function} onSuccess + * Callback to execute when all connections have been closed + */ +PeerConnectionTest.prototype.close = function PCT_close(onSuccess) { + var self = this; + var pendingDcClose = [] + var closeTimeout = null; + + info("PeerConnectionTest.close() called"); + + function _closePeerConnection() { + info("Now closing PeerConnection"); + self.closePC.call(self, onSuccess); + } + + function _closePeerConnectionCallback(index) { + info("_closePeerConnection called with index " + index); + var pos = pendingDcClose.indexOf(index); + if (pos != -1) { + pendingDcClose.splice(pos, 1); + } + else { + info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose); + } + if (pendingDcClose.length === 0) { + clearTimeout(closeTimeout); + _closePeerConnection(); + } + } + + var myDataChannels = null; + if (self.pcLocal) { + myDataChannels = self.pcLocal.dataChannels; + } + else if (self.pcRemote) { + myDataChannels = self.pcRemote.dataChannels; + } + var length = myDataChannels.length; + for (var i = 0; i < length; i++) { + var dataChannel = myDataChannels[i]; + if (dataChannel.readyState !== "closed") { + pendingDcClose.push(i); + self.closeDataChannels(i, _closePeerConnectionCallback); + } + } + if (pendingDcClose.length === 0) { + _closePeerConnection(); + } + else { + closeTimeout = setTimeout(function() { + ok(false, "Failed to properly close data channels: " + + pendingDcClose); + _closePeerConnection(); + }, 60000); + } +}; + +/** + * Close the specified data channels + * + * @param {Number} index + * Index of the data channels to close on both sides + * @param {Function} onSuccess + * Callback to execute when the data channels has been closed + */ +PeerConnectionTest.prototype.closeDataChannels = function PCT_closeDataChannels(index, onSuccess) { + info("closeDataChannels called with index: " + index); + var localChannel = null; + if (this.pcLocal) { + localChannel = this.pcLocal.dataChannels[index]; + } + var remoteChannel = null; + if (this.pcRemote) { + remoteChannel = this.pcRemote.dataChannels[index]; + } + + var self = this; + var wait = false; + var pollingMode = false; + var everythingClosed = false; + var verifyInterval = null; + var remoteCloseTimer = null; + + function _allChannelsAreClosed() { + var ret = null; + if (localChannel) { + ret = (localChannel.readyState === "closed"); + } + if (remoteChannel) { + if (ret !== null) { + ret = (ret && (remoteChannel.readyState === "closed")); + } + else { + ret = (remoteChannel.readyState === "closed"); + } + } + return ret; + } + + function verifyClosedChannels() { + if (everythingClosed) { + // safety protection against events firing late + return; + } + if (_allChannelsAreClosed()) { + ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index); + if (remoteCloseTimer !== null) { + clearTimeout(remoteCloseTimer); + } + if (verifyInterval !== null) { + clearInterval(verifyInterval); + } + everythingClosed = true; + onSuccess(index); + } + else { + info("Still waiting for DataChannel closure"); + } + } + + if ((localChannel) && (localChannel.readyState !== "closed")) { + // in case of steeplechase there is no far end, so we can only poll + if (remoteChannel) { + remoteChannel.onclose = function () { + is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'"); + verifyClosedChannels(); + }; + } + else { + pollingMode = true; + verifyInterval = setInterval(verifyClosedChannels, 1000); + } + + localChannel.close(); + wait = true; + } + if ((remoteChannel) && (remoteChannel.readyState !== "closed")) { + if (localChannel) { + localChannel.onclose = function () { + is(localChannel.readyState, "closed", "localChannel is in state 'closed'"); + verifyClosedChannels(); + }; + + // Apparently we are running a local test which has both ends of the + // data channel locally available, so by default lets wait for the + // remoteChannel.onclose handler from above to confirm closure on both + // ends. + remoteCloseTimer = setTimeout(function() { + todo(false, "localChannel.close() did not resulted in close signal on remote side"); + remoteChannel.close(); + verifyClosedChannels(); + }, 30000); + } + else { + pollingMode = true; + verifyTimer = setInterval(verifyClosedChannels, 1000); + + remoteChannel.close(); + } + + wait = true; + } + + if (!wait) { + onSuccess(index); + } +}; + + +/** + * Wait for the initial data channel to get into the open state + * + * @param {PeerConnectionWrapper} peer + * The peer connection wrapper to run the command on + * @param {Function} onSuccess + * Callback when the creation was successful + */ +PeerConnectionTest.prototype.waitForInitialDataChannel = + function PCT_waitForInitialDataChannel(peer, onSuccess, onFailure) { + var dcConnectionTimeout = null; + var dcOpened = false; + + function dataChannelConnected(channel) { + // in case the switch statement below had called onSuccess already we + // don't want to call it again + if (!dcOpened) { + clearTimeout(dcConnectionTimeout); + is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'"); + dcOpened = true; + onSuccess(); + } else { + info("dataChannelConnected() called, but data channel was open already"); + } + } + + // TODO: drno: convert dataChannels into an object and make + // registerDataChannelOpenEvent a generic function + if (peer == this.pcLocal) { + peer.dataChannels[0].onopen = dataChannelConnected; + } else { + peer.registerDataChannelOpenEvents(dataChannelConnected); + } + + if (peer.dataChannels.length >= 1) { + // snapshot of the live value as it might change during test execution + const readyState = peer.dataChannels[0].readyState; + switch (readyState) { + case "open": { + is(readyState, "open", peer + " dataChannels[0] is already in state: 'open'"); + dcOpened = true; + onSuccess(); + break; + } + case "connecting": { + is(readyState, "connecting", peer + " dataChannels[0] is in state: 'connecting'"); + if (onFailure) { + dcConnectionTimeout = setTimeout(function () { + is(peer.dataChannels[0].readyState, "open", peer + " timed out while waiting for dataChannels[0] to open"); + onFailure(); + }, 60000); + } + break; + } + default: { + ok(false, "dataChannels[0] is in unexpected state " + readyState); + if (onFailure) { + onFailure() + } + } + } + } +}; + +/** + * Send data (message or blob) to the other peer + * + * @param {String|Blob} data + * Data to send to the other peer. For Blobs the MIME type will be lost. + * @param {Function} onSuccess + * Callback to execute when data has been sent + * @param {Object} [options={ }] + * Options to specify the data channels to be used + * @param {DataChannelWrapper} [options.sourceChannel=pcLocal.dataChannels[length - 1]] + * Data channel to use for sending the message + * @param {DataChannelWrapper} [options.targetChannel=pcRemote.dataChannels[length - 1]] + * Data channel to use for receiving the message + */ +PeerConnectionTest.prototype.send = function PCT_send(data, onSuccess, options) { + options = options || { }; + var source = options.sourceChannel || + this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1]; + var target = options.targetChannel || + this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1]; + + // Register event handler for the target channel + target.onmessage = function (recv_data) { + onSuccess(target, recv_data); + }; + + source.send(data); +}; + +/** + * Create a data channel + * + * @param {Dict} options + * Options for the data channel (see nsIPeerConnection) + * @param {Function} onSuccess + * Callback when the creation was successful + */ +PeerConnectionTest.prototype.createDataChannel = function DCT_createDataChannel(options, onSuccess) { + var localChannel = null; + var remoteChannel = null; + var self = this; + + // Method to synchronize all asynchronous events. + function check_next_test() { + if (localChannel && remoteChannel) { + onSuccess(localChannel, remoteChannel); + } + } + + if (!options.negotiated) { + // Register handlers for the remote peer + this.pcRemote.registerDataChannelOpenEvents(function (channel) { + remoteChannel = channel; + check_next_test(); + }); + } + + // Create the datachannel and handle the local 'onopen' event + this.pcLocal.createDataChannel(options, function (channel) { + localChannel = channel; + + if (options.negotiated) { + // externally negotiated - we need to open from both ends + options.id = options.id || channel.id; // allow for no id to let the impl choose + self.pcRemote.createDataChannel(options, function (channel) { + remoteChannel = channel; + check_next_test(); + }); + } else { + check_next_test(); + } + }); +}; + /** * Executes the next command. */ @@ -995,372 +1305,6 @@ PCT_getSignalingMessage(messageType, onMessage) { this.registerSignalingCallback(messageType, onMessage); } -/** - * This class handles tests for data channels. - * - * @constructor - * @param {object} [options={}] - * Optional options for the peer connection test - * @param {object} [options.commands=commandsDataChannel] - * Commands to run for the test - * @param {object} [options.config_local=undefined] - * Configuration for the local peer connection instance - * @param {object} [options.config_remote=undefined] - * Configuration for the remote peer connection instance. If not defined - * the configuration from the local instance will be used - */ -function DataChannelTest(options) { - options = options || { }; - options.commands = options.commands || commandsDataChannel; - - PeerConnectionTest.call(this, options); -} - -DataChannelTest.prototype = Object.create(PeerConnectionTest.prototype, { - close : { - /** - * Close the open data channels, followed by the underlying peer connection - * - * @param {Function} onSuccess - * Callback to execute when all connections have been closed - */ - value : function DCT_close(onSuccess) { - var self = this; - var pendingDcClose = [] - var closeTimeout = null; - - info("DataChannelTest.close() called"); - - function _closePeerConnection() { - info("DataChannelTest closing PeerConnection"); - PeerConnectionTest.prototype.close.call(self, onSuccess); - } - - function _closePeerConnectionCallback(index) { - info("_closePeerConnection called with index " + index); - var pos = pendingDcClose.indexOf(index); - if (pos != -1) { - pendingDcClose.splice(pos, 1); - } - else { - info("_closePeerConnection index " + index + " is missing from pendingDcClose: " + pendingDcClose); - } - if (pendingDcClose.length === 0) { - clearTimeout(closeTimeout); - _closePeerConnection(); - } - } - - var myDataChannels = null; - if (self.pcLocal) { - myDataChannels = self.pcLocal.dataChannels; - } - else if (self.pcRemote) { - myDataChannels = self.pcRemote.dataChannels; - } - var length = myDataChannels.length; - for (var i = 0; i < length; i++) { - var dataChannel = myDataChannels[i]; - if (dataChannel.readyState !== "closed") { - pendingDcClose.push(i); - self.closeDataChannels(i, _closePeerConnectionCallback); - } - } - if (pendingDcClose.length === 0) { - _closePeerConnection(); - } - else { - closeTimeout = setTimeout(function() { - ok(false, "Failed to properly close data channels: " + - pendingDcClose); - _closePeerConnection(); - }, 60000); - } - } - }, - - closeDataChannels : { - /** - * Close the specified data channels - * - * @param {Number} index - * Index of the data channels to close on both sides - * @param {Function} onSuccess - * Callback to execute when the data channels has been closed - */ - value : function DCT_closeDataChannels(index, onSuccess) { - info("_closeDataChannels called with index: " + index); - var localChannel = null; - if (this.pcLocal) { - localChannel = this.pcLocal.dataChannels[index]; - } - var remoteChannel = null; - if (this.pcRemote) { - remoteChannel = this.pcRemote.dataChannels[index]; - } - - var self = this; - var wait = false; - var pollingMode = false; - var everythingClosed = false; - var verifyInterval = null; - var remoteCloseTimer = null; - - function _allChannelsAreClosed() { - var ret = null; - if (localChannel) { - ret = (localChannel.readyState === "closed"); - } - if (remoteChannel) { - if (ret !== null) { - ret = (ret && (remoteChannel.readyState === "closed")); - } - else { - ret = (remoteChannel.readyState === "closed"); - } - } - return ret; - } - - function verifyClosedChannels() { - if (everythingClosed) { - // safety protection against events firing late - return; - } - if (_allChannelsAreClosed) { - ok(true, "DataChannel(s) have reached 'closed' state for data channel " + index); - if (remoteCloseTimer !== null) { - clearTimeout(remoteCloseTimer); - } - if (verifyInterval !== null) { - clearInterval(verifyInterval); - } - everythingClosed = true; - onSuccess(index); - } - else { - info("Still waiting for DataChannel closure"); - } - } - - if ((localChannel) && (localChannel.readyState !== "closed")) { - // in case of steeplechase there is no far end, so we can only poll - if (remoteChannel) { - remoteChannel.onclose = function () { - is(remoteChannel.readyState, "closed", "remoteChannel is in state 'closed'"); - verifyClosedChannels(); - }; - } - else { - pollingMode = true; - verifyInterval = setInterval(verifyClosedChannels, 1000); - } - - localChannel.close(); - wait = true; - } - if ((remoteChannel) && (remoteChannel.readyState !== "closed")) { - if (localChannel) { - localChannel.onclose = function () { - is(localChannel.readyState, "closed", "localChannel is in state 'closed'"); - verifyClosedChannels(); - }; - - // Apparently we are running a local test which has both ends of the - // data channel locally available, so by default lets wait for the - // remoteChannel.onclose handler from above to confirm closure on both - // ends. - remoteCloseTimer = setTimeout(function() { - todo(false, "localChannel.close() did not resulted in close signal on remote side"); - remoteChannel.close(); - verifyClosedChannels(); - }, 30000); - } - else { - pollingMode = true; - verifyTimer = setInterval(verifyClosedChannels, 1000); - - remoteChannel.close(); - } - - wait = true; - } - - if (!wait) { - onSuccess(index); - } - } - }, - - createDataChannel : { - /** - * Create a data channel - * - * @param {Dict} options - * Options for the data channel (see nsIPeerConnection) - * @param {Function} onSuccess - * Callback when the creation was successful - */ - value : function DCT_createDataChannel(options, onSuccess) { - var localChannel = null; - var remoteChannel = null; - var self = this; - - // Method to synchronize all asynchronous events. - function check_next_test() { - if (localChannel && remoteChannel) { - onSuccess(localChannel, remoteChannel); - } - } - - if (!options.negotiated) { - // Register handlers for the remote peer - this.pcRemote.registerDataChannelOpenEvents(function (channel) { - remoteChannel = channel; - check_next_test(); - }); - } - - // Create the datachannel and handle the local 'onopen' event - this.pcLocal.createDataChannel(options, function (channel) { - localChannel = channel; - - if (options.negotiated) { - // externally negotiated - we need to open from both ends - options.id = options.id || channel.id; // allow for no id to let the impl choose - self.pcRemote.createDataChannel(options, function (channel) { - remoteChannel = channel; - check_next_test(); - }); - } else { - check_next_test(); - } - }); - } - }, - - send : { - /** - * Send data (message or blob) to the other peer - * - * @param {String|Blob} data - * Data to send to the other peer. For Blobs the MIME type will be lost. - * @param {Function} onSuccess - * Callback to execute when data has been sent - * @param {Object} [options={ }] - * Options to specify the data channels to be used - * @param {DataChannelWrapper} [options.sourceChannel=pcLocal.dataChannels[length - 1]] - * Data channel to use for sending the message - * @param {DataChannelWrapper} [options.targetChannel=pcRemote.dataChannels[length - 1]] - * Data channel to use for receiving the message - */ - value : function DCT_send(data, onSuccess, options) { - options = options || { }; - var source = options.sourceChannel || - this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1]; - var target = options.targetChannel || - this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1]; - - // Register event handler for the target channel - target.onmessage = function (recv_data) { - onSuccess(target, recv_data); - }; - - source.send(data); - } - }, - - createOffer : { - value : function DCT_createOffer(peer, onSuccess) { - PeerConnectionTest.prototype.createOffer.call(this, peer, onSuccess); - } - }, - - setLocalDescription : { - /** - * Sets the local description for the specified peer connection instance - * and automatically handles the failure case. - * - * @param {PeerConnectionWrapper} peer - The peer connection wrapper to run the command on - * @param {mozRTCSessionDescription} desc - * Session description for the local description request - * @param {function} onSuccess - * Callback to execute if the local description was set successfully - */ - value : function DCT_setLocalDescription(peer, desc, state, onSuccess) { - PeerConnectionTest.prototype.setLocalDescription.call(this, peer, - desc, state, onSuccess); - - } - }, - - waitForInitialDataChannel : { - /** - * Wait for the initial data channel to get into the open state - * - * @param {PeerConnectionWrapper} peer - * The peer connection wrapper to run the command on - * @param {Function} onSuccess - * Callback when the creation was successful - */ - value : function DCT_waitForInitialDataChannel(peer, onSuccess, onFailure) { - var dcConnectionTimeout = null; - var dcOpened = false; - - function dataChannelConnected(channel) { - // in case the switch statement below had called onSuccess already we - // don't want to call it again - if (!dcOpened) { - clearTimeout(dcConnectionTimeout); - is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'"); - dcOpened = true; - onSuccess(); - } else { - info("dataChannelConnected() called, but data channel was open already"); - } - } - - // TODO: drno: convert dataChannels into an object and make - // registerDataChannelOpenEvent a generic function - if (peer == this.pcLocal) { - peer.dataChannels[0].onopen = dataChannelConnected; - } else { - peer.registerDataChannelOpenEvents(dataChannelConnected); - } - - if (peer.dataChannels.length >= 1) { - // snapshot of the live value as it might change during test execution - const readyState = peer.dataChannels[0].readyState; - switch (readyState) { - case "open": { - is(readyState, "open", peer + " dataChannels[0] is already in state: 'open'"); - dcOpened = true; - onSuccess(); - break; - } - case "connecting": { - is(readyState, "connecting", peer + " dataChannels[0] is in state: 'connecting'"); - if (onFailure) { - dcConnectionTimeout = setTimeout(function () { - is(peer.dataChannels[0].readyState, "open", peer + " timed out while waiting for dataChannels[0] to open"); - onFailure(); - }, 60000); - } - break; - } - default: { - ok(false, "dataChannels[0] is in unexpected state " + readyState); - if (onFailure) { - onFailure() - } - } - } - } - } - } - -}); /** * This class acts as a wrapper around a DataChannel instance. @@ -2574,6 +2518,7 @@ PeerConnectionWrapper.prototype = { "Spec and MapClass variant of RTCStatsReport enumeration agree"); var nin = numTracks(this._pc.getRemoteStreams()); var nout = numTracks(this._pc.getLocalStreams()); + var ndata = this.dataChannels.length; // TODO(Bug 957145): Restore stronger inboundrtp test once Bug 948249 is fixed //is(toNum(counters["inboundrtp"]), nin, "Have " + nin + " inboundrtp stat(s)"); @@ -2584,7 +2529,7 @@ PeerConnectionWrapper.prototype = { var numLocalCandidates = toNum(counters.localcandidate); var numRemoteCandidates = toNum(counters.remotecandidate); // If there are no tracks, there will be no stats either. - if (nin + nout > 0) { + if (nin + nout + ndata > 0) { ok(numLocalCandidates, "Have localcandidate stat(s)"); ok(numRemoteCandidates, "Have remotecandidate stat(s)"); } else { @@ -2648,7 +2593,7 @@ PeerConnectionWrapper.prototype = { * The SDP answer to check for SDP bundle support */ checkStatsIceConnections : function PCW_checkStatsIceConnections(stats, - offerConstraintsList, offerOptions, numDataTracks, answer) { + offerConstraintsList, offerOptions, answer) { var numIceConnections = 0; Object.keys(stats).forEach(function(key) { if ((stats[key].type === "candidatepair") && stats[key].selected) { @@ -2669,6 +2614,8 @@ PeerConnectionWrapper.prototype = { this.countVideoTracksInMediaConstraint(offerConstraintsList) || this.videoInOfferOptions(offerOptions); + var numDataTracks = this.dataChannels.length; + var numAudioVideoDataTracks = numAudioTracks + numVideoTracks + numDataTracks; info("expected audio + video + data tracks: " + numAudioVideoDataTracks); is(numAudioVideoDataTracks, numIceConnections, "stats ICE connections matches expected A/V tracks"); diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js index 2cd50819d10..eb0ff01545d 100644 --- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -535,7 +535,6 @@ var commandsPeerConnection = [ test.pcLocal.checkStatsIceConnections(stats, test._offer_constraints, test._offer_options, - 0, test.originalAnswer); test.next(); }); @@ -548,7 +547,6 @@ var commandsPeerConnection = [ test.pcRemote.checkStatsIceConnections(stats, test._offer_constraints, test._offer_options, - 0, test.originalAnswer); test.next(); }); @@ -724,626 +722,3 @@ var commandsPeerConnection = [ ] ]; - -/** - * Default list of commands to execute for a Datachannel test. - */ -var commandsDataChannel = [ - [ - 'PC_LOCAL_SETUP_ICE_LOGGER', - function (test) { - test.pcLocal.logIceConnectionState(); - test.next(); - } - ], - [ - 'PC_REMOTE_SETUP_ICE_LOGGER', - function (test) { - test.pcRemote.logIceConnectionState(); - test.next(); - } - ], - [ - 'PC_LOCAL_SETUP_SIGNALING_LOGGER', - function (test) { - test.pcLocal.logSignalingState(); - test.next(); - } - ], - [ - 'PC_REMOTE_SETUP_SIGNALING_LOGGER', - function (test) { - test.pcRemote.logSignalingState(); - test.next(); - } - ], - [ - 'PC_LOCAL_GUM', - function (test) { - test.pcLocal.getAllUserMedia(test.pcLocal.constraints, function () { - test.next(); - }); - } - ], - [ - 'PC_LOCAL_INITIAL_SIGNALINGSTATE', - function (test) { - is(test.pcLocal.signalingState, STABLE, - "Initial local signalingState is stable"); - test.next(); - } - ], - [ - 'PC_LOCAL_CHECK_INITIAL_ICE_STATE', - function (test) { - is(test.pcLocal.iceConnectionState, ICE_NEW, - "Initial local ICE connection state is 'new'"); - test.next(); - } - ], - [ - 'PC_REMOTE_GUM', - function (test) { - test.pcRemote.getAllUserMedia(test.pcRemote.constraints, function () { - test.next(); - }); - } - ], - [ - 'PC_REMOTE_INITIAL_SIGNALINGSTATE', - function (test) { - is(test.pcRemote.signalingState, STABLE, - "Initial remote signalingState is stable"); - test.next(); - } - ], - [ - 'PC_REMOTE_CHECK_INITIAL_ICE_STATE', - function (test) { - is(test.pcRemote.iceConnectionState, ICE_NEW, - "Initial remote ICE connection state is 'new'"); - test.next(); - } - ], - [ - 'PC_LOCAL_SETUP_ICE_HANDLER', - function (test) { - test.pcLocal.setupIceCandidateHandler(test); - test.next(); - } - ], - [ - 'PC_REMOTE_SETUP_ICE_HANDLER', - function (test) { - test.pcRemote.setupIceCandidateHandler(test); - test.next(); - } - ], - [ - 'PC_LOCAL_CREATE_DATA_CHANNEL', - function (test) { - var channel = test.pcLocal.createDataChannel({}); - - is(channel.binaryType, "blob", channel + " is of binary type 'blob'"); - is(channel.readyState, "connecting", channel + " is in state: 'connecting'"); - - is(test.pcLocal.signalingState, STABLE, - "Create datachannel does not change signaling state"); - - test.next(); - } - ], - [ - 'PC_LOCAL_CREATE_OFFER', - function (test) { - test.createOffer(test.pcLocal, function (offer) { - is(test.pcLocal.signalingState, STABLE, - "Local create offer does not change signaling state"); - ok(offer.sdp.contains("m=application"), - "m=application is contained in the SDP"); - test.next(); - }); - } - ], - [ - 'PC_LOCAL_STEEPLECHASE_SIGNAL_OFFER', - function (test) { - if (test.steeplechase) { - send_message({"type": "offer", - "offer": test.originalOffer, - "offer_constraints": test.pcLocal.constraints, - "offer_options": test.pcLocal.offerOptions}); - test._local_offer = test.originalOffer; - test._offer_constraints = test.pcLocal.constraints; - test._offer_options = test.pcLocal.offerOptions; - } - test.next(); - } - ], - [ - 'PC_LOCAL_SET_LOCAL_DESCRIPTION', - function (test) { - test.setLocalDescription(test.pcLocal, test.originalOffer, HAVE_LOCAL_OFFER, - function () { - is(test.pcLocal.signalingState, HAVE_LOCAL_OFFER, - "signalingState after local setLocalDescription is 'have-local-offer'"); - test.next(); - }); - } - ], - [ - 'PC_REMOTE_GET_OFFER', - function (test) { - if (!test.steeplechase) { - test._local_offer = test.originalOffer; - test._offer_constraints = test.pcLocal.constraints; - test._offer_options = test.pcLocal.offerOptions; - test.next(); - } else { - test.getSignalingMessage("offer", function (message) { - ok("offer" in message, "Got an offer message"); - test._local_offer = new mozRTCSessionDescription(message.offer); - test._offer_constraints = message.offer_constraints; - test._offer_options = message.offer_options; - test.next(); - }); - } - } - ], - [ - 'PC_REMOTE_SET_REMOTE_DESCRIPTION', - function (test) { - test.setRemoteDescription(test.pcRemote, test._local_offer, HAVE_REMOTE_OFFER, - function () { - is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER, - "signalingState after remote setRemoteDescription is 'have-remote-offer'"); - test.next(); - }); - } - ], - [ - 'PC_LOCAL_SANE_LOCAL_SDP', - function (test) { - test.pcLocal.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - function(trickle) { - test.pcLocal.localRequiresTrickleIce = trickle; - }); - test.next(); - } - ], - [ - 'PC_REMOTE_SANE_REMOTE_SDP', - function (test) { - test.pcRemote.verifySdp(test._local_offer, "offer", - test._offer_constraints, test._offer_options, - function (trickle) { - test.pcRemote.remoteRequiresTrickleIce = trickle; - }); - test.next(); - } - ], - [ - 'PC_REMOTE_CREATE_ANSWER', - function (test) { - test.createAnswer(test.pcRemote, function (answer) { - is(test.pcRemote.signalingState, HAVE_REMOTE_OFFER, - "Remote createAnswer does not change signaling state"); - ok(answer.sdp.contains("m=application"), - "m=application is contained in the SDP"); - if (test.steeplechase) { - send_message({"type":"answer", - "answer": test.originalAnswer, - "answer_constraints": test.pcRemote.constraints}); - test._remote_answer = test.pcRemote._last_answer; - test._answer_constraints = test.pcRemote.constraints; - } - test.next(); - }); - } - ], - [ - 'PC_LOCAL_SETUP_DATA_CHANNEL_CALLBACK', - function (test) { - test.waitForInitialDataChannel(test.pcLocal, function () { - ok(true, test.pcLocal + " dataChannels[0] switched to 'open'"); - }, - // At this point a timeout failure will be of no value - null); - test.next(); - } - ], - [ - 'PC_REMOTE_SETUP_DATA_CHANNEL_CALLBACK', - function (test) { - test.waitForInitialDataChannel(test.pcRemote, function () { - ok(true, test.pcRemote + " dataChannels[0] switched to 'open'"); - }, - // At this point a timeout failure will be of no value - null); - test.next(); - } - ], - [ - 'PC_REMOTE_SET_LOCAL_DESCRIPTION', - function (test) { - test.setLocalDescription(test.pcRemote, test.originalAnswer, STABLE, - function () { - is(test.pcRemote.signalingState, STABLE, - "signalingState after remote setLocalDescription is 'stable'"); - test.next(); - } - ); - } - ], - [ - 'PC_LOCAL_GET_ANSWER', - function (test) { - if (!test.steeplechase) { - test._remote_answer = test.originalAnswer; - test._answer_constraints = test.pcRemote.constraints; - test.next(); - } else { - test.getSignalingMessage("answer", function (message) { - ok("answer" in message, "Got an answer message"); - test._remote_answer = new mozRTCSessionDescription(message.answer); - test._answer_constraints = message.answer_constraints; - test.next(); - }); - } - } - ], - [ - 'PC_LOCAL_SET_REMOTE_DESCRIPTION', - function (test) { - test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE, - function () { - is(test.pcLocal.signalingState, STABLE, - "signalingState after local setRemoteDescription is 'stable'"); - test.next(); - } - ); - } - ], - [ - 'PC_REMOTE_SANE_LOCAL_SDP', - function (test) { - test.pcRemote.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - function (trickle) { - test.pcRemote.localRequiresTrickleIce = trickle; - }); - test.next(); - } - ], - [ - 'PC_LOCAL_SANE_REMOTE_SDP', - function (test) { - test.pcLocal.verifySdp(test._remote_answer, "answer", - test._offer_constraints, test._offer_options, - function (trickle) { - test.pcLocal.remoteRequiresTrickleIce = trickle; - }); - test.next(); - } - ], - [ - 'PC_LOCAL_WAIT_FOR_ICE_CONNECTED', - function (test) { - var myTest = test; - var myPc = myTest.pcLocal; - - function onIceConnectedSuccess () { - info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog); - ok(true, "pc_local: ICE switched to 'connected' state"); - myTest.next(); - }; - function onIceConnectedFailed () { - dumpSdp(myTest); - ok(false, "pc_local: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState); - myTest.next(); - }; - - if (myPc.isIceConnected()) { - info("pcLocal ICE connection state log: " + test.pcLocal.iceConnectionLog); - ok(true, "pc_local: ICE is in connected state"); - myTest.next(); - } else if (myPc.isIceConnectionPending()) { - myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed); - } else { - dumpSdp(myTest); - ok(false, "pc_local: ICE is already in bad state: " + myPc.iceConnectionState); - myTest.next(); - } - } - ], - [ - 'PC_LOCAL_VERIFY_ICE_GATHERING', - function (test) { - if (test.pcLocal.localRequiresTrickleIce) { - ok(test.pcLocal._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); - } - isnot(test.pcLocal._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); - test.next(); - } - ], - [ - 'PC_REMOTE_WAIT_FOR_ICE_CONNECTED', - function (test) { - var myTest = test; - var myPc = myTest.pcRemote; - - function onIceConnectedSuccess () { - info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog); - ok(true, "pc_remote: ICE switched to 'connected' state"); - myTest.next(); - }; - function onIceConnectedFailed () { - dumpSdp(myTest); - ok(false, "pc_remote: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState); - myTest.next(); - }; - - if (myPc.isIceConnected()) { - info("pcRemote ICE connection state log: " + test.pcRemote.iceConnectionLog); - ok(true, "pc_remote: ICE is in connected state"); - myTest.next(); - } else if (myPc.isIceConnectionPending()) { - myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed); - } else { - dumpSdp(myTest); - ok(false, "pc_remote: ICE is already in bad state: " + myPc.iceConnectionState); - myTest.next(); - } - } - ], - [ - 'PC_REMOTE_VERIFY_ICE_GATHERING', - function (test) { - if (test.pcRemote.localRequiresTrickleIce) { - ok(test.pcRemote._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); - } - isnot(test.pcRemote._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); - test.next(); - } - ], - [ - 'PC_LOCAL_VERIFY_DATA_CHANNEL_STATE', - function (test) { - test.waitForInitialDataChannel(test.pcLocal, function() { - test.next(); - }, function() { - ok(false, test.pcLocal + " initial dataChannels[0] failed to switch to 'open'"); - //TODO: use stopAndExit() once bug 1019323 has landed - unexpectedEventAndFinish(this, 'timeout') - // to prevent test framework timeouts - test.next(); - }); - } - ], - [ - 'PC_REMOTE_VERIFY_DATA_CHANNEL_STATE', - function (test) { - test.waitForInitialDataChannel(test.pcRemote, function() { - test.next(); - }, function() { - ok(false, test.pcRemote + " initial dataChannels[0] failed to switch to 'open'"); - //TODO: use stopAndExit() once bug 1019323 has landed - unexpectedEventAndFinish(this, 'timeout'); - // to prevent test framework timeouts - test.next(); - }); - } - ], - [ - 'PC_LOCAL_CHECK_MEDIA_TRACKS', - function (test) { - test.pcLocal.checkMediaTracks(test._answer_constraints, function () { - test.next(); - }); - } - ], - [ - 'PC_REMOTE_CHECK_MEDIA_TRACKS', - function (test) { - test.pcRemote.checkMediaTracks(test._offer_constraints, function () { - test.next(); - }); - } - ], - [ - 'PC_LOCAL_CHECK_MEDIA_FLOW_PRESENT', - function (test) { - test.pcLocal.checkMediaFlowPresent(function () { - test.next(); - }); - } - ], - [ - 'PC_REMOTE_CHECK_MEDIA_FLOW_PRESENT', - function (test) { - test.pcRemote.checkMediaFlowPresent(function () { - test.next(); - }); - } - ], - [ - 'PC_LOCAL_CHECK_ICE_CONNECTIONS', - function (test) { - test.pcLocal.getStats(null, function(stats) { - test.pcLocal.checkStatsIceConnections(stats, - test._offer_constraints, - test._offer_options, - 1, - test.originalAnswer); - test.next(); - }); - } - ], - [ - 'PC_REMOTE_CHECK_ICE_CONNECTIONS', - function (test) { - test.pcRemote.getStats(null, function(stats) { - test.pcRemote.checkStatsIceConnections(stats, - test._offer_constraints, - test._offer_options, - 1, - test.originalAnswer); - test.next(); - }); - } - ], - [ - 'SEND_MESSAGE', - function (test) { - var message = "Lorem ipsum dolor sit amet"; - - test.send(message, function (channel, data) { - is(data, message, "Message correctly transmitted from pcLocal to pcRemote."); - - test.next(); - }); - } - ], - [ - 'SEND_BLOB', - function (test) { - var contents = ["At vero eos et accusam et justo duo dolores et ea rebum."]; - var blob = new Blob(contents, { "type" : "text/plain" }); - - test.send(blob, function (channel, data) { - ok(data instanceof Blob, "Received data is of instance Blob"); - is(data.size, blob.size, "Received data has the correct size."); - - getBlobContent(data, function (recv_contents) { - is(recv_contents, contents, "Received data has the correct content."); - - test.next(); - }); - }); - } - ], - [ - 'CREATE_SECOND_DATA_CHANNEL', - function (test) { - test.createDataChannel({ }, function (sourceChannel, targetChannel) { - is(sourceChannel.readyState, "open", sourceChannel + " is in state: 'open'"); - is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); - - is(targetChannel.binaryType, "blob", targetChannel + " is of binary type 'blob'"); - is(targetChannel.readyState, "open", targetChannel + " is in state: 'open'"); - - test.next(); - }); - } - ], - [ - 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL', - function (test) { - var channels = test.pcRemote.dataChannels; - var message = "Lorem ipsum dolor sit amet"; - - test.send(message, function (channel, data) { - is(channels.indexOf(channel), channels.length - 1, "Last channel used"); - is(data, message, "Received message has the correct content."); - - test.next(); - }); - } - ], - [ - 'SEND_MESSAGE_THROUGH_FIRST_CHANNEL', - function (test) { - var message = "Message through 1st channel"; - var options = { - sourceChannel: test.pcLocal.dataChannels[0], - targetChannel: test.pcRemote.dataChannels[0] - }; - - test.send(message, function (channel, data) { - is(test.pcRemote.dataChannels.indexOf(channel), 0, "1st channel used"); - is(data, message, "Received message has the correct content."); - - test.next(); - }, options); - } - ], - [ - 'SEND_MESSAGE_BACK_THROUGH_FIRST_CHANNEL', - function (test) { - var message = "Return a message also through 1st channel"; - var options = { - sourceChannel: test.pcRemote.dataChannels[0], - targetChannel: test.pcLocal.dataChannels[0] - }; - - test.send(message, function (channel, data) { - is(test.pcLocal.dataChannels.indexOf(channel), 0, "1st channel used"); - is(data, message, "Return message has the correct content."); - - test.next(); - }, options); - } - ], - [ - 'CREATE_NEGOTIATED_DATA_CHANNEL', - function (test) { - var options = {negotiated:true, id: 5, protocol:"foo/bar", ordered:false, - maxRetransmits:500}; - test.createDataChannel(options, function (sourceChannel2, targetChannel2) { - is(sourceChannel2.readyState, "open", sourceChannel2 + " is in state: 'open'"); - is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); - - is(targetChannel2.binaryType, "blob", targetChannel2 + " is of binary type 'blob'"); - is(targetChannel2.readyState, "open", targetChannel2 + " is in state: 'open'"); - - if (options.id != undefined) { - is(sourceChannel2.id, options.id, sourceChannel2 + " id is:" + sourceChannel2.id); - } - else { - options.id = sourceChannel2.id; - } - var reliable = !options.ordered ? false : (options.maxRetransmits || options.maxRetransmitTime); - is(sourceChannel2.protocol, options.protocol, sourceChannel2 + " protocol is:" + sourceChannel2.protocol); - is(sourceChannel2.reliable, reliable, sourceChannel2 + " reliable is:" + sourceChannel2.reliable); -/* - These aren't exposed by IDL yet - is(sourceChannel2.ordered, options.ordered, sourceChannel2 + " ordered is:" + sourceChannel2.ordered); - is(sourceChannel2.maxRetransmits, options.maxRetransmits, sourceChannel2 + " maxRetransmits is:" + - sourceChannel2.maxRetransmits); - is(sourceChannel2.maxRetransmitTime, options.maxRetransmitTime, sourceChannel2 + " maxRetransmitTime is:" + - sourceChannel2.maxRetransmitTime); -*/ - - is(targetChannel2.id, options.id, targetChannel2 + " id is:" + targetChannel2.id); - is(targetChannel2.protocol, options.protocol, targetChannel2 + " protocol is:" + targetChannel2.protocol); - is(targetChannel2.reliable, reliable, targetChannel2 + " reliable is:" + targetChannel2.reliable); -/* - These aren't exposed by IDL yet - is(targetChannel2.ordered, options.ordered, targetChannel2 + " ordered is:" + targetChannel2.ordered); - is(targetChannel2.maxRetransmits, options.maxRetransmits, targetChannel2 + " maxRetransmits is:" + - targetChannel2.maxRetransmits); - is(targetChannel2.maxRetransmitTime, options.maxRetransmitTime, targetChannel2 + " maxRetransmitTime is:" + - targetChannel2.maxRetransmitTime); -*/ - - test.next(); - }); - } - ], - [ - 'SEND_MESSAGE_THROUGH_LAST_OPENED_CHANNEL2', - function (test) { - var channels = test.pcRemote.dataChannels; - var message = "Lorem ipsum dolor sit amet"; - - test.send(message, function (channel, data) { - is(channels.indexOf(channel), channels.length - 1, "Last channel used"); - is(data, message, "Received message has the correct content."); - - test.next(); - }); - } - ] -]; - diff --git a/dom/media/tests/mochitest/test_dataChannel_basicAudio.html b/dom/media/tests/mochitest/test_dataChannel_basicAudio.html index f1306105da4..94829d78e30 100644 --- a/dom/media/tests/mochitest/test_dataChannel_basicAudio.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudio.html @@ -3,6 +3,7 @@ + @@ -17,8 +18,9 @@ }); var test; - runNetworkTest(function () { - test = new DataChannelTest(); + runNetworkTest(function (options) { + test = new PeerConnectionTest(options); + addInitialDataChannel(test.chain); test.setMediaConstraints([{audio: true}], [{audio: true}]); test.run(); }); diff --git a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html index 19f11868401..f79e5f285a3 100644 --- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html @@ -3,6 +3,7 @@ + @@ -17,8 +18,9 @@ }); var test; - runNetworkTest(function () { - test = new DataChannelTest(); + runNetworkTest(function (options) { + test = new PeerConnectionTest(options); + addInitialDataChannel(test.chain); test.setMediaConstraints([{audio: true}, {video: true}], [{audio: true}, {video: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html index 26c14e90dbc..f3b83501f50 100644 --- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html @@ -3,6 +3,7 @@ + @@ -17,8 +18,9 @@ }); var test; - runNetworkTest(function () { - test = new DataChannelTest(); + runNetworkTest(function (options) { + test = new PeerConnectionTest(options); + addInitialDataChannel(test.chain); test.setMediaConstraints([{audio: true, video: true}], [{audio: true, video: true}]); test.run(); diff --git a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html index 8d6ee5e2dc2..3756fa8a12a 100644 --- a/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html +++ b/dom/media/tests/mochitest/test_dataChannel_basicAudioVideoNoBundle.html @@ -3,6 +3,7 @@ + @@ -11,8 +12,6 @@
 
+  
   
   
   
@@ -17,8 +18,9 @@
   });
 
   var test;
-  runNetworkTest(function () {
-    test = new DataChannelTest();
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    addInitialDataChannel(test.chain);
     test.run();
   });
 
diff --git a/dom/media/tests/mochitest/test_dataChannel_basicVideo.html b/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
index fc8692b68e8..f7d97acbc7b 100644
--- a/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
+++ b/dom/media/tests/mochitest/test_dataChannel_basicVideo.html
@@ -3,6 +3,7 @@
 
   
   
+  
   
   
   
@@ -17,8 +18,9 @@
   });
 
   var test;
-  runNetworkTest(function () {
-    test = new DataChannelTest();
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    addInitialDataChannel(test.chain);
     test.setMediaConstraints([{video: true}], [{video: true}]);
     test.run();
   });
diff --git a/dom/media/tests/mochitest/test_dataChannel_bug1013809.html b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
index 3f64b1e16c3..616f70a16e4 100644
--- a/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
+++ b/dom/media/tests/mochitest/test_dataChannel_bug1013809.html
@@ -3,6 +3,7 @@
 
   
   
+  
   
   
   
@@ -17,8 +18,9 @@
   });
 
   var test;
-  runNetworkTest(function () {
-    test = new DataChannelTest();
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    addInitialDataChannel(test.chain);
     var sld = test.chain.remove("PC_REMOTE_SET_LOCAL_DESCRIPTION");
     test.chain.insertAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION", sld);
     test.setMediaConstraints([{audio: true}], [{audio: true}]);
diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp
index a65c3b12694..b02e6289834 100644
--- a/dom/plugins/base/android/ANPAudio.cpp
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -260,7 +260,7 @@ anp_audio_newTrack(uint32_t sampleRate,    // sampling rate in Hz
 
   if (autoFrame.CheckForException() || obj == nullptr) {
     jenv->DeleteGlobalRef(s->at_class);
-    free(s);
+    delete s;
     return nullptr;
   }
 
@@ -268,7 +268,7 @@ anp_audio_newTrack(uint32_t sampleRate,    // sampling rate in Hz
 
   if (autoFrame.CheckForException() || state == STATE_UNINITIALIZED) {
     jenv->DeleteGlobalRef(s->at_class);
-    free(s);
+    delete s;
     return nullptr;
   }
 
@@ -310,7 +310,7 @@ anp_audio_start(ANPAudioTrack* s)
 
   if (autoFrame.CheckForException()) {
     jenv->DeleteGlobalRef(s->at_class);
-    free(s);
+    delete s;
     return;
   }
 
diff --git a/dom/plugins/ipc/BrowserStreamChild.cpp b/dom/plugins/ipc/BrowserStreamChild.cpp
index 4a3ff033fab..0f50a4e99c8 100644
--- a/dom/plugins/ipc/BrowserStreamChild.cpp
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -62,7 +62,10 @@ BrowserStreamChild::StreamConstructed(
     &mStream, seekable, stype);
   if (rv != NPERR_NO_ERROR) {
     mState = DELETING;
-    mStreamNotify = nullptr;
+    if (mStreamNotify) {
+      mStreamNotify->SetAssociatedStream(nullptr);
+      mStreamNotify = nullptr;
+    }
   }
   else {
     mState = ALIVE;
diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp
index 1adc1a1935f..d9711646988 100644
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -2440,7 +2440,6 @@ StreamNotifyChild::ActorDestroy(ActorDestroyReason why)
 void
 StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs)
 {
-    NS_ASSERTION(bs, "Shouldn't be null");
     NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?");
 
     mBrowserStream = bs;
diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp
index e0f1c9facc1..daf7df57a6a 100755
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -35,8 +35,9 @@
 #include "nsPluginTags.h"
 
 #ifdef XP_WIN
-#include "PluginHangUIParent.h"
 #include "mozilla/widget/AudioSession.h"
+#include "nsWindowsHelpers.h"
+#include "PluginHangUIParent.h"
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -2481,7 +2482,7 @@ PluginModuleChromeParent::InitializeInjector()
         return;
 
     TimeStamp th32Start = TimeStamp::Now();
-    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    nsAutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
     if (INVALID_HANDLE_VALUE == snapshot)
         return;
     TimeStamp th32End = TimeStamp::Now();
diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp
index 03508fe69bf..7bb76d2b6ac 100644
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -444,7 +444,7 @@ nsCSPParser::path(nsCSPHostSrc* aCspHost)
   }
   // path can begin with "/" but not "//"
   // see http://tools.ietf.org/html/rfc3986#section-3.3
-  if (!hostChar()) {
+  if (peek(SLASH)) {
     const char16_t* params[] = { mCurToken.get() };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
                              params, ArrayLength(params));
diff --git a/dom/system/gonk/NetworkUtils.cpp b/dom/system/gonk/NetworkUtils.cpp
index 4e224cf13f1..d307fd2b1a3 100644
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -826,13 +826,16 @@ void NetworkUtils::startTethering(CommandChain* aChain,
   if (aResult.mResultReason.Find("started") != kNotFound) {
     snprintf(command, MAX_COMMAND_SIZE - 1, "%s", DUMMY_COMMAND);
   } else {
-    snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s", GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp));
-
     // If usbStartIp/usbEndIp is not valid, don't append them since
     // the trailing white spaces will be parsed to extra empty args
     // See: http://androidxref.com/4.3_r2.1/xref/system/core/libsysutils/src/FrameworkListener.cpp#78
     if (!GET_FIELD(mUsbStartIp).IsEmpty() && !GET_FIELD(mUsbEndIp).IsEmpty()) {
-      snprintf(command, MAX_COMMAND_SIZE - 1, "%s %s %s", command, GET_CHAR(mUsbStartIp), GET_CHAR(mUsbEndIp));
+      snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s %s %s",
+               GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp),
+               GET_CHAR(mUsbStartIp),  GET_CHAR(mUsbEndIp));
+    } else {
+      snprintf(command, MAX_COMMAND_SIZE - 1, "tether start %s %s",
+               GET_CHAR(mWifiStartIp), GET_CHAR(mWifiEndIp));
     }
   }
 
diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini
index 890c053ab42..78b337b96f8 100644
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -9,6 +9,7 @@ support-files =
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_clone_callbacks.html]
 [test_custom_element_clone_callbacks_extended.html]
+[test_custom_element_import_node_created_callback.html]
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_dest_insertion_points_shadow.html]
diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_import_node_created_callback.html b/dom/tests/mochitest/webcomponents/test_custom_element_import_node_created_callback.html
new file mode 100644
index 00000000000..f533b507c5a
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_import_node_created_callback.html
@@ -0,0 +1,34 @@
+
+
+
+
+  Test created callback order for imported custom element.
+  
+  
+
+
+
+Bug 1093680
+
+
+
diff --git a/dom/webidl/HTMLAppletElement.webidl b/dom/webidl/HTMLAppletElement.webidl
index f787d5a9d11..d75c42d15c2 100644
--- a/dom/webidl/HTMLAppletElement.webidl
+++ b/dom/webidl/HTMLAppletElement.webidl
@@ -12,7 +12,7 @@
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-applet-element
-[NeedResolve]
+[NeedResolve, UnsafeInPrerendering]
 interface HTMLAppletElement : HTMLElement {
   [Pure, SetterThrows]
            attribute DOMString align;
diff --git a/dom/webidl/HTMLObjectElement.webidl b/dom/webidl/HTMLObjectElement.webidl
index fe2318c38af..e3836e6d7ad 100644
--- a/dom/webidl/HTMLObjectElement.webidl
+++ b/dom/webidl/HTMLObjectElement.webidl
@@ -13,7 +13,7 @@
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-object-element
-[NeedResolve]
+[NeedResolve, UnsafeInPrerendering]
 interface HTMLObjectElement : HTMLElement {
   [Pure, SetterThrows]
            attribute DOMString data;
diff --git a/dom/webidl/MozNFC.webidl b/dom/webidl/MozNFC.webidl
index 2351148cd9f..1b4ed3e712e 100644
--- a/dom/webidl/MozNFC.webidl
+++ b/dom/webidl/MozNFC.webidl
@@ -73,7 +73,8 @@ interface MozNFCManager {
  NavigatorProperty="mozNfc",
  Func="Navigator::HasNFCSupport",
  CheckPermissions="nfc nfc-share",
- AvailableIn="PrivilegedApps"]
+ AvailableIn="PrivilegedApps",
+ UnsafeInPrerendering]
 interface MozNFC : EventTarget {
   /**
    * Indicate if NFC is enabled.
diff --git a/dom/webidl/MozWifiManager.webidl b/dom/webidl/MozWifiManager.webidl
index 1539e21adb5..baa39dd283f 100644
--- a/dom/webidl/MozWifiManager.webidl
+++ b/dom/webidl/MozWifiManager.webidl
@@ -126,7 +126,8 @@ dictionary IPConfiguration {
 
 [JSImplementation="@mozilla.org/wifimanager;1",
  NavigatorProperty="mozWifiManager",
- Func="Navigator::HasWifiManagerSupport"]
+ Func="Navigator::HasWifiManagerSupport",
+ UnsafeInPrerendering]
 interface MozWifiManager : EventTarget {
   /**
    * Turn on/off wifi functionality.
diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl
index 653facf8237..720b082e14d 100644
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -193,7 +193,7 @@ partial interface Navigator {
   readonly attribute boolean cookieEnabled;
   [Throws]
   readonly attribute DOMString buildID;
-  [Throws, CheckPermissions="power"]
+  [Throws, CheckPermissions="power", UnsafeInPrerendering]
   readonly attribute MozPowerManager mozPower;
 
   // WebKit/Blink/Trident/Presto support this.
@@ -240,7 +240,7 @@ partial interface Navigator {
    *
    * @param aTopic resource name
    */
-  [Throws, Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport"]
+  [Throws, Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport", UnsafeInPrerendering]
   MozWakeLock requestWakeLock(DOMString aTopic);
 };
 
@@ -344,7 +344,7 @@ partial interface Navigator {
 #ifdef MOZ_TIME_MANAGER
 // nsIDOMMozNavigatorTime
 partial interface Navigator {
-  [Throws, CheckPermissions="time"]
+  [Throws, CheckPermissions="time", UnsafeInPrerendering]
   readonly attribute MozTimeManager mozTime;
 };
 #endif // MOZ_TIME_MANAGER
diff --git a/dom/webidl/XULElement.webidl b/dom/webidl/XULElement.webidl
index 7d9772ebb1f..ff2af0e7484 100644
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -122,6 +122,9 @@ interface MozFrameLoaderOwner {
   [ChromeOnly]
   readonly attribute MozFrameLoader? frameLoader;
 
+  [ChromeOnly]
+  void setIsPrerendered();
+
   [ChromeOnly, Throws]
   void swapFrameLoaders(XULElement aOtherOwner);
 };
diff --git a/dom/wifi/WifiUtils.cpp b/dom/wifi/WifiUtils.cpp
index 140e32f4054..4cfbdf48481 100644
--- a/dom/wifi/WifiUtils.cpp
+++ b/dom/wifi/WifiUtils.cpp
@@ -13,7 +13,6 @@ using namespace mozilla::dom;
 
 #define BUFFER_SIZE        4096
 #define COMMAND_SIZE       256
-#define PROPERTY_VALUE_MAX 80
 
 // Intentionally not trying to dlclose() this handle. That's playing
 // Russian roulette with security bugs.
@@ -43,7 +42,7 @@ GetWifiP2pSupported()
   return (0 == strcmp(propP2pSupported, "1"));
 }
 
-int
+static int
 hex2num(char c)
 {
   if (c >= '0' && c <= '9')
@@ -55,7 +54,7 @@ hex2num(char c)
   return -1;
 }
 
-int
+static int
 hex2byte(const char* hex)
 {
   int a, b;
@@ -71,7 +70,7 @@ hex2byte(const char* hex)
 // This function is equivalent to printf_decode() at src/utils/common.c in
 // the supplicant.
 
-uint32_t
+static uint32_t
 convertToBytes(char* buf, uint32_t maxlen, const char* str)
 {
   const char *pos = str;
@@ -156,7 +155,8 @@ convertToBytes(char* buf, uint32_t maxlen, const char* str)
 
 static const uint32_t REPLACE_UTF8 = 0xFFFD;
 
-void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
+static void
+LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
 {
   JS::UTF8Chars src(aInput, aLength);
 
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
index fefc1b959e8..4b5335abdca 100644
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5702,13 +5702,7 @@ WorkerPrivate::SetTimeout(JSContext* aCx,
   newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
 
   if (!newInfo->mTimeoutString.IsEmpty()) {
-    const char* filenameChars;
-    uint32_t lineNumber;
-    if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) {
-      newInfo->mFilename = filenameChars;
-      newInfo->mLineNumber = lineNumber;
-    }
-    else {
+    if (!nsJSUtils::GetCallingLocation(aCx, newInfo->mFilename, &newInfo->mLineNumber)) {
       NS_WARNING("Failed to get calling location!");
     }
   }
diff --git a/dom/xslt/xpath/txXPathOptimizer.cpp b/dom/xslt/xpath/txXPathOptimizer.cpp
index 95921059863..a31e44a6d48 100644
--- a/dom/xslt/xpath/txXPathOptimizer.cpp
+++ b/dom/xslt/xpath/txXPathOptimizer.cpp
@@ -3,6 +3,7 @@
  * 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 "mozilla/Assertions.h"
 #include "txXPathOptimizer.h"
 #include "txExprResult.h"
 #include "nsIAtom.h"
@@ -23,18 +24,15 @@ public:
     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
                          txAExprResult*& aResult)
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-        return NS_ERROR_FAILURE;
+        MOZ_CRASH("shouldn't depend on this context");
     }
     bool isStripSpaceAllowed(const txXPathNode& aNode)
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-        return false;
+        MOZ_CRASH("shouldn't depend on this context");
     }
     void* getPrivateContext()
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-        return nullptr;
+        MOZ_CRASH("shouldn't depend on this context");
     }
     txResultRecycler* recycler()
     {
@@ -45,22 +43,15 @@ public:
     }
     const txXPathNode& getContextNode()
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-
-        // This will return an invalid node, but we should never
-        // get here so that's fine.
-
-        return *static_cast(nullptr);
+        MOZ_CRASH("shouldn't depend on this context");
     }
     uint32_t size()
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-        return 1;
+        MOZ_CRASH("shouldn't depend on this context");
     }
     uint32_t position()
     {
-        NS_NOTREACHED("shouldn't depend on this context");
-        return 1;
+        MOZ_CRASH("shouldn't depend on this context");
     }
 
 private:
diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp
index 6e73570451c..07c9d74869d 100644
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1603,6 +1603,12 @@ nsXULElement::LoadSrc()
         // Usually xul elements are used in chrome, which doesn't have
         // session history at all.
         slots->mFrameLoader = nsFrameLoader::Create(this, false);
+        if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::prerendered,
+                        NS_LITERAL_STRING("true"), eIgnoreCase)) {
+            nsresult rv = slots->mFrameLoader->SetIsPrerendered();
+            NS_ENSURE_SUCCESS(rv,rv);
+        }
+
         NS_ENSURE_TRUE(slots->mFrameLoader, NS_OK);
     }
 
@@ -1627,6 +1633,13 @@ nsXULElement::GetFrameLoader()
     return loader.forget();
 }
 
+nsresult
+nsXULElement::SetIsPrerendered()
+{
+  return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
+                 NS_LITERAL_STRING("true"), true);
+}
+
 nsresult
 nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
 {
diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h
index fe2e6179c4b..4346f6f2371 100644
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -440,6 +440,7 @@ public:
     virtual mozilla::EventStates IntrinsicState() const MOZ_OVERRIDE;
 
     nsresult GetFrameLoader(nsIFrameLoader** aFrameLoader);
+    nsresult SetIsPrerendered();
     nsresult SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner);
 
     virtual void RecompileScriptEventListeners() MOZ_OVERRIDE;
diff --git a/dom/xul/test/1061864.html b/dom/xul/test/1061864.html
new file mode 100644
index 00000000000..ec8091a6bd4
--- /dev/null
+++ b/dom/xul/test/1061864.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/dom/xul/test/chrome.ini b/dom/xul/test/chrome.ini
index 2ecf9323ced..d5dd737c991 100644
--- a/dom/xul/test/chrome.ini
+++ b/dom/xul/test/chrome.ini
@@ -7,6 +7,7 @@ support-files =
   overlay2_bug335375.xul
   window_bug583948.xul
   window_bug757137.xul
+  1061864.html
 
 [test_bug199692.xul]
 [test_bug233643.xul]
@@ -26,5 +27,7 @@ support-files =
 [test_bug640158_overlay_persist.xul]
 [test_bug757137.xul]
 [test_bug775972.xul]
+[test_bug1061864_1.xul]
+[test_bug1061864_2.xul]
 [test_bug1070049_throw_from_script.xul]
 [test_import_xul_to_content.xul]
diff --git a/dom/xul/test/test_bug1061864_1.xul b/dom/xul/test/test_bug1061864_1.xul
new file mode 100644
index 00000000000..8fe3b8e3011
--- /dev/null
+++ b/dom/xul/test/test_bug1061864_1.xul
@@ -0,0 +1,50 @@
+
+
+
+
+
+  
+
+  
+  
+  Mozilla Bug 1061864
+  
+
diff --git a/dom/xul/test/test_bug1061864_2.xul b/dom/xul/test/test_bug1061864_2.xul
new file mode 100644
index 00000000000..e7ed5a630c9
--- /dev/null
+++ b/dom/xul/test/test_bug1061864_2.xul
@@ -0,0 +1,52 @@
+
+
+
+
+
+  
+
+  
+  
+  Mozilla Bug 1061864
+  
+
diff --git a/gfx/gl/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp
index 0f36523ff60..d538b1c74ff 100755
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -525,11 +525,18 @@ GLScreenBuffer::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
   }
 
   {
+      // Even though we're reading. We're doing it on
+      // the producer side. So we call ProducerAcquire
+      // instead of ConsumerAcquire.
+      src->ProducerAcquire();
+
       UniquePtr buffer = CreateRead(src);
       MOZ_ASSERT(buffer);
 
       ScopedBindFramebuffer autoFB(mGL, buffer->mFB);
       ReadPixelsIntoDataSurface(mGL, dest);
+
+      src->ProducerRelease();
   }
 
   if (needsSwap) {
diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp
index 5a16c07ccc5..de4de735c5d 100644
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -102,8 +102,6 @@ SharedSurface_IOSurface::SharedSurface_IOSurface(const RefPtr& ioS
                   size,
                   hasAlpha)
   , mIOSurf(ioSurf)
-  , mCurConsGL(nullptr)
-  , mConsTex(0)
 {
     gl->MakeCurrent();
     mProdTex = 0;
@@ -111,31 +109,12 @@ SharedSurface_IOSurface::SharedSurface_IOSurface(const RefPtr& ioS
     BackTextureWithIOSurf(gl, mProdTex, mIOSurf);
 }
 
-GLuint
-SharedSurface_IOSurface::ConsTexture(GLContext* consGL)
-{
-    if (!mCurConsGL) {
-        mCurConsGL = consGL;
-    }
-    MOZ_ASSERT(consGL == mCurConsGL);
-
-    if (!mConsTex) {
-        consGL->MakeCurrent();
-        mConsTex = 0;
-        consGL->fGenTextures(1, &mConsTex);
-        BackTextureWithIOSurf(consGL, mConsTex, mIOSurf);
-    }
-
-    return mConsTex;
-}
-
 SharedSurface_IOSurface::~SharedSurface_IOSurface()
 {
     if (mProdTex) {
         DebugOnly success = mGL->MakeCurrent();
         MOZ_ASSERT(success);
         mGL->fDeleteTextures(1, &mProdTex);
-        mGL->fDeleteTextures(1, &mConsTex); // This will work if we're shared.
     }
 }
 
diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h
index e7488f2b4d6..f9464e85aba 100644
--- a/gfx/gl/SharedSurfaceIO.h
+++ b/gfx/gl/SharedSurfaceIO.h
@@ -46,12 +46,6 @@ public:
         return static_cast(surf);
     }
 
-    GLuint ConsTexture(GLContext* consGL);
-
-    GLenum ConsTextureTarget() const {
-        return LOCAL_GL_TEXTURE_RECTANGLE_ARB;
-    }
-
     MacIOSurface* GetIOSurface() const {
         return mIOSurf;
     }
@@ -67,8 +61,6 @@ private:
 
     RefPtr mIOSurf;
     GLuint mProdTex;
-    const GLContext* mCurConsGL;
-    GLuint mConsTex;
 };
 
 class SurfaceFactory_IOSurface : public SurfaceFactory
diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h
index 2ce3680b6a4..488540e4006 100644
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -165,8 +165,6 @@ enum SurfaceInitMode
  *    construct an EffectChain for the quad,
  *    call DrawQuad,
  *  call EndFrame.
- * If the user has to stop compositing at any point before EndFrame, call
- * AbortFrame.
  * If the compositor is usually used for compositing but compositing is
  * temporarily done without the compositor, call EndFrameForExternalComposition
  * after compositing each frame so the compositor can remain internally
@@ -355,11 +353,6 @@ public:
    */
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) = 0;
 
-  /**
-   * Tidy up if BeginFrame has been called, but EndFrame won't be.
-   */
-  virtual void AbortFrame() = 0;
-
   /**
    * Setup the viewport and projection matrix for rendering to a target of the
    * given dimensions. The size and transform here will override those set in
diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp
index 0bef0769f47..8a94eff1b52 100644
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -501,14 +501,5 @@ BasicCompositor::EndFrame()
   mRenderTarget = nullptr;
 }
 
-void
-BasicCompositor::AbortFrame()
-{
-  mRenderTarget->mDrawTarget->PopClip();
-  mRenderTarget->mDrawTarget->PopClip();
-  mDrawTarget = nullptr;
-  mRenderTarget = nullptr;
-}
-
 }
 }
diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h
index 80cf919a4fe..1eb60b690e7 100644
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -100,7 +100,6 @@ public:
   {
     NS_RUNTIMEABORT("We shouldn't ever hit this");
   }
-  virtual void AbortFrame() MOZ_OVERRIDE;
 
   virtual bool SupportsPartialTextureUpdate() MOZ_OVERRIDE { return true; }
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) MOZ_OVERRIDE { return true; }
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
old mode 100644
new mode 100755
index 536fe8882c8..58680ea96de
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -292,6 +292,11 @@ ContainerPrepare(ContainerT* aContainer,
    * Setup our temporary surface for rendering the contents of this container.
    */
 
+  gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
+  if (surfaceRect.IsEmpty()) {
+    return;
+  }
+
   bool surfaceCopyNeeded;
   // DefaultComputeSupportsComponentAlphaChildren can mutate aContainer so call it unconditionally
   aContainer->DefaultComputeSupportsComponentAlphaChildren(&surfaceCopyNeeded);
@@ -300,7 +305,6 @@ ContainerPrepare(ContainerT* aContainer,
       RefPtr surface = nullptr;
 
       RefPtr& lastSurf = aContainer->mLastIntermediateSurface;
-      gfx::IntRect surfaceRect = ContainerVisibleRect(aContainer);
       if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
         surface = lastSurf;
       }
diff --git a/gfx/layers/d3d10/ImageLayerD3D10.cpp b/gfx/layers/d3d10/ImageLayerD3D10.cpp
index a723ff6e161..805ba49bba9 100644
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp
@@ -304,6 +304,8 @@ ImageLayerD3D10::RenderLayer()
         (float)yuvImage->GetData()->mPicSize.width / yuvImage->GetData()->mYSize.width,
         (float)yuvImage->GetData()->mPicSize.height / yuvImage->GetData()->mYSize.height)
        );
+  } else {
+    MOZ_CRASH("unexpected image format");
   }
 
   bool resetTexCoords = image->GetFormat() == ImageFormat::PLANAR_YCBCR;
diff --git a/gfx/layers/d3d11/CompositorD3D11.h b/gfx/layers/d3d11/CompositorD3D11.h
index f2a00292eef..10d589bcc23 100644
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -124,11 +124,6 @@ public:
    */
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) MOZ_OVERRIDE {}
 
-  /**
-   * Tidy up if BeginFrame has been called, but EndFrame won't be
-   */
-  virtual void AbortFrame() MOZ_OVERRIDE {}
-
   /**
    * Setup the viewport and projection matrix for rendering
    * to a window of the given dimensions.
diff --git a/gfx/layers/d3d9/CompositorD3D9.h b/gfx/layers/d3d9/CompositorD3D9.h
index 38e7082ceff..1a9eae8c160 100644
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -69,8 +69,6 @@ public:
 
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) MOZ_OVERRIDE {}
 
-  virtual void AbortFrame() MOZ_OVERRIDE {}
-
   virtual void PrepareViewport(const gfx::IntSize& aSize) MOZ_OVERRIDE;
 
   virtual bool SupportsPartialTextureUpdate() MOZ_OVERRIDE{ return true; }
diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp
index 83dad194da6..58a6bfa0bdc 100644
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -199,6 +199,7 @@ static void SetThreadPriority()
 CompositorVsyncObserver::CompositorVsyncObserver(CompositorParent* aCompositorParent, nsIWidget* aWidget)
   : mNeedsComposite(false)
   , mIsObservingVsync(false)
+  , mVsyncNotificationsSkipped(0)
   , mCompositorParent(aCompositorParent)
   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
   , mCurrentCompositeTask(nullptr)
@@ -285,6 +286,9 @@ CompositorVsyncObserver::Composite(TimeStamp aVsyncTimestamp)
   if (mNeedsComposite && mCompositorParent) {
     mNeedsComposite = false;
     mCompositorParent->CompositeCallback(aVsyncTimestamp);
+    mVsyncNotificationsSkipped = 0;
+  } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
+    UnobserveVsync();
   }
 
   DispatchTouchEvents(aVsyncTimestamp);
diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h
index 79c67a6d36c..72b4395d71e 100644
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -117,6 +117,7 @@ private:
 
   bool mNeedsComposite;
   bool mIsObservingVsync;
+  int32_t mVsyncNotificationsSkipped;
   nsRefPtr mCompositorParent;
   nsRefPtr mCompositorVsyncDispatcher;
 
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
index 121c42bc942..ceb94499d47 100644
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -576,7 +576,7 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
   PROFILER_LABEL("CompositorOGL", "BeginFrame",
     js::ProfileEntry::Category::GRAPHICS);
 
-  MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame or AbortFrame");
+  MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame");
 
   mFrameInProgress = true;
   gfx::Rect rect;
@@ -1322,18 +1322,6 @@ CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
   }
 }
 
-void
-CompositorOGL::AbortFrame()
-{
-  mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
-  mFrameInProgress = false;
-  mCurrentRenderTarget = nullptr;
-
-  if (mTexturePool) {
-    mTexturePool->EndFrame();
-  }
-}
-
 void
 CompositorOGL::SetDestinationSurfaceSize(const gfx::IntSize& aSize)
 {
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
index 372c1b9dabe..bf143ce5639 100644
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -239,7 +239,6 @@ public:
   virtual void SetFBAcquireFence(Layer* aLayer) MOZ_OVERRIDE;
   virtual FenceHandle GetReleaseFence() MOZ_OVERRIDE;
   virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) MOZ_OVERRIDE;
-  virtual void AbortFrame() MOZ_OVERRIDE;
 
   virtual bool SupportsPartialTextureUpdate() MOZ_OVERRIDE;
 
diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h
index 023aa763a1f..8f0a7a2744c 100644
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -19,6 +19,7 @@
 #include "nsStringGlue.h"               // for nsCString
 #include "xpcom-config.h"               // for CPP_THROW_NEW
 #include "mozilla/TypedEnum.h"          // for the VisitEdges typed enum
+#include "mozilla/Move.h"               // for mozilla::Move
 
 class nsIntRegion;
 class gfx3DMatrix;
@@ -61,6 +62,13 @@ public:
                                                                           aRect.width,
                                                                           aRect.height); }
   nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); }
+  nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); }
+  nsRegion& operator = (nsRegion&& aRegion) {
+      pixman_region32_fini(&mImpl);
+      mImpl = aRegion.mImpl;
+      pixman_region32_init(&aRegion.mImpl);
+      return *this;
+  }
  ~nsRegion () { pixman_region32_fini(&mImpl); }
   nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; }
   nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; }
@@ -461,8 +469,10 @@ public:
   nsIntRegion () {}
   MOZ_IMPLICIT nsIntRegion (const nsIntRect& aRect) : mImpl (ToRect(aRect)) {}
   nsIntRegion (const nsIntRegion& aRegion) : mImpl (aRegion.mImpl) {}
+  nsIntRegion (nsIntRegion&& aRegion) : mImpl (mozilla::Move(aRegion.mImpl)) {}
   nsIntRegion& operator = (const nsIntRect& aRect) { mImpl = ToRect (aRect); return *this; }
   nsIntRegion& operator = (const nsIntRegion& aRegion) { mImpl = aRegion.mImpl; return *this; }
+  nsIntRegion& operator = (nsIntRegion&& aRegion) { mImpl = mozilla::Move(aRegion.mImpl); return *this; }
 
   bool operator==(const nsIntRegion& aRgn) const
   {
diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h
index 4e85ec8fdc5..626f6654d9b 100644
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -219,6 +219,9 @@ private:
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled",            HardwareVsyncEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor",                  VsyncAlignedCompositor, bool, false);
+  // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread
+  // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10.
+  DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
   DECL_GFX_PREF(Once, "gfx.touch.resample",                    TouchResampling, bool, false);
   // These times should be in milliseconds
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
diff --git a/js/src/asmjs/AsmJSSignalHandlers.cpp b/js/src/asmjs/AsmJSSignalHandlers.cpp
index e7aea7e599b..fd344a2ef18 100644
--- a/js/src/asmjs/AsmJSSignalHandlers.cpp
+++ b/js/src/asmjs/AsmJSSignalHandlers.cpp
@@ -438,7 +438,6 @@ HandleFault(PEXCEPTION_POINTERS exception)
 
     uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
-    MOZ_ASSERT(pc == record->ExceptionAddress);
 
     if (record->NumberParameters < 2)
         return false;
@@ -453,11 +452,9 @@ HandleFault(PEXCEPTION_POINTERS exception)
     if (!activation)
         return false;
 
-    const AsmJSModule &module = activation->module();
-    if (!module.containsFunctionPC(pc))
-        return false;
-
 # if defined(JS_CODEGEN_X64)
+    const AsmJSModule &module = activation->module();
+
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     void *faultingAddress = (void*)record->ExceptionInformation[1];
@@ -468,6 +465,25 @@ HandleFault(PEXCEPTION_POINTERS exception)
         return false;
     }
 
+    if (!module.containsFunctionPC(pc)) {
+        // On Windows, it is possible for InterruptRunningCode to execute
+        // between a faulting heap access and the handling of the fault due
+        // to InterruptRunningCode's use of SuspendThread. When this happens,
+        // after ResumeThread, the exception handler is called with pc equal to
+        // module.interruptExit, which is logically wrong. The Right Thing would
+        // be for the OS to make fault-handling atomic (so that CONTEXT.pc was
+        // always the logically-faulting pc). Fortunately, we can detect this
+        // case and silence the exception ourselves (the exception will
+        // retrigger after the interrupt jumps back to resumePC).
+        if (pc == module.interruptExit() &&
+            module.containsFunctionPC(activation->resumePC()) &&
+            module.lookupHeapAccess(activation->resumePC()))
+        {
+            return true;
+        }
+        return false;
+    }
+
     const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc);
     if (!heapAccess)
         return false;
diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js
index 407d5c8b1a4..9260f23f35a 100644
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -2,6 +2,47 @@
  * 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/. */
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.7 %TypedArray%.prototype.every(callbackfn[, thisArg]).
+function TypedArrayEvery(callbackfn, thisArg = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+                            "TypedArrayEvery");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (arguments.length === 0)
+        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    var T = thisArg;
+
+    // Steps 8-9.
+    // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
+    for (var k = 0; k < len; k++) {
+        // Steps 9.d.i-9.d.ii.
+        var kValue = O[k];
+
+        // Steps 9.d.iii-9.d.iv.
+        var testResult = callFunction(callbackfn, T, kValue, k, O);
+
+        // Step 9.d.v.
+        if (!testResult)
+            return false;
+    }
+
+    // Step 10.
+    return true;
+}
+
 // ES6 draft rev29 (2014/12/06) 22.2.3.8 %TypedArray%.prototype.fill(value [, start [, end ]])
 function TypedArrayFill(value, start = 0, end = undefined) {
     // This function is not generic.
@@ -283,6 +324,47 @@ function TypedArrayReverse() {
     return O;
 }
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
+function TypedArraySome(callbackfn, thisArg = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+                            "TypedArraySome");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (arguments.length === 0)
+        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    var T = thisArg;
+
+    // Steps 8-9.
+    // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
+    for (var k = 0; k < len; k++) {
+        // Steps 9.d.i-9.d.ii.
+        var kValue = O[k];
+
+        // Steps 9.d.iii-9.d.iv.
+        var testResult = callFunction(callbackfn, T, kValue, k, O);
+
+        // Step 9.d.v.
+        if (testResult)
+            return true;
+    }
+
+    // Step 10.
+    return false;
+}
+
 // Proposed for ES7:
 // https://github.com/tc39/Array.prototype.includes/blob/7c023c19a0/spec.md
 function TypedArrayIncludes(searchElement, fromIndex = 0) {
diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp
index 28093a76f7b..df6bcd006ff 100644
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -120,11 +120,13 @@ MarkExactStackRootsAcrossTypes(T context, JSTracer *trc)
     MarkExactStackRootList(trc, context, "PropDesc");
 }
 
+#ifdef JSGC_FJGENERATIONAL
 static void
 MarkExactStackRoots(ThreadSafeContext* cx, JSTracer *trc)
 {
     MarkExactStackRootsAcrossTypes(cx, trc);
 }
+#endif
 
 static void
 MarkExactStackRoots(JSRuntime* rt, JSTracer *trc)
diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp
index b60935c1a65..eb910f64cd7 100644
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -297,7 +297,7 @@ struct ExtraPhaseInfo
 
     // Index into the set of parallel arrays of timing data, for parents with
     // at least one multi-parented child
-    int dagSlot;
+    size_t dagSlot;
 };
 
 static const Phase PHASE_NO_PARENT = PHASE_LIMIT;
@@ -382,7 +382,7 @@ static const PhaseInfo phases[] = {
     { PHASE_LIMIT, nullptr, PHASE_NO_PARENT }
 };
 
-ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
+static ExtraPhaseInfo phaseExtra[PHASE_LIMIT] = { { 0, 0 } };
 
 // Mapping from all nodes with a multi-parented child to a Vector of all
 // multi-parented children and their descendants. (Single-parented children will
diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp
index de3dab4202d..d23717ae634 100644
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -82,7 +82,7 @@ static const int kSurrogateRangeCount = ArrayLength(kSurrogateRanges);
 static const int kLineTerminatorRanges[] = { 0x000A, 0x000B, 0x000D, 0x000E,
     0x2028, 0x202A, 0x10000 };
 static const int kLineTerminatorRangeCount = ArrayLength(kLineTerminatorRanges);
-static const unsigned kMaxOneByteCharCode = 0xff;
+static const int kMaxOneByteCharCode = 0xff;
 static const int kMaxUtf16CodeUnit = 0xffff;
 
 static char16_t
@@ -3180,7 +3180,7 @@ SplitSearchSpace(RangeBoundaryVector &ranges,
     // range with a single not-taken branch, speeding up this important
     // character range (even non-ASCII charset-based text has spaces and
     // punctuation).
-    if (*border - 1 > (int) kMaxOneByteCharCode &&  // ASCII case.
+    if (*border - 1 > kMaxOneByteCharCode &&  // ASCII case.
         end_index - start_index > (*new_start_index - start_index) * 2 &&
         last - first > kSize * 2 &&
         binary_chop_index > *new_start_index &&
diff --git a/js/src/irregexp/RegExpParser.cpp b/js/src/irregexp/RegExpParser.cpp
index 1e3fcd28cab..4d770acefc0 100644
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -518,7 +518,7 @@ RegExpParser::ScanForCaptures()
             Advance();
             break;
           case '[': {
-            int c;
+            widechar c;
             while ((c = current()) != kEndMarker) {
                 Advance();
                 if (c == '\\') {
diff --git a/js/src/jit-test/tests/asm.js/testBug1117235.js b/js/src/jit-test/tests/asm.js/testBug1117235.js
new file mode 100644
index 00000000000..80cac617b31
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug1117235.js
@@ -0,0 +1,8 @@
+load(libdir + "asserts.js");
+
+if (helperThreadCount() === 0)
+  quit(0);
+
+options('werror');
+offThreadCompileScript("function f() {'use asm'}");
+assertThrowsInstanceOf(()=>runOffThreadScript(), TypeError);
diff --git a/js/src/jit-test/tests/asm.js/testBug1117255.js b/js/src/jit-test/tests/asm.js/testBug1117255.js
new file mode 100644
index 00000000000..0cda6f35977
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug1117255.js
@@ -0,0 +1,14 @@
+function f(stdlib, foreign, buffer) {
+    "use asm";
+    var i32 = new stdlib.Int32Array(buffer);
+    function g(i) {
+        i=i|0;
+        var j=0;
+        for (; (j>>>0) < 100000; j=(j+1)|0)
+            i32[i>>2] = j;
+    }
+    return g
+}
+var g = f(this, null, new ArrayBuffer(1<<16));
+timeout(.1, function cb() { return true });
+g(1<<16);
diff --git a/js/src/jit-test/tests/collections/iterator-noSuchMethod.js b/js/src/jit-test/tests/collections/iterator-noSuchMethod.js
new file mode 100644
index 00000000000..f79d3fa317e
--- /dev/null
+++ b/js/src/jit-test/tests/collections/iterator-noSuchMethod.js
@@ -0,0 +1,24 @@
+// __noSuchMethod__ is totally non-standard and evil, but in this one weird case
+// below we don't actually use it.  So this test is bog-standard ES6, not
+// SpiderMonkey-specific.
+//
+// In ES6:
+//   Accessing 1[Symbol.iterator]() throws a TypeError calling |undefined|.
+// In SpiderMonkey:
+//   Accessing 1[Symbol.iterator]() does *not* invoke __noSuchMethod__ looked up
+//   on 1 (or on an implicitly boxed 1), because 1 is a primitive value.
+//   SpiderMonkey then does exactly the ES6 thing here and throws a TypeError
+//   calling |undefined|.
+
+Object.prototype.__noSuchMethod__ = {};
+
+try
+{
+  var [x] = 1;
+  throw new Error("didn't throw");
+}
+catch (e)
+{
+  assertEq(e instanceof TypeError, true,
+           "expected TypeError, got " + e);
+}
diff --git a/js/src/jit-test/tests/debug/bug1116103.js b/js/src/jit-test/tests/debug/bug1116103.js
new file mode 100644
index 00000000000..2c3fd0698de
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1116103.js
@@ -0,0 +1,11 @@
+// |jit-test| error: ReferenceError
+
+evaluate(`
+    var g = newGlobal();
+    g.parent = this;
+    g.eval('new Debugger(parent).onExceptionUnwind = function() {};');
+`)
+{
+    while (x && 0) {}
+    let x
+}
diff --git a/js/src/jit-test/tests/ion/bug1113139.js b/js/src/jit-test/tests/ion/bug1113139.js
new file mode 100644
index 00000000000..31ae9e384b8
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1113139.js
@@ -0,0 +1,9 @@
+var lfcode = new Array();
+lfcode.push = function(x) { eval("(function() { " + x + " })();"); };
+lfcode.push("\
+function error(str) { try { eval(str); } catch (e) { return e; } }\
+const YIELD_PAREN = error('(function*(){(for (y of (yield 1, 2)) y)})').message;\
+const GENEXP_YIELD = error('(function*(){(for (x of yield 1) x)})').message;\
+const GENERIC = error('(for)').message;\
+const eval = [];\
+");
diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js
new file mode 100644
index 00000000000..e86bc456fac
--- /dev/null
+++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js
@@ -0,0 +1,14 @@
+load(libdir + "asserts.js");
+
+// Make sure that a proxy only has a [[Construct]] if the target does
+
+var handler = {};
+var p = new Proxy(Math.sin, handler);
+var r = Proxy.revocable(Math.sin, handler).proxy;
+
+assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target");
+assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target");
+// Better throw regardless of whether we have a handler trap.
+handler.construct = (() => ({}));
+assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target");
+assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target");
diff --git a/js/src/jit/BaselineDebugModeOSR.cpp b/js/src/jit/BaselineDebugModeOSR.cpp
index 4f28e556779..225f13328d4 100644
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -205,22 +205,21 @@ CollectJitStackScripts(JSContext *cx, const Debugger::ExecutionObservableSet &ob
                     return false;
             } else {
                 uint8_t *retAddr = iter.returnAddressToFp();
-                ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr);
-                if (icEntry) {
-                    // Normally, the frame is settled on a pc with an ICEntry.
-                    if (!entries.append(DebugModeOSREntry(script, *icEntry)))
-                        return false;
-                } else {
-                    // Otherwise, we are in the middle of handling an
-                    // exception. This happens since we could have bailed out
-                    // in place from Ion after a throw, settling on the pc
-                    // which may have no ICEntry (e.g., Ion is free to insert
-                    // resume points after non-effectful ops for better
-                    // register allocation).
-                    MOZ_ASSERT(iter.baselineFrame()->isDebuggerHandlingException());
+                if (iter.baselineFrame()->isDebuggerHandlingException()) {
+                    // We are in the middle of handling an exception. This
+                    // happens since we could have bailed out in place from
+                    // Ion after a throw, settling on the pc which may have no
+                    // ICEntry (e.g., Ion is free to insert resume points
+                    // after non-effectful ops for better register
+                    // allocation).
                     jsbytecode *pc = script->baselineScript()->pcForNativeAddress(script, retAddr);
                     if (!entries.append(DebugModeOSREntry(script, script->pcToOffset(pc))))
                         return false;
+                } else {
+                    // Normally, the frame is settled on a pc with an ICEntry.
+                    ICEntry &icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
+                    if (!entries.append(DebugModeOSREntry(script, icEntry)))
+                        return false;
                 }
             }
 
diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp
index 3a8caf55275..5d2ac7142fd 100644
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1069,7 +1069,7 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
             // the original box.
             phi->replaceOperand(i, in->toUnbox()->input());
         } else {
-            MDefinition *box = BoxInputsPolicy::alwaysBoxAt(alloc(), in->block()->lastIns(), in);
+            MDefinition *box = AlwaysBoxAt(alloc(), in->block()->lastIns(), in);
             phi->replaceOperand(i, box);
         }
     }
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
index 0b70f6b5f07..8fc30b2a535 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -4389,6 +4389,15 @@ NameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
     if (!LookupName(cx, name, scopeChain, &obj, &holder, &shape))
         return false;
 
+    // Look first. Don't generate cache entries if the lookup fails.
+    if (cache.isTypeOf()) {
+        if (!FetchName(cx, obj, holder, name, shape, vp))
+            return false;
+    } else {
+        if (!FetchName(cx, obj, holder, name, shape, vp))
+            return false;
+    }
+
     if (cache.canAttachStub()) {
         if (IsCacheableNameReadSlot(scopeChain, obj, holder, shape, pc, cache.outputReg())) {
             if (!cache.attachReadSlot(cx, outerScript, ion, scopeChain, obj,
@@ -4402,14 +4411,6 @@ NameIC::update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain,
         }
     }
 
-    if (cache.isTypeOf()) {
-        if (!FetchName(cx, obj, holder, name, shape, vp))
-            return false;
-    } else {
-        if (!FetchName(cx, obj, holder, name, shape, vp))
-            return false;
-    }
-
     // Monitor changes to cache entry.
     types::TypeScript::Monitor(cx, script, pc, vp);
 
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 4e06dddbc36..c54355046b7 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -979,7 +979,9 @@ class MAryInstruction : public MInstruction
     }
 };
 
-class MNullaryInstruction : public MAryInstruction<0>
+class MNullaryInstruction
+  : public MAryInstruction<0>,
+    public NoTypePolicy::Data
 { };
 
 class MUnaryInstruction : public MAryInstruction<1>
@@ -1315,7 +1317,9 @@ class MConstant : public MNullaryInstruction
 };
 
 // Generic constructor of SIMD valuesX4.
-class MSimdValueX4 : public MQuaternaryInstruction
+class MSimdValueX4
+  : public MQuaternaryInstruction,
+    public NoTypePolicy::Data
 {
   protected:
     MSimdValueX4(MIRType type, MDefinition *x, MDefinition *y, MDefinition *z, MDefinition *w)
@@ -1360,7 +1364,9 @@ class MSimdValueX4 : public MQuaternaryInstruction
 };
 
 // Generic constructor of SIMD valuesX4.
-class MSimdSplatX4 : public MUnaryInstruction
+class MSimdSplatX4
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   protected:
     MSimdSplatX4(MIRType type, MDefinition *v)
@@ -1400,7 +1406,8 @@ class MSimdSplatX4 : public MUnaryInstruction
 };
 
 // A constant SIMD value.
-class MSimdConstant : public MNullaryInstruction
+class MSimdConstant
+  : public MNullaryInstruction
 {
     SimdConstant value_;
 
@@ -1435,7 +1442,9 @@ class MSimdConstant : public MNullaryInstruction
 };
 
 // Converts all lanes of a given vector into the type of another vector
-class MSimdConvert : public MUnaryInstruction
+class MSimdConvert
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MSimdConvert(MDefinition *obj, MIRType fromType, MIRType toType)
       : MUnaryInstruction(obj)
@@ -1463,7 +1472,9 @@ class MSimdConvert : public MUnaryInstruction
 };
 
 // Casts bits of a vector input to another SIMD type (doesn't generate code).
-class MSimdReinterpretCast : public MUnaryInstruction
+class MSimdReinterpretCast
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MSimdReinterpretCast(MDefinition *obj, MIRType fromType, MIRType toType)
       : MUnaryInstruction(obj)
@@ -1491,7 +1502,9 @@ class MSimdReinterpretCast : public MUnaryInstruction
 };
 
 // Extracts a lane element from a given vector type, given by its lane symbol.
-class MSimdExtractElement : public MUnaryInstruction
+class MSimdExtractElement
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   protected:
     SimdLane lane_;
@@ -1533,7 +1546,9 @@ class MSimdExtractElement : public MUnaryInstruction
 };
 
 // Replaces the datum in the given lane by a scalar value of the same type.
-class MSimdInsertElement : public MBinaryInstruction
+class MSimdInsertElement
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     SimdLane lane_;
@@ -1583,7 +1598,9 @@ class MSimdInsertElement : public MBinaryInstruction
 };
 
 // Extracts the sign bits from a given vector, returning an MIRType_Int32.
-class MSimdSignMask : public MUnaryInstruction
+class MSimdSignMask
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   protected:
     explicit MSimdSignMask(MDefinition *obj)
@@ -1651,7 +1668,10 @@ class MSimdShuffleBase
 // Applies a shuffle operation to the input, putting the input lanes as
 // indicated in the output register's lanes. This implements the SIMD.js
 // "shuffle" function, that takes one vector and one mask.
-class MSimdSwizzle : public MUnaryInstruction, public MSimdShuffleBase
+class MSimdSwizzle
+  : public MUnaryInstruction,
+    public MSimdShuffleBase,
+    public NoTypePolicy::Data
 {
   protected:
     MSimdSwizzle(MDefinition *obj, MIRType type,
@@ -1694,7 +1714,10 @@ class MSimdSwizzle : public MUnaryInstruction, public MSimdShuffleBase
 // Applies a shuffle operation to the inputs, selecting the 2 first lanes of the
 // output from lanes of the first input, and the 2 last lanes of the output from
 // lanes of the second input.
-class MSimdShuffle : public MBinaryInstruction, public MSimdShuffleBase
+class MSimdShuffle
+  : public MBinaryInstruction,
+    public MSimdShuffleBase,
+    public NoTypePolicy::Data
 {
     MSimdShuffle(MDefinition *lhs, MDefinition *rhs, MIRType type,
                  uint32_t laneX, uint32_t laneY, uint32_t laneZ, uint32_t laneW)
@@ -1750,7 +1773,9 @@ class MSimdShuffle : public MBinaryInstruction, public MSimdShuffleBase
     ALLOW_CLONE(MSimdShuffle)
 };
 
-class MSimdUnaryArith : public MUnaryInstruction
+class MSimdUnaryArith
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   public:
     enum Operation {
@@ -1799,7 +1824,9 @@ class MSimdUnaryArith : public MUnaryInstruction
 // Compares each value of a SIMD vector to each corresponding lane's value of
 // another SIMD vector, and returns a int32x4 vector containing the results of
 // the comparison: all bits are set to 1 if the comparison is true, 0 otherwise.
-class MSimdBinaryComp : public MBinaryInstruction
+class MSimdBinaryComp
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   public:
     enum Operation {
@@ -1878,7 +1905,9 @@ class MSimdBinaryComp : public MBinaryInstruction
     ALLOW_CLONE(MSimdBinaryComp)
 };
 
-class MSimdBinaryArith : public MBinaryInstruction
+class MSimdBinaryArith
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   public:
     enum Operation {
@@ -1945,7 +1974,9 @@ class MSimdBinaryArith : public MBinaryInstruction
     ALLOW_CLONE(MSimdBinaryArith)
 };
 
-class MSimdBinaryBitwise : public MBinaryInstruction
+class MSimdBinaryBitwise
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   public:
     enum Operation {
@@ -1991,7 +2022,9 @@ class MSimdBinaryBitwise : public MBinaryInstruction
     ALLOW_CLONE(MSimdBinaryBitwise)
 };
 
-class MSimdShift : public MBinaryInstruction
+class MSimdShift
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   public:
     enum Operation {
@@ -2034,7 +2067,9 @@ class MSimdShift : public MBinaryInstruction
     ALLOW_CLONE(MSimdShift)
 };
 
-class MSimdSelect : public MTernaryInstruction
+class MSimdSelect
+  : public MTernaryInstruction,
+    public NoTypePolicy::Data
 {
     bool isElementWise_;
 
@@ -2372,7 +2407,9 @@ class MAryControlInstruction : public MControlInstruction
 };
 
 // Jump to the start of another basic block.
-class MGoto : public MAryControlInstruction<0, 1>
+class MGoto
+  : public MAryControlInstruction<0, 1>,
+    public NoTypePolicy::Data
 {
     explicit MGoto(MBasicBlock *target) {
         setSuccessor(0, target);
@@ -2466,7 +2503,8 @@ class MTest
 // method. This allows IonBuilder to insert fake CFG edges to magically protect
 // control flow for try-catch blocks.
 class MGotoWithFake
-  : public MAryControlInstruction<0, 2>
+  : public MAryControlInstruction<0, 2>,
+    public NoTypePolicy::Data
 {
     MGotoWithFake(MBasicBlock *successor, MBasicBlock *fake)
     {
@@ -2579,7 +2617,9 @@ typedef AlwaysTenured AlwaysTenuredScript;
 typedef AlwaysTenured AlwaysTenuredPropertyName;
 typedef AlwaysTenured AlwaysTenuredShape;
 
-class MNewArray : public MUnaryInstruction
+class MNewArray
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     // Number of space to allocate for the array.
@@ -2734,7 +2774,9 @@ class MNewArrayDynamicLength
     }
 };
 
-class MNewObject : public MUnaryInstruction
+class MNewObject
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     gc::InitialHeap initialHeap_;
     bool templateObjectIsClassPrototype_;
@@ -2795,7 +2837,9 @@ class MNewObject : public MUnaryInstruction
 };
 
 // Could be allocating either a new array or a new object.
-class MNewPar : public MUnaryInstruction
+class MNewPar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     AlwaysTenuredNativeObject templateObject_;
 
@@ -3504,7 +3548,9 @@ class MBail : public MNullaryInstruction
     }
 };
 
-class MUnreachable : public MAryControlInstruction<0, 0>
+class MUnreachable
+  : public MAryControlInstruction<0, 0>,
+    public NoTypePolicy::Data
 {
   public:
     INSTRUCTION_HEADER(Unreachable)
@@ -3518,7 +3564,9 @@ class MUnreachable : public MAryControlInstruction<0, 0>
     }
 };
 
-class MAssertFloat32 : public MUnaryInstruction
+class MAssertFloat32
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   protected:
     bool mustBeFloat32_;
@@ -3826,7 +3874,9 @@ class MCompare
 };
 
 // Takes a typed value and returns an untyped value.
-class MBox : public MUnaryInstruction
+class MBox
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MBox(TempAllocator &alloc, MDefinition *ins)
       : MUnaryInstruction(ins)
@@ -3982,7 +4032,9 @@ class MUnbox MOZ_FINAL : public MUnaryInstruction, public BoxInputsPolicy::Data
     ALLOW_CLONE(MUnbox)
 };
 
-class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy::Data
+class MGuardObject
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
 {
     explicit MGuardObject(MDefinition *ins)
       : MUnaryInstruction(ins)
@@ -4028,7 +4080,8 @@ class MGuardString
 };
 
 class MAssertRange
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     // This is the range checked by the assertion. Don't confuse this with the
     // range_ member or the range() accessor. Since MAssertRange doesn't return
@@ -4064,7 +4117,8 @@ class MAssertRange
 // Caller-side allocation of |this| for |new|:
 // Given a templateobject, construct |this| for JSOP_NEW
 class MCreateThisWithTemplate
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     gc::InitialHeap initialHeap_;
 
@@ -4475,7 +4529,8 @@ class MToFloat32
 
 // Converts a uint32 to a double (coming from asm.js).
 class MAsmJSUnsignedToDouble
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MAsmJSUnsignedToDouble(MDefinition *def)
       : MUnaryInstruction(def)
@@ -4501,7 +4556,8 @@ class MAsmJSUnsignedToDouble
 
 // Converts a uint32 to a float32 (coming from asm.js).
 class MAsmJSUnsignedToFloat32
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MAsmJSUnsignedToFloat32(MDefinition *def)
       : MUnaryInstruction(def)
@@ -6020,7 +6076,8 @@ class MConcat
 };
 
 class MConcatPar
-  : public MTernaryInstruction
+  : public MTernaryInstruction,
+    public NoTypePolicy::Data
 {
     MConcatPar(MDefinition *cx, MDefinition *left, MDefinition *right)
       : MTernaryInstruction(cx, left, right)
@@ -6232,7 +6289,10 @@ class MLoadArrowThis
     }
 };
 
-class MPhi MOZ_FINAL : public MDefinition, public InlineListNode
+class MPhi MOZ_FINAL
+  : public MDefinition,
+    public InlineListNode,
+    public NoTypePolicy::Data
 {
     js::Vector inputs_;
 
@@ -6415,7 +6475,9 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineListNode
 
 // The goal of a Beta node is to split a def at a conditionally taken
 // branch, so that uses dominated by it have a different name.
-class MBeta : public MUnaryInstruction
+class MBeta
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     // This is the range induced by a comparison and branch in a preceding
@@ -6449,7 +6511,9 @@ class MBeta : public MUnaryInstruction
 
 // MIR representation of a Value on the OSR BaselineFrame.
 // The Value is indexed off of OsrFrameReg.
-class MOsrValue : public MUnaryInstruction
+class MOsrValue
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     ptrdiff_t frameOffset_;
@@ -6482,7 +6546,9 @@ class MOsrValue : public MUnaryInstruction
 
 // MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame.
 // The pointer is indexed off of OsrFrameReg.
-class MOsrScopeChain : public MUnaryInstruction
+class MOsrScopeChain
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     explicit MOsrScopeChain(MOsrEntry *entry)
@@ -6504,7 +6570,9 @@ class MOsrScopeChain : public MUnaryInstruction
 
 // MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame.
 // The pointer is indexed off of OsrFrameReg.
-class MOsrArgumentsObject : public MUnaryInstruction
+class MOsrArgumentsObject
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     explicit MOsrArgumentsObject(MOsrEntry *entry)
@@ -6526,7 +6594,9 @@ class MOsrArgumentsObject : public MUnaryInstruction
 
 // MIR representation of the return value on the OSR BaselineFrame.
 // The Value is indexed off of OsrFrameReg.
-class MOsrReturnValue : public MUnaryInstruction
+class MOsrReturnValue
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     explicit MOsrReturnValue(MOsrEntry *entry)
@@ -6547,7 +6617,8 @@ class MOsrReturnValue : public MUnaryInstruction
 };
 
 // Check the current frame for over-recursion past the global stack limit.
-class MCheckOverRecursed : public MNullaryInstruction
+class MCheckOverRecursed
+  : public MNullaryInstruction
 {
   public:
     INSTRUCTION_HEADER(CheckOverRecursed)
@@ -6559,7 +6630,9 @@ class MCheckOverRecursed : public MNullaryInstruction
 
 // Check the current frame for over-recursion past the global stack limit.
 // Uses the per-thread recursion limit.
-class MCheckOverRecursedPar : public MUnaryInstruction
+class MCheckOverRecursedPar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MCheckOverRecursedPar(MDefinition *cx)
       : MUnaryInstruction(cx)
@@ -6582,7 +6655,9 @@ class MCheckOverRecursedPar : public MUnaryInstruction
 };
 
 // Check for an interrupt (or rendezvous) in parallel mode.
-class MInterruptCheckPar : public MUnaryInstruction
+class MInterruptCheckPar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MInterruptCheckPar(MDefinition *cx)
       : MUnaryInstruction(cx)
@@ -6627,7 +6702,8 @@ class MInterruptCheck : public MNullaryInstruction
 // Check whether we need to fire the interrupt handler at loop headers and
 // function prologues in asm.js. Generated only if we can't use implicit
 // interrupt checks with signal handlers.
-class MAsmJSInterruptCheck : public MNullaryInstruction
+class MAsmJSInterruptCheck
+  : public MNullaryInstruction
 {
     Label *interruptExit_;
     CallSiteDesc funcDesc_;
@@ -6702,7 +6778,9 @@ class MThrowUninitializedLexical : public MNullaryInstruction
 };
 
 // If not defined, set a global variable to |undefined|.
-class MDefVar : public MUnaryInstruction
+class MDefVar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     AlwaysTenuredPropertyName name_; // Target name to be defined.
     unsigned attrs_; // Attributes to be set.
@@ -6738,7 +6816,9 @@ class MDefVar : public MUnaryInstruction
     }
 };
 
-class MDefFun : public MUnaryInstruction
+class MDefFun
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     AlwaysTenuredFunction fun_;
 
@@ -6977,7 +7057,7 @@ class MStringReplace
 
 class MSubstr
   : public MTernaryInstruction,
-    public Mix3Policy, IntPolicy<1>, IntPolicy<2>>
+    public Mix3Policy, IntPolicy<1>, IntPolicy<2>>::Data
 {
   private:
 
@@ -7259,7 +7339,8 @@ class MConstantElements : public MNullaryInstruction
 
 // Passes through an object's elements, after ensuring it is entirely doubles.
 class MConvertElementsToDoubles
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MConvertElementsToDoubles(MDefinition *elements)
       : MUnaryInstruction(elements)
@@ -7373,7 +7454,8 @@ class MMaybeCopyElementsForWrite
 
 // Load the initialized length from an elements header.
 class MInitializedLength
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MInitializedLength(MDefinition *elements)
       : MUnaryInstruction(elements)
@@ -7407,7 +7489,8 @@ class MInitializedLength
 // Store to the initialized length in an elements header. Note the input is an
 // *index*, one less than the desired length.
 class MSetInitializedLength
-  : public MAryInstruction<2>
+  : public MAryInstruction<2>,
+    public NoTypePolicy::Data
 {
     MSetInitializedLength(MDefinition *elements, MDefinition *index) {
         initOperand(0, elements);
@@ -7436,7 +7519,8 @@ class MSetInitializedLength
 
 // Load the array length from an elements header.
 class MArrayLength
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MArrayLength(MDefinition *elements)
       : MUnaryInstruction(elements)
@@ -7470,7 +7554,8 @@ class MArrayLength
 // Store to the length in an elements header. Note the input is an *index*, one
 // less than the desired length.
 class MSetArrayLength
-  : public MAryInstruction<2>
+  : public MAryInstruction<2>,
+    public NoTypePolicy::Data
 {
     MSetArrayLength(MDefinition *elements, MDefinition *index) {
         initOperand(0, elements);
@@ -7606,7 +7691,8 @@ class MTypedObjectElements
 
 // Inlined version of the js::SetTypedObjectOffset() intrinsic.
 class MSetTypedObjectOffset
-  : public MBinaryInstruction
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
   private:
     MSetTypedObjectOffset(MDefinition *object, MDefinition *offset)
@@ -7709,7 +7795,8 @@ class MNot
 // in a bounds check must not be negative, or the wrong result may be computed
 // (unsigned comparisons may be used).
 class MBoundsCheck
-  : public MBinaryInstruction
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
     // Range over which to perform the bounds check, may be modified by GVN.
     int32_t minimum_;
@@ -7770,7 +7857,8 @@ class MBoundsCheck
 
 // Bailout if index < minimum.
 class MBoundsCheckLower
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     int32_t minimum_;
     bool fallible_;
@@ -8904,7 +8992,9 @@ class MStoreTypedArrayElementStatic :
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
-class MEffectiveAddress : public MBinaryInstruction
+class MEffectiveAddress
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
       : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement)
@@ -9944,7 +10034,8 @@ class MForkJoinContext
 // Calls the ForkJoinGetSlice stub, used for inlining the eponymous intrinsic.
 // Only applicable in ParallelExecution.
 class MForkJoinGetSlice
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MForkJoinGetSlice(MDefinition *cx)
       : MUnaryInstruction(cx)
@@ -10891,7 +10982,8 @@ class MIteratorMore
 };
 
 class MIsNoIter
-  : public MUnaryInstruction
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     explicit MIsNoIter(MDefinition *def)
       : MUnaryInstruction(def)
@@ -11397,7 +11489,9 @@ class MTypeBarrier
 // Like MTypeBarrier, guard that the value is in the given type set. This is
 // used before property writes to ensure the value being written is represented
 // in the property types for the object.
-class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy::Data
+class MMonitorTypes
+  : public MUnaryInstruction,
+    public BoxInputsPolicy::Data
 {
     const types::TemporaryTypeSet *typeSet_;
     BarrierKind barrierKind_;
@@ -11552,7 +11646,9 @@ class MNewRunOnceCallObject : public MNewCallObjectBase
     }
 };
 
-class MNewCallObjectPar : public MUnaryInstruction
+class MNewCallObjectPar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     AlwaysTenured templateObj_;
 
@@ -11671,7 +11767,9 @@ class MEnclosingScope : public MLoadFixedSlot
 // Creates a dense array of the given length.
 //
 // Note: the template object should be an *empty* dense array!
-class MNewDenseArrayPar : public MBinaryInstruction
+class MNewDenseArrayPar
+  : public MBinaryInstruction,
+    public NoTypePolicy::Data
 {
     AlwaysTenured templateObject_;
 
@@ -12082,7 +12180,7 @@ class MMemoryBarrier
 
 class MCompareExchangeTypedArrayElement
   : public MAryInstruction<4>,
-    public MixPolicy< MixPolicy, IntPolicy<1> >, MixPolicy, IntPolicy<3> > >
+    public Mix4Policy, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>::Data
 {
     Scalar::Type arrayType_;
 
@@ -12137,7 +12235,7 @@ class MCompareExchangeTypedArrayElement
 
 class MAtomicTypedArrayElementBinop
     : public MAryInstruction<3>,
-      public Mix3Policy< ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >
+      public Mix3Policy< ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data
 {
   private:
     AtomicOp op_;
@@ -12200,7 +12298,9 @@ class MDebugger : public MNullaryInstruction
     }
 };
 
-class MAsmJSNeg : public MUnaryInstruction
+class MAsmJSNeg
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MAsmJSNeg(MDefinition *op, MIRType type)
       : MUnaryInstruction(op)
@@ -12231,7 +12331,10 @@ class MAsmJSHeapAccess
     void removeBoundsCheck() { needsBoundsCheck_ = false; }
 };
 
-class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
+class MAsmJSLoadHeap
+  : public MUnaryInstruction,
+    public MAsmJSHeapAccess,
+    public NoTypePolicy::Data
 {
     MemoryBarrierBits barrierBefore_;
     MemoryBarrierBits barrierAfter_;
@@ -12297,7 +12400,10 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
     bool mightAlias(const MDefinition *def) const;
 };
 
-class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
+class MAsmJSStoreHeap
+  : public MBinaryInstruction,
+    public MAsmJSHeapAccess,
+    public NoTypePolicy::Data
 {
     MemoryBarrierBits barrierBefore_;
     MemoryBarrierBits barrierAfter_;
@@ -12335,7 +12441,10 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
     }
 };
 
-class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapAccess
+class MAsmJSCompareExchangeHeap
+  : public MTernaryInstruction,
+    public MAsmJSHeapAccess,
+    public NoTypePolicy::Data
 {
     MAsmJSCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv,
                               bool needsBoundsCheck)
@@ -12365,7 +12474,10 @@ class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapA
     }
 };
 
-class MAsmJSAtomicBinopHeap : public MBinaryInstruction, public MAsmJSHeapAccess
+class MAsmJSAtomicBinopHeap
+  : public MBinaryInstruction,
+    public MAsmJSHeapAccess,
+    public NoTypePolicy::Data
 {
     AtomicOp op_;
 
@@ -12432,7 +12544,9 @@ class MAsmJSLoadGlobalVar : public MNullaryInstruction
     bool mightAlias(const MDefinition *def) const;
 };
 
-class MAsmJSStoreGlobalVar : public MUnaryInstruction
+class MAsmJSStoreGlobalVar
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MAsmJSStoreGlobalVar(unsigned globalDataOffset, MDefinition *v)
       : MUnaryInstruction(v), globalDataOffset_(globalDataOffset)
@@ -12455,7 +12569,9 @@ class MAsmJSStoreGlobalVar : public MUnaryInstruction
     }
 };
 
-class MAsmJSLoadFuncPtr : public MUnaryInstruction
+class MAsmJSLoadFuncPtr
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MAsmJSLoadFuncPtr(unsigned globalDataOffset, MDefinition *index)
       : MUnaryInstruction(index), globalDataOffset_(globalDataOffset)
@@ -12525,7 +12641,9 @@ class MAsmJSParameter : public MNullaryInstruction
     ABIArg abi() const { return abi_; }
 };
 
-class MAsmJSReturn : public MAryControlInstruction<1, 0>
+class MAsmJSReturn
+  : public MAryControlInstruction<1, 0>,
+    public NoTypePolicy::Data
 {
     explicit MAsmJSReturn(MDefinition *ins) {
         initOperand(0, ins);
@@ -12538,7 +12656,9 @@ class MAsmJSReturn : public MAryControlInstruction<1, 0>
     }
 };
 
-class MAsmJSVoidReturn : public MAryControlInstruction<0, 0>
+class MAsmJSVoidReturn
+  : public MAryControlInstruction<0, 0>,
+    public NoTypePolicy::Data
 {
   public:
     INSTRUCTION_HEADER(AsmJSVoidReturn);
@@ -12547,7 +12667,9 @@ class MAsmJSVoidReturn : public MAryControlInstruction<0, 0>
     }
 };
 
-class MAsmJSPassStackArg : public MUnaryInstruction
+class MAsmJSPassStackArg
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
 {
     MAsmJSPassStackArg(uint32_t spOffset, MDefinition *ins)
       : MUnaryInstruction(ins),
@@ -12572,7 +12694,9 @@ class MAsmJSPassStackArg : public MUnaryInstruction
     }
 };
 
-class MAsmJSCall MOZ_FINAL : public MVariadicInstruction
+class MAsmJSCall MOZ_FINAL
+  : public MVariadicInstruction,
+    public NoTypePolicy::Data
 {
   public:
     class Callee {
diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp
index 98798769be8..bf982032823 100644
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -27,15 +27,7 @@ EnsureOperandNotFloat32(TempAllocator &alloc, MInstruction *def, unsigned op)
 }
 
 MDefinition *
-BoxInputsPolicy::boxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand)
-{
-    if (operand->isUnbox())
-        return operand->toUnbox()->input();
-    return alwaysBoxAt(alloc, at, operand);
-}
-
-MDefinition *
-BoxInputsPolicy::alwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand)
+js::jit::AlwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand)
 {
     MDefinition *boxedOperand = operand;
     // Replace Float32 by double
@@ -49,14 +41,22 @@ BoxInputsPolicy::alwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition
     return box;
 }
 
+static MDefinition *
+BoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand)
+{
+    if (operand->isUnbox())
+        return operand->toUnbox()->input();
+    return AlwaysBoxAt(alloc, at, operand);
+}
+
 bool
-BoxInputsPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
+BoxInputsPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == MIRType_Value)
             continue;
-        ins->replaceOperand(i, boxAt(alloc, ins, in));
+        ins->replaceOperand(i, BoxAt(alloc, ins, in));
     }
     return true;
 }
@@ -66,7 +66,7 @@ ArithPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MIRType specialization = ins->typePolicySpecialization();
     if (specialization == MIRType_None)
-        return BoxInputsPolicy::adjustInputs(alloc, ins);
+        return BoxInputsPolicy::staticAdjustInputs(alloc, ins);
 
     MOZ_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32 || ins->type() == MIRType_Float32);
 
@@ -114,7 +114,7 @@ ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
     if (compare->compareType() == MCompare::Compare_Unknown ||
         compare->compareType() == MCompare::Compare_Value)
     {
-        return BoxInputsPolicy::adjustInputs(alloc, def);
+        return BoxInputsPolicy::staticAdjustInputs(alloc, def);
     }
 
     // Compare_Boolean specialization is done for "Anything === Bool"
@@ -255,14 +255,14 @@ TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
         // XXX: Possible optimization: decrease resultTypeSet to only include
         // the inputType. This will remove the need for boxing.
         MOZ_ASSERT(inputType != MIRType_Value);
-        ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
+        ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
         return true;
     }
 
     // Box input if needed.
     if (inputType != MIRType_Value) {
         MOZ_ASSERT(ins->alwaysBails());
-        ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
+        ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
     }
 
     // We can't unbox a value to null/undefined/lazyargs. So keep output
@@ -319,7 +319,7 @@ TestPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
       }
 
       default:
-        ins->replaceOperand(0, boxAt(alloc, ins, op));
+        ins->replaceOperand(0, BoxAt(alloc, ins, op));
         break;
     }
     return true;
@@ -330,7 +330,7 @@ BitwisePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MIRType specialization = ins->typePolicySpecialization();
     if (specialization == MIRType_None)
-        return BoxInputsPolicy::adjustInputs(alloc, ins);
+        return BoxInputsPolicy::staticAdjustInputs(alloc, ins);
 
     MOZ_ASSERT(ins->type() == specialization);
     MOZ_ASSERT(specialization == MIRType_Int32 || specialization == MIRType_Double);
@@ -528,7 +528,7 @@ BoxPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
     if (in->type() == MIRType_Value)
         return true;
 
-    ins->replaceOperand(Op, boxAt(alloc, ins, in));
+    ins->replaceOperand(Op, BoxAt(alloc, ins, in));
     return true;
 }
 
@@ -594,7 +594,7 @@ ToDoublePolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
         break;
     }
 
-    in = boxAt(alloc, ins, in);
+    in = BoxAt(alloc, ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
@@ -642,7 +642,7 @@ ToInt32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
         break;
     }
 
-    in = boxAt(alloc, ins, in);
+    in = BoxAt(alloc, ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
@@ -654,7 +654,7 @@ ToStringPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 
     MIRType type = ins->getOperand(0)->type();
     if (type == MIRType_Object || type == MIRType_Symbol) {
-        ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
+        ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
         return true;
     }
 
@@ -712,14 +712,14 @@ bool
 CallSetElementPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     // The first operand should be an object.
-    SingleObjectPolicy::adjustInputs(alloc, ins);
+    SingleObjectPolicy::staticAdjustInputs(alloc, ins);
 
     // Box the index and value operands.
     for (size_t i = 1, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == MIRType_Value)
             continue;
-        ins->replaceOperand(i, boxAt(alloc, ins, in));
+        ins->replaceOperand(i, BoxAt(alloc, ins, in));
     }
     return true;
 }
@@ -761,7 +761,7 @@ StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins,
       case MIRType_Object:
       case MIRType_String:
       case MIRType_Symbol:
-        value = boxAt(alloc, ins, value);
+        value = BoxAt(alloc, ins, value);
         break;
       default:
         MOZ_CRASH("Unexpected type");
@@ -836,7 +836,7 @@ StoreTypedArrayHolePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
     MOZ_ASSERT(store->index()->type() == MIRType_Int32);
     MOZ_ASSERT(store->length()->type() == MIRType_Int32);
 
-    return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 3);
+    return StoreTypedArrayPolicy::adjustValueInput(alloc, ins, store->arrayType(), store->value(), 3);
 }
 
 bool
@@ -845,7 +845,7 @@ StoreTypedArrayElementStaticPolicy::adjustInputs(TempAllocator &alloc, MInstruct
     MStoreTypedArrayElementStatic *store = ins->toStoreTypedArrayElementStatic();
 
     return ConvertToInt32Policy<0>::staticAdjustInputs(alloc, ins) &&
-        adjustValueInput(alloc, ins, store->viewType(), store->value(), 1);
+        StoreTypedArrayPolicy::adjustValueInput(alloc, ins, store->viewType(), store->value(), 1);
 }
 
 bool
@@ -894,7 +894,7 @@ ClampPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
       case MIRType_Value:
         break;
       default:
-          ins->replaceOperand(0, boxAt(alloc, ins, in));
+          ins->replaceOperand(0, BoxAt(alloc, ins, in));
         break;
     }
 
@@ -915,7 +915,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
     // Output is a value, box the input.
     if (outputType == MIRType_Value) {
         MOZ_ASSERT(inputType != MIRType_Value);
-        ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
+        ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
         return true;
     }
 
@@ -927,7 +927,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
         ins->block()->insertBefore(ins, bail);
         bail->setDependency(ins->dependency());
         ins->setDependency(bail);
-        ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
+        ins->replaceOperand(0, BoxAt(alloc, ins, ins->getOperand(0)));
     }
 
     // We can't unbox a value to null/undefined/lazyargs. So keep output
@@ -993,8 +993,10 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
     _(Mix3Policy, IntPolicy<1>, BoxPolicy<2> >)         \
     _(Mix3Policy, IntPolicy<1>, IntPolicy<2> >)         \
     _(Mix3Policy, ObjectPolicy<1>, IntPolicy<2> >)      \
+    _(Mix3Policy, IntPolicy<1>, IntPolicy<2>>)          \
     _(Mix3Policy, ObjectPolicy<1>, StringPolicy<2> >)   \
     _(Mix3Policy, StringPolicy<1>, StringPolicy<2> >)   \
+    _(Mix4Policy, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
     _(MixPolicy, ObjectPolicy<1> >)                        \
     _(MixPolicy, ConvertToStringPolicy<1> >)   \
     _(MixPolicy, ObjectPolicy<1> >)            \
@@ -1046,15 +1048,15 @@ namespace jit {
 
 namespace {
 
-// Default function visited by the C++ lookup rules, if the MIR Instruction does
-// not inherit from a TypePolicy::Data type.
-static TypePolicy *
-thisTypePolicy() {
-    return nullptr;
-}
+// For extra-good measure in case an unqualified use is ever introduced.  (The
+// main use in the macro below is explicitly qualified so as not to consult
+// this scope and find this function.)
+inline TypePolicy *
+thisTypePolicy() MOZ_DELETE;
 
 static MIRType
-thisTypeSpecialization() {
+thisTypeSpecialization()
+{
     MOZ_CRASH("TypeSpecialization lacks definition of thisTypeSpecialization.");
 }
 
@@ -1069,16 +1071,15 @@ MGetElementCache::thisTypePolicy()
 }
 
 // For each MIR Instruction, this macro define the |typePolicy| method which is
-// using the |thisTypePolicy| function.  We use the C++ lookup rules to select
-// the right |thisTypePolicy| member.  The |thisTypePolicy| function can either
-// be a member of the MIR Instruction, such as done for MGetElementCache, or a
-// member inherited from the TypePolicy::Data structure, or at last the global
-// with the same name if the instruction has no TypePolicy.
+// using the |thisTypePolicy| method.  The |thisTypePolicy| method is either a
+// member of the MIR Instruction, such as with MGetElementCache, a member
+// inherited from the TypePolicy::Data structure, or a member inherited from
+// NoTypePolicy if the MIR instruction has no type policy.
 #define DEFINE_MIR_TYPEPOLICY_MEMBERS_(op)      \
     TypePolicy *                                \
     js::jit::M##op::typePolicy()                \
     {                                           \
-        return thisTypePolicy();                \
+        return M##op::thisTypePolicy();         \
     }                                           \
                                                 \
     MIRType                                     \
diff --git a/js/src/jit/TypePolicy.h b/js/src/jit/TypePolicy.h
index 199e8030660..246405f9f75 100644
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -16,6 +16,9 @@ namespace jit {
 class MInstruction;
 class MDefinition;
 
+extern MDefinition *
+AlwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand);
+
 // A type policy directs the type analysis phases, which insert conversion,
 // boxing, unboxing, and type changes as necessary.
 class TypePolicy
@@ -62,136 +65,146 @@ struct TypeSpecializationData
 
 #define SPECIALIZATION_DATA_ INHERIT_DATA_(TypeSpecializationData)
 
-class BoxInputsPolicy : public TypePolicy
+class NoTypePolicy
 {
-  protected:
-    static MDefinition *boxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand);
-
   public:
-    EMPTY_DATA_;
-    static MDefinition *alwaysBoxAt(TempAllocator &alloc, MInstruction *at, MDefinition *operand);
-    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    struct Data
+    {
+        static TypePolicy *thisTypePolicy() {
+            return nullptr;
+        }
+    };
 };
 
-class ArithPolicy : public BoxInputsPolicy
+class BoxInputsPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
+        return staticAdjustInputs(alloc, def);
+    }
 };
 
-class BitwisePolicy : public BoxInputsPolicy
+class ArithPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
-class ComparePolicy : public BoxInputsPolicy
+class BitwisePolicy MOZ_FINAL : public TypePolicy
+{
+  public:
+    SPECIALIZATION_DATA_;
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
+};
+
+class ComparePolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 // Policy for MTest instructions.
-class TestPolicy : public BoxInputsPolicy
+class TestPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class TypeBarrierPolicy : public BoxInputsPolicy
+class TypeBarrierPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class CallPolicy : public BoxInputsPolicy
+class CallPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 // Policy for MPow. First operand Double; second Double or Int32.
-class PowPolicy : public BoxInputsPolicy
+class PowPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
 // Expect a string for operand Op. If the input is a Value, it is unboxed.
 template 
-class StringPolicy : public BoxInputsPolicy
+class StringPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Expect a string for operand Op. Else a ToString instruction is inserted.
 template 
-class ConvertToStringPolicy : public TypePolicy
+class ConvertToStringPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Expect an Int for operand Op. If the input is a Value, it is unboxed.
 template 
-class IntPolicy : public BoxInputsPolicy
+class IntPolicy MOZ_FINAL : private TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Expect an Int for operand Op. Else a ToInt32 instruction is inserted.
 template 
-class ConvertToInt32Policy : public BoxInputsPolicy
+class ConvertToInt32Policy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Expect a double for operand Op. If the input is a Value, it is unboxed.
 template 
-class DoublePolicy : public BoxInputsPolicy
+class DoublePolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Expect a float32 for operand Op. If the input is a Value, it is unboxed.
 template 
-class Float32Policy : public BoxInputsPolicy
+class Float32Policy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
@@ -199,7 +212,7 @@ class Float32Policy : public BoxInputsPolicy
 // Expect a float32 OR a double for operand Op, but will prioritize Float32
 // if the result type is set as such. If the input is a Value, it is unboxed.
 template 
-class FloatingPointPolicy : public TypePolicy
+class FloatingPointPolicy MOZ_FINAL : public TypePolicy
 {
 
   public:
@@ -219,16 +232,16 @@ class FloatingPointPolicy : public TypePolicy
 
     INHERIT_DATA_(PolicyTypeData);
 
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 template 
-class NoFloatPolicy : public TypePolicy
+class NoFloatPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
@@ -236,53 +249,53 @@ class NoFloatPolicy : public TypePolicy
 // Policy for guarding variadic instructions such as object / array state
 // instructions.
 template 
-class NoFloatPolicyAfter : public TypePolicy
+class NoFloatPolicyAfter MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
 // Box objects or strings as an input to a ToDouble instruction.
-class ToDoublePolicy : public BoxInputsPolicy
+class ToDoublePolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Box objects, strings and undefined as input to a ToInt32 instruction.
-class ToInt32Policy : public BoxInputsPolicy
+class ToInt32Policy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 // Box objects as input to a ToString instruction.
-class ToStringPolicy : public BoxInputsPolicy
+class ToStringPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, def);
     }
 };
 
 template 
-class ObjectPolicy : public BoxInputsPolicy
+class ObjectPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, ins);
     }
 };
@@ -292,19 +305,19 @@ class ObjectPolicy : public BoxInputsPolicy
 typedef ObjectPolicy<0> SingleObjectPolicy;
 
 template 
-class BoxPolicy : public BoxInputsPolicy
+class BoxPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins);
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, ins);
     }
 };
 
 // Boxes everything except inputs of type Type.
 template 
-class BoxExceptPolicy : public TypePolicy
+class BoxExceptPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
@@ -316,21 +329,21 @@ class BoxExceptPolicy : public TypePolicy
 
 // Combine multiple policies.
 template 
-class MixPolicy : public TypePolicy
+class MixPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
     static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) {
         return Lhs::staticAdjustInputs(alloc, ins) && Rhs::staticAdjustInputs(alloc, ins);
     }
-    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, ins);
     }
 };
 
 // Combine three policies.
 template 
-class Mix3Policy : public TypePolicy
+class Mix3Policy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
@@ -339,71 +352,94 @@ class Mix3Policy : public TypePolicy
                Policy2::staticAdjustInputs(alloc, ins) &&
                Policy3::staticAdjustInputs(alloc, ins);
     }
-    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) {
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
         return staticAdjustInputs(alloc, ins);
     }
 };
 
-class CallSetElementPolicy : public SingleObjectPolicy
+// Combine four policies.  (Missing variadic templates yet?)
+template 
+class Mix4Policy : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    static bool staticAdjustInputs(TempAllocator &alloc, MInstruction *ins) {
+        return Policy1::staticAdjustInputs(alloc, ins) &&
+               Policy2::staticAdjustInputs(alloc, ins) &&
+               Policy3::staticAdjustInputs(alloc, ins) &&
+               Policy4::staticAdjustInputs(alloc, ins);
+    }
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE {
+        return staticAdjustInputs(alloc, ins);
+    }
+};
+
+class CallSetElementPolicy MOZ_FINAL : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 // First operand will be boxed to a Value (except for an object)
 // Second operand (if specified) will forcefully be unboxed to an object
-class InstanceOfPolicy : public TypePolicy
+class InstanceOfPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
-class StoreTypedArrayPolicy : public BoxInputsPolicy
+class StoreTypedArrayHolePolicy;
+class StoreTypedArrayElementStaticPolicy;
+
+class StoreTypedArrayPolicy : public TypePolicy
 {
-  protected:
-    bool adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType, MDefinition *value, int valueOperand);
+  private:
+    static bool adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType, MDefinition *value, int valueOperand);
+
+    friend class StoreTypedArrayHolePolicy;
+    friend class StoreTypedArrayElementStaticPolicy;
 
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class StoreTypedArrayHolePolicy : public StoreTypedArrayPolicy
+class StoreTypedArrayHolePolicy MOZ_FINAL : public StoreTypedArrayPolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class StoreTypedArrayElementStaticPolicy : public StoreTypedArrayPolicy
+class StoreTypedArrayElementStaticPolicy MOZ_FINAL : public StoreTypedArrayPolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class StoreUnboxedObjectOrNullPolicy : public TypePolicy
+class StoreUnboxedObjectOrNullPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 // Accepts integers and doubles. Everything else is boxed.
-class ClampPolicy : public BoxInputsPolicy
+class ClampPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
-class FilterTypeSetPolicy : public BoxInputsPolicy
+class FilterTypeSetPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     EMPTY_DATA_;
-    bool adjustInputs(TempAllocator &alloc, MInstruction *ins);
+    virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
 
 static inline bool
diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp
index 1d9ced6cb9c..364d4957f05 100644
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2127,10 +2127,11 @@ CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 
     MIRType type = mir->value()->type();
     MOZ_ASSERT(IsNumberType(type));
+
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
-    if (mir->value()->type() == MIRType_Int32) {
+    if (type == MIRType_Int32) {
         masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
-    } else if (mir->value()->type() == MIRType_Float32) {
+    } else if (type == MIRType_Float32) {
         VFPRegister vd(ToFloatRegister(ins->value()));
         masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr));
     } else {
diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build
index 30e010f52ee..87ff5ecf75e 100644
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -30,6 +30,7 @@ UNIFIED_SOURCES += [
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForOfIterator.cpp',
+    'testForwardSetProperty.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
diff --git a/js/src/jsapi-tests/testForwardSetProperty.cpp b/js/src/jsapi-tests/testForwardSetProperty.cpp
new file mode 100644
index 00000000000..1886b8659ce
--- /dev/null
+++ b/js/src/jsapi-tests/testForwardSetProperty.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* 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 "jsapi-tests/tests.h"
+
+BEGIN_TEST(testForwardSetProperty)
+{
+    JS::RootedValue v1(cx);
+    EVAL("var foundValue; \n"
+         "var obj1 = { set prop(val) { foundValue = this; } }; \n"
+         "obj1;",
+         &v1);
+
+    JS::RootedValue v2(cx);
+    EVAL("var obj2 = Object.create(obj1); \n"
+         "obj2;",
+         &v2);
+
+    JS::RootedValue v3(cx);
+    EVAL("var obj3 = {}; \n"
+         "obj3;",
+         &v3);
+
+    JS::RootedObject obj1(cx, &v1.toObject());
+    JS::RootedObject obj2(cx, &v2.toObject());
+    JS::RootedObject obj3(cx, &v3.toObject());
+
+    JS::RootedValue setval(cx, JS::Int32Value(42));
+
+    JS::RootedValue propkey(cx);
+    EVAL("'prop';", &propkey);
+
+    JS::RootedId prop(cx);
+    CHECK(JS_ValueToId(cx, propkey, &prop));
+
+    EXEC("function assertEq(a, b, msg) \n"
+         "{ \n"
+         "  if (!Object.is(a, b)) \n"
+         "    throw new Error('Assertion failure: ' + msg); \n"
+         "}");
+
+    // Non-strict setter
+
+    CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, v3, true, setval));
+
+    EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to setter');");
+
+    CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, true, setval));
+
+    EXEC("assertEq(typeof foundValue === 'object', true, \n"
+         "         'passing 42 as receiver to non-strict setter ' + \n"
+         "         'must box');");
+
+    EXEC("assertEq(foundValue instanceof Number, true, \n"
+         "         'passing 42 as receiver to non-strict setter ' + \n"
+         "         'must box to a Number');");
+
+    EXEC("assertEq(foundValue.valueOf(), 42, \n"
+         "         'passing 42 as receiver to non-strict setter ' + \n"
+         "         'must box to new Number(42)');");
+
+    // Strict setter
+
+    EVAL("obj1 = { set prop(val) { 'use strict'; foundValue = this; } }; \n"
+         "obj1;",
+         &v1);
+
+    CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, v3, true, setval));
+
+    EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to strict setter');");
+
+    CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, true, setval));
+
+
+    JS::RootedValue strictSetSupported(cx);
+    EVAL("var strictSetSupported = false; \n"
+         "Object.defineProperty(Object.prototype, \n"
+         "                      'strictSetter', \n"
+         "                      { \n"
+         "                        set(v) { \n"
+         "                          'use strict'; \n"
+         "                          strictSetSupported = \n"
+         "                            typeof this === 'number'; \n"
+         "                        } \n"
+         "                      }); \n"
+         "17..strictSetter = 42; \n"
+         "strictSetSupported;",
+         &strictSetSupported);
+    CHECK(strictSetSupported.isBoolean());
+
+    if (strictSetSupported.toBoolean()) {
+        // XXX Bug 603201 will fix this.
+        MOZ_ASSERT(false,
+                   "remove the support-testing check when bug 603201 is fixt");
+        EXEC("assertEq(foundValue, 42, \n"
+             "         '42 passed as receiver to strict setter ' + \n"
+             "         'was mangled');");
+    }
+
+    return true;
+}
+END_TEST(testForwardSetProperty)
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 23d313a1855..ab04d153420 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3054,6 +3054,24 @@ JS_SetPropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue v)
     return JSObject::setGeneric(cx, obj, obj, id, &value, false);
 }
 
+JS_PUBLIC_API(bool)
+JS_ForwardSetPropertyTo(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf,
+                        bool strict, JS::HandleValue v)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+    assertSameCompartment(cx, onBehalfOf);
+
+    // XXX Bug 603201 will eliminate this ToObject.
+    RootedObject receiver(cx, ToObject(cx, onBehalfOf));
+    if (!receiver)
+        return false;
+
+    RootedValue value(cx, v);
+    return JSObject::setGeneric(cx, obj, receiver, id, &value, strict);
+}
+
 static bool
 SetElement(JSContext *cx, HandleObject obj, uint32_t index, MutableHandleValue vp)
 {
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 5f2db1c4ba1..5ebb8a8bbf6 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1116,6 +1116,13 @@ ToBooleanSlow(JS::HandleValue v);
  */
 extern JS_PUBLIC_API(JSString*)
 ToStringSlow(JSContext *cx, JS::HandleValue v);
+
+/*
+ * DO NOT CALL THIS. Use JS::ToObject.
+ */
+extern JS_PUBLIC_API(JSObject*)
+ToObjectSlow(JSContext *cx, JS::HandleValue vp, bool reportScanStack);
+
 } /* namespace js */
 
 namespace JS {
@@ -1161,6 +1168,15 @@ ToString(JSContext *cx, HandleValue v)
     return js::ToStringSlow(cx, v);
 }
 
+/* ES5 9.9 ToObject. */
+MOZ_ALWAYS_INLINE JSObject*
+ToObject(JSContext *cx, HandleValue vp)
+{
+    if (vp.isObject())
+        return &vp.toObject();
+    return js::ToObjectSlow(cx, vp, false);
+}
+
 /*
  * Implements ES6 draft rev 28 (2014 Oct 14) 7.1.1, second algorithm.
  *
@@ -3213,6 +3229,10 @@ JS_SetProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::Handle
 extern JS_PUBLIC_API(bool)
 JS_SetPropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
 
+extern JS_PUBLIC_API(bool)
+JS_ForwardSetPropertyTo(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf,
+                        bool strict, JS::HandleValue vp);
+
 extern JS_PUBLIC_API(bool)
 JS_DeleteProperty(JSContext *cx, JS::HandleObject obj, const char *name);
 
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index 3109b20dc78..6f55dd929d8 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3617,7 +3617,13 @@ js::PrimitiveToObject(JSContext *cx, const Value &v)
     return SymbolObject::create(cx, v.toSymbol());
 }
 
-/* Callers must handle the already-object case. */
+/*
+ * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
+ * already be an object, use ToObject. reportCantConvert controls how null and
+ * undefined errors are reported.
+ *
+ * Callers must handle the already-object case.
+ */
 JSObject *
 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
 {
diff --git a/js/src/jsobj.h b/js/src/jsobj.h
index 4036631ac47..166b4f643ea 100644
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1142,30 +1142,13 @@ PrimitiveToObject(JSContext *cx, const Value &v);
 
 namespace js {
 
-/*
- * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
- * already be an object, use ToObject. reportCantConvert controls how null and
- * undefined errors are reported.
- */
-extern JSObject *
-ToObjectSlow(JSContext *cx, HandleValue vp, bool reportScanStack);
-
-/* For object conversion in e.g. native functions. */
-MOZ_ALWAYS_INLINE JSObject *
-ToObject(JSContext *cx, HandleValue vp)
-{
-    if (vp.isObject())
-        return &vp.toObject();
-    return ToObjectSlow(cx, vp, false);
-}
-
 /* For converting stack values to objects. */
 MOZ_ALWAYS_INLINE JSObject *
 ToObjectFromStack(JSContext *cx, HandleValue vp)
 {
     if (vp.isObject())
         return &vp.toObject();
-    return ToObjectSlow(cx, vp, true);
+    return js::ToObjectSlow(cx, vp, true);
 }
 
 template
diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp
index 5c81b3cb139..8da5b2b74ca 100644
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -1111,7 +1111,16 @@ bool
 ScriptedDirectProxyHandler::isCallable(JSObject *obj) const
 {
     MOZ_ASSERT(obj->as().handler() == &ScriptedDirectProxyHandler::singleton);
-    return obj->as().extra(IS_CALLABLE_EXTRA).toBoolean();
+    uint32_t callConstruct = obj->as().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
+    return !!(callConstruct & IS_CALLABLE);
+}
+
+bool
+ScriptedDirectProxyHandler::isConstructor(JSObject *obj) const
+{
+    MOZ_ASSERT(obj->as().handler() == &ScriptedDirectProxyHandler::singleton);
+    uint32_t callConstruct = obj->as().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
+    return !!(callConstruct & IS_CONSTRUCTOR);
 }
 
 const char ScriptedDirectProxyHandler::family = 0;
@@ -1139,9 +1148,13 @@ js::proxy(JSContext *cx, unsigned argc, jsval *vp)
     if (!proxy_)
         return false;
     Rooted proxy(cx, &proxy_->as());
-    bool targetIsCallable = target->isCallable(); // Can GC - don't compute it inline.
     proxy->setExtra(ScriptedDirectProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
-    proxy->setExtra(ScriptedDirectProxyHandler::IS_CALLABLE_EXTRA, BooleanValue(targetIsCallable));
+
+    // Assign [[Call]] and [[Construct]]
+    uint32_t callable = target->isCallable() ? ScriptedDirectProxyHandler::IS_CALLABLE : 0;
+    uint32_t constructor = target->isConstructor() ? ScriptedDirectProxyHandler::IS_CONSTRUCTOR : 0;
+    proxy->as().setExtra(ScriptedDirectProxyHandler::IS_CALLCONSTRUCT_EXTRA,
+                                      PrivateUint32Value(callable | constructor));
     args.rval().setObject(*proxy);
     return true;
 }
diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h
index 004706177c6..c23fa6cc066 100644
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -65,11 +65,8 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
     }
 
     virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
-    virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
-        // For now we maintain the broken behavior that a scripted proxy is constructable if it's
-        // callable. See bug 929467.
-        return isCallable(obj);
-    }
+    virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
+
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
     static const char family;
@@ -78,7 +75,10 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
     // The "proxy extra" slot index in which the handler is stored. Revocable proxies need to set
     // this at revocation time.
     static const int HANDLER_EXTRA = 0;
-    static const int IS_CALLABLE_EXTRA = 1;
+    static const int IS_CALLCONSTRUCT_EXTRA = 1;
+    // Bitmasks for the "call/construct" slot
+    static const int IS_CALLABLE    = 1 << 0;
+    static const int IS_CONSTRUCTOR = 1 << 1;
     // The "function extended" slot index in which the revocation object is stored. Per spec, this
     // is to be cleared during the first revocation.
     static const int REVOKE_SLOT = 0;
diff --git a/js/src/tests/ecma_6/TypedArray/every-and-some.js b/js/src/tests/ecma_6/TypedArray/every-and-some.js
new file mode 100644
index 00000000000..132ea16db22
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/every-and-some.js
@@ -0,0 +1,261 @@
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array
+];
+
+// Tests for TypedArray#every.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.every.length, 1);
+
+    // Basic tests.
+    assertEq(new constructor([1, 3, 5]).every(v => v % 2), true);
+    assertEq(new constructor([1, 3, 5]).every(v => v > 2), false);
+    assertEq(new constructor(10).every(v => v === 0), true);
+    assertEq(new constructor().every(v => v > 1), true);
+
+    var arr = new constructor([1, 2, 3, 4, 5]);
+    var sum = 0;
+    var count = 0;
+    assertEq(arr.every((v, k, o) => {
+        count++;
+        sum += v;
+        assertEq(k, v - 1);
+        assertEq(o, arr);
+        return v < 3;
+    }), false);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // Tests for `thisArg` argument.
+    function assertThisArg(thisArg, thisValue) {
+        // In sloppy mode, `this` could be global object or a wrapper of `thisArg`.
+        assertEq(arr.every(function() {
+            assertDeepEq(this, thisValue);
+            return true;
+        }, thisArg), true);
+
+        // In strict mode, `this` strictly equals `thisArg`.
+        assertEq(arr.every(function() {
+            "use strict";
+            assertDeepEq(this, thisArg);
+            return true;
+        }, thisArg), true);
+
+        // Passing `thisArg` has no effect if callback is an arrow function.
+        var self = this;
+        assertEq(arr.every(() => {
+            assertEq(this, self);
+            return true;
+        }, thisArg), true);
+    }
+    assertThisArg([1, 2, 3], [1, 2, 3]);
+    assertThisArg(Object, Object);
+    assertThisArg(1, Object(1));
+    assertThisArg("1", Object("1"));
+    assertThisArg(false, Object(false));
+    assertThisArg(undefined, this);
+    assertThisArg(null, this);
+
+    // Throw an exception in the callback.
+    var sum = 0;
+    var count = 0;
+    var thrown = false;
+    try {
+        arr.every((v, k, o) => {
+            count++;
+            sum += v;
+            assertEq(k, v - 1);
+            assertEq(o, arr);
+            if (v === 3) {
+                throw "every";
+            }
+            return true
+        })
+    } catch(e) {
+        assertEq(e, "every");
+        thrown = true;
+    }
+    assertEq(thrown, true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.every();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.every(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.every(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var every = newGlobal()[constructor.name].prototype.every;
+        var sum = 0;
+        assertEq(every.call(new constructor([1, 2, 3]), v => sum += v), true);
+        assertEq(sum, 6);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.every.call(invalidReceiver, () => true);
+        }, TypeError, "Assert that every fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.every.call(new Proxy(new constructor(), {}), () => true);
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).every(() => true), true);
+}
+
+assertEq(new Float32Array([undefined, , NaN]).every(v => Object.is(v, NaN)), true);
+assertEq(new Float64Array([undefined, , NaN]).every(v => Object.is(v, NaN)), true);
+
+// Tests for TypedArray#some.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.some.length, 1);
+
+    // Basic tests.
+    assertEq(new constructor([1, 2, 3]).some(v => v % 2), true);
+    assertEq(new constructor([0, 2, 4]).some(v => v % 2), false);
+    assertEq(new constructor([1, 3, 5]).some(v => v > 2), true);
+    assertEq(new constructor([1, 3, 5]).some(v => v < 0), false);
+    assertEq(new constructor(10).some(v => v !== 0), false);
+    assertEq(new constructor().some(v => v > 1), false);
+
+    var arr = new constructor([1, 2, 3, 4, 5]);
+    var sum = 0;
+    var count = 0;
+    assertEq(arr.some((v, k, o) => {
+        count++;
+        sum += v;
+        assertEq(k, v - 1);
+        assertEq(o, arr);
+        return v > 2;
+    }), true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // Tests for `thisArg` argument.
+    function assertThisArg(thisArg, thisValue) {
+        // In sloppy mode, `this` could be global object or a wrapper of `thisArg`.
+        assertEq(arr.some(function() {
+            assertDeepEq(this, thisValue);
+            return false;
+        }, thisArg), false);
+
+        // In strict mode, `this` strictly equals `thisArg`.
+        assertEq(arr.some(function() {
+            "use strict";
+            assertDeepEq(this, thisArg);
+            return false;
+        }, thisArg), false);
+
+        // Passing `thisArg` has no effect if callback is an arrow function.
+        var self = this;
+        assertEq(arr.some(() => {
+            assertEq(this, self);
+            return false;
+        }, thisArg), false);
+    }
+    assertThisArg([1, 2, 3], [1, 2, 3]);
+    assertThisArg(Object, Object);
+    assertThisArg(1, Object(1));
+    assertThisArg("1", Object("1"));
+    assertThisArg(false, Object(false));
+    assertThisArg(undefined, this);
+    assertThisArg(null, this);
+
+    // Throw an exception in the callback.
+    var sum = 0;
+    var count = 0;
+    var thrown = false;
+    try {
+        arr.some((v, k, o) => {
+            count++;
+            sum += v;
+            assertEq(k, v - 1);
+            assertEq(o, arr);
+            if (v === 3) {
+                throw "some";
+            }
+            return false
+        })
+    } catch(e) {
+        assertEq(e, "some");
+        thrown = true;
+    }
+    assertEq(thrown, true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.some();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.some(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.some(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var some = newGlobal()[constructor.name].prototype.some;
+        var sum = 0;
+        assertEq(some.call(new constructor([1, 2, 3]), v => {
+            sum += v;
+            return false;
+        }), false);
+        assertEq(sum, 6);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.some.call(invalidReceiver, () => true);
+        }, TypeError, "Assert that some fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.some.call(new Proxy(new constructor(), {}), () => false);
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).some(() => false), false);
+}
+
+assertEq(new Float32Array([undefined, , NaN]).some(v => v === v), false);
+assertEq(new Float64Array([undefined, , NaN]).some(v => v === v), false);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
\ No newline at end of file
diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp
index e0a30673c50..d29eb1a9cce 100644
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -926,6 +926,8 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void
         parseTask->errors[i]->throwError(cx);
     if (parseTask->overRecursed)
         js_ReportOverRecursed(cx);
+    if (cx->isExceptionPending())
+        return nullptr;
 
     if (script) {
         // The Debugger only needs to be told about the topmost script that was compiled.
diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h
index e6248f0b919..0bf92b350fd 100644
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -485,12 +485,62 @@ static MOZ_ALWAYS_INLINE bool
 GetPrimitiveElementOperation(JSContext *cx, JSOp op, JS::HandleValue receiver,
                              HandleValue key, MutableHandleValue res)
 {
-    // FIXME: We shouldn't be boxing here (bug 603201).
+    MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
+
+    // FIXME: We shouldn't be boxing here or exposing the boxed object as
+    //        receiver anywhere below (bug 603201).
     RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
     if (!boxed)
         return false;
 
-    return GetObjectElementOperation(cx, op, boxed, key, res);
+    do {
+        uint32_t index;
+        if (IsDefinitelyIndex(key, &index)) {
+            if (JSObject::getElementNoGC(cx, boxed, boxed, index, res.address()))
+                break;
+
+            if (!JSObject::getElement(cx, boxed, boxed, index, res))
+                return false;
+            break;
+        }
+
+        if (IsSymbolOrSymbolWrapper(key)) {
+            RootedId id(cx, SYMBOL_TO_JSID(ToSymbolPrimitive(key)));
+            if (!JSObject::getGeneric(cx, boxed, boxed, id, res))
+                return false;
+            break;
+        }
+
+        if (JSAtom *name = ToAtom(cx, key)) {
+            if (name->isIndex(&index)) {
+                if (JSObject::getElementNoGC(cx, boxed, boxed, index, res.address()))
+                    break;
+            } else {
+                if (JSObject::getPropertyNoGC(cx, boxed, boxed, name->asPropertyName(),
+                                              res.address()))
+                {
+                    break;
+                }
+            }
+        }
+
+        JSAtom *name = ToAtom(cx, key);
+        if (!name)
+            return false;
+
+        if (name->isIndex(&index)) {
+            if (!JSObject::getElement(cx, boxed, boxed, index, res))
+                return false;
+        } else {
+            if (!JSObject::getProperty(cx, boxed, boxed, name->asPropertyName(), res))
+                return false;
+        }
+    } while (false);
+
+    // Note: we don't call a __noSuchMethod__ hook when |this| was primitive.
+
+    assertSameCompartmentDebugOnly(cx, res);
+    return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp
index b555e357340..8b73346800a 100644
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -710,6 +710,8 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext *cx)
     Rooted proto(cx, templateObject->getTaggedProto());
     types::TypeObject *type =
         cx->compartment()->types.newTypeObject(cx, templateObject->getClass(), proto);
+    if (!type)
+        return matchResultTemplateObject_; // = nullptr
     templateObject->setType(type);
 
     /* Set dummy index property */
diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h
index 8eeef7fc577..5d680df2a66 100644
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1492,8 +1492,9 @@ class AsmJSActivation : public Activation
     static unsigned offsetOfFP() { return offsetof(AsmJSActivation, fp_); }
     static unsigned offsetOfExitReason() { return offsetof(AsmJSActivation, exitReason_); }
 
-    // Set from SIGSEGV handler:
+    // Read/written from SIGSEGV handler:
     void setResumePC(void *pc) { resumePC_ = pc; }
+    void *resumePC() const { return resumePC_; }
 };
 
 // A FrameIter walks over the runtime's stack of JS script activations,
diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp
index da5e20639f4..37cd2125730 100644
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -781,6 +781,7 @@ TypedArrayObject::protoFunctions[] = {
     JS_FN("subarray", TypedArrayObject::subarray, 2, 0),
     JS_FN("set", TypedArrayObject::set, 2, 0),
     JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0),
+    JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 2, 0),
     JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
     JS_SELF_HOSTED_FN("find", "TypedArrayFind", 2, 0),
     JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 2, 0),
@@ -788,6 +789,7 @@ TypedArrayObject::protoFunctions[] = {
     JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
     JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0),
     JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
+    JS_SELF_HOSTED_FN("some", "TypedArraySome", 2, 0),
 #ifdef NIGHTLY_BUILD
     JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
 #endif
diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp
index 6a03d79130a..60122afdb6a 100644
--- a/js/xpconnect/src/nsScriptError.cpp
+++ b/js/xpconnect/src/nsScriptError.cpp
@@ -11,6 +11,8 @@
 
 #include "xpcprivate.h"
 #include "jsprf.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
 #include "nsGlobalWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsILoadContext.h"
@@ -29,12 +31,43 @@ nsScriptError::nsScriptError()
        mOuterWindowID(0),
        mInnerWindowID(0),
        mTimeStamp(0),
+       mInitializedOnMainThread(false),
        mIsFromPrivateWindow(false)
 {
 }
 
 nsScriptError::~nsScriptError() {}
 
+void
+nsScriptError::InitializeOnMainThread()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!mInitializedOnMainThread);
+
+    if (mInnerWindowID) {
+        nsGlobalWindow* window =
+          nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID);
+        if (window) {
+            nsPIDOMWindow* outer = window->GetOuterWindow();
+            if (outer)
+                mOuterWindowID = outer->WindowID();
+
+            nsIDocShell* docShell = window->GetDocShell();
+            nsCOMPtr loadContext = do_QueryInterface(docShell);
+
+            if (loadContext) {
+                // Never mark exceptions from chrome windows as having come from
+                // private windows, since we always want them to be reported.
+                nsIPrincipal* winPrincipal = window->GetPrincipal();
+                mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() &&
+                                       !nsContentUtils::IsSystemPrincipal(winPrincipal);
+            }
+        }
+    }
+
+    mInitializedOnMainThread = true;
+}
+
 // nsIConsoleMessage methods
 NS_IMETHODIMP
 nsScriptError::GetMessageMoz(char16_t **result) {
@@ -140,26 +173,8 @@ nsScriptError::InitWithWindowID(const nsAString& message,
     mTimeStamp = JS_Now() / 1000;
     mInnerWindowID = aInnerWindowID;
 
-    if (aInnerWindowID) {
-        nsGlobalWindow* window =
-          nsGlobalWindow::GetInnerWindowWithId(aInnerWindowID);
-        if (window) {
-            nsPIDOMWindow* outer = window->GetOuterWindow();
-            if (outer)
-                mOuterWindowID = outer->WindowID();
-
-            nsIDocShell* docShell = window->GetDocShell();
-            nsCOMPtr loadContext = do_QueryInterface(docShell);
-
-            if (loadContext) {
-                // Never mark exceptions from chrome windows as having come from
-                // private windows, since we always want them to be reported.
-                nsIPrincipal* winPrincipal = window->GetPrincipal();
-                mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() &&
-                                       !nsContentUtils::IsSystemPrincipal(winPrincipal);
-            }
-
-        }
+    if (aInnerWindowID && NS_IsMainThread()) {
+        InitializeOnMainThread();
     }
 
     return NS_OK;
@@ -229,6 +244,14 @@ nsScriptError::ToString(nsACString& /*UTF8*/ aResult)
 NS_IMETHODIMP
 nsScriptError::GetOuterWindowID(uint64_t *aOuterWindowID)
 {
+    NS_WARN_IF_FALSE(NS_IsMainThread() || mInitializedOnMainThread,
+                     "This can't be safely determined off the main thread, "
+                     "returning an inaccurate value!");
+
+    if (!mInitializedOnMainThread && NS_IsMainThread()) {
+        InitializeOnMainThread();
+    }
+
     *aOuterWindowID = mOuterWindowID;
     return NS_OK;
 }
@@ -250,6 +273,14 @@ nsScriptError::GetTimeStamp(int64_t *aTimeStamp)
 NS_IMETHODIMP
 nsScriptError::GetIsFromPrivateWindow(bool *aIsFromPrivateWindow)
 {
+    NS_WARN_IF_FALSE(NS_IsMainThread() || mInitializedOnMainThread,
+                     "This can't be safely determined off the main thread, "
+                     "returning an inaccurate value!");
+
+    if (!mInitializedOnMainThread && NS_IsMainThread()) {
+        InitializeOnMainThread();
+    }
+
     *aIsFromPrivateWindow = mIsFromPrivateWindow;
     return NS_OK;
 }
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
index 689765e9c10..c76e1843738 100644
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -77,6 +77,7 @@
 #include "mozilla/Alignment.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/GuardObjects.h"
@@ -2971,6 +2972,9 @@ public:
 private:
     virtual ~nsScriptError();
 
+    void
+    InitializeOnMainThread();
+
     nsString mMessage;
     nsString mSourceName;
     uint32_t mLineNumber;
@@ -2978,9 +2982,13 @@ private:
     uint32_t mColumnNumber;
     uint32_t mFlags;
     nsCString mCategory;
+    // mOuterWindowID is set on the main thread from InitializeOnMainThread().
     uint64_t mOuterWindowID;
     uint64_t mInnerWindowID;
     int64_t mTimeStamp;
+    // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
+    // thread from InitializeOnMainThread().
+    mozilla::Atomic mInitializedOnMainThread;
     bool mIsFromPrivateWindow;
 };
 
diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul
index 572187c2cb0..5222a171448 100644
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -181,7 +181,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
   gPrototypeProperties['TypedArray'] =
     ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray",
      "set", "copyWithin", "find", "findIndex", "indexOf", "lastIndexOf", "reverse",
-     "join"];
+     "join", "every", "some"];
   if (isNightlyBuild) {
     gPrototypeProperties['TypedArray'].push('includes');
   }
diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp
index 2cbe1a7046d..02a651af520 100644
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -761,14 +761,14 @@ void
 nsRefreshDriver::RestoreNormalRefresh()
 {
   mTestControllingRefreshes = false;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted(eAllowTimeToGoBackwards);
   mCompletedTransaction = mPendingTransaction;
 }
 
 TimeStamp
 nsRefreshDriver::MostRecentRefresh() const
 {
-  const_cast(this)->EnsureTimerStarted(false);
+  const_cast(this)->EnsureTimerStarted();
 
   return mMostRecentRefresh;
 }
@@ -776,7 +776,7 @@ nsRefreshDriver::MostRecentRefresh() const
 int64_t
 nsRefreshDriver::MostRecentRefreshEpochTime() const
 {
-  const_cast(this)->EnsureTimerStarted(false);
+  const_cast(this)->EnsureTimerStarted();
 
   return mMostRecentRefreshEpochTime;
 }
@@ -787,7 +787,7 @@ nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
 {
   ObserverArray& array = ArrayFor(aFlushType);
   bool success = array.AppendElement(aObserver) != nullptr;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
   return success;
 }
 
@@ -828,7 +828,7 @@ nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
     start->mEntries.PutEntry(aRequest);
   }
 
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 
   return true;
 }
@@ -849,13 +849,13 @@ nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
 }
 
 void
-nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
+nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
 {
   if (mTestControllingRefreshes)
     return;
 
   // will it already fire, and no other changes needed?
-  if (mActiveTimer && !aAdjustingTimer)
+  if (mActiveTimer && !(aFlags & eAdjustingTimer))
     return;
 
   if (IsFrozen() || !mPresContext) {
@@ -891,11 +891,19 @@ nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
   // timers, the most recent refresh of the new timer may be *before* the
   // most recent refresh of the old timer. However, the refresh driver time
   // should not go backwards so we clamp the most recent refresh time.
+  //
+  // The one exception to this is when we are restoring the refresh driver
+  // from test control in which case the time is expected to go backwards
+  // (see bug 1043078).
   mMostRecentRefresh =
-    std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
+    aFlags & eAllowTimeToGoBackwards
+    ? mActiveTimer->MostRecentRefresh()
+    : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
   mMostRecentRefreshEpochTime =
-    std::max(mActiveTimer->MostRecentRefreshEpochTime(),
-             mMostRecentRefreshEpochTime);
+    aFlags & eAllowTimeToGoBackwards
+    ? mActiveTimer->MostRecentRefreshEpochTime()
+    : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
+               mMostRecentRefreshEpochTime);
 }
 
 void
@@ -1503,7 +1511,7 @@ nsRefreshDriver::Thaw()
       // and notify our observers until we get back to the event loop.
       // Thus MostRecentRefresh() will lie between now and the DoRefresh.
       NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsRefreshDriver::DoRefresh));
-      EnsureTimerStarted(false);
+      EnsureTimerStarted();
     }
   }
 }
@@ -1631,7 +1639,7 @@ nsRefreshDriver::SetThrottled(bool aThrottled)
     if (mActiveTimer) {
       // We want to switch our timer type here, so just stop and
       // restart the timer.
-      EnsureTimerStarted(true);
+      EnsureTimerStarted(eAdjustingTimer);
     }
   }
 }
@@ -1661,7 +1669,7 @@ nsRefreshDriver::ScheduleViewManagerFlush()
   NS_ASSERTION(mPresContext->IsRoot(),
                "Should only schedule view manager flush on root prescontexts");
   mViewManagerFlushIsPending = true;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 }
 
 void
@@ -1674,7 +1682,7 @@ nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
 
   // make sure that the timer is running
   ConfigureHighPrecision();
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 }
 
 void
diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h
index 142fc1db20c..439883f9aef 100644
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -159,7 +159,7 @@ public:
       mStyleCause = profiler_get_backtrace();
     }
     bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
 
     return appended;
   }
@@ -177,7 +177,7 @@ public:
       mReflowCause = profiler_get_backtrace();
     }
     bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
     return appended;
   }
   void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
@@ -190,7 +190,7 @@ public:
     NS_ASSERTION(!mPresShellsToInvalidateIfHidden.Contains(aShell),
 		 "Double-adding style flush observer");
     bool appended = mPresShellsToInvalidateIfHidden.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
     return appended;
   }
   void RemovePresShellToInvalidateIfHidden(nsIPresShell* aShell) {
@@ -298,7 +298,12 @@ private:
 
   void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
 
-  void EnsureTimerStarted(bool aAdjustingTimer);
+  enum EnsureTimerStartedFlags {
+    eNone = 0,
+    eAdjustingTimer = 1 << 0,
+    eAllowTimeToGoBackwards = 1 << 1
+  };
+  void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
   void StopTimer();
 
   uint32_t ObserverCount() const;
diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp
index d3d86ef9d54..3c420c87c86 100644
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -81,7 +81,7 @@ nsContainerFrame*
 NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   nsListControlFrame* it =
-    new (aPresShell) nsListControlFrame(aPresShell, aPresShell->GetDocument(), aContext);
+    new (aPresShell) nsListControlFrame(aContext);
 
   it->AddStateBits(NS_FRAME_INDEPENDENT_SELECTION);
 
@@ -91,9 +91,8 @@ NS_NewListControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame)
 
 //---------------------------------------------------------
-nsListControlFrame::nsListControlFrame(
-  nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext)
-  : nsHTMLScrollFrame(aShell, aContext, false),
+nsListControlFrame::nsListControlFrame(nsStyleContext* aContext)
+  : nsHTMLScrollFrame(aContext, false),
     mMightNeedSecondPass(false),
     mHasPendingInterruptAtStartOfReflow(false),
     mDropdownCanGrow(false),
diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h
index fd20fb11aee..81bc9b000e4 100644
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -315,7 +315,7 @@ protected:
    */
   virtual void ResetList(bool aAllowScrolling);
 
-  nsListControlFrame(nsIPresShell* aShell, nsIDocument* aDocument, nsStyleContext* aContext);
+  explicit nsListControlFrame(nsStyleContext* aContext);
   virtual ~nsListControlFrame();
 
   /**
diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp
index 9c5daf7850d..c3ca51fbff8 100644
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -54,7 +54,7 @@ using namespace mozilla;
 nsIFrame*
 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
-  return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
+  return new (aPresShell) nsTextControlFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
@@ -99,7 +99,7 @@ private:
 };
 #endif
 
-nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
+nsTextControlFrame::nsTextControlFrame(nsStyleContext* aContext)
   : nsContainerFrame(aContext)
   , mEditorHasBeenInitialized(false)
   , mIsProcessing(false)
diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h
index 4d85c23ee5d..c8d70d34d13 100644
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -34,7 +34,7 @@ public:
 
   NS_DECLARE_FRAME_PROPERTY(ContentScrollPos, DestroyPoint)
 
-  nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext);
+  explicit nsTextControlFrame(nsStyleContext* aContext);
   virtual ~nsTextControlFrame();
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp
index ca31c9e31f1..fbfe787737d 100644
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -5034,8 +5034,8 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
   }
   
   // Attempt to find the line that contains the previous sibling
-  FrameLines* overflowLines;
   nsLineList* lineList = &mLines;
+  nsFrameList* frames = &mFrames;
   nsLineList::iterator prevSibLine = lineList->end();
   int32_t prevSiblingIndex = -1;
   if (aPrevSibling) {
@@ -5048,23 +5048,23 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
                                         prevSibLine, mFrames.LastChild(),
                                         &prevSiblingIndex)) {
       // Not in mLines - try overflow lines.
-      overflowLines = GetOverflowLines();
-      lineList = overflowLines ? &overflowLines->mLines : nullptr;
+      FrameLines* overflowLines = GetOverflowLines();
+      bool found = false;
       if (overflowLines) {
         prevSibLine = overflowLines->mLines.end();
         prevSiblingIndex = -1;
-        if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
-                                            prevSibLine,
-                                            overflowLines->mFrames.LastChild(),
-                                            &prevSiblingIndex)) {
-          lineList = nullptr;
-        }
+        found = nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
+                                               prevSibLine,
+                                               overflowLines->mFrames.LastChild(),
+                                               &prevSiblingIndex);
       }
-      if (!lineList) {
+      if (MOZ_LIKELY(found)) {
+        lineList = &overflowLines->mLines;
+        frames = &overflowLines->mFrames;
+      } else {
         // Note: defensive code! RFindLineContaining must not return
         // false in this case, so if it does...
         NS_NOTREACHED("prev sibling not in line list");
-        lineList = &mLines;
         aPrevSibling = nullptr;
         prevSibLine = lineList->end();
       }
@@ -5095,9 +5095,8 @@ nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
     lineList->front()->MarkDirty();
     lineList->front()->SetInvalidateTextRuns(true);
   }
-  nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames;
   const nsFrameList::Slice& newFrames =
-    frames.InsertFrames(nullptr, aPrevSibling, aFrameList);
+    frames->InsertFrames(nullptr, aPrevSibling, aFrameList);
 
   // Walk through the new frames being added and update the line data
   // structures to fit.
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index a89daa99e60..abc42ee91b0 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -87,12 +87,12 @@ BuildScrollContainerLayers()
 nsHTMLScrollFrame*
 NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
 {
-  return new (aPresShell) nsHTMLScrollFrame(aPresShell, aContext, aIsRoot);
+  return new (aPresShell) nsHTMLScrollFrame(aContext, aIsRoot);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame)
 
-nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot)
+nsHTMLScrollFrame::nsHTMLScrollFrame(nsStyleContext* aContext, bool aIsRoot)
   : nsContainerFrame(aContext),
     mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
 {
@@ -959,15 +959,15 @@ nsXULScrollFrame*
 NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
                      bool aIsRoot, bool aClipAllDescendants)
 {
-  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot,
+  return new (aPresShell) nsXULScrollFrame(aContext, aIsRoot,
                                            aClipAllDescendants);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
 
-nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext,
+nsXULScrollFrame::nsXULScrollFrame(nsStyleContext* aContext,
                                    bool aIsRoot, bool aClipAllDescendants)
-  : nsBoxFrame(aShell, aContext, aIsRoot),
+  : nsBoxFrame(aContext, aIsRoot),
     mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
 {
   SetLayoutManager(nullptr);
diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h
index a1da010b430..876d06c714e 100644
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -813,7 +813,7 @@ public:
 #endif
 
 protected:
-  nsHTMLScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot);
+  nsHTMLScrollFrame(nsStyleContext* aContext, bool aIsRoot);
   void SetSuppressScrollbarUpdate(bool aSuppress) {
     mHelper.mSupppressScrollbarUpdate = aSuppress;
   }
@@ -1175,7 +1175,7 @@ public:
 #endif
 
 protected:
-  nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot,
+  nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
                    bool aClipAllDescendants);
 
   void ClampAndSetBounds(nsBoxLayoutState& aState, 
diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp
index 0f51337414b..dbc7f7dca96 100644
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -236,7 +236,7 @@ ViewportFrame::Reflow(nsPresContext*           aPresContext,
   aDesiredSize.SetSize(wm, maxSize);
   aDesiredSize.SetOverflowAreasToDesiredBounds();
 
-  if (IsAbsoluteContainer()) {
+  if (HasAbsolutelyPositionedChildren()) {
     // Make a copy of the reflow state and change the computed width and height
     // to reflect the available space for the fixed items
     nsHTMLReflowState reflowState(aReflowState);
@@ -252,18 +252,16 @@ ViewportFrame::Reflow(nsPresContext*           aPresContext,
     }
 
     nsRect rect = AdjustReflowStateAsContainingBlock(&reflowState);
-
-    // Just reflow all the fixed-pos frames.
-    GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus,
-                                         rect,
-                                         false, true, true, // XXX could be optimized
-                                         &aDesiredSize.mOverflowAreas);
-
+    nsOverflowAreas* overflowAreas = &aDesiredSize.mOverflowAreas;
     nsIScrollableFrame* rootScrollFrame =
                     aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
     if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) {
-      aDesiredSize.SetOverflowAreasToDesiredBounds();
+      overflowAreas = nullptr;
     }
+    GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus,
+                                         rect,
+                                         false, true, true, // XXX could be optimized
+                                         overflowAreas);
   }
 
   if (mFrames.NotEmpty()) {
diff --git a/layout/reftests/bugs/1103258-1-ref.html b/layout/reftests/bugs/1103258-1-ref.html
new file mode 100644
index 00000000000..96d79e14fa5
--- /dev/null
+++ b/layout/reftests/bugs/1103258-1-ref.html
@@ -0,0 +1,13 @@
+
+
+
diff --git a/layout/reftests/bugs/1103258-1.html b/layout/reftests/bugs/1103258-1.html new file mode 100644 index 00000000000..7e16b110e09 --- /dev/null +++ b/layout/reftests/bugs/1103258-1.html @@ -0,0 +1,37 @@ + +
+
+ sadf +
+
+ sdf +
+
+
+
diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 7f9e3b173ce..8f232e0a4e8 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1849,5 +1849,6 @@ test-pref(dom.webcomponents.enabled,true) == 1066554-1.html 1066554-1-ref.html test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html fuzzy-if(winWidget&&!layersGPUAccelerated,1,31) fuzzy-if(B2G,128,75) == 1081185-1.html 1081185-1-ref.html # fuzzy with event-regions, see bug 1107843 == 1097437-1.html 1097437-1-ref.html +== 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test == 1105137-1.html 1105137-1-ref.html fuzzy-if(d2d,36,304) HTTP(..) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 8c18336d2dc..0afaa20aef5 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -496,7 +496,6 @@ CSS_KEY(space-between, space_between) CSS_KEY(span, span) CSS_KEY(spell-out, spell_out) CSS_KEY(square, square) -CSS_KEY(stack, stack) CSS_KEY(stacked-fractions, stacked_fractions) CSS_KEY(start, start) CSS_KEY(static, static) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 19e5b9aea2e..5f1be7ff3fd 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -7647,7 +7647,6 @@ CSSParserImpl::ParseGridAutoFlow() static const int32_t mask[] = { NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN, - NS_STYLE_GRID_AUTO_FLOW_DENSE | NS_STYLE_GRID_AUTO_FLOW_STACK, MASK_END_VALUE }; if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) { @@ -7655,15 +7654,9 @@ CSSParserImpl::ParseGridAutoFlow() } int32_t bitField = value.GetIntValue(); - // Requires one of these - if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_ROW || - bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN || - bitField & NS_STYLE_GRID_AUTO_FLOW_STACK)) { - return false; - } - - // 'stack' without 'row' or 'column' defaults to 'stack row' - if (bitField == NS_STYLE_GRID_AUTO_FLOW_STACK) { + // If neither row nor column is provided, row is assumed. + if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW | + NS_STYLE_GRID_AUTO_FLOW_COLUMN))) { value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated); } @@ -8526,11 +8519,10 @@ CSSParserImpl::ParseGrid() } // The values starts with a <'grid-auto-flow'> if and only if - // it starts with a 'stack', 'dense', 'column' or 'row' keyword. + // it starts with a 'dense', 'column' or 'row' keyword. if (mToken.mType == eCSSToken_Ident) { nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); - if (keyword == eCSSKeyword_stack || - keyword == eCSSKeyword_dense || + if (keyword == eCSSKeyword_dense || keyword == eCSSKeyword_column || keyword == eCSSKeyword_row) { UngetToken(); diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index a9408be01e9..b943cfbd552 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1358,7 +1358,6 @@ const KTableValue nsCSSProps::kFontWeightKTable[] = { }; const KTableValue nsCSSProps::kGridAutoFlowKTable[] = { - eCSSKeyword_stack, NS_STYLE_GRID_AUTO_FLOW_STACK, eCSSKeyword_row, NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSKeyword_column, NS_STYLE_GRID_AUTO_FLOW_COLUMN, eCSSKeyword_dense, NS_STYLE_GRID_AUTO_FLOW_DENSE, diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index 52a1fe2fd61..b73909c7d1b 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -1264,7 +1264,7 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult, case eCSSProperty_grid_auto_flow: nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, - NS_STYLE_GRID_AUTO_FLOW_STACK, + NS_STYLE_GRID_AUTO_FLOW_ROW, NS_STYLE_GRID_AUTO_FLOW_DENSE, aResult); break; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index c772ee0d560..9e07a00b2bc 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -2370,7 +2370,7 @@ nsComputedDOMStyle::DoGetGridAutoFlow() nsAutoString str; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_grid_auto_flow, StylePosition()->mGridAutoFlow, - NS_STYLE_GRID_AUTO_FLOW_STACK, + NS_STYLE_GRID_AUTO_FLOW_ROW, NS_STYLE_GRID_AUTO_FLOW_DENSE, str); nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index a6de27d3e22..2e9aaca0109 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -574,10 +574,9 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_FONT_FIELD 16 // grid-auto-flow keywords -#define NS_STYLE_GRID_AUTO_FLOW_STACK (1 << 0) -#define NS_STYLE_GRID_AUTO_FLOW_ROW (1 << 1) -#define NS_STYLE_GRID_AUTO_FLOW_COLUMN (1 << 2) -#define NS_STYLE_GRID_AUTO_FLOW_DENSE (1 << 3) +#define NS_STYLE_GRID_AUTO_FLOW_ROW (1 << 0) +#define NS_STYLE_GRID_AUTO_FLOW_COLUMN (1 << 1) +#define NS_STYLE_GRID_AUTO_FLOW_DENSE (1 << 2) // 'subgrid' keyword in grid-template-{columns,rows} #define NS_STYLE_GRID_TEMPLATE_SUBGRID 0 diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 3a18edd588f..ffbc26bd222 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -452,6 +452,7 @@ nsTransitionManager::ConsiderStartingTransition( // currently in the 'transition-delay'). It also might happen because we // just got a style change to a value that can't be interpolated. AnimationPlayerPtrArray& players = aElementTransitions->mPlayers; + players[currentIndex]->Cancel(); oldPT = nullptr; // Clear pointer so it doesn't dangle players.RemoveElementAt(currentIndex); aElementTransitions->UpdateAnimationGeneration(mPresContext); diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 34049760d6f..1f2d0102180 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -5077,19 +5077,13 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) { "row dense", "dense column", "dense row", - "stack column", - "stack row", - "stack", + "dense", ], invalid_values: [ "", "auto", "none", "10px", - "dense", - "stack dense", - "stack stack", - "stack row stack", "column row", "dense row dense", ] @@ -5320,7 +5314,7 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) { "none / none", ], other_values: [ - "stack 40px", + "column 40px", "column dense auto", "dense row minmax(min-content, 2fr)", "row 40px / 100px", diff --git a/layout/style/test/test_grid_container_shorthands.html b/layout/style/test/test_grid_container_shorthands.html index 23a412d8927..fa12971d513 100644 --- a/layout/style/test/test_grid_container_shorthands.html +++ b/layout/style/test/test_grid_container_shorthands.html @@ -179,13 +179,17 @@ grid_test_cases = grid_template_test_cases.concat([ gridAutoFlow: "row", }, { - specified: "stack 40px", - gridAutoFlow: "stack row", + specified: "dense", + gridAutoFlow: "row dense", + }, + { + specified: "row 40px", + gridAutoFlow: "row", gridAutoColumns: "40px", }, { - specified: "stack column 40px", - gridAutoFlow: "stack column", + specified: "column 40px", + gridAutoFlow: "column", gridAutoColumns: "40px", }, { diff --git a/layout/xul/grid/nsGridRowGroupFrame.cpp b/layout/xul/grid/nsGridRowGroupFrame.cpp index ff07a36fe15..6ddbeff512f 100644 --- a/layout/xul/grid/nsGridRowGroupFrame.cpp +++ b/layout/xul/grid/nsGridRowGroupFrame.cpp @@ -23,7 +23,7 @@ NS_NewGridRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { nsCOMPtr layout = NS_NewGridRowGroupLayout(); - return new (aPresShell) nsGridRowGroupFrame(aPresShell, aContext, layout); + return new (aPresShell) nsGridRowGroupFrame(aContext, layout); } NS_IMPL_FRAMEARENA_HELPERS(nsGridRowGroupFrame) diff --git a/layout/xul/grid/nsGridRowGroupFrame.h b/layout/xul/grid/nsGridRowGroupFrame.h index c2d98f655a9..8982fee759b 100644 --- a/layout/xul/grid/nsGridRowGroupFrame.h +++ b/layout/xul/grid/nsGridRowGroupFrame.h @@ -35,10 +35,9 @@ public: } #endif - nsGridRowGroupFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, + nsGridRowGroupFrame(nsStyleContext* aContext, nsBoxLayout* aLayoutManager): - nsBoxFrame(aPresShell, aContext, false, aLayoutManager) {} + nsBoxFrame(aContext, false, aLayoutManager) {} virtual nscoord GetFlex(nsBoxLayoutState& aBoxLayoutState) MOZ_OVERRIDE; diff --git a/layout/xul/grid/nsGridRowLeafFrame.cpp b/layout/xul/grid/nsGridRowLeafFrame.cpp index e3c2c367e23..ee4e0f3271f 100644 --- a/layout/xul/grid/nsGridRowLeafFrame.cpp +++ b/layout/xul/grid/nsGridRowLeafFrame.cpp @@ -23,8 +23,7 @@ NS_NewGridRowLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { nsCOMPtr layout = NS_NewGridRowLeafLayout(); - return new (aPresShell) nsGridRowLeafFrame(aPresShell, aContext, false, - layout); + return new (aPresShell) nsGridRowLeafFrame(aContext, false, layout); } NS_IMPL_FRAMEARENA_HELPERS(nsGridRowLeafFrame) diff --git a/layout/xul/grid/nsGridRowLeafFrame.h b/layout/xul/grid/nsGridRowLeafFrame.h index 409cf3a282b..1fbdae06326 100644 --- a/layout/xul/grid/nsGridRowLeafFrame.h +++ b/layout/xul/grid/nsGridRowLeafFrame.h @@ -39,11 +39,10 @@ public: } #endif - nsGridRowLeafFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, + nsGridRowLeafFrame(nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager): - nsBoxFrame(aPresShell, aContext, aIsRoot, aLayoutManager) {} + nsBoxFrame(aContext, aIsRoot, aLayoutManager) {} virtual nsresult GetBorderAndPadding(nsMargin& aBorderAndPadding) MOZ_OVERRIDE; diff --git a/layout/xul/nsBoxFrame.cpp b/layout/xul/nsBoxFrame.cpp index 3829542a358..0e5070f01be 100644 --- a/layout/xul/nsBoxFrame.cpp +++ b/layout/xul/nsBoxFrame.cpp @@ -97,13 +97,13 @@ nsIFrame* nsBoxFrame::mDebugChild = nullptr; nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager) { - return new (aPresShell) nsBoxFrame(aPresShell, aContext, aIsRoot, aLayoutManager); + return new (aPresShell) nsBoxFrame(aContext, aIsRoot, aLayoutManager); } nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsBoxFrame(aPresShell, aContext); + return new (aPresShell) nsBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame) @@ -114,8 +114,7 @@ NS_QUERYFRAME_HEAD(nsBoxFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) #endif -nsBoxFrame::nsBoxFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, +nsBoxFrame::nsBoxFrame(nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager) : nsContainerFrame(aContext) @@ -133,7 +132,7 @@ nsBoxFrame::nsBoxFrame(nsIPresShell* aPresShell, nsCOMPtr layout = aLayoutManager; if (layout == nullptr) { - NS_NewSprocketLayout(aPresShell, layout); + NS_NewSprocketLayout(PresContext()->PresShell(), layout); } SetLayoutManager(layout); diff --git a/layout/xul/nsBoxFrame.h b/layout/xul/nsBoxFrame.h index 0fc5c9ec301..32410a3fcd5 100644 --- a/layout/xul/nsBoxFrame.h +++ b/layout/xul/nsBoxFrame.h @@ -143,7 +143,7 @@ public: virtual ~nsBoxFrame(); - nsBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot = false, nsBoxLayout* aLayoutManager = nullptr); + explicit nsBoxFrame(nsStyleContext* aContext, bool aIsRoot = false, nsBoxLayout* aLayoutManager = nullptr); // virtual so nsStackFrame, nsButtonBoxFrame, nsSliderFrame and nsMenuFrame // can override it diff --git a/layout/xul/nsButtonBoxFrame.cpp b/layout/xul/nsButtonBoxFrame.cpp index d4f9dc00773..c3777c070cf 100644 --- a/layout/xul/nsButtonBoxFrame.cpp +++ b/layout/xul/nsButtonBoxFrame.cpp @@ -30,7 +30,7 @@ using namespace mozilla; nsIFrame* NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsButtonBoxFrame(aPresShell, aContext); + return new (aPresShell) nsButtonBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsButtonBoxFrame) diff --git a/layout/xul/nsButtonBoxFrame.h b/layout/xul/nsButtonBoxFrame.h index 3bd4579a1eb..eb9ad25de4d 100644 --- a/layout/xul/nsButtonBoxFrame.h +++ b/layout/xul/nsButtonBoxFrame.h @@ -15,8 +15,8 @@ public: friend nsIFrame* NS_NewButtonBoxFrame(nsIPresShell* aPresShell); - nsButtonBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) - :nsBoxFrame(aPresShell, aContext, false) { + explicit nsButtonBoxFrame(nsStyleContext* aContext) + :nsBoxFrame(aContext, false) { UpdateMouseThrough(); } diff --git a/layout/xul/nsDeckFrame.cpp b/layout/xul/nsDeckFrame.cpp index f92f283f952..0aa59e8d213 100644 --- a/layout/xul/nsDeckFrame.cpp +++ b/layout/xul/nsDeckFrame.cpp @@ -34,7 +34,7 @@ nsIFrame* NS_NewDeckFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsDeckFrame(aPresShell, aContext); + return new (aPresShell) nsDeckFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsDeckFrame) @@ -44,11 +44,11 @@ NS_QUERYFRAME_HEAD(nsDeckFrame) NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) -nsDeckFrame::nsDeckFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) - : nsBoxFrame(aPresShell, aContext), mIndex(0) +nsDeckFrame::nsDeckFrame(nsStyleContext* aContext) + : nsBoxFrame(aContext), mIndex(0) { nsCOMPtr layout; - NS_NewStackLayout(aPresShell, layout); + NS_NewStackLayout(PresContext()->PresShell(), layout); SetLayoutManager(layout); } diff --git a/layout/xul/nsDeckFrame.h b/layout/xul/nsDeckFrame.h index 266c1a75ee9..cebf18db067 100644 --- a/layout/xul/nsDeckFrame.h +++ b/layout/xul/nsDeckFrame.h @@ -57,7 +57,7 @@ public: } #endif - nsDeckFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsDeckFrame(nsStyleContext* aContext); nsIFrame* GetSelectedBox(); diff --git a/layout/xul/nsDocElementBoxFrame.cpp b/layout/xul/nsDocElementBoxFrame.cpp index 1470a268044..9d6de5de08e 100644 --- a/layout/xul/nsDocElementBoxFrame.cpp +++ b/layout/xul/nsDocElementBoxFrame.cpp @@ -35,8 +35,8 @@ public: friend nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - nsDocElementBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext) - :nsBoxFrame(aShell, aContext, true) {} + explicit nsDocElementBoxFrame(nsStyleContext* aContext) + :nsBoxFrame(aContext, true) {} NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS @@ -67,7 +67,7 @@ private: nsContainerFrame* NS_NewDocElementBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsDocElementBoxFrame (aPresShell, aContext); + return new (aPresShell) nsDocElementBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsDocElementBoxFrame) diff --git a/layout/xul/nsGroupBoxFrame.cpp b/layout/xul/nsGroupBoxFrame.cpp index 3ec276fa2e1..7fb8fb41cac 100644 --- a/layout/xul/nsGroupBoxFrame.cpp +++ b/layout/xul/nsGroupBoxFrame.cpp @@ -21,8 +21,8 @@ class nsGroupBoxFrame : public nsBoxFrame { public: NS_DECL_FRAMEARENA_HELPERS - nsGroupBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext) {} + explicit nsGroupBoxFrame(nsStyleContext* aContext): + nsBoxFrame(aContext) {} virtual nsresult GetBorderAndPadding(nsMargin& aBorderAndPadding); @@ -75,7 +75,7 @@ public: nsIFrame* NS_NewGroupBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsGroupBoxFrame(aPresShell, aContext); + return new (aPresShell) nsGroupBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsGroupBoxFrame) diff --git a/layout/xul/nsImageBoxFrame.cpp b/layout/xul/nsImageBoxFrame.cpp index 4c8846b6f3a..14db5b06edf 100644 --- a/layout/xul/nsImageBoxFrame.cpp +++ b/layout/xul/nsImageBoxFrame.cpp @@ -119,7 +119,7 @@ FireImageDOMEvent(nsIContent* aContent, uint32_t aMessage) nsIFrame* NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsImageBoxFrame (aPresShell, aContext); + return new (aPresShell) nsImageBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame) @@ -143,8 +143,8 @@ nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID, return rv; } -nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsLeafBoxFrame(aShell, aContext), +nsImageBoxFrame::nsImageBoxFrame(nsStyleContext* aContext): + nsLeafBoxFrame(aContext), mIntrinsicSize(0,0), mRequestRegistered(false), mLoadFlags(nsIRequest::LOAD_NORMAL), diff --git a/layout/xul/nsImageBoxFrame.h b/layout/xul/nsImageBoxFrame.h index c13309c3c76..05c6615b5c5 100644 --- a/layout/xul/nsImageBoxFrame.h +++ b/layout/xul/nsImageBoxFrame.h @@ -96,7 +96,7 @@ public: already_AddRefed GetContainer(LayerManager* aManager); protected: - nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsImageBoxFrame(nsStyleContext* aContext); virtual void GetImageSize(); diff --git a/layout/xul/nsLeafBoxFrame.cpp b/layout/xul/nsLeafBoxFrame.cpp index 6ee9cbc8904..1c373300d03 100644 --- a/layout/xul/nsLeafBoxFrame.cpp +++ b/layout/xul/nsLeafBoxFrame.cpp @@ -35,12 +35,12 @@ using namespace mozilla; nsIFrame* NS_NewLeafBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsLeafBoxFrame(aPresShell, aContext); + return new (aPresShell) nsLeafBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsLeafBoxFrame) -nsLeafBoxFrame::nsLeafBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext) +nsLeafBoxFrame::nsLeafBoxFrame(nsStyleContext* aContext) : nsLeafFrame(aContext) { } diff --git a/layout/xul/nsLeafBoxFrame.h b/layout/xul/nsLeafBoxFrame.h index 0d23334d0a6..dd687096525 100644 --- a/layout/xul/nsLeafBoxFrame.h +++ b/layout/xul/nsLeafBoxFrame.h @@ -85,7 +85,7 @@ protected: virtual nscoord GetIntrinsicISize() MOZ_OVERRIDE; - nsLeafBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsLeafBoxFrame(nsStyleContext* aContext); private: diff --git a/layout/xul/nsListBoxBodyFrame.cpp b/layout/xul/nsListBoxBodyFrame.cpp index 8ecd8804256..46483ebcc8a 100644 --- a/layout/xul/nsListBoxBodyFrame.cpp +++ b/layout/xul/nsListBoxBodyFrame.cpp @@ -139,10 +139,9 @@ NS_IMPL_ISUPPORTS(nsListScrollSmoother, nsITimerCallback) /////////////// nsListBoxBodyFrame ////////////////// -nsListBoxBodyFrame::nsListBoxBodyFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, +nsListBoxBodyFrame::nsListBoxBodyFrame(nsStyleContext* aContext, nsBoxLayout* aLayoutManager) - : nsBoxFrame(aPresShell, aContext, false, aLayoutManager), + : nsBoxFrame(aContext, false, aLayoutManager), mTopFrame(nullptr), mBottomFrame(nullptr), mLinkupFrame(nullptr), @@ -1526,7 +1525,7 @@ nsIFrame* NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { nsCOMPtr layout = NS_NewListBoxLayout(); - return new (aPresShell) nsListBoxBodyFrame(aPresShell, aContext, layout); + return new (aPresShell) nsListBoxBodyFrame(aContext, layout); } NS_IMPL_FRAMEARENA_HELPERS(nsListBoxBodyFrame) diff --git a/layout/xul/nsListBoxBodyFrame.h b/layout/xul/nsListBoxBodyFrame.h index a829decad6a..a5e20f7efa5 100644 --- a/layout/xul/nsListBoxBodyFrame.h +++ b/layout/xul/nsListBoxBodyFrame.h @@ -24,7 +24,7 @@ class nsListBoxBodyFrame MOZ_FINAL : public nsBoxFrame, public nsIScrollbarMediator, public nsIReflowCallback { - nsListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, + nsListBoxBodyFrame(nsStyleContext* aContext, nsBoxLayout* aLayoutManager); virtual ~nsListBoxBodyFrame(); diff --git a/layout/xul/nsListItemFrame.cpp b/layout/xul/nsListItemFrame.cpp index 54a16e4a1e0..69d36b1df50 100644 --- a/layout/xul/nsListItemFrame.cpp +++ b/layout/xul/nsListItemFrame.cpp @@ -14,11 +14,10 @@ #include "nsBoxLayout.h" #include "nsIContent.h" -nsListItemFrame::nsListItemFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext, +nsListItemFrame::nsListItemFrame(nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager) - : nsGridRowLeafFrame(aPresShell, aContext, aIsRoot, aLayoutManager) + : nsGridRowLeafFrame(aContext, aIsRoot, aLayoutManager) { } @@ -64,7 +63,7 @@ NS_NewListItemFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return nullptr; } - return new (aPresShell) nsListItemFrame(aPresShell, aContext, false, layout); + return new (aPresShell) nsListItemFrame(aContext, false, layout); } NS_IMPL_FRAMEARENA_HELPERS(nsListItemFrame) diff --git a/layout/xul/nsListItemFrame.h b/layout/xul/nsListItemFrame.h index f28a79a52e3..84a3c2a2f6d 100644 --- a/layout/xul/nsListItemFrame.h +++ b/layout/xul/nsListItemFrame.h @@ -26,10 +26,9 @@ public: virtual nsSize GetPrefSize(nsBoxLayoutState& aState) MOZ_OVERRIDE; protected: - nsListItemFrame(nsIPresShell* aPresShell, - nsStyleContext *aContext, - bool aIsRoot = false, - nsBoxLayout* aLayoutManager = nullptr); + explicit nsListItemFrame(nsStyleContext *aContext, + bool aIsRoot = false, + nsBoxLayout* aLayoutManager = nullptr); virtual ~nsListItemFrame(); }; // class nsListItemFrame diff --git a/layout/xul/nsMenuBarFrame.cpp b/layout/xul/nsMenuBarFrame.cpp index b57aef10798..32c9c1de212 100644 --- a/layout/xul/nsMenuBarFrame.cpp +++ b/layout/xul/nsMenuBarFrame.cpp @@ -37,7 +37,7 @@ using namespace mozilla; nsIFrame* NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsMenuBarFrame (aPresShell, aContext); + return new (aPresShell) nsMenuBarFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMenuBarFrame) @@ -49,8 +49,8 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) // // nsMenuBarFrame cntr // -nsMenuBarFrame::nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext), +nsMenuBarFrame::nsMenuBarFrame(nsStyleContext* aContext): + nsBoxFrame(aContext), mMenuBarListener(nullptr), mStayActive(false), mIsActive(false), diff --git a/layout/xul/nsMenuBarFrame.h b/layout/xul/nsMenuBarFrame.h index e125907bd0f..57a6c80289d 100644 --- a/layout/xul/nsMenuBarFrame.h +++ b/layout/xul/nsMenuBarFrame.h @@ -29,7 +29,7 @@ public: NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS - nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsMenuBarFrame(nsStyleContext* aContext); // nsMenuParent interface virtual nsMenuFrame* GetCurrentMenuItem() MOZ_OVERRIDE; diff --git a/layout/xul/nsMenuFrame.cpp b/layout/xul/nsMenuFrame.cpp index 199676d115e..4409efbb47b 100644 --- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -155,7 +155,7 @@ protected: nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - nsMenuFrame* it = new (aPresShell) nsMenuFrame (aPresShell, aContext); + nsMenuFrame* it = new (aPresShell) nsMenuFrame(aContext); it->SetIsMenu(true); return it; } @@ -163,7 +163,7 @@ NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) nsIFrame* NS_NewMenuItemFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - nsMenuFrame* it = new (aPresShell) nsMenuFrame (aPresShell, aContext); + nsMenuFrame* it = new (aPresShell) nsMenuFrame(aContext); it->SetIsMenu(false); return it; } @@ -174,8 +174,8 @@ NS_QUERYFRAME_HEAD(nsMenuFrame) NS_QUERYFRAME_ENTRY(nsMenuFrame) NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) -nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext), +nsMenuFrame::nsMenuFrame(nsStyleContext* aContext): + nsBoxFrame(aContext), mIsMenu(false), mChecked(false), mIgnoreAccelTextChange(false), diff --git a/layout/xul/nsMenuFrame.h b/layout/xul/nsMenuFrame.h index 6e11804ca25..0e43627f2f1 100644 --- a/layout/xul/nsMenuFrame.h +++ b/layout/xul/nsMenuFrame.h @@ -75,7 +75,7 @@ private: class nsMenuFrame MOZ_FINAL : public nsBoxFrame { public: - nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsMenuFrame(nsStyleContext* aContext); NS_DECL_QUERYFRAME_TARGET(nsMenuFrame) NS_DECL_QUERYFRAME diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp index 03963f2a356..896fa2b400d 100644 --- a/layout/xul/nsMenuPopupFrame.cpp +++ b/layout/xul/nsMenuPopupFrame.cpp @@ -67,7 +67,7 @@ int8_t nsMenuPopupFrame::sDefaultLevelIsTop = -1; nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsMenuPopupFrame (aPresShell, aContext); + return new (aPresShell) nsMenuPopupFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsMenuPopupFrame) @@ -79,8 +79,8 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) // // nsMenuPopupFrame ctor // -nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext) - :nsBoxFrame(aShell, aContext), +nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext) + :nsBoxFrame(aContext), mCurrentMenu(nullptr), mPrefSize(-1, -1), mLastClientOffset(0, 0), diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h index b7db6c2968b..39829b976a5 100644 --- a/layout/xul/nsMenuPopupFrame.h +++ b/layout/xul/nsMenuPopupFrame.h @@ -155,7 +155,7 @@ public: NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS - nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsMenuPopupFrame(nsStyleContext* aContext); // nsMenuParent interface virtual nsMenuFrame* GetCurrentMenuItem() MOZ_OVERRIDE; diff --git a/layout/xul/nsPopupSetFrame.cpp b/layout/xul/nsPopupSetFrame.cpp index d65a8199fa5..f1d243aa50d 100644 --- a/layout/xul/nsPopupSetFrame.cpp +++ b/layout/xul/nsPopupSetFrame.cpp @@ -17,7 +17,7 @@ nsIFrame* NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsPopupSetFrame (aPresShell, aContext); + return new (aPresShell) nsPopupSetFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsPopupSetFrame) diff --git a/layout/xul/nsPopupSetFrame.h b/layout/xul/nsPopupSetFrame.h index 34fd874346b..a1a6dc559a4 100644 --- a/layout/xul/nsPopupSetFrame.h +++ b/layout/xul/nsPopupSetFrame.h @@ -18,8 +18,8 @@ class nsPopupSetFrame : public nsBoxFrame public: NS_DECL_FRAMEARENA_HELPERS - nsPopupSetFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext) {} + explicit nsPopupSetFrame(nsStyleContext* aContext): + nsBoxFrame(aContext) {} ~nsPopupSetFrame() {} diff --git a/layout/xul/nsProgressMeterFrame.cpp b/layout/xul/nsProgressMeterFrame.cpp index 50954650265..06afc40a2a8 100644 --- a/layout/xul/nsProgressMeterFrame.cpp +++ b/layout/xul/nsProgressMeterFrame.cpp @@ -63,7 +63,7 @@ nsReflowFrameRunnable::Run() nsIFrame* NS_NewProgressMeterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsProgressMeterFrame(aPresShell, aContext); + return new (aPresShell) nsProgressMeterFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsProgressMeterFrame) diff --git a/layout/xul/nsProgressMeterFrame.h b/layout/xul/nsProgressMeterFrame.h index 023d3e18f6a..6881af770e3 100644 --- a/layout/xul/nsProgressMeterFrame.h +++ b/layout/xul/nsProgressMeterFrame.h @@ -38,8 +38,8 @@ public: #endif protected: - nsProgressMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) : - nsBoxFrame(aPresShell, aContext), mNeedsReflowCallback(true) {} + explicit nsProgressMeterFrame(nsStyleContext* aContext) : + nsBoxFrame(aContext), mNeedsReflowCallback(true) {} virtual ~nsProgressMeterFrame(); bool mNeedsReflowCallback; diff --git a/layout/xul/nsResizerFrame.cpp b/layout/xul/nsResizerFrame.cpp index 83477dbba0a..950de191e4c 100644 --- a/layout/xul/nsResizerFrame.cpp +++ b/layout/xul/nsResizerFrame.cpp @@ -38,13 +38,13 @@ using namespace mozilla; nsIFrame* NS_NewResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsResizerFrame(aPresShell, aContext); + return new (aPresShell) nsResizerFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsResizerFrame) -nsResizerFrame::nsResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -:nsTitleBarFrame(aPresShell, aContext) +nsResizerFrame::nsResizerFrame(nsStyleContext* aContext) +:nsTitleBarFrame(aContext) { } diff --git a/layout/xul/nsResizerFrame.h b/layout/xul/nsResizerFrame.h index d81f6fcfe59..faa75e99434 100644 --- a/layout/xul/nsResizerFrame.h +++ b/layout/xul/nsResizerFrame.h @@ -25,7 +25,7 @@ public: friend nsIFrame* NS_NewResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - nsResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsResizerFrame(nsStyleContext* aContext); virtual nsresult HandleEvent(nsPresContext* aPresContext, mozilla::WidgetGUIEvent* aEvent, diff --git a/layout/xul/nsRootBoxFrame.cpp b/layout/xul/nsRootBoxFrame.cpp index 4593cc05c93..583f3c7a7d1 100644 --- a/layout/xul/nsRootBoxFrame.cpp +++ b/layout/xul/nsRootBoxFrame.cpp @@ -46,7 +46,7 @@ public: friend nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - nsRootBoxFrame(nsIPresShell* aShell, nsStyleContext *aContext); + explicit nsRootBoxFrame(nsStyleContext* aContext); NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS @@ -108,18 +108,18 @@ protected: nsContainerFrame* NS_NewRootBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsRootBoxFrame (aPresShell, aContext); + return new (aPresShell) nsRootBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsRootBoxFrame) -nsRootBoxFrame::nsRootBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext, true) +nsRootBoxFrame::nsRootBoxFrame(nsStyleContext* aContext): + nsBoxFrame(aContext, true) { mPopupSetFrame = nullptr; nsCOMPtr layout; - NS_NewStackLayout(aShell, layout); + NS_NewStackLayout(PresContext()->PresShell(), layout); SetLayoutManager(layout); } diff --git a/layout/xul/nsScrollBoxFrame.cpp b/layout/xul/nsScrollBoxFrame.cpp index 3f226768aeb..bc5b5a9fc38 100644 --- a/layout/xul/nsScrollBoxFrame.cpp +++ b/layout/xul/nsScrollBoxFrame.cpp @@ -41,8 +41,8 @@ public: nsEventStatus* aEventStatus) MOZ_OVERRIDE; protected: - nsAutoRepeatBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): - nsButtonBoxFrame(aPresShell, aContext) {} + explicit nsAutoRepeatBoxFrame(nsStyleContext* aContext): + nsButtonBoxFrame(aContext) {} void StartRepeat() { if (IsActivatedOnHover()) { @@ -68,7 +68,7 @@ protected: nsIFrame* NS_NewAutoRepeatBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsAutoRepeatBoxFrame (aPresShell, aContext); + return new (aPresShell) nsAutoRepeatBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsAutoRepeatBoxFrame) diff --git a/layout/xul/nsScrollbarButtonFrame.cpp b/layout/xul/nsScrollbarButtonFrame.cpp index 12cbfff4890..22117a8ccbc 100644 --- a/layout/xul/nsScrollbarButtonFrame.cpp +++ b/layout/xul/nsScrollbarButtonFrame.cpp @@ -33,7 +33,7 @@ using namespace mozilla; nsIFrame* NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsScrollbarButtonFrame(aPresShell, aContext); + return new (aPresShell) nsScrollbarButtonFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarButtonFrame) diff --git a/layout/xul/nsScrollbarButtonFrame.h b/layout/xul/nsScrollbarButtonFrame.h index 2a575167e17..ebd90156539 100644 --- a/layout/xul/nsScrollbarButtonFrame.h +++ b/layout/xul/nsScrollbarButtonFrame.h @@ -25,8 +25,8 @@ class nsScrollbarButtonFrame : public nsButtonBoxFrame public: NS_DECL_FRAMEARENA_HELPERS - nsScrollbarButtonFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): - nsButtonBoxFrame(aPresShell, aContext), mCursorOnThis(false) {} + explicit nsScrollbarButtonFrame(nsStyleContext* aContext): + nsButtonBoxFrame(aContext), mCursorOnThis(false) {} // Overrides virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; diff --git a/layout/xul/nsScrollbarFrame.cpp b/layout/xul/nsScrollbarFrame.cpp index b7c8bda5846..12679183705 100644 --- a/layout/xul/nsScrollbarFrame.cpp +++ b/layout/xul/nsScrollbarFrame.cpp @@ -29,9 +29,9 @@ using namespace mozilla; // Creates a new scrollbar frame and returns it // nsIFrame* -NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) +NS_NewScrollbarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsScrollbarFrame (aPresShell, aContext); + return new (aPresShell) nsScrollbarFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarFrame) diff --git a/layout/xul/nsScrollbarFrame.h b/layout/xul/nsScrollbarFrame.h index e4a88ab08c9..1c17f776f47 100644 --- a/layout/xul/nsScrollbarFrame.h +++ b/layout/xul/nsScrollbarFrame.h @@ -20,8 +20,8 @@ nsIFrame* NS_NewScrollbarFrame(nsIPresShell* aPresShell, nsStyleContext* aContex class nsScrollbarFrame : public nsBoxFrame { public: - nsScrollbarFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsBoxFrame(aShell, aContext), mScrollbarMediator(nullptr) {} + explicit nsScrollbarFrame(nsStyleContext* aContext): + nsBoxFrame(aContext), mScrollbarMediator(nullptr) {} NS_DECL_QUERYFRAME_TARGET(nsScrollbarFrame) diff --git a/layout/xul/nsSliderFrame.cpp b/layout/xul/nsSliderFrame.cpp index 90ac5e88f94..4093da5c806 100644 --- a/layout/xul/nsSliderFrame.cpp +++ b/layout/xul/nsSliderFrame.cpp @@ -55,7 +55,7 @@ GetContentOfBox(nsIFrame *aBox) nsIFrame* NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsSliderFrame(aPresShell, aContext); + return new (aPresShell) nsSliderFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame) @@ -64,8 +64,8 @@ NS_QUERYFRAME_HEAD(nsSliderFrame) NS_QUERYFRAME_ENTRY(nsSliderFrame) NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) -nsSliderFrame::nsSliderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): - nsBoxFrame(aPresShell, aContext), +nsSliderFrame::nsSliderFrame(nsStyleContext* aContext): + nsBoxFrame(aContext), mCurPos(0), mChange(0), mDragFinished(true), diff --git a/layout/xul/nsSliderFrame.h b/layout/xul/nsSliderFrame.h index ff4133eef98..123144dc68f 100644 --- a/layout/xul/nsSliderFrame.h +++ b/layout/xul/nsSliderFrame.h @@ -47,7 +47,7 @@ public: friend class nsSliderMediator; - nsSliderFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsSliderFrame(nsStyleContext* aContext); virtual ~nsSliderFrame(); #ifdef DEBUG_FRAME_DUMP diff --git a/layout/xul/nsSplitterFrame.cpp b/layout/xul/nsSplitterFrame.cpp index f68a330b25d..e610eddc156 100644 --- a/layout/xul/nsSplitterFrame.cpp +++ b/layout/xul/nsSplitterFrame.cpp @@ -198,13 +198,13 @@ nsSplitterFrameInner::GetState() nsIFrame* NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsSplitterFrame(aPresShell, aContext); + return new (aPresShell) nsSplitterFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame) -nsSplitterFrame::nsSplitterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -: nsBoxFrame(aPresShell, aContext), +nsSplitterFrame::nsSplitterFrame(nsStyleContext* aContext) +: nsBoxFrame(aContext), mInner(0) { } diff --git a/layout/xul/nsSplitterFrame.h b/layout/xul/nsSplitterFrame.h index 397781a21d4..72c4db87881 100644 --- a/layout/xul/nsSplitterFrame.h +++ b/layout/xul/nsSplitterFrame.h @@ -23,7 +23,7 @@ class nsSplitterFrame : public nsBoxFrame public: NS_DECL_FRAMEARENA_HELPERS - nsSplitterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsSplitterFrame(nsStyleContext* aContext); virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; #ifdef DEBUG_FRAME_DUMP diff --git a/layout/xul/nsStackFrame.cpp b/layout/xul/nsStackFrame.cpp index bd34b6fc700..3ef80e4015e 100644 --- a/layout/xul/nsStackFrame.cpp +++ b/layout/xul/nsStackFrame.cpp @@ -24,16 +24,16 @@ nsIFrame* NS_NewStackFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsStackFrame(aPresShell, aContext); + return new (aPresShell) nsStackFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsStackFrame) -nsStackFrame::nsStackFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): - nsBoxFrame(aPresShell, aContext) +nsStackFrame::nsStackFrame(nsStyleContext* aContext): + nsBoxFrame(aContext) { nsCOMPtr layout; - NS_NewStackLayout(aPresShell, layout); + NS_NewStackLayout(PresContext()->PresShell(), layout); SetLayoutManager(layout); } diff --git a/layout/xul/nsStackFrame.h b/layout/xul/nsStackFrame.h index f1f90b4e4e7..516619f0df9 100644 --- a/layout/xul/nsStackFrame.h +++ b/layout/xul/nsStackFrame.h @@ -37,7 +37,7 @@ public: const nsDisplayListSet& aLists) MOZ_OVERRIDE; protected: - nsStackFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsStackFrame(nsStyleContext* aContext); }; // class nsStackFrame diff --git a/layout/xul/nsTextBoxFrame.cpp b/layout/xul/nsTextBoxFrame.cpp index fbbddd92ab4..4d74fc0b818 100644 --- a/layout/xul/nsTextBoxFrame.cpp +++ b/layout/xul/nsTextBoxFrame.cpp @@ -61,7 +61,7 @@ bool nsTextBoxFrame::gInsertSeparatorPrefInitialized = false; nsIFrame* NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsTextBoxFrame (aPresShell, aContext); + return new (aPresShell) nsTextBoxFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsTextBoxFrame) @@ -97,8 +97,8 @@ nsTextBoxFrame::AttributeChanged(int32_t aNameSpaceID, return NS_OK; } -nsTextBoxFrame::nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): - nsLeafBoxFrame(aShell, aContext), mAccessKeyInfo(nullptr), mCropType(CropRight), +nsTextBoxFrame::nsTextBoxFrame(nsStyleContext* aContext): + nsLeafBoxFrame(aContext), mAccessKeyInfo(nullptr), mCropType(CropRight), mNeedsReflowCallback(false) { MarkIntrinsicISizesDirty(); diff --git a/layout/xul/nsTextBoxFrame.h b/layout/xul/nsTextBoxFrame.h index 1133132d3b0..467db8a530c 100644 --- a/layout/xul/nsTextBoxFrame.h +++ b/layout/xul/nsTextBoxFrame.h @@ -92,7 +92,7 @@ protected: void CalcDrawRect(nsRenderingContext &aRenderingContext); - nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext); + explicit nsTextBoxFrame(nsStyleContext* aContext); nscoord CalculateTitleForWidth(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, diff --git a/layout/xul/nsTitleBarFrame.cpp b/layout/xul/nsTitleBarFrame.cpp index df0a5534d8c..b1835db9ccc 100644 --- a/layout/xul/nsTitleBarFrame.cpp +++ b/layout/xul/nsTitleBarFrame.cpp @@ -28,13 +28,13 @@ using namespace mozilla; nsIFrame* NS_NewTitleBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsTitleBarFrame(aPresShell, aContext); + return new (aPresShell) nsTitleBarFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsTitleBarFrame) -nsTitleBarFrame::nsTitleBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -:nsBoxFrame(aPresShell, aContext, false) +nsTitleBarFrame::nsTitleBarFrame(nsStyleContext* aContext) +:nsBoxFrame(aContext, false) { mTrackingMouseMove = false; UpdateMouseThrough(); diff --git a/layout/xul/nsTitleBarFrame.h b/layout/xul/nsTitleBarFrame.h index b754e52277f..f4e9881a92f 100644 --- a/layout/xul/nsTitleBarFrame.h +++ b/layout/xul/nsTitleBarFrame.h @@ -16,7 +16,7 @@ public: friend nsIFrame* NS_NewTitleBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - nsTitleBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsTitleBarFrame(nsStyleContext* aContext); virtual void BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 6ba624315e1..259c39a9a6e 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -101,7 +101,7 @@ CancelImageRequest(const nsAString& aKey, nsIFrame* NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsTreeBodyFrame(aPresShell, aContext); + return new (aPresShell) nsTreeBodyFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame) @@ -112,8 +112,8 @@ NS_QUERYFRAME_HEAD(nsTreeBodyFrame) NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame) // Constructor -nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -:nsLeafBoxFrame(aPresShell, aContext), +nsTreeBodyFrame::nsTreeBodyFrame(nsStyleContext* aContext) +:nsLeafBoxFrame(aContext), mSlots(nullptr), mImageCache(), mTopRowIndex(0), diff --git a/layout/xul/tree/nsTreeBodyFrame.h b/layout/xul/tree/nsTreeBodyFrame.h index eca5b2702c3..8f8c29d18e4 100644 --- a/layout/xul/tree/nsTreeBodyFrame.h +++ b/layout/xul/tree/nsTreeBodyFrame.h @@ -56,7 +56,7 @@ class nsTreeBodyFrame MOZ_FINAL public: typedef mozilla::layout::ScrollbarActivity ScrollbarActivity; - nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + explicit nsTreeBodyFrame(nsStyleContext* aContext); ~nsTreeBodyFrame(); NS_DECL_QUERYFRAME_TARGET(nsTreeBodyFrame) diff --git a/layout/xul/tree/nsTreeColFrame.cpp b/layout/xul/tree/nsTreeColFrame.cpp index 2903f4ac4d2..5db81f03412 100644 --- a/layout/xul/tree/nsTreeColFrame.cpp +++ b/layout/xul/tree/nsTreeColFrame.cpp @@ -25,7 +25,7 @@ nsIFrame* NS_NewTreeColFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { - return new (aPresShell) nsTreeColFrame(aPresShell, aContext); + return new (aPresShell) nsTreeColFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsTreeColFrame) diff --git a/layout/xul/tree/nsTreeColFrame.h b/layout/xul/tree/nsTreeColFrame.h index f32f2aa1785..1c52df135f6 100644 --- a/layout/xul/tree/nsTreeColFrame.h +++ b/layout/xul/tree/nsTreeColFrame.h @@ -16,9 +16,8 @@ class nsTreeColFrame : public nsBoxFrame public: NS_DECL_FRAMEARENA_HELPERS - nsTreeColFrame(nsIPresShell* aPresShell, - nsStyleContext* aContext): - nsBoxFrame(aPresShell, aContext) {} + explicit nsTreeColFrame(nsStyleContext* aContext): + nsBoxFrame(aContext) {} virtual void Init(nsIContent* aContent, nsContainerFrame* aParent, diff --git a/media/libstagefright/binding/H264.cpp b/media/libstagefright/binding/H264.cpp index 7b7320ce8c7..0a8fbddd0f0 100644 --- a/media/libstagefright/binding/H264.cpp +++ b/media/libstagefright/binding/H264.cpp @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ArrayUtils.h" +#include "mozilla/PodOperations.h" #include "mp4_demuxer/AnnexB.h" #include "mp4_demuxer/ByteReader.h" #include "mp4_demuxer/ByteWriter.h" diff --git a/mfbt/Attributes.h b/mfbt/Attributes.h index 3ac9c1f0d4c..8e021c4c123 100644 --- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -520,6 +520,10 @@ * tells the compiler that the raw pointer is a weak reference, and that * property is somehow enforced by the code. This can make the compiler * ignore these pointers when validating the usage of pointers otherwise. + * MOZ_UNSAFE_REF: Applies to declarations of pointer types. This attribute + * should be used for non-owning references that can be unsafe, and their + * safety needs to be validated through code inspection. The string argument + * passed to this macro documents the safety conditions. */ #ifdef MOZ_CLANG_PLUGIN # define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override"))) @@ -537,6 +541,7 @@ # define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg"))) # define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref"))) # define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref"))) +# define MOZ_UNSAFE_REF(reason) __attribute__((annotate("moz_strong_ref"))) /* * It turns out that clang doesn't like void func() __attribute__ {} without a * warning, so use pragmas to disable the warning. This code won't work on GCC @@ -558,6 +563,7 @@ # define MOZ_HEAP_ALLOCATOR /* nothing */ # define MOZ_OWNING_REF /* nothing */ # define MOZ_NON_OWNING_REF /* nothing */ +# define MOZ_UNSAFE_REF(reason) /* nothing */ #endif /* MOZ_CLANG_PLUGIN */ /* diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e66eac1ef7f..34502aef4d4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1595,10 +1595,8 @@ pref("network.predictor.preserve", 80); // percentage of predictor data to keep // [scheme "://"] [host [":" port]] // For example, "foo.com" would match "http://www.foo.com/bar", etc. -// Allow insecure NTLMv1 when needed. -pref("network.negotiate-auth.allow-insecure-ntlm-v1", false); -// Allow insecure NTLMv1 for HTTPS protected sites by default. -pref("network.negotiate-auth.allow-insecure-ntlm-v1-https", true); +// Force less-secure NTLMv1 when needed (NTLMv2 is the default). +pref("network.auth.force-generic-ntlm-v1", false); // This list controls which URIs can use the negotiate-auth protocol. This // list should be limited to the servers you know you'll need to login to. @@ -1642,14 +1640,6 @@ pref("network.automatic-ntlm-auth.allow-proxies", true); pref("network.automatic-ntlm-auth.allow-non-fqdn", false); pref("network.automatic-ntlm-auth.trusted-uris", ""); -// This preference controls whether or not the LM hash will be included in -// response to a NTLM challenge. By default, this is disabled since servers -// should almost never need the LM hash, and the LM hash is what makes NTLM -// authentication less secure. See bug 250691 for further details. -// NOTE: automatic-ntlm-auth which leverages the OS-provided NTLM -// implementation will not be affected by this preference. -pref("network.ntlm.send-lm-response", false); - pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign pref("network.proxy.type", 5); @@ -2164,11 +2154,7 @@ pref("layout.css.grid.enabled", false); pref("layout.css.ruby.enabled", false); // Is support for CSS display:contents enabled? -#ifdef RELEASE_BUILD -pref("layout.css.display-contents.enabled", false); -#else pref("layout.css.display-contents.enabled", true); -#endif // Is support for CSS box-decoration-break enabled? pref("layout.css.box-decoration-break.enabled", true); diff --git a/netwerk/ipc/ChannelEventQueue.h b/netwerk/ipc/ChannelEventQueue.h index 5382ba4c8fd..d7ec87349e0 100644 --- a/netwerk/ipc/ChannelEventQueue.h +++ b/netwerk/ipc/ChannelEventQueue.h @@ -37,7 +37,7 @@ class AutoEventEnqueuerBase; class ChannelEventQueue MOZ_FINAL { - NS_INLINE_DECL_REFCOUNTING(ChannelEventQueue) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChannelEventQueue) public: explicit ChannelEventQueue(nsISupports *owner) diff --git a/netwerk/protocol/http/Http2Push.cpp b/netwerk/protocol/http/Http2Push.cpp index 864e89d165b..e17eeefb728 100644 --- a/netwerk/protocol/http/Http2Push.cpp +++ b/netwerk/protocol/http/Http2Push.cpp @@ -166,7 +166,8 @@ Http2PushedStream::ReadSegments(nsAHttpSegmentReader *, // the write side of a pushed transaction just involves manipulating a little state SetSentFin(true); - Http2Stream::mAllHeadersSent = 1; + Http2Stream::mRequestHeadersDone = 1; + Http2Stream::mOpenGenerated = 1; Http2Stream::ChangeState(UPSTREAM_COMPLETE); *count = 0; return NS_OK; @@ -353,13 +354,13 @@ Http2PushTransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, mIsDone = true; } - if (Available()) { + if (Available() || mIsDone) { Http2Stream *consumer = mPushStream->GetConsumerStream(); if (consumer) { LOG3(("Http2PushTransactionBuffer::WriteSegments notifying connection " - "consumer data available 0x%X [%u]\n", - mPushStream->StreamID(), Available())); + "consumer data available 0x%X [%u] done=%d\n", + mPushStream->StreamID(), Available(), mIsDone)); mPushStream->ConnectPushedStream(consumer); } } diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index ac5332cbf9b..c06730db3b9 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -438,13 +438,15 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, mStreamTransactionHash.Put(aHttpTransaction, stream); - if (RoomForMoreConcurrent()) { - LOG3(("Http2Session::AddStream %p stream %p activated immediately.", - this, stream)); - ActivateStream(stream); - } else { - LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream)); - mQueuedStreams.Push(stream); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); + + // Kick off the SYN transmit without waiting for the poll loop + // This won't work for the first stream because there is no segment reader + // yet. + if (mSegmentReader) { + uint32_t countRead; + ReadSegments(nullptr, kDefaultBufferSize, &countRead); } if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && @@ -458,32 +460,14 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, } void -Http2Session::ActivateStream(Http2Stream *stream) +Http2Session::QueueStream(Http2Stream *stream) { + // will be removed via processpending or a shutdown path MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), - "Do not activate pushed streams"); - MOZ_ASSERT(!stream->CountAsActive()); - stream->SetCountAsActive(true); - ++mConcurrent; - if (mConcurrent > mConcurrentHighWater) - mConcurrentHighWater = mConcurrent; - LOG3(("Http2Session::AddStream %p activating stream %p Currently %d " - "streams in session, high water mark is %d", - this, stream, mConcurrent, mConcurrentHighWater)); - - mReadyForWrite.Push(stream); - SetWriteCallbacks(); - - // Kick off the headers transmit without waiting for the poll loop - // This won't work for stream id=1 because there is no segment reader - // yet. - if (mSegmentReader) { - uint32_t countRead; - ReadSegments(nullptr, kDefaultBufferSize, &countRead); - } + LOG3(("Http2Session::QueueStream %p stream %p queued.", this, stream)); + mQueuedStreams.Push(stream); } void @@ -491,13 +475,15 @@ Http2Session::ProcessPending() { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - while (RoomForMoreConcurrent()) { - Http2Stream *stream = static_cast(mQueuedStreams.PopFront()); - if (!stream) - return; - LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.", + Http2Stream*stream; + while (RoomForMoreConcurrent() && + (stream = static_cast(mQueuedStreams.PopFront()))) { + + LOG3(("Http2Session::ProcessPending %p stream %p woken from queue.", this, stream)); - ActivateStream(stream); + MOZ_ASSERT(!stream->CountAsActive()); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); } } @@ -616,6 +602,44 @@ Http2Session::ResetDownstreamState() mInputFrameDataStream = nullptr; } +// return true if activated (and counted against max) +// otherwise return false and queue +bool +Http2Session::TryToActivate(Http2Stream *aStream) +{ + if (!RoomForMoreConcurrent()) { + LOG3(("Http2Session::TryToActivate %p stream=%p no room for more concurrent " + "streams %d\n", this, aStream)); + QueueStream(aStream); + return false; + } + IncrementConcurrent(aStream); + return true; +} + +void +Http2Session::IncrementConcurrent(Http2Stream *stream) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), + "Do not activate pushed streams"); + + nsAHttpTransaction *trans = stream->Transaction(); + if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { + + MOZ_ASSERT(!stream->CountAsActive()); + stream->SetCountAsActive(true); + ++mConcurrent; + + if (mConcurrent > mConcurrentHighWater) { + mConcurrentHighWater = mConcurrent; + } + LOG3(("Http2Session::IncrementCounter %p counting stream %p Currently %d " + "streams in session, high water mark is %d\n", + this, stream, mConcurrent, mConcurrentHighWater)); + } +} + // call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header) // dest must have 9 bytes of allocated space template void @@ -656,6 +680,7 @@ Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength, void Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream) { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n", this, aStream->StreamID(), mConcurrent, aStream->CountAsActive())); @@ -1450,6 +1475,7 @@ Http2Session::RecvSettings(Http2Session *self) case SETTINGS_TYPE_MAX_CONCURRENT: self->mMaxConcurrent = value; Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); + self->ProcessPending(); break; case SETTINGS_TYPE_INITIAL_WINDOW: diff --git a/netwerk/protocol/http/Http2Session.h b/netwerk/protocol/http/Http2Session.h index 1f3433cf12e..655f0abe0a8 100644 --- a/netwerk/protocol/http/Http2Session.h +++ b/netwerk/protocol/http/Http2Session.h @@ -205,8 +205,8 @@ public: uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } + bool TryToActivate(Http2Stream *stream); void ConnectPushedStream(Http2Stream *stream); - void MaybeDecrementConcurrent(Http2Stream *stream); nsresult ConfirmTLSProfile(); static bool ALPNCallback(nsISupports *securityInfo); @@ -266,8 +266,6 @@ private: void SetWriteCallbacks(); void RealignOutputQueue(); - bool RoomForMoreConcurrent(); - void ActivateStream(Http2Stream *); void ProcessPending(); nsresult SetInputFrameDataStream(uint32_t); void CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *); @@ -278,6 +276,11 @@ private: void UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes); void UpdateLocalSessionWindow(uint32_t bytes); + void MaybeDecrementConcurrent(Http2Stream *stream); + bool RoomForMoreConcurrent(); + void IncrementConcurrent(Http2Stream *stream); + void QueueStream(Http2Stream *stream); + // a wrapper for all calls to the nshttpconnection level segment writer. Used // to track network I/O for timeout purposes nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); diff --git a/netwerk/protocol/http/Http2Stream.cpp b/netwerk/protocol/http/Http2Stream.cpp index 0de1dc866b8..2497263d273 100644 --- a/netwerk/protocol/http/Http2Stream.cpp +++ b/netwerk/protocol/http/Http2Stream.cpp @@ -46,7 +46,8 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction, , mSession(session) , mUpstreamState(GENERATING_HEADERS) , mState(IDLE) - , mAllHeadersSent(0) + , mRequestHeadersDone(0) + , mOpenGenerated(0) , mAllHeadersReceived(0) , mTransaction(httpTransaction) , mSocketTransport(session->SocketTransport()) @@ -154,7 +155,7 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader, // If not, mark the stream for callback when writing can proceed. if (NS_SUCCEEDED(rv) && mUpstreamState == GENERATING_HEADERS && - !mAllHeadersSent) + !mRequestHeadersDone) mSession->TransactionHasDataToWrite(this); // mTxinlineFrameUsed represents any queued un-sent frame. It might @@ -303,16 +304,17 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, uint32_t *countUsed) { // Returns NS_OK even if the headers are incomplete - // set mAllHeadersSent flag if they are complete + // set mRequestHeadersDone flag if they are complete MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS); + MOZ_ASSERT(!mRequestHeadersDone); LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x", this, avail, mUpstreamState)); mFlatHttpRequestHeaders.Append(buf, avail); - nsHttpRequestHead *head = mTransaction->RequestHead(); + const nsHttpRequestHead *head = mTransaction->RequestHead(); // We can use the simple double crlf because firefox is the // only client we are parsing @@ -333,7 +335,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, uint32_t oldLen = mFlatHttpRequestHeaders.Length(); mFlatHttpRequestHeaders.SetLength(endHeader + 2); *countUsed = avail - (oldLen - endHeader) + 4; - mAllHeadersSent = 1; + mRequestHeadersDone = 1; nsAutoCString authorityHeader; nsAutoCString hashkey; @@ -388,28 +390,32 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, SetSentFin(true); AdjustPushedPriority(); - // This stream has been activated (and thus counts against the concurrency - // limit intentionally), but will not be registered via - // RegisterStreamID (below) because of the push match. - // Release that semaphore count immediately (instead of waiting for - // cleanup stream) so we can initiate more pull streams. - mSession->MaybeDecrementConcurrent(this); - // There is probably pushed data buffered so trigger a read manually // as we can't rely on future network events to do it mSession->ConnectPushedStream(this); + mOpenGenerated = 1; return NS_OK; } } + return NS_OK; +} +// This is really a headers frame, but open is pretty clear from a workflow pov +nsresult +Http2Stream::GenerateOpen() +{ // It is now OK to assign a streamID that we are assured will // be monotonically increasing amongst new streams on this // session mStreamID = mSession->RegisterStreamID(this); MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd"); - LOG3(("Stream ID 0x%X [session=%p] for URI %s\n", - mStreamID, mSession, - nsCString(head->RequestURI()).get())); + MOZ_ASSERT(!mOpenGenerated); + + mOpenGenerated = 1; + + const nsHttpRequestHead *head = mTransaction->RequestHead(); + LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n", + this, mStreamID, mSession, nsCString(head->RequestURI()).get())); if (mStreamID >= 0x80000000) { // streamID must fit in 31 bits. Evading This is theoretically possible @@ -428,6 +434,9 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, // of HTTP/2 headers by writing to mTxInlineFrame{sz} nsCString compressedData; + nsAutoCString authorityHeader; + head->GetHeader(nsHttp::Host, authorityHeader); + nsDependentCString scheme(head->IsHTTPS() ? "https" : "http"); if (head->IsConnect()) { MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction()); @@ -440,6 +449,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf, if (!ci) { return NS_ERROR_UNEXPECTED; } + authorityHeader = ci->GetHost(); authorityHeader.Append(':'); authorityHeader.AppendInt(ci->Port()); @@ -1200,12 +1210,26 @@ Http2Stream::OnReadSegment(const char *buf, // the number of those bytes that we consume (i.e. the portion that are // header bytes) - rv = ParseHttpRequestHeaders(buf, count, countRead); - if (NS_FAILED(rv)) - return rv; - LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d", - this, *countRead, count, mAllHeadersSent)); - if (mAllHeadersSent) { + if (!mRequestHeadersDone) { + if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) { + return rv; + } + } + + if (mRequestHeadersDone && !mOpenGenerated) { + if (!mSession->TryToActivate(this)) { + LOG3(("Http2Stream::OnReadSegment %p cannot activate now. queued.\n", this)); + return NS_OK; + } + if (NS_FAILED(rv = GenerateOpen())) { + return rv; + } + } + + LOG3(("ParseHttpRequestHeaders %p used %d of %d. " + "requestheadersdone = %d mOpenGenerated = %d\n", + this, *countRead, count, mRequestHeadersDone, mOpenGenerated)); + if (mOpenGenerated) { SetHTTPState(OPEN); AdjustInitialWindow(); // This version of TransmitFrame cannot block diff --git a/netwerk/protocol/http/Http2Stream.h b/netwerk/protocol/http/Http2Stream.h index 172fe59947b..1828cb4a69a 100644 --- a/netwerk/protocol/http/Http2Stream.h +++ b/netwerk/protocol/http/Http2Stream.h @@ -178,10 +178,13 @@ protected: // The HTTP/2 state for the stream from section 5.1 enum stateType mState; - // Flag is set when all http request headers have been read and ID is stable - uint32_t mAllHeadersSent : 1; + // Flag is set when all http request headers have been read ID is not stable + uint32_t mRequestHeadersDone : 1; - // Flag is set when all http request headers have been read and ID is stable + // Flag is set when ID is stable and concurrency limits are met + uint32_t mOpenGenerated : 1; + + // Flag is set when all http response headers have been read uint32_t mAllHeadersReceived : 1; void ChangeState(enum upstreamStateType); @@ -190,6 +193,8 @@ private: friend class nsAutoPtr; nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *); + nsresult GenerateOpen(); + void AdjustPushedPriority(); void AdjustInitialWindow(); nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment); diff --git a/netwerk/protocol/http/SpdyPush31.cpp b/netwerk/protocol/http/SpdyPush31.cpp index 3084e7837f2..8fb7d795b59 100644 --- a/netwerk/protocol/http/SpdyPush31.cpp +++ b/netwerk/protocol/http/SpdyPush31.cpp @@ -106,7 +106,8 @@ SpdyPushedStream31::ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *co // the write side of a pushed transaction just involves manipulating a little state SpdyStream31::mSentFinOnData = 1; - SpdyStream31::mSynFrameComplete = 1; + SpdyStream31::mRequestHeadersDone = 1; + SpdyStream31::mSynFrameGenerated = 1; SpdyStream31::ChangeState(UPSTREAM_COMPLETE); *count = 0; return NS_OK; @@ -282,13 +283,13 @@ SpdyPush31TransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, mIsDone = true; } - if (Available()) { + if (Available() || mIsDone) { SpdyStream31 *consumer = mPushStream->GetConsumerStream(); if (consumer) { LOG3(("SpdyPush31TransactionBuffer::WriteSegments notifying connection " - "consumer data available 0x%X [%u]\n", - mPushStream->StreamID(), Available())); + "consumer data available 0x%X [%u] done=%d\n", + mPushStream->StreamID(), Available(), mIsDone)); mPushStream->ConnectPushedStream(consumer); } } diff --git a/netwerk/protocol/http/SpdySession31.cpp b/netwerk/protocol/http/SpdySession31.cpp index 0647e02723b..c4a4efd18ea 100644 --- a/netwerk/protocol/http/SpdySession31.cpp +++ b/netwerk/protocol/http/SpdySession31.cpp @@ -379,14 +379,15 @@ SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction, mStreamTransactionHash.Put(aHttpTransaction, stream); - if (RoomForMoreConcurrent()) { - LOG3(("SpdySession31::AddStream %p stream %p activated immediately.", - this, stream)); - ActivateStream(stream); - } - else { - LOG3(("SpdySession31::AddStream %p stream %p queued.", this, stream)); - mQueuedStreams.Push(stream); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); + + // Kick off the SYN transmit without waiting for the poll loop + // This won't work for stream id=1 because there is no segment reader + // yet. + if (mSegmentReader) { + uint32_t countRead; + ReadSegments(nullptr, kDefaultBufferSize, &countRead); } if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && @@ -400,33 +401,14 @@ SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction, } void -SpdySession31::ActivateStream(SpdyStream31 *stream) +SpdySession31::QueueStream(SpdyStream31 *stream) { + // will be removed via processpending or a shutdown path MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), - "Do not activate pushed streams"); + MOZ_ASSERT(!stream->CountAsActive()); - nsAHttpTransaction *trans = stream->Transaction(); - if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { - ++mConcurrent; - if (mConcurrent > mConcurrentHighWater) { - mConcurrentHighWater = mConcurrent; - } - LOG3(("SpdySession31::AddStream %p activating stream %p Currently %d " - "streams in session, high water mark is %d", - this, stream, mConcurrent, mConcurrentHighWater)); - } - - mReadyForWrite.Push(stream); - SetWriteCallbacks(); - - // Kick off the SYN transmit without waiting for the poll loop - // This won't work for stream id=1 because there is no segment reader - // yet. - if (mSegmentReader) { - uint32_t countRead; - ReadSegments(nullptr, kDefaultBufferSize, &countRead); - } + LOG3(("SpdySession31::QueueStream %p stream %p queued.", this, stream)); + mQueuedStreams.Push(stream); } void @@ -434,13 +416,15 @@ SpdySession31::ProcessPending() { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - while (RoomForMoreConcurrent()) { - SpdyStream31 *stream = static_cast(mQueuedStreams.PopFront()); - if (!stream) - return; - LOG3(("SpdySession31::ProcessPending %p stream %p activated from queue.", + SpdyStream31 *stream; + while (RoomForMoreConcurrent() && + (stream = static_cast(mQueuedStreams.PopFront()))) { + + LOG3(("SpdySession31::ProcessPending %p stream %p woken from queue.", this, stream)); - ActivateStream(stream); + MOZ_ASSERT(!stream->CountAsActive()); + mReadyForWrite.Push(stream); + SetWriteCallbacks(); } } @@ -561,18 +545,60 @@ SpdySession31::ResetDownstreamState() mInputFrameDataStream = nullptr; } +// return true if activated (and counted against max) +// otherwise return false and queue +bool +SpdySession31::TryToActivate(SpdyStream31 *aStream) +{ + if (!RoomForMoreConcurrent()) { + LOG3(("SpdySession31::TryToActivate %p stream=%p no room for more concurrent " + "streams %d\n", this, aStream)); + QueueStream(aStream); + return false; + } + IncrementConcurrent(aStream); + return true; +} + +void +SpdySession31::IncrementConcurrent(SpdyStream31 *stream) +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), + "Do not activate pushed streams"); + + nsAHttpTransaction *trans = stream->Transaction(); + if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) { + + MOZ_ASSERT(!stream->CountAsActive()); + stream->SetCountAsActive(true); + ++mConcurrent; + + if (mConcurrent > mConcurrentHighWater) { + mConcurrentHighWater = mConcurrent; + } + LOG3(("SpdySession31::AddStream %p counting stream %p Currently %d " + "streams in session, high water mark is %d", + this, stream, mConcurrent, mConcurrentHighWater)); + } +} + void SpdySession31::DecrementConcurrent(SpdyStream31 *aStream) { - uint32_t id = aStream->StreamID(); + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - if (id && !(id & 0x1)) - return; // pushed streams aren't counted in concurrent limit + if (!aStream->CountAsActive()) { + return; + } MOZ_ASSERT(mConcurrent); + aStream->SetCountAsActive(false); --mConcurrent; + LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n", - this, id, mConcurrent)); + this, aStream->StreamID(), mConcurrent)); + ProcessPending(); } @@ -1414,6 +1440,7 @@ SpdySession31::HandleSettings(SpdySession31 *self) case SETTINGS_TYPE_MAX_CONCURRENT: self->mMaxConcurrent = value; Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); + self->ProcessPending(); break; case SETTINGS_TYPE_CWND: diff --git a/netwerk/protocol/http/SpdySession31.h b/netwerk/protocol/http/SpdySession31.h index 6192ca01988..595b06f4b96 100644 --- a/netwerk/protocol/http/SpdySession31.h +++ b/netwerk/protocol/http/SpdySession31.h @@ -178,6 +178,7 @@ public: uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; } + bool TryToActivate(SpdyStream31 *stream); void ConnectPushedStream(SpdyStream31 *stream); void DecrementConcurrent(SpdyStream31 *stream); @@ -223,8 +224,6 @@ private: void SetWriteCallbacks(); void RealignOutputQueue(); - bool RoomForMoreConcurrent(); - void ActivateStream(SpdyStream31 *); void ProcessPending(); nsresult SetInputFrameDataStream(uint32_t); bool VerifyStream(SpdyStream31 *, uint32_t); @@ -234,6 +233,10 @@ private: void UpdateLocalStreamWindow(SpdyStream31 *stream, uint32_t bytes); void UpdateLocalSessionWindow(uint32_t bytes); + bool RoomForMoreConcurrent(); + void IncrementConcurrent(SpdyStream31 *stream); + void QueueStream(SpdyStream31 *stream); + // a wrapper for all calls to the nshttpconnection level segment writer. Used // to track network I/O for timeout purposes nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *); diff --git a/netwerk/protocol/http/SpdyStream31.cpp b/netwerk/protocol/http/SpdyStream31.cpp index 068720575ae..42242853c50 100644 --- a/netwerk/protocol/http/SpdyStream31.cpp +++ b/netwerk/protocol/http/SpdyStream31.cpp @@ -42,7 +42,8 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction, : mStreamID(0) , mSession(spdySession) , mUpstreamState(GENERATING_SYN_STREAM) - , mSynFrameComplete(0) + , mRequestHeadersDone(0) + , mSynFrameGenerated(0) , mSentFinOnData(0) , mTransaction(httpTransaction) , mSocketTransport(spdySession->SocketTransport()) @@ -55,6 +56,7 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction, , mSentWaitingFor(0) , mReceivedData(0) , mSetTCPSocketBuffer(0) + , mCountAsActive(0) , mTxInlineFrameSize(SpdySession31::kDefaultBufferSize) , mTxInlineFrameUsed(0) , mTxStreamFrameSize(0) @@ -129,7 +131,7 @@ SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader, // If not, mark the stream for callback when writing can proceed. if (NS_SUCCEEDED(rv) && mUpstreamState == GENERATING_SYN_STREAM && - !mSynFrameComplete) + !mRequestHeadersDone) mSession->TransactionHasDataToWrite(this); // mTxinlineFrameUsed represents any queued un-sent frame. It might @@ -259,10 +261,11 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, uint32_t *countUsed) { // Returns NS_OK even if the headers are incomplete - // set mSynFrameComplete flag if they are complete + // set mRequestHeadersDone flag if they are complete MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM); + MOZ_ASSERT(!mRequestHeadersDone); LOG3(("SpdyStream31::ParseHttpRequestHeaders %p avail=%d state=%x", this, avail, mUpstreamState)); @@ -288,7 +291,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, uint32_t oldLen = mFlatHttpRequestHeaders.Length(); mFlatHttpRequestHeaders.SetLength(endHeader + 2); *countUsed = avail - (oldLen - endHeader) + 4; - mSynFrameComplete = 1; + mRequestHeadersDone = 1; nsAutoCString hostHeader; nsAutoCString hashkey; @@ -330,15 +333,24 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, // There is probably pushed data buffered so trigger a read manually // as we can't rely on future network events to do it mSession->ConnectPushedStream(this); + mSynFrameGenerated = 1; return NS_OK; } } + return NS_OK; +} +nsresult +SpdyStream31::GenerateSynFrame() +{ // It is now OK to assign a streamID that we are assured will // be monotonically increasing amongst syn-streams on this // session mStreamID = mSession->RegisterStreamID(this); MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd"); + MOZ_ASSERT(!mSynFrameGenerated); + + mSynFrameGenerated = 1; if (mStreamID >= 0x80000000) { // streamID must fit in 31 bits. This is theoretically possible @@ -507,6 +519,8 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf, CompressToFrame(NS_LITERAL_CSTRING(":version")); CompressToFrame(versionHeader); + nsAutoCString hostHeader; + mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader); CompressToFrame(NS_LITERAL_CSTRING(":host")); CompressToFrame(hostHeader); @@ -1455,12 +1469,26 @@ SpdyStream31::OnReadSegment(const char *buf, // the number of those bytes that we consume (i.e. the portion that are // header bytes) - rv = ParseHttpRequestHeaders(buf, count, countRead); - if (NS_FAILED(rv)) - return rv; - LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d", - this, *countRead, count, mSynFrameComplete)); - if (mSynFrameComplete) { + if (!mRequestHeadersDone) { + if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) { + return rv; + } + } + + if (mRequestHeadersDone && !mSynFrameGenerated) { + if (!mSession->TryToActivate(this)) { + LOG3(("SpdyStream31::OnReadSegment %p cannot activate now. queued.\n", this)); + return NS_OK; + } + if (NS_FAILED(rv = GenerateSynFrame())) { + return rv; + } + } + + LOG3(("ParseHttpRequestHeaders %p used %d of %d. " + "requestheadersdone = %d mSynFrameGenerated = %d\n", + this, *countRead, count, mRequestHeadersDone, mSynFrameGenerated)); + if (mSynFrameGenerated) { AdjustInitialWindow(); rv = TransmitFrame(nullptr, nullptr, true); if (rv == NS_BASE_STREAM_WOULD_BLOCK) { diff --git a/netwerk/protocol/http/SpdyStream31.h b/netwerk/protocol/http/SpdyStream31.h index 77d12c8b222..5fc584be886 100644 --- a/netwerk/protocol/http/SpdyStream31.h +++ b/netwerk/protocol/http/SpdyStream31.h @@ -55,6 +55,9 @@ public: void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; } bool RecvdData() { return mReceivedData; } + void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; } + bool CountAsActive() { return mCountAsActive; } + void UpdateTransportSendEvents(uint32_t count); void UpdateTransportReadEvents(uint32_t count); @@ -118,8 +121,11 @@ protected: // sending_request_body for each SPDY chunk in the upload. enum stateType mUpstreamState; - // Flag is set when all http request headers have been read and ID is stable - uint32_t mSynFrameComplete : 1; + // Flag is set when all http request headers have been read + uint32_t mRequestHeadersDone : 1; + + // Flag is set when stream ID is stable + uint32_t mSynFrameGenerated : 1; // Flag is set when a FIN has been placed on a data or syn packet // (i.e after the client has closed) @@ -135,6 +141,8 @@ private: void *); nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *); + nsresult GenerateSynFrame(); + void AdjustInitialWindow(); nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment); void GenerateDataFrameHeader(uint32_t, bool); @@ -185,6 +193,9 @@ private: // Flag is set after TCP send autotuning has been disabled uint32_t mSetTCPSocketBuffer : 1; + // Flag is set when stream is counted towards MAX_CONCURRENT streams in session + uint32_t mCountAsActive : 1; + // The InlineFrame and associated data is used for composing control // frames and data frame headers. nsAutoArrayPtr mTxInlineFrame; diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.cpp b/netwerk/protocol/http/nsHttpNTLMAuth.cpp index 764c3c1b330..0348dbc8551 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp +++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp @@ -24,7 +24,6 @@ #include "nsISSLStatusProvider.h" #endif #include "mozilla/Attributes.h" -#include "nsThreadUtils.h" namespace mozilla { namespace net { @@ -33,8 +32,6 @@ static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies"; static const char kAllowNonFqdn[] = "network.automatic-ntlm-auth.allow-non-fqdn"; static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris"; static const char kForceGeneric[] = "network.auth.force-generic-ntlm"; -static const char kAllowGenericHTTP[] = "network.negotiate-auth.allow-insecure-ntlm-v1"; -static const char kAllowGenericHTTPS[] = "network.negotiate-auth.allow-insecure-ntlm-v1-https"; // XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp, // but since that file lives in a separate library we cannot directly share it. @@ -180,47 +177,6 @@ ForceGenericNTLM() return flag; } -// Check to see if we should use our generic (internal) NTLM auth module. -static bool -AllowGenericNTLM() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (!prefs) - return false; - - bool flag = false; - if (NS_FAILED(prefs->GetBoolPref(kAllowGenericHTTP, &flag))) - flag = false; - - LOG(("Allow use of generic ntlm auth module: %d\n", flag)); - return flag; -} - -// Check to see if we should use our generic (internal) NTLM auth module. -static bool -AllowGenericNTLMforHTTPS(nsIHttpAuthenticableChannel *channel) -{ - bool isSSL = false; - channel->GetIsSSL(&isSSL); - if (!isSSL) - return false; - - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (!prefs) - return false; - - bool flag = false; - if (NS_FAILED(prefs->GetBoolPref(kAllowGenericHTTPS, &flag))) - flag = false; - - LOG(("Allow use of generic ntlm auth module for only https: %d\n", flag)); - return flag; -} - // Check to see if we should use default credentials for this host or proxy. static bool CanUseDefaultCredentials(nsIHttpAuthenticableChannel *channel, @@ -343,14 +299,8 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel, // Use our internal NTLM implementation. Note, this is less secure, // see bug 520607 for details. - - // For now with default preference settings (i.e. allow-insecure-ntlm-v1-https = true - // and allow-insecure-ntlm-v1 = false) we don't allow authentication to any proxy, - // either http or https. This will be fixed in a followup bug. - if (AllowGenericNTLM() || (!isProxyAuth && AllowGenericNTLMforHTTPS(channel))) { - LOG(("Trying to fall back on internal ntlm auth.\n")); - module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm"); - } + LOG(("Trying to fall back on internal ntlm auth.\n")); + module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm"); mUseNative = false; diff --git a/security/manager/ssl/src/nsCertOverrideService.cpp b/security/manager/ssl/src/nsCertOverrideService.cpp index 0278bea8984..22c1b4a7a0e 100644 --- a/security/manager/ssl/src/nsCertOverrideService.cpp +++ b/security/manager/ssl/src/nsCertOverrideService.cpp @@ -134,18 +134,7 @@ nsCertOverrideService::Observe(nsISupports *, // The profile is about to change, // or is going away because the application is shutting down. - ReentrantMonitorAutoEnter lock(monitor); - - if (!nsCRT::strcmp(aData, MOZ_UTF16("shutdown-cleanse"))) { - RemoveAllFromMemory(); - // delete the storage file - if (mSettingsFile) { - mSettingsFile->Remove(false); - } - } else { - RemoveAllFromMemory(); - } - + RemoveAllFromMemory(); } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { // The profile has already changed. // Now read from the new profile location. diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp index 9933d95681d..40fdb38b904 100644 --- a/security/manager/ssl/src/nsNSSComponent.cpp +++ b/security/manager/ssl/src/nsNSSComponent.cpp @@ -1142,8 +1142,6 @@ nsNSSComponent::ShutdownNSS() } } -static const bool SEND_LM_DEFAULT = false; - NS_IMETHODIMP nsNSSComponent::Init() { @@ -1179,10 +1177,6 @@ nsNSSComponent::Init() getter_Copies(result)); } - bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response", - SEND_LM_DEFAULT); - nsNTLMAuthModule::SetSendLM(sendLM); - // Do that before NSS init, to make sure we won't get unloaded. RegisterObservers(); @@ -1362,11 +1356,6 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, prefName.EqualsLiteral("security.cert_pinning.enforcement_level")) { MutexAutoLock lock(mutex); setValidationOptions(false, lock); - } else if (prefName.EqualsLiteral("network.ntlm.send-lm-response")) { - bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response", - SEND_LM_DEFAULT); - nsNTLMAuthModule::SetSendLM(sendLM); - clearSessionCache = false; } else { clearSessionCache = false; } diff --git a/security/manager/ssl/src/nsNTLMAuthModule.cpp b/security/manager/ssl/src/nsNTLMAuthModule.cpp index bea7887e41d..d032878c67c 100644 --- a/security/manager/ssl/src/nsNTLMAuthModule.cpp +++ b/security/manager/ssl/src/nsNTLMAuthModule.cpp @@ -3,17 +3,31 @@ * 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 "prlog.h" - #include "nsNTLMAuthModule.h" -#include "nsNSSShutDown.h" -#include "nsNativeCharsetUtils.h" -#include "prsystem.h" -#include "pk11pub.h" + +#include + #include "md4.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/Endian.h" #include "mozilla/Likely.h" -#include "mozilla/Telemetry.h" #include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" +#include "nsCOMPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsICryptoHash.h" +#include "nsICryptoHMAC.h" +#include "nsIKeyModule.h" +#include "nsKeyModule.h" +#include "nsNativeCharsetUtils.h" +#include "nsNetCID.h" +#include "nsNSSShutDown.h" +#include "nsUnicharUtils.h" +#include "pk11pub.h" +#include "prlog.h" +#include "prsystem.h" + +static bool sNTLMv1Forced = false; #ifdef PR_LOGGING static PRLogModuleInfo * @@ -33,7 +47,6 @@ GetNTLMLog() static void des_makekey(const uint8_t *raw, uint8_t *key); static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash); -static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result); //----------------------------------------------------------------------------- // this file contains a cross-platform NTLM authentication implementation. it @@ -88,24 +101,21 @@ static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 }; static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 }; #define NTLM_TYPE1_HEADER_LEN 32 -#define NTLM_TYPE2_HEADER_LEN 32 +#define NTLM_TYPE2_HEADER_LEN 48 #define NTLM_TYPE3_HEADER_LEN 64 -#define LM_HASH_LEN 16 +/** + * We don't actually send a LM response, but we still have to send something in this spot + */ #define LM_RESP_LEN 24 +#define NTLM_CHAL_LEN 8 + #define NTLM_HASH_LEN 16 +#define NTLMv2_HASH_LEN 16 #define NTLM_RESP_LEN 24 - -//----------------------------------------------------------------------------- - -static bool sendLM = false; - -/*static*/ void -nsNTLMAuthModule::SetSendLM(bool newSendLM) -{ - sendLM = newSendLM; -} +#define NTLMv2_RESP_LEN 16 +#define NTLMv2_BLOB1_LEN 28 //----------------------------------------------------------------------------- @@ -325,51 +335,12 @@ ZapBuf(void *buf, size_t bufLen) memset(buf, 0, bufLen); } -static void -ZapString(nsCString &s) -{ - ZapBuf(s.BeginWriting(), s.Length()); -} - static void ZapString(nsString &s) { ZapBuf(s.BeginWriting(), s.Length() * 2); } -static const unsigned char LM_MAGIC[] = "KGS!@#$%"; - -/** - * LM_Hash computes the LM hash of the given password. - * - * @param password - * null-terminated unicode password. - * @param hash - * 16-byte result buffer - */ -static void -LM_Hash(const nsString &password, unsigned char *hash) -{ - // convert password to OEM character set. we'll just use the native - // filesystem charset. - nsAutoCString passbuf; - NS_CopyUnicodeToNative(password, passbuf); - ToUpperCase(passbuf); - uint32_t n = passbuf.Length(); - passbuf.SetLength(14); - for (uint32_t i=n; i<14; ++i) - passbuf.SetCharAt('\0', i); - - unsigned char k1[8], k2[8]; - des_makekey((const unsigned char *) passbuf.get() , k1); - des_makekey((const unsigned char *) passbuf.get() + 7, k2); - ZapString(passbuf); - - // use password keys to hash LM magic string twice. - des_encrypt(k1, LM_MAGIC, hash); - des_encrypt(k2, LM_MAGIC, hash + 8); -} - /** * NTLM_Hash computes the NTLM hash of the given password. * @@ -475,10 +446,12 @@ GenerateType1Msg(void **outBuf, uint32_t *outLen) struct Type2Msg { - uint32_t flags; // NTLM_Xxx bitwise combination - uint8_t challenge[8]; // 8 byte challenge - const void *target; // target string (type depends on flags) - uint32_t targetLen; // target length in bytes + uint32_t flags; // NTLM_Xxx bitwise combination + uint8_t challenge[NTLM_CHAL_LEN]; // 8 byte challenge + const uint8_t *target; // target string (type depends on flags) + uint32_t targetLen; // target length in bytes + const uint8_t *targetInfo; // target Attribute-Value pairs (DNS domain, et al) + uint32_t targetInfoLen; // target AV pairs length in bytes }; static nsresult @@ -491,12 +464,13 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) // 12 Target Name // 20 Flags // 24 Challenge - // 32 end of header, start of optional data blocks + // 32 targetInfo + // 48 start of optional data blocks // if (inLen < NTLM_TYPE2_HEADER_LEN) return NS_ERROR_UNEXPECTED; - const uint8_t *cursor = (const uint8_t *) inBuf; + const uint8_t *cursor = reinterpret_cast(inBuf); // verify NTLMSSP signature if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) @@ -517,14 +491,14 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) ReadUint16(cursor); // ... read offset from inBuf. uint32_t offset = ReadUint32(cursor); + mozilla::CheckedInt targetEnd = offset; + targetEnd += targetLen; // Check the offset / length combo is in range of the input buffer, including // integer overflow checking. - if (MOZ_LIKELY(offset < offset + targetLen && offset + targetLen <= inLen)) { + if (MOZ_LIKELY(targetEnd.isValid() && targetEnd.value() <= inLen)) { msg->targetLen = targetLen; - msg->target = ((const uint8_t *) inBuf) + offset; - } - else - { + msg->target = reinterpret_cast(inBuf) + offset; + } else { // Do not error out, for (conservative) backward compatibility. msg->targetLen = 0; msg->target = nullptr; @@ -537,16 +511,34 @@ ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg) memcpy(msg->challenge, cursor, sizeof(msg->challenge)); cursor += sizeof(msg->challenge); - LOG(("NTLM type 2 message:\n")); - LogBuf("target", (const uint8_t *) msg->target, msg->targetLen); - LogBuf("flags", (const uint8_t *) &msg->flags, 4); + LogBuf("target", reinterpret_cast (msg->target), msg->targetLen); + LogBuf("flags", reinterpret_cast (&msg->flags), 4); LogFlags(msg->flags); LogBuf("challenge", msg->challenge, sizeof(msg->challenge)); - // we currently do not implement LMv2/NTLMv2 or NTLM2 responses, - // so we can ignore target information. we may want to enable - // support for these alternate mechanisms in the future. + // Read (and skip) the reserved field + ReadUint32(cursor); + ReadUint32(cursor); + // Read target name security buffer: ... + // ... read target length. + uint32_t targetInfoLen = ReadUint16(cursor); + // ... skip next 16-bit "allocated space" value. + ReadUint16(cursor); + // ... read offset from inBuf. + offset = ReadUint32(cursor); + mozilla::CheckedInt targetInfoEnd = offset; + targetInfoEnd += targetInfoLen; + // Check the offset / length combo is in range of the input buffer, including + // integer overflow checking. + if (MOZ_LIKELY(targetInfoEnd.isValid() && targetInfoEnd.value() <= inLen)) { + msg->targetInfoLen = targetInfoLen; + msg->targetInfo = reinterpret_cast(inBuf) + offset; + } else { + NS_ERROR("failed to get NTLMv2 target info"); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; } @@ -570,6 +562,10 @@ GenerateType3Msg(const nsString &domain, bool unicode = (msg.flags & NTLM_NegotiateUnicode); + // There is no negotiation for NTLMv2, so we just do it unless we are forced + // by explict user configuration to use the older DES-based cryptography. + bool ntlmv2 = (sNTLMv1Forced == false); + // temporary buffers for unicode strings #ifdef IS_BIG_ENDIAN nsAutoString ucsDomainBuf, ucsUserBuf; @@ -582,6 +578,9 @@ GenerateType3Msg(const nsString &domain, const void *domainPtr, *userPtr, *hostPtr; uint32_t domainLen, userLen, hostLen; + // This is for NTLM, for NTLMv2 we set the new full length once we know it + mozilla::CheckedInt ntlmRespLen = NTLM_RESP_LEN; + // // get domain name // @@ -591,7 +590,7 @@ GenerateType3Msg(const nsString &domain, ucsDomainBuf = domain; domainPtr = ucsDomainBuf.get(); domainLen = ucsDomainBuf.Length() * 2; - WriteUnicodeLE((void *) domainPtr, (const char16_t *) domainPtr, + WriteUnicodeLE((void *) domainPtr, reinterpret_cast domainPtr, ucsDomainBuf.Length()); #else domainPtr = domain.get(); @@ -614,7 +613,7 @@ GenerateType3Msg(const nsString &domain, ucsUserBuf = username; userPtr = ucsUserBuf.get(); userLen = ucsUserBuf.Length() * 2; - WriteUnicodeLE((void *) userPtr, (const char16_t *) userPtr, + WriteUnicodeLE((void *) userPtr, reinterpret_cast userPtr, ucsUserBuf.Length()); #else userPtr = username.get(); @@ -642,7 +641,7 @@ GenerateType3Msg(const nsString &domain, hostPtr = ucsHostBuf.get(); hostLen = ucsHostBuf.Length() * 2; #ifdef IS_BIG_ENDIAN - WriteUnicodeLE((void *) hostPtr, (const char16_t *) hostPtr, + WriteUnicodeLE((void *) hostPtr, reinterpret_cast hostPtr, ucsHostBuf.Length()); #endif } @@ -652,56 +651,243 @@ GenerateType3Msg(const nsString &domain, // // now that we have generated all of the strings, we can allocate outBuf. // - *outLen = NTLM_TYPE3_HEADER_LEN + hostLen + domainLen + userLen + - LM_RESP_LEN + NTLM_RESP_LEN; - *outBuf = nsMemory::Alloc(*outLen); - if (!*outBuf) - return NS_ERROR_OUT_OF_MEMORY; + // + // next, we compute the NTLM or NTLM2 responses. + // + uint8_t lmResp[LM_RESP_LEN]; + uint8_t ntlmResp[NTLM_RESP_LEN]; + uint8_t ntlmv2Resp[NTLMv2_RESP_LEN]; + uint8_t ntlmHash[NTLM_HASH_LEN]; + uint8_t ntlmv2_blob1[NTLMv2_BLOB1_LEN]; + if (ntlmv2) { + // NTLMv2 mode, the default + nsString userUpper, domainUpper; + nsAutoCString ntlmHashStr; + nsAutoCString ntlmv2HashStr; + nsAutoCString lmv2ResponseStr; + nsAutoCString ntlmv2ResponseStr; - // - // next, we compute the LM and NTLM responses. - // - uint8_t lmResp[LM_RESP_LEN], ntlmResp[NTLM_RESP_LEN], ntlmHash[NTLM_HASH_LEN]; - if (msg.flags & NTLM_NegotiateNTLM2Key) - { + // temporary buffers for unicode strings + nsAutoString ucsDomainUpperBuf; + nsAutoString ucsUserUpperBuf; + const void *domainUpperPtr; + const void *userUpperPtr; + uint32_t domainUpperLen; + uint32_t userUpperLen; + + if (msg.targetInfoLen == 0) { + NS_ERROR("failed to get NTLMv2 target info, can not do NTLMv2"); + return NS_ERROR_UNEXPECTED; + } + + ToUpperCase(username, ucsUserUpperBuf); + userUpperPtr = ucsUserUpperBuf.get(); + userUpperLen = ucsUserUpperBuf.Length() * 2; +#ifdef IS_BIG_ENDIAN + WriteUnicodeLE((void *) userUpperPtr, reinterpret_cast (userUpperPtr), + ucsUserUpperBuf.Length()); +#endif + ToUpperCase(domain, ucsDomainUpperBuf); + domainUpperPtr = ucsDomainUpperBuf.get(); + domainUpperLen = ucsDomainUpperBuf.Length() * 2; +#ifdef IS_BIG_ENDIAN + WriteUnicodeLE((void *) domainUpperPtr, reinterpret_cast (domainUpperPtr), + ucsDomainUpperBuf.Length()); +#endif + + NTLM_Hash(password, ntlmHash); + ntlmHashStr = nsAutoCString(reinterpret_cast(ntlmHash), NTLM_HASH_LEN); + + nsCOMPtr keyFactory = + do_CreateInstance(NS_KEYMODULEOBJECTFACTORY_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr ntlmKey = + do_CreateInstance(NS_KEYMODULEOBJECT_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + rv = keyFactory->KeyFromString(nsIKeyObject::HMAC, ntlmHashStr, getter_AddRefs(ntlmKey)); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr hasher = + do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Init(nsICryptoHMAC::MD5, ntlmKey); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(reinterpret_cast (userUpperPtr), userUpperLen); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(reinterpret_cast (domainUpperPtr), domainUpperLen); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Finish(false, ntlmv2HashStr); + if (NS_FAILED(rv)) { + return rv; + } + + uint8_t client_random[NTLM_CHAL_LEN]; + PK11_GenerateRandom(client_random, NTLM_CHAL_LEN); + + nsCOMPtr ntlmv2Key = + do_CreateInstance(NS_KEYMODULEOBJECT_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + + // Prepare the LMv2 response + rv = keyFactory->KeyFromString(nsIKeyObject::HMAC, ntlmv2HashStr, getter_AddRefs(ntlmv2Key)); + if (NS_FAILED(rv)) { + return rv; + } + + rv = hasher->Init(nsICryptoHMAC::MD5, ntlmv2Key); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(client_random, NTLM_CHAL_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Finish(false, lmv2ResponseStr); + if (NS_FAILED(rv)) { + return rv; + } + + if (lmv2ResponseStr.Length() != NTLMv2_HASH_LEN) { + return NS_ERROR_UNEXPECTED; + } + + memcpy(lmResp, lmv2ResponseStr.get(), NTLMv2_HASH_LEN); + memcpy(lmResp + NTLMv2_HASH_LEN, client_random, NTLM_CHAL_LEN); + + memset(ntlmv2_blob1, 0, NTLMv2_BLOB1_LEN); + + time_t unix_time; + uint64_t nt_time = time(&unix_time); + nt_time += 11644473600LL; // Number of seconds betwen 1601 and 1970 + nt_time *= 1000 * 1000 * 10; // Convert seconds to 100 ns units + + ntlmv2_blob1[0] = 1; + ntlmv2_blob1[1] = 1; + mozilla::LittleEndian::writeUint64(&ntlmv2_blob1[8], nt_time); + PK11_GenerateRandom(&ntlmv2_blob1[16], NTLM_CHAL_LEN); + + rv = hasher->Init(nsICryptoHMAC::MD5, ntlmv2Key); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(ntlmv2_blob1, NTLMv2_BLOB1_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(reinterpret_cast (msg.targetInfo), msg.targetInfoLen); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Finish(false, ntlmv2ResponseStr); + if (NS_FAILED(rv)) { + return rv; + } + + if (ntlmv2ResponseStr.Length() != NTLMv2_RESP_LEN) { + return NS_ERROR_UNEXPECTED; + } + + memcpy(ntlmv2Resp, ntlmv2ResponseStr.get(), NTLMv2_RESP_LEN); + ntlmRespLen = NTLMv2_RESP_LEN + NTLMv2_BLOB1_LEN; + ntlmRespLen += msg.targetInfoLen; + if (!ntlmRespLen.isValid()) { + NS_ERROR("failed to do NTLMv2: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + } else if (msg.flags & NTLM_NegotiateNTLM2Key) { // compute NTLM2 session response - uint8_t sessionHash[16], temp[16]; + nsCString sessionHashString; + const uint8_t *sessionHash; - PK11_GenerateRandom(lmResp, 8); - memset(lmResp + 8, 0, LM_RESP_LEN - 8); + PK11_GenerateRandom(lmResp, NTLM_CHAL_LEN); + memset(lmResp + NTLM_CHAL_LEN, 0, LM_RESP_LEN - NTLM_CHAL_LEN); - memcpy(temp, msg.challenge, 8); - memcpy(temp + 8, lmResp, 8); - md5sum(temp, 16, sessionHash); + nsCOMPtr hasher = + do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Init(nsICryptoHash::MD5); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Update(lmResp, NTLM_CHAL_LEN); + if (NS_FAILED(rv)) { + return rv; + } + rv = hasher->Finish(false, sessionHashString); + if (NS_FAILED(rv)) { + return rv; + } + + sessionHash = reinterpret_cast (sessionHashString.get()); + + LogBuf("NTLM2 effective key: ", sessionHash, 8); NTLM_Hash(password, ntlmHash); LM_Response(ntlmHash, sessionHash, ntlmResp); - } - else - { + } else { NTLM_Hash(password, ntlmHash); LM_Response(ntlmHash, msg.challenge, ntlmResp); - if (sendLM) - { - uint8_t lmHash[LM_HASH_LEN]; - LM_Hash(password, lmHash); - LM_Response(lmHash, msg.challenge, lmResp); - } - else - { - // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2, - // the correct way to not send the LM hash is to send the NTLM hash twice - // in both the LM and NTLM response fields. - LM_Response(ntlmHash, msg.challenge, lmResp); - } + // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2, + // the correct way to not send the LM hash is to send the NTLM hash twice + // in both the LM and NTLM response fields. + LM_Response(ntlmHash, msg.challenge, lmResp); + } + + mozilla::CheckedInt totalLen = NTLM_TYPE3_HEADER_LEN + LM_RESP_LEN; + totalLen += hostLen; + totalLen += domainLen; + totalLen += userLen; + totalLen += ntlmRespLen.value(); + + if (!totalLen.isValid()) { + NS_ERROR("failed preparing to allocate NTLM response: integer overflow?!?"); + return NS_ERROR_FAILURE; + } + *outBuf = nsMemory::Alloc(totalLen.value()); + *outLen = totalLen.value(); + if (!*outBuf) { + return NS_ERROR_OUT_OF_MEMORY; } // // finally, we assemble the Type-3 msg :-) // void *cursor = *outBuf; - uint32_t offset; + mozilla::CheckedInt offset; // 0 : signature cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); @@ -710,29 +896,63 @@ GenerateType3Msg(const nsString &domain, cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER)); // 12 : LM response sec buf - offset = NTLM_TYPE3_HEADER_LEN + domainLen + userLen + hostLen; - cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset); - memcpy((uint8_t *) *outBuf + offset, lmResp, LM_RESP_LEN); + offset = NTLM_TYPE3_HEADER_LEN; + offset += domainLen; + offset += userLen; + offset += hostLen; + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset.value()); + memcpy((uint8_t *) *outBuf + offset.value(), lmResp, LM_RESP_LEN); - // 20 : NTLM response sec buf + // 20 : NTLM or NTLMv2 response sec buf offset += LM_RESP_LEN; - cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset); - memcpy((uint8_t *) *outBuf + offset, ntlmResp, NTLM_RESP_LEN); - + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + cursor = WriteSecBuf(cursor, ntlmRespLen.value(), offset.value()); + if (ntlmv2) { + memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmv2Resp, NTLMv2_RESP_LEN); + offset += NTLMv2_RESP_LEN; + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmv2_blob1, NTLMv2_BLOB1_LEN); + offset += NTLMv2_BLOB1_LEN; + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + memcpy(reinterpret_cast (*outBuf) + offset.value(), msg.targetInfo, msg.targetInfoLen); + } else { + memcpy(reinterpret_cast (*outBuf) + offset.value(), ntlmResp, NTLM_RESP_LEN); + } // 28 : domain name sec buf offset = NTLM_TYPE3_HEADER_LEN; - cursor = WriteSecBuf(cursor, domainLen, offset); - memcpy((uint8_t *) *outBuf + offset, domainPtr, domainLen); + cursor = WriteSecBuf(cursor, domainLen, offset.value()); + memcpy((uint8_t *) *outBuf + offset.value(), domainPtr, domainLen); // 36 : user name sec buf offset += domainLen; - cursor = WriteSecBuf(cursor, userLen, offset); - memcpy((uint8_t *) *outBuf + offset, userPtr, userLen); + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + cursor = WriteSecBuf(cursor, userLen, offset.value()); + memcpy(reinterpret_cast (*outBuf) + offset.value(), userPtr, userLen); // 44 : workstation (host) name sec buf offset += userLen; - cursor = WriteSecBuf(cursor, hostLen, offset); - memcpy((uint8_t *) *outBuf + offset, hostPtr, hostLen); + if (!offset.isValid()) { + NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); + return NS_ERROR_UNEXPECTED; + } + cursor = WriteSecBuf(cursor, hostLen, offset.value()); + memcpy(reinterpret_cast (*outBuf) + offset.value(), hostPtr, hostLen); // 52 : session key sec buf (not used) cursor = WriteSecBuf(cursor, 0, 0); @@ -755,6 +975,13 @@ nsNTLMAuthModule::~nsNTLMAuthModule() nsresult nsNTLMAuthModule::InitTest() { + static bool prefObserved = false; + if (!prefObserved) { + mozilla::Preferences::AddBoolVarCache( + &sNTLMv1Forced, "network.auth.force-generic-ntlm-v1", sNTLMv1Forced); + prefObserved = true; + } + nsNSSShutDownPreventionLock locker; // // disable NTLM authentication when FIPS mode is enabled. @@ -912,22 +1139,19 @@ des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash) ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, symkey, param); - if (!ctxt) - { + if (!ctxt) { NS_ERROR("no context"); goto done; } rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (uint8_t *) src, 8); - if (rv != SECSuccess) - { + if (rv != SECSuccess) { NS_ERROR("des failure"); goto done; } rv = PK11_DigestFinal(ctxt, hash+8, &n, 0); - if (rv != SECSuccess) - { + if (rv != SECSuccess) { NS_ERROR("des failure"); goto done; } @@ -942,23 +1166,3 @@ done: if (slot) PK11_FreeSlot(slot); } - -//----------------------------------------------------------------------------- -// MD5 support code - -static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result) -{ - PK11Context *ctxt = PK11_CreateDigestContext(SEC_OID_MD5); - if (ctxt) - { - if (PK11_DigestBegin(ctxt) == SECSuccess) - { - if (PK11_DigestOp(ctxt, input, inputLen) == SECSuccess) - { - uint32_t resultLen = 16; - PK11_DigestFinal(ctxt, result, &resultLen, resultLen); - } - } - PK11_DestroyContext(ctxt, true); - } -} diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp index 322bd44af0a..2c86cfa5c85 100644 --- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp +++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp @@ -94,7 +94,7 @@ SandboxBroker::SetSecurityLevelForContentProcess(bool aMoreStrict) ret = ret && (sandbox::SBOX_ALL_OK == result); } else { result = mPolicy->SetJobLevel(sandbox::JOB_NONE, 0); - bool ret = (sandbox::SBOX_ALL_OK == result); + ret = (sandbox::SBOX_ALL_OK == result); result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, sandbox::USER_NON_ADMIN); diff --git a/testing/mochitest/tests/SimpleTest/TestRunner.js b/testing/mochitest/tests/SimpleTest/TestRunner.js index 1de60148fce..34218eb2c8a 100644 --- a/testing/mochitest/tests/SimpleTest/TestRunner.js +++ b/testing/mochitest/tests/SimpleTest/TestRunner.js @@ -5,6 +5,9 @@ * type = eventName (QuitApplication) * data = json object {"filename":filename} <- for LoggerInit */ + +"use strict"; + function getElement(id) { return ((typeof(id) == "string") ? document.getElementById(id) : id); @@ -264,6 +267,7 @@ function StructuredLogger(name) { } else { str = LOG_DELIMITER + JSON.stringify(message) + LOG_DELIMITER; } + // BUGFIX: browser-chrome tests doesn't use LogController if (Object.keys(LogController.listeners).length !== 0) { LogController.log(str); @@ -272,7 +276,7 @@ function StructuredLogger(name) { } // Checking for error messages - if (message.expected || message.level === "ERROR") { + if (message.expected || (message.level && message.level === "ERROR")) { TestRunner.failureHandler(); } }; @@ -281,7 +285,6 @@ function StructuredLogger(name) { this.validMessage = function(message) { return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0; }; - } /** diff --git a/testing/mochitest/tests/SimpleTest/setup.js b/testing/mochitest/tests/SimpleTest/setup.js index c23130f8fcd..d71b3e9552b 100644 --- a/testing/mochitest/tests/SimpleTest/setup.js +++ b/testing/mochitest/tests/SimpleTest/setup.js @@ -4,14 +4,17 @@ * 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/. */ +"use strict"; + TestRunner.logEnabled = true; TestRunner.logger = LogController; /* Helper function */ -parseQueryString = function(encodedString, useArrays) { +function parseQueryString(encodedString, useArrays) { // strip a leading '?' from the encoded string - var qstr = (encodedString[0] == "?") ? encodedString.substring(1) : - encodedString; + var qstr = (encodedString.length > 0 && encodedString[0] == "?") + ? encodedString.substring(1) + : encodedString; var pairs = qstr.replace(/\+/g, "%20").split(/(\&\;|\&\#38\;|\&|\&)/); var o = {}; var decode; diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini index 6547fa045ef..bac178b41b5 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-a-bitrate.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 audio-only bitrate changes.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini index 45fd4061d11..68ddfa906b5 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-audio-bitrate.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-av-audio-bitrate.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 audio bitrate changes in multiplexed content.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-framesize.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-framesize.html.ini index 655e637ade0..47c5d53465b 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-framesize.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-framesize.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-av-framesize.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 frame size changes in multiplexed content.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-video-bitrate.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-video-bitrate.html.ini index 7b58fc5a8a0..37d35deae58 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-video-bitrate.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-av-video-bitrate.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-av-video-bitrate.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 video bitrate changes in multiplexed content.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-bitrate.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-bitrate.html.ini index 728d83f02f9..d4f30e02e1d 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-bitrate.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-bitrate.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-v-bitrate.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 video-only bitrate changes.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framerate.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framerate.html.ini index a3e998d3574..7fcf76f9e17 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framerate.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framerate.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-v-framerate.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 video-only frame rate changes.] + expected: FAIL + diff --git a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framesize.html.ini b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framesize.html.ini index a8beb3268c3..37d1e217409 100644 --- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framesize.html.ini +++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-v-framesize.html.ini @@ -1,3 +1,5 @@ [mediasource-config-change-mp4-v-framesize.html] type: testharness - disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467 + [Tests mp4 video-only frame size changes.] + expected: FAIL + diff --git a/toolkit/components/addoncompat/Prefetcher.jsm b/toolkit/components/addoncompat/Prefetcher.jsm new file mode 100644 index 00000000000..6c13a41a7f4 --- /dev/null +++ b/toolkit/components/addoncompat/Prefetcher.jsm @@ -0,0 +1,549 @@ +// 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/. + +this.EXPORTED_SYMBOLS = ["Prefetcher"]; + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Preferences", + "resource://gre/modules/Preferences.jsm"); + +// Rules are defined at the bottom of this file. +let PrefetcherRules = {}; + +/* + * When events that trigger in the content process are forwarded to + * add-ons in the chrome process, we expect the add-ons to send a lot + * of CPOWs to query content nodes while processing the events. To + * speed this up, the prefetching system anticipates which properties + * will be read and reads them ahead of time. The prefetched + * properties are passed to the chrome process along with each + * event. A typical scenario might work like this: + * + * 1. "load" event fires in content + * 2. Content process prefetches: + * event.target.defaultView = + * .location = + * event.target.getElementsByTagName("form") = [, ] + * .id = "login-form" + * .id = "subscribe-form" + * 3. Content process forwards "load" event to add-on along with + * prefetched data + * 4. Add-on reads: + * event.target.defaultView (already prefetched) + * event.target.getElementsByTagName("form") (already prefetched) + * .id (already prefetched) + * .className (not prefetched; CPOW must be sent) + * + * The amount of data to prefetch is determined based on the add-on ID + * and the event type. The specific data to select is determined using + * a set of Datalog-like rules (http://en.wikipedia.org/wiki/Datalog). + * + * Rules operate on a series of "tables" like in a database. Each + * table contains a set of content-process objects. When an event + * handler runs, it seeds some initial tables with objects of + * interest. For example, the Event table might start out containing + * the event that fired. + * + * Objects are added to tables using a set of rules of the form "if X + * is in table A, then add F(X) to table B", where F(X) is typically a + * property access or a method call. The most common functions F are: + * + * PropertyOp(destTable, sourceTable, property): + * For each object X in sourceTable, add X.property to destTable. + * MethodOp(destTable, sourceTable, method, args): + * For each object X in sourceTable, add X.method(args) to destTable. + * CollectionOp(destTable, sourceTable): + * For each object X in sourceTable, add X[i] to destTable for + * all i from 0 to X.length - 1. + * + * To generate the prefetching in the example above, the following + * rules would work: + * + * 1. PropertyOp("EventTarget", "Event", "target") + * 2. PropertyOp("Window", "EventTarget", "defaultView") + * 3. MethodOp("FormCollection", "EventTarget", "getElementsByTagName", "form") + * 4. CollectionOp("Form", "FormCollection") + * 5. PropertyOp(null, "Form", "id") + * + * Rules are written at the bottom of this file. + * + * When a rule runs, it will usually generate some cache entries that + * will be passed to the chrome process. For example, when PropertyOp + * prefetches obj.prop and gets the value X, it caches the value of + * obj and X. When the chrome process receives this data, it creates a + * two-level map [obj -> [prop -> X]]. When the add-on accesses a + * property on obj, the add-on shim code consults this map to see if + * the property has already been cached. + */ + +const PREF_PREFETCHING_ENABLED = "extensions.interposition.prefetching"; + +function objAddr(obj) +{ + if (obj && typeof(obj) == "object") { + return String(obj) + "[" + Cu.getJSTestingFunctions().objectAddress(obj) + "]"; + } + return String(obj); +} + +function log(...args) +{ + return; + + for (let arg of args) { + dump(arg); + dump(" "); + } + dump("\n"); +} + +function logPrefetch(kind, value1, component, value2) +{ + return; + log("prefetching", kind, objAddr(value1) + "." + component, "=", objAddr(value2)); +} + +/* + * All the Op classes (representing Datalog rules) have the same interface: + * outputTable: Table that objects generated by the rule are added to. + * Note that this can be null. + * inputTable: Table that the rule draws objects from. + * addObject(database, obj): Called when an object is added to inputTable. + * This code should take care of adding objects to outputTable. + * Data to be cached should be stored by calling database.cache. + * makeCacheEntry(item, cache): + * Called by the chrome process to create the two-level map of + * prefetched objects. |item| holds the cached data + * generated by the content process. |cache| is the map to be + * generated. + */ + +function PropertyOp(outputTable, inputTable, prop) +{ + this.outputTable = outputTable; + this.inputTable = inputTable; + this.prop = prop; +} + +PropertyOp.prototype.addObject = function(database, obj) +{ + let has = false, propValue; + try { + if (this.prop in obj) { + has = true; + propValue = obj[this.prop]; + } + } catch (e) { + // Don't cache anything if an exception is thrown. + return; + } + + logPrefetch("prop", obj, this.prop, propValue); + database.cache(this.index, obj, has, propValue); + if (has && typeof(propValue) == "object" && propValue && this.outputTable) { + database.add(this.outputTable, propValue); + } +} + +PropertyOp.prototype.makeCacheEntry = function(item, cache) +{ + let [index, obj, has, propValue] = item; + + let desc = { configurable: false, enumerable: true, writable: false, value: propValue }; + + if (!cache.has(obj)) { + cache.set(obj, new Map()); + } + let propMap = cache.get(obj); + propMap.set(this.prop, desc); +} + +function MethodOp(outputTable, inputTable, method, ...args) +{ + this.outputTable = outputTable; + this.inputTable = inputTable; + this.method = method; + this.args = args; +} + +MethodOp.prototype.addObject = function(database, obj) +{ + let result; + try { + result = obj[this.method].apply(obj, this.args); + } catch (e) { + // Don't cache anything if an exception is thrown. + return; + } + + logPrefetch("method", obj, this.method + "(" + this.args + ")", result); + database.cache(this.index, obj, result); + if (result && typeof(result) == "object" && this.outputTable) { + database.add(this.outputTable, result); + } +} + +MethodOp.prototype.makeCacheEntry = function(item, cache) +{ + let [index, obj, result] = item; + + if (!cache.has(obj)) { + cache.set(obj, new Map()); + } + let propMap = cache.get(obj); + let fallback = propMap.get(this.method); + + let method = this.method; + let selfArgs = this.args; + let methodImpl = function(...args) { + if (args.length == selfArgs.length && args.every((v, i) => v === selfArgs[i])) { + return result; + } + + if (fallback) { + return fallback.value(...args); + } else { + return obj[method](...args); + } + }; + + let desc = { configurable: false, enumerable: true, writable: false, value: methodImpl }; + propMap.set(this.method, desc); +} + +function CollectionOp(outputTable, inputTable) +{ + this.outputTable = outputTable; + this.inputTable = inputTable; +} + +CollectionOp.prototype.addObject = function(database, obj) +{ + let elements = []; + try { + let len = obj.length; + for (let i = 0; i < len; i++) { + logPrefetch("index", obj, i, obj[i]); + elements.push(obj[i]); + } + } catch (e) { + // Don't cache anything if an exception is thrown. + return; + } + + database.cache(this.index, obj, ...elements); + for (let i = 0; i < elements.length; i++) { + if (elements[i] && typeof(elements[i]) == "object" && this.outputTable) { + database.add(this.outputTable, elements[i]); + } + } +} + +CollectionOp.prototype.makeCacheEntry = function(item, cache) +{ + let [index, obj, ...elements] = item; + + if (!cache.has(obj)) { + cache.set(obj, new Map()); + } + let propMap = cache.get(obj); + + let lenDesc = { configurable: false, enumerable: true, writable: false, value: elements.length }; + propMap.set("length", lenDesc); + + for (let i = 0; i < elements.length; i++) { + let desc = { configurable: false, enumerable: true, writable: false, value: elements[i] }; + propMap.set(i, desc); + } +} + +function CopyOp(outputTable, inputTable) +{ + this.outputTable = outputTable; + this.inputTable = inputTable; +} + +CopyOp.prototype.addObject = function(database, obj) +{ + database.add(this.outputTable, obj); +} + +function Database(trigger, addons) +{ + // Create a map of rules that apply to this specific trigger and set + // of add-ons. The rules are indexed based on their inputTable. + this.rules = new Map(); + for (let addon of addons) { + let addonRules = PrefetcherRules[addon] || {}; + let triggerRules = addonRules[trigger] || []; + for (let rule of triggerRules) { + let inTable = rule.inputTable; + if (!this.rules.has(inTable)) { + this.rules.set(inTable, new Set()); + } + let set = this.rules.get(inTable); + set.add(rule); + } + } + + // this.tables maps table names to sets of objects contained in them. + this.tables = new Map(); + + // todo is a worklist of items added to tables that have not had + // rules run on them yet. + this.todo = []; + + // Cached data to be sent to the chrome process. + this.cached = []; +} + +Database.prototype = { + // Add an object to a table. + add: function(table, obj) { + if (!this.tables.has(table)) { + this.tables.set(table, new Set()); + } + let tableSet = this.tables.get(table); + if (tableSet.has(obj)) { + return; + } + tableSet.add(obj); + + this.todo.push([table, obj]); + }, + + cache: function(...args) { + this.cached.push(args); + }, + + // Run a fixed-point iteration that adds objects to table based on + // this.rules until there are no more objects to add. + process: function() { + while (this.todo.length) { + let [table, obj] = this.todo.pop(); + let rules = this.rules.get(table); + if (!rules) { + continue; + } + for (let rule of rules) { + rule.addObject(this, obj); + } + } + }, +}; + +let Prefetcher = { + init: function() { + // Give an index to each rule and store it in this.ruleMap based + // on the index. The index is used to serialize and deserialize + // data from content to chrome. + let counter = 0; + this.ruleMap = new Map(); + for (let addon in PrefetcherRules) { + for (let trigger in PrefetcherRules[addon]) { + for (let rule of PrefetcherRules[addon][trigger]) { + rule.index = counter++; + this.ruleMap.set(rule.index, rule); + } + } + } + + this.prefetchingEnabled = Preferences.get(PREF_PREFETCHING_ENABLED, false); + Services.prefs.addObserver(PREF_PREFETCHING_ENABLED, this, false); + Services.obs.addObserver(this, "xpcom-shutdown", false); + }, + + observe: function(subject, topic, data) { + if (topic == "xpcom-shutdown") { + Services.prefs.removeObserver(PREF_PREFETCHING_ENABLED, this); + Services.obs.removeObserver(this, "xpcom-shutdown"); + } else if (topic == PREF_PREFETCHING_ENABLED) { + this.prefetchingEnabled = Preferences.get(PREF_PREFETCHING_ENABLED, false); + } + }, + + // Called when an event occurs in the content process. The event is + // described by the trigger string. |addons| is a list of addons + // that have listeners installed for the event. |args| is + // event-specific data (such as the event object). + prefetch: function(trigger, addons, args) { + if (!this.prefetchingEnabled) { + return [[], []]; + } + + let db = new Database(trigger, addons); + for (let table in args) { + log("root", table, "=", objAddr(args[table])); + db.add(table, args[table]); + } + + // Prefetch objects and add them to tables. + db.process(); + + // Data passed to sendAsyncMessage must be split into a JSON + // portion and a CPOW portion. This code splits apart db.cached + // into these two pieces. Any object in db.cache is added to an + // array of CPOWs and replaced with {cpow: }. + let cpowIndexes = new Map(); + let prefetched = []; + let cpows = []; + for (let item of db.cached) { + item = item.map((elt) => { + if (elt && typeof(elt) == "object") { + if (!cpowIndexes.has(elt)) { + let index = cpows.length; + cpows.push(elt); + cpowIndexes.set(elt, index); + } + return {cpow: cpowIndexes.get(elt)}; + } else { + return elt; + } + }); + + prefetched.push(item); + } + + return [prefetched, cpows]; + }, + + cache: null, + + // Generate a two-level mapping based on cached data received from + // the content process. + generateCache: function(prefetched, cpows) { + let cache = new Map(); + for (let item of prefetched) { + // Replace anything of the form {cpow: } with the actual + // object in |cpows|. + item = item.map((elt) => { + if (elt && typeof(elt) == "object") { + return cpows[elt.cpow]; + } + return elt; + }); + + let index = item[0]; + let op = this.ruleMap.get(index); + op.makeCacheEntry(item, cache); + } + return cache; + }, + + // Run |func|, using the prefetched data in |prefetched| and |cpows| + // as a cache. + withPrefetching: function(prefetched, cpows, func) { + if (!this.prefetchingEnabled) { + return func(); + } + + this.cache = this.generateCache(prefetched, cpows); + + try { + log("Prefetching on"); + return func(); + } finally { + // After we return from this event handler, the content process + // is free to continue executing, so we invalidate our cache. + log("Prefetching off"); + this.cache = null; + } + }, + + // Called by shim code in the chrome process to check if target.prop + // is cached. + lookupInCache: function(addon, target, prop) { + if (!this.cache || !Cu.isCrossProcessWrapper(target)) { + return null; + } + + let propMap = this.cache.get(target); + if (!propMap) { + return null; + } + + return propMap.get(prop); + }, +}; + +let AdblockId = "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}"; +let AdblockRules = { + "ContentPolicy.shouldLoad": [ + new MethodOp("Node", "InitNode", "QueryInterface", Ci.nsISupports), + new PropertyOp("Document", "Node", "ownerDocument"), + new PropertyOp("Window", "Node", "defaultView"), + new PropertyOp("Window", "Document", "defaultView"), + new PropertyOp("TopWindow", "Window", "top"), + new PropertyOp("WindowLocation", "Window", "location"), + new PropertyOp(null, "WindowLocation", "href"), + new PropertyOp("Window", "Window", "parent"), + new PropertyOp(null, "Window", "name"), + new PropertyOp("Document", "Window", "document"), + new PropertyOp("TopDocumentElement", "Document", "documentElement"), + new MethodOp(null, "TopDocumentElement", "getAttribute", "data-adblockkey"), + ] +}; +PrefetcherRules[AdblockId] = AdblockRules; + +let LastpassId = "support@lastpass.com"; +let LastpassRules = { + "EventTarget.handleEvent": [ + new PropertyOp("EventTarget", "Event", "target"), + new PropertyOp("EventOriginalTarget", "Event", "originalTarget"), + new PropertyOp("Window", "EventOriginalTarget", "defaultView"), + + new CopyOp("Frame", "Window"), + new PropertyOp("FrameCollection", "Window", "frames"), + new CollectionOp("Frame", "FrameCollection"), + new PropertyOp("FrameCollection", "Frame", "frames"), + new PropertyOp("FrameDocument", "Frame", "document"), + new PropertyOp(null, "Frame", "window"), + new PropertyOp(null, "FrameDocument", "defaultView"), + + new PropertyOp("FrameDocumentLocation", "FrameDocument", "location"), + new PropertyOp(null, "FrameDocumentLocation", "href"), + new PropertyOp("FrameLocation", "Frame", "location"), + new PropertyOp(null, "FrameLocation", "href"), + + new MethodOp("FormCollection", "FrameDocument", "getElementsByTagName", "form"), + new MethodOp("FormCollection", "FrameDocument", "getElementsByTagName", "FORM"), + new CollectionOp("Form", "FormCollection"), + new PropertyOp("FormElementCollection", "Form", "elements"), + new CollectionOp("FormElement", "FormElementCollection"), + new PropertyOp("Style", "Form", "style"), + + new PropertyOp(null, "FormElement", "type"), + new PropertyOp(null, "FormElement", "name"), + new PropertyOp(null, "FormElement", "value"), + new PropertyOp(null, "FormElement", "tagName"), + new PropertyOp(null, "FormElement", "id"), + new PropertyOp("Style", "FormElement", "style"), + + new PropertyOp(null, "Style", "visibility"), + + new MethodOp("MetaElementsCollection", "EventOriginalTarget", "getElementsByTagName", "meta"), + new CollectionOp("MetaElement", "MetaElementsCollection"), + new PropertyOp(null, "MetaElement", "httpEquiv"), + + new MethodOp("InputElementCollection", "FrameDocument", "getElementsByTagName", "input"), + new MethodOp("InputElementCollection", "FrameDocument", "getElementsByTagName", "INPUT"), + new CollectionOp("InputElement", "InputElementCollection"), + new PropertyOp(null, "InputElement", "type"), + new PropertyOp(null, "InputElement", "name"), + new PropertyOp(null, "InputElement", "tagName"), + new PropertyOp(null, "InputElement", "form"), + + new PropertyOp("BodyElement", "FrameDocument", "body"), + new PropertyOp("BodyInnerText", "BodyElement", "innerText"), + + new PropertyOp("DocumentFormCollection", "FrameDocument", "forms"), + new CollectionOp("DocumentForm", "DocumentFormCollection"), + ] +}; +PrefetcherRules[LastpassId] = LastpassRules; diff --git a/toolkit/components/addoncompat/RemoteAddonsChild.jsm b/toolkit/components/addoncompat/RemoteAddonsChild.jsm index 3ae195fd2bf..41b8a9914e1 100644 --- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm @@ -14,6 +14,8 @@ Cu.import("resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Prefetcher", + "resource://gre/modules/Prefetcher.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "SystemPrincipal", "@mozilla.org/systemprincipal;1", "nsIPrincipal"); @@ -165,6 +167,11 @@ let ContentPolicyChild = { shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra, requestPrincipal) { + let addons = NotificationTracker.findSuffixes(["content-policy"]); + let [prefetched, cpows] = Prefetcher.prefetch("ContentPolicy.shouldLoad", + addons, {InitNode: node}); + cpows.node = node; + let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"] .getService(Ci.nsISyncMessageSender); let rval = cpmm.sendRpcMessage("Addons:ContentPolicy:Run", { @@ -173,9 +180,8 @@ let ContentPolicyChild = { requestOrigin: requestOrigin ? requestOrigin.spec : null, mimeTypeGuess: mimeTypeGuess, requestPrincipal: requestPrincipal, - }, { - node: node, // Sent as a CPOW. - }); + prefetched: prefetched, + }, cpows); if (rval.length != 1) { return Ci.nsIContentPolicy.ACCEPT; } @@ -416,11 +422,20 @@ EventTargetChild.prototype = { }, handleEvent: function(capturing, event) { + let addons = NotificationTracker.findSuffixes(["event", event.type, capturing]); + let [prefetched, cpows] = Prefetcher.prefetch("EventTarget.handleEvent", + addons, + {Event: event, + Window: this._childGlobal.content}); + cpows.event = event; + cpows.eventTarget = event.target; + this._childGlobal.sendRpcMessage("Addons:Event:Run", {type: event.type, capturing: capturing, - isTrusted: event.isTrusted}, - {event: event}); + isTrusted: event.isTrusted, + prefetched: prefetched}, + cpows); } }; @@ -481,6 +496,7 @@ let RemoteAddonsChild = { _ready: false, makeReady: function() { + Prefetcher.init(); NotificationTracker.init(); ContentPolicyChild.init(); AboutProtocolChild.init(); diff --git a/toolkit/components/addoncompat/RemoteAddonsParent.jsm b/toolkit/components/addoncompat/RemoteAddonsParent.jsm index 72ab2c3f9af..5854fe6f507 100644 --- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm +++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm @@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Prefetcher", + "resource://gre/modules/Prefetcher.jsm"); // Similar to Python. Returns dict[key] if it exists. Otherwise, // sets dict[key] to default_ and returns default_. @@ -143,13 +145,15 @@ let ContentPolicyParent = { let contentLocation = BrowserUtils.makeURI(aData.contentLocation); let requestOrigin = aData.requestOrigin ? BrowserUtils.makeURI(aData.requestOrigin) : null; - let result = policy.shouldLoad(aData.contentType, - contentLocation, - requestOrigin, - aObjects.node, - aData.mimeTypeGuess, - null, - aData.requestPrincipal); + let result = Prefetcher.withPrefetching(aData.prefetched, aObjects, () => { + return policy.shouldLoad(aData.contentType, + contentLocation, + requestOrigin, + aObjects.node, + aData.mimeTypeGuess, + null, + aData.requestPrincipal); + }); if (result != Ci.nsIContentPolicy.ACCEPT && result != 0) return result; } catch (e) { @@ -461,12 +465,14 @@ let EventTargetParent = { switch (msg.name) { case "Addons:Event:Run": this.dispatch(msg.target, msg.data.type, msg.data.capturing, - msg.data.isTrusted, msg.objects.event); + msg.data.isTrusted, msg.data.prefetched, msg.objects); break; } }, - dispatch: function(browser, type, capturing, isTrusted, event) { + dispatch: function(browser, type, capturing, isTrusted, prefetched, cpows) { + let event = cpows.event; + let eventTarget = cpows.eventTarget; let targets = this.getTargets(browser); for (let target of targets) { let listeners = this._listeners.get(target); @@ -485,11 +491,13 @@ let EventTargetParent = { for (let handler of handlers) { try { - if ("handleEvent" in handler) { - handler.handleEvent(event); - } else { - handler.call(event.target, event); - } + Prefetcher.withPrefetching(prefetched, cpows, () => { + if ("handleEvent" in handler) { + handler.handleEvent(event); + } else { + handler.call(event.target, event); + } + }); } catch (e) { Cu.reportError(e); } @@ -742,14 +750,25 @@ let DummyContentDocument = { location: { href: "about:blank" } }; -RemoteBrowserElementInterposition.getters.contentDocument = function(addon, target) { - // If we don't have a CPOW yet, just return something we can use to - // examine readyState. This is useful for tests that create a new - // tab and then immediately start polling readyState. - if (!target.contentDocumentAsCPOW) { +function getContentDocument(addon, browser) +{ + let doc = Prefetcher.lookupInCache(addon, browser.contentWindowAsCPOW, "document"); + if (doc) { + return doc; + } + + doc = browser.contentDocumentAsCPOW; + if (!doc) { + // If we don't have a CPOW yet, just return something we can use to + // examine readyState. This is useful for tests that create a new + // tab and then immediately start polling readyState. return DummyContentDocument; } - return target.contentDocumentAsCPOW; + return doc; +} + +RemoteBrowserElementInterposition.getters.contentDocument = function(addon, target) { + return getContentDocument(addon, target); }; let TabBrowserElementInterposition = new Interposition("TabBrowserElementInterposition", @@ -764,10 +783,7 @@ TabBrowserElementInterposition.getters.contentWindow = function(addon, target) { TabBrowserElementInterposition.getters.contentDocument = function(addon, target) { let browser = target.selectedBrowser; - if (!browser.contentDocumentAsCPOW) { - return DummyContentDocument; - } - return browser.contentDocumentAsCPOW; + return getContentDocument(addon, browser); }; let ChromeWindowInterposition = new Interposition("ChromeWindowInterposition", diff --git a/toolkit/components/addoncompat/moz.build b/toolkit/components/addoncompat/moz.build index 56e8d050a72..a87c1861296 100644 --- a/toolkit/components/addoncompat/moz.build +++ b/toolkit/components/addoncompat/moz.build @@ -13,6 +13,7 @@ EXTRA_COMPONENTS += [ ] EXTRA_JS_MODULES += [ + 'Prefetcher.jsm', 'RemoteAddonsChild.jsm', 'RemoteAddonsParent.jsm', ] diff --git a/toolkit/components/addoncompat/multiprocessShims.js b/toolkit/components/addoncompat/multiprocessShims.js index 9cbcce8afc0..96e02d0c71f 100644 --- a/toolkit/components/addoncompat/multiprocessShims.js +++ b/toolkit/components/addoncompat/multiprocessShims.js @@ -9,6 +9,8 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Prefetcher", + "resource://gre/modules/Prefetcher.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RemoteAddonsParent", "resource://gre/modules/RemoteAddonsParent.jsm"); @@ -61,6 +63,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "RemoteAddonsParent", function AddonInterpositionService() { + Prefetcher.init(); RemoteAddonsParent.init(); // These maps keep track of the interpositions for all different @@ -116,7 +119,7 @@ AddonInterpositionService.prototype = { } if (!interp) { - return null; + return Prefetcher.lookupInCache(addon, target, prop); } let desc = { configurable: false, enumerable: true }; @@ -138,7 +141,7 @@ AddonInterpositionService.prototype = { return desc; } - return null; + return Prefetcher.lookupInCache(addon, target, prop); }, }; diff --git a/webapprt/content/downloads/downloads.js b/webapprt/content/downloads/downloads.js index 1a5907379fe..78cd36120c6 100644 --- a/webapprt/content/downloads/downloads.js +++ b/webapprt/content/downloads/downloads.js @@ -727,7 +727,7 @@ let gDownloadList = { this.downloadView.parentNode.replaceChild(empty, this.downloadView); this.downloadView = empty; - for (let downloadItem of this.downloadItems) { + for each (let downloadItem in this.downloadItems) { if (downloadItem.inProgress || downloadItem.matchesSearch(this.searchTerms, this.searchAttributes)) { this.downloadView.appendChild(downloadItem.element); @@ -773,7 +773,7 @@ let gDownloadList = { let button = document.getElementById("clearListButton"); // The button is enabled if we have items in the list that we can clean up. - for (let downloadItem of this.downloadItems) { + for each (let downloadItem in this.downloadItems) { if (!downloadItem.inProgress && downloadItem.matchesSearch(this.searchTerms, this.searchAttributes)) { button.disabled = false; @@ -983,7 +983,7 @@ let gDownloadList = { let totalSize = 0; let totalTransferred = 0; - for (let downloadItem of this.downloadItems) { + for each (let downloadItem in this.downloadItems) { if (!downloadItem.inProgress) { continue; }