diff --git a/CLOBBER b/CLOBBER index 5327c769277..39881310233 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,7 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1108782 - Follow-up: clobber. r=me - -There's all kinds fail waiting at the end of these dependency changes. -Definitely clobber needed! \ No newline at end of file +Bug 1205242 - Changed toolchain, needs clobber diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index cb2b73ebf4f..b460be5551a 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -2071,6 +2071,10 @@ DocAccessible::ValidateARIAOwned() nsIContent* childEl = childEls->ElementAt(idx); Accessible* child = GetAccessible(childEl); if (child && child->IsInDocument() && !child->GetFrame()) { + if (!child->Parent()) { + NS_ERROR("An element in the document doesn't have a parent?"); + continue; + } UpdateTreeOnRemoval(child->Parent(), childEl); } } diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl index 8938aa28038..3f7e1f766a7 100644 --- a/accessible/ipc/PDocAccessible.ipdl +++ b/accessible/ipc/PDocAccessible.ipdl @@ -225,7 +225,7 @@ child: prio(high) sync MaxValue(uint64_t aID) returns(double aValue); prio(high) sync Step(uint64_t aID) returns(double aStep); - prio(high) sync TakeFocus(uint64_t aID); + async TakeFocus(uint64_t aID); prio(high) sync EmbeddedChildCount(uint64_t aID) returns(uint32_t aCount); prio(high) sync IndexOfEmbeddedChild(uint64_t aID, uint64_t aChildID) returns(uint32_t childIdx); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 5912c1b5207..7983f3d5f7c 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1872,6 +1872,9 @@ pref("privacy.trackingprotection.ui.enabled", false); pref("privacy.trackingprotection.introCount", 0); pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/"); +// Enable Contextual Identity Containers +pref("privacy.userContext.enabled", false); + #ifndef RELEASE_BUILD // At the moment, autostart.2 is used, while autostart.1 is unused. // We leave it here set to false to reset users' defaults and allow diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index be34ae7a28d..4c66c38a1b6 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -11,12 +11,44 @@ style="border:0px;padding:0px;margin:0px;-moz-appearance:none"> - + + + diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 236bdaee9d0..f81cd2d873a 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4060,6 +4060,31 @@ function updateEditUIVisibility() #endif } +/** + * Opens a new tab with the userContextId specified as an attribute of + * sourceEvent. This attribute is propagated to the top level originAttributes + * living on the tab's docShell. + * + * @param event + * A click event on a userContext File Menu option + */ +function openNewUserContextTab(event) +{ + openUILinkIn(BROWSER_NEW_TAB_URL, "tab", { + userContextId: event.target.getAttribute('usercontextid'), + }); +} + +/** + * Updates File Menu User Context UI visibility depending on + * privacy.userContext.enabled pref state. + */ +function updateUserContextUIVisibility() +{ + let userContextEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled"); + document.getElementById("menu_newUserContext").hidden = !userContextEnabled; +} + /** * Makes the Character Encoding menu enabled or disabled as appropriate. * To be called when the View menu or the app menu is opened. diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 1c0c5149299..1d72f4f288b 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1415,6 +1415,7 @@ var aSkipAnimation; var aForceNotRemote; var aNoReferrer; + var aUserContextId; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1431,6 +1432,7 @@ aSkipAnimation = params.skipAnimation; aForceNotRemote = params.forceNotRemote; aNoReferrer = params.noReferrer; + aUserContextId = params.userContextId; } var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : @@ -1448,7 +1450,8 @@ skipAnimation: aSkipAnimation, allowMixedContent: aAllowMixedContent, forceNotRemote: aForceNotRemote, - noReferrer: aNoReferrer }); + noReferrer: aNoReferrer, + userContextId: aUserContextId }); if (!bgLoad) this.selectedTab = tab; @@ -1690,6 +1693,7 @@ let remote = aParams && aParams.remote; let uriIsAboutBlank = aParams && aParams.uriIsAboutBlank; let isPreloadBrowser = aParams && aParams.isPreloadBrowser; + let userContextId = aParams && aParams.userContextId; let b = document.createElementNS(NS_XUL, "browser"); b.permanentKey = {}; @@ -1699,6 +1703,9 @@ b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu")); b.setAttribute("tooltip", this.getAttribute("contenttooltip")); + if (userContextId) + b.setAttribute("usercontextid", userContextId); + if (remote) b.setAttribute("remote", "true"); @@ -1767,6 +1774,7 @@ var aAllowMixedContent; var aForceNotRemote; var aNoReferrer; + var aUserContextId; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1783,6 +1791,7 @@ aAllowMixedContent = params.allowMixedContent; aForceNotRemote = params.forceNotRemote; aNoReferrer = params.noReferrer; + aUserContextId = params.userContextId; } // if we're adding tabs, we're past interrupt mode, ditch the owner @@ -1831,16 +1840,18 @@ let b; let usingPreloadedContent = false; - // If we open a new tab with the newtab URL, - // check if there is a preloaded browser ready. - if (aURI == BROWSER_NEW_TAB_URL) { + // If we open a new tab with the newtab URL in the default + // userContext, check if there is a preloaded browser ready. + if (aURI == BROWSER_NEW_TAB_URL && !aUserContextId) { b = this._getPreloadedBrowser(); usingPreloadedContent = !!b; } if (!b) { // No preloaded browser found, create one. - b = this._createBrowser({remote, uriIsAboutBlank}); + b = this._createBrowser({remote: remote, + uriIsAboutBlank: uriIsAboutBlank, + userContextId: aUserContextId}); } let notificationbox = this.getNotificationBox(b); @@ -2486,6 +2497,11 @@ if (ourBrowser.isRemoteBrowser != otherBrowser.isRemoteBrowser) return; + // Keep the userContextId if set on other browser + if (otherBrowser.hasAttribute("usercontextid")) { + ourBrowser.setAttribute("usercontextid", otherBrowser.getAttribute("usercontextid")); + } + // That's gBrowser for the other window, not the tab's browser! var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser; var isPending = aOtherTab.hasAttribute("pending"); diff --git a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js index 1c9e3bbb8d4..91a4a7e9cea 100644 --- a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js +++ b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js @@ -18,6 +18,7 @@ function onTabModalDialogLoaded(node) { } } +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); // Listen for the dialog being created Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded", false); diff --git a/browser/base/content/test/general/browser_double_close_tab.js b/browser/base/content/test/general/browser_double_close_tab.js index 19211e571f1..84a62cd0f0d 100644 --- a/browser/base/content/test/general/browser_double_close_tab.js +++ b/browser/base/content/test/general/browser_double_close_tab.js @@ -2,6 +2,8 @@ const TEST_PAGE = "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; var testTab; +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + function waitForDialog(callback) { function onTabModalDialogLoaded(node) { Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded"); diff --git a/browser/base/content/test/general/browser_tabs_close_beforeunload.js b/browser/base/content/test/general/browser_tabs_close_beforeunload.js index 08966b655e7..807ba7a211e 100644 --- a/browser/base/content/test/general/browser_tabs_close_beforeunload.js +++ b/browser/base/content/test/general/browser_tabs_close_beforeunload.js @@ -1,5 +1,7 @@ "use strict"; +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html"; const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html"; diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index c841266c0e9..a68af9173f4 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -173,6 +173,7 @@ function whereToOpenLink( e, ignoreButton, ignoreAlt ) * skipTabAnimation (boolean) * allowPinnedTabHostChange (boolean) * allowPopups (boolean) + * userContextId (unsigned int) */ function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) { var params; @@ -216,6 +217,7 @@ function openLinkIn(url, where, params) { var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange; var aNoReferrer = params.noReferrer; var aAllowPopups = !!params.allowPopups; + var aUserContextId = params.userContextId; if (where == "save") { if (!aInitiatingDoc) { @@ -357,7 +359,8 @@ function openLinkIn(url, where, params) { relatedToCurrent: aRelatedToCurrent, skipAnimation: aSkipTabAnimation, allowMixedContent: aAllowMixedContent, - noReferrer: aNoReferrer + noReferrer: aNoReferrer, + userContextId: aUserContextId }); break; } diff --git a/browser/components/tabview/test/browser_tabview_bug599626.js b/browser/components/tabview/test/browser_tabview_bug599626.js index 47ecda3b0c7..15ea3e91b57 100644 --- a/browser/components/tabview/test/browser_tabview_bug599626.js +++ b/browser/components/tabview/test/browser_tabview_bug599626.js @@ -6,6 +6,8 @@ const TEST_URL = 'data:text/html,'; +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + function test() { waitForExplicitFinish(); showTabView(onTabViewShown); diff --git a/browser/components/tabview/test/browser_tabview_bug626455.js b/browser/components/tabview/test/browser_tabview_bug626455.js index 4344b9ac242..8c8bb16e2eb 100644 --- a/browser/components/tabview/test/browser_tabview_bug626455.js +++ b/browser/components/tabview/test/browser_tabview_bug626455.js @@ -15,6 +15,8 @@ const TEST_URL = 'data:text/html, + + + + diff --git a/dom/animation/test/css-animations/test_keyframeeffect-getframes.html b/dom/animation/test/css-animations/test_keyframeeffect-getframes.html new file mode 100644 index 00000000000..bf7592f6613 --- /dev/null +++ b/dom/animation/test/css-animations/test_keyframeeffect-getframes.html @@ -0,0 +1,15 @@ + + + + +
+ + diff --git a/dom/animation/test/css-transitions/file_keyframeeffect-getframes.html b/dom/animation/test/css-transitions/file_keyframeeffect-getframes.html new file mode 100644 index 00000000000..2ae6916ae5b --- /dev/null +++ b/dom/animation/test/css-transitions/file_keyframeeffect-getframes.html @@ -0,0 +1,73 @@ + + + + + + diff --git a/dom/animation/test/css-transitions/test_keyframeeffect-getframes.html b/dom/animation/test/css-transitions/test_keyframeeffect-getframes.html new file mode 100644 index 00000000000..251a3647ee8 --- /dev/null +++ b/dom/animation/test/css-transitions/test_keyframeeffect-getframes.html @@ -0,0 +1,14 @@ + + + + +
+ diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index eeb5f980c02..10d8866c851 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -32,6 +32,8 @@ support-files = css-animations/file_animation-reverse.html support-files = css-animations/file_animation-starttime.html [css-animations/test_cssanimation-animationname.html] support-files = css-animations/file_cssanimation-animationname.html +[css-animations/test_keyframeeffect-getframes.html] +support-files = css-animations/file_keyframeeffect-getframes.html [css-animations/test_effect-target.html] support-files = css-animations/file_effect-target.html [css-animations/test_element-get-animations.html] @@ -53,6 +55,8 @@ support-files = css-transitions/file_animation-ready.html support-files = css-transitions/file_animation-starttime.html [css-transitions/test_csstransition-transitionproperty.html] support-files = css-transitions/file_csstransition-transitionproperty.html +[css-transitions/test_keyframeeffect-getframes.html] +support-files = css-transitions/file_keyframeeffect-getframes.html [css-transitions/test_effect-target.html] support-files = css-transitions/file_effect-target.html [css-transitions/test_element-get-animations.html] diff --git a/dom/base/DOMParser.cpp b/dom/base/DOMParser.cpp index 0c3edb9fe4f..40e9b8ebcc2 100644 --- a/dom/base/DOMParser.cpp +++ b/dom/base/DOMParser.cpp @@ -8,6 +8,7 @@ #include "nsIDOMDocument.h" #include "nsNetUtil.h" +#include "nsIStreamListener.h" #include "nsStringStream.h" #include "nsIScriptSecurityManager.h" #include "nsCRT.h" diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index b4f71dafce3..ac35e0d9eb7 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -17,6 +17,7 @@ #include "nsNetUtil.h" #include "nsIAuthPrompt.h" #include "nsIAuthPrompt2.h" +#include "nsIInputStream.h" #include "nsIInterfaceRequestorUtils.h" #include "nsMimeTypes.h" #include "nsIPromptFactory.h" diff --git a/dom/base/FileList.cpp b/dom/base/FileList.cpp index 642b167f982..5b807e7deac 100644 --- a/dom/base/FileList.cpp +++ b/dom/base/FileList.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" +#include "mozilla/dom/File.h" namespace mozilla { namespace dom { diff --git a/dom/base/PostMessageEvent.cpp b/dom/base/PostMessageEvent.cpp index 25b203c55e6..b5df8f0a602 100644 --- a/dom/base/PostMessageEvent.cpp +++ b/dom/base/PostMessageEvent.cpp @@ -8,6 +8,7 @@ #include "MessageEvent.h" #include "mozilla/dom/BlobBinding.h" +#include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" #include "mozilla/dom/MessagePort.h" diff --git a/dom/base/ProcessGlobal.h b/dom/base/ProcessGlobal.h index 5c8b0639cd4..3568cbeed3c 100644 --- a/dom/base/ProcessGlobal.h +++ b/dom/base/ProcessGlobal.h @@ -17,7 +17,9 @@ #include "nsIRunnable.h" #include "nsIGlobalObject.h" #include "nsIScriptObjectPrincipal.h" +#include "nsServiceManagerUtils.h" #include "nsWeakReference.h" +#include "nsWrapperCache.h" namespace mozilla { namespace dom { diff --git a/dom/base/ScreenOrientation.cpp b/dom/base/ScreenOrientation.cpp index ca24ca57fab..a1e327e9695 100644 --- a/dom/base/ScreenOrientation.cpp +++ b/dom/base/ScreenOrientation.cpp @@ -4,9 +4,18 @@ #include "ScreenOrientation.h" #include "nsIDeviceSensors.h" +#include "nsIDocShell.h" +#include "nsIDocument.h" +#include "nsGlobalWindow.h" #include "nsSandboxFlags.h" #include "nsScreen.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/Hal.h" +#include "mozilla/Preferences.h" + +#include "mozilla/dom/Promise.h" + using namespace mozilla::dom; NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation, diff --git a/dom/base/nsCopySupport.h b/dom/base/nsCopySupport.h index ea0b85f224e..554ce839868 100644 --- a/dom/base/nsCopySupport.h +++ b/dom/base/nsCopySupport.h @@ -6,6 +6,8 @@ #ifndef nsCopySupport_h__ #define nsCopySupport_h__ +#include "nsError.h" +#include "nsIDocument.h" #include "mozilla/EventForwards.h" class nsINode; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index c2c05e6466d..b5a8c84b10c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -31,6 +31,7 @@ #include "nsICategoryManager.h" #include "nsIComponentRegistrar.h" #include "nsXPCOM.h" +#include "nsISimpleEnumerator.h" #include "nsISupportsPrimitives.h" #include "nsIXPConnect.h" #include "xptcall.h" diff --git a/dom/base/nsDOMDataChannel.cpp b/dom/base/nsDOMDataChannel.cpp index 5bb85bbb003..d86784bc925 100644 --- a/dom/base/nsDOMDataChannel.cpp +++ b/dom/base/nsDOMDataChannel.cpp @@ -213,6 +213,12 @@ nsDOMDataChannel::BufferedAmount() const return mDataChannel->GetBufferedAmount(); } +uint32_t +nsDOMDataChannel::BufferedAmountLowThreshold() const +{ + return mDataChannel->GetBufferedAmountLowThreshold(); +} + NS_IMETHODIMP nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount) { @@ -220,6 +226,12 @@ nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount) return NS_OK; } +void +nsDOMDataChannel::SetBufferedAmountLowThreshold(uint32_t aThreshold) +{ + mDataChannel->SetBufferedAmountLowThreshold(aThreshold); +} + NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType) { switch (mBinaryType) { @@ -468,6 +480,14 @@ nsDOMDataChannel::OnChannelClosed(nsISupports* aContext) return OnSimpleEvent(aContext, NS_LITERAL_STRING("close")); } +nsresult +nsDOMDataChannel::OnBufferLow(nsISupports* aContext) +{ + LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__)); + + return OnSimpleEvent(aContext, NS_LITERAL_STRING("bufferedamountlow")); +} + void nsDOMDataChannel::AppReady() { diff --git a/dom/base/nsDOMDataChannel.h b/dom/base/nsDOMDataChannel.h index ebe2054db5f..7369a15e96e 100644 --- a/dom/base/nsDOMDataChannel.h +++ b/dom/base/nsDOMDataChannel.h @@ -54,11 +54,14 @@ public: bool Reliable() const; mozilla::dom::RTCDataChannelState ReadyState() const; uint32_t BufferedAmount() const; + uint32_t BufferedAmountLowThreshold() const; + void SetBufferedAmountLowThreshold(uint32_t aThreshold); IMPL_EVENT_HANDLER(open) IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(close) // Uses XPIDL Close. IMPL_EVENT_HANDLER(message) + IMPL_EVENT_HANDLER(bufferedamountlow) mozilla::dom::RTCDataChannelType BinaryType() const { return static_cast( @@ -97,6 +100,9 @@ public: virtual nsresult OnChannelClosed(nsISupports* aContext) override; + virtual nsresult + OnBufferLow(nsISupports* aContext) override; + virtual void AppReady(); diff --git a/dom/base/nsDOMFileReader.cpp b/dom/base/nsDOMFileReader.cpp index 0798f99ad3f..4fb394dd518 100644 --- a/dom/base/nsDOMFileReader.cpp +++ b/dom/base/nsDOMFileReader.cpp @@ -12,6 +12,7 @@ #include "nsError.h" #include "nsIFile.h" #include "nsNetCID.h" +#include "nsNetUtil.h" #include "nsXPCOM.h" #include "nsIDOMEventListener.h" diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 16bc5599e9a..4fdef3fd2ce 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -19,6 +19,7 @@ #include "nsTextFragment.h" #include "nsThreadUtils.h" +using mozilla::dom::TreeOrderComparator; using mozilla::dom::Animation; nsAutoTArray, 4>* diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 7251b9891ea..235ef828f4c 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1532,7 +1532,8 @@ nsIDocument::nsIDocument() mGetUserFontSetCalled(false), mPostedFlushUserFontSet(false), mPartID(0), - mDidFireDOMContentLoaded(true) + mDidFireDOMContentLoaded(true), + mUserHasInteracted(false) { SetInDocument(); diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 431d4da4f22..a86813faf1b 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1343,7 +1343,7 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags, nsCOMPtr docShell = newWindow->GetDocShell(); nsCOMPtr presShell = docShell->GetPresShell(); - if (presShell) + if (presShell && presShell->DidInitialize()) ScrollIntoView(presShell, contentToFocus, aFlags); } diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 5ff573ca435..45e8405847d 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -14,6 +14,7 @@ #include "prenv.h" #include "mozIApplication.h" +#include "nsDocShell.h" #include "nsIDOMHTMLIFrameElement.h" #include "nsIDOMHTMLFrameElement.h" #include "nsIDOMMozBrowserFrame.h" diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 21b6b43551a..bb70d58f101 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -699,6 +699,7 @@ GK_ATOM(onblocked, "onblocked") GK_ATOM(onblur, "onblur") GK_ATOM(onbroadcast, "onbroadcast") GK_ATOM(onbusy, "onbusy") +GK_ATOM(onbufferedamountlow, "onbufferedamountlow") GK_ATOM(oncached, "oncached") GK_ATOM(oncallschanged, "oncallschanged") GK_ATOM(oncancel, "oncancel") diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index bc5820c035d..9076c09f4d5 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2602,6 +2602,16 @@ public: void PropagateUseCounters(nsIDocument* aParentDocument); + void SetUserHasInteracted(bool aUserHasInteracted) + { + mUserHasInteracted = aUserHasInteracted; + } + + bool UserHasInteracted() + { + return mUserHasInteracted; + } + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { @@ -3007,6 +3017,9 @@ protected: // Flags for whether we've notified our top-level "page" of a use counter // for this child document. std::bitset mNotifiedPageForUseCounter; + + // Whether the user has interacted with the document or not: + bool mUserHasInteracted; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) diff --git a/dom/base/nsNoDataProtocolContentPolicy.cpp b/dom/base/nsNoDataProtocolContentPolicy.cpp index 02891507acc..cd1484a224a 100644 --- a/dom/base/nsNoDataProtocolContentPolicy.cpp +++ b/dom/base/nsNoDataProtocolContentPolicy.cpp @@ -16,6 +16,7 @@ #include "nsIProtocolHandler.h" #include "nsIIOService.h" #include "nsIExternalProtocolHandler.h" +#include "nsIURI.h" #include "nsNetUtil.h" #include "nsContentUtils.h" diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 26737ec7551..945f618fc4e 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -1473,6 +1473,37 @@ nsObjectLoadingContent::CheckJavaCodebase() return true; } +bool +nsObjectLoadingContent::IsYoutubeEmbed() +{ + nsCOMPtr thisContent = + do_QueryInterface(static_cast(this)); + NS_ASSERTION(thisContent, "Must be an instance of content"); + + // We're only interested in switching out embed tags + if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) { + return false; + } + nsCOMPtr tldService = + do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + // If we can't analyze the URL, just pass on through. + if(!tldService) { + NS_WARNING("Could not get TLD service!"); + return false; + } + nsAutoCString currentBaseDomain; + bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(mURI, 0, currentBaseDomain)); + if (!ok) { + NS_WARNING("Could not parse plugin domain!"); + return false; + } + nsAutoCString domain("youtube.com"); + if (StringEndsWith(domain, currentBaseDomain)) { + return true; + } + return false; +} + bool nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy) { @@ -2122,6 +2153,11 @@ nsObjectLoadingContent::LoadObject(bool aNotify, return NS_OK; } + // Check whether this is a youtube embed. + if (IsYoutubeEmbed()) { + Telemetry::Accumulate(Telemetry::YOUTUBE_EMBED_SEEN, 1); + } + // // Security checks // diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index d12c1fa0167..573adc20a5d 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -519,6 +519,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent */ nsPluginFrame* GetExistingFrame(); + bool IsYoutubeEmbed(); + // Helper class for SetupProtoChain class SetupProtoChainRunner final : public nsIRunnable { diff --git a/dom/base/nsXHTMLContentSerializer.cpp b/dom/base/nsXHTMLContentSerializer.cpp index 296183529cc..4e190b994d6 100644 --- a/dom/base/nsXHTMLContentSerializer.cpp +++ b/dom/base/nsXHTMLContentSerializer.cpp @@ -31,6 +31,7 @@ #include "nsContentUtils.h" #include "nsLWBrkCIID.h" #include "nsIScriptElement.h" +#include "nsStubMutationObserver.h" #include "nsAttrName.h" #include "nsParserConstants.h" #include "nsComputedDOMStyle.h" diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index fe5d3cfe1b3..a28326213aa 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -503,6 +503,8 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, mouseEvent->mMessage != eMouseEnterIntoWidget && mouseEvent->mMessage != eMouseExitFromWidget) || aEvent->mClass == eWheelEventClass || + aEvent->mClass == ePointerEventClass || + aEvent->mClass == eTouchEventClass || aEvent->mClass == eKeyboardEventClass)) { if (gMouseOrKeyboardEventCounter == 0) { nsCOMPtr obs = @@ -513,6 +515,20 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, } } ++gMouseOrKeyboardEventCounter; + + + nsCOMPtr node = do_QueryInterface(aTargetContent); + if (node && + (aEvent->mMessage == eKeyUp || aEvent->mMessage == eMouseUp || + aEvent->mMessage == eWheel || aEvent->mMessage == eTouchEnd || + aEvent->mMessage == ePointerUp)) { + nsIDocument* doc = node->OwnerDoc(); + while (doc && !doc->UserHasInteracted()) { + doc->SetUserHasInteracted(true); + doc = nsContentUtils::IsChildOfSameType(doc) ? + doc->GetParentDocument() : nullptr; + } + } } WheelTransaction::OnEvent(aEvent); diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 8a790fcb33f..460d8d2af46 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1411,7 +1411,9 @@ MediaFormatReader::Seek(int64_t aTime, int64_t aUnused) nsRefPtr p = mSeekPromise.Ensure(__func__); - AttemptSeek(); + RefPtr task( + NS_NewRunnableMethod(this, &MediaFormatReader::AttemptSeek)); + OwnerThread()->Dispatch(task.forget()); return p; } @@ -1420,6 +1422,13 @@ void MediaFormatReader::AttemptSeek() { MOZ_ASSERT(OnTaskQueue()); + if (mPendingSeekTime.isNothing()) { + return; + } + // An internal seek may be pending due to Seek queueing multiple tasks calling + // AttemptSeek ; we can ignore those by resetting any pending demuxer's seek. + mAudio.mSeekRequest.DisconnectIfExists(); + mVideo.mSeekRequest.DisconnectIfExists(); if (HasVideo()) { DoVideoSeek(); } else if (HasAudio()) { diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp index c6eb3331b01..6bada8ebef7 100644 --- a/dom/media/VideoUtils.cpp +++ b/dom/media/VideoUtils.cpp @@ -18,6 +18,9 @@ #include "mozilla/SharedThreadPool.h" #include "nsIRandomGenerator.h" #include "nsIServiceManager.h" +#include "nsServiceManagerUtils.h" +#include "nsIConsoleService.h" +#include "nsThreadUtils.h" #include @@ -369,4 +372,98 @@ CreateFlushableMediaDecodeTaskQueue() return queue.forget(); } +void +SimpleTimer::Cancel() { + if (mTimer) { +#ifdef DEBUG + nsCOMPtr target; + mTimer->GetTarget(getter_AddRefs(target)); + nsCOMPtr thread(do_QueryInterface(target)); + MOZ_ASSERT(NS_GetCurrentThread() == thread); +#endif + mTimer->Cancel(); + mTimer = nullptr; + } + mTask = nullptr; +} + +NS_IMETHODIMP +SimpleTimer::Notify(nsITimer *timer) { + nsRefPtr deathGrip(this); + if (mTask) { + mTask->Run(); + mTask = nullptr; + } + return NS_OK; +} + +nsresult +SimpleTimer::Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget) +{ + nsresult rv; + + // Get target thread first, so we don't have to cancel the timer if it fails. + nsCOMPtr target; + if (aTarget) { + target = aTarget; + } else { + rv = NS_GetMainThread(getter_AddRefs(target)); + if (NS_FAILED(rv)) { + return rv; + } + } + + nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + return rv; + } + // Note: set target before InitWithCallback in case the timer fires before + // we change the event target. + rv = timer->SetTarget(aTarget); + if (NS_FAILED(rv)) { + timer->Cancel(); + return rv; + } + rv = timer->InitWithCallback(this, aTimeoutMs, nsITimer::TYPE_ONE_SHOT); + if (NS_FAILED(rv)) { + return rv; + } + + mTimer = timer.forget(); + mTask = aTask; + return NS_OK; +} + +NS_IMPL_ISUPPORTS(SimpleTimer, nsITimerCallback) + +already_AddRefed +SimpleTimer::Create(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget) +{ + nsRefPtr t(new SimpleTimer()); + if (NS_FAILED(t->Init(aTask, aTimeoutMs, aTarget))) { + return nullptr; + } + return t.forget(); +} + +void +LogToBrowserConsole(const nsAString& aMsg) +{ + if (!NS_IsMainThread()) { + nsAutoString msg(aMsg); + nsCOMPtr task = + NS_NewRunnableFunction([msg]() { LogToBrowserConsole(msg); }); + NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL); + return; + } + nsCOMPtr console( + do_GetService("@mozilla.org/consoleservice;1")); + if (!console) { + NS_WARNING("Failed to log message to console."); + return; + } + nsAutoString msg(aMsg); + console->LogStringMessage(msg.get()); +} + } // end namespace mozilla diff --git a/dom/media/VideoUtils.h b/dom/media/VideoUtils.h index 1371f6b1328..aa1e525887d 100644 --- a/dom/media/VideoUtils.h +++ b/dom/media/VideoUtils.h @@ -22,6 +22,8 @@ #include "prtime.h" #include "AudioSampleFormat.h" #include "TimeUnits.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" using mozilla::CheckedInt64; using mozilla::CheckedUint64; @@ -301,6 +303,32 @@ nsRefPtr InvokeUntil(Work aWork, Condition aCondition) { return p.forget(); } +// Simple timer to run a runnable after a timeout. +class SimpleTimer : public nsITimerCallback +{ +public: + NS_DECL_ISUPPORTS + + // Create a new timer to run aTask after aTimeoutMs milliseconds + // on thread aTarget. If aTarget is null, task is run on the main thread. + static already_AddRefed Create(nsIRunnable* aTask, + uint32_t aTimeoutMs, + nsIThread* aTarget = nullptr); + void Cancel(); + + NS_IMETHOD Notify(nsITimer *timer) override; + +private: + virtual ~SimpleTimer() {} + nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget); + + nsRefPtr mTask; + nsCOMPtr mTimer; +}; + +void +LogToBrowserConsole(const nsAString& aMsg); + } // end namespace mozilla #endif diff --git a/dom/media/eme/EMEUtils.cpp b/dom/media/eme/EMEUtils.cpp index b6f5121d0bf..5d0af6f7179 100644 --- a/dom/media/eme/EMEUtils.cpp +++ b/dom/media/eme/EMEUtils.cpp @@ -5,8 +5,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/EMEUtils.h" -#include "nsServiceManagerUtils.h" -#include "nsIConsoleService.h" namespace mozilla { @@ -110,19 +108,6 @@ ParseKeySystem(const nsAString& aInputKeySystem, return false; } -void -LogToBrowserConsole(const nsAString& aMsg) -{ - nsCOMPtr console( - do_GetService("@mozilla.org/consoleservice;1")); - if (!console) { - NS_WARNING("Failed to log message to console."); - return; - } - nsAutoString msg(aMsg); - console->LogStringMessage(msg.get()); -} - void ConstructKeySystem(const nsAString& aKeySystem, const nsAString& aCDMVersion, diff --git a/dom/media/eme/EMEUtils.h b/dom/media/eme/EMEUtils.h index 6c2fbe2f1ac..a8fde65f853 100644 --- a/dom/media/eme/EMEUtils.h +++ b/dom/media/eme/EMEUtils.h @@ -51,9 +51,6 @@ bool ParseKeySystem(const nsAString& aKeySystem, nsAString& aOutKeySystem, int32_t& aOutMinCDMVersion); -void -LogToBrowserConsole(const nsAString& aMsg); - void ConstructKeySystem(const nsAString& aKeySystem, const nsAString& aCDMVersion, diff --git a/dom/media/gmp/GMPVideoDecoderParent.cpp b/dom/media/gmp/GMPVideoDecoderParent.cpp index 0209ecd4fd5..eacc05b447e 100644 --- a/dom/media/gmp/GMPVideoDecoderParent.cpp +++ b/dom/media/gmp/GMPVideoDecoderParent.cpp @@ -50,6 +50,7 @@ GMPVideoDecoderParent::GMPVideoDecoderParent(GMPContentParent* aPlugin) , mCallback(nullptr) , mVideoHost(this) , mPluginId(aPlugin->GetPluginId()) + , mFrameCount(0) { MOZ_ASSERT(mPlugin); } @@ -157,6 +158,7 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr aInputFrame, aRenderTimeMs)) { return NS_ERROR_FAILURE; } + mFrameCount++; // Async IPC, we don't have access to a return value. return NS_OK; @@ -180,14 +182,33 @@ GMPVideoDecoderParent::Reset() mIsAwaitingResetComplete = true; + nsRefPtr self(this); + nsCOMPtr task = NS_NewRunnableFunction([self]() -> void + { + LOGD(("GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out waiting for ResetComplete", self.get())); + self->mResetCompleteTimeout = nullptr; + LogToBrowserConsole(NS_LITERAL_STRING("GMPVideoDecoderParent timed out waiting for ResetComplete()")); + }); + CancelResetCompleteTimeout(); + mResetCompleteTimeout = SimpleTimer::Create(task, 5000, mPlugin->GMPThread()); + // Async IPC, we don't have access to a return value. return NS_OK; } +void +GMPVideoDecoderParent::CancelResetCompleteTimeout() +{ + if (mResetCompleteTimeout) { + mResetCompleteTimeout->Cancel(); + mResetCompleteTimeout = nullptr; + } +} + nsresult GMPVideoDecoderParent::Drain() { - LOGD(("GMPVideoDecoderParent[%p]::Drain()", this)); + LOGD(("GMPVideoDecoderParent[%p]::Drain() frameCount=%d", this, mFrameCount)); if (!mIsOpen) { NS_WARNING("Trying to use an dead GMP video decoder"); @@ -280,8 +301,9 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) bool GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) { - LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld", - this, aDecodedFrame.mTimestamp())); + --mFrameCount; + LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld frameCount=%d", + this, aDecodedFrame.mTimestamp(), mFrameCount)); if (!mCallback) { return false; @@ -345,8 +367,11 @@ GMPVideoDecoderParent::RecvInputDataExhausted() bool GMPVideoDecoderParent::RecvDrainComplete() { - LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete()", this)); - + LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d", this, mFrameCount)); + nsAutoString msg; + msg.AppendLiteral("GMPVideoDecoderParent::RecvDrainComplete() outstanding frames="); + msg.AppendInt(mFrameCount); + LogToBrowserConsole(msg); if (!mCallback) { return false; } @@ -367,6 +392,8 @@ GMPVideoDecoderParent::RecvResetComplete() { LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this)); + CancelResetCompleteTimeout(); + if (!mCallback) { return false; } @@ -375,6 +402,7 @@ GMPVideoDecoderParent::RecvResetComplete() return true; } mIsAwaitingResetComplete = false; + mFrameCount = 0; // Ignore any return code. It is OK for this to fail without killing the process. mCallback->ResetComplete(); @@ -457,7 +485,9 @@ GMPVideoDecoderParent::Recv__delete__() void GMPVideoDecoderParent::UnblockResetAndDrain() { - LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain()", this)); + LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain() " + "awaitingResetComplete=%d awaitingDrainComplete=%d", + this, mIsAwaitingResetComplete, mIsAwaitingDrainComplete)); if (!mCallback) { MOZ_ASSERT(!mIsAwaitingResetComplete); @@ -472,6 +502,7 @@ GMPVideoDecoderParent::UnblockResetAndDrain() mIsAwaitingDrainComplete = false; mCallback->DrainComplete(); } + CancelResetCompleteTimeout(); } } // namespace gmp diff --git a/dom/media/gmp/GMPVideoDecoderParent.h b/dom/media/gmp/GMPVideoDecoderParent.h index 1f477ed4176..b6b6ad32561 100644 --- a/dom/media/gmp/GMPVideoDecoderParent.h +++ b/dom/media/gmp/GMPVideoDecoderParent.h @@ -14,6 +14,7 @@ #include "GMPUtils.h" #include "GMPVideoHost.h" #include "GMPVideoDecoderProxy.h" +#include "VideoUtils.h" namespace mozilla { namespace gmp { @@ -80,6 +81,7 @@ private: virtual bool Recv__delete__() override; void UnblockResetAndDrain(); + void CancelResetCompleteTimeout(); bool mIsOpen; bool mShuttingDown; @@ -90,6 +92,8 @@ private: GMPVideoDecoderCallbackProxy* mCallback; GMPVideoHostImpl mVideoHost; const uint32_t mPluginId; + int32_t mFrameCount; + nsRefPtr mResetCompleteTimeout; }; } // namespace gmp diff --git a/dom/media/mediasource/MediaSourceDemuxer.cpp b/dom/media/mediasource/MediaSourceDemuxer.cpp index 1fc0aa8ad40..82f592aff34 100644 --- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -309,7 +309,7 @@ MediaSourceTrackDemuxer::Reset() nsRefPtr self = this; nsCOMPtr task = NS_NewRunnableFunction([self] () { - self->mManager->Seek(self->mType, TimeUnit()); + self->mManager->Seek(self->mType, TimeUnit(), TimeUnit()); { MonitorAutoLock mon(self->mMonitor); self->mNextRandomAccessPoint = @@ -364,7 +364,8 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__); } - TimeUnit seekTime = mManager->Seek(mType, aTime); + TimeUnit seekTime = + mManager->Seek(mType, aTime, TimeUnit::FromMicroseconds(EOS_FUZZ_US)); { MonitorAutoLock mon(mMonitor); mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType); diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 16b88f7ffe2..d5f4f61eed3 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -1660,11 +1660,14 @@ TrackBuffersManager::InsertFrames(TrackBuffer& aSamples, } // Adjust our demuxing index if necessary. - if (trackBuffer.mNextGetSampleIndex.isSome() && - (trackBuffer.mNextInsertionIndex.ref() < trackBuffer.mNextGetSampleIndex.ref() || - (trackBuffer.mNextInsertionIndex.ref() == trackBuffer.mNextGetSampleIndex.ref() && - aIntervals.GetEnd() < trackBuffer.mNextSampleTime))) { - trackBuffer.mNextGetSampleIndex.ref() += aSamples.Length(); + if (trackBuffer.mNextGetSampleIndex.isSome()) { + if (trackBuffer.mNextInsertionIndex.ref() == trackBuffer.mNextGetSampleIndex.ref() && + aIntervals.GetEnd() >= trackBuffer.mNextSampleTime) { + MSE_DEBUG("Next sample to be played got overwritten"); + trackBuffer.mNextGetSampleIndex.reset(); + } else if (trackBuffer.mNextInsertionIndex.ref() <= trackBuffer.mNextGetSampleIndex.ref()) { + trackBuffer.mNextGetSampleIndex.ref() += aSamples.Length(); + } } TrackBuffer& data = trackBuffer.mBuffers.LastElement(); @@ -1890,36 +1893,82 @@ TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack) return GetTracksData(aTrack).mBuffers.LastElement(); } +uint32_t TrackBuffersManager::FindSampleIndex(const TrackBuffer& aTrackBuffer, + const TimeInterval& aInterval) +{ + TimeUnit target = aInterval.mStart - aInterval.mFuzz; + + for (uint32_t i = 0; i < aTrackBuffer.Length(); i++) { + const nsRefPtr& sample = aTrackBuffer[i]; + if (sample->mTime >= target.ToMicroseconds() || + sample->GetEndTime() > target.ToMicroseconds()) { + return i; + } + } + NS_ASSERTION(false, "FindSampleIndex called with invalid arguments"); + + return 0; +} + TimeUnit TrackBuffersManager::Seek(TrackInfo::TrackType aTrack, - const TimeUnit& aTime) + const TimeUnit& aTime, + const TimeUnit& aFuzz) { MOZ_ASSERT(OnTaskQueue()); auto& trackBuffer = GetTracksData(aTrack); const TrackBuffersManager::TrackBuffer& track = GetTrackBuffer(aTrack); - TimeUnit lastKeyFrameTime; + + if (!track.Length()) { + // This a reset. It will be followed by another valid seek. + trackBuffer.mNextGetSampleIndex = Some(uint32_t(0)); + trackBuffer.mNextSampleTimecode = TimeUnit(); + trackBuffer.mNextSampleTime = TimeUnit(); + return TimeUnit(); + } + + uint32_t i = 0; + + if (aTime != TimeUnit()) { + // Determine the interval of samples we're attempting to seek to. + TimeIntervals buffered = trackBuffer.mBufferedRanges; + TimeIntervals::IndexType index = buffered.Find(aTime); + buffered.SetFuzz(aFuzz); + index = buffered.Find(aTime); + MOZ_ASSERT(index != TimeIntervals::NoIndex); + + TimeInterval target = buffered[index]; + i = FindSampleIndex(track, target); + } + + Maybe lastKeyFrameTime; TimeUnit lastKeyFrameTimecode; uint32_t lastKeyFrameIndex = 0; - for (uint32_t i = 0; i < track.Length(); i++) { + for (; i < track.Length(); i++) { const nsRefPtr& sample = track[i]; TimeUnit sampleTime = TimeUnit::FromMicroseconds(sample->mTime); - if (sampleTime > aTime) { + if (sampleTime > aTime && lastKeyFrameTime.isSome()) { break; } if (sample->mKeyframe) { lastKeyFrameTimecode = TimeUnit::FromMicroseconds(sample->mTimecode); - lastKeyFrameTime = sampleTime; + lastKeyFrameTime = Some(sampleTime); lastKeyFrameIndex = i; } - if (sampleTime == aTime) { + if (sampleTime == aTime || + (sampleTime > aTime && lastKeyFrameTime.isSome())) { break; } } + MSE_DEBUG("Keyframe %s found at %lld", + lastKeyFrameTime.isSome() ? "" : "not", + lastKeyFrameTime.refOr(TimeUnit()).ToMicroseconds()); + trackBuffer.mNextGetSampleIndex = Some(lastKeyFrameIndex); trackBuffer.mNextSampleTimecode = lastKeyFrameTimecode; - trackBuffer.mNextSampleTime = lastKeyFrameTime; + trackBuffer.mNextSampleTime = lastKeyFrameTime.refOr(TimeUnit()); - return lastKeyFrameTime; + return lastKeyFrameTime.refOr(TimeUnit()); } uint32_t diff --git a/dom/media/mediasource/TrackBuffersManager.h b/dom/media/mediasource/TrackBuffersManager.h index a54db4b5b87..26a18ff3553 100644 --- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -88,7 +88,8 @@ public: return mEnded; } media::TimeUnit Seek(TrackInfo::TrackType aTrack, - const media::TimeUnit& aTime); + const media::TimeUnit& aTime, + const media::TimeUnit& aFuzz); uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack, const media::TimeUnit& aTimeThreadshold, bool& aFound); @@ -262,6 +263,8 @@ private: nsRefPtr mLastInfo; // If set, position of the next sample to be retrieved by GetSample(). + // If the position is equal to the TrackBuffer's length, it indicates that + // we've reached EOS. Maybe mNextGetSampleIndex; // Approximation of the next sample's decode timestamp. media::TimeUnit mNextSampleTimecode; @@ -290,6 +293,9 @@ private: void RemoveFrames(const media::TimeIntervals& aIntervals, TrackData& aTrackData, uint32_t aStartIndex); + // Find index of sample. Return a negative value if not found. + uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer, + const media::TimeInterval& aInterval); void UpdateBufferedRanges(); void RejectProcessing(nsresult aRejectValue, const char* aName); void ResolveProcessing(bool aResolveValue, const char* aName); diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp index f88e1bc7a1d..fc9cc6c4da0 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.cpp +++ b/dom/media/platforms/android/AndroidDecoderModule.cpp @@ -19,7 +19,6 @@ #include "nsPromiseFlatString.h" #include -#include using namespace mozilla; using namespace mozilla::gl; @@ -34,10 +33,21 @@ namespace mozilla { NS_WARNING("callback not set"); \ } +static const char* TranslateMimeType(const nsACString& aMimeType) +{ + if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) { + return "video/x-vnd.on2.vp8"; + } else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) { + return "video/x-vnd.on2.vp9"; + } + return PromiseFlatCString(aMimeType).get(); +} + static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType) { MediaCodec::LocalRef codec; - NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(PromiseFlatCString(aMimeType).get(), &codec), nullptr); + NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(TranslateMimeType(aMimeType), + &codec), nullptr); return codec; } @@ -76,8 +86,10 @@ public: } bool WantCopy() { - // Allocating a texture is incredibly slow on PowerVR - return mGLContext->Vendor() != GLVendor::Imagination; + // Allocating a texture is incredibly slow on PowerVR and may fail on + // emulators, see bug 1190379. + return mGLContext->Vendor() != GLVendor::Imagination && + mGLContext->Renderer() != GLRenderer::AndroidEmulator; } EGLImage CopySurface(layers::Image* img) { @@ -287,7 +299,7 @@ AndroidDecoderModule::CreateVideoDecoder( MediaFormat::LocalRef format; NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat( - aConfig.mMimeType, + TranslateMimeType(aConfig.mMimeType), aConfig.mDisplay.width, aConfig.mDisplay.height, &format), nullptr); diff --git a/dom/media/tests/mochitest/dataChannel.js b/dom/media/tests/mochitest/dataChannel.js index 1714c65619e..f34ad89b73d 100644 --- a/dom/media/tests/mochitest/dataChannel.js +++ b/dom/media/tests/mochitest/dataChannel.js @@ -168,9 +168,24 @@ var commandsCheckDataChannel = [ } ]; +var commandsCheckLargeXfer = [ + function SEND_BIG_BUFFER(test) { + var size = 512*1024; // SCTP internal buffer is 256K, so we'll have ~256K queued + var buffer = new ArrayBuffer(size); + // note: type received is always blob for binary data + var options = {}; + options.bufferedAmountLowThreshold = 64*1024; + return test.send(buffer, options).then(result => { + ok(result.data instanceof Blob, "Received data is of instance Blob"); + is(result.data.size, size, "Received data has the correct size."); + }); + }, +]; + function addInitialDataChannel(chain) { chain.insertBefore('PC_LOCAL_CREATE_OFFER', commandsCreateDataChannel); chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', commandsWaitForDataChannel); chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS'); + chain.append(commandsCheckLargeXfer); chain.append(commandsCheckDataChannel); } diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index c3a6f90c6f5..53ab0b9d3ca 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -214,11 +214,21 @@ PeerConnectionTest.prototype.send = function(data, options) { this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1]; var target = options.targetChannel || this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1]; + var bufferedamount = options.bufferedAmountLowThreshold || 0; + var bufferlow_fired = true; // to make testing later easier + if (bufferedamount != 0) { + source.bufferedAmountLowThreshold = bufferedamount; + bufferlow_fired = false; + source.onbufferedamountlow = function() { + bufferlow_fired = true; + }; + } return new Promise(resolve => { // Register event handler for the target channel - target.onmessage = e => { - resolve({ channel: target, data: e.data }); + target.onmessage = e => { + ok(bufferlow_fired, "bufferedamountlow event fired"); + resolve({ channel: target, data: e.data }); }; source.send(data); @@ -563,6 +573,7 @@ function DataChannelWrapper(dataChannel, peerConnectionWrapper) { createOneShotEventWrapper(this, this._channel, 'close'); createOneShotEventWrapper(this, this._channel, 'error'); createOneShotEventWrapper(this, this._channel, 'message'); + createOneShotEventWrapper(this, this._channel, 'bufferedamountlow'); this.opened = timerGuard(new Promise(resolve => { this._channel.onopen = () => { @@ -640,6 +651,16 @@ DataChannelWrapper.prototype = { return this._channel.readyState; }, + /** + * Sets the bufferlowthreshold of the channel + * + * @param {integer} amoutn + * The new threshold for the chanel + */ + set bufferedAmountLowThreshold(amount) { + this._channel.bufferedAmountLowThreshold = amount; + }, + /** * Close the data channel */ diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index afad673855a..8d9b4157ed2 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -289,7 +289,7 @@ WebMDemuxer::ReadMetadata() return NS_ERROR_FAILURE; } int type = nestegg_track_type(mContext, track); - if (type == NESTEGG_TRACK_VIDEO) { + if (type == NESTEGG_TRACK_VIDEO && !mHasVideo) { nestegg_video_params params; r = nestegg_track_video_params(mContext, track, ¶ms); if (r == -1) { @@ -364,7 +364,7 @@ WebMDemuxer::ReadMetadata() if (!r) { mInfo.mVideo.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds(); } - } else if (type == NESTEGG_TRACK_AUDIO) { + } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) { nestegg_audio_params params; r = nestegg_track_audio_params(mContext, track, ¶ms); if (r == -1) { diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp index d563ba12fa9..3fed7918467 100644 --- a/dom/security/nsContentSecurityManager.cpp +++ b/dom/security/nsContentSecurityManager.cpp @@ -209,12 +209,20 @@ DoContentSecurityChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) } case nsIContentPolicy::TYPE_WEBSOCKET: - case nsIContentPolicy::TYPE_CSP_REPORT: - case nsIContentPolicy::TYPE_XSLT: { + case nsIContentPolicy::TYPE_CSP_REPORT: { MOZ_ASSERT(false, "contentPolicyType not supported yet"); break; } + case nsIContentPolicy::TYPE_XSLT: { + mimeTypeGuess = NS_LITERAL_CSTRING("application/xml"); + requestingContext = aLoadInfo->LoadingNode(); + MOZ_ASSERT(!requestingContext || + requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, + "type_xslt requires requestingContext of type Document"); + break; + } + case nsIContentPolicy::TYPE_BEACON: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); diff --git a/dom/smil/nsSMILKeySpline.h b/dom/smil/nsSMILKeySpline.h index f8b121a9298..36c14fec1fe 100644 --- a/dom/smil/nsSMILKeySpline.h +++ b/dom/smil/nsSMILKeySpline.h @@ -57,6 +57,13 @@ public: bool operator!=(const nsSMILKeySpline& aOther) const { return !(*this == aOther); } + int32_t Compare(const nsSMILKeySpline& aRhs) const { + if (mX1 != aRhs.mX1) return mX1 < aRhs.mX1 ? -1 : 1; + if (mY1 != aRhs.mY1) return mY1 < aRhs.mY1 ? -1 : 1; + if (mX2 != aRhs.mX2) return mX2 < aRhs.mX2 ? -1 : 1; + if (mY2 != aRhs.mY2) return mY2 < aRhs.mY2 ? -1 : 1; + return 0; + } private: void diff --git a/dom/tests/browser/browser_test_new_window_from_content.js b/dom/tests/browser/browser_test_new_window_from_content.js index 2666892d79e..1514369321b 100644 --- a/dom/tests/browser/browser_test_new_window_from_content.js +++ b/dom/tests/browser/browser_test_new_window_from_content.js @@ -49,6 +49,8 @@ const kSameTab = "same tab"; const kNewWin = "new window"; const kNewTab = "new tab"; +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + requestLongerTimeout(2); // The following "matrices" represent the result of content attempting to diff --git a/dom/webidl/DataChannel.webidl b/dom/webidl/DataChannel.webidl index 5599403baa0..db99111d04b 100644 --- a/dom/webidl/DataChannel.webidl +++ b/dom/webidl/DataChannel.webidl @@ -21,11 +21,13 @@ interface DataChannel : EventTarget readonly attribute boolean reliable; readonly attribute RTCDataChannelState readyState; readonly attribute unsigned long bufferedAmount; + attribute unsigned long bufferedAmountLowThreshold; attribute EventHandler onopen; attribute EventHandler onerror; attribute EventHandler onclose; void close(); attribute EventHandler onmessage; + attribute EventHandler onbufferedamountlow; attribute RTCDataChannelType binaryType; [Throws] void send(DOMString data); diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 13454f0b799..4ada749ec1b 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -392,6 +392,12 @@ partial interface Document { void removeAnonymousContent(AnonymousContent aContent); }; +// Extension to give chrome JS the ability to determine whether +// the user has interacted with the document or not. +partial interface Document { + [ChromeOnly] readonly attribute boolean userHasInteracted; +}; + Document implements XPathEvaluator; Document implements GlobalEventHandlers; Document implements TouchEventHandlers; diff --git a/dom/webidl/Keyframe.webidl b/dom/webidl/Keyframe.webidl new file mode 100644 index 00000000000..3dc08d11236 --- /dev/null +++ b/dom/webidl/Keyframe.webidl @@ -0,0 +1,25 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://w3c.github.io/web-animations/#the-compositeoperation-enumeration + * https://w3c.github.io/web-animations/#the-keyframe-dictionary + * https://w3c.github.io/web-animations/#the-computedkeyframe-dictionary + * + * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +enum CompositeOperation { "replace", "add", "accumulate" }; + +dictionary Keyframe { + double? offset = null; + DOMString easing = "linear"; + CompositeOperation? composite = null; +}; + +dictionary ComputedKeyframe : Keyframe { + double computedOffset; +}; diff --git a/dom/webidl/KeyframeEffect.webidl b/dom/webidl/KeyframeEffect.webidl index b3392b9a939..d7c2ef75df1 100644 --- a/dom/webidl/KeyframeEffect.webidl +++ b/dom/webidl/KeyframeEffect.webidl @@ -19,5 +19,8 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly { // readonly attribute CompositeOperation composite; // readonly attribute DOMString spacing; // KeyframeEffect clone(); - // sequence getFrames (); + + // We use object instead of ComputedKeyframe so that we can put the + // property-value pairs on the object. + [Throws] sequence getFrames(); }; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 59af9f16a6e..0477391a4eb 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -274,6 +274,7 @@ WEBIDL_FILES = [ 'KeyAlgorithm.webidl', 'KeyboardEvent.webidl', 'KeyEvent.webidl', + 'Keyframe.webidl', 'KeyframeEffect.webidl', 'KillSwitch.webidl', 'LegacyQueryInterface.webidl', diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 898026a9407..d3298c9a185 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -575,6 +575,13 @@ private: return true; } + NS_IMETHOD Cancel() override + { + // We need to run regardless. + Run(); + return WorkerRunnable::Cancel(); + } + virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) override @@ -1230,6 +1237,13 @@ private: return true; } + + NS_IMETHOD Cancel() override + { + // We need to run regardless. + Run(); + return WorkerRunnable::Cancel(); + } }; class UpdateRuntimeOptionsRunnable final : public WorkerControlRunnable @@ -3823,6 +3837,7 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx, , mRunningExpiredTimeouts(false) , mCloseHandlerStarted(false) , mCloseHandlerFinished(false) + , mPendingEventQueueClearing(false) , mMemoryReporterRunning(false) , mBlockedForMemoryReporter(false) , mCancelAllPendingRunnables(false) @@ -4651,6 +4666,7 @@ WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) AssertIsOnWorkerThread(); MOZ_ASSERT(mChildWorkers.IsEmpty()); MOZ_ASSERT(mSyncLoopStack.IsEmpty()); + MOZ_ASSERT(!mPendingEventQueueClearing); ClearMainEventQueue(aRanOrNot); #ifdef DEBUG @@ -4881,6 +4897,7 @@ WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot) { AssertIsOnWorkerThread(); + MOZ_ASSERT(!mSyncLoopStack.Length()); MOZ_ASSERT(!mCancelAllPendingRunnables); mCancelAllPendingRunnables = true; @@ -5243,6 +5260,11 @@ WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread) MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget))); + if (!mSyncLoopStack.Length() && mPendingEventQueueClearing) { + ClearMainEventQueue(WorkerRan); + mPendingEventQueueClearing = false; + } + return result; } @@ -5509,7 +5531,13 @@ WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) // If this is the first time our status has changed then we need to clear the // main event queue. if (previousStatus == Running) { - ClearMainEventQueue(WorkerRan); + // NB: If we're in a sync loop, we can't clear the queue immediately, + // because this is the wrong queue. So we have to defer it until later. + if (mSyncLoopStack.Length()) { + mPendingEventQueueClearing = true; + } else { + ClearMainEventQueue(WorkerRan); + } } // If we've run the close handler, we don't need to do anything else. diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 9e49c2d4766..d11e97f4540 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -944,6 +944,7 @@ class WorkerPrivate : public WorkerPrivateParent bool mRunningExpiredTimeouts; bool mCloseHandlerStarted; bool mCloseHandlerFinished; + bool mPendingEventQueueClearing; bool mMemoryReporterRunning; bool mBlockedForMemoryReporter; bool mCancelAllPendingRunnables; diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index a3955414c53..a5f8d95e175 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -90,7 +90,8 @@ public: private: nsRefPtr mCompiler; - nsCOMPtr mListener; + nsCOMPtr mListener; + nsCOMPtr mParser; bool mCheckedForXML; protected: @@ -102,8 +103,9 @@ protected: txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler, nsIParser* aParser) - : mCompiler(aCompiler), - mCheckedForXML(false) + : mCompiler(aCompiler) + , mParser(aParser) + , mCheckedForXML(false) { mListener = do_QueryInterface(aParser); } @@ -227,9 +229,8 @@ txStylesheetSink::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, uint64_t aOffset, uint32_t aCount) { if (!mCheckedForXML) { - nsCOMPtr parser = do_QueryInterface(aContext); nsCOMPtr dtd; - parser->GetDTD(getter_AddRefs(dtd)); + mParser->GetDTD(getter_AddRefs(dtd)); if (dtd) { mCheckedForXML = true; if (!(dtd->GetType() & NS_IPARSER_FLAG_XML)) { @@ -244,7 +245,7 @@ txStylesheetSink::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, } } - return mListener->OnDataAvailable(aRequest, aContext, aInputStream, + return mListener->OnDataAvailable(aRequest, mParser, aInputStream, aOffset, aCount); } @@ -268,8 +269,7 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) charset.AssignLiteral("UTF-8"); } - nsCOMPtr parser = do_QueryInterface(aContext); - parser->SetDocumentCharset(charset, charsetSource); + mParser->SetDocumentCharset(charset, charsetSource); nsAutoCString contentType; channel->GetContentType(contentType); @@ -289,7 +289,7 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, "*/*", mListener, - aContext, + mParser, getter_AddRefs(converter)); if (NS_SUCCEEDED(rv)) { mListener = converter; @@ -297,7 +297,7 @@ txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) } } - return mListener->OnStartRequest(aRequest, aContext); + return mListener->OnStartRequest(aRequest, mParser); } NS_IMETHODIMP @@ -319,9 +319,8 @@ txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, result = NS_ERROR_XSLT_NETWORK_ERROR; } else if (!mCheckedForXML) { - nsCOMPtr parser = do_QueryInterface(aContext); nsCOMPtr dtd; - parser->GetDTD(getter_AddRefs(dtd)); + mParser->GetDTD(getter_AddRefs(dtd)); if (dtd && !(dtd->GetType() & NS_IPARSER_FLAG_XML)) { result = NS_ERROR_XSLT_WRONG_MIME_TYPE; } @@ -334,8 +333,9 @@ txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, mCompiler->cancel(result, nullptr, spec.get()); } - nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode); + nsresult rv = mListener->OnStopRequest(aRequest, mParser, aStatusCode); mListener = nullptr; + mParser = nullptr; return rv; } @@ -422,20 +422,6 @@ txCompileObserver::loadURI(const nsAString& aUri, getter_AddRefs(referrerPrincipal)); NS_ENSURE_SUCCESS(rv, rv); - // Content Policy - int16_t shouldLoad = nsIContentPolicy::ACCEPT; - rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET, - uri, - referrerPrincipal, - mLoaderDocument, - NS_LITERAL_CSTRING("application/xml"), - nullptr, - &shouldLoad); - NS_ENSURE_SUCCESS(rv, rv); - if (NS_CP_REJECTED(shouldLoad)) { - return NS_ERROR_DOM_BAD_URI; - } - return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy); } @@ -469,7 +455,7 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, aUri, mLoaderDocument, aReferrerPrincipal, // triggeringPrincipal - nsILoadInfo::SEC_NORMAL, + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, nsIContentPolicy::TYPE_XSLT, loadGroup); @@ -502,13 +488,7 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler, parser->SetContentSink(sink); parser->Parse(aUri); - // Always install in case of redirects - nsRefPtr listener = - new nsCORSListenerProxy(sink, aReferrerPrincipal, false); - rv = listener->Init(channel, DataURIHandling::Disallow); - NS_ENSURE_SUCCESS(rv, rv); - - return channel->AsyncOpen(listener, parser); + return channel->AsyncOpen2(sink); } nsresult @@ -521,21 +501,6 @@ TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, aUri->GetSpec(spec); MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get())); - // Content Policy - int16_t shouldLoad = nsIContentPolicy::ACCEPT; - nsresult rv = - NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET, - aUri, - principal, - aLoaderDocument, - NS_LITERAL_CSTRING("application/xml"), - nullptr, - &shouldLoad); - NS_ENSURE_SUCCESS(rv, rv); - if (NS_CP_REJECTED(shouldLoad)) { - return NS_ERROR_DOM_BAD_URI; - } - nsRefPtr observer = new txCompileObserver(aProcessor, aLoaderDocument); NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY); diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index aefe9a2405e..13b75b2fba9 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -1039,15 +1039,15 @@ GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size, } RefPtr gl; - SurfaceCaps offscreenCaps = minCaps; + SurfaceCaps minOffscreenCaps = minCaps; if (canOffscreenUseHeadless) { gl = CreateHeadless(flags); if (!gl) return nullptr; } else { - SurfaceCaps minBackbufferCaps = minCaps; - if (minCaps.antialias) { + SurfaceCaps minBackbufferCaps = minOffscreenCaps; + if (minOffscreenCaps.antialias) { minBackbufferCaps.antialias = false; minBackbufferCaps.depth = false; minBackbufferCaps.stencil = false; @@ -1057,14 +1057,19 @@ GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size, if (!gl) return nullptr; - offscreenCaps = gl->Caps(); - if (minCaps.antialias) { - offscreenCaps.depth = minCaps.depth; - offscreenCaps.stencil = minCaps.stencil; + // Pull the actual resulting caps to ensure that our offscreen matches our + // backbuffer. + minOffscreenCaps.alpha = gl->Caps().alpha; + if (!minOffscreenCaps.antialias) { + // Only update these if we don't have AA. If we do have AA, we ignore + // backbuffer depth/stencil. + minOffscreenCaps.depth = gl->Caps().depth; + minOffscreenCaps.stencil = gl->Caps().stencil; } } - if (!gl->InitOffscreen(size, offscreenCaps)) + // Init the offscreen with the updated offscreen caps. + if (!gl->InitOffscreen(size, minOffscreenCaps)) return nullptr; return gl.forget(); diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index c4482a9966d..a815d5c114e 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -463,9 +463,11 @@ Layer::SetAnimations(const AnimationArray& aAnimations) NS_ASSERTION(tf.type() == TimingFunction::TStepFunction, "Function must be bezier or step"); StepFunction sf = tf.get_StepFunction(); - nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart - : nsTimingFunction::StepEnd; - ctf->Init(nsTimingFunction(type, sf.steps())); + nsTimingFunction::Type type = sf.type() == 1 ? + nsTimingFunction::Type::StepStart : + nsTimingFunction::Type::StepEnd; + ctf->Init(nsTimingFunction(type, sf.steps(), + nsTimingFunction::Keyword::Explicit)); break; } } @@ -1675,7 +1677,7 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml) DumpSelf(aStream, aPrefix); #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting && AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) { + if (gfxUtils::sDumpCompositorTextures && AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) { AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml); } #endif diff --git a/gfx/layers/apz/src/WheelScrollAnimation.cpp b/gfx/layers/apz/src/WheelScrollAnimation.cpp index f70682626de..d4f857dc779 100644 --- a/gfx/layers/apz/src/WheelScrollAnimation.cpp +++ b/gfx/layers/apz/src/WheelScrollAnimation.cpp @@ -53,6 +53,14 @@ WheelScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom; + if (!IsZero(displacement)) { + // Velocity is measured in ParentLayerCoords / Milliseconds + float xVelocity = displacement.x / aDelta.ToMilliseconds(); + float yVelocity = displacement.y / aDelta.ToMilliseconds(); + mApzc.mX.SetVelocity(xVelocity); + mApzc.mY.SetVelocity(yVelocity); + } + // Note: we ignore overscroll for wheel animations. ParentLayerPoint adjustedOffset, overscroll; mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index 878d105e8f4..50e1e63f3b5 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -89,7 +89,7 @@ CanvasLayerComposite::RenderLayer(const IntRect& aClipRect) mCompositor->MakeCurrent(); #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting) { + if (gfxUtils::sDumpCompositorTextures) { RefPtr surf = mCompositableHost->GetAsSurface(); WriteSnapshotToDumpFile(this, surf); } diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index c099c13edd8..cc04e83cc7c 100755 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -707,7 +707,7 @@ ContainerRender(ContainerT* aContainer, gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().GetBounds()); nsRefPtr compositor = aManager->GetCompositor(); #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting) { + if (gfxUtils::sDumpCompositorTextures) { RefPtr surf = surface->Dump(compositor); if (surf) { WriteSnapshotToDumpFile(aContainer, surf); diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index 13cbb9ce7a1..0a79a91d848 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -87,7 +87,7 @@ ImageLayerComposite::RenderLayer(const IntRect& aClipRect) } #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting) { + if (gfxUtils::sDumpCompositorTextures) { RefPtr surf = mImageHost->GetAsSurface(); WriteSnapshotToDumpFile(this, surf); } diff --git a/gfx/layers/composite/PaintedLayerComposite.cpp b/gfx/layers/composite/PaintedLayerComposite.cpp index 476b88e182e..e1b98020de2 100644 --- a/gfx/layers/composite/PaintedLayerComposite.cpp +++ b/gfx/layers/composite/PaintedLayerComposite.cpp @@ -115,7 +115,7 @@ PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect) const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion(); #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting) { + if (gfxUtils::sDumpCompositorTextures) { RefPtr surf = mBuffer->GetAsSurface(); if (surf) { WriteSnapshotToDumpFile(this, surf); diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 534f74b632a..441a6ca2e6e 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -1415,7 +1415,7 @@ CompositorOGL::EndFrame() MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored"); #ifdef MOZ_DUMP_PAINTING - if (gfxUtils::sDumpPainting) { + if (gfxUtils::sDumpCompositorTextures) { IntRect rect; if (mUseExternalSurfaceSize) { rect = IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height); diff --git a/gfx/thebes/gfx2DGlue.h b/gfx/thebes/gfx2DGlue.h index 2a077635f6e..8b1c32a4c18 100644 --- a/gfx/thebes/gfx2DGlue.h +++ b/gfx/thebes/gfx2DGlue.h @@ -106,21 +106,6 @@ inline ExtendMode ToExtendMode(gfxPattern::GraphicsExtend aExtend) } } -inline gfxPattern::GraphicsPatternType -ThebesPatternType(PatternType aType) -{ - switch (aType) { - case PatternType::SURFACE: - return gfxPattern::PATTERN_SURFACE; - case PatternType::LINEAR_GRADIENT: - return gfxPattern::PATTERN_LINEAR; - case PatternType::RADIAL_GRADIENT: - return gfxPattern::PATTERN_RADIAL; - default: - return gfxPattern::PATTERN_SOLID; - } -} - inline gfxPattern::GraphicsExtend ThebesExtend(ExtendMode aExtend) { switch (aExtend) { diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp index ebb2161c0ea..d253354f656 100644 --- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -1136,7 +1136,7 @@ gfxFT2FontList::FindFonts() gfxFontCache *fc = gfxFontCache::GetCache(); if (fc) fc->AgeAllGenerations(); - mPrefFonts.Clear(); + ClearLangGroupPrefFonts(); mCodepointsWithNoFonts.reset(); mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 547ae441786..03ff72f62a6 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -409,6 +409,7 @@ public: uint32_t fallbackSystem; uint32_t textrunConst; uint32_t textrunDestr; + uint32_t genericLookups; }; uint32_t reflowCount; @@ -442,6 +443,7 @@ public: cumulative.fallbackSystem += current.fallbackSystem; cumulative.textrunConst += current.textrunConst; cumulative.textrunDestr += current.textrunDestr; + cumulative.genericLookups += current.genericLookups; memset(¤t, 0, sizeof(current)); } }; diff --git a/gfx/thebes/gfxFontConstants.h b/gfx/thebes/gfxFontConstants.h index 31d038ba695..1ed31e47d06 100644 --- a/gfx/thebes/gfxFontConstants.h +++ b/gfx/thebes/gfxFontConstants.h @@ -210,4 +210,16 @@ enum { #define NS_FONT_SUB_SUPER_SMALL_SIZE (20.0) #define NS_FONT_SUB_SUPER_LARGE_SIZE (45.0) +// pref lang id's for font prefs +enum eFontPrefLang { + #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_ + #include "gfxFontPrefLangList.h" + #undef FONT_PREF_LANG + + , eFontPrefLang_CJKSet // special code for CJK set + , eFontPrefLang_First = eFontPrefLang_Western + , eFontPrefLang_Last = eFontPrefLang_Others + , eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1) +}; + #endif diff --git a/gfx/thebes/gfxFontFamilyList.h b/gfx/thebes/gfxFontFamilyList.h index 6bd7c47001c..94bc95bc2aa 100644 --- a/gfx/thebes/gfxFontFamilyList.h +++ b/gfx/thebes/gfxFontFamilyList.h @@ -29,7 +29,7 @@ enum FontFamilyType { eFamily_named_quoted, // generics - eFamily_serif, + eFamily_serif, // pref font code relies on this ordering!!! eFamily_sans_serif, eFamily_monospace, eFamily_cursive, @@ -37,7 +37,11 @@ enum FontFamilyType { // special eFamily_moz_variable, - eFamily_moz_fixed + eFamily_moz_fixed, + + eFamily_generic_first = eFamily_serif, + eFamily_generic_last = eFamily_fantasy, + eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1) }; enum QuotedName { eQuotedName, eUnquotedName }; diff --git a/gfx/thebes/gfxFontconfigFonts.cpp b/gfx/thebes/gfxFontconfigFonts.cpp index 8e10548b2b4..f1ea2f0bdb8 100644 --- a/gfx/thebes/gfxFontconfigFonts.cpp +++ b/gfx/thebes/gfxFontconfigFonts.cpp @@ -25,6 +25,9 @@ #include "gfxFontconfigUtils.h" #include "gfxUserFontSet.h" #include "gfxFontConstants.h" +#include "nsGkAtoms.h" +#include "nsILanguageAtomService.h" +#include "nsServiceManagerUtils.h" #include #include @@ -1281,9 +1284,140 @@ gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle) } void -gfxPangoFontGroup::FindPlatformFont(const nsAString& fontName, - bool aUseFontSet, - void *aClosure) +gfxPangoFontGroup::FindGenericFontsPFG(FontFamilyType aGenericType, + nsIAtom *aLanguage, + void *aClosure) +{ + nsAutoTArray resolvedGenerics; + ResolveGenericFontNamesPFG(aGenericType, aLanguage, resolvedGenerics); + uint32_t g = 0, numGenerics = resolvedGenerics.Length(); + for (g = 0; g < numGenerics; g++) { + FindPlatformFontPFG(resolvedGenerics[g], false, aClosure); + } +} + +/* static */ void +gfxPangoFontGroup::ResolveGenericFontNamesPFG(FontFamilyType aGenericType, + nsIAtom *aLanguage, + nsTArray& aGenericFamilies) +{ + static const char kGeneric_serif[] = "serif"; + static const char kGeneric_sans_serif[] = "sans-serif"; + static const char kGeneric_monospace[] = "monospace"; + static const char kGeneric_cursive[] = "cursive"; + static const char kGeneric_fantasy[] = "fantasy"; + + // treat -moz-fixed as monospace + if (aGenericType == eFamily_moz_fixed) { + aGenericType = eFamily_monospace; + } + + // type should be standard generic type at this point + NS_ASSERTION(aGenericType >= eFamily_serif && + aGenericType <= eFamily_fantasy, + "standard generic font family type required"); + + // create the lang string + nsIAtom *langGroupAtom = nullptr; + nsAutoCString langGroupString; + if (aLanguage) { + if (!gLangService) { + CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService); + } + if (gLangService) { + nsresult rv; + langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv); + } + } + if (!langGroupAtom) { + langGroupAtom = nsGkAtoms::Unicode; + } + langGroupAtom->ToUTF8String(langGroupString); + + // map generic type to string + const char *generic = nullptr; + switch (aGenericType) { + case eFamily_serif: + generic = kGeneric_serif; + break; + case eFamily_sans_serif: + generic = kGeneric_sans_serif; + break; + case eFamily_monospace: + generic = kGeneric_monospace; + break; + case eFamily_cursive: + generic = kGeneric_cursive; + break; + case eFamily_fantasy: + generic = kGeneric_fantasy; + break; + default: + break; + } + + if (!generic) { + return; + } + + aGenericFamilies.Clear(); + + // load family for "font.name.generic.lang" + nsAutoCString prefFontName("font.name."); + prefFontName.Append(generic); + prefFontName.Append('.'); + prefFontName.Append(langGroupString); + gfxFontUtils::AppendPrefsFontList(prefFontName.get(), + aGenericFamilies); + + // if lang has pref fonts, also load fonts for "font.name-list.generic.lang" + if (!aGenericFamilies.IsEmpty()) { + nsAutoCString prefFontListName("font.name-list."); + prefFontListName.Append(generic); + prefFontListName.Append('.'); + prefFontListName.Append(langGroupString); + gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), + aGenericFamilies); + } + +#if 0 // dump out generic mappings + printf("%s ===> ", prefFontName.get()); + for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) { + if (k > 0) printf(", "); + printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get()); + } + printf("\n"); +#endif +} + +void gfxPangoFontGroup::EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure) +{ + // initialize fonts in the font family list + const nsTArray& fontlist = mFamilyList.GetFontlist(); + + // lookup fonts in the fontlist + uint32_t i, numFonts = fontlist.Length(); + for (i = 0; i < numFonts; i++) { + const FontFamilyName& name = fontlist[i]; + if (name.IsNamed()) { + FindPlatformFontPFG(name.mName, true, aClosure); + } else { + FindGenericFontsPFG(name.mType, aLanguage, aClosure); + } + } + + // if necessary, append default generic onto the end + if (mFamilyList.GetDefaultFontType() != eFamily_none && + !mFamilyList.HasDefaultGeneric()) { + FindGenericFontsPFG(mFamilyList.GetDefaultFontType(), + aLanguage, aClosure); + } +} + +void +gfxPangoFontGroup::FindPlatformFontPFG(const nsAString& fontName, + bool aUseFontSet, + void *aClosure) { nsTArray *list = static_cast*>(aClosure); @@ -1360,8 +1494,8 @@ gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor, } nsAutoTArray fcFamilyList; - EnumerateFontList(langGroup ? langGroup.get() : mStyle.language.get(), - &fcFamilyList); + EnumerateFontListPFG(langGroup ? langGroup.get() : mStyle.language.get(), + &fcFamilyList); // To consider: A fontset cache here could be helpful. diff --git a/gfx/thebes/gfxFontconfigFonts.h b/gfx/thebes/gfxFontconfigFonts.h index 44d9a7c2487..1a7c1dbf3b4 100644 --- a/gfx/thebes/gfxFontconfigFonts.h +++ b/gfx/thebes/gfxFontconfigFonts.h @@ -94,9 +94,27 @@ private: return mSizeAdjustFactor; } - virtual void FindPlatformFont(const nsAString& aName, - bool aUseFontSet, - void *aClosure); + // old helper methods from gfxFontGroup, moved here so that those methods + // can be revamped without affecting the legacy code here + + // iterate over the fontlist, lookup names and expand generics + void EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure); + + // expand a generic to a list of specific names based on prefs + void FindGenericFontsPFG(mozilla::FontFamilyType aGenericType, + nsIAtom *aLanguage, + void *aClosure); + + // lookup and add a font with a given name (i.e. *not* a generic!) + void FindPlatformFontPFG(const nsAString& aName, + bool aUseFontSet, + void *aClosure); + + static void + ResolveGenericFontNamesPFG(mozilla::FontFamilyType aGenericType, + nsIAtom *aLanguage, + nsTArray& aGenericFamilies); + friend class gfxSystemFcFontEntry; static FT_Library GetFTLibrary(); diff --git a/gfx/thebes/gfxPattern.cpp b/gfx/thebes/gfxPattern.cpp index 3a0dce873de..f83aff7d359 100644 --- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -219,12 +219,6 @@ gfxPattern::GetSolidColor(Color& aColorOut) return false; } -gfxPattern::GraphicsPatternType -gfxPattern::GetType() const -{ - return ThebesPatternType(mGfxPattern.GetPattern()->GetType()); -} - int gfxPattern::CairoStatus() { diff --git a/gfx/thebes/gfxPattern.h b/gfx/thebes/gfxPattern.h index 875efa436b3..04ed39f6140 100644 --- a/gfx/thebes/gfxPattern.h +++ b/gfx/thebes/gfxPattern.h @@ -74,15 +74,6 @@ public: void SetExtend(GraphicsExtend extend); GraphicsExtend Extend() const; - enum GraphicsPatternType { - PATTERN_SOLID, - PATTERN_SURFACE, - PATTERN_LINEAR, - PATTERN_RADIAL - }; - - GraphicsPatternType GetType() const; - int CairoStatus(); void SetFilter(GraphicsFilter filter); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index c40266dca10..112a521f7a8 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -368,27 +368,6 @@ MemoryPressureObserver::Observe(nsISupports *aSubject, return NS_OK; } -// xxx - this can probably be eliminated by reworking pref font handling code -static const char *gPrefLangNames[] = { - #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_ - #include "gfxFontPrefLangList.h" - #undef FONT_PREF_LANG -}; - -static nsIAtom* PrefLangToLangGroups(uint32_t aIndex) -{ - // static array here avoids static constructor - static nsIAtom* gPrefLangToLangGroups[] = { - #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_ - #include "gfxFontPrefLangList.h" - #undef FONT_PREF_LANG - }; - - return aIndex < ArrayLength(gPrefLangToLangGroups) - ? gPrefLangToLangGroups[aIndex] - : nsGkAtoms::Unicode; -} - gfxPlatform::gfxPlatform() : mTileWidth(-1) , mTileHeight(-1) @@ -1361,160 +1340,6 @@ gfxPlatform::MakePlatformFont(const nsAString& aFontName, return nullptr; } -bool gfxPlatform::ForEachPrefFont(eFontPrefLang aLangArray[], uint32_t aLangArrayLen, PrefFontCallback aCallback, - void *aClosure) -{ - NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); - - uint32_t i; - for (i = 0; i < aLangArrayLen; i++) { - eFontPrefLang prefLang = aLangArray[i]; - const char *langGroup = GetPrefLangName(prefLang); - - nsAutoCString prefName; - - prefName.AssignLiteral("font.default."); - prefName.Append(langGroup); - nsAdoptingCString genericDotLang = Preferences::GetCString(prefName.get()); - - genericDotLang.Append('.'); - genericDotLang.Append(langGroup); - - // fetch font.name.xxx value - prefName.AssignLiteral("font.name."); - prefName.Append(genericDotLang); - nsAdoptingCString nameValue = Preferences::GetCString(prefName.get()); - if (nameValue) { - if (!aCallback(prefLang, NS_ConvertUTF8toUTF16(nameValue), aClosure)) - return false; - } - - // fetch font.name-list.xxx value - prefName.AssignLiteral("font.name-list."); - prefName.Append(genericDotLang); - nsAdoptingCString nameListValue = Preferences::GetCString(prefName.get()); - if (nameListValue && !nameListValue.Equals(nameValue)) { - const char kComma = ','; - const char *p, *p_end; - nsAutoCString list(nameListValue); - list.BeginReading(p); - list.EndReading(p_end); - while (p < p_end) { - while (nsCRT::IsAsciiSpace(*p)) { - if (++p == p_end) - break; - } - if (p == p_end) - break; - const char *start = p; - while (++p != p_end && *p != kComma) - /* nothing */ ; - nsAutoCString fontName(Substring(start, p)); - fontName.CompressWhitespace(false, true); - if (!aCallback(prefLang, NS_ConvertUTF8toUTF16(fontName), aClosure)) - return false; - p++; - } - } - } - - return true; -} - -eFontPrefLang -gfxPlatform::GetFontPrefLangFor(const char* aLang) -{ - if (!aLang || !aLang[0]) { - return eFontPrefLang_Others; - } - for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) { - if (!PL_strcasecmp(gPrefLangNames[i], aLang)) { - return eFontPrefLang(i); - } - } - return eFontPrefLang_Others; -} - -eFontPrefLang -gfxPlatform::GetFontPrefLangFor(nsIAtom *aLang) -{ - if (!aLang) - return eFontPrefLang_Others; - nsAutoCString lang; - aLang->ToUTF8String(lang); - return GetFontPrefLangFor(lang.get()); -} - -nsIAtom* -gfxPlatform::GetLangGroupForPrefLang(eFontPrefLang aLang) -{ - // the special CJK set pref lang should be resolved into separate - // calls to individual CJK pref langs before getting here - NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang"); - - return PrefLangToLangGroups(uint32_t(aLang)); -} - -const char* -gfxPlatform::GetPrefLangName(eFontPrefLang aLang) -{ - if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) { - return gPrefLangNames[uint32_t(aLang)]; - } - return nullptr; -} - -eFontPrefLang -gfxPlatform::GetFontPrefLangFor(uint8_t aUnicodeRange) -{ - switch (aUnicodeRange) { - case kRangeSetLatin: return eFontPrefLang_Western; - case kRangeCyrillic: return eFontPrefLang_Cyrillic; - case kRangeGreek: return eFontPrefLang_Greek; - case kRangeHebrew: return eFontPrefLang_Hebrew; - case kRangeArabic: return eFontPrefLang_Arabic; - case kRangeThai: return eFontPrefLang_Thai; - case kRangeKorean: return eFontPrefLang_Korean; - case kRangeJapanese: return eFontPrefLang_Japanese; - case kRangeSChinese: return eFontPrefLang_ChineseCN; - case kRangeTChinese: return eFontPrefLang_ChineseTW; - case kRangeDevanagari: return eFontPrefLang_Devanagari; - case kRangeTamil: return eFontPrefLang_Tamil; - case kRangeArmenian: return eFontPrefLang_Armenian; - case kRangeBengali: return eFontPrefLang_Bengali; - case kRangeCanadian: return eFontPrefLang_Canadian; - case kRangeEthiopic: return eFontPrefLang_Ethiopic; - case kRangeGeorgian: return eFontPrefLang_Georgian; - case kRangeGujarati: return eFontPrefLang_Gujarati; - case kRangeGurmukhi: return eFontPrefLang_Gurmukhi; - case kRangeKhmer: return eFontPrefLang_Khmer; - case kRangeMalayalam: return eFontPrefLang_Malayalam; - case kRangeOriya: return eFontPrefLang_Oriya; - case kRangeTelugu: return eFontPrefLang_Telugu; - case kRangeKannada: return eFontPrefLang_Kannada; - case kRangeSinhala: return eFontPrefLang_Sinhala; - case kRangeTibetan: return eFontPrefLang_Tibetan; - case kRangeSetCJK: return eFontPrefLang_CJKSet; - default: return eFontPrefLang_Others; - } -} - -bool -gfxPlatform::IsLangCJK(eFontPrefLang aLang) -{ - switch (aLang) { - case eFontPrefLang_Japanese: - case eFontPrefLang_ChineseTW: - case eFontPrefLang_ChineseCN: - case eFontPrefLang_ChineseHK: - case eFontPrefLang_Korean: - case eFontPrefLang_CJKSet: - return true; - default: - return false; - } -} - mozilla::layers::DiagnosticTypes gfxPlatform::GetLayerDiagnosticTypes() { @@ -1534,143 +1359,6 @@ gfxPlatform::GetLayerDiagnosticTypes() return type; } -void -gfxPlatform::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) -{ - if (IsLangCJK(aCharLang)) { - AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang); - } else { - AppendPrefLang(aPrefLangs, aLen, aCharLang); - } - - AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others); -} - -void -gfxPlatform::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) -{ - // prefer the lang specified by the page *if* CJK - if (IsLangCJK(aPageLang)) { - AppendPrefLang(aPrefLangs, aLen, aPageLang); - } - - // if not set up, set up the default CJK order, based on accept lang settings and locale - if (mCJKPrefLangs.Length() == 0) { - - // temp array - eFontPrefLang tempPrefLangs[kMaxLenPrefLangList]; - uint32_t tempLen = 0; - - // Add the CJK pref fonts from accept languages, the order should be same order - nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages"); - if (!list.IsEmpty()) { - const char kComma = ','; - const char *p, *p_end; - list.BeginReading(p); - list.EndReading(p_end); - while (p < p_end) { - while (nsCRT::IsAsciiSpace(*p)) { - if (++p == p_end) - break; - } - if (p == p_end) - break; - const char *start = p; - while (++p != p_end && *p != kComma) - /* nothing */ ; - nsAutoCString lang(Substring(start, p)); - lang.CompressWhitespace(false, true); - eFontPrefLang fpl = gfxPlatform::GetFontPrefLangFor(lang.get()); - switch (fpl) { - case eFontPrefLang_Japanese: - case eFontPrefLang_Korean: - case eFontPrefLang_ChineseCN: - case eFontPrefLang_ChineseHK: - case eFontPrefLang_ChineseTW: - AppendPrefLang(tempPrefLangs, tempLen, fpl); - break; - default: - break; - } - p++; - } - } - - do { // to allow 'break' to abort this block if a call fails - nsresult rv; - nsCOMPtr ls = - do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); - if (NS_FAILED(rv)) - break; - - nsCOMPtr appLocale; - rv = ls->GetApplicationLocale(getter_AddRefs(appLocale)); - if (NS_FAILED(rv)) - break; - - nsString localeStr; - rv = appLocale-> - GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr); - if (NS_FAILED(rv)) - break; - - const nsAString& lang = Substring(localeStr, 0, 2); - if (lang.EqualsLiteral("ja")) { - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); - } else if (lang.EqualsLiteral("zh")) { - const nsAString& region = Substring(localeStr, 3, 2); - if (region.EqualsLiteral("CN")) { - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); - } else if (region.EqualsLiteral("TW")) { - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); - } else if (region.EqualsLiteral("HK")) { - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); - } - } else if (lang.EqualsLiteral("ko")) { - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); - } - } while (0); - - // last resort... (the order is same as old gfx.) - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); - AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); - - // copy into the cached array - uint32_t j; - for (j = 0; j < tempLen; j++) { - mCJKPrefLangs.AppendElement(tempPrefLangs[j]); - } - } - - // append in cached CJK langs - uint32_t i, numCJKlangs = mCJKPrefLangs.Length(); - - for (i = 0; i < numCJKlangs; i++) { - AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i])); - } - -} - -void -gfxPlatform::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang) -{ - if (aLen >= kMaxLenPrefLangList) return; - - // make sure - uint32_t i = 0; - while (i < aLen && aPrefLangs[i] != aAddLang) { - i++; - } - - if (i == aLen) { - aPrefLangs[aLen] = aAddLang; - aLen++; - } -} - void gfxPlatform::InitBackendPrefs(uint32_t aCanvasBitmask, BackendType aCanvasDefault, uint32_t aContentBitmask, BackendType aContentDefault) diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index b3ec1352263..bc4a5b25d98 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -70,15 +70,6 @@ BackendTypeBit(BackendType b) extern cairo_user_data_key_t kDrawTarget; -// pref lang id's for font prefs -enum eFontPrefLang { - #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_ - #include "gfxFontPrefLangList.h" - #undef FONT_PREF_LANG - - , eFontPrefLang_CJKSet // special code for CJK set -}; - enum eCMSMode { eCMSMode_Off = 0, // No color management eCMSMode_All = 1, // Color manage everything @@ -427,41 +418,6 @@ public: virtual bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) { return false; } - // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs - void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang); - - /** - * Iterate over pref fonts given a list of lang groups. For a single lang - * group, multiple pref fonts are possible. If error occurs, returns false, - * true otherwise. Callback returns false to abort process. - */ - typedef bool (*PrefFontCallback) (eFontPrefLang aLang, const nsAString& aName, - void *aClosure); - static bool ForEachPrefFont(eFontPrefLang aLangArray[], uint32_t aLangArrayLen, - PrefFontCallback aCallback, - void *aClosure); - - // convert a lang group to enum constant (i.e. "zh-TW" ==> eFontPrefLang_ChineseTW) - static eFontPrefLang GetFontPrefLangFor(const char* aLang); - - // convert a lang group atom to enum constant - static eFontPrefLang GetFontPrefLangFor(nsIAtom *aLang); - - // convert an enum constant to a lang group atom - static nsIAtom* GetLangGroupForPrefLang(eFontPrefLang aLang); - - // convert a enum constant to lang group string (i.e. eFontPrefLang_ChineseTW ==> "zh-TW") - static const char* GetPrefLangName(eFontPrefLang aLang); - - // map a Unicode range (based on char code) to a font language for Preferences - static eFontPrefLang GetFontPrefLangFor(uint8_t aUnicodeRange); - - // returns true if a pref lang is CJK - static bool IsLangCJK(eFontPrefLang aLang); - - // helper method to add a pref lang to an array, if not already in array - static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang); - // returns a list of commonly used fonts for a given character // these are *possible* matches, no cmap-checking is done at this level virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, uint32_t /*aNextCh*/, @@ -667,9 +623,6 @@ protected: gfxPlatform(); virtual ~gfxPlatform(); - void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, - eFontPrefLang aCharLang, eFontPrefLang aPageLang); - /** * Initialized hardware vsync based on each platform. */ @@ -784,7 +737,6 @@ private: void PopulateScreenInfo(); nsRefPtr mScreenReferenceSurface; - nsTArray mCJKPrefLangs; nsCOMPtr mSRGBOverrideObserver; nsCOMPtr mFontPrefsObserver; nsCOMPtr mMemoryPressureObserver; diff --git a/gfx/thebes/gfxPlatformFontList.cpp b/gfx/thebes/gfxPlatformFontList.cpp index 84ddaddef28..95e10665d2f 100644 --- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -9,6 +9,10 @@ #include "gfxTextRun.h" #include "gfxUserFontSet.h" +#include "nsCRT.h" +#include "nsGkAtoms.h" +#include "nsILocaleService.h" +#include "nsServiceManagerUtils.h" #include "nsUnicharUtils.h" #include "nsUnicodeRange.h" #include "nsUnicodeProperties.h" @@ -94,6 +98,16 @@ static const char* kObservedPrefs[] = { nullptr }; +// xxx - this can probably be eliminated by reworking pref font handling code +static const char *gPrefLangNames[] = { + #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_ + #include "gfxFontPrefLangList.h" + #undef FONT_PREF_LANG +}; + +static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count), + "size of pref lang name array doesn't match pref lang enum size"); + class gfxFontListPrefObserver final : public nsIObserver { ~gfxFontListPrefObserver() {} public: @@ -113,7 +127,7 @@ gfxFontListPrefObserver::Observe(nsISupports *aSubject, NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic"); // XXX this could be made to only clear out the cache for the prefs that were changed // but it probably isn't that big a deal. - gfxPlatformFontList::PlatformFontList()->ClearPrefFonts(); + gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts(); gfxFontCache::GetCache()->AgeAllGenerations(); return NS_OK; } @@ -162,7 +176,7 @@ gfxPlatformFontList::MemoryReporter::CollectReports( gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames) : mFontFamilies(64), mOtherFamilyNames(16), - mPrefFonts(8), mBadUnderlineFamilyNames(8), mSharedCmaps(8), + mBadUnderlineFamilyNames(8), mSharedCmaps(8), mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0) { mOtherFamilyNamesInitialized = false; @@ -187,11 +201,15 @@ gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames) gfxPlatformFontList::~gfxPlatformFontList() { mSharedCmaps.Clear(); + ClearLangGroupPrefFonts(); NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer"); Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs); NS_RELEASE(gFontListPrefObserver); } +// number of CSS generic font families +const uint32_t kNumGenerics = 5; + nsresult gfxPlatformFontList::InitFontList() { @@ -216,7 +234,7 @@ gfxPlatformFontList::InitFontList() mExtraNames->mPostscriptNames.Clear(); } mFaceNameListsInitialized = false; - mPrefFonts.Clear(); + ClearLangGroupPrefFonts(); mReplacementCharFallbackFamily = nullptr; CancelLoader(); @@ -703,18 +721,6 @@ gfxPlatformFontList::FindFontForFamily(const nsAString& aFamily, const gfxFontSt return nullptr; } -bool -gfxPlatformFontList::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray > *array) -{ - return mPrefFonts.Get(uint32_t(aLangGroup), array); -} - -void -gfxPlatformFontList::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray >& array) -{ - mPrefFonts.Put(uint32_t(aLangGroup), array); -} - void gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName) { @@ -801,6 +807,419 @@ gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap) } } +void +gfxPlatformFontList::ResolveGenericFontNames( + FontFamilyType aGenericType, + eFontPrefLang aPrefLang, + nsTArray>* aGenericFamilies +) +{ + const char* langGroupStr = GetPrefLangName(aPrefLang); + + static const char kGeneric_serif[] = "serif"; + static const char kGeneric_sans_serif[] = "sans-serif"; + static const char kGeneric_monospace[] = "monospace"; + static const char kGeneric_cursive[] = "cursive"; + static const char kGeneric_fantasy[] = "fantasy"; + + // type should be standard generic type at this point + NS_ASSERTION(aGenericType >= eFamily_serif && + aGenericType <= eFamily_fantasy, + "standard generic font family type required"); + + // map generic type to string + const char *generic = nullptr; + switch (aGenericType) { + case eFamily_serif: + generic = kGeneric_serif; + break; + case eFamily_sans_serif: + generic = kGeneric_sans_serif; + break; + case eFamily_monospace: + generic = kGeneric_monospace; + break; + case eFamily_cursive: + generic = kGeneric_cursive; + break; + case eFamily_fantasy: + generic = kGeneric_fantasy; + break; + default: + break; + } + + if (!generic) { + return; + } + + nsAutoTArray genericFamilies; + + // load family for "font.name.generic.lang" + nsAutoCString prefFontName("font.name."); + prefFontName.Append(generic); + prefFontName.Append('.'); + prefFontName.Append(langGroupStr); + gfxFontUtils::AppendPrefsFontList(prefFontName.get(), genericFamilies); + + // load fonts for "font.name-list.generic.lang" + nsAutoCString prefFontListName("font.name-list."); + prefFontListName.Append(generic); + prefFontListName.Append('.'); + prefFontListName.Append(langGroupStr); + gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies); + + nsIAtom* langGroup = GetLangGroupForPrefLang(aPrefLang); + NS_ASSERTION(langGroup, "null lang group for pref lang"); + + // lookup and add platform fonts uniquely + for (const nsString& genericFamily : genericFamilies) { + nsRefPtr family = + FindFamily(genericFamily, langGroup, false); + if (family) { + bool notFound = true; + for (const gfxFontFamily* f : *aGenericFamilies) { + if (f == family) { + notFound = false; + break; + } + } + if (notFound) { + aGenericFamilies->AppendElement(family); + } + } + } + +#if 0 // dump out generic mappings + printf("%s ===> ", prefFontName.get()); + for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) { + if (k > 0) printf(", "); + printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get()); + } + printf("\n"); +#endif +} + +nsTArray>* +gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType, + eFontPrefLang aPrefLang) +{ + // treat -moz-fixed as monospace + if (aGenericType == eFamily_moz_fixed) { + aGenericType = eFamily_monospace; + } + + PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][aGenericType]; + if (MOZ_UNLIKELY(!prefFonts)) { + prefFonts = new PrefFontList; + ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts); + mLangGroupPrefFonts[aPrefLang][aGenericType] = prefFonts; + } + return prefFonts; +} + +void +gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType, + nsIAtom* aLanguage, + nsTArray& aFamilyList) +{ + // map lang ==> langGroup + nsIAtom *langGroup = nullptr; + if (aLanguage) { + if (!mLangService) { + mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID); + } + if (mLangService) { + nsresult rv; + langGroup = mLangService->GetLanguageGroup(aLanguage, &rv); + } + } + if (!langGroup) { + langGroup = nsGkAtoms::Unicode; + } + + // langGroup ==> prefLang + eFontPrefLang prefLang = GetFontPrefLangFor(langGroup); + + // lookup pref fonts + nsTArray>* prefFonts = + GetPrefFontsLangGroup(aGenericType, prefLang); + + if (!prefFonts->IsEmpty()) { + aFamilyList.AppendElements(*prefFonts); + } +} + +static nsIAtom* PrefLangToLangGroups(uint32_t aIndex) +{ + // static array here avoids static constructor + static nsIAtom* gPrefLangToLangGroups[] = { + #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_ + #include "gfxFontPrefLangList.h" + #undef FONT_PREF_LANG + }; + + return aIndex < ArrayLength(gPrefLangToLangGroups) + ? gPrefLangToLangGroups[aIndex] + : nsGkAtoms::Unicode; +} + +eFontPrefLang +gfxPlatformFontList::GetFontPrefLangFor(const char* aLang) +{ + if (!aLang || !aLang[0]) { + return eFontPrefLang_Others; + } + for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) { + if (!PL_strcasecmp(gPrefLangNames[i], aLang)) { + return eFontPrefLang(i); + } + } + return eFontPrefLang_Others; +} + +eFontPrefLang +gfxPlatformFontList::GetFontPrefLangFor(nsIAtom *aLang) +{ + if (!aLang) + return eFontPrefLang_Others; + nsAutoCString lang; + aLang->ToUTF8String(lang); + return GetFontPrefLangFor(lang.get()); +} + +nsIAtom* +gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang) +{ + // the special CJK set pref lang should be resolved into separate + // calls to individual CJK pref langs before getting here + NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang"); + + return PrefLangToLangGroups(uint32_t(aLang)); +} + +const char* +gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang) +{ + if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) { + return gPrefLangNames[uint32_t(aLang)]; + } + return nullptr; +} + +eFontPrefLang +gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange) +{ + switch (aUnicodeRange) { + case kRangeSetLatin: return eFontPrefLang_Western; + case kRangeCyrillic: return eFontPrefLang_Cyrillic; + case kRangeGreek: return eFontPrefLang_Greek; + case kRangeHebrew: return eFontPrefLang_Hebrew; + case kRangeArabic: return eFontPrefLang_Arabic; + case kRangeThai: return eFontPrefLang_Thai; + case kRangeKorean: return eFontPrefLang_Korean; + case kRangeJapanese: return eFontPrefLang_Japanese; + case kRangeSChinese: return eFontPrefLang_ChineseCN; + case kRangeTChinese: return eFontPrefLang_ChineseTW; + case kRangeDevanagari: return eFontPrefLang_Devanagari; + case kRangeTamil: return eFontPrefLang_Tamil; + case kRangeArmenian: return eFontPrefLang_Armenian; + case kRangeBengali: return eFontPrefLang_Bengali; + case kRangeCanadian: return eFontPrefLang_Canadian; + case kRangeEthiopic: return eFontPrefLang_Ethiopic; + case kRangeGeorgian: return eFontPrefLang_Georgian; + case kRangeGujarati: return eFontPrefLang_Gujarati; + case kRangeGurmukhi: return eFontPrefLang_Gurmukhi; + case kRangeKhmer: return eFontPrefLang_Khmer; + case kRangeMalayalam: return eFontPrefLang_Malayalam; + case kRangeOriya: return eFontPrefLang_Oriya; + case kRangeTelugu: return eFontPrefLang_Telugu; + case kRangeKannada: return eFontPrefLang_Kannada; + case kRangeSinhala: return eFontPrefLang_Sinhala; + case kRangeTibetan: return eFontPrefLang_Tibetan; + case kRangeSetCJK: return eFontPrefLang_CJKSet; + default: return eFontPrefLang_Others; + } +} + +bool +gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang) +{ + switch (aLang) { + case eFontPrefLang_Japanese: + case eFontPrefLang_ChineseTW: + case eFontPrefLang_ChineseCN: + case eFontPrefLang_ChineseHK: + case eFontPrefLang_Korean: + case eFontPrefLang_CJKSet: + return true; + default: + return false; + } +} + +void +gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) +{ + if (IsLangCJK(aCharLang)) { + AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang); + } else { + AppendPrefLang(aPrefLangs, aLen, aCharLang); + } + + AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others); +} + +void +gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang) +{ + // prefer the lang specified by the page *if* CJK + if (IsLangCJK(aPageLang)) { + AppendPrefLang(aPrefLangs, aLen, aPageLang); + } + + // if not set up, set up the default CJK order, based on accept lang settings and locale + if (mCJKPrefLangs.Length() == 0) { + + // temp array + eFontPrefLang tempPrefLangs[kMaxLenPrefLangList]; + uint32_t tempLen = 0; + + // Add the CJK pref fonts from accept languages, the order should be same order + nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages"); + if (!list.IsEmpty()) { + const char kComma = ','; + const char *p, *p_end; + list.BeginReading(p); + list.EndReading(p_end); + while (p < p_end) { + while (nsCRT::IsAsciiSpace(*p)) { + if (++p == p_end) + break; + } + if (p == p_end) + break; + const char *start = p; + while (++p != p_end && *p != kComma) + /* nothing */ ; + nsAutoCString lang(Substring(start, p)); + lang.CompressWhitespace(false, true); + eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get()); + switch (fpl) { + case eFontPrefLang_Japanese: + case eFontPrefLang_Korean: + case eFontPrefLang_ChineseCN: + case eFontPrefLang_ChineseHK: + case eFontPrefLang_ChineseTW: + AppendPrefLang(tempPrefLangs, tempLen, fpl); + break; + default: + break; + } + p++; + } + } + + do { // to allow 'break' to abort this block if a call fails + nsresult rv; + nsCOMPtr ls = + do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + break; + + nsCOMPtr appLocale; + rv = ls->GetApplicationLocale(getter_AddRefs(appLocale)); + if (NS_FAILED(rv)) + break; + + nsString localeStr; + rv = appLocale-> + GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr); + if (NS_FAILED(rv)) + break; + + const nsAString& lang = Substring(localeStr, 0, 2); + if (lang.EqualsLiteral("ja")) { + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); + } else if (lang.EqualsLiteral("zh")) { + const nsAString& region = Substring(localeStr, 3, 2); + if (region.EqualsLiteral("CN")) { + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); + } else if (region.EqualsLiteral("TW")) { + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); + } else if (region.EqualsLiteral("HK")) { + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); + } + } else if (lang.EqualsLiteral("ko")) { + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); + } + } while (0); + + // last resort... (the order is same as old gfx.) + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese); + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean); + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN); + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK); + AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW); + + // copy into the cached array + uint32_t j; + for (j = 0; j < tempLen; j++) { + mCJKPrefLangs.AppendElement(tempPrefLangs[j]); + } + } + + // append in cached CJK langs + uint32_t i, numCJKlangs = mCJKPrefLangs.Length(); + + for (i = 0; i < numCJKlangs; i++) { + AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i])); + } + +} + +void +gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang) +{ + if (aLen >= kMaxLenPrefLangList) return; + + // make sure + uint32_t i = 0; + while (i < aLen && aPrefLangs[i] != aAddLang) { + i++; + } + + if (i == aLen) { + aPrefLangs[aLen] = aAddLang; + aLen++; + } +} + +mozilla::FontFamilyType +gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang) +{ + // initialize lang group pref font defaults (i.e. serif/sans-serif) + if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) { + mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames)); + for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) { + nsAutoCString prefDefaultFontType("font.default."); + prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i))); + nsAdoptingCString serifOrSans = + Preferences::GetCString(prefDefaultFontType.get()); + if (serifOrSans.EqualsLiteral("sans-serif")) { + mDefaultGenericsLangGroup[i] = eFamily_sans_serif; + } else { + mDefaultGenericsLangGroup[i] = eFamily_serif; + } + } + } + + if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) { + return mDefaultGenericsLangGroup[uint32_t(aLang)]; + } + return eFamily_serif; +} + void gfxPlatformFontList::GetFontFamilyNames(nsTArray& aFontFamilyNames) { @@ -952,6 +1371,19 @@ gfxPlatformFontList::RebuildLocalFonts() } } +void +gfxPlatformFontList::ClearLangGroupPrefFonts() +{ + for (uint32_t i = eFontPrefLang_First; + i < eFontPrefLang_First + eFontPrefLang_Count; i++) { + auto& prefFontsLangGroup = mLangGroupPrefFonts[i]; + for (uint32_t j = eFamily_generic_first; + j < eFamily_generic_first + eFamily_generic_count; j++) { + prefFontsLangGroup[j] = nullptr; + } + } +} + // Support for memory reporting // this is also used by subclasses that hold additional font tables @@ -1008,21 +1440,24 @@ gfxPlatformFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, aMallocSizeOf); } + for (uint32_t i = eFontPrefLang_First; + i < eFontPrefLang_First + eFontPrefLang_Count; i++) { + auto& prefFontsLangGroup = mLangGroupPrefFonts[i]; + for (uint32_t j = eFamily_generic_first; + j < eFamily_generic_first + eFamily_generic_count; j++) { + PrefFontList* pf = prefFontsLangGroup[j]; + if (pf) { + aSizes->mFontListSize += + pf->ShallowSizeOfExcludingThis(aMallocSizeOf); + } + } + } + aSizes->mFontListSize += mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf); aSizes->mFontListSize += mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf); - aSizes->mFontListSize += - mPrefFonts.ShallowSizeOfExcludingThis(aMallocSizeOf); - for (auto iter = mPrefFonts.ConstIter(); !iter.Done(); iter.Next()) { - // Again, we only care about the size of the array itself; we don't - // follow the refPtrs stored in it, because they point to entries - // already owned and accounted-for by the main font list. - aSizes->mFontListSize += - iter.Data().ShallowSizeOfExcludingThis(aMallocSizeOf); - } - aSizes->mFontListSize += mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf); diff --git a/gfx/thebes/gfxPlatformFontList.h b/gfx/thebes/gfxPlatformFontList.h index 97e3526041b..80b4bacbcf2 100644 --- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -13,11 +13,15 @@ #include "gfxFontUtils.h" #include "gfxFontInfoLoader.h" #include "gfxFont.h" +#include "gfxFontConstants.h" #include "gfxPlatform.h" +#include "gfxFontFamilyList.h" #include "nsIMemoryReporter.h" #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/RangedArray.h" +#include "nsILanguageAtomService.h" class CharMapHashKey : public PLDHashEntryHdr { @@ -116,7 +120,7 @@ public: void UpdateFontList(); - void ClearPrefFonts() { mPrefFonts.Clear(); } + void ClearLangGroupPrefFonts(); virtual void GetFontFamilyList(nsTArray >& aFamilyArray); @@ -131,9 +135,6 @@ public: gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold); - bool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray > *array); - void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray >& array); - // name lookup table methods void AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName); @@ -199,6 +200,43 @@ public: aLoaderState = (uint32_t) mState; } + virtual void + AddGenericFonts(mozilla::FontFamilyType aGenericType, + nsIAtom* aLanguage, + nsTArray& aFamilyList); + + nsTArray>* + GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType, + eFontPrefLang aPrefLang); + + // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs + void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang); + + // convert a lang group to enum constant (i.e. "zh-TW" ==> eFontPrefLang_ChineseTW) + static eFontPrefLang GetFontPrefLangFor(const char* aLang); + + // convert a lang group atom to enum constant + static eFontPrefLang GetFontPrefLangFor(nsIAtom *aLang); + + // convert an enum constant to a lang group atom + static nsIAtom* GetLangGroupForPrefLang(eFontPrefLang aLang); + + // convert a enum constant to lang group string (i.e. eFontPrefLang_ChineseTW ==> "zh-TW") + static const char* GetPrefLangName(eFontPrefLang aLang); + + // map a Unicode range (based on char code) to a font language for Preferences + static eFontPrefLang GetFontPrefLangFor(uint8_t aUnicodeRange); + + // returns true if a pref lang is CJK + static bool IsLangCJK(eFontPrefLang aLang); + + // helper method to add a pref lang to an array, if not already in array + static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang); + + // default serif/sans-serif choice based on font.default.xxx prefs + mozilla::FontFamilyType + GetDefaultGeneric(eFontPrefLang aLang); + protected: class MemoryReporter final : public nsIMemoryReporter { @@ -241,6 +279,9 @@ protected: // if system fallback is used, no need to load all cmaps virtual bool UsesSystemFallback() { return false; } + void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, + eFontPrefLang aCharLang, eFontPrefLang aPageLang); + // verifies that a family contains a non-zero font count gfxFontFamily* CheckFamily(gfxFontFamily *aFamily); @@ -287,6 +328,11 @@ protected: void RebuildLocalFonts(); + void + ResolveGenericFontNames(mozilla::FontFamilyType aGenericType, + eFontPrefLang aPrefLang, + nsTArray>* aGenericFamilies); + typedef nsRefPtrHashtable FontFamilyTable; typedef nsRefPtrHashtable FontEntryTable; @@ -332,9 +378,13 @@ protected: // localized family names missed when face name loading takes a long time nsAutoPtr > mOtherNamesMissed; - // cached pref font lists - // maps list of family names ==> array of family entries, one per lang group - nsDataHashtable > > mPrefFonts; + typedef nsTArray> PrefFontList; + typedef mozilla::RangedArray, + mozilla::eFamily_generic_first, + mozilla::eFamily_generic_count> PrefFontsForLangGroup; + mozilla::RangedArray mLangGroupPrefFonts; // when system-wide font lookup fails for a character, cache it to skip future searches gfxSparseBitSet mCodepointsWithNoFonts; @@ -360,6 +410,10 @@ protected: uint32_t mFontlistInitCount; // num times InitFontList called nsTHashtable > mUserFontSetList; + + nsCOMPtr mLangService; + nsTArray mCJKPrefLangs; + nsTArray mDefaultGenericsLangGroup; }; #endif /* GFXPLATFORMFONTLIST_H_ */ diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 49aba62e159..6d1b1560baa 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1543,7 +1543,7 @@ gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList, , mHyphenWidth(-1) , mUserFontSet(aUserFontSet) , mTextPerf(aTextPerf) - , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language)) + , mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language)) , mSkipDrawing(false) , mSkipUpdateUserFonts(false) { @@ -1557,138 +1557,6 @@ gfxFontGroup::~gfxFontGroup() { } -void -gfxFontGroup::FindGenericFonts(FontFamilyType aGenericType, - nsIAtom *aLanguage, - void *aClosure) -{ - nsAutoTArray resolvedGenerics; - ResolveGenericFontNames(aGenericType, aLanguage, resolvedGenerics); - uint32_t g = 0, numGenerics = resolvedGenerics.Length(); - for (g = 0; g < numGenerics; g++) { - FindPlatformFont(resolvedGenerics[g], false, aClosure); - } -} - -/* static */ void -gfxFontGroup::ResolveGenericFontNames(FontFamilyType aGenericType, - nsIAtom *aLanguage, - nsTArray& aGenericFamilies) -{ - static const char kGeneric_serif[] = "serif"; - static const char kGeneric_sans_serif[] = "sans-serif"; - static const char kGeneric_monospace[] = "monospace"; - static const char kGeneric_cursive[] = "cursive"; - static const char kGeneric_fantasy[] = "fantasy"; - - // treat -moz-fixed as monospace - if (aGenericType == eFamily_moz_fixed) { - aGenericType = eFamily_monospace; - } - - // type should be standard generic type at this point - NS_ASSERTION(aGenericType >= eFamily_serif && - aGenericType <= eFamily_fantasy, - "standard generic font family type required"); - - // create the lang string - nsIAtom *langGroupAtom = nullptr; - nsAutoCString langGroupString; - if (aLanguage) { - if (!gLangService) { - CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService); - } - if (gLangService) { - nsresult rv; - langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv); - } - } - if (!langGroupAtom) { - langGroupAtom = nsGkAtoms::Unicode; - } - langGroupAtom->ToUTF8String(langGroupString); - - // map generic type to string - const char *generic = nullptr; - switch (aGenericType) { - case eFamily_serif: - generic = kGeneric_serif; - break; - case eFamily_sans_serif: - generic = kGeneric_sans_serif; - break; - case eFamily_monospace: - generic = kGeneric_monospace; - break; - case eFamily_cursive: - generic = kGeneric_cursive; - break; - case eFamily_fantasy: - generic = kGeneric_fantasy; - break; - default: - break; - } - - if (!generic) { - return; - } - - aGenericFamilies.Clear(); - - // load family for "font.name.generic.lang" - nsAutoCString prefFontName("font.name."); - prefFontName.Append(generic); - prefFontName.Append('.'); - prefFontName.Append(langGroupString); - gfxFontUtils::AppendPrefsFontList(prefFontName.get(), - aGenericFamilies); - - // if lang has pref fonts, also load fonts for "font.name-list.generic.lang" - if (!aGenericFamilies.IsEmpty()) { - nsAutoCString prefFontListName("font.name-list."); - prefFontListName.Append(generic); - prefFontListName.Append('.'); - prefFontListName.Append(langGroupString); - gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), - aGenericFamilies); - } - -#if 0 // dump out generic mappings - printf("%s ===> ", prefFontName.get()); - for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) { - if (k > 0) printf(", "); - printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get()); - } - printf("\n"); -#endif -} - -void gfxFontGroup::EnumerateFontList(nsIAtom *aLanguage, void *aClosure) -{ - // initialize fonts in the font family list - const nsTArray& fontlist = mFamilyList.GetFontlist(); - - // lookup fonts in the fontlist - uint32_t i, numFonts = fontlist.Length(); - for (i = 0; i < numFonts; i++) { - const FontFamilyName& name = fontlist[i]; - if (name.IsNamed()) { - FindPlatformFont(name.mName, true, aClosure); - } else { - FindGenericFonts(name.mType, aLanguage, aClosure); - } - } - - // if necessary, append default generic onto the end - if (mFamilyList.GetDefaultFontType() != eFamily_none && - !mFamilyList.HasDefaultGeneric()) { - FindGenericFonts(mFamilyList.GetDefaultFontType(), - aLanguage, - aClosure); - } -} - void gfxFontGroup::BuildFontList() { @@ -1700,52 +1568,84 @@ gfxFontGroup::BuildFontList() #elif defined(MOZ_WIDGET_QT) enumerateFonts = false; #endif - if (enumerateFonts) { - EnumerateFontList(mStyle.language); + if (!enumerateFonts) { + return; + } + + // initialize fonts in the font family list + nsAutoTArray fonts; + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); + + // lookup fonts in the fontlist + for (const FontFamilyName& name : mFamilyList.GetFontlist()) { + if (name.IsNamed()) { + AddPlatformFont(name.mName, fonts); + } else { + pfl->AddGenericFonts(name.mType, mStyle.language, fonts); + if (mTextPerf) { + mTextPerf->current.genericLookups++; + } + } + } + + // if necessary, append default generic onto the end + if (mFamilyList.GetDefaultFontType() != eFamily_none && + !mFamilyList.HasDefaultGeneric()) { + pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(), + mStyle.language, fonts); + if (mTextPerf) { + mTextPerf->current.genericLookups++; + } + } + + // build the fontlist from the specified families + for (gfxFontFamily* fontFamily : fonts) { + AddFamilyToFontList(fontFamily); } } void -gfxFontGroup::FindPlatformFont(const nsAString& aName, - bool aUseFontSet, - void *aClosure) +gfxFontGroup::AddPlatformFont(const nsAString& aName, + nsTArray& aFamilyList) { - bool needsBold; - gfxFontFamily *family = nullptr; + gfxFontFamily* family = nullptr; - if (aUseFontSet) { - // First, look up in the user font set... - // If the fontSet matches the family, we must not look for a platform - // font of the same name, even if we fail to actually get a fontEntry - // here; we'll fall back to the next name in the CSS font-family list. - if (mUserFontSet) { - // Add userfonts to the fontlist whether already loaded - // or not. Loading is initiated during font matching. - family = mUserFontSet->LookupFamily(aName); - } + // First, look up in the user font set... + // If the fontSet matches the family, we must not look for a platform + // font of the same name, even if we fail to actually get a fontEntry + // here; we'll fall back to the next name in the CSS font-family list. + if (mUserFontSet) { + // Add userfonts to the fontlist whether already loaded + // or not. Loading is initiated during font matching. + family = mUserFontSet->LookupFamily(aName); } // Not known in the user font set ==> check system fonts if (!family) { - gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList(); + gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList(); family = fontList->FindFamily(aName, mStyle.language, mStyle.systemFont); } - // if family found, do style matching and add all font entries to mFonts if (family) { - nsAutoTArray fontEntryList; - family->FindAllFontsForStyle(mStyle, fontEntryList, needsBold); - // add these to the fontlist - uint32_t n = fontEntryList.Length(); - for (uint32_t i = 0; i < n; i++) { - gfxFontEntry* fe = fontEntryList[i]; - if (!HasFont(fe)) { - FamilyFace ff(family, fe, needsBold); - if (fe->mIsUserFontContainer) { - ff.CheckState(mSkipDrawing); - } - mFonts.AppendElement(ff); + aFamilyList.AppendElement(family); + } +} + +void +gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily) +{ + NS_ASSERTION(aFamily, "trying to add a null font family to fontlist"); + nsAutoTArray fontEntryList; + bool needsBold; + aFamily->FindAllFontsForStyle(mStyle, fontEntryList, needsBold); + // add these to the fontlist + for (gfxFontEntry* fe : fontEntryList) { + if (!HasFont(fe)) { + FamilyFace ff(aFamily, fe, needsBold); + if (fe->mIsUserFontContainer) { + ff.CheckState(mSkipDrawing); } + mFonts.AppendElement(ff); } } } @@ -3135,7 +3035,7 @@ struct PrefFontCallbackData { PrefFontCallbackData *prefFontData = static_cast(aClosure); // map pref lang to langGroup for language-sensitive lookups - nsIAtom* lang = gfxPlatform::GetLangGroupForPrefLang(aLang); + nsIAtom* lang = gfxPlatformFontList::GetLangGroupForPrefLang(aLang); gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName, lang); if (family) { @@ -3152,7 +3052,8 @@ gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh) // get the pref font list if it hasn't been set up already uint32_t unicodeRange = FindCharUnicodeRange(aCh); - eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange); + gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); + eFontPrefLang charLang = pfl->GetFontPrefLangFor(unicodeRange); // if the last pref font was the first family in the pref list, no need to recheck through a list of families if (mLastPrefFont && charLang == mLastPrefLang && @@ -3165,29 +3066,22 @@ gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh) eFontPrefLang prefLangs[kMaxLenPrefLangList]; uint32_t i, numLangs = 0; - gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang); + pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang); for (i = 0; i < numLangs; i++) { - nsAutoTArray, 5> families; eFontPrefLang currentLang = prefLangs[i]; - - gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList(); - - // get the pref families for a single pref lang - if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) { - eFontPrefLang prefLangsToSearch[1] = { currentLang }; - PrefFontCallbackData prefFontData(families); - gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry, - &prefFontData); - fontList->SetPrefFontFamilyEntries(currentLang, families); - } + mozilla::FontFamilyType defaultGeneric = + pfl->GetDefaultGeneric(currentLang); + nsTArray>* families = + pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang); + NS_ASSERTION(families, "no pref font families found"); // find the first pref font that includes the character uint32_t j, numPrefs; - numPrefs = families.Length(); + numPrefs = families->Length(); for (j = 0; j < numPrefs; j++) { // look up the appropriate face - gfxFontFamily *family = families[j]; + gfxFontFamily *family = (*families)[j]; if (!family) continue; // if a pref font is used, it's likely to be used again in the same text run. diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index 75f7e3b377c..68db2c5d4a1 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -10,6 +10,7 @@ #include "nsString.h" #include "gfxPoint.h" #include "gfxFont.h" +#include "gfxFontConstants.h" #include "nsTArray.h" #include "gfxSkipChars.h" #include "gfxPlatform.h" @@ -872,12 +873,6 @@ public: gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags, LazyReferenceContextGetter& aRefContextGetter); - // helper method for resolving generic font families - static void - ResolveGenericFontNames(mozilla::FontFamilyType aGenericType, - nsIAtom *aLanguage, - nsTArray& aGenericFamilies); - protected: // search through pref fonts for a character, return nullptr if no matching pref font already_AddRefed WhichPrefFontSupportsChar(uint32_t aCh); @@ -1117,18 +1112,12 @@ protected: // helper methods for looking up fonts - // iterate over the fontlist, lookup names and expand generics - void EnumerateFontList(nsIAtom *aLanguage, void *aClosure = nullptr); - - // expand a generic to a list of specific names based on prefs - void FindGenericFonts(mozilla::FontFamilyType aGenericType, - nsIAtom *aLanguage, - void *aClosure); - // lookup and add a font with a given name (i.e. *not* a generic!) - virtual void FindPlatformFont(const nsAString& aName, - bool aUseFontSet, - void *aClosure); + void AddPlatformFont(const nsAString& aName, + nsTArray& aFamilyList); + + // do style selection and add entries to list + void AddFamilyToFontList(gfxFontFamily* aFamily); static nsILanguageAtomService* gLangService; }; diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index ae89a882fda..b301586ff10 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -1562,11 +1562,13 @@ bool gfxUtils::sDumpPainting = getenv("MOZ_DUMP_PAINT") != 0; bool gfxUtils::sDumpPaintingIntermediate = getenv("MOZ_DUMP_PAINT_INTERMEDIATE") != 0; bool gfxUtils::sDumpPaintingToFile = getenv("MOZ_DUMP_PAINT_TO_FILE") != 0; bool gfxUtils::sDumpPaintItems = getenv("MOZ_DUMP_PAINT_ITEMS") != 0; +bool gfxUtils::sDumpCompositorTextures = getenv("MOZ_DUMP_COMPOSITOR_TEXTURES") != 0; #else bool gfxUtils::sDumpPainting = false; bool gfxUtils::sDumpPaintingIntermediate = false; bool gfxUtils::sDumpPaintingToFile = false; bool gfxUtils::sDumpPaintItems = false; +bool gfxUtils::sDumpCompositorTextures = false; #endif namespace mozilla { diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 335ca90230a..d8d62b8ade5 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -296,6 +296,12 @@ public: static bool sDumpPaintingIntermediate; static bool sDumpPaintingToFile; static bool sDumpPaintItems; + // TODO: Dumping compositor textures is broken pretty badly. For example, + // on Linux it crashes because TextureHost::GetAsSurface() returns + // null. Expect to have to fix things like this if you turn it on. + // Meanwhile, content-side texture dumping (conditioned on + // sDumpPainting) is a good replacement. + static bool sDumpCompositorTextures; static FILE* sDumpPaintFile; }; diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 4acf25909c5..04b26748883 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -2410,6 +2410,12 @@ gfxWindowsPlatform::CheckD2DSupport() if (!IsVistaOrLater()) { return FeatureStatus::Unavailable; } + + // Normally we don't use D2D content drawing when using WARP. However if + // WARP is force-enabled, we will let Direct2D use WARP as well. + if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) { + return FeatureStatus::Blocked; + } return FeatureStatus::Available; } @@ -2463,11 +2469,6 @@ gfxWindowsPlatform::CheckD2D1Support() if (!gfxPrefs::Direct2DUse1_1()) { return FeatureStatus::Disabled; } - // Normally we don't use D2D content drawing when using WARP. However if - // WARP is force-enabled, we will let Direct2D use WARP as well. - if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) { - return FeatureStatus::Blocked; - } return FeatureStatus::Available; } diff --git a/image/OrientedImage.cpp b/image/OrientedImage.cpp index 0ec6dfe1d72..d22d8b27f64 100644 --- a/image/OrientedImage.cpp +++ b/image/OrientedImage.cpp @@ -344,7 +344,7 @@ OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect) } // Transform the invalidation rect into the correct orientation. - gfxMatrix matrix(OrientationMatrix(innerSize, /* aInvert = */ true)); + gfxMatrix matrix(OrientationMatrix(innerSize)); gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y, rect.width, rect.height))); invalidRect.RoundOut(); diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 16693bbed38..1a5e814ff82 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -653,6 +653,8 @@ class HashMapEntry template friend class detail::HashTableEntry; template friend class HashMap; + Key & mutableKey() { return key_; } + public: template HashMapEntry(KeyInput&& k, ValueInput&& v) @@ -668,10 +670,9 @@ class HashMapEntry typedef Key KeyType; typedef Value ValueType; - const Key& key() const { return key_; } - Key& mutableKey() { return key_; } - const Value& value() const { return value_; } - Value& value() { return value_; } + const Key & key() const { return key_; } + const Value & value() const { return value_; } + Value & value() { return value_; } private: HashMapEntry(const HashMapEntry&) = delete; @@ -740,7 +741,6 @@ class HashTableEntry } T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); } - NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); } bool isFree() const { return keyHash == sFreeKey; } void clearLive() { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); } @@ -980,16 +980,6 @@ class HashTable : private AllocPolicy #endif } - NonConstT& mutableFront() { - MOZ_ASSERT(!this->empty()); -#ifdef JS_DEBUG - MOZ_ASSERT(this->validEntry); - MOZ_ASSERT(this->generation == this->Range::table_->generation()); - MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount); -#endif - return this->cur->getMutable(); - } - // Removes the |front()| element and re-inserts it into the table with // a new key at the new Lookup position. |front()| is invalid after // this operation until the next call to |popFront()|. diff --git a/js/public/HeapAPI.h b/js/public/HeapAPI.h index 016d9f1953b..2987b77356f 100644 --- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -54,8 +54,7 @@ const size_t ChunkMarkBitmapOffset = 1032352; const size_t ChunkMarkBitmapBits = 129024; #endif const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*); -const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t); -const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize; +const size_t ChunkLocationOffset = ChunkSize - 2 * sizeof(void*) - sizeof(uint64_t); const size_t ArenaZoneOffset = 0; /* diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 84adc0904d7..5bffb6d8a0c 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -19,7 +19,6 @@ #include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/TypedArrayObject.h" -#include "vm/WeakMapObject.h" #include "jsatominlines.h" #include "jsobjinlines.h" diff --git a/js/src/builtin/WeakMapObject.cpp b/js/src/builtin/WeakMapObject.cpp new file mode 100644 index 00000000000..c900971bf0d --- /dev/null +++ b/js/src/builtin/WeakMapObject.cpp @@ -0,0 +1,485 @@ +/* -*- 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 "builtin/WeakMapObject.h" + +#include "jsapi.h" +#include "jscntxt.h" + +#include "vm/Interpreter-inl.h" + +using namespace js; +using namespace js::gc; + +using mozilla::UniquePtr; + +MOZ_ALWAYS_INLINE bool +IsWeakMap(HandleValue v) +{ + return v.isObject() && v.toObject().is(); +} + +MOZ_ALWAYS_INLINE bool +WeakMap_has_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsWeakMap(args.thisv())); + + if (!args.get(0).isObject()) { + args.rval().setBoolean(false); + return true; + } + + if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { + JSObject* key = &args[0].toObject(); + if (map->has(key)) { + args.rval().setBoolean(true); + return true; + } + } + + args.rval().setBoolean(false); + return true; +} + +bool +js::WeakMap_has(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +MOZ_ALWAYS_INLINE bool +WeakMap_clear_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsWeakMap(args.thisv())); + + // We can't js_delete the weakmap because the data gathered during GC is + // used by the Cycle Collector. + if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) + map->clear(); + + args.rval().setUndefined(); + return true; +} + +bool +js::WeakMap_clear(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +MOZ_ALWAYS_INLINE bool +WeakMap_get_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsWeakMap(args.thisv())); + + if (!args.get(0).isObject()) { + args.rval().setUndefined(); + return true; + } + + if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { + JSObject* key = &args[0].toObject(); + if (ObjectValueMap::Ptr ptr = map->lookup(key)) { + args.rval().set(ptr->value()); + return true; + } + } + + args.rval().setUndefined(); + return true; +} + +bool +js::WeakMap_get(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +MOZ_ALWAYS_INLINE bool +WeakMap_delete_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsWeakMap(args.thisv())); + + if (!args.get(0).isObject()) { + args.rval().setBoolean(false); + return true; + } + + if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { + JSObject* key = &args[0].toObject(); + if (ObjectValueMap::Ptr ptr = map->lookup(key)) { + map->remove(ptr); + args.rval().setBoolean(true); + return true; + } + } + + args.rval().setBoolean(false); + return true; +} + +bool +js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +static bool +TryPreserveReflector(JSContext* cx, HandleObject obj) +{ + if (obj->getClass()->ext.isWrappedNative || + (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) || + (obj->is() && + obj->as().handler()->family() == GetDOMProxyHandlerFamily())) + { + MOZ_ASSERT(cx->runtime()->preserveWrapperCallback); + if (!cx->runtime()->preserveWrapperCallback(cx, obj)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY); + return false; + } + } + return true; +} + +static inline void +WeakMapPostWriteBarrier(JSRuntime* rt, ObjectValueMap* weakMap, JSObject* key) +{ + if (key && IsInsideNursery(key)) + rt->gc.storeBuffer.putGeneric(gc::HashKeyRef(weakMap, key)); +} + +static MOZ_ALWAYS_INLINE bool +SetWeakMapEntryInternal(JSContext* cx, Handle mapObj, + HandleObject key, HandleValue value) +{ + ObjectValueMap* map = mapObj->getMap(); + if (!map) { + AutoInitGCManagedObject newMap( + cx->make_unique(cx, mapObj.get())); + if (!newMap) + return false; + if (!newMap->init()) { + JS_ReportOutOfMemory(cx); + return false; + } + map = newMap.release(); + mapObj->setPrivate(map); + } + + // Preserve wrapped native keys to prevent wrapper optimization. + if (!TryPreserveReflector(cx, key)) + return false; + + if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) { + RootedObject delegate(cx, op(key)); + if (delegate && !TryPreserveReflector(cx, delegate)) + return false; + } + + MOZ_ASSERT(key->compartment() == mapObj->compartment()); + MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment()); + if (!map->put(key, value)) { + JS_ReportOutOfMemory(cx); + return false; + } + WeakMapPostWriteBarrier(cx->runtime(), map, key.get()); + return true; +} + +MOZ_ALWAYS_INLINE bool +WeakMap_set_impl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsWeakMap(args.thisv())); + + if (!args.get(0).isObject()) { + UniquePtr bytes = + DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr); + if (!bytes) + return false; + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); + return false; + } + + RootedObject key(cx, &args[0].toObject()); + Rooted thisObj(cx, &args.thisv().toObject()); + Rooted map(cx, &thisObj->as()); + + if (!SetWeakMapEntryInternal(cx, map, key, args.get(1))) + return false; + args.rval().set(args.thisv()); + return true; +} + +bool +js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +JS_FRIEND_API(bool) +JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret) +{ + RootedObject obj(cx, objArg); + obj = UncheckedUnwrap(obj); + if (!obj || !obj->is()) { + ret.set(nullptr); + return true; + } + RootedObject arr(cx, NewDenseEmptyArray(cx)); + if (!arr) + return false; + ObjectValueMap* map = obj->as().getMap(); + if (map) { + // Prevent GC from mutating the weakmap while iterating. + AutoSuppressGC suppress(cx); + for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) { + JS::ExposeObjectToActiveJS(r.front().key()); + RootedObject key(cx, r.front().key()); + if (!cx->compartment()->wrap(cx, &key)) + return false; + if (!NewbornArrayPush(cx, arr, ObjectValue(*key))) + return false; + } + } + ret.set(arr); + return true; +} + +static void +WeakMap_mark(JSTracer* trc, JSObject* obj) +{ + if (ObjectValueMap* map = obj->as().getMap()) + map->trace(trc); +} + +static void +WeakMap_finalize(FreeOp* fop, JSObject* obj) +{ + if (ObjectValueMap* map = obj->as().getMap()) { +#ifdef DEBUG + map->~ObjectValueMap(); + memset(static_cast(map), 0xdc, sizeof(*map)); + fop->free_(map); +#else + fop->delete_(map); +#endif + } +} + +JS_PUBLIC_API(JSObject*) +JS::NewWeakMapObject(JSContext* cx) +{ + return NewBuiltinClassInstance(cx, &WeakMapObject::class_); +} + +JS_PUBLIC_API(bool) +JS::IsWeakMapObject(JSObject* obj) +{ + return obj->is(); +} + +JS_PUBLIC_API(bool) +JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key, + MutableHandleValue rval) +{ + CHECK_REQUEST(cx); + assertSameCompartment(cx, key); + rval.setUndefined(); + ObjectValueMap* map = mapObj->as().getMap(); + if (!map) + return true; + if (ObjectValueMap::Ptr ptr = map->lookup(key)) { + // Read barrier to prevent an incorrectly gray value from escaping the + // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp + ExposeValueToActiveJS(ptr->value().get()); + rval.set(ptr->value()); + } + return true; +} + +JS_PUBLIC_API(bool) +JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key, + HandleValue val) +{ + CHECK_REQUEST(cx); + assertSameCompartment(cx, key, val); + Rooted rootedMap(cx, &mapObj->as()); + return SetWeakMapEntryInternal(cx, rootedMap, key, val); +} + +static bool +WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1. + if (!ThrowIfNotConstructing(cx, args, "WeakMap")) + return false; + + RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_)); + if (!obj) + return false; + + // Steps 5-6, 11. + if (!args.get(0).isNullOrUndefined()) { + // Steps 7a-b. + RootedValue adderVal(cx); + if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal)) + return false; + + // Step 7c. + if (!IsCallable(adderVal)) + return ReportIsNotFunction(cx, adderVal); + + bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set); + RootedValue mapVal(cx, ObjectValue(*obj)); + FastInvokeGuard fig(cx, adderVal); + InvokeArgs& args2 = fig.args(); + + // Steps 7d-e. + JS::ForOfIterator iter(cx); + if (!iter.init(args[0])) + return false; + + RootedValue pairVal(cx); + RootedObject pairObject(cx); + RootedValue keyVal(cx); + RootedObject keyObject(cx); + RootedValue val(cx); + while (true) { + // Steps 12a-e. + bool done; + if (!iter.next(&pairVal, &done)) + return false; + if (done) + break; + + // Step 12f. + if (!pairVal.isObject()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_INVALID_MAP_ITERABLE, "WeakMap"); + return false; + } + + pairObject = &pairVal.toObject(); + if (!pairObject) + return false; + + // Steps 12g-h. + if (!GetElement(cx, pairObject, pairObject, 0, &keyVal)) + return false; + + // Steps 12i-j. + if (!GetElement(cx, pairObject, pairObject, 1, &val)) + return false; + + // Steps 12k-l. + if (isOriginalAdder) { + if (keyVal.isPrimitive()) { + UniquePtr bytes = + DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr); + if (!bytes) + return false; + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); + return false; + } + + keyObject = &keyVal.toObject(); + if (!SetWeakMapEntry(cx, obj, keyObject, val)) + return false; + } else { + if (!args2.init(2)) + return false; + + args2.setCallee(adderVal); + args2.setThis(mapVal); + args2[0].set(keyVal); + args2[1].set(val); + + if (!fig.invoke(cx)) + return false; + } + } + } + + args.rval().setObject(*obj); + return true; +} + +const Class WeakMapObject::class_ = { + "WeakMap", + JSCLASS_HAS_PRIVATE | + JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + nullptr, /* convert */ + WeakMap_finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + WeakMap_mark +}; + +static const JSFunctionSpec weak_map_methods[] = { + JS_FN("has", WeakMap_has, 1, 0), + JS_FN("get", WeakMap_get, 1, 0), + JS_FN("delete", WeakMap_delete, 1, 0), + JS_FN("set", WeakMap_set, 2, 0), + JS_FN("clear", WeakMap_clear, 0, 0), + JS_FS_END +}; + +static JSObject* +InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers) +{ + MOZ_ASSERT(obj->isNative()); + + Rooted global(cx, &obj->as()); + + RootedPlainObject proto(cx, NewBuiltinClassInstance(cx)); + if (!proto) + return nullptr; + + RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct, + cx->names().WeakMap, 0)); + if (!ctor) + return nullptr; + + if (!LinkConstructorAndPrototype(cx, ctor, proto)) + return nullptr; + + if (defineMembers) { + if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods)) + return nullptr; + } + + if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto)) + return nullptr; + return proto; +} + +JSObject* +js::InitWeakMapClass(JSContext* cx, HandleObject obj) +{ + return InitWeakMapClass(cx, obj, true); +} + +JSObject* +js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj) +{ + return InitWeakMapClass(cx, obj, false); +} + diff --git a/js/src/builtin/WeakMapObject.h b/js/src/builtin/WeakMapObject.h new file mode 100644 index 00000000000..c9b95cea39d --- /dev/null +++ b/js/src/builtin/WeakMapObject.h @@ -0,0 +1,25 @@ +/* -*- 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/. */ + +#ifndef builtin_WeakMapObject_h +#define builtin_WeakMapObject_h + +#include "jsobj.h" +#include "jsweakmap.h" + +namespace js { + +class WeakMapObject : public NativeObject +{ + public: + static const Class class_; + + ObjectValueMap* getMap() { return static_cast(getPrivate()); } +}; + +} // namespace js + +#endif /* builtin_WeakMapObject_h */ diff --git a/js/src/configure.in b/js/src/configure.in index 2f461e0f0b7..32f7c6f7a9e 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -125,6 +125,8 @@ MOZ_ARG_WITH_STRING(dist-dir, TOP_DIST=dist) AC_SUBST(TOP_DIST) +MOZ_BUILD_BACKEND + MOZ_DEFAULT_COMPILER COMPILE_ENVIRONMENT=1 diff --git a/js/src/ds/LockGuard.h b/js/src/ds/LockGuard.h deleted file mode 100644 index 7baaa896b67..00000000000 --- a/js/src/ds/LockGuard.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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/. */ - -#ifndef js_LockGuard_h -#define js_LockGuard_h - -#include "mozilla/GuardObjects.h" - -namespace js { - -// An implementation of C++11's std::lock_guard, enhanced with a guard object -// to help with correct usage. -template -class LockGuard -{ - LockType& lockRef_; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; - - public: - explicit LockGuard(LockType& lock - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : lockRef_(lock) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - lockRef_.lock(); - } - - ~LockGuard() { - lockRef_.unlock(); - } -}; - -} // namespace js - -#endif // js_LockGuard_h diff --git a/js/src/ds/SpinLock.h b/js/src/ds/SpinLock.h deleted file mode 100644 index 87253d99023..00000000000 --- a/js/src/ds/SpinLock.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- 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/. */ - -#ifndef js_SpinLock_h -#define js_SpinLock_h - -#include "mozilla/Atomics.h" - -#include "ds/LockGuard.h" - -namespace js { - -// A trivial spin-lock implementation. Extremely fast when rarely-contended. -class SpinLock -{ - mozilla::Atomic locked_; - - public: - SpinLock() : locked_(false) {} - - void lock() { - do { - while (locked_) - ; // Spin until the lock seems free. - } while (!locked_.compareExchange(false, true)); // Atomically take the lock. - } - - void unlock() { - locked_ = false; - } -}; - -using AutoSpinLock = LockGuard; - -} // namespace js - -#endif // js_SpinLock_h diff --git a/js/src/gc/Barrier.cpp b/js/src/gc/Barrier.cpp index 4014f4a8e6f..c361d97f7da 100644 --- a/js/src/gc/Barrier.cpp +++ b/js/src/gc/Barrier.cpp @@ -10,9 +10,7 @@ #include "jsobj.h" #include "gc/Zone.h" -#include "js/HashTable.h" #include "js/Value.h" -#include "vm/ScopeObject.h" #include "vm/Symbol.h" #ifdef DEBUG @@ -104,56 +102,3 @@ JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next MOZ_ASSERT(valuep); js::InternalGCMethods::postBarrier(valuep, prev, next); } - -template -/* static */ js::HashNumber -js::MovableCellHasher::hash(const Lookup& l) -{ - if (!l) - return 0; - - // We have to access the zone from-any-thread here: a worker thread may be - // cloning a self-hosted object from the main-thread-runtime-owned self- - // hosting zone into the off-main-thread runtime. The zone's uid lock will - // protect against multiple workers doing this simultaneously. - MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || - l->zoneFromAnyThread()->isSelfHostingZone()); - - js::HashNumber hn; - if (!l->zoneFromAnyThread()->getHashCode(l, &hn)) - js::CrashAtUnhandlableOOM("failed to get a stable hash code"); - return hn; -} - -template -/* static */ bool -js::MovableCellHasher::match(const Key& k, const Lookup& l) -{ - // Return true if both are null or false if only one is null. - if (!k) - return !l; - if (!l) - return false; - - MOZ_ASSERT(k); - MOZ_ASSERT(l); - MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) || - l->zoneFromAnyThread()->isSelfHostingZone()); - - Zone* zone = k->zoneFromAnyThread(); - if (zone != l->zoneFromAnyThread()) - return false; - MOZ_ASSERT(zone->hasUniqueId(k)); - MOZ_ASSERT(zone->hasUniqueId(l)); - - // Since both already have a uid (from hash), the get is infallible. - uint64_t uidK, uidL; - MOZ_ALWAYS_TRUE(zone->getUniqueId(k, &uidK)); - MOZ_ALWAYS_TRUE(zone->getUniqueId(l, &uidL)); - return uidK == uidL; -} - -template struct js::MovableCellHasher; -template struct js::MovableCellHasher; -template struct js::MovableCellHasher; -template struct js::MovableCellHasher; diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index f80a918e446..ec8c00debaf 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -693,31 +693,6 @@ class ImmutableTenuredPtr const T* address() { return &value; } }; -// Provide hash codes for Cell kinds that may be relocated and, thus, not have -// a stable address to use as the base for a hash code. Instead of the address, -// this hasher uses Cell::getUniqueId to provide exact matches and as a base -// for generating hash codes. -// -// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr -// would not likely be a useful key, there are some cases where being able to -// hash a nullptr is useful, either on purpose or because of bugs: -// (1) existence checks where the key may happen to be null and (2) some -// aggregate Lookup kinds embed a JSObject* that is frequently null and do not -// null test before dispatching to the hasher. -template -struct MovableCellHasher -{ - static_assert(mozilla::IsBaseOf::Type>::value, - "MovableCellHasher's T must be a Cell type that may move"); - - using Key = T; - using Lookup = T; - - static HashNumber hash(const Lookup& l); - static bool match(const Key& k, const Lookup& l); - static void rekey(Key& k, const Key& newKey) { k = newKey; } -}; - /* Useful for hashtables with a HeapPtr as key. */ template struct HeapPtrHasher diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 588f9c02575..e9521ce09e3 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -655,11 +655,6 @@ class GCRuntime size_t maxMallocBytesAllocated() { return maxMallocBytes; } - uint64_t nextCellUniqueId() { - MOZ_ASSERT(nextCellUniqueId_ > 0); - return nextCellUniqueId_++; - } - public: // Internal public interface js::gc::State state() const { return incrementalState; } @@ -1018,9 +1013,6 @@ class GCRuntime size_t maxMallocBytes; - // An incrementing id used to assign unique ids to cells that require one. - uint64_t nextCellUniqueId_; - /* * Number of the committed arenas in all GC chunks including empty chunks. */ diff --git a/js/src/gc/Heap.h b/js/src/gc/Heap.h index a478ae8ee09..c0f02714921 100644 --- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -294,9 +294,6 @@ class TenuredCell : public Cell #endif }; -/* Cells are aligned to CellShift, so the largest tagged null pointer is: */ -const uintptr_t LargestTaggedNullCellPointer = (1 << CellShift) - 1; - /* * The mark bitmap has one bit per each GC cell. For multi-cell GC things this * wastes space but allows to avoid expensive devisions by thing's size when @@ -809,17 +806,6 @@ ArenaHeader::getThingSize() const */ struct ChunkTrailer { - /* Construct a Nursery ChunkTrailer. */ - ChunkTrailer(JSRuntime* rt, StoreBuffer* sb) - : location(gc::ChunkLocationBitNursery), storeBuffer(sb), runtime(rt) - {} - - /* Construct a Tenured heap ChunkTrailer. */ - explicit ChunkTrailer(JSRuntime* rt) - : location(gc::ChunkLocationBitTenuredHeap), storeBuffer(nullptr), runtime(rt) - {} - - public: /* The index the chunk in the nursery, or LocationTenuredHeap. */ uint32_t location; uint32_t padding; @@ -827,12 +813,11 @@ struct ChunkTrailer /* The store buffer for writes to things in this chunk or nullptr. */ StoreBuffer* storeBuffer; - /* This provides quick access to the runtime from absolutely anywhere. */ JSRuntime* runtime; }; -static_assert(sizeof(ChunkTrailer) == ChunkTrailerSize, - "ChunkTrailer size must match the API defined size."); +static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t), + "ChunkTrailer size is incorrect."); /* The chunk header (located at the end of the chunk to preserve arena alignment). */ struct ChunkInfo @@ -1021,16 +1006,13 @@ struct Chunk return reinterpret_cast(addr); } - static bool withinValidRange(uintptr_t addr) { + static bool withinArenasRange(uintptr_t addr) { uintptr_t offset = addr & ChunkMask; - return Chunk::fromAddress(addr)->isNurseryChunk() - ? offset < ChunkSize - sizeof(ChunkTrailer) - : offset < ArenasPerChunk * ArenaSize; + return offset < ArenasPerChunk * ArenaSize; } static size_t arenaIndex(uintptr_t addr) { - MOZ_ASSERT(!Chunk::fromAddress(addr)->isNurseryChunk()); - MOZ_ASSERT(withinValidRange(addr)); + MOZ_ASSERT(withinArenasRange(addr)); return (addr & ChunkMask) >> ArenaShift; } @@ -1048,10 +1030,6 @@ struct Chunk return info.numArenasFree != 0; } - bool isNurseryChunk() const { - return info.trailer.storeBuffer; - } - ArenaHeader* allocateArena(JSRuntime* rt, JS::Zone* zone, AllocKind kind, const AutoLockGC& lock); @@ -1151,7 +1129,7 @@ ArenaHeader::address() const uintptr_t addr = reinterpret_cast(this); MOZ_ASSERT(addr); MOZ_ASSERT(!(addr & ArenaMask)); - MOZ_ASSERT(Chunk::withinValidRange(addr)); + MOZ_ASSERT(Chunk::withinArenasRange(addr)); return addr; } @@ -1320,7 +1298,7 @@ Cell::address() const { uintptr_t addr = uintptr_t(this); MOZ_ASSERT(addr % CellSize == 0); - MOZ_ASSERT(Chunk::withinValidRange(addr)); + MOZ_ASSERT(Chunk::withinArenasRange(addr)); return addr; } diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 83d8eee794e..f11c621d04c 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -2117,13 +2117,7 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind if (src->is()) tenuredSize = srcSize = sizeof(NativeObject); - // Copy the Cell contents. js_memcpy(dst, src, srcSize); - - // Move any hash code attached to the object. - src->zone()->transferUniqueId(dst, src); - - // Move the slots and elements, if we need to. if (src->isNative()) { NativeObject* ndst = &dst->as(); NativeObject* nsrc = &src->as(); diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 96ffe0f49ad..dcc042234d2 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -425,7 +425,7 @@ ToMarkable(Cell* cell) MOZ_ALWAYS_INLINE bool IsNullTaggedPointer(void* p) { - return uintptr_t(p) <= LargestTaggedNullCellPointer; + return uintptr_t(p) < 32; } // HashKeyRef represents a reference to a HashMap key. This should normally diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index a5bcb6b9696..2c5b266fd9c 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -67,9 +67,6 @@ js::Nursery::init(uint32_t maxNurseryBytes) if (!mallocedBuffers.init()) return false; - if (!cellsWithUid_.init()) - return false; - void* heap = MapAlignedPages(nurserySize(), Alignment); if (!heap) return false; @@ -656,16 +653,6 @@ js::Nursery::waitBackgroundFreeEnd() void js::Nursery::sweep() { - /* Sweep unique id's in all in-use chunks. */ - for (CellsWithUniqueIdSet::Enum e(cellsWithUid_); !e.empty(); e.popFront()) { - JSObject* obj = static_cast(e.front()); - if (!IsForwarded(obj)) - obj->zone()->removeUniqueId(obj); - else - MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj))); - } - cellsWithUid_.clear(); - #ifdef JS_GC_ZEAL /* Poison the nursery contents so touching a freed object will crash. */ JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize()); @@ -683,8 +670,10 @@ js::Nursery::sweep() { #ifdef JS_CRASH_DIAGNOSTICS JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start()); - for (int i = 0; i < numActiveChunks_; ++i) - initChunk(i); + for (int i = 0; i < numActiveChunks_; ++i) { + chunk(i).trailer.location = gc::ChunkLocationBitNursery; + chunk(i).trailer.runtime = runtime(); + } #endif setCurrentChunk(0); } diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index a13468e0602..267578f25c1 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -183,14 +183,6 @@ class Nursery void waitBackgroundFreeEnd(); - bool addedUniqueIdToCell(gc::Cell* cell) { - if (!IsInsideNursery(cell) || !isEnabled()) - return true; - MOZ_ASSERT(cellsWithUid_.initialized()); - MOZ_ASSERT(!cellsWithUid_.has(cell)); - return cellsWithUid_.put(cell); - } - size_t sizeOfHeapCommitted() const { return numActiveChunks_ * gc::ChunkSize; } @@ -274,21 +266,6 @@ class Nursery typedef HashMap, SystemAllocPolicy> ForwardedBufferMap; ForwardedBufferMap forwardedBuffers; - /* - * When we assign a unique id to cell in the nursery, that almost always - * means that the cell will be in a hash table, and thus, held live, - * automatically moving the uid from the nursery to its new home in - * tenured. It is possible, if rare, for an object that acquired a uid to - * be dead before the next collection, in which case we need to know to - * remove it when we sweep. - * - * Note: we store the pointers as Cell* here, resulting in an ugly cast in - * sweep. This is because this structure is used to help implement - * stable object hashing and we have to break the cycle somehow. - */ - using CellsWithUniqueIdSet = HashSet, SystemAllocPolicy>; - CellsWithUniqueIdSet cellsWithUid_; - /* The maximum number of bytes allowed to reside in nursery buffers. */ static const size_t MaxNurseryBufferSize = 1024; @@ -310,8 +287,10 @@ class Nursery } MOZ_ALWAYS_INLINE void initChunk(int chunkno) { - gc::StoreBuffer* sb = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr(); - new (&chunk(chunkno).trailer) gc::ChunkTrailer(runtime(), sb); + NurseryChunkLayout& c = chunk(chunkno); + c.trailer.storeBuffer = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr(); + c.trailer.location = gc::ChunkLocationBitNursery; + c.trailer.runtime = runtime(); } MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) { diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 7472b2e075d..67b0986323c 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -24,7 +24,6 @@ #include "js/HashTable.h" #include "vm/Debugger.h" #include "vm/JSONParser.h" -#include "vm/WeakMapObject.h" #include "jsgcinlines.h" #include "jsobjinlines.h" diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 5821ed7ce7f..b9ee0efd5fa 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -66,7 +66,7 @@ Zone::~Zone() bool Zone::init(bool isSystemArg) { isSystem = isSystemArg; - return uniqueIds_.init() && gcZoneGroupEdges.init(); + return gcZoneGroupEdges.init(); } void @@ -156,6 +156,7 @@ Zone::logPromotionsToTenured() awaitingTenureLogging.clear(); } + void Zone::sweepBreakpoints(FreeOp* fop) { @@ -256,15 +257,6 @@ Zone::discardJitCode(FreeOp* fop) } } -#ifdef JSGC_HASH_TABLE_CHECKS -void -JS::Zone::checkUniqueIdTableAfterMovingGC() -{ - for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) - js::gc::CheckGCThingAfterMovingGC(e.front().key()); -} -#endif - uint64_t Zone::gcNumber() { diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 07f71c38a61..74c3a977fec 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -13,12 +13,9 @@ #include "jscntxt.h" -#include "ds/SpinLock.h" -#include "ds/SplayTree.h" #include "gc/FindSCCs.h" #include "gc/GCRuntime.h" #include "js/TracingAPI.h" -#include "vm/MallocProvider.h" #include "vm/TypeInference.h" namespace js { @@ -61,11 +58,6 @@ class ZoneHeapThreshold const GCSchedulingTunables& tunables); }; -// Maps a Cell* to a unique, 64bit id. -using UniqueIdMap = HashMap, SystemAllocPolicy>; - -extern uint64_t NextCellUniqueId(JSRuntime* rt); - } // namespace gc } // namespace js @@ -250,7 +242,6 @@ struct Zone : public JS::shadow::Zone, LogTenurePromotionQueue awaitingTenureLogging; void sweepBreakpoints(js::FreeOp* fop); - void sweepUniqueIds(js::FreeOp* fop); void sweepWeakMaps(); void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC); @@ -260,14 +251,6 @@ struct Zone : public JS::shadow::Zone, return isOnList(); } - // Side map for storing a unique ids for cells, independent of address. - js::gc::UniqueIdMap uniqueIds_; - - // Guards the uniqueIds_ map, which is accessed concurrently from - // off-thread parsing and the main thread. This uses a spinlock, since it - // is normally uncontended. - js::SpinLock uniqueIdsLock_; - public: bool hasDebuggers() const { return debuggers && debuggers->length(); } DebuggerVector* getDebuggers() const { return debuggers; } @@ -340,68 +323,6 @@ struct Zone : public JS::shadow::Zone, mozilla::DebugOnly gcLastZoneGroupIndex; - // Creates a HashNumber based on getUniqueId. Returns false on OOM. - bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) { - uint64_t uid; - if (!getUniqueId(cell, &uid)) - return false; - *hashp = js::HashNumber(uid >> 32) ^ js::HashNumber(uid & 0xFFFFFFFF); - return true; - } - - // Puts an existing UID in |uidp|, or creates a new UID for this Cell and - // puts that into |uidp|. Returns false on OOM. - bool getUniqueId(js::gc::Cell* cell, uint64_t* uidp) { - MOZ_ASSERT(uidp); - js::AutoSpinLock lock(uniqueIdsLock_); - - // Get an existing uid, if one has been set. - auto p = uniqueIds_.lookupForAdd(cell); - if (p) { - *uidp = p->value(); - return true; - } - - // Set a new uid on the cell. - *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread()); - if (!uniqueIds_.add(p, cell, *uidp)) - return false; - - // If the cell was in the nursery, hopefully unlikely, then we need to - // tell the nursery about it so that it can sweep the uid if the thing - // does not get tenured. - if (!runtimeFromAnyThread()->gc.nursery.addedUniqueIdToCell(cell)) - js::CrashAtUnhandlableOOM("failed to allocate tracking data for a nursery uid"); - return true; - } - - // Return true if this cell has a UID associated with it. - bool hasUniqueId(js::gc::Cell* cell) { - js::AutoSpinLock lock(uniqueIdsLock_); - return uniqueIds_.has(cell); - } - - // Transfer an id from another cell. This must only be called on behalf of a - // moving GC. This method is infallible. - void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) { - MOZ_ASSERT(src != tgt); - MOZ_ASSERT(!IsInsideNursery(tgt)); - MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromMainThread())); - js::AutoSpinLock lock(uniqueIdsLock_); - uniqueIds_.rekeyIfMoved(src, tgt); - } - - // Remove any unique id associated with this Cell. - void removeUniqueId(js::gc::Cell* cell) { - js::AutoSpinLock lock(uniqueIdsLock_); - uniqueIds_.remove(cell); - } - -#ifdef JSGC_HASH_TABLE_CHECKS - // Assert that the UniqueId table has been redirected successfully. - void checkUniqueIdTableAfterMovingGC(); -#endif - private: js::jit::JitZone* jitZone_; diff --git a/js/src/jit-test/tests/arrays/dense-from-sparse.js b/js/src/jit-test/manual-tests/dense-to-sparse.js similarity index 100% rename from js/src/jit-test/tests/arrays/dense-from-sparse.js rename to js/src/jit-test/manual-tests/dense-to-sparse.js diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index e81927c89c9..6bb73ef1605 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -300,20 +300,19 @@ MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op, { if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op)) return false; + ALUOp interop = getDestVariant(op); Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value); if (both.fst.invalid) return false; - ScratchRegisterScope scratch(asMasm()); - // For the most part, there is no good reason to set the condition codes for // the first instruction. We can do better things if the second instruction // doesn't have a dest, such as check for overflow by doing first operation // don't do second operation if first operation overflowed. This preserves // the overflow condition code. Unfortunately, it is horribly brittle. - as_alu(scratch, src1, Operand2(both.fst), interop, LeaveCC, c); - as_alu(dest, scratch, Operand2(both.snd), op, s, c); + as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c); + as_alu(dest, dest, Operand2(both.snd), op, s, c); return true; } @@ -2015,15 +2014,13 @@ MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest) Register base = src.base; uint32_t scale = Imm32::ShiftOf(src.scale).value; - ScratchRegisterScope scratch(asMasm()); - - if (src.offset != 0) { - ma_mov(base, scratch); - base = scratch; - ma_add(base, Imm32(src.offset), base); + if (src.offset == 0) { + ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); + } else { + ScratchRegisterScope scratch(asMasm()); + ma_add(base, Imm32(src.offset), scratch); + ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest); } - ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest); - } void diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 3c4e55e74cb..716ef20bdc3 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -30,9 +30,22 @@ MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = { 0x4530000000000000LL }; +static const double TO_DOUBLE_HIGH_SCALE = 0x100000000; + void MacroAssemblerX86::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) { + // SUBPD needs SSE2, HADDPD needs SSE3. + if (!HasSSE3()) { + convertUInt32ToDouble(src.high, dest); + movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), temp); + loadDouble(Address(temp, 0), ScratchDoubleReg); + mulDouble(ScratchDoubleReg, dest); + convertUInt32ToDouble(src.low, ScratchDoubleReg); + addDouble(ScratchDoubleReg, dest); + return; + } + // Following operation uses entire 128-bit of dest XMM register. // Currently higher 64-bit is free when we have access to lower 64-bit. MOZ_ASSERT(dest.size() == 8); diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 6c2b3a49cd8..b7f913a39fc 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -43,7 +43,6 @@ UNIFIED_SOURCES += [ 'testGCMarking.cpp', 'testGCOutOfMemory.cpp', 'testGCStoreBufferRemoval.cpp', - 'testGCUniqueId.cpp', 'testGetPropertyDescriptor.cpp', 'testHashTable.cpp', 'testIndexToString.cpp', diff --git a/js/src/jsapi-tests/testGCUniqueId.cpp b/js/src/jsapi-tests/testGCUniqueId.cpp deleted file mode 100644 index 6efc8167f73..00000000000 --- a/js/src/jsapi-tests/testGCUniqueId.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- 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 "gc/GCInternals.h" -#include "gc/Zone.h" - -static void -MinimizeHeap(JSRuntime* rt) -{ - // The second collection is to force us to wait for the background - // sweeping that the first GC started to finish. - JS_GC(rt); - JS_GC(rt); - js::gc::AutoFinishGC finish(rt); -} - -BEGIN_TEST(testGCUID) -{ -#ifdef JS_GC_ZEAL - AutoLeaveZeal nozeal(cx); -#endif /* JS_GC_ZEAL */ - - uint64_t uid = 0; - uint64_t tmp = 0; - - // Ensure the heap is as minimal as it can get. - MinimizeHeap(rt); - - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); - uintptr_t nurseryAddr = uintptr_t(obj.get()); - CHECK(obj); - CHECK(js::gc::IsInsideNursery(obj)); - - // Do not start with an ID. - CHECK(!obj->zone()->hasUniqueId(obj)); - - // Ensure we can get a new UID. - CHECK(obj->zone()->getUniqueId(obj, &uid)); - CHECK(uid > js::gc::LargestTaggedNullCellPointer); - - // We should now have an id. - CHECK(obj->zone()->hasUniqueId(obj)); - - // Calling again should get us the same thing. - CHECK(obj->zone()->getUniqueId(obj, &tmp)); - CHECK(uid == tmp); - - // Tenure the thing and check that the UID moved with it. - MinimizeHeap(rt); - uintptr_t tenuredAddr = uintptr_t(obj.get()); - CHECK(tenuredAddr != nurseryAddr); - CHECK(!js::gc::IsInsideNursery(obj)); - CHECK(obj->zone()->hasUniqueId(obj)); - CHECK(obj->zone()->getUniqueId(obj, &tmp)); - CHECK(uid == tmp); - - // Allocate a new nursery thing in the same location and check that we - // removed the prior uid that was attached to the location. - obj = JS_NewPlainObject(cx); - CHECK(obj); - CHECK(uintptr_t(obj.get()) == nurseryAddr); - CHECK(!obj->zone()->hasUniqueId(obj)); - - // Try to get another tenured object in the same location and check that - // the uid was removed correctly. - obj = nullptr; - MinimizeHeap(rt); - obj = JS_NewPlainObject(cx); - MinimizeHeap(rt); - CHECK(uintptr_t(obj.get()) == tenuredAddr); - CHECK(!obj->zone()->hasUniqueId(obj)); - CHECK(obj->zone()->getUniqueId(obj, &tmp)); - CHECK(uid != tmp); - uid = tmp; - - // Allocate a few arenas worth of objects to ensure we get some compaction. - const static size_t N = 2049; - using ObjectVector = js::TraceableVector; - JS::Rooted vec(cx, ObjectVector(cx)); - for (size_t i = 0; i < N; ++i) { - obj = JS_NewPlainObject(cx); - CHECK(obj); - CHECK(vec.append(obj)); - } - - // Transfer our vector to tenured if it isn't there already. - MinimizeHeap(rt); - - // Tear holes in the heap by unrooting the even objects and collecting. - JS::Rooted vec2(cx, ObjectVector(cx)); - for (size_t i = 0; i < N; ++i) { - if (i % 2 == 1) - vec2.append(vec[i]); - } - vec.clear(); - MinimizeHeap(rt); - - // Grab the last object in the vector as our object of interest. - obj = vec2.back(); - CHECK(obj); - tenuredAddr = uintptr_t(obj.get()); - CHECK(obj->zone()->getUniqueId(obj, &uid)); - - // Force a compaction to move the object and check that the uid moved to - // the new tenured heap location. - JS::PrepareForFullGC(rt); - JS::GCForReason(rt, GC_SHRINK, JS::gcreason::API); - MinimizeHeap(rt); - CHECK(uintptr_t(obj.get()) != tenuredAddr); - CHECK(obj->zone()->hasUniqueId(obj)); - CHECK(obj->zone()->getUniqueId(obj, &tmp)); - CHECK(uid == tmp); - - return true; -} -END_TEST(testGCUID) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 7fa22e95fa0..9a19d5485e0 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -84,7 +84,6 @@ #include "vm/StringBuffer.h" #include "vm/Symbol.h" #include "vm/TypedArrayCommon.h" -#include "vm/WeakMapObject.h" #include "vm/WrapperObject.h" #include "vm/Xdr.h" diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index cf35a0d3fd8..03ee24dcd20 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -24,7 +24,6 @@ #include "proxy/DeadObjectProxy.h" #include "vm/ArgumentsObject.h" #include "vm/Time.h" -#include "vm/WeakMapObject.h" #include "vm/WrapperObject.h" #include "jsobjinlines.h" diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 71204205abf..2d785f4141a 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -833,7 +833,9 @@ Chunk::init(JSRuntime* rt) /* Initialize the chunk info. */ info.init(); - new (&info.trailer) ChunkTrailer(rt); + info.trailer.storeBuffer = nullptr; + info.trailer.location = ChunkLocationBitTenuredHeap; + info.trailer.runtime = rt; /* The rest of info fields are initialized in pickChunk. */ } @@ -1108,7 +1110,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) : usage(nullptr), mMemProfiler(rt), maxMallocBytes(0), - nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers. numArenasFreeCommitted(0), verifyPreData(nullptr), chunkAllocationSinceLastGC(false), @@ -2041,9 +2042,6 @@ RelocateCell(Zone* zone, TenuredCell* src, AllocKind thingKind, size_t thingSize // Copy source cell contents to destination. memcpy(dst, src, thingSize); - // Move any uid attached to the object. - src->zone()->transferUniqueId(dst, src); - if (IsObjectAllocKind(thingKind)) { JSObject* srcObj = static_cast(static_cast(src)); JSObject* dstObj = static_cast(static_cast(dst)); @@ -2175,6 +2173,7 @@ bool ArenaLists::relocateArenas(Zone* zone, ArenaHeader*& relocatedListOut, JS::gcreason::Reason reason, SliceBudget& sliceBudget, gcstats::Statistics& stats) { + // This is only called from the main thread while we are doing a GC, so // there is no need to lock. MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_)); @@ -3589,29 +3588,6 @@ GCRuntime::shouldReleaseObservedTypes() return releaseTypes; } -struct IsAboutToBeFinalizedFunctor { - template bool operator()(Cell** t) { - mozilla::DebugOnly prior = *t; - bool result = IsAboutToBeFinalizedUnbarriered(reinterpret_cast(t)); - // Sweep should not have to deal with moved pointers, since moving GC - // handles updating the UID table manually. - MOZ_ASSERT(*t == prior); - return result; - } -}; - -void -JS::Zone::sweepUniqueIds(js::FreeOp* fop) -{ - for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) { - if (DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), e.front().key()->getTraceKind(), - &e.front().mutableKey())) - { - e.removeFront(); - } - } -} - /* * It's simpler if we preserve the invariant that every zone has at least one * compartment. If we know we're deleting the entire zone, then @@ -5082,12 +5058,6 @@ GCRuntime::beginSweepingZoneGroup() zone->sweepBreakpoints(&fop); } } - - { - gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT); - for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) - zone->sweepUniqueIds(&fop); - } } if (sweepingAtoms) { @@ -7180,9 +7150,6 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) * Check that internal hash tables no longer have any pointers to things * that have been moved. */ - for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - zone->checkUniqueIdTableAfterMovingGC(); - } for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { c->objectGroups.checkTablesAfterMovingGC(); c->checkInitialShapesTableAfterMovingGC(); @@ -7408,12 +7375,6 @@ JS::IsGenerationalGCEnabled(JSRuntime* rt) return rt->gc.isGenerationalGCEnabled(); } -uint64_t -js::gc::NextCellUniqueId(JSRuntime* rt) -{ - return rt->gc.nextCellUniqueId(); -} - namespace js { namespace gc { namespace MemInfo { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 8b81d5acf6a..f4fba5d4aa1 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -76,7 +76,6 @@ JSObject::finalize(js::FreeOp* fop) MOZ_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime())); } #endif - const js::Class* clasp = getClass(); if (clasp->finalize) clasp->finalize(fop, this); diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index bcfaf450c9f..9aed71a0913 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -16,18 +16,12 @@ #include "js/GCAPI.h" #include "vm/GlobalObject.h" -#include "vm/WeakMapObject.h" #include "jsobjinlines.h" -#include "vm/Interpreter-inl.h" -#include "vm/NativeObject-inl.h" - using namespace js; using namespace js::gc; -using mozilla::UniquePtr; - WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone) : memberOf(memOf), zone(zone), @@ -213,473 +207,6 @@ ObjectValueMap::findZoneEdges() return true; } -MOZ_ALWAYS_INLINE bool -IsWeakMap(HandleValue v) -{ - return v.isObject() && v.toObject().is(); -} - -MOZ_ALWAYS_INLINE bool -WeakMap_has_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsWeakMap(args.thisv())); - - if (!args.get(0).isObject()) { - args.rval().setBoolean(false); - return true; - } - - if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { - JSObject* key = &args[0].toObject(); - if (map->has(key)) { - args.rval().setBoolean(true); - return true; - } - } - - args.rval().setBoolean(false); - return true; -} - -bool -js::WeakMap_has(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - -MOZ_ALWAYS_INLINE bool -WeakMap_clear_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsWeakMap(args.thisv())); - - // We can't js_delete the weakmap because the data gathered during GC is - // used by the Cycle Collector. - if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) - map->clear(); - - args.rval().setUndefined(); - return true; -} - -bool -js::WeakMap_clear(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - -MOZ_ALWAYS_INLINE bool -WeakMap_get_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsWeakMap(args.thisv())); - - if (!args.get(0).isObject()) { - args.rval().setUndefined(); - return true; - } - - if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { - JSObject* key = &args[0].toObject(); - if (ObjectValueMap::Ptr ptr = map->lookup(key)) { - args.rval().set(ptr->value()); - return true; - } - } - - args.rval().setUndefined(); - return true; -} - -bool -js::WeakMap_get(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - -MOZ_ALWAYS_INLINE bool -WeakMap_delete_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsWeakMap(args.thisv())); - - if (!args.get(0).isObject()) { - args.rval().setBoolean(false); - return true; - } - - if (ObjectValueMap* map = args.thisv().toObject().as().getMap()) { - JSObject* key = &args[0].toObject(); - if (ObjectValueMap::Ptr ptr = map->lookup(key)) { - map->remove(ptr); - args.rval().setBoolean(true); - return true; - } - } - - args.rval().setBoolean(false); - return true; -} - -bool -js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - -static bool -TryPreserveReflector(JSContext* cx, HandleObject obj) -{ - if (obj->getClass()->ext.isWrappedNative || - (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) || - (obj->is() && - obj->as().handler()->family() == GetDOMProxyHandlerFamily())) - { - MOZ_ASSERT(cx->runtime()->preserveWrapperCallback); - if (!cx->runtime()->preserveWrapperCallback(cx, obj)) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY); - return false; - } - } - return true; -} - -static inline void -WeakMapPostWriteBarrier(JSRuntime* rt, ObjectValueMap* weakMap, JSObject* key) -{ - if (key && IsInsideNursery(key)) - rt->gc.storeBuffer.putGeneric(gc::HashKeyRef(weakMap, key)); -} - -static MOZ_ALWAYS_INLINE bool -SetWeakMapEntryInternal(JSContext* cx, Handle mapObj, - HandleObject key, HandleValue value) -{ - ObjectValueMap* map = mapObj->getMap(); - if (!map) { - AutoInitGCManagedObject newMap( - cx->make_unique(cx, mapObj.get())); - if (!newMap) - return false; - if (!newMap->init()) { - JS_ReportOutOfMemory(cx); - return false; - } - map = newMap.release(); - mapObj->setPrivate(map); - } - - // Preserve wrapped native keys to prevent wrapper optimization. - if (!TryPreserveReflector(cx, key)) - return false; - - if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) { - RootedObject delegate(cx, op(key)); - if (delegate && !TryPreserveReflector(cx, delegate)) - return false; - } - - MOZ_ASSERT(key->compartment() == mapObj->compartment()); - MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment()); - if (!map->put(key, value)) { - JS_ReportOutOfMemory(cx); - return false; - } - WeakMapPostWriteBarrier(cx->runtime(), map, key.get()); - return true; -} - -MOZ_ALWAYS_INLINE bool -WeakMap_set_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsWeakMap(args.thisv())); - - if (!args.get(0).isObject()) { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr); - if (!bytes) - return false; - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); - return false; - } - - RootedObject key(cx, &args[0].toObject()); - Rooted thisObj(cx, &args.thisv().toObject()); - Rooted map(cx, &thisObj->as()); - - if (!SetWeakMapEntryInternal(cx, map, key, args.get(1))) - return false; - args.rval().set(args.thisv()); - return true; -} - -bool -js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); -} - -JS_FRIEND_API(bool) -JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret) -{ - RootedObject obj(cx, objArg); - obj = UncheckedUnwrap(obj); - if (!obj || !obj->is()) { - ret.set(nullptr); - return true; - } - RootedObject arr(cx, NewDenseEmptyArray(cx)); - if (!arr) - return false; - ObjectValueMap* map = obj->as().getMap(); - if (map) { - // Prevent GC from mutating the weakmap while iterating. - AutoSuppressGC suppress(cx); - for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) { - JS::ExposeObjectToActiveJS(r.front().key()); - RootedObject key(cx, r.front().key()); - if (!cx->compartment()->wrap(cx, &key)) - return false; - if (!NewbornArrayPush(cx, arr, ObjectValue(*key))) - return false; - } - } - ret.set(arr); - return true; -} - -static void -WeakMap_mark(JSTracer* trc, JSObject* obj) -{ - if (ObjectValueMap* map = obj->as().getMap()) - map->trace(trc); -} - -static void -WeakMap_finalize(FreeOp* fop, JSObject* obj) -{ - if (ObjectValueMap* map = obj->as().getMap()) { -#ifdef DEBUG - map->~ObjectValueMap(); - memset(static_cast(map), 0xdc, sizeof(*map)); - fop->free_(map); -#else - fop->delete_(map); -#endif - } -} - -JS_PUBLIC_API(JSObject*) -JS::NewWeakMapObject(JSContext* cx) -{ - return NewBuiltinClassInstance(cx, &WeakMapObject::class_); -} - -JS_PUBLIC_API(bool) -JS::IsWeakMapObject(JSObject* obj) -{ - return obj->is(); -} - -JS_PUBLIC_API(bool) -JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key, - MutableHandleValue rval) -{ - CHECK_REQUEST(cx); - assertSameCompartment(cx, key); - rval.setUndefined(); - ObjectValueMap* map = mapObj->as().getMap(); - if (!map) - return true; - if (ObjectValueMap::Ptr ptr = map->lookup(key)) { - // Read barrier to prevent an incorrectly gray value from escaping the - // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp - ExposeValueToActiveJS(ptr->value().get()); - rval.set(ptr->value()); - } - return true; -} - -JS_PUBLIC_API(bool) -JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key, - HandleValue val) -{ - CHECK_REQUEST(cx); - assertSameCompartment(cx, key, val); - Rooted rootedMap(cx, &mapObj->as()); - return SetWeakMapEntryInternal(cx, rootedMap, key, val); -} - -static bool -WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1. - if (!ThrowIfNotConstructing(cx, args, "WeakMap")) - return false; - - RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_)); - if (!obj) - return false; - - // Steps 5-6, 11. - if (!args.get(0).isNullOrUndefined()) { - // Steps 7a-b. - RootedValue adderVal(cx); - if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal)) - return false; - - // Step 7c. - if (!IsCallable(adderVal)) - return ReportIsNotFunction(cx, adderVal); - - bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set); - RootedValue mapVal(cx, ObjectValue(*obj)); - FastInvokeGuard fig(cx, adderVal); - InvokeArgs& args2 = fig.args(); - - // Steps 7d-e. - JS::ForOfIterator iter(cx); - if (!iter.init(args[0])) - return false; - - RootedValue pairVal(cx); - RootedObject pairObject(cx); - RootedValue keyVal(cx); - RootedObject keyObject(cx); - RootedValue val(cx); - while (true) { - // Steps 12a-e. - bool done; - if (!iter.next(&pairVal, &done)) - return false; - if (done) - break; - - // Step 12f. - if (!pairVal.isObject()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, - JSMSG_INVALID_MAP_ITERABLE, "WeakMap"); - return false; - } - - pairObject = &pairVal.toObject(); - if (!pairObject) - return false; - - // Steps 12g-h. - if (!GetElement(cx, pairObject, pairObject, 0, &keyVal)) - return false; - - // Steps 12i-j. - if (!GetElement(cx, pairObject, pairObject, 1, &val)) - return false; - - // Steps 12k-l. - if (isOriginalAdder) { - if (keyVal.isPrimitive()) { - UniquePtr bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr); - if (!bytes) - return false; - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); - return false; - } - - keyObject = &keyVal.toObject(); - if (!SetWeakMapEntry(cx, obj, keyObject, val)) - return false; - } else { - if (!args2.init(2)) - return false; - - args2.setCallee(adderVal); - args2.setThis(mapVal); - args2[0].set(keyVal); - args2[1].set(val); - - if (!fig.invoke(cx)) - return false; - } - } - } - - args.rval().setObject(*obj); - return true; -} - -const Class WeakMapObject::class_ = { - "WeakMap", - JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* convert */ - WeakMap_finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - WeakMap_mark -}; - -static const JSFunctionSpec weak_map_methods[] = { - JS_FN("has", WeakMap_has, 1, 0), - JS_FN("get", WeakMap_get, 1, 0), - JS_FN("delete", WeakMap_delete, 1, 0), - JS_FN("set", WeakMap_set, 2, 0), - JS_FN("clear", WeakMap_clear, 0, 0), - JS_FS_END -}; - -static JSObject* -InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers) -{ - MOZ_ASSERT(obj->isNative()); - - Rooted global(cx, &obj->as()); - - RootedPlainObject proto(cx, NewBuiltinClassInstance(cx)); - if (!proto) - return nullptr; - - RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct, - cx->names().WeakMap, 0)); - if (!ctor) - return nullptr; - - if (!LinkConstructorAndPrototype(cx, ctor, proto)) - return nullptr; - - if (defineMembers) { - if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods)) - return nullptr; - } - - if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto)) - return nullptr; - return proto; -} - -JSObject* -js::InitWeakMapClass(JSContext* cx, HandleObject obj) -{ - return InitWeakMapClass(cx, obj, true); -} - -JSObject* -js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj) -{ - return InitWeakMapClass(cx, obj, false); -} - ObjectWeakMap::ObjectWeakMap(JSContext* cx) : map(cx, nullptr) {} diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index aff5b262c7e..fa72ddbaf6b 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -397,6 +397,44 @@ WeakMap_clear(JSContext* cx, unsigned argc, Value* vp); extern JSObject* InitWeakMapClass(JSContext* cx, HandleObject obj); + +class ObjectValueMap : public WeakMap +{ + public: + ObjectValueMap(JSContext* cx, JSObject* obj) + : WeakMap(cx, obj) {} + + virtual bool findZoneEdges(); +}; + + +// Generic weak map for mapping objects to other objects. +class ObjectWeakMap +{ + private: + ObjectValueMap map; + typedef gc::HashKeyRef StoreBufferRef; + + public: + explicit ObjectWeakMap(JSContext* cx); + bool init(); + ~ObjectWeakMap(); + + JSObject* lookup(const JSObject* obj); + bool add(JSContext* cx, JSObject* obj, JSObject* target); + void clear(); + + void trace(JSTracer* trc); + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { + return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); + } + +#ifdef JSGC_HASH_TABLE_CHECKS + void checkAfterMovingGC(); +#endif +}; + } /* namespace js */ #endif /* jsweakmap_h */ diff --git a/js/src/moz.build b/js/src/moz.build index 486a095fe32..7f2ea6ce811 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -158,6 +158,7 @@ UNIFIED_SOURCES += [ 'builtin/SymbolObject.cpp', 'builtin/TestingFunctions.cpp', 'builtin/TypedObject.cpp', + 'builtin/WeakMapObject.cpp', 'builtin/WeakSetObject.cpp', 'devtools/sharkctl.cpp', 'ds/LifoAlloc.cpp', diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 3abcc76dc40..08c28fab7a2 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -27,13 +27,13 @@ #include "builtin/SIMD.h" #include "builtin/SymbolObject.h" #include "builtin/TypedObject.h" +#include "builtin/WeakMapObject.h" #include "builtin/WeakSetObject.h" #include "vm/HelperThreads.h" #include "vm/PIC.h" #include "vm/RegExpStatics.h" #include "vm/RegExpStaticsObject.h" #include "vm/StopIterationObject.h" -#include "vm/WeakMapObject.h" #include "jscompartmentinlines.h" #include "jsobjinlines.h" diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index a540f5d2091..1f8ba7a14db 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -18,7 +18,6 @@ #include "vm/GlobalObject.h" #include "vm/ProxyObject.h" #include "vm/Shape.h" -#include "vm/WeakMapObject.h" #include "vm/Xdr.h" #include "jsatominlines.h" diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index ca07da53189..b4828df9d0a 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -14,7 +14,6 @@ #include "gc/Barrier.h" #include "vm/ArgumentsObject.h" #include "vm/ProxyObject.h" -#include "vm/WeakMapObject.h" namespace js { diff --git a/js/src/vm/WeakMapObject.h b/js/src/vm/WeakMapObject.h deleted file mode 100644 index abeffd973c7..00000000000 --- a/js/src/vm/WeakMapObject.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- 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/. */ - -#ifndef vm_WeakMapObject_h -#define vm_WeakMapObject_h - -#include "jsobj.h" -#include "jsweakmap.h" - -namespace js { - -class ObjectValueMap : public WeakMap -{ - public: - ObjectValueMap(JSContext* cx, JSObject* obj) - : WeakMap(cx, obj) {} - - virtual bool findZoneEdges(); -}; - -class WeakMapObject : public NativeObject -{ - public: - static const Class class_; - - ObjectValueMap* getMap() { return static_cast(getPrivate()); } -}; - -// Generic weak map for mapping objects to other objects. -class ObjectWeakMap -{ - private: - ObjectValueMap map; - typedef gc::HashKeyRef StoreBufferRef; - - public: - explicit ObjectWeakMap(JSContext* cx); - bool init(); - ~ObjectWeakMap(); - - JSObject* lookup(const JSObject* obj); - bool add(JSContext* cx, JSObject* obj, JSObject* target); - void clear(); - - void trace(JSTracer* trc); - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); - size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { - return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); - } - -#ifdef JSGC_HASH_TABLE_CHECKS - void checkAfterMovingGC(); -#endif -}; - -} // namespace js - -#endif /* vm_WeakMapObject_h */ diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 69c1f83a9e9..30c5a35c5a8 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1042,7 +1042,7 @@ public: MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame()); mContainerAnimatedGeometryRoot = isAtRoot ? mContainerReferenceFrame - : aContainerItem->AnimatedGeometryRoot(); + : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder); MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(), mContainerAnimatedGeometryRoot)); NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(mBuilder), @@ -2100,7 +2100,8 @@ ContainerState::GetLayerCreationHint(const nsIFrame* aAnimatedGeometryRoot) nsIFrame* fParent; for (const nsIFrame* f = aAnimatedGeometryRoot; f != mContainerAnimatedGeometryRoot; - f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, fParent)) { + f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, + fParent, mContainerAnimatedGeometryRoot)) { fParent = nsLayoutUtils::GetCrossDocParentFrame(f); if (!fParent) { break; @@ -2781,7 +2782,7 @@ PaintedLayerDataTree::GetParentAnimatedGeometryRoot(const nsIFrame* aAnimatedGeo } nsIFrame* agr = Builder()->FindAnimatedGeometryRootFor( - const_cast(aAnimatedGeometryRoot)); + const_cast(aAnimatedGeometryRoot), Builder()->RootReferenceFrame()); MOZ_ASSERT_IF(agr, nsLayoutUtils::IsAncestorFrameCrossDoc(Builder()->RootReferenceFrame(), agr)); if (agr != aAnimatedGeometryRoot) { return agr; @@ -2792,7 +2793,7 @@ PaintedLayerDataTree::GetParentAnimatedGeometryRoot(const nsIFrame* aAnimatedGeo if (!parent) { return nullptr; } - return Builder()->FindAnimatedGeometryRootFor(parent); + return Builder()->FindAnimatedGeometryRootFor(parent, Builder()->RootReferenceFrame()); } void @@ -3694,7 +3695,8 @@ ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, // Try using the actual active scrolled root of the backmost item, as that // should result in the least invalidation when scrolling. - *aAnimatedGeometryRoot = item->AnimatedGeometryRoot(); + *aAnimatedGeometryRoot = + nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); return true; } return false; @@ -3769,7 +3771,7 @@ GetScrollClipIntersection(nsDisplayListBuilder* aBuilder, const nsIFrame* aAnima for (const nsIFrame* f = aAnimatedGeometryRoot; f != aStopAtAnimatedGeometryRoot; f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder, - fParent)) { + fParent, aStopAtAnimatedGeometryRoot)) { fParent = nsLayoutUtils::GetCrossDocParentFrame(f); if (!fParent) { // This means aStopAtAnimatedGeometryRoot was not an ancestor @@ -3870,7 +3872,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) bool forceInactive; const nsIFrame* animatedGeometryRoot; const nsIFrame* animatedGeometryRootForScrollMetadata = nullptr; - const nsIFrame* realAnimatedGeometryRootOfItem = item->AnimatedGeometryRoot(); + const nsIFrame* realAnimatedGeometryRootOfItem = + nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); if (mFlattenToSingleLayer) { forceInactive = true; animatedGeometryRoot = lastAnimatedGeometryRoot; @@ -3882,7 +3885,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) // take ShouldFixToViewport() into account, so it will return something different // for fixed background items. animatedGeometryRootForScrollMetadata = nsLayoutUtils::GetAnimatedGeometryRootForFrame( - mBuilder, item->Frame()); + mBuilder, item->Frame(), item->ReferenceFrame()); } else { // For inactive layer subtrees, splitting content into PaintedLayers // based on animated geometry roots is pointless. It's more efficient @@ -4479,6 +4482,15 @@ FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData* aLayerData, layerBuilder->WillEndTransaction(); tempManager->AbortTransaction(); +#ifdef MOZ_DUMP_PAINTING + if (gfxUtils::DumpDisplayList() || gfxUtils::sDumpPainting) { + fprintf_stderr(gfxUtils::sDumpPaintFile, "Basic layer tree for painting contents of display item %s(%p):\n", aItem->Name(), aItem->Frame()); + std::stringstream stream; + tempManager->Dump(stream, "", gfxUtils::sDumpPaintingToFile); + fprint_stderr(gfxUtils::sDumpPaintFile, stream); // not a typo, fprint_stderr declared in LayersLogging.h + } +#endif + nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForPaintedLayer(layer); props->MoveBy(-offset); nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr); @@ -4693,7 +4705,8 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) nsIFrame* fParent; for (const nsIFrame* f = aEntry->mAnimatedGeometryRootForScrollMetadata; f != mContainerAnimatedGeometryRoot; - f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(this->mBuilder, fParent)) { + f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(this->mBuilder, + fParent, mContainerAnimatedGeometryRoot)) { fParent = nsLayoutUtils::GetCrossDocParentFrame(f); if (!fParent) { // This means mContainerAnimatedGeometryRoot was not an ancestor @@ -4837,7 +4850,8 @@ ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer if (!e->mOpaqueRegion.IsEmpty()) { const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness; if (e->mOpaqueForAnimatedGeometryRootParent && - nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent()) + nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent(), + mContainerAnimatedGeometryRoot) == mContainerAnimatedGeometryRoot) { animatedGeometryRootToCover = mContainerAnimatedGeometryRoot; data = FindOpaqueRegionEntry(opaqueRegions, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 53b60d68a1b..ba83bf6c1dc 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -340,13 +340,13 @@ static void AddTransformFunctions(nsCSSValueList* aList, static TimingFunction ToTimingFunction(const ComputedTimingFunction& aCTF) { - if (aCTF.GetType() == nsTimingFunction::Function) { + if (aCTF.HasSpline()) { const nsSMILKeySpline* spline = aCTF.GetFunction(); return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(), spline->X2(), spline->Y2())); } - uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2; + uint32_t type = aCTF.GetType() == nsTimingFunction::Type::StepStart ? 1 : 2; return TimingFunction(StepFunction(aCTF.GetSteps(), type)); } @@ -1030,9 +1030,6 @@ nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParen // for background-attachment:fixed elements. return true; } - if (aFrame->IsTransformed()) { - return true; - } nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); if (!parent) @@ -1071,20 +1068,23 @@ nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParen bool nsDisplayListBuilder::GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame, + const nsIFrame* aStopAtAncestor, nsIFrame** aOutResult) { - return mAnimatedGeometryRootCache.Get(const_cast(aFrame), aOutResult); + AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor); + return mAnimatedGeometryRootCache.Get(lookup, aOutResult); } static nsIFrame* ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + const nsIFrame* aStopAtAncestor = nullptr, bool aUseCache = false) { nsIFrame* cursor = aFrame; - while (cursor != aBuilder->RootReferenceFrame()) { + while (cursor != aStopAtAncestor) { if (aUseCache) { nsIFrame* result; - if (aBuilder->GetCachedAnimatedGeometryRoot(cursor, &result)) { + if (aBuilder->GetCachedAnimatedGeometryRoot(cursor, aStopAtAncestor, &result)) { return result; } } @@ -1097,14 +1097,15 @@ ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, } nsIFrame* -nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame) +nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor) { if (aFrame == mCurrentFrame) { return mCurrentAnimatedGeometryRoot; } - nsIFrame* result = ComputeAnimatedGeometryRootFor(this, aFrame, true); - mAnimatedGeometryRootCache.Put(aFrame, result); + nsIFrame* result = ComputeAnimatedGeometryRootFor(this, aFrame, aStopAtAncestor, true); + AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor); + mAnimatedGeometryRootCache.Put(lookup, result); return result; } @@ -1112,9 +1113,8 @@ void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() { mCurrentAnimatedGeometryRoot = ComputeAnimatedGeometryRootFor(this, const_cast(mCurrentFrame)); - MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(), - mCurrentAnimatedGeometryRoot), "Bad"); - mAnimatedGeometryRootCache.Put(const_cast(mCurrentFrame), mCurrentAnimatedGeometryRoot); + AnimatedGeometryRootLookup lookup(mCurrentFrame, nullptr); + mAnimatedGeometryRootCache.Put(lookup, mCurrentAnimatedGeometryRoot); } void @@ -1980,17 +1980,11 @@ void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : mFrame(aFrame) , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder)) - , mAnimatedGeometryRoot(nullptr) #ifdef MOZ_DUMP_PAINTING , mPainted(false) #endif { mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame); - // This can return the wrong result if the item override ShouldFixToViewport(), - // the item needs to set it again in its constructor. - mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder); - MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), - mAnimatedGeometryRoot), "Bad"); NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 || !aBuilder->IsForPainting(), "dirty rect not set"); // The dirty rect is for mCurrentFrame, so we have to use @@ -2130,9 +2124,6 @@ nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilde MOZ_COUNT_CTOR(nsDisplayBackgroundImage); mBounds = GetBoundsInternal(aBuilder); - if (ShouldFixToViewport(aBuilder)) { - mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder); - } } nsDisplayBackgroundImage::~nsDisplayBackgroundImage() @@ -3699,7 +3690,8 @@ RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder, LayerState result = LAYER_INACTIVE; for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { if (result == LAYER_INACTIVE && - i->AnimatedGeometryRoot() != aExpectedAnimatedGeometryRootForChildren) { + nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) != + aExpectedAnimatedGeometryRootForChildren) { result = LAYER_ACTIVE; } @@ -3956,7 +3948,8 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, if (NeedsActiveLayer(aBuilder)) return LAYER_ACTIVE; - return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, AnimatedGeometryRoot()); + return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, + nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); } bool @@ -4670,14 +4663,10 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, void nsDisplayTransform::SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder) { - if (mFrame == aBuilder->RootReferenceFrame()) { - return; - } nsIFrame *outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame); mReferenceFrame = aBuilder->FindReferenceFrameFor(outerFrame); mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame); - mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder, outerFrame); mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index e0631bf1e31..b1b9d8c81d6 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -257,7 +257,7 @@ public: * Returns the nearest ancestor frame to aFrame that is considered to have * (or will have) animated geometry. This can return aFrame. */ - nsIFrame* FindAnimatedGeometryRootFor(nsIFrame* aFrame); + nsIFrame* FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor = nullptr); /** * @return the root of the display list's frame (sub)tree, whose origin @@ -635,11 +635,11 @@ public: aBuilder->mCurrentAnimatedGeometryRoot = aForChild; } } else { + // Stop at the previous animated geometry root to help cases that + // aren't immediate descendents. aBuilder->mCurrentAnimatedGeometryRoot = - aBuilder->FindAnimatedGeometryRootFor(aForChild); + aBuilder->FindAnimatedGeometryRootFor(aForChild, aBuilder->mCurrentAnimatedGeometryRoot); } - MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), - aBuilder->mCurrentAnimatedGeometryRoot), "Bad"); aBuilder->mCurrentFrame = aForChild; aBuilder->mDirtyRect = aDirtyRect; aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot; @@ -957,6 +957,7 @@ public: * true if the cache was hit. Return false if the cache was not hit. */ bool GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame, + const nsIFrame* aStopAtAncestor, nsIFrame** aOutResult); void SetCommittedScrollInfoItemList(nsDisplayList* aScrollInfoItemStorage) { @@ -1088,8 +1089,27 @@ private: // The animated geometry root for mCurrentFrame. nsIFrame* mCurrentAnimatedGeometryRoot; + struct AnimatedGeometryRootLookup { + const nsIFrame* mFrame; + const nsIFrame* mStopAtFrame; + + AnimatedGeometryRootLookup(const nsIFrame* aFrame, const nsIFrame* aStopAtFrame) + : mFrame(aFrame) + , mStopAtFrame(aStopAtFrame) + { + } + + PLDHashNumber Hash() const { + return mozilla::HashBytes(this, sizeof(this)); + } + + bool operator==(const AnimatedGeometryRootLookup& aOther) const { + return mFrame == aOther.mFrame && mStopAtFrame == aOther.mStopAtFrame; + } + }; // Cache for storing animated geometry roots for arbitrary frames - nsDataHashtable, nsIFrame*> mAnimatedGeometryRootCache; + nsDataHashtable, nsIFrame*> + mAnimatedGeometryRootCache; // will-change budget tracker nsDataHashtable, DocumentWillChangeBudget> mWillChangeBudget; @@ -1205,7 +1225,6 @@ public: : mFrame(aFrame) , mClip(nullptr) , mReferenceFrame(nullptr) - , mAnimatedGeometryRoot(nullptr) #ifdef MOZ_DUMP_PAINTING , mPainted(false) #endif @@ -1652,11 +1671,6 @@ public: */ virtual const nsIFrame* ReferenceFrameForChildren() const { return mReferenceFrame; } - nsIFrame* AnimatedGeometryRoot() const { - MOZ_ASSERT(mAnimatedGeometryRoot, "Must have cached AGR before accessing it!"); - return mAnimatedGeometryRoot; - } - /** * Checks if this display item (or any children) contains content that might * be rendered with component alpha (e.g. subpixel antialiasing). Returns the @@ -1716,7 +1730,6 @@ protected: const DisplayItemClip* mClip; // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null const nsIFrame* mReferenceFrame; - nsIFrame* mAnimatedGeometryRoot; // Result of ToReferenceFrame(mFrame), if mFrame is non-null nsPoint mToReferenceFrame; // This is the rectangle that needs to be painted. diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index bf57c617fe8..4271ee904a6 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -128,6 +128,7 @@ using namespace mozilla; using namespace mozilla::dom; #define BEFOREUNLOAD_DISABLED_PREFNAME "dom.disable_beforeunload" +#define BEFOREUNLOAD_REQUIRES_INTERACTION_PREFNAME "dom.require_user_interaction_for_beforeunload" //----------------------------------------------------- // LOGGING @@ -1074,12 +1075,15 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow, } static bool sIsBeforeUnloadDisabled; - static bool sBeforeUnloadPrefCached = false; + static bool sBeforeUnloadRequiresInteraction; + static bool sBeforeUnloadPrefsCached = false; - if (!sBeforeUnloadPrefCached ) { - sBeforeUnloadPrefCached = true; + if (!sBeforeUnloadPrefsCached ) { + sBeforeUnloadPrefsCached = true; Preferences::AddBoolVarCache(&sIsBeforeUnloadDisabled, BEFOREUNLOAD_DISABLED_PREFNAME); + Preferences::AddBoolVarCache(&sBeforeUnloadRequiresInteraction, + BEFOREUNLOAD_REQUIRES_INTERACTION_PREFNAME); } // First, get the script global object from the document... @@ -1137,7 +1141,10 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow, nsAutoString text; beforeUnload->GetReturnValue(text); - if (!sIsBeforeUnloadDisabled && *aShouldPrompt && dialogsAreEnabled && + // NB: we nullcheck mDocument because it might now be dead as a result of + // the event being dispatched. + if (!sIsBeforeUnloadDisabled && *aShouldPrompt && dialogsAreEnabled && mDocument && + (!sBeforeUnloadRequiresInteraction || mDocument->UserHasInteracted()) && (event->GetInternalNSEvent()->mFlags.mDefaultPrevented || !text.IsEmpty())) { // Ask the user if it's ok to unload the current page diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index f518a8c6ed8..c7aa8bac6b5 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -162,7 +162,9 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, } bool snap; nsRect rect = aItem->GetBounds(aBuilder, &snap); - nsRect layerRect = rect - aItem->AnimatedGeometryRoot()->GetOffsetToCrossDoc(aItem->ReferenceFrame()); + nsRect layerRect = rect - + nsLayoutUtils::GetAnimatedGeometryRootFor(aItem, aBuilder)-> + GetOffsetToCrossDoc(aItem->ReferenceFrame()); nscolor color; nsRect vis = aItem->GetVisibleRect(); nsRect component = aItem->GetComponentAlphaBounds(aBuilder); @@ -177,7 +179,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, aStream << nsPrintfCString("", string.BeginReading()); } #endif - aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s ref=0x%p agr=0x%p", + aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s", aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(), (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""), rect.x, rect.y, rect.width, rect.height, @@ -185,8 +187,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, vis.x, vis.y, vis.width, vis.height, component.x, component.y, component.width, component.height, clip.ToString().get(), - aItem->IsUniform(aBuilder, &color) ? " uniform" : "", - aItem->ReferenceFrame(), aItem->AnimatedGeometryRoot()); + aItem->IsUniform(aBuilder, &color) ? " uniform" : ""); nsRegionRectIterator iter(opaque); for (const nsRect* r = iter.Next(); r; r = iter.Next()) { diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index fce3f37e24a..20790ee4805 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8,6 +8,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/BasicEvents.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/EventDispatcher.h" #include "mozilla/FloatingPoint.h" #include "mozilla/gfx/PathHelpers.h" @@ -112,6 +113,14 @@ #include "nsTransitionManager.h" #include "RestyleManager.h" +// Make sure getpid() works. +#ifdef XP_WIN +#include +#define getpid _getpid +#else +#include +#endif + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::image; @@ -1835,9 +1844,10 @@ nsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame* aThumbFrame) nsIFrame* nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame) + nsIFrame* aFrame, + const nsIFrame* aStopAtAncestor) { - return aBuilder->FindAnimatedGeometryRootFor(aFrame); + return aBuilder->FindAnimatedGeometryRootFor(aFrame, aStopAtAncestor); } nsIFrame* @@ -1853,9 +1863,10 @@ nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, nsIFrame* viewportFrame = nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame); NS_ASSERTION(viewportFrame, "no viewport???"); - return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame); + return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame, + aBuilder->FindReferenceFrameFor(viewportFrame)); } - return GetAnimatedGeometryRootForFrame(aBuilder, f); + return GetAnimatedGeometryRootForFrame(aBuilder, f, aItem->ReferenceFrame()); } // static @@ -2871,7 +2882,20 @@ nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType, #include static bool gDumpEventList = false; -int gPaintCount = 0; + +// nsLayoutUtils::PaintFrame() can call itself recursively, so rather than +// maintaining a single paint count, we need a stack. +StaticAutoPtr> gPaintCountStack; + +struct AutoNestedPaintCount { + AutoNestedPaintCount() { + gPaintCountStack->AppendElement(0); + } + ~AutoNestedPaintCount() { + gPaintCountStack->RemoveElementAt(gPaintCountStack->Length() - 1); + } +}; + #endif nsIFrame* @@ -3076,6 +3100,17 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram PROFILER_LABEL("nsLayoutUtils", "PaintFrame", js::ProfileEntry::Category::GRAPHICS); +#ifdef MOZ_DUMP_PAINTING + if (!gPaintCountStack) { + gPaintCountStack = new nsTArray(); + ClearOnShutdown(&gPaintCountStack); + + gPaintCountStack->AppendElement(0); + } + ++gPaintCountStack->LastElement(); + AutoNestedPaintCount nestedPaintCount; +#endif + if (aFlags & PAINT_WIDGET_LAYERS) { nsView* view = aFrame->GetView(); if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) { @@ -3264,7 +3299,13 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPaintingToFile) { nsCString string("dump-"); - string.AppendInt(gPaintCount); + // Include the process ID in the dump file name, to make sure that in an + // e10s setup different processes don't clobber each other's dump files. + string.AppendInt(getpid()); + for (int paintCount : *gPaintCountStack) { + string.AppendLiteral("-"); + string.AppendInt(paintCount); + } string.AppendLiteral(".html"); gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w"); } else { @@ -3279,7 +3320,9 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram nsFrame::PrintDisplayList(&builder, list, *ss, gfxUtils::sDumpPaintingToFile); if (gfxUtils::sDumpPaintingToFile) { *ss << ""); diff --git a/layout/base/tests/browser_onbeforeunload_only_after_interaction.js b/layout/base/tests/browser_onbeforeunload_only_after_interaction.js new file mode 100644 index 00000000000..d079bcb380c --- /dev/null +++ b/layout/base/tests/browser_onbeforeunload_only_after_interaction.js @@ -0,0 +1,53 @@ +function pageScript() { + window.addEventListener("beforeunload", function (event) { + var str = "Some text that causes the beforeunload dialog to be shown"; + event.returnValue = str; + return str; + }, true); +} + +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", true]]}); + +const PAGE_URL = + "data:text/html," + encodeURIComponent(""); + +add_task(function* doClick() { + // The onbeforeunload dialog should appear. + let dialogShown = false; + function onDialogShown(node) { + dialogShown = true; + let dismissButton = node.ui.button0; + dismissButton.click(); + } + let obsName = "tabmodal-dialog-loaded"; + Services.obs.addObserver(onDialogShown, obsName, false); + yield* openPage(true); + Services.obs.removeObserver(onDialogShown, obsName); + Assert.ok(dialogShown, "Should have shown dialog."); +}); + +add_task(function* noClick() { + // The onbeforeunload dialog should NOT appear. + yield openPage(false); + info("If we time out here, then the dialog was shown..."); +}); + +function* openPage(shouldClick) { + // Open about:blank in a new tab. + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { + // Load the page. + yield BrowserTestUtils.loadURI(browser, PAGE_URL); + yield BrowserTestUtils.browserLoaded(browser); + + if (shouldClick) { + yield BrowserTestUtils.synthesizeMouse("body", 2, 2, {}, browser); + } + let hasInteractedWith = yield ContentTask.spawn(browser, "", function() { + return content.document.userHasInteracted; + }); + is(shouldClick, hasInteractedWith, "Click should update document interactivity state"); + // And then navigate away. + yield BrowserTestUtils.loadURI(browser, "http://example.com/"); + yield BrowserTestUtils.browserLoaded(browser); + }); +} diff --git a/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js b/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js new file mode 100644 index 00000000000..34dbf230891 --- /dev/null +++ b/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js @@ -0,0 +1,59 @@ +function pageScript() { + window.addEventListener("beforeunload", function (event) { + var str = "Some text that causes the beforeunload dialog to be shown"; + event.returnValue = str; + return str; + }, true); +} + +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", true]]}); + +const FRAME_URL = + "data:text/html," + encodeURIComponent("Just a frame"); + +const PAGE_URL = + "data:text/html," + encodeURIComponent(""); + +add_task(function* doClick() { + // The onbeforeunload dialog should appear. + let dialogShown = false; + function onDialogShown(node) { + dialogShown = true; + let dismissButton = node.ui.button0; + dismissButton.click(); + } + let obsName = "tabmodal-dialog-loaded"; + Services.obs.addObserver(onDialogShown, obsName, false); + yield* openPage(true); + Services.obs.removeObserver(onDialogShown, obsName); + Assert.ok(dialogShown, "Should have shown dialog."); +}); + +add_task(function* noClick() { + // The onbeforeunload dialog should NOT appear. + yield openPage(false); + info("If we time out here, then the dialog was shown..."); +}); + +function* openPage(shouldClick) { + // Open about:blank in a new tab. + yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) { + // Load the page. + yield BrowserTestUtils.loadURI(browser, PAGE_URL); + yield BrowserTestUtils.browserLoaded(browser); + + if (shouldClick) { + yield BrowserTestUtils.synthesizeMouse(function() { + return content.frames[0].document.body; + }, 2, 2, {}, browser); + } + let hasInteractedWith = yield ContentTask.spawn(browser, "", function() { + return [content.document.userHasInteracted, content.frames[0].document.userHasInteracted]; + }); + is(shouldClick, hasInteractedWith[0], "Click should update parent interactivity state"); + is(shouldClick, hasInteractedWith[1], "Click should update frame interactivity state"); + // And then navigate away. + yield BrowserTestUtils.loadURI(browser, "http://example.com/"); + yield BrowserTestUtils.browserLoaded(browser); + }); +} diff --git a/layout/base/tests/test_scroll_snapping.html b/layout/base/tests/test_scroll_snapping.html index c17b5eeb7ec..1ba862ee1ca 100644 --- a/layout/base/tests/test_scroll_snapping.html +++ b/layout/base/tests/test_scroll_snapping.html @@ -9,8 +9,8 @@

-
-
+
+
@@ -574,7 +574,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 250px. (Page Down)",
+    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 500px. (Page Down)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -589,7 +589,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 250px. (Page Up)",
+    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 500px. (Page Up)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -604,7 +604,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 250px. (Page Down)",
+    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 500px. (Page Down)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -619,7 +619,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 250px. (Page Up)",
+    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 500px. (Page Up)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -687,6 +687,25 @@ var lastScrollTop;
 var lastScrollLeft;
 var stopFrameCount;
 
+// The tests should work the same way when all the values for the scroll
+// container are provided in percentages. To assert that, we just duplicate all
+// the test cases and replace the pixel values related to the scroll container
+// with percentage values, based on its clientWidth/Height sespectively.
+function addPercentageTests() {
+  var width = sc.clientWidth;
+  var height = sc.clientHeight;
+  var pxRegexp = /(\d+)px/;
+  var rewriteW = (_, w) => (parseInt(w, 10) / width * 100) + "%";
+  var rewriteH = (_, h) => (parseInt(h, 10) / height * 100) + "%";
+  testCases = testCases.concat(testCases.map(testCase => Object.assign({}, testCase, {
+    description: "With Percentages: " + testCase.description,
+    scrollSnapPointsX: testCase.scrollSnapPointsX.replace(pxRegexp, rewriteW),
+    scrollSnapPointsY: testCase.scrollSnapPointsY.replace(pxRegexp, rewriteH),
+    scrollSnapDestination: testCase.scrollSnapDestination
+      .replace(pxRegexp, rewriteW).replace(pxRegexp, rewriteH),
+  })));
+}
+
 function initTest() {
   var testCase = testCases[step];
   sc.style.scrollSnapType = testCase.scrollSnapType;
@@ -726,6 +745,8 @@ function testScrollSnapping() {
   sc = document.getElementById("sc");
   sd = document.getElementById("sd");
 
+  addPercentageTests();
+
   initTest();
 }
 
diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h
index b0d5d22ef00..4e56b80af0a 100644
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -239,11 +239,7 @@ public:
   nsDisplayCanvasBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                  uint32_t aLayer, const nsStyleBackground* aBg)
     : nsDisplayBackgroundImage(aBuilder, aFrame, aLayer, aBg)
-  {
-    if (ShouldFixToViewport(aBuilder)) {
-      mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
-    }
-  }
+  {}
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
 
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index 59bbbce2f99..f82d587d4c6 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -5713,7 +5713,7 @@ ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUn
   }
   if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
     nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsY,
-                                                           scrollPortSize.width);
+                                                           scrollPortSize.height);
     calcSnapPoints.AddHorizontalEdgeInterval(scrollRange, interval, destPos.y);
   }
 
diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp
index c798b0278e1..dbe6a1f59b6 100644
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -14687,12 +14687,11 @@ CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
     return false;
   }
 
-  int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
+  int32_t type = -1;  // indicates an implicit end value
   if (ExpectSymbol(',', true)) {
     if (!GetToken(true)) {
       return false;
     }
-    type = -1;
     if (mToken.mType == eCSSToken_Ident) {
       if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp
index c9d072b66cd..6720656a9b1 100644
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1054,21 +1054,21 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult,
              eCSSProperty_transition_timing_function != aProperty) ||
             unit == eCSSUnit_Symbols)
           aResult.Append(' ');
-        else
+        else if (unit != eCSSUnit_Steps)
           aResult.AppendLiteral(", ");
       }
       if (unit == eCSSUnit_Steps && i == 1) {
-        MOZ_ASSERT(array->Item(i).GetUnit() == eCSSUnit_Enumerated &&
-                   (array->Item(i).GetIntValue() ==
-                     NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
-                    array->Item(i).GetIntValue() ==
-                     NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
+        MOZ_ASSERT(array->Item(i).GetUnit() == eCSSUnit_Enumerated,
                    "unexpected value");
-        if (array->Item(i).GetIntValue() ==
-              NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) {
-          aResult.AppendLiteral("start");
-        } else {
-          aResult.AppendLiteral("end");
+        int32_t side = array->Item(i).GetIntValue();
+        MOZ_ASSERT(side == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
+                   side == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END ||
+                   side == -1,
+                   "unexpected value");
+        if (side == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) {
+          aResult.AppendLiteral(", start");
+        } else if (side == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END) {
+          aResult.AppendLiteral(", end");
         }
         continue;
       }
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp
index 26b2cf888ea..40e6d4f15b8 100644
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5800,27 +5800,25 @@ nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
   aValueList->AppendCSSValue(timingFunction);
 
   nsAutoString tmp;
-
-  if (aTimingFunction.mType == nsTimingFunction::Function) {
-    // set the value from the cubic-bezier control points
-    // (We could try to regenerate the keywords if we want.)
-    tmp.AppendLiteral("cubic-bezier(");
-    tmp.AppendFloat(aTimingFunction.mFunc.mX1);
-    tmp.AppendLiteral(", ");
-    tmp.AppendFloat(aTimingFunction.mFunc.mY1);
-    tmp.AppendLiteral(", ");
-    tmp.AppendFloat(aTimingFunction.mFunc.mX2);
-    tmp.AppendLiteral(", ");
-    tmp.AppendFloat(aTimingFunction.mFunc.mY2);
-    tmp.Append(')');
-  } else {
-    tmp.AppendLiteral("steps(");
-    tmp.AppendInt(aTimingFunction.mSteps);
-    if (aTimingFunction.mType == nsTimingFunction::StepStart) {
-      tmp.AppendLiteral(", start)");
-    } else {
-      tmp.AppendLiteral(", end)");
-    }
+  switch (aTimingFunction.mType) {
+    case nsTimingFunction::Type::CubicBezier:
+      nsStyleUtil::AppendCubicBezierTimingFunction(aTimingFunction.mFunc.mX1,
+                                                   aTimingFunction.mFunc.mY1,
+                                                   aTimingFunction.mFunc.mX2,
+                                                   aTimingFunction.mFunc.mY2,
+                                                   tmp);
+      break;
+    case nsTimingFunction::Type::StepStart:
+    case nsTimingFunction::Type::StepEnd:
+      nsStyleUtil::AppendStepsTimingFunction(aTimingFunction.mType,
+                                             aTimingFunction.mSteps,
+                                             aTimingFunction.mStepSyntax,
+                                             tmp);
+      break;
+    default:
+      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(aTimingFunction.mType,
+                                                          tmp);
+      break;
   }
   timingFunction->SetString(tmp);
 }
diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp
index 427fb243bba..be4df0dc36b 100644
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4848,13 +4848,18 @@ ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult)
                      (array->Item(1).GetIntValue() ==
                        NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
                       array->Item(1).GetIntValue() ==
-                       NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
+                       NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END ||
+                      array->Item(1).GetIntValue() == -1),
                      "unexpected second value");
         nsTimingFunction::Type type =
           (array->Item(1).GetIntValue() ==
-            NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END)
-            ? nsTimingFunction::StepEnd : nsTimingFunction::StepStart;
-        aResult = nsTimingFunction(type, array->Item(0).GetIntValue());
+            NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) ?
+              nsTimingFunction::Type::StepStart :
+              nsTimingFunction::Type::StepEnd;
+        aResult = nsTimingFunction(type, array->Item(0).GetIntValue(),
+                                   array->Item(1).GetIntValue() == -1 ?
+                                     nsTimingFunction::Keyword::Implicit :
+                                     nsTimingFunction::Keyword::Explicit);
       }
       break;
     default:
diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp
index 0955e626bd4..2630731f759 100644
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -389,6 +389,8 @@ SortStyleSheetsByScope(nsTArray& aSheets)
 nsresult
 nsStyleSet::GatherRuleProcessors(sheetType aType)
 {
+  NS_ENSURE_FALSE(mInShutdown, NS_ERROR_FAILURE);
+
   // We might be in GatherRuleProcessors because we are dropping a sheet,
   // resulting in an nsCSSSelector being destroyed.  Tell the
   // RestyleManager for each document we're used in so that they can
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
index f6ff47d1b4b..08dafa4a50d 100644
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2462,15 +2462,24 @@ void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType)
 {
   switch (aTimingFunctionType) {
     case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
-      mType = StepStart;
-      mSteps = 1;
-      return;
-    case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
-      mType = StepEnd;
+      mType = Type::StepStart;
+      mStepSyntax = StepSyntax::Keyword;
       mSteps = 1;
       return;
     default:
-      mType = Function;
+      MOZ_ASSERT_UNREACHABLE("aTimingFunctionType must be a keyword value");
+      // fall through
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
+      mType = Type::StepEnd;
+      mStepSyntax = StepSyntax::Keyword;
+      mSteps = 1;
+      return;
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE:
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR:
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN:
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT:
+    case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT:
+      mType = static_cast(aTimingFunctionType);
       break;
   }
 
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
index 29e4bb132d0..2bb1546a101 100644
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1874,7 +1874,31 @@ struct nsStyleVisibility {
 };
 
 struct nsTimingFunction {
-  enum Type { Function, StepStart, StepEnd };
+
+  enum class Type {
+    Ease,         // ease
+    Linear,       // linear
+    EaseIn,       // ease-in
+    EaseOut,      // ease-out
+    EaseInOut,    // ease-in-out
+    StepStart,    // step-start and steps(..., start)
+    StepEnd,      // step-end, steps(..., end) and steps(...)
+    CubicBezier,  // cubic-bezier()
+  };
+
+  enum class StepSyntax {
+    Keyword,                     // step-start and step-end
+    FunctionalWithoutKeyword,    // steps(...)
+    FunctionalWithStartKeyword,  // steps(..., start)
+    FunctionalWithEndKeyword,    // steps(..., end)
+  };
+
+  // Whether the timing function type is represented by a spline,
+  // and thus will have mFunc filled in.
+  static bool IsSplineType(Type aType)
+  {
+    return aType != Type::StepStart && aType != Type::StepEnd;
+  }
 
   explicit nsTimingFunction(int32_t aTimingFunctionType
                               = NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)
@@ -1883,7 +1907,7 @@ struct nsTimingFunction {
   }
 
   nsTimingFunction(float x1, float y1, float x2, float y2)
-    : mType(Function)
+    : mType(Type::CubicBezier)
   {
     mFunc.mX1 = x1;
     mFunc.mY1 = y1;
@@ -1891,11 +1915,23 @@ struct nsTimingFunction {
     mFunc.mY2 = y2;
   }
 
-  nsTimingFunction(Type aType, uint32_t aSteps)
+  enum class Keyword { Implicit, Explicit };
+
+  nsTimingFunction(Type aType, uint32_t aSteps, Keyword aKeyword)
     : mType(aType)
   {
-    MOZ_ASSERT(mType == StepStart || mType == StepEnd, "wrong type");
+    MOZ_ASSERT(mType == Type::StepStart || mType == Type::StepEnd,
+               "wrong type");
     mSteps = aSteps;
+    if (mType == Type::StepStart) {
+      MOZ_ASSERT(aKeyword == Keyword::Explicit,
+                 "only StepEnd can have an implicit keyword");
+      mStepSyntax = StepSyntax::FunctionalWithStartKeyword;
+    } else {
+      mStepSyntax = aKeyword == Keyword::Explicit ?
+                      StepSyntax::FunctionalWithEndKeyword :
+                      StepSyntax::FunctionalWithoutKeyword;
+    }
   }
 
   nsTimingFunction(const nsTimingFunction& aOther)
@@ -1911,7 +1947,10 @@ struct nsTimingFunction {
       float mX2;
       float mY2;
     } mFunc;
-    uint32_t mSteps;
+    struct {
+      StepSyntax mStepSyntax;
+      uint32_t mSteps;
+    };
   };
 
   nsTimingFunction&
@@ -1922,13 +1961,14 @@ struct nsTimingFunction {
 
     mType = aOther.mType;
 
-    if (mType == Function) {
+    if (HasSpline()) {
       mFunc.mX1 = aOther.mFunc.mX1;
       mFunc.mY1 = aOther.mFunc.mY1;
       mFunc.mX2 = aOther.mFunc.mX2;
       mFunc.mY2 = aOther.mFunc.mY2;
     } else {
       mSteps = aOther.mSteps;
+      mStepSyntax = aOther.mStepSyntax;
     }
 
     return *this;
@@ -1939,11 +1979,12 @@ struct nsTimingFunction {
     if (mType != aOther.mType) {
       return false;
     }
-    if (mType == Function) {
+    if (HasSpline()) {
       return mFunc.mX1 == aOther.mFunc.mX1 && mFunc.mY1 == aOther.mFunc.mY1 &&
              mFunc.mX2 == aOther.mFunc.mX2 && mFunc.mY2 == aOther.mFunc.mY2;
     }
-    return mSteps == aOther.mSteps;
+    return mSteps == aOther.mSteps &&
+           mStepSyntax == aOther.mStepSyntax;
   }
 
   bool operator!=(const nsTimingFunction& aOther) const
@@ -1951,6 +1992,8 @@ struct nsTimingFunction {
     return !(*this == aOther);
   }
 
+  bool HasSpline() const { return IsSplineType(mType); }
+
 private:
   void AssignFromKeyword(int32_t aTimingFunctionType);
 };
diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp
index 2107b28ad3c..d1f9b4461a5 100644
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -562,6 +562,84 @@ nsStyleUtil::AppendSerializedFontSrc(const nsCSSValue& aValue,
   aResult.Truncate(aResult.Length() - 2); // remove the last comma-space
 }
 
+/* static */ void
+nsStyleUtil::AppendStepsTimingFunction(nsTimingFunction::Type aType,
+                                       uint32_t aSteps,
+                                       nsTimingFunction::StepSyntax aSyntax,
+                                       nsAString& aResult)
+{
+  MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
+             aType == nsTimingFunction::Type::StepEnd);
+
+  if (aSyntax == nsTimingFunction::StepSyntax::Keyword) {
+    if (aType == nsTimingFunction::Type::StepStart) {
+      aResult.AppendLiteral("step-start");
+    } else {
+      aResult.AppendLiteral("step-end");
+    }
+    return;
+  }
+
+  aResult.AppendLiteral("steps(");
+  aResult.AppendInt(aSteps);
+  switch (aSyntax) {
+    case nsTimingFunction::StepSyntax::Keyword:
+      // handled above
+      break;
+    case nsTimingFunction::StepSyntax::FunctionalWithStartKeyword:
+      aResult.AppendLiteral(", start)");
+      break;
+    case nsTimingFunction::StepSyntax::FunctionalWithEndKeyword:
+      aResult.AppendLiteral(", end)");
+      break;
+    case nsTimingFunction::StepSyntax::FunctionalWithoutKeyword:
+      aResult.Append(')');
+      break;
+  }
+}
+
+/* static */ void
+nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
+                                             float aX2, float aY2,
+                                             nsAString& aResult)
+{
+  // set the value from the cubic-bezier control points
+  // (We could try to regenerate the keywords if we want.)
+  aResult.AppendLiteral("cubic-bezier(");
+  aResult.AppendFloat(aX1);
+  aResult.AppendLiteral(", ");
+  aResult.AppendFloat(aY1);
+  aResult.AppendLiteral(", ");
+  aResult.AppendFloat(aX2);
+  aResult.AppendLiteral(", ");
+  aResult.AppendFloat(aY2);
+  aResult.Append(')');
+}
+
+/* static */ void
+nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
+    nsTimingFunction::Type aType,
+    nsAString& aResult)
+{
+  switch (aType) {
+    case nsTimingFunction::Type::Ease:
+    case nsTimingFunction::Type::Linear:
+    case nsTimingFunction::Type::EaseIn:
+    case nsTimingFunction::Type::EaseOut:
+    case nsTimingFunction::Type::EaseInOut: {
+      nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
+          static_cast(aType),
+          nsCSSProps::kTransitionTimingFunctionKTable);
+      AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
+                         aResult);
+      break;
+    }
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected aType");
+      break;
+  }
+}
+
 /* static */ float
 nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha)
 {
diff --git a/layout/style/nsStyleUtil.h b/layout/style/nsStyleUtil.h
index 3f32b433cae..4199ed96016 100644
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -10,6 +10,7 @@
 #include "nsString.h"
 #include "nsTArrayForwardDeclare.h"
 #include "gfxFontFamilyList.h"
+#include "nsStyleStruct.h"
 
 class nsCSSValue;
 class nsStringComparator;
@@ -20,7 +21,6 @@ class nsIURI;
 struct gfxFontFeature;
 struct gfxAlternateValue;
 struct nsCSSValueList;
-struct nsStylePosition;
 
 // Style utility functions
 class nsStyleUtil {
@@ -72,6 +72,17 @@ public:
     aResult.AppendFloat(aNumber);
   }
 
+  static void AppendStepsTimingFunction(nsTimingFunction::Type aType,
+                                        uint32_t aSteps,
+                                        nsTimingFunction::StepSyntax aSyntax,
+                                        nsAString& aResult);
+  static void AppendCubicBezierTimingFunction(float aX1, float aY1,
+                                              float aX2, float aY2,
+                                              nsAString& aResult);
+  static void AppendCubicBezierKeywordTimingFunction(
+      nsTimingFunction::Type aType,
+      nsAString& aResult);
+
   static void AppendSerializedFontSrc(const nsCSSValue& aValue,
                                       nsAString& aResult);
 
diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js
index bf49b183295..88af653c33e 100644
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -595,7 +595,7 @@ var gCSSProperties = {
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ],
     initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ],
-    other_values: [ "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
+    other_values: [ "none none 0s 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) normal running 1.0", "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
     invalid_values: [  "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial", "2s all,, 1s bounce", "2s all, , 1s bounce" ]
   },
   "animation-delay": {
@@ -660,8 +660,8 @@ var gCSSProperties = {
     domProp: "animationTimingFunction",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
-    initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
-    other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+    initial_values: [ "ease" ],
+    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
     invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
   },
   "-moz-appearance": {
@@ -3448,7 +3448,7 @@ var gCSSProperties = {
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ],
     initial_values: [ "all 0s ease 0s", "all", "0s", "0s 0s", "ease" ],
-    other_values: [ "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all" ],
+    other_values: [ "all 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0s", "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all" ],
     invalid_values: [ "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial", "1s width,,2s color", "1s width, ,2s color" ]
   },
   "transition-delay": {
@@ -3479,8 +3479,8 @@ var gCSSProperties = {
     domProp: "transitionTimingFunction",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
-    initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
-    other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+    initial_values: [ "ease" ],
+    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
     invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
   },
   "unicode-bidi": {
@@ -4391,7 +4391,7 @@ var gCSSProperties = {
     alias_for: "transition",
     subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ],
     initial_values: [ "all 0s ease 0s", "all", "0s", "0s 0s", "ease" ],
-    other_values: [ "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all" ],
+    other_values: [ "all 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0s", "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all" ],
     invalid_values: [ "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial" ]
   },
   "-moz-transition-delay": {
@@ -4430,8 +4430,8 @@ var gCSSProperties = {
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "transition-timing-function",
     subproperties: [ "transition-timing-function" ],
-    initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
-    other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+    initial_values: [ "ease" ],
+    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
     invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
   },
   "-moz-animation": {
@@ -4441,7 +4441,7 @@ var gCSSProperties = {
     alias_for: "animation",
     subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ],
     initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ],
-    other_values: [ "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
+    other_values: [ "none none 0s 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) normal running 1.0", "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
     invalid_values: [  "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial" ]
   },
   "-moz-animation-delay": {
@@ -4522,8 +4522,8 @@ var gCSSProperties = {
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "animation-timing-function",
     subproperties: [ "animation-timing-function" ],
-    initial_values: [ "ease", "cubic-bezier(0.25, 0.1, 0.25, 1.0)" ],
-    other_values: [ "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
+    initial_values: [ "ease" ],
+    other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ],
     invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ]
   },
   "-moz-font-feature-settings": {
diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in
index 09479750b7a..68065601ae2 100644
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -602,16 +602,10 @@ bin/libfreebl_32int64_3.so
 #endif
 
 ; [Crash Reporter]
-;
+; CrashService is not used on Android but the ini files are required for L10N
+; strings, see bug 1191351.
 #ifdef MOZ_CRASHREPORTER
-@BINPATH@/components/CrashService.manifest
-@BINPATH@/components/CrashService.js
-@BINPATH@/crashreporter@BIN_SUFFIX@
-@BINPATH@/crashreporter.crt
 @BINPATH@/crashreporter.ini
-#ifdef XP_UNIX
-@BINPATH@/Throbber-small.gif
-#endif
 @BINPATH@/crashreporter-override.ini
 #endif
 
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 8aaeb296091..000c6fa970a 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1030,6 +1030,8 @@ pref("dom.disable_window_open_feature.status",      true);
 
 pref("dom.allow_scripts_to_close_windows",          false);
 
+pref("dom.require_user_interaction_for_beforeunload", true);
+
 pref("dom.disable_open_during_load",                false);
 pref("dom.popup_maximum",                           20);
 pref("dom.popup_allowed_events", "change click dblclick mouseup notificationclick reset submit touchend");
diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
index 3bdc40547c8..3c74f817282 100644
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2851,6 +2851,21 @@ nsCookieService::SetCookieInternal(nsIURI                        *aHostURI,
   // so we can handle them separately.
   bool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
 
+  // Collect telemetry on how often secure cookies are set from non-secure
+  // origins, and vice-versa.
+  //
+  // 0 = nonsecure and "http:"
+  // 1 = nonsecure and "https:"
+  // 2 = secure and "http:"
+  // 3 = secure and "https:"
+  bool isHTTPS;
+  nsresult rv = aHostURI->SchemeIs("https", &isHTTPS);
+  if (NS_SUCCEEDED(rv)) {
+    Telemetry::Accumulate(Telemetry::COOKIE_SCHEME_SECURITY,
+                          ((cookieAttributes.isSecure)? 0x02 : 0x00) |
+                          ((isHTTPS)? 0x01 : 0x00));
+  }
+
   int64_t currentTimeInUsec = PR_Now();
 
   // calculate expiry time of cookie.
diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp
index 7298462bfe2..c39e199ed8e 100644
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -149,7 +149,7 @@ NS_IMPL_ISUPPORTS(DataChannelShutdown, nsIObserver);
 
 
 BufferedMsg::BufferedMsg(struct sctp_sendv_spa &spa, const char *data,
-                         uint32_t length) : mLength(length)
+                         size_t length) : mLength(length)
 {
   mSpa = new sctp_sendv_spa;
   *mSpa = spa;
@@ -1123,11 +1123,16 @@ DataChannelConnection::SendDeferredMessages()
       if (channel->mState == CLOSED || channel->mState == CLOSING) {
         channel->mBufferedData.Clear();
       }
+
+      uint32_t buffered_amount = channel->GetBufferedAmount();
+      uint32_t threshold = channel->GetBufferedAmountLowThreshold();
+      bool was_over_threshold = buffered_amount >= threshold;
+
       while (!channel->mBufferedData.IsEmpty() &&
              !failed_send) {
         struct sctp_sendv_spa *spa = channel->mBufferedData[0]->mSpa;
         const char *data           = channel->mBufferedData[0]->mData;
-        uint32_t len               = channel->mBufferedData[0]->mLength;
+        size_t len                 = channel->mBufferedData[0]->mLength;
 
         // SCTP will return EMSGSIZE if the message is bigger than the buffer
         // size (or EAGAIN if there isn't space)
@@ -1147,7 +1152,19 @@ DataChannelConnection::SendDeferredMessages()
         } else {
           LOG(("Resent buffer of %d bytes (%d)", len, result));
           sent = true;
+          // In theory this could underflow if >4GB was buffered and re
+          // truncated in GetBufferedAmount(), but this won't cause any problems.
+          buffered_amount -= channel->mBufferedData[0]->mLength;
           channel->mBufferedData.RemoveElementAt(0);
+          // can never fire with default threshold of 0
+          if (was_over_threshold && buffered_amount < threshold) {
+            LOG(("%s: sending BUFFER_LOW_THRESHOLD for %s/%s: %u", __FUNCTION__,
+                 channel->mLabel.get(), channel->mProtocol.get(), channel->mStream));
+            NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable(
+                                                DataChannelOnMessageAvailable::BUFFER_LOW_THRESHOLD,
+                                                this, channel)));
+            was_over_threshold = false;
+          }
         }
       }
       if (channel->mBufferedData.IsEmpty())
@@ -1288,7 +1305,7 @@ DataChannelConnection::DeliverQueuedData(uint16_t stream)
     // Careful! we may modify the array length from within the loop!
     if (mQueuedData[i]->mStream == stream) {
       LOG(("Delivering queued data for stream %u, length %u",
-           stream, mQueuedData[i]->mLength));
+           stream, (unsigned int) mQueuedData[i]->mLength));
       // Deliver the queued data
       HandleDataMessage(mQueuedData[i]->mPpid,
                         mQueuedData[i]->mData, mQueuedData[i]->mLength,
@@ -2199,7 +2216,7 @@ request_error_cleanup:
 
 int32_t
 DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data,
-                                       uint32_t length, uint32_t ppid)
+                                       size_t length, uint32_t ppid)
 {
   uint16_t flags;
   struct sctp_sendv_spa spa;
@@ -2267,7 +2284,7 @@ DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data,
 // Handles fragmenting binary messages
 int32_t
 DataChannelConnection::SendBinary(DataChannel *channel, const char *data,
-                                  uint32_t len,
+                                  size_t len,
                                   uint32_t ppid_partial, uint32_t ppid_final)
 {
   // Since there's a limit on network buffer size and no limits on message
@@ -2292,7 +2309,7 @@ DataChannelConnection::SendBinary(DataChannel *channel, const char *data,
     LOG(("Sending binary message length %u in chunks", len));
     // XXX check flags for out-of-order, or force in-order for large binary messages
     while (len > 0) {
-      uint32_t sendlen = PR_MIN(len, DATA_CHANNEL_MAX_BINARY_FRAGMENT);
+      size_t sendlen = PR_MIN(len, DATA_CHANNEL_MAX_BINARY_FRAGMENT);
       uint32_t ppid;
       len -= sendlen;
       ppid = len > 0 ? ppid_partial : ppid_final;
@@ -2622,13 +2639,36 @@ DataChannel::AppReady()
 uint32_t
 DataChannel::GetBufferedAmount()
 {
-  uint32_t buffered = 0;
-  for (uint32_t i = 0; i < mBufferedData.Length(); ++i) {
-    buffered += mBufferedData[i]->mLength;
+  size_t buffered = 0;
+  for (auto& buffer : mBufferedData) {
+    buffered += buffer->mLength;
+  }
+  // XXX Note: per Michael Tuexen, there's no way to currently get the buffered
+  // amount from the SCTP stack for a single stream.  It is on their to-do
+  // list, and once we import a stack with support for that, we'll need to
+  // add it to what we buffer.  Also we'll need to ask for notification of a per-
+  // stream buffer-low event and merge that into the handling of buffer-low
+  // (the equivalent to TCP_NOTSENT_LOWAT on TCP sockets)
+
+  if (buffered > UINT32_MAX) { // paranoia - >4GB buffered is very very unlikely
+    buffered = UINT32_MAX;
   }
   return buffered;
 }
 
+uint32_t
+DataChannel::GetBufferedAmountLowThreshold()
+{
+  return mBufferedThreshold;
+}
+
+// Never fire immediately, as it's defined to fire on transitions, not state
+void
+DataChannel::SetBufferedAmountLowThreshold(uint32_t aThreshold)
+{
+  mBufferedThreshold = aThreshold;
+}
+
 // Called with mLock locked!
 void
 DataChannel::SendOrQueue(DataChannelOnMessageAvailable *aMessage)
diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h
index 227b98434d4..8ba1ba85235 100644
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -57,12 +57,12 @@ class BufferedMsg
 {
 public:
   BufferedMsg(struct sctp_sendv_spa &spa,const char *data,
-              uint32_t length);
+              size_t length);
   ~BufferedMsg();
 
   struct sctp_sendv_spa *mSpa;
   const char *mData;
-  uint32_t mLength;
+  size_t mLength;
 };
 
 // for queuing incoming data messages before the Open or
@@ -213,9 +213,9 @@ private:
                                  bool unordered, uint16_t prPolicy, uint32_t prValue);
   int32_t SendOpenAckMessage(uint16_t stream);
   int32_t SendMsgInternal(DataChannel *channel, const char *data,
-                          uint32_t length, uint32_t ppid);
+                          size_t length, uint32_t ppid);
   int32_t SendBinary(DataChannel *channel, const char *data,
-                     uint32_t len, uint32_t ppid_partial, uint32_t ppid_final);
+                     size_t len, uint32_t ppid_partial, uint32_t ppid_final);
   int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary);
 
   void DeliverQueuedData(uint16_t stream);
@@ -328,6 +328,7 @@ public:
     , mPrValue(value)
     , mFlags(flags)
     , mIsRecvBinary(false)
+    , mBufferedThreshold(0) // default from spec
     {
       NS_ASSERTION(mConnection,"NULL connection");
     }
@@ -387,6 +388,10 @@ public:
   // Amount of data buffered to send
   uint32_t GetBufferedAmount();
 
+  // Trigger amount for generating BufferedAmountLow events
+  uint32_t GetBufferedAmountLowThreshold();
+  void SetBufferedAmountLowThreshold(uint32_t aThreshold);
+
   // Find out state
   uint16_t GetReadyState()
     {
@@ -429,6 +434,7 @@ private:
   uint32_t mFlags;
   uint32_t mId;
   bool mIsRecvBinary;
+  size_t mBufferedThreshold;
   nsCString mRecvBuffer;
   nsTArray > mBufferedData;
   nsTArray > mQueuedMessages;
@@ -448,6 +454,7 @@ public:
     ON_CHANNEL_CLOSED,
     ON_DATA,
     START_DEFER,
+    BUFFER_LOW_THRESHOLD,
   };  /* types */
 
   DataChannelOnMessageAvailable(int32_t     aType,
@@ -489,6 +496,7 @@ public:
       case ON_DATA:
       case ON_CHANNEL_OPEN:
       case ON_CHANNEL_CLOSED:
+      case BUFFER_LOW_THRESHOLD:
         {
           MutexAutoLock lock(mChannel->mListenerLock);
           if (!mChannel->mListener) {
@@ -510,6 +518,9 @@ public:
             case ON_CHANNEL_CLOSED:
               mChannel->mListener->OnChannelClosed(mChannel->mContext);
               break;
+            case BUFFER_LOW_THRESHOLD:
+              mChannel->mListener->OnBufferLow(mChannel->mContext);
+              break;
           }
           break;
         }
diff --git a/netwerk/sctp/datachannel/DataChannelListener.h b/netwerk/sctp/datachannel/DataChannelListener.h
index e5ca0534831..2e4b1cf45b1 100644
--- a/netwerk/sctp/datachannel/DataChannelListener.h
+++ b/netwerk/sctp/datachannel/DataChannelListener.h
@@ -32,6 +32,9 @@ public:
 
   // Called when the channel is closed
   virtual nsresult OnChannelClosed(nsISupports *aContext) = 0;
+
+  // Called when the BufferedAmount drops below the BufferedAmountLowThreshold
+  virtual nsresult OnBufferLow(nsISupports *aContext) = 0;
 };
 
 }
diff --git a/python/mozbuild/mozbuild/backend/android_eclipse.py b/python/mozbuild/mozbuild/backend/android_eclipse.py
index 97508d13787..30bc0a676cd 100644
--- a/python/mozbuild/mozbuild/backend/android_eclipse.py
+++ b/python/mozbuild/mozbuild/backend/android_eclipse.py
@@ -24,6 +24,7 @@ from ..frontend.data import (
 )
 from ..makeutil import Makefile
 from ..util import ensureParentDir
+from mozbuild.base import ExecutionSummary
 
 
 def pretty_print(element):
@@ -38,42 +39,35 @@ class AndroidEclipseBackend(CommonBackend):
     """Backend that generates Android Eclipse project files.
     """
 
-    def _init(self):
-        CommonBackend._init(self)
-
-        def detailed(summary):
-            s = 'Wrote {:d} Android Eclipse projects to {:s}; ' \
-                '{:d} created; {:d} updated'.format(
-                summary.created_count + summary.updated_count,
-                mozpath.join(self.environment.topobjdir, 'android_eclipse'),
-                summary.created_count,
-                summary.updated_count)
-
-            return s
-
-        # This is a little kludgy and could be improved with a better API.
-        self.summary.backend_detailed_summary = types.MethodType(detailed,
-            self.summary)
+    def summary(self):
+        return ExecutionSummary(
+            'AndroidEclipse backend executed in {execution_time:.2f}s\n'
+            'Wrote {projects:d} Android Eclipse projects to {path:s}; '
+            '{created:d} created; {updated:d} updated',
+            execution_time=self._execution_time,
+            projects=self._created_count + self._updated_count,
+            path=mozpath.join(self.environment.topobjdir, 'android_eclipse'),
+            created=self._created_count,
+            updated=self._updated_count,
+        )
 
     def consume_object(self, obj):
         """Write out Android Eclipse project files."""
 
         if not isinstance(obj, ContextDerived):
-            return
+            return False
 
-        CommonBackend.consume_object(self, obj)
+        if CommonBackend.consume_object(self, obj):
+            # If CommonBackend acknowledged the object, we're done with it.
+            return True
 
-        # If CommonBackend acknowledged the object, we're done with it.
-        if obj._ack:
-            return
-
-        # We don't want to handle most things, so we just acknowledge all objects...
-        obj.ack()
-
-        # ... and handle the one case we care about specially.
+        # Handle the one case we care about specially.
         if isinstance(obj, ContextWrapped) and isinstance(obj.wrapped, AndroidEclipseProjectData):
             self._process_android_eclipse_project_data(obj.wrapped, obj.srcdir, obj.objdir)
 
+        # We don't want to handle most things, so we just acknowledge all objects
+        return True
+
     def consume_finished(self):
         """The common backend handles WebIDL and test files. We don't handle
         these, so we don't call our superclass.
@@ -257,7 +251,7 @@ class AndroidEclipseBackend(CommonBackend):
 
         # When we re-create the build backend, we kill everything that was there.
         if os.path.isdir(project_directory):
-            self.summary.updated_count += 1
+            self._updated_count += 1
         else:
-            self.summary.created_count += 1
+            self._created_count += 1
         copier.copy(project_directory, skip_if_older=False, remove_unaccounted=True)
diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py
index 5c67572d661..3a875ed90ed 100644
--- a/python/mozbuild/mozbuild/backend/base.py
+++ b/python/mozbuild/mozbuild/backend/base.py
@@ -21,102 +21,9 @@ import mozpack.path as mozpath
 from ..preprocessor import Preprocessor
 from ..pythonutil import iter_modules_in_path
 from ..util import FileAvoidWrite
-from ..frontend.data import (
-    ContextDerived,
-    ReaderSummary,
-)
+from ..frontend.data import ContextDerived
 from .configenvironment import ConfigEnvironment
-
-
-class BackendConsumeSummary(object):
-    """Holds state about what a backend did.
-
-    This is used primarily to print a summary of what the backend did
-    so people know what's going on.
-    """
-    def __init__(self):
-        # How many moz.build files were read. This includes included files.
-        self.mozbuild_count = 0
-
-        # The number of derived objects from the read moz.build files.
-        self.object_count = 0
-
-        # The number of backend files created.
-        self.created_count = 0
-
-        # The number of backend files updated.
-        self.updated_count = 0
-
-        # The number of unchanged backend files.
-        self.unchanged_count = 0
-
-        # The number of deleted backend files.
-        self.deleted_count = 0
-
-        # The total wall time this backend spent consuming objects. If
-        # the iterable passed into consume() is a generator, this includes the
-        # time spent to read moz.build files.
-        self.wall_time = 0.0
-
-        # CPU time spent by during the interval captured by wall_time.
-        self.cpu_time = 0.0
-
-        # The total wall time spent executing moz.build files. This is just
-        # the read and execute time. It does not cover consume time.
-        self.mozbuild_execution_time = 0.0
-
-        # The total wall time spent emitting objects from sandboxes.
-        self.emitter_execution_time = 0.0
-
-        # The total wall time spent in the backend. This counts the time the
-        # backend writes out files, etc.
-        self.backend_execution_time = 0.0
-
-        # How much wall time the system spent doing other things. This is
-        # wall_time - mozbuild_execution_time - emitter_execution_time -
-        # backend_execution_time.
-        self.other_time = 0.0
-
-        # Mapping of changed file paths to diffs of the changes.
-        self.file_diffs = {}
-
-    @property
-    def reader_summary(self):
-        return 'Finished reading {:d} moz.build files in {:.2f}s'.format(
-            self.mozbuild_count,
-            self.mozbuild_execution_time)
-
-    @property
-    def emitter_summary(self):
-        return 'Processed into {:d} build config descriptors in {:.2f}s'.format(
-            self.object_count, self.emitter_execution_time)
-
-    @property
-    def backend_summary(self):
-        return 'Backend executed in {:.2f}s'.format(self.backend_execution_time)
-
-    def backend_detailed_summary(self):
-        """Backend summary to be supplied by BuildBackend implementations."""
-        return None
-
-    @property
-    def total_summary(self):
-        efficiency_value = self.cpu_time / self.wall_time if self.wall_time else 100
-        return 'Total wall time: {:.2f}s; CPU time: {:.2f}s; Efficiency: ' \
-            '{:.0%}; Untracked: {:.2f}s'.format(
-                self.wall_time, self.cpu_time, efficiency_value,
-                self.other_time)
-
-    def summaries(self):
-        yield self.reader_summary
-        yield self.emitter_summary
-        yield self.backend_summary
-
-        detailed = self.backend_detailed_summary()
-        if detailed:
-            yield detailed
-
-        yield self.total_summary
+from mozbuild.base import ExecutionSummary
 
 
 class BuildBackend(LoggingMixin):
@@ -135,7 +42,6 @@ class BuildBackend(LoggingMixin):
         self.populate_logger()
 
         self.environment = environment
-        self.summary = BackendConsumeSummary()
 
         # Files whose modification should cause a new read and backend
         # generation.
@@ -155,8 +61,44 @@ class BuildBackend(LoggingMixin):
         self._environments = {}
         self._environments[environment.topobjdir] = environment
 
+        # The number of backend files created.
+        self._created_count = 0
+
+        # The number of backend files updated.
+        self._updated_count = 0
+
+        # The number of unchanged backend files.
+        self._unchanged_count = 0
+
+        # The number of deleted backend files.
+        self._deleted_count = 0
+
+        # The total wall time spent in the backend. This counts the time the
+        # backend writes out files, etc.
+        self._execution_time = 0.0
+
+        # Mapping of changed file paths to diffs of the changes.
+        self.file_diffs = {}
+
         self._init()
 
+    def summary(self):
+        return ExecutionSummary(
+            self.__class__.__name__.replace('Backend', '') +
+            ' backend executed in {execution_time:.2f}s\n  '
+            '{total:d} total backend files; '
+            '{created:d} created; '
+            '{updated:d} updated; '
+            '{unchanged:d} unchanged; '
+            '{deleted:d} deleted',
+            execution_time=self._execution_time,
+            total=self._created_count + self._updated_count +
+            self._unchanged_count,
+            created=self._created_count,
+            updated=self._updated_count,
+            unchanged=self._unchanged_count,
+            deleted=self._deleted_count)
+
     def _init():
         """Hook point for child classes to perform actions during __init__.
 
@@ -173,24 +115,15 @@ class BuildBackend(LoggingMixin):
         base class consumes objects and calls methods (possibly) implemented by
         child classes.
         """
-        cpu_start = time.clock()
-        time_start = time.time()
-        backend_time = 0.0
-
         for obj in objs:
-            self.summary.object_count += 1
             obj_start = time.time()
-            self.consume_object(obj)
-            backend_time += time.time() - obj_start
+            if not self.consume_object(obj):
+                raise Exception('Unhandled object of type %s' % type(obj))
+            self._execution_time += time.time() - obj_start
 
             if isinstance(obj, ContextDerived):
                 self.backend_input_files |= obj.context_all_paths
 
-            if isinstance(obj, ReaderSummary):
-                self.summary.mozbuild_count = obj.total_file_count
-                self.summary.mozbuild_execution_time = obj.total_sandbox_execution_time
-                self.summary.emitter_execution_time = obj.total_emitter_execution_time
-
         # Pull in all loaded Python as dependencies so any Python changes that
         # could influence our output result in a rescan.
         self.backend_input_files |= set(iter_modules_in_path(
@@ -198,14 +131,14 @@ class BuildBackend(LoggingMixin):
 
         finished_start = time.time()
         self.consume_finished()
-        backend_time += time.time() - finished_start
+        self._execution_time += time.time() - finished_start
 
         # Purge backend files created in previous run, but not created anymore
         delete_files = self._backend_output_list - self._backend_output_files
         for path in delete_files:
             try:
                 os.unlink(mozpath.join(self.environment.topobjdir, path))
-                self.summary.deleted_count += 1
+                self._deleted_count += 1
             except OSError:
                 pass
         # Remove now empty directories
@@ -216,24 +149,14 @@ class BuildBackend(LoggingMixin):
                 pass
 
         # Write out the list of backend files generated, if it changed.
-        if self.summary.deleted_count or self.summary.created_count or \
+        if self._deleted_count or self._created_count or \
                 not os.path.exists(self._backend_output_list_file):
             with open(self._backend_output_list_file, 'w') as fh:
                 fh.write('\n'.join(sorted(self._backend_output_files)))
-        elif self.summary.updated_count:
+        elif self._updated_count:
             with open(self._backend_output_list_file, 'a'):
                 os.utime(self._backend_output_list_file, None)
 
-        self.summary.cpu_time = time.clock() - cpu_start
-        self.summary.wall_time = time.time() - time_start
-        self.summary.backend_execution_time = backend_time
-        self.summary.other_time = self.summary.wall_time - \
-            self.summary.mozbuild_execution_time - \
-            self.summary.emitter_execution_time - \
-            self.summary.backend_execution_time
-
-        return self.summary
-
     @abstractmethod
     def consume_object(self, obj):
         """Consumes an individual TreeMetadata instance.
@@ -250,7 +173,7 @@ class BuildBackend(LoggingMixin):
         """Context manager to write a file.
 
         This is a glorified wrapper around FileAvoidWrite with integration to
-        update the BackendConsumeSummary on this instance.
+        update the summary data on this instance.
 
         Example usage:
 
@@ -276,13 +199,13 @@ class BuildBackend(LoggingMixin):
         self._backend_output_files.add(mozpath.relpath(fh.name, self.environment.topobjdir))
         existed, updated = fh.close()
         if not existed:
-            self.summary.created_count += 1
+            self._created_count += 1
         elif updated:
-            self.summary.updated_count += 1
+            self._updated_count += 1
             if fh.diff:
-                self.summary.file_diffs[fh.name] = fh.diff
+                self.file_diffs[fh.name] = fh.diff
         else:
-            self.summary.unchanged_count += 1
+            self._unchanged_count += 1
 
     @contextmanager
     def _get_preprocessor(self, obj):
diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py
index 3599220bde6..723a2ad1067 100644
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -206,7 +206,7 @@ class CommonBackend(BuildBackend):
             # Do not handle ConfigFileSubstitution for Makefiles. Leave that
             # to other
             if mozpath.basename(obj.output_path) == 'Makefile':
-                return
+                return False
             with self._get_preprocessor(obj) as pp:
                 pp.do_include(obj.input_path)
             self.backend_input_files.add(obj.input_path)
@@ -251,9 +251,9 @@ class CommonBackend(BuildBackend):
             if hasattr(self, '_process_unified_sources'):
                 self._process_unified_sources(obj)
         else:
-            return
+            return False
 
-        obj.ack()
+        return True
 
     def consume_finished(self):
         if len(self._idl_manager.idls):
diff --git a/python/mozbuild/mozbuild/backend/cpp_eclipse.py b/python/mozbuild/mozbuild/backend/cpp_eclipse.py
index a7b4a5548d7..51062ab8a09 100644
--- a/python/mozbuild/mozbuild/backend/cpp_eclipse.py
+++ b/python/mozbuild/mozbuild/backend/cpp_eclipse.py
@@ -15,6 +15,7 @@ from .common import CommonBackend
 from ..frontend.data import (
     Defines,
 )
+from mozbuild.base import ExecutionSummary
 
 # TODO Have ./mach eclipse generate the workspace and index it:
 # /Users/bgirard/mozilla/eclipse/eclipse/eclipse/eclipse -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data $PWD/workspace -importAll $PWD/eclipse
@@ -41,15 +42,15 @@ class CppEclipseBackend(CommonBackend):
         # Note: We need the C Pre Processor (CPP) flags, not the CXX flags
         self._cppflags = self.environment.substs.get('CPPFLAGS', '')
 
-        def detailed(summary):
-            return ('Generated Cpp Eclipse workspace in "%s".\n' + \
-                   'If missing, import the project using File > Import > General > Existing Project into workspace\n' + \
-                   '\n' + \
-                   'Run with: eclipse -data %s\n') \
-                   % (self._workspace_dir, self._workspace_dir)
-
-        self.summary.backend_detailed_summary = types.MethodType(detailed,
-            self.summary)
+    def summary(self):
+        return ExecutionSummary(
+            'CppEclipse backend executed in {execution_time:.2f}s\n'
+            'Generated Cpp Eclipse workspace in "{workspace:s}".\n'
+            'If missing, import the project using File > Import > General > Existing Project into workspace\n'
+            '\n'
+            'Run with: eclipse -data {workspace:s}\n',
+            execution_time=self._execution_time,
+            workspace=self._workspace_dir)
 
     def _get_workspace_path(self):
         return CppEclipseBackend.get_workspace_path(self.environment.topsrcdir, self.environment.topobjdir)
@@ -64,8 +65,6 @@ class CppEclipseBackend(CommonBackend):
         return os.path.join(srcdir_parent, workspace_dirname)
 
     def consume_object(self, obj):
-        obj.ack()
-
         reldir = getattr(obj, 'relativedir', None)
 
         # Note that unlike VS, Eclipse' indexer seem to crawl the headers and
@@ -73,6 +72,8 @@ class CppEclipseBackend(CommonBackend):
         if isinstance(obj, Defines):
             self._paths_to_defines.setdefault(reldir, {}).update(obj.defines)
 
+        return True
+
     def consume_finished(self):
         settings_dir = os.path.join(self._project_dir, '.settings')
         launch_dir = os.path.join(self._project_dir, 'RunConfigurations')
diff --git a/python/mozbuild/mozbuild/backend/fastermake.py b/python/mozbuild/mozbuild/backend/fastermake.py
new file mode 100644
index 00000000000..1ceea74e25a
--- /dev/null
+++ b/python/mozbuild/mozbuild/backend/fastermake.py
@@ -0,0 +1,257 @@
+# 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/.
+
+from __future__ import absolute_import, unicode_literals
+
+from mozbuild.backend.common import CommonBackend
+from mozbuild.frontend.data import (
+    ContextDerived,
+    Defines,
+    DistFiles,
+    FinalTargetFiles,
+    JARManifest,
+    JavaScriptModules,
+    JsPreferenceFile,
+    Resources,
+    VariablePassthru,
+)
+from mozbuild.makeutil import Makefile
+from mozbuild.util import OrderedDefaultDict
+from mozpack.manifests import InstallManifest
+import mozpack.path as mozpath
+from collections import OrderedDict
+from itertools import chain
+
+
+class FasterMakeBackend(CommonBackend):
+    def _init(self):
+        super(FasterMakeBackend, self)._init()
+
+        self._seen_directories = set()
+        self._defines = dict()
+        self._jar_manifests = OrderedDict()
+
+        self._preprocess_files = OrderedDict()
+
+        self._manifest_entries = OrderedDefaultDict(list)
+
+        self._install_manifests = OrderedDefaultDict(InstallManifest)
+
+    def consume_object(self, obj):
+        if not isinstance(obj, Defines) and isinstance(obj, ContextDerived):
+            defines = self._defines.get(obj.objdir, [])
+            if defines:
+                defines = list(defines.get_defines())
+
+        if isinstance(obj, Defines):
+            self._defines[obj.objdir] = obj
+
+            # We're assuming below that Defines come first for a given objdir,
+            # which is kind of set in stone from the order things are treated
+            # in emitter.py.
+            assert obj.objdir not in self._seen_directories
+
+        elif isinstance(obj, JARManifest) and \
+                obj.install_target.startswith('dist/bin'):
+            self._jar_manifests[obj.path] = (obj.objdir,
+                                             obj.install_target,
+                                             defines)
+
+        elif isinstance(obj, VariablePassthru) and \
+                obj.install_target.startswith('dist/bin'):
+            for f in obj.variables.get('EXTRA_COMPONENTS', {}):
+                path = mozpath.join(obj.install_target, 'components',
+                                    mozpath.basename(f))
+                self._install_manifests[obj.install_target].add_symlink(
+                    mozpath.join(obj.srcdir, f),
+                    mozpath.join('components', mozpath.basename(f))
+                )
+                if f.endswith('.manifest'):
+                    manifest = mozpath.join(obj.install_target,
+                                            'chrome.manifest')
+                    self._manifest_entries[manifest].append(
+                        'manifest components/%s' % mozpath.basename(f))
+
+            for f in obj.variables.get('EXTRA_PP_COMPONENTS', {}):
+                path = mozpath.join(obj.install_target, 'components',
+                                    mozpath.basename(f))
+                self._preprocess_files[path] = (obj.srcdir, f, defines)
+                if f.endswith('.manifest'):
+                    manifest = mozpath.join(obj.install_target,
+                                            'chrome.manifest')
+                    self._manifest_entries[manifest].append(
+                        'manifest components/%s' % mozpath.basename(f))
+
+        elif isinstance(obj, JavaScriptModules) and \
+                obj.install_target.startswith('dist/bin'):
+            for path, strings in obj.modules.walk():
+                base = mozpath.join(obj.install_target, 'modules', path)
+                for f in strings:
+                    if obj.flavor == 'extra':
+                        self._install_manifests[obj.install_target].add_symlink(
+                            mozpath.join(obj.srcdir, f),
+                            mozpath.join('modules', path, mozpath.basename(f))
+                        )
+                    elif obj.flavor == 'extra_pp':
+                        dest = mozpath.join(base, mozpath.basename(f))
+                        self._preprocess_files[dest] = (obj.srcdir, f, defines)
+
+        elif isinstance(obj, JsPreferenceFile) and \
+                obj.install_target.startswith('dist/bin'):
+            # The condition for the directory value in config/rules.mk is:
+            # ifneq (,$(DIST_SUBDIR)$(XPI_NAME)$(LIBXUL_SDK))
+            # - LIBXUL_SDK is not supported (it likely doesn't work in the
+            # recursive backend anyways
+            # - when XPI_NAME is set, obj.install_target will start with
+            # dist/xpi-stage
+            # - when DIST_SUBDIR is set, obj.install_target will start with
+            # dist/bin/$(DIST_SUBDIR)
+            # So an equivalent condition that is not cumbersome for us and that
+            # is enough at least for now is checking if obj.install_target is
+            # different from dist/bin.
+            if obj.install_target == 'dist/bin':
+                pref_dir = 'defaults/pref'
+            else:
+                pref_dir = 'defaults/preferences'
+
+            dest = mozpath.join(obj.install_target, pref_dir,
+                                mozpath.basename(obj.path))
+            # on win32, pref files need CRLF line endings... see bug 206029
+            if self.environment.substs['OS_ARCH'] == 'WINNT':
+                defines.append('--line-endings=crlf')
+            # We preprocess these, but they don't necessarily have preprocessor
+            # directives, so tell the preprocessor to not complain about that.
+            defines.append('--silence-missing-directive-warnings')
+            self._preprocess_files[dest] = (obj.srcdir, obj.path, defines)
+
+        elif isinstance(obj, Resources) and \
+                obj.install_target.startswith('dist/bin'):
+            for path, strings in obj.resources.walk():
+                base = mozpath.join(obj.install_target, 'res', path)
+                for f in strings:
+                    flags = strings.flags_for(f)
+                    if flags and flags.preprocess:
+                        dest = mozpath.join(base, mozpath.basename(f))
+                        defines = Defines(obj._context, obj.defines)
+                        defines = list(defines.get_defines())
+                        defines.extend(['--marker', '%'])
+                        self._preprocess_files[dest] = (obj.srcdir, f, defines)
+                    else:
+                        self._install_manifests[obj.install_target].add_symlink(
+                            mozpath.join(obj.srcdir, f),
+                            mozpath.join('res', path, mozpath.basename(f))
+                        )
+
+        elif isinstance(obj, FinalTargetFiles) and \
+                obj.install_target.startswith('dist/bin'):
+            for path, strings in obj.files.walk():
+                base = mozpath.join(obj.install_target, path)
+                for f in strings:
+                    self._install_manifests[obj.install_target].add_symlink(
+                        mozpath.join(obj.srcdir, f),
+                        mozpath.join(path, mozpath.basename(f))
+                    )
+
+        elif isinstance(obj, DistFiles) and \
+                obj.install_target.startswith('dist/bin'):
+            # We preprocess these, but they don't necessarily have preprocessor
+            # directives, so tell the preprocessor to not complain about that.
+            defines.append('--silence-missing-directive-warnings')
+            for f in obj.files:
+                dest = mozpath.join(obj.install_target, mozpath.basename(f))
+                self._preprocess_files[dest] = (obj.srcdir, f, defines)
+
+        else:
+            # We currently ignore a lot of object types, so just acknowledge
+            # everything.
+            return True
+
+        self._seen_directories.add(obj.objdir)
+        return True
+
+    def consume_finished(self):
+        mk = Makefile()
+        # Add the default rule at the very beginning.
+        mk.create_rule(['default'])
+        mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir)
+        mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir)
+
+        # Add a few necessary variables inherited from configure
+        for var in (
+            'PYTHON',
+            'ACDEFINES',
+            'MOZ_CHROME_FILE_FORMAT',
+        ):
+            mk.add_statement('%s = %s' % (var, self.environment.substs[var]))
+
+        # Add all necessary information for jar manifest processing
+        jar_mn_targets = []
+
+        for path, (objdir, install_target, defines) in \
+                self._jar_manifests.iteritems():
+            rel_manifest = mozpath.relpath(path, self.environment.topsrcdir)
+            target = rel_manifest.replace('/', '-')
+            assert target not in jar_mn_targets
+            jar_mn_targets.append(target)
+            target = 'jar-%s' % target
+            mk.create_rule([target]).add_dependencies([path])
+            if objdir != mozpath.join(self.environment.topobjdir,
+                                      mozpath.dirname(rel_manifest)):
+                mk.create_rule([target]).add_dependencies(
+                    ['objdir = %s' % objdir])
+            if install_target != 'dist/bin':
+                mk.create_rule([target]).add_dependencies(
+                    ['install_target = %s' % install_target])
+            if defines:
+                mk.create_rule([target]).add_dependencies(
+                    ['defines = %s' % ' '.join(defines)])
+
+        mk.add_statement('JAR_MN_TARGETS = %s' % ' '.join(jar_mn_targets))
+
+        # Add information for chrome manifest generation
+        manifest_targets = []
+
+        for target, entries in self._manifest_entries.iteritems():
+            manifest_targets.append(target)
+            target = '$(TOPOBJDIR)/%s' % target
+            mk.create_rule([target]).add_dependencies(
+                ['content = %s' % ' '.join('"%s"' % e for e in entries)])
+
+        mk.add_statement('MANIFEST_TARGETS = %s' % ' '.join(manifest_targets))
+
+        # Add information for preprocessed files.
+        preprocess_targets = []
+
+        for target, (srcdir, f, defines) in self._preprocess_files.iteritems():
+            # This matches what PP_TARGETS do in config/rules.
+            if target.endswith('.in'):
+                target = target[:-3]
+                # PP_TARGETS assumes this is true, but doesn't enforce it.
+                assert target not in self._preprocess_files
+            preprocess_targets.append(target)
+            target = '$(TOPOBJDIR)/%s' % target
+            mk.create_rule([target]).add_dependencies(
+                [mozpath.join(srcdir, f)])
+            if defines:
+                mk.create_rule([target]).add_dependencies(
+                        ['defines = %s' % ' '.join(defines)])
+
+        mk.add_statement('PP_TARGETS = %s' % ' '.join(preprocess_targets))
+
+        # Add information for install manifests.
+        mk.add_statement('INSTALL_MANIFESTS = %s'
+                         % ' '.join(self._install_manifests.keys()))
+
+        mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk')
+
+        for base, install_manifest in self._install_manifests.iteritems():
+            with self._write_file(
+                    mozpath.join(self.environment.topobjdir, 'faster',
+                                 'install_%s' % base.replace('/', '_'))) as fh:
+                install_manifest.write(fileobj=fh)
+
+        with self._write_file(
+                mozpath.join(self.environment.topobjdir, 'faster',
+                             'Makefile')) as fh:
+            mk.dump(fh, removal_guard=False)
diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py
index d2e8daa743c..9cabd717195 100644
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -376,26 +376,8 @@ class RecursiveMakeBackend(CommonBackend):
         self._backend_files = {}
         self._idl_dirs = set()
 
-        def detailed(summary):
-            s = '{:d} total backend files; ' \
-                '{:d} created; {:d} updated; {:d} unchanged; ' \
-                '{:d} deleted; {:d} -> {:d} Makefile'.format(
-                summary.created_count + summary.updated_count +
-                summary.unchanged_count,
-                summary.created_count,
-                summary.updated_count,
-                summary.unchanged_count,
-                summary.deleted_count,
-                summary.makefile_in_count,
-                summary.makefile_out_count)
-
-            return s
-
-        # This is a little kludgy and could be improved with a better API.
-        self.summary.backend_detailed_summary = types.MethodType(detailed,
-            self.summary)
-        self.summary.makefile_in_count = 0
-        self.summary.makefile_out_count = 0
+        self._makefile_in_count = 0
+        self._makefile_out_count = 0
 
         self._test_manifests = {}
 
@@ -428,6 +410,13 @@ class RecursiveMakeBackend(CommonBackend):
             'tools': set(),
         }
 
+    def summary(self):
+        summary = super(RecursiveMakeBackend, self).summary()
+        summary.extend('; {makefile_in:d} -> {makefile_out:d} Makefile',
+                       makefile_in=self._makefile_in_count,
+                       makefile_out=self._makefile_out_count)
+        return summary
+
     def _get_backend_file_for(self, obj):
         if obj.objdir not in self._backend_files:
             self._backend_files[obj.objdir] = \
@@ -439,11 +428,11 @@ class RecursiveMakeBackend(CommonBackend):
         """Write out build files necessary to build with recursive make."""
 
         if not isinstance(obj, ContextDerived):
-            return
+            return False
 
         backend_file = self._get_backend_file_for(obj)
 
-        CommonBackend.consume_object(self, obj)
+        consumed = CommonBackend.consume_object(self, obj)
 
         # CommonBackend handles XPIDLFile and TestManifest, but we want to do
         # some extra things for them.
@@ -456,8 +445,8 @@ class RecursiveMakeBackend(CommonBackend):
             self._process_test_manifest(obj, backend_file)
 
         # If CommonBackend acknowledged the object, we're done with it.
-        if obj._ack:
-            return
+        if consumed:
+            return True
 
         if isinstance(obj, DirectoryTraversal):
             self._process_directory_traversal(obj, backend_file)
@@ -587,7 +576,7 @@ class RecursiveMakeBackend(CommonBackend):
             elif isinstance(obj.wrapped, AndroidEclipseProjectData):
                 self._process_android_eclipse_project_data(obj.wrapped, backend_file)
             else:
-                return
+                return False
 
         elif isinstance(obj, SharedLibrary):
             self._process_shared_library(obj, backend_file)
@@ -629,8 +618,9 @@ class RecursiveMakeBackend(CommonBackend):
                 backend_file.write('ANDROID_EXTRA_PACKAGES += %s\n' % p)
 
         else:
-            return
-        obj.ack()
+            return False
+
+        return True
 
     def _fill_root_mk(self):
         """
@@ -802,7 +792,7 @@ class RecursiveMakeBackend(CommonBackend):
                 if not stub:
                     self.log(logging.DEBUG, 'substitute_makefile',
                         {'path': makefile}, 'Substituting makefile: {path}')
-                    self.summary.makefile_in_count += 1
+                    self._makefile_in_count += 1
 
                     for tier, skiplist in self._may_skip.items():
                         if bf.relobjdir in skiplist:
@@ -1451,7 +1441,7 @@ INSTALL_TARGETS += %(prefix)s
             # the autogenerated one automatically.
             self.backend_input_files.add(obj.input_path)
 
-        self.summary.makefile_out_count += 1
+        self._makefile_out_count += 1
 
     def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
                              unified_ipdl_cppsrcs_mapping):
diff --git a/python/mozbuild/mozbuild/backend/visualstudio.py b/python/mozbuild/mozbuild/backend/visualstudio.py
index 05865305c12..51dd2e298b6 100644
--- a/python/mozbuild/mozbuild/backend/visualstudio.py
+++ b/python/mozbuild/mozbuild/backend/visualstudio.py
@@ -27,6 +27,7 @@ from ..frontend.data import (
     Sources,
     UnifiedSources,
 )
+from mozbuild.base import ExecutionSummary
 
 
 MSBUILD_NAMESPACE = 'http://schemas.microsoft.com/developer/msbuild/2003'
@@ -101,17 +102,14 @@ class VisualStudioBackend(CommonBackend):
         self._paths_to_configs = {}
         self._libs_to_paths = {}
 
-        def detailed(summary):
-            return 'Generated Visual Studio solution at %s' % (
-                os.path.join(self._out_dir, 'mozilla.sln'))
-
-        self.summary.backend_detailed_summary = types.MethodType(detailed,
-            self.summary)
+    def summary(self):
+        return ExecutionSummary(
+            'VisualStudio backend executed in {execution_time:.2f}s\n'
+            'Generated Visual Studio solution at {path:s}',
+            execution_time=self._execution_time,
+            path=os.path.join(self._out_dir, 'mozilla.sln'))
 
     def consume_object(self, obj):
-        # Just acknowledge everything.
-        obj.ack()
-
         reldir = getattr(obj, 'relativedir', None)
 
         if hasattr(obj, 'config') and reldir not in self._paths_to_configs:
@@ -146,6 +144,9 @@ class VisualStudioBackend(CommonBackend):
             else:
                 includes.append(os.path.join('$(TopSrcDir)', reldir, p))
 
+        # Just acknowledge everything.
+        return True
+
     def _add_sources(self, reldir, obj):
         s = self._paths_to_sources.setdefault(reldir, set())
         s.update(obj.files)
diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py
index 8b4ea73f5db..3a1bb9caee4 100644
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -843,3 +843,22 @@ class PathArgument(object):
 
     def objdir_path(self):
         return mozpath.join(self.topobjdir, self.relpath())
+
+
+class ExecutionSummary(dict):
+    """Helper for execution summaries."""
+
+    def __init__(self, summary_format, **data):
+        self._summary_format = ''
+        assert 'execution_time' in data
+        self.extend(summary_format, **data)
+
+    def extend(self, summary_format, **data):
+        self._summary_format += summary_format
+        self.update(data)
+
+    def __str__(self):
+        return self._summary_format.format(**self)
+
+    def __getattr__(self, key):
+        return self[key]
diff --git a/python/mozbuild/mozbuild/compilation/codecomplete.py b/python/mozbuild/mozbuild/compilation/codecomplete.py
index 9e2a7c7f642..be8d698715e 100644
--- a/python/mozbuild/mozbuild/compilation/codecomplete.py
+++ b/python/mozbuild/mozbuild/compilation/codecomplete.py
@@ -26,12 +26,9 @@ class Introspection(MachCommandBase):
         help='Source file to display compilation flags for')
     def compileflags(self, what):
         from mozbuild.util import resolve_target_to_make
-        import shlex
+        from mozbuild.compilation import util
 
-        top_make = os.path.join(self.topobjdir, 'Makefile')
-        if not os.path.exists(top_make):
-            print('Your tree has not been built yet. Please run '
-                '|mach build| with no arguments.')
+        if not util.check_top_objdir(self.topobjdir):
             return 1
 
         path_arg = self._wrap_path_argument(what)
@@ -42,23 +39,7 @@ class Introspection(MachCommandBase):
         if make_dir is None and make_target is None:
             return 1
 
-        build_vars = {}
-
-        def on_line(line):
-            elements = [s.strip() for s in line.split('=', 1)]
-
-            if len(elements) != 2:
-                return
-
-            build_vars[elements[0]] = elements[1]
-
-        try:
-            old_logger = self.log_manager.replace_terminal_handler(None)
-            self._run_make(directory=make_dir, target='showbuild', log=False,
-                    print_directory=False, allow_parallel=False, silent=True,
-                    line_handler=on_line)
-        finally:
-            self.log_manager.replace_terminal_handler(old_logger)
+        build_vars = util.get_build_vars(make_dir, self)
 
         if what.endswith('.c'):
             name = 'COMPILE_CFLAGS'
@@ -68,20 +49,5 @@ class Introspection(MachCommandBase):
         if name not in build_vars:
             return
 
-        flags = ['-isystem', '-I', '-include', '-MF']
-        new_args = []
-        path = os.path.join(self.topobjdir, make_dir)
-        for arg in shlex.split(build_vars[name]):
-            if new_args and new_args[-1] in flags:
-                arg = os.path.normpath(os.path.join(path, arg))
-            else:
-                flag = [(f, arg[len(f):]) for f in flags + ['--sysroot=']
-                        if arg.startswith(f)]
-                if flag:
-                    flag, val = flag[0]
-                    if val:
-                        arg = flag + os.path.normpath(os.path.join(path, val))
-            new_args.append(arg)
-
-        print(' '.join(new_args))
+        print(util.get_flags(self.topobjdir, make_dir, build_vars, name))
 
diff --git a/python/mozbuild/mozbuild/compilation/database.py b/python/mozbuild/mozbuild/compilation/database.py
new file mode 100644
index 00000000000..c0bd3b867e1
--- /dev/null
+++ b/python/mozbuild/mozbuild/compilation/database.py
@@ -0,0 +1,119 @@
+# 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 modules provides functionality for dealing with code completion.
+
+import os
+
+from mozbuild.base import MozbuildObject
+from mozbuild.compilation import util
+from mozbuild.backend.common import CommonBackend
+from mozbuild.frontend.data import (
+    Sources,
+    HostSources,
+    UnifiedSources,
+    GeneratedSources,
+)
+from mach.config import ConfigSettings
+from mach.logging import LoggingManager
+
+
+class CompileDBBackend(CommonBackend):
+    def _init(self):
+        CommonBackend._init(self)
+        if not util.check_top_objdir(self.environment.topobjdir):
+            raise Exception()
+
+        # The database we're going to dump out to.
+        self._db = []
+
+        # The cache for per-directory flags
+        self._flags = {}
+
+        log_manager = LoggingManager()
+        self._cmd = MozbuildObject(self.environment.topsrcdir, ConfigSettings(),
+                                   log_manager, self.environment.topobjdir)
+
+
+    def consume_object(self, obj):
+        if isinstance(obj, UnifiedSources):
+            # For unified sources, only include the unified source file.
+            # Note that unified sources are never used for host sources.
+            for f in obj.unified_source_mapping:
+                flags = self._get_dir_flags(obj.objdir)
+                self._build_db_line(obj, self.environment, f[0],
+                                    obj.canonical_suffix, flags, False)
+        elif isinstance(obj, Sources) or isinstance(obj, HostSources) or \
+             isinstance(obj, GeneratedSources):
+            # For other sources, include each source file.
+            for f in obj.files:
+                flags = self._get_dir_flags(obj.objdir)
+                self._build_db_line(obj, self.environment, f,
+                                    obj.canonical_suffix, flags,
+                                    isinstance(obj, HostSources))
+
+        return True
+
+    def consume_finished(self):
+        import json
+        # Output the database (a JSON file) to objdir/compile_commands.json
+        outputfile = os.path.join(self.environment.topobjdir, 'compile_commands.json')
+        with self._write_file(outputfile) as jsonout:
+            json.dump(self._db, jsonout, indent=0)
+
+    def _get_dir_flags(self, directory):
+        if directory in self._flags:
+            return self._flags[directory]
+
+        from mozbuild.util import resolve_target_to_make
+
+        make_dir, make_target = resolve_target_to_make(self.environment.topobjdir, directory)
+        if make_dir is None and make_target is None:
+            raise Exception('Cannot figure out the make dir and target for ' + directory)
+
+        build_vars = util.get_build_vars(directory, self._cmd)
+
+        # We only care about the following build variables.
+        for name in ('COMPILE_CFLAGS', 'COMPILE_CXXFLAGS',
+                     'COMPILE_CMFLAGS', 'COMPILE_CMMFLAGS'):
+            if name not in build_vars:
+                continue
+
+            build_vars[name] = util.get_flags(self.environment.topobjdir, directory,
+                                              build_vars, name)
+
+        self._flags[directory] = build_vars
+        return self._flags[directory]
+
+    def _build_db_line(self, obj, cenv, filename, canonical_suffix, flags, ishost):
+        # Distinguish between host and target files.
+        prefix = 'HOST_' if ishost else ''
+        if canonical_suffix == '.c':
+            compiler = cenv.substs[prefix + 'CC']
+            cflags = flags['COMPILE_CFLAGS']
+            # Add the Objective-C flags if needed.
+            if filename.endswith('.m'):
+                cflags += ' ' + flags['COMPILE_CMFLAGS']
+        elif canonical_suffix == '.cpp':
+            compiler = cenv.substs[prefix + 'CXX']
+            cflags = flags['COMPILE_CXXFLAGS']
+            # Add the Objective-C++ flags if needed.
+            if filename.endswith('.mm'):
+                cflags += ' ' + flags['COMPILE_CMMFLAGS']
+        else:
+            return
+
+        cmd = ' '.join([
+          compiler,
+          '-o', '/dev/null', '-c',
+          cflags,
+          filename,
+        ])
+
+        self._db.append({
+            'directory': obj.objdir,
+            'command': cmd,
+            'file': filename
+        })
+
diff --git a/python/mozbuild/mozbuild/compilation/util.py b/python/mozbuild/mozbuild/compilation/util.py
new file mode 100644
index 00000000000..8610ece772e
--- /dev/null
+++ b/python/mozbuild/mozbuild/compilation/util.py
@@ -0,0 +1,63 @@
+# 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/.
+
+import os
+import shlex
+
+def check_top_objdir(topobjdir):
+    top_make = os.path.join(topobjdir, 'Makefile')
+    if not os.path.exists(top_make):
+        print('Your tree has not been built yet. Please run '
+            '|mach build| with no arguments.')
+        return False
+    return True
+
+def get_build_vars(directory, cmd):
+    build_vars = {}
+
+    def on_line(line):
+        elements = [s.strip() for s in line.split('=', 1)]
+
+        if len(elements) != 2:
+            return
+
+        build_vars[elements[0]] = elements[1]
+
+    try:
+        old_logger = cmd.log_manager.replace_terminal_handler(None)
+        cmd._run_make(directory=directory, target='showbuild', log=False,
+                print_directory=False, allow_parallel=False, silent=True,
+                line_handler=on_line)
+    finally:
+        cmd.log_manager.replace_terminal_handler(old_logger)
+
+    return build_vars
+
+def get_flags(topobjdir, make_dir, build_vars, name):
+    flags = ['-isystem', '-I', '-include', '-MF']
+    new_args = []
+    path = os.path.join(topobjdir, make_dir)
+
+    # Take case to handle things such as the following correctly:
+    #   * -DMOZ_APP_VERSION='"40.0a1"'
+    #   * -DR_PLATFORM_INT_TYPES=''
+    #   * -DAPP_ID='{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+    #   * -D__UNUSED__='__attribute__((unused))'
+    lex = shlex.shlex(build_vars[name])
+    lex.quotes = '"'
+    lex.wordchars += '+/\'"-=.*{}()[]<>'
+
+    for arg in list(lex):
+        if new_args and new_args[-1] in flags:
+            arg = os.path.normpath(os.path.join(path, arg))
+        else:
+            flag = [(f, arg[len(f):]) for f in flags + ['--sysroot=']
+                    if arg.startswith(f)]
+            if flag:
+                flag, val = flag[0]
+                if val:
+                    arg = flag + os.path.normpath(os.path.join(path, val))
+        new_args.append(arg)
+
+    return ' '.join(new_args)
diff --git a/python/mozbuild/mozbuild/config_status.py b/python/mozbuild/mozbuild/config_status.py
index d78fb3fabdd..94a811e7382 100644
--- a/python/mozbuild/mozbuild/config_status.py
+++ b/python/mozbuild/mozbuild/config_status.py
@@ -11,8 +11,9 @@ from __future__ import absolute_import, print_function
 import logging
 import os
 import sys
+import time
 
-from optparse import OptionParser
+from argparse import ArgumentParser
 
 from mach.logging import LoggingManager
 from mozbuild.backend.configenvironment import ConfigEnvironment
@@ -21,6 +22,7 @@ from mozbuild.base import MachCommandConditions
 from mozbuild.frontend.emitter import TreeMetadataEmitter
 from mozbuild.frontend.reader import BuildReader
 from mozbuild.mozinfo import write_mozinfo
+from itertools import chain
 
 
 log_manager = LoggingManager()
@@ -86,20 +88,31 @@ def config_status(topobjdir='.', topsrcdir='.',
         raise Exception('topsrcdir must be defined as an absolute directory: '
             '%s' % topsrcdir)
 
-    parser = OptionParser()
-    parser.add_option('--recheck', dest='recheck', action='store_true',
-                      help='update config.status by reconfiguring in the same conditions')
-    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
-                      help='display verbose output')
-    parser.add_option('-n', dest='not_topobjdir', action='store_true',
-                      help='do not consider current directory as top object directory')
-    parser.add_option('-d', '--diff', action='store_true',
-                      help='print diffs of changed files.')
-    parser.add_option('-b', '--backend',
-                      choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', 'VisualStudio'],
-                      default='RecursiveMake',
-                      help='what backend to build (default: RecursiveMake).')
-    options, args = parser.parse_args()
+    default_backends = ['RecursiveMake']
+    # We have a chicken/egg problem, where we only have a dict for substs after
+    # creating the ConfigEnvironment, which requires argument parsing to have
+    # occurred.
+    for name, value in substs:
+        if name == 'BUILD_BACKENDS':
+            default_backends = value
+            break
+
+    parser = ArgumentParser()
+    parser.add_argument('--recheck', dest='recheck', action='store_true',
+                        help='update config.status by reconfiguring in the same conditions')
+    parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
+                        help='display verbose output')
+    parser.add_argument('-n', dest='not_topobjdir', action='store_true',
+                        help='do not consider current directory as top object directory')
+    parser.add_argument('-d', '--diff', action='store_true',
+                        help='print diffs of changed files.')
+    parser.add_argument('-b', '--backend', nargs='+',
+                        choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse',
+                                 'VisualStudio', 'FasterMake', 'CompileDB'],
+                        default=default_backends,
+                        help='what backend to build (default: %s).' %
+                        ' '.join(default_backends))
+    options = parser.parse_args()
 
     # Without -n, the current directory is meant to be the top object directory
     if not options.not_topobjdir:
@@ -114,22 +127,34 @@ def config_status(topobjdir='.', topsrcdir='.',
         write_mozinfo(os.path.join(topobjdir, 'mozinfo.json'), env, os.environ)
 
     # Make an appropriate backend instance, defaulting to RecursiveMakeBackend.
-    backend_cls = RecursiveMakeBackend
-    if options.backend == 'AndroidEclipse':
-        from mozbuild.backend.android_eclipse import AndroidEclipseBackend
-        if not MachCommandConditions.is_android(env):
-            raise Exception('The Android Eclipse backend is not available with this configuration.')
-        backend_cls = AndroidEclipseBackend
-    elif options.backend == 'CppEclipse':
-        from mozbuild.backend.cpp_eclipse import CppEclipseBackend
-        backend_cls = CppEclipseBackend
-        if os.name == 'nt':
-          raise Exception('Eclipse is not supported on Windows. Consider using Visual Studio instead.')
-    elif options.backend == 'VisualStudio':
-        from mozbuild.backend.visualstudio import VisualStudioBackend
-        backend_cls = VisualStudioBackend
+    backends_cls = []
+    for backend in options.backend:
+        if backend == 'AndroidEclipse':
+            from mozbuild.backend.android_eclipse import AndroidEclipseBackend
+            if not MachCommandConditions.is_android(env):
+                raise Exception('The Android Eclipse backend is not available with this configuration.')
+            backends_cls.append(AndroidEclipseBackend)
+        elif backend == 'CppEclipse':
+            from mozbuild.backend.cpp_eclipse import CppEclipseBackend
+            backends_cls.append(CppEclipseBackend)
+            if os.name == 'nt':
+              raise Exception('Eclipse is not supported on Windows. Consider using Visual Studio instead.')
+        elif backend == 'VisualStudio':
+            from mozbuild.backend.visualstudio import VisualStudioBackend
+            backends_cls.append(VisualStudioBackend)
+        elif backend == 'FasterMake':
+            from mozbuild.backend.fastermake import FasterMakeBackend
+            backends_cls.append(FasterMakeBackend)
+        elif backend == 'CompileDB':
+            from mozbuild.compilation.database import CompileDBBackend
+            backends_cls.append(CompileDBBackend)
+        else:
+            backends_cls.append(RecursiveMakeBackend)
 
-    the_backend = backend_cls(env)
+    cpu_start = time.clock()
+    time_start = time.time()
+
+    backends = [cls(env) for cls in backends_cls]
 
     reader = BuildReader(env)
     emitter = TreeMetadataEmitter(env)
@@ -146,20 +171,40 @@ def config_status(topobjdir='.', topsrcdir='.',
     log_manager.enable_unstructured()
 
     print('Reticulating splines...', file=sys.stderr)
-    summary = the_backend.consume(definitions)
+    if len(backends) > 1:
+        definitions = list(definitions)
 
-    for line in summary.summaries():
-        print(line, file=sys.stderr)
+    for the_backend in backends:
+        the_backend.consume(definitions)
+
+    execution_time = 0.0
+    for obj in chain((reader, emitter), backends):
+        summary = obj.summary()
+        print(summary, file=sys.stderr)
+        execution_time += summary.execution_time
+
+    cpu_time = time.clock() - cpu_start
+    wall_time = time.time() - time_start
+    efficiency = cpu_time / wall_time if wall_time else 100
+    untracked = wall_time - execution_time
+
+    print(
+        'Total wall time: {:.2f}s; CPU time: {:.2f}s; Efficiency: '
+        '{:.0%}; Untracked: {:.2f}s'.format(
+            wall_time, cpu_time, efficiency, untracked),
+        file=sys.stderr
+    )
 
     if options.diff:
-        for path, diff in sorted(summary.file_diffs.items()):
-            print('\n'.join(diff))
+        for the_backend in backends:
+            for path, diff in sorted(the_backend.file_diffs.items()):
+                print('\n'.join(diff))
 
     # Advertise Visual Studio if appropriate.
-    if os.name == 'nt' and options.backend == 'RecursiveMake':
+    if os.name == 'nt' and 'VisualStudio' not in options.backend:
         print(VISUAL_STUDIO_ADVERTISEMENT)
 
     # Advertise Eclipse if it is appropriate.
     if MachCommandConditions.is_android(env):
-        if options.backend == 'RecursiveMake':
+        if 'AndroidEclipse' not in options.backend:
             print(ANDROID_IDE_ADVERTISEMENT)
diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py
index e8c8d80c841..b89f5c35c5a 100644
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -88,7 +88,6 @@ class Context(KeyedDefaultDict):
         # a list to be a problem.
         self._all_paths = []
         self.config = config
-        self.execution_time = 0
         self._sandbox = None
         KeyedDefaultDict.__init__(self, self._factory)
 
diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py
index 84097e34be5..4352c96db70 100644
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -36,23 +36,6 @@ from ..testing import (
 class TreeMetadata(object):
     """Base class for all data being captured."""
 
-    def __init__(self):
-        self._ack = False
-
-    def ack(self):
-        self._ack = True
-
-
-class ReaderSummary(TreeMetadata):
-    """A summary of what the reader did."""
-
-    def __init__(self, total_file_count, total_sandbox_execution_time,
-        total_emitter_execution_time):
-        TreeMetadata.__init__(self)
-        self.total_file_count = total_file_count
-        self.total_sandbox_execution_time = total_sandbox_execution_time
-        self.total_emitter_execution_time = total_emitter_execution_time
-
 
 class ContextDerived(TreeMetadata):
     """Build object derived from a single Context instance.
@@ -88,6 +71,12 @@ class ContextDerived(TreeMetadata):
 
         self.config = context.config
 
+        self._context = context
+
+    @property
+    def install_target(self):
+        return self._context['FINAL_TARGET']
+
     @property
     def relobjdir(self):
         return mozpath.relpath(self.objdir, self.topobjdir)
@@ -163,7 +152,6 @@ class XPIDLFile(ContextDerived):
     __slots__ = (
         'add_to_manifest',
         'basename',
-        'install_target',
         'source_path',
     )
 
@@ -175,8 +163,6 @@ class XPIDLFile(ContextDerived):
         self.module = module
         self.add_to_manifest = add_to_manifest
 
-        self.install_target = context['FINAL_TARGET']
-
 class BaseDefines(ContextDerived):
     """Context derived container object for DEFINES/HOST_DEFINES,
     which are OrderedDicts.
diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py
index 1baa9b30da4..554f8b255b1 100644
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -63,7 +63,6 @@ from .data import (
     PreprocessedTestWebIDLFile,
     PreprocessedWebIDLFile,
     Program,
-    ReaderSummary,
     Resources,
     SharedLibrary,
     SimpleProgram,
@@ -95,6 +94,8 @@ from .context import (
     TemplateContext,
 )
 
+from mozbuild.base import ExecutionSummary
+
 
 class TreeMetadataEmitter(LoggingMixin):
     """Converts the executed mozbuild files into data structures.
@@ -136,22 +137,28 @@ class TreeMetadataEmitter(LoggingMixin):
         # Add security/nss manually, since it doesn't have a subconfigure.
         self._external_paths.add('security/nss')
 
+        self._emitter_time = 0.0
+        self._object_count = 0
+
+    def summary(self):
+        return ExecutionSummary(
+            'Processed into {object_count:d} build config descriptors in '
+            '{execution_time:.2f}s',
+            execution_time=self._emitter_time,
+            object_count=self._object_count)
+
     def emit(self, output):
         """Convert the BuildReader output into data structures.
 
         The return value from BuildReader.read_topsrcdir() (a generator) is
         typically fed into this function.
         """
-        file_count = 0
-        sandbox_execution_time = 0.0
-        emitter_time = 0.0
         contexts = {}
 
         def emit_objs(objs):
             for o in objs:
+                self._object_count += 1
                 yield o
-                if not o._ack:
-                    raise Exception('Unhandled object of type %s' % type(o))
 
         for out in output:
             # Nothing in sub-contexts is currently of interest to us. Filter
@@ -166,14 +173,10 @@ class TreeMetadataEmitter(LoggingMixin):
                 start = time.time()
                 # We need to expand the generator for the timings to work.
                 objs = list(self.emit_from_context(out))
-                emitter_time += time.time() - start
+                self._emitter_time += time.time() - start
 
                 for o in emit_objs(objs): yield o
 
-                # Update the stats.
-                file_count += len(out.all_paths)
-                sandbox_execution_time += out.execution_time
-
             else:
                 raise Exception('Unhandled output type: %s' % type(out))
 
@@ -182,12 +185,10 @@ class TreeMetadataEmitter(LoggingMixin):
         if self.config.substs.get('COMPILE_ENVIRONMENT', True):
             start = time.time()
             objs = list(self._emit_libs_derived(contexts))
-            emitter_time += time.time() - start
+            self._emitter_time += time.time() - start
 
             for o in emit_objs(objs): yield o
 
-        yield ReaderSummary(file_count, sandbox_execution_time, emitter_time)
-
     def _emit_libs_derived(self, contexts):
         # First do FINAL_LIBRARY linkage.
         for lib in (l for libs in self._libs.values() for l in libs):
diff --git a/python/mozbuild/mozbuild/frontend/gyp_reader.py b/python/mozbuild/mozbuild/frontend/gyp_reader.py
index 1346a66e817..4b29ba6a628 100644
--- a/python/mozbuild/mozbuild/frontend/gyp_reader.py
+++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py
@@ -6,7 +6,6 @@ from __future__ import absolute_import, unicode_literals
 
 import gyp
 import sys
-import time
 import os
 import mozpack.path as mozpath
 from mozpack.files import FileFinder
@@ -83,8 +82,6 @@ def read_from_gyp(config, path, output, vars, non_unified_sources = set()):
     processor.
     """
 
-    time_start = time.time()
-
     # gyp expects plain str instead of unicode. The frontend code gives us
     # unicode strings, so convert them.
     path = encode(path)
@@ -234,6 +231,4 @@ def read_from_gyp(config, path, output, vars, non_unified_sources = set()):
             context['DEFINES']['_UNICODE'] = True
         context['DISABLE_STL_WRAPPING'] = True
 
-        context.execution_time = time.time() - time_start
         yield context
-        time_start = time.time()
diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py
index 0b2d0044a3e..4eb597d3b63 100644
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -78,6 +78,9 @@ from .context import (
     TemplateContext,
 )
 
+from mozbuild.base import ExecutionSummary
+
+
 if sys.version_info.major == 2:
     text_type = unicode
     type_type = types.TypeType
@@ -869,6 +872,16 @@ class BuildReader(object):
         self._execution_stack = []
         self._finder = finder
 
+        self._execution_time = 0.0
+        self._file_count = 0
+
+    def summary(self):
+        return ExecutionSummary(
+            'Finished reading {file_count:d} moz.build files in '
+            '{execution_time:.2f}s',
+            file_count=self._file_count,
+            execution_time=self._execution_time)
+
     def read_topsrcdir(self):
         """Read the tree of linked moz.build files.
 
@@ -1103,7 +1116,8 @@ class BuildReader(object):
         sandbox = MozbuildSandbox(context, metadata=metadata,
                                   finder=self._finder)
         sandbox.exec_file(path)
-        context.execution_time = time.time() - time_start
+        self._execution_time += time.time() - time_start
+        self._file_count += len(context.all_paths)
 
         # Yield main context before doing any processing. This gives immediate
         # consumers an opportunity to change state before our remaining
@@ -1141,6 +1155,7 @@ class BuildReader(object):
                     raise SandboxValidationError('Cannot find %s.' % source,
                         context)
                 non_unified_sources.add(source)
+            time_start = time.time()
             for gyp_context in read_from_gyp(context.config,
                                              mozpath.join(curdir, gyp_dir.input),
                                              mozpath.join(context.objdir,
@@ -1149,6 +1164,8 @@ class BuildReader(object):
                                              non_unified_sources = non_unified_sources):
                 gyp_context.update(gyp_dir.sandbox_vars)
                 gyp_contexts.append(gyp_context)
+                self._file_count += len(gyp_context.all_paths)
+            self._execution_time += time.time() - time_start
 
         for gyp_context in gyp_contexts:
             context['DIRS'].append(mozpath.relpath(gyp_context.objdir, context.objdir))
diff --git a/python/mozbuild/mozbuild/jar.py b/python/mozbuild/mozbuild/jar.py
index b3a8815b3bc..8a33abc0e4f 100644
--- a/python/mozbuild/mozbuild/jar.py
+++ b/python/mozbuild/mozbuild/jar.py
@@ -20,6 +20,7 @@ from MozZipFile import ZipFile
 from cStringIO import StringIO
 
 from mozbuild.util import (
+    ensureParentDir,
     lock_file,
     PushbackIter,
 )
@@ -200,6 +201,7 @@ class JarMaker(object):
         with the given chrome base path, and updates the given manifest file.
         '''
 
+        ensureParentDir(manifestPath)
         lock = lock_file(manifestPath + '.lck')
         try:
             myregister = dict.fromkeys(map(lambda s: s.replace('%',
diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py
index 21f262d1893..de8687c9877 100644
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -558,11 +558,11 @@ class Build(MachCommandBase):
         help='Show a diff of changes.')
     # It would be nice to filter the choices below based on
     # conditions, but that is for another day.
-    @CommandArgument('-b', '--backend',
-        choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', 'VisualStudio'],
-        default='RecursiveMake',
-        help='Which backend to build (default: RecursiveMake).')
-    def build_backend(self, backend='RecursiveMake', diff=False):
+    @CommandArgument('-b', '--backend', nargs='+',
+        choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse',
+                 'VisualStudio', 'FasterMake', 'CompileDB'],
+        help='Which backend to build.')
+    def build_backend(self, backend, diff=False):
         python = self.virtualenv_manager.python_path
         config_status = os.path.join(self.topobjdir, 'config.status')
 
@@ -572,7 +572,10 @@ class Build(MachCommandBase):
                   % backend)
             return 1
 
-        args = [python, config_status, '--backend=%s' % backend]
+        args = [python, config_status]
+        if backend:
+            args.append('--backend')
+            args.extend(backend)
         if diff:
             args.append('--diff')
 
diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
index c7ce9768092..e46191ad63b 100644
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -27,7 +27,6 @@ from mozbuild.frontend.data import (
     JsPreferenceFile,
     LocalInclude,
     Program,
-    ReaderSummary,
     Resources,
     SimpleProgram,
     Sources,
@@ -73,23 +72,14 @@ class TestEmitterBasic(unittest.TestCase):
 
     def read_topsrcdir(self, reader, filter_common=True):
         emitter = TreeMetadataEmitter(reader.config)
-        def ack(obj):
-            obj.ack()
-            return obj
-
-        objs = list(ack(o) for o in emitter.emit(reader.read_topsrcdir()))
+        objs = list(emitter.emit(reader.read_topsrcdir()))
         self.assertGreater(len(objs), 0)
-        self.assertIsInstance(objs[-1], ReaderSummary)
 
         filtered = []
         for obj in objs:
             if filter_common and isinstance(obj, DirectoryTraversal):
                 continue
 
-            # Always filter ReaderSummary because it's asserted above.
-            if isinstance(obj, ReaderSummary):
-                continue
-
             filtered.append(obj)
 
         return filtered
diff --git a/security/manager/ssl/StaticHPKPins.h b/security/manager/ssl/StaticHPKPins.h
index ada30758103..046ced243c2 100644
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -81,7 +81,7 @@ static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
 
 /* End Entity Test Cert */
 static const char kEnd_Entity_Test_CertFingerprint[] =
-  "lzCakFt+nADIfIkgk+UE/EQ9SaT2nay2yu2iykVbvV8=";
+  "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
 
 /* Entrust Root Certification Authority */
 static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
diff --git a/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec
new file mode 100644
index 00000000000..1b368c26f1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:EE with bad subjectAltNames
+extension:subjectAlternativeName:*.*.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec
new file mode 100644
index 00000000000..ac97b2231a2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test End-entity
+validity:19460214-20310101
+extension:subjectAlternativeName:before-epoch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec
new file mode 100644
index 00000000000..835e63f2b63
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test Intermediate
+validity:19460214-20310101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec
new file mode 100644
index 00000000000..9aabe21628e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Before UNIX Epoch Test Intermediate
+subject:Test End-entity with Before UNIX Epoch issuer
+extension:subjectAlternativeName:before-epoch-issuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec
new file mode 100644
index 00000000000..8e16705b503
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate used as End-Entity
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:ca-used-as-end-entity.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec
new file mode 100644
index 00000000000..4ad96d51599
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec
new file mode 100644
index 00000000000..554339ff526
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec
new file mode 100644
index 00000000000..63c36d34b36
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test End-entity
+subject:EE Issued by non-CA
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec
new file mode 100644
index 00000000000..9ed9b33db7a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec
@@ -0,0 +1,3 @@
+issuer:V1 Cert
+subject:EE Issued by V1 Cert
+extension:subjectAlternativeName:localhost,*.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec
new file mode 100644
index 00000000000..0a03bc36f4d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Expired Test End-entity
+validity:20130101-20140101
+extension:subjectAlternativeName:expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec
new file mode 100644
index 00000000000..38a0abd8a48
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Expired Test Intermediate
+validity:20110101-20130101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec
new file mode 100644
index 00000000000..855f454221d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Expired Test Intermediate
+subject:Test End-entity with expired issuer
+extension:subjectAlternativeName:expiredissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec
new file mode 100644
index 00000000000..21ed73d60b0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec
@@ -0,0 +1 @@
+rsa1016
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec
new file mode 100644
index 00000000000..02b595dc9aa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test Intermediate
+subject:Inadequate Key Size End-Entity
+subjectKey:rsa1016
+extension:subjectAlternativeName:inadequate-key-size-ee.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec
new file mode 100644
index 00000000000..4d553890b92
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Inadequate Key Usage Test End-entity
+extension:keyUsage:cRLSign
+extension:subjectAlternativeName:inadequatekeyusage.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec
new file mode 100644
index 00000000000..76623384707
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:127.0.0.1
+extension:subjectAlternativeName:127.0.0.1
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec
new file mode 100644
index 00000000000..e4c2b7008d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Test CA
+subject:Test MD5Signature-Expired End-entity
+validity:20110101-20130101
+signature:md5WithRSAEncryption
+extension:subjectAlternativeName:md5signature-expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec
new file mode 100644
index 00000000000..02742d910eb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test End-entity with MD5 signature
+signature:md5WithRSAEncryption
+extension:subjectAlternativeName:md5signature.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec
new file mode 100644
index 00000000000..262f08d6be7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Mismatch-Expired Test End-entity
+validity:20130101-20140101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec
new file mode 100644
index 00000000000..947eb7d678c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Mismatch-Not Yet Valid Test End-entity
+validity:20330101-20340101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec
new file mode 100644
index 00000000000..adc8ebaf8be
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Other test CA
+subject:Mismatch-Untrusted-Expired Test End-entity
+validity:20110101-20130101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec
new file mode 100644
index 00000000000..91c5f548b65
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Other test CA
+subject:Mismatch-Untrusted Test End-entity
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec
new file mode 100644
index 00000000000..b93599fc881
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Mismatch Test End-entity
+extension:subjectAlternativeName:doesntmatch.example.com,*.alsodoesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec
new file mode 100644
index 00000000000..86ef45b7ce8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec
@@ -0,0 +1,2 @@
+issuer:Test CA
+subject:doesntmatch.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/moz.build b/security/manager/ssl/tests/unit/bad_certs/moz.build
new file mode 100644
index 00000000000..15aa27c4333
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/moz.build
@@ -0,0 +1,70 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+test_certificates = (
+    'badSubjectAltNames.pem',
+    'beforeEpoch.pem',
+    'beforeEpochINT.pem',
+    'beforeEpochIssuer.pem',
+    'ca-used-as-end-entity.pem',
+    'default-ee.pem',
+    'eeIssuedByNonCA.pem',
+    'eeIssuedByV1Cert.pem',
+    'expired-ee.pem',
+    'expiredINT.pem',
+    'expiredissuer.pem',
+    'inadequateKeySizeEE.pem',
+    'inadequatekeyusage-ee.pem',
+    'ipAddressAsDNSNameInSAN.pem',
+    'md5signature-expired.pem',
+    'md5signature.pem',
+    'mismatchCN.pem',
+    'mismatch-expired.pem',
+    'mismatch-notYetValid.pem',
+    'mismatch.pem',
+    'mismatch-untrusted-expired.pem',
+    'mismatch-untrusted.pem',
+    'notYetValidINT.pem',
+    'notYetValidIssuer.pem',
+    'notYetValid.pem',
+    'noValidNames.pem',
+    'nsCertTypeCritical.pem',
+    'nsCertTypeCriticalWithExtKeyUsage.pem',
+    'nsCertTypeNotCritical.pem',
+    'other-issuer-ee.pem',
+    'other-test-ca.pem',
+    'self-signed-EE-with-cA-true.pem',
+    'selfsigned-inadequateEKU.pem',
+    'selfsigned.pem',
+    'test-ca.pem',
+    'test-int.pem',
+    'unknownissuer.pem',
+    'untrusted-expired.pem',
+    'untrustedissuer.pem',
+    'v1Cert.pem',
+)
+
+for test_certificate in test_certificates:
+    input_file = test_certificate + '.certspec'
+    GENERATED_FILES += [test_certificate]
+    props = GENERATED_FILES[test_certificate]
+    props.script = '../pycert.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.bad_certs += ['!%s' % test_certificate]
+
+test_keys = (
+    'default-ee.key',
+    'inadequateKeySizeEE.key',
+    'other-test-ca.key',
+)
+
+for test_key in test_keys:
+    input_file = test_key + '.keyspec'
+    GENERATED_FILES += [test_key]
+    props = GENERATED_FILES[test_key]
+    props.script = '../pykey.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.bad_certs += ['!%s' % test_key]
diff --git a/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec
new file mode 100644
index 00000000000..87088e87e55
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:End-entity with no valid names
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec
new file mode 100644
index 00000000000..5b60c29ebe8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Not Yet Valid Test End-entity
+validity:20310101-20320101
+extension:subjectAlternativeName:notyetvalid.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec
new file mode 100644
index 00000000000..8a00f2ee23c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Not Yet Valid Test Intermediate
+validity:20310101-20330101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec
new file mode 100644
index 00000000000..d8420898e71
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Not Yet Valid Test Intermediate
+subject:Test End-entity with not yet valid issuer
+extension:subjectAlternativeName:notyetvalidissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec
new file mode 100644
index 00000000000..b236bdea47f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:nsCertType Critical
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType[critical]:sslServer
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec
new file mode 100644
index 00000000000..0ae63e20f27
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Test CA
+subject:nsCertType Critical With extKeyUsage
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType[critical]:sslServer
+extension:basicConstraints:,
+extension:extKeyUsage:serverAuth
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec
new file mode 100644
index 00000000000..a44a1feeef4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:nsCertType Not Critical
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType:sslServer
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec
new file mode 100644
index 00000000000..a905a66ac2d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Other test CA
+subject:Wrong CA Pin Test End-Entity
+issuerKey:alternate
+subjectKey:alternate
+extension:subjectAlternativeName:*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec
new file mode 100644
index 00000000000..cbd5f309c09
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec
@@ -0,0 +1 @@
+alternate
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec
new file mode 100644
index 00000000000..3bc975aa222
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec
@@ -0,0 +1,7 @@
+issuer:Other test CA
+subject:Other test CA
+issuerKey:alternate
+subjectKey:alternate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec
new file mode 100644
index 00000000000..0ca92d7fd14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test Self-signed End-entity with CA true
+subject:Test Self-signed End-entity with CA true
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:self-signed-end-entity-with-cA-true.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec
new file mode 100644
index 00000000000..477b90ce140
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Self-signed Inadequate EKU Test End-entity
+subject:Self-signed Inadequate EKU Test End-entity
+extension:keyUsage:keyEncipherment,dataEncipherment
+extension:extKeyUsage:serverAuth
+extension:subjectAlternativeName:selfsigned-inadequateEKU.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec
new file mode 100644
index 00000000000..99a814be179
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Self-signed Test End-entity
+subject:Self-signed Test End-entity
+extension:subjectAlternativeName:selfsigned.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec
new file mode 100644
index 00000000000..5d2435d7bbe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test CA
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec
new file mode 100644
index 00000000000..33b42c2f411
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec
new file mode 100644
index 00000000000..a735c730ca6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate to delete
+subject:Test End-entity from unknown issuer
+extension:subjectAlternativeName:unknownissuer.example.com,unknownissuer.include-subdomains.pinning.example.com,unknownissuer.test-mode.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec
new file mode 100644
index 00000000000..3efd1ce6779
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Other test CA
+subject:Untrusted-Expired Test End-entity
+validity:20110101-20130101
+extension:subjectAlternativeName:untrusted-expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec
new file mode 100644
index 00000000000..5ba0bc25358
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Other test CA
+subject:Test End-entity with untrusted issuer
+extension:subjectAlternativeName:untrustedissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec
new file mode 100644
index 00000000000..7824630bbc2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:V1 Cert
+version:1
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
index 6041fdc7e65..5c4b5d6731b 100644
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -267,7 +267,7 @@ function clearSessionCache() {
 
 function run_test() {
   do_get_profile();
-  add_tls_server_setup("");
+  add_tls_server_setup("", "");
 
   add_connection_test(".example.com",
                       SEC_ERROR_xxx,
@@ -279,11 +279,11 @@ function run_test() {
 
   run_next_test();
 }
-
 */
-function add_tls_server_setup(serverBinName) {
+
+function add_tls_server_setup(serverBinName, certsPath) {
   add_test(function() {
-    _setupTLSServerTest(serverBinName);
+    _setupTLSServerTest(serverBinName, certsPath);
   });
 }
 
@@ -430,12 +430,12 @@ function _getBinaryUtil(binaryUtilName) {
 }
 
 // Do not call this directly; use add_tls_server_setup
-function _setupTLSServerTest(serverBinName)
+function _setupTLSServerTest(serverBinName, certsPath)
 {
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
   // The trusted CA that is typically used for "good" certificates.
-  addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u");
+  addCertFromFile(certdb, `${certsPath}/test-ca.pem`, "CTu,u,u");
 
   const CALLBACK_PORT = 8444;
 
@@ -468,8 +468,8 @@ function _setupTLSServerTest(serverBinName)
   let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
   process.init(serverBin);
   let certDir = directoryService.get("CurWorkD", Ci.nsILocalFile);
-  certDir.append("tlsserver");
-  Assert.ok(certDir.exists(), "tlsserver folder should exist");
+  certDir.append(`${certsPath}`);
+  Assert.ok(certDir.exists(), `certificate folder (${certsPath}) should exist`);
   // Using "sql:" causes the SQL DB to be used so we can run tests on Android.
   process.run(false, [ "sql:" + certDir.path ], 1);
 
@@ -639,9 +639,8 @@ FakeSSLStatus.prototype = {
 
 // Utility functions for adding tests relating to certificate error overrides
 
-// Helper function for add_cert_override_test and
-// add_prevented_cert_override_test. Probably doesn't need to be called
-// directly.
+// Helper function for add_cert_override_test. Probably doesn't need to be
+// called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
   let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                                .SSLStatus;
@@ -668,6 +667,28 @@ function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
   add_connection_test(aHost, PRErrorCodeSuccess);
 }
 
+// Helper function for add_prevented_cert_override_test. This is much like
+// add_cert_override except it may not be the case that the connection has an
+// SSLStatus set on it. In this case, the error was not overridable anyway, so
+// we consider it a success.
+function attempt_adding_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                               .SSLStatus;
+  if (sslstatus) {
+    let bits =
+      (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
+      (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
+      (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
+    Assert.equal(bits, aExpectedBits,
+                 "Actual and expected override bits should match");
+    let cert = sslstatus.serverCert;
+    let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                                .getService(Ci.nsICertOverrideService);
+    certOverrideService.rememberValidityOverride(aHost, 8443, cert, aExpectedBits,
+                                                 true);
+  }
+}
+
 // Given a host, expected error bits (see nsICertOverrideService.idl), and
 // an expected error code, tests that an initial connection to the host fails
 // with the expected errors and that adding an override does not result in a
@@ -676,6 +697,6 @@ function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
 // overridable, even if an entry is added to the override service.
 function add_prevented_cert_override_test(aHost, aExpectedBits, aExpectedError) {
   add_connection_test(aHost, aExpectedError, null,
-                      add_cert_override.bind(this, aHost, aExpectedBits));
+                      attempt_adding_cert_override.bind(this, aHost, aExpectedBits));
   add_connection_test(aHost, aExpectedError);
 }
diff --git a/security/manager/ssl/tests/unit/moz.build b/security/manager/ssl/tests/unit/moz.build
index 6da4910194b..85ca2b6590c 100644
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -6,6 +6,8 @@
 
 DIRS += ['tlsserver']
 TEST_DIRS += [
+    'bad_certs',
+    'ocsp_certs',
     'test_cert_eku',
     'test_cert_embedded_null',
     'test_cert_keyUsage',
@@ -20,6 +22,7 @@ TEST_DIRS += [
     'test_name_constraints',
     'test_ocsp_fetch_method',
     'test_ocsp_url',
+    'test_onecrl',
     'test_pinning_dynamic',
     'test_validity',
 ]
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec
new file mode 100644
index 00000000000..8e16705b503
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate used as End-Entity
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:ca-used-as-end-entity.example.com
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec
new file mode 100644
index 00000000000..4ad96d51599
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec
new file mode 100644
index 00000000000..554339ff526
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec
new file mode 100644
index 00000000000..bdf3e2ee4dc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test SHA1 Delegated Responder
+subjectKey:alternate
+signature:sha1WithRSAEncryption
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec
new file mode 100644
index 00000000000..19971eeb4dd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Delegated Responder
+subjectKey:alternate
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec
new file mode 100644
index 00000000000..be0d3e9e5fc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate
+subject:Test Invalid Delegated Responder From Intermediate
+subjectKey:alternate
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec
new file mode 100644
index 00000000000..2833ed9b52c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder keyUsage crlSigning
+subjectKey:alternate
+extension:keyUsage:cRLSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec
new file mode 100644
index 00000000000..92444c94ada
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder No extKeyUsage
+subjectKey:alternate
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec
new file mode 100644
index 00000000000..bc704fbd41f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder Wrong extKeyUsage
+subjectKey:alternate
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/moz.build b/security/manager/ssl/tests/unit/ocsp_certs/moz.build
new file mode 100644
index 00000000000..7d27a912931
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/moz.build
@@ -0,0 +1,44 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+test_certificates = (
+    'ca-used-as-end-entity.pem',
+    'default-ee.pem',
+    'delegatedSHA1Signer.pem',
+    'delegatedSigner.pem',
+    'invalidDelegatedSignerFromIntermediate.pem',
+    'invalidDelegatedSignerKeyUsageCrlSigning.pem',
+    'invalidDelegatedSignerNoExtKeyUsage.pem',
+    'invalidDelegatedSignerWrongExtKeyUsage.pem',
+    'ocspEEWithIntermediate.pem',
+    'ocspOtherEndEntity.pem',
+    'other-test-ca.pem',
+    'rsa-1016-keysizeDelegatedSigner.pem',
+    'test-ca.pem',
+    'test-int.pem',
+)
+
+for test_certificate in test_certificates:
+    input_file = test_certificate + '.certspec'
+    GENERATED_FILES += [test_certificate]
+    props = GENERATED_FILES[test_certificate]
+    props.script = '../pycert.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.ocsp_certs += ['!%s' % test_certificate]
+
+test_keys = (
+    'default-ee.key',
+    'other-test-ca.key',
+    'rsa-1016-keysizeDelegatedSigner.key',
+)
+
+for test_key in test_keys:
+    input_file = test_key + '.keyspec'
+    GENERATED_FILES += [test_key]
+    props = GENERATED_FILES[test_key]
+    props.script = '../pykey.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.ocsp_certs += ['!%s' % test_key]
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec
new file mode 100644
index 00000000000..ae3a51565a8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate
+subject:Test End-entity with Intermediate
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec
new file mode 100644
index 00000000000..5756f6ab5f6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Other Cert
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec
new file mode 100644
index 00000000000..cbd5f309c09
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec
@@ -0,0 +1 @@
+alternate
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec
new file mode 100644
index 00000000000..3bc975aa222
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec
@@ -0,0 +1,7 @@
+issuer:Other test CA
+subject:Other test CA
+issuerKey:alternate
+subjectKey:alternate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec
new file mode 100644
index 00000000000..21ed73d60b0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec
@@ -0,0 +1 @@
+rsa1016
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec
new file mode 100644
index 00000000000..05f73368a80
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:RSA 1016 Key Size Test Delegated Responder
+subjectKey:rsa1016
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec
new file mode 100644
index 00000000000..5d2435d7bbe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test CA
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec
new file mode 100644
index 00000000000..33b42c2f411
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py
index 86889ba3425..0a28a663f4e 100755
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -16,7 +16,8 @@ subject:
 [validity:]
 [issuerKey:]
 [subjectKey:]
-[signature:{sha1WithRSAEncryption,sha256WithRSAEncryption,ecdsaWithSHA256}]
+[signature:{sha256WithRSAEncryption,sha1WithRSAEncryption,
+            md5WithRSAEncryption,ecdsaWithSHA256}]
 [extension:>]
 [...]
 
@@ -31,6 +32,7 @@ subjectAlternativeName:[,...]
 authorityInformationAccess:
 certificatePolicies:
 nameConstraints:{permitted,excluded}:[,...]
+nsCertType:sslServer
 
 Where:
   [] indicates an optional field or component of a field
@@ -64,6 +66,9 @@ be 'utf8String' or 'printableString'. If the given string does not
 contain a '/', it is assumed to represent a common name.
 DirectoryNames also use this format. When specifying a directoryName in
 a nameConstraints extension, the implicit form may not be used.
+
+If an extension name has '[critical]' after it, it will be marked as
+critical. Otherwise (by default), it will not be marked as critical.
 """
 
 from pyasn1.codec.der import decoder
@@ -178,6 +183,14 @@ class UnknownDNTypeError(UnknownBaseError):
         self.category = 'DN'
 
 
+class UnknownNSCertTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown nsCertType types."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'nsCertType'
+
+
 def getASN1Tag(asn1Type):
     """Helper function for returning the base tag value of a given
     type from the pyasn1 package"""
@@ -254,27 +267,29 @@ def stringToDN(string, tag=None):
     name.setComponentByPosition(0, rdns)
     return name
 
-def stringToAlgorithmIdentifier(string):
+def stringToAlgorithmIdentifiers(string):
     """Helper function that converts a description of an algorithm
-    to a representation usable by the pyasn1 package"""
+    to a representation usable by the pyasn1 package and a hash
+    algorithm name for use by pykey."""
     algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+    algorithmName = None
     algorithm = None
-    name = None
     if string == 'sha1WithRSAEncryption':
-        name = 'SHA-1'
+        algorithmName = 'SHA-1'
         algorithm = rfc2459.sha1WithRSAEncryption
     elif string == 'sha256WithRSAEncryption':
-        name = 'SHA-256'
+        algorithmName = 'SHA-256'
         algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
+    elif string == 'md5WithRSAEncryption':
+        algorithmName = 'MD5'
+        algorithm = rfc2459.md5WithRSAEncryption
     elif string == 'ecdsaWithSHA256':
-        # Note that this value is only used by pykey.py to tell if
-        # ECDSA is allowed.  It does not conform to the pyECC syntax.
-        name = 'SHA-256'
+        algorithmName = 'sha256'
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.2')
     else:
         raise UnknownAlgorithmTypeError(string)
     algorithmIdentifier.setComponentByName('algorithm', algorithm)
-    return (algorithmIdentifier, name)
+    return (algorithmIdentifier, algorithmName)
 
 def datetimeToTime(dt):
     """Takes a datetime object and returns an rfc2459.Time object with
@@ -380,22 +395,28 @@ class Certificate:
             self.notAfter = self.now + delta
 
     def decodeExtension(self, extension):
-        extensionType = extension.split(':')[0]
-        value = ':'.join(extension.split(':')[1:])
+        match = re.search('([a-zA-Z]+)(\[critical\])?:(.*)', extension)
+        if not match:
+            raise UnknownExtensionTypeError(extension)
+        extensionType = match.group(1)
+        critical = match.group(2)
+        value = match.group(3)
         if extensionType == 'basicConstraints':
-            self.addBasicConstraints(value)
+            self.addBasicConstraints(value, critical)
         elif extensionType == 'keyUsage':
-            self.addKeyUsage(value)
+            self.addKeyUsage(value, critical)
         elif extensionType == 'extKeyUsage':
-            self.addExtKeyUsage(value)
+            self.addExtKeyUsage(value, critical)
         elif extensionType == 'subjectAlternativeName':
-            self.addSubjectAlternativeName(value)
+            self.addSubjectAlternativeName(value, critical)
         elif extensionType == 'authorityInformationAccess':
-            self.addAuthorityInformationAccess(value)
+            self.addAuthorityInformationAccess(value, critical)
         elif extensionType == 'certificatePolicies':
-            self.addCertificatePolicies(value)
+            self.addCertificatePolicies(value, critical)
         elif extensionType == 'nameConstraints':
-            self.addNameConstraints(value)
+            self.addNameConstraints(value, critical)
+        elif extensionType == 'nsCertType':
+            self.addNSCertType(value, critical)
         else:
             raise UnknownExtensionTypeError(extensionType)
 
@@ -407,16 +428,20 @@ class Certificate:
         else:
             raise UnknownKeyTargetError(subjectOrIssuer)
 
-    def addExtension(self, extensionType, extensionValue):
+    def addExtension(self, extensionType, extensionValue, critical):
         if not self.extensions:
             self.extensions = []
         encapsulated = univ.OctetString(encoder.encode(extensionValue))
         extension = rfc2459.Extension()
         extension.setComponentByName('extnID', extensionType)
+        # critical is either the string '[critical]' or None.
+        # We only care whether or not it is truthy.
+        if critical:
+            extension.setComponentByName('critical', True)
         extension.setComponentByName('extnValue', encapsulated)
         self.extensions.append(extension)
 
-    def addBasicConstraints(self, basicConstraints):
+    def addBasicConstraints(self, basicConstraints, critical):
         cA = basicConstraints.split(',')[0]
         pathLenConstraint = basicConstraints.split(',')[1]
         basicConstraintsExtension = rfc2459.BasicConstraints()
@@ -427,11 +452,11 @@ class Certificate:
                     subtypeSpec=constraint.ValueRangeConstraint(0, 64))
             basicConstraintsExtension.setComponentByName('pathLenConstraint',
                                                          pathLenConstraintValue)
-        self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension)
+        self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension, critical)
 
-    def addKeyUsage(self, keyUsage):
+    def addKeyUsage(self, keyUsage, critical):
         keyUsageExtension = rfc2459.KeyUsage(keyUsage)
-        self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension)
+        self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension, critical)
 
     def keyPurposeToOID(self, keyPurpose):
         if keyPurpose == 'serverAuth':
@@ -452,15 +477,15 @@ class Certificate:
             return rfc2459.id_kp_timeStamping
         raise UnknownKeyPurposeTypeError(keyPurpose)
 
-    def addExtKeyUsage(self, extKeyUsage):
+    def addExtKeyUsage(self, extKeyUsage, critical):
         extKeyUsageExtension = rfc2459.ExtKeyUsageSyntax()
         count = 0
         for keyPurpose in extKeyUsage.split(','):
             extKeyUsageExtension.setComponentByPosition(count, self.keyPurposeToOID(keyPurpose))
             count += 1
-        self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension)
+        self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension, critical)
 
-    def addSubjectAlternativeName(self, dNSNames):
+    def addSubjectAlternativeName(self, dNSNames, critical):
         subjectAlternativeName = rfc2459.SubjectAltName()
         count = 0
         for dNSName in dNSNames.split(','):
@@ -471,15 +496,15 @@ class Certificate:
             generalName.setComponentByName('dNSName', dNSName.decode(encoding='string_escape'))
             subjectAlternativeName.setComponentByPosition(count, generalName)
             count += 1
-        self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName)
+        self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical)
 
-    def addAuthorityInformationAccess(self, ocspURI):
+    def addAuthorityInformationAccess(self, ocspURI, critical):
         sequence = univ.Sequence()
         accessDescription = stringToAccessDescription(ocspURI)
         sequence.setComponentByPosition(0, accessDescription)
-        self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence)
+        self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence, critical)
 
-    def addCertificatePolicies(self, policyOID):
+    def addCertificatePolicies(self, policyOID, critical):
         policies = rfc2459.CertificatePolicies()
         policy = rfc2459.PolicyInformation()
         if policyOID == 'any':
@@ -487,9 +512,9 @@ class Certificate:
         policyIdentifier = rfc2459.CertPolicyId(policyOID)
         policy.setComponentByName('policyIdentifier', policyIdentifier)
         policies.setComponentByPosition(0, policy)
-        self.addExtension(rfc2459.id_ce_certificatePolicies, policies)
+        self.addExtension(rfc2459.id_ce_certificatePolicies, policies, critical)
 
-    def addNameConstraints(self, constraints):
+    def addNameConstraints(self, constraints, critical):
         nameConstraints = NameConstraints()
         if constraints.startswith('permitted:'):
             (subtreesType, subtreesTag) = ('permittedSubtrees', 0)
@@ -514,7 +539,13 @@ class Certificate:
             generalSubtrees.setComponentByPosition(pos, generalSubtree)
             pos = pos + 1
         nameConstraints.setComponentByName(subtreesType, generalSubtrees)
-        self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints)
+        self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints, critical)
+
+    def addNSCertType(self, certType, critical):
+        if certType != 'sslServer':
+            raise UnknownNSCertTypeError(certType)
+        self.addExtension(univ.ObjectIdentifier('2.16.840.1.113730.1.1'), univ.BitString("'01'B"),
+            critical)
 
     def getVersion(self):
         return rfc2459.Version(self.versionValue).subtype(
@@ -523,9 +554,6 @@ class Certificate:
     def getSerialNumber(self):
         return decoder.decode(self.serialNumber)[0]
 
-    def getSignature(self):
-        return stringToAlgorithmIdentifier(self.signature)
-
     def getIssuer(self):
         return stringToDN(self.issuer)
 
@@ -545,8 +573,7 @@ class Certificate:
         return stringToDN(self.subject)
 
     def toDER(self):
-        (signatureOID, hashAlg) = self.getSignature()
-
+        (signatureOID, hashName) = stringToAlgorithmIdentifiers(self.signature)
         tbsCertificate = rfc2459.TBSCertificate()
         tbsCertificate.setComponentByName('version', self.getVersion())
         tbsCertificate.setComponentByName('serialNumber', self.getSerialNumber())
@@ -568,8 +595,7 @@ class Certificate:
         certificate.setComponentByName('tbsCertificate', tbsCertificate)
         certificate.setComponentByName('signatureAlgorithm', signatureOID)
         tbsDER = encoder.encode(tbsCertificate)
-
-        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER, hashAlg))
+        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER, hashName))
         return encoder.encode(certificate)
 
     def toPEM(self):
diff --git a/security/manager/ssl/tests/unit/pykey.py b/security/manager/ssl/tests/unit/pykey.py
index d31c9ebdbe5..bbdc30c0e95 100755
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -66,13 +66,6 @@ class UnknownKeySpecificationError(UnknownBaseError):
         UnknownBaseError.__init__(self, value)
         self.category = 'key specification'
 
-class ParameterError(UnknownBaseError):
-    """Exception type indicating that the key was misconfigured"""
-
-    def __init__(self, value):
-        UnknownBaseError.__init__(self, value)
-        self.category = 'key parameter'
-
 class RSAPublicKey(univ.Sequence):
     """Helper type for encoding an RSA public key"""
     componentType = namedtype.NamedTypes(
@@ -560,12 +553,12 @@ class RSAKey:
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data, digest):
+    def sign(self, data, hashAlgorithmName):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
         rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q)
-        signature = rsa.sign(data, rsaPrivateKey, digest)
+        signature = rsa.sign(data, rsaPrivateKey, hashAlgorithmName)
         return byteStringToHexifiedBitString(signature)
 
 
@@ -671,14 +664,10 @@ class ECCKey:
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data, digest):
+    def sign(self, data, hashAlgorithmName):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
-        # This should really only be used with SHA-256
-        if digest != "SHA-256":
-            raise ParameterError(digest)
-
         # There is some non-determinism in ECDSA signatures. Work around
         # this by patching ecc.ecdsa.urandom to not be random.
         with mock.patch('ecc.ecdsa.urandom', side_effect=notRandom):
@@ -688,9 +677,9 @@ class ECCKey:
             # Also patch in secp256k1 if applicable.
             if self.keyOID == secp256k1:
                 with mock.patch('ecc.curves.DOMAINS', {256: secp256k1Params}):
-                    x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+                    x, y = encoding.dec_point(self.key.sign(data, hashAlgorithmName))
             else:
-                x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+                x, y = encoding.dec_point(self.key.sign(data, hashAlgorithmName))
             point = ECPoint()
             point.setComponentByName('x', x)
             point.setComponentByName('y', y)
diff --git a/security/manager/ssl/tests/unit/test_cert_blocklist.js b/security/manager/ssl/tests/unit/test_cert_blocklist.js
index b854f597d8b..df077f47fcf 100644
--- a/security/manager/ssl/tests/unit/test_cert_blocklist.js
+++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js
@@ -94,15 +94,15 @@ var blocklist_contents =
     "and serial" +
     // some mixed
     // In this case, the issuer name and the valid serialNumber correspond
-    // to test-int.der in tlsserver/
-    "" +
+    // to test-int.pem in bad_certs/
+    "" +
     "oops! more nonsense." +
-    "X1o=" +
+    "Y1HQqXGtw7ek2v/QAqBL8jf6rbA=" +
     // ... and some good
     // In this case, the issuer name and the valid serialNumber correspond
-    // to other-test-ca.der in tlsserver/ (for testing root revocation)
-    "" +
-    "AKEIivg=" +
+    // to other-test-ca.pem in bad_certs/ (for testing root revocation)
+    "" +
+    "Szin5enUEn9TnVq29c4IMPNFuqE=" +
     // This item corresponds to an entry in sample_revocations.txt where:
     // isser name is "another imaginary issuer" base-64 encoded, and
     // serialNumbers are:
@@ -113,8 +113,9 @@ var blocklist_contents =
     "" +
     "c2VyaWFsMi4=" +
     "YW5vdGhlciBzZXJpYWwu" +
-    "" +
+    // This item revokes same-issuer-ee.pem by subject and serial number.
+    "" +
     "";
 testserver.registerPathHandler("/push_blocked_cert/",
   function serveResponse(request, response) {
@@ -136,13 +137,12 @@ var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
 converter.charset = "UTF-8";
 
 function verify_cert(file, expectedError) {
-  let cert_der = readFile(do_get_file(file));
-  let ee = certDB.constructX509(cert_der, cert_der.length);
+  let ee = constructCertFromFile(file);
   checkCertErrorGeneric(certDB, ee, expectedError, certificateUsageSSLServer);
 }
 
 function load_cert(cert, trust) {
-  let file = "tlsserver/" + cert + ".der";
+  let file = "bad_certs/" + cert + ".pem";
   addCertFromFile(certDB, file, trust);
 }
 
@@ -201,20 +201,20 @@ function run_test() {
   ok(test_is_revoked(certList, "another imaginary issuer", "serial2."),
      "issuer / serial pair should be blocked");
 
-  // Soon we'll load a blocklist which revokes test-int.der, which issued
-  // test-int-ee.der.
+  // Soon we'll load a blocklist which revokes test-int.pem, which issued
+  // test-int-ee.pem.
   // Check the cert validates before we load the blocklist
-  let file = "tlsserver/test-int-ee.der";
+  let file = "test_onecrl/test-int-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
-  // The blocklist also revokes other-test-ca.der, which issued other-ca-ee.der.
+  // The blocklist also revokes other-test-ca.pem, which issued other-ca-ee.pem.
   // Check the cert validates before we load the blocklist
-  file = "tlsserver/other-issuer-ee.der";
+  file = "bad_certs/other-issuer-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
-  // The blocklist will revoke same-issuer-ee.der via subject / pubKeyHash.
+  // The blocklist will revoke same-issuer-ee.pem via subject / pubKeyHash.
   // Check the cert validates before we load the blocklist
-  file = "tlsserver/same-issuer-ee.der";
+  file = "test_onecrl/same-issuer-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
   // blocklist load is async so we must use add_test from here
@@ -271,35 +271,35 @@ function run_test() {
       contents = contents + (contents.length == 0 ? "" : "\n") + line.value;
     } while (hasmore);
     let expected = "# Auto generated contents. Do not edit.\n" +
-                  "MCIxIDAeBgNVBAMTF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5\n"+
-                  "\t2ETEb0QP574JkM+35JVwS899PLUmt1rrJyWOV6GRfAE=\n"+
-                  "MBgxFjAUBgNVBAMTDU90aGVyIHRlc3QgQ0E=\n" +
-                  " AKEIivg=\n" +
-                  "MBIxEDAOBgNVBAMTB1Rlc3QgQ0E=\n" +
-                  " X1o=\n" +
+                  "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5\n"+
+                  "\tVCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=\n"+
+                  "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=\n" +
+                  " Y1HQqXGtw7ek2v/QAqBL8jf6rbA=\n" +
+                  "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=\n" +
+                  " Szin5enUEn9TnVq29c4IMPNFuqE=\n" +
                   "YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy\n" +
                   " YW5vdGhlciBzZXJpYWwu\n" +
                   " c2VyaWFsMi4=";
     equal(contents, expected, "revocations.txt should be as expected");
 
     // Check the blocklisted intermediate now causes a failure
-    let file = "tlsserver/test-int-ee.der";
+    let file = "test_onecrl/test-int-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check the ee with the blocklisted root also causes a failure
-    file = "tlsserver/other-issuer-ee.der";
+    file = "bad_certs/other-issuer-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check the ee blocked by subject / pubKey causes a failure
-    file = "tlsserver/same-issuer-ee.der";
+    file = "test_onecrl/same-issuer-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check a non-blocklisted chain still validates OK
-    file = "tlsserver/default-ee.der";
+    file = "bad_certs/default-ee.pem";
     verify_cert(file, PRErrorCodeSuccess);
 
     // Check a bad cert is still bad (unknown issuer)
-    file = "tlsserver/unknown-issuer.der";
+    file = "bad_certs/unknownissuer.pem";
     verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER);
 
     // check that save with no further update is a no-op
diff --git a/security/manager/ssl/tests/unit/test_cert_chains.js b/security/manager/ssl/tests/unit/test_cert_chains.js
index 02e23cec850..8abcb4e65cc 100644
--- a/security/manager/ssl/tests/unit/test_cert_chains.js
+++ b/security/manager/ssl/tests/unit/test_cert_chains.js
@@ -9,16 +9,16 @@ function build_cert_chain(certNames) {
   let certList = Cc["@mozilla.org/security/x509certlist;1"]
                    .createInstance(Ci.nsIX509CertList);
   certNames.forEach(function(certName) {
-    let cert = constructCertFromFile("tlsserver/" + certName + ".der");
+    let cert = constructCertFromFile("bad_certs/" + certName + ".pem");
     certList.addCert(cert);
   });
   return certList;
 }
 
 function test_cert_equals() {
-  let certA = constructCertFromFile("tlsserver/default-ee.der");
-  let certB = constructCertFromFile("tlsserver/default-ee.der");
-  let certC = constructCertFromFile("tlsserver/expired-ee.der");
+  let certA = constructCertFromFile("bad_certs/default-ee.pem");
+  let certB = constructCertFromFile("bad_certs/default-ee.pem");
+  let certC = constructCertFromFile("bad_certs/expired-ee.pem");
 
   ok(certA != certB,
      "Cert objects constructed from the same file should not be equal" +
@@ -68,7 +68,7 @@ function test_security_info_serialization(securityInfo, expectedErrorCode) {
 
 function run_test() {
   do_get_profile();
-  add_tls_server_setup("BadCertServer");
+  add_tls_server_setup("BadCertServer", "bad_certs");
 
   // Test nsIX509Cert.equals
   add_test(function() {
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
index e60c6681965..b2e1d45d5ad 100644
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -13,20 +13,6 @@
 
 do_get_profile();
 
-function add_non_overridable_test(aHost, aExpectedError) {
-  add_connection_test(
-    aHost, aExpectedError, null,
-    function (securityInfo) {
-      // bug 754369 - no SSLStatus probably means this is a non-overridable
-      // error, which is what we're testing (although it would be best to test
-      // this directly).
-      securityInfo.QueryInterface(Ci.nsISSLStatusProvider);
-      equal(securityInfo.SSLStatus, null,
-            "As a proxy to checking that the connection error is" +
-            " non-overridable, SSLStatus should be null");
-    });
-}
-
 function check_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
@@ -70,11 +56,11 @@ function check_telemetry() {
                            .snapshot();
   equal(keySizeHistogram.counts[0], 0,
         "Actual and expected unchecked key size counts should match");
-  equal(keySizeHistogram.counts[1], 0,
+  equal(keySizeHistogram.counts[1], 12,
         "Actual and expected successful verifications of 2048-bit keys should match");
-  equal(keySizeHistogram.counts[2], 12,
+  equal(keySizeHistogram.counts[2], 0,
         "Actual and expected successful verifications of 1024-bit keys should match");
-  equal(keySizeHistogram.counts[3], 48,
+  equal(keySizeHistogram.counts[3], 54,
         "Actual and expected key size verification failures should match");
 
   run_next_test();
@@ -82,7 +68,7 @@ function check_telemetry() {
 
 function run_test() {
   Services.prefs.setIntPref("security.OCSP.enabled", 1);
-  add_tls_server_setup("BadCertServer");
+  add_tls_server_setup("BadCertServer", "bad_certs");
 
   let fakeOCSPResponder = new HttpServer();
   fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
@@ -141,28 +127,29 @@ function add_simple_tests() {
                          SSL_ERROR_BAD_CERT_DOMAIN);
 
   // A Microsoft IIS utility generates self-signed certificates with
-  // properties similar to the one this "host" will present (see
-  // tlsserver/generate_certs.sh).
+  // properties similar to the one this "host" will present.
   add_cert_override_test("selfsigned-inadequateEKU.example.com",
                          Ci.nsICertOverrideService.ERROR_UNTRUSTED,
                          SEC_ERROR_UNKNOWN_ISSUER);
 
-  add_non_overridable_test("inadequatekeyusage.example.com",
-                           SEC_ERROR_INADEQUATE_KEY_USAGE);
+  add_prevented_cert_override_test("inadequatekeyusage.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SEC_ERROR_INADEQUATE_KEY_USAGE);
 
   // This is intended to test the case where a verification has failed for one
   // overridable reason (e.g. unknown issuer) but then, in the process of
   // reporting that error, a non-overridable error is encountered. The
   // non-overridable error should be prioritized.
   add_test(function() {
-    let rootCert = constructCertFromFile("tlsserver/test-ca.der");
+    let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
     setCertTrust(rootCert, ",,");
     run_next_test();
   });
-  add_non_overridable_test("nsCertTypeCritical.example.com",
-                           SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+  add_prevented_cert_override_test("nsCertTypeCritical.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
   add_test(function() {
-    let rootCert = constructCertFromFile("tlsserver/test-ca.der");
+    let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
     setCertTrust(rootCert, "CTu,,");
     run_next_test();
   });
@@ -188,7 +175,7 @@ function add_simple_tests() {
     let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
                                 .getService(Ci.nsICertOverrideService);
     certOverrideService.clearValidityOverride("end-entity-issued-by-v1-cert.example.com", 8443);
-    let v1Cert = constructCertFromFile("tlsserver/v1Cert.der");
+    let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
     setCertTrust(v1Cert, "CTu,,");
     clearSessionCache();
     run_next_test();
@@ -197,7 +184,7 @@ function add_simple_tests() {
                       PRErrorCodeSuccess);
   // Reset the trust for that certificate.
   add_test(function() {
-    let v1Cert = constructCertFromFile("tlsserver/v1Cert.der");
+    let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
     setCertTrust(v1Cert, ",,");
     clearSessionCache();
     run_next_test();
@@ -209,10 +196,11 @@ function add_simple_tests() {
                          Ci.nsICertOverrideService.ERROR_UNTRUSTED,
                          SEC_ERROR_CA_CERT_INVALID);
 
-  // This host presents a 1008-bit RSA key. NSS determines this key is too
+  // This host presents a 1016-bit RSA key. NSS determines this key is too
   // small and terminates the connection. The error is not overridable.
-  add_non_overridable_test("inadequate-key-size-ee.example.com",
-                           SSL_ERROR_WEAK_SERVER_CERT_KEY);
+  add_prevented_cert_override_test("inadequate-key-size-ee.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SSL_ERROR_WEAK_SERVER_CERT_KEY);
 
   add_cert_override_test("ipAddressAsDNSNameInSAN.example.com",
                          Ci.nsICertOverrideService.ERROR_MISMATCH,
@@ -263,13 +251,13 @@ function add_distrust_tests() {
   // Before we specifically distrust this certificate, it should be trusted.
   add_connection_test("untrusted.example.com", PRErrorCodeSuccess);
 
-  add_distrust_test("tlsserver/default-ee.der", "untrusted.example.com",
+  add_distrust_test("bad_certs/default-ee.pem", "untrusted.example.com",
                     SEC_ERROR_UNTRUSTED_CERT);
 
-  add_distrust_test("tlsserver/other-test-ca.der",
+  add_distrust_test("bad_certs/other-test-ca.pem",
                     "untrustedissuer.example.com", SEC_ERROR_UNTRUSTED_ISSUER);
 
-  add_distrust_test("tlsserver/test-ca.der",
+  add_distrust_test("bad_certs/test-ca.pem",
                     "ca-used-as-end-entity.example.com",
                     SEC_ERROR_UNTRUSTED_ISSUER);
 }
@@ -283,7 +271,9 @@ function add_distrust_test(certFileName, hostName, expectedResult) {
     clearSessionCache();
     run_next_test();
   });
-  add_non_overridable_test(hostName, expectedResult);
+  add_prevented_cert_override_test(hostName,
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   expectedResult);
   add_test(function () {
     setCertTrust(certToDistrust, "u,,");
     run_next_test();
diff --git a/security/manager/ssl/tests/unit/test_logoutAndTeardown.js b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js
index 86fbbd1d1a4..853d3e2854b 100644
--- a/security/manager/ssl/tests/unit/test_logoutAndTeardown.js
+++ b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js
@@ -55,7 +55,7 @@ function connect_and_teardown() {
 }
 
 function run_test() {
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
   add_test(connect_and_teardown);
   run_next_test();
 }
diff --git a/security/manager/ssl/tests/unit/test_nsCertType.js b/security/manager/ssl/tests/unit/test_nsCertType.js
index 3e1a07c223b..2e378541d4e 100644
--- a/security/manager/ssl/tests/unit/test_nsCertType.js
+++ b/security/manager/ssl/tests/unit/test_nsCertType.js
@@ -18,7 +18,7 @@
 
 function run_test() {
   do_get_profile();
-  add_tls_server_setup("BadCertServer");
+  add_tls_server_setup("BadCertServer", "bad_certs");
   add_connection_test("nsCertTypeNotCritical.example.com", PRErrorCodeSuccess);
   add_connection_test("nsCertTypeCriticalWithExtKeyUsage.example.com",
                       PRErrorCodeSuccess);
diff --git a/security/manager/ssl/tests/unit/test_ocsp_caching.js b/security/manager/ssl/tests/unit/test_ocsp_caching.js
index 8826ba38a74..3f8a6c43044 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_caching.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js
@@ -24,8 +24,8 @@ function respondWithSHA1OCSP(request, response) {
   response.setStatusLine(request.httpVersion, 200, "OK");
   response.setHeader("Content-Type", "application/ocsp-response");
 
-  let args = [ ["good-delegated", "localhostAndExampleCom", "delegatedSHA1Signer" ] ];
-  let responses = generateOCSPResponses(args, "tlsserver");
+  let args = [ ["good-delegated", "default-ee", "delegatedSHA1Signer" ] ];
+  let responses = generateOCSPResponses(args, "ocsp_certs");
   response.write(responses[0]);
 }
 
@@ -37,8 +37,8 @@ function respondWithError(request, response) {
 }
 
 function generateGoodOCSPResponse() {
-  let args = [ ["good", "localhostAndExampleCom", "unused" ] ];
-  let responses = generateOCSPResponses(args, "tlsserver");
+  let args = [ ["good", "default-ee", "unused" ] ];
+  let responses = generateOCSPResponses(args, "ocsp_certs");
   return responses[0];
 }
 
@@ -54,7 +54,7 @@ function add_ocsp_test(aHost, aExpectedResult, aResponses, aMessage) {
         // check the number of requests matches the size of aResponses
         equal(gFetchCount, aResponses.length,
               "should have made " + aResponses.length +
-              " OCSP request" + aResponses.length == 1 ? "" : "s");
+              " OCSP request" + (aResponses.length == 1 ? "" : "s"));
       });
 }
 
@@ -62,7 +62,7 @@ function run_test() {
   do_get_profile();
   Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
   Services.prefs.setIntPref("security.OCSP.enabled", 1);
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
   let ocspResponder = new HttpServer();
   ocspResponder.registerPrefixHandler("/", function(request, response) {
@@ -134,6 +134,10 @@ function add_tests() {
                   respondWithError,
                   respondWithError,
                   respondWithError,
+                  respondWithError,
+                  respondWithError,
+                  respondWithError,
+                  respondWithError,
                 ],
                 "No stapled response -> a fetch should have been attempted");
 
diff --git a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
index 17594cd71b1..62ff1ba25d8 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
@@ -14,10 +14,10 @@ function run_test() {
 
   // We don't actually make use of stapling in this test. This is just how we
   // get a TLS connection.
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
-  let args = [["good", "localhostAndExampleCom", "unused"]];
-  let ocspResponses = generateOCSPResponses(args, "tlsserver");
+  let args = [["good", "default-ee", "unused"]];
+  let ocspResponses = generateOCSPResponses(args, "ocsp_certs");
   let goodOCSPResponse = ocspResponses[0];
 
   let ocspResponder = new HttpServer();
diff --git a/security/manager/ssl/tests/unit/test_ocsp_required.js b/security/manager/ssl/tests/unit/test_ocsp_required.js
index c21db90c200..81b8677bc01 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_required.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_required.js
@@ -18,10 +18,10 @@ function run_test() {
 
   // We don't actually make use of stapling in this test. This is just how we
   // get a TLS connection.
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
-  let args = [["bad-signature", "localhostAndExampleCom", "unused"]];
-  let ocspResponses = generateOCSPResponses(args, "tlsserver");
+  let args = [["bad-signature", "default-ee", "unused"]];
+  let ocspResponses = generateOCSPResponses(args, "ocsp_certs");
   let ocspResponseBadSignature = ocspResponses[0];
 
   let ocspResponder = new HttpServer();
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling.js b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
index 3b6939eddc6..ac2c85ebb53 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -21,7 +21,7 @@ function add_ocsp_test(aHost, aExpectedResult, aStaplingEnabled) {
     });
 }
 
-function add_tests(certDB, otherTestCA) {
+function add_tests() {
   // In the absence of OCSP stapling, these should actually all work.
   add_ocsp_test("ocsp-stapling-good.example.com",
                 PRErrorCodeSuccess, false);
@@ -73,6 +73,9 @@ function add_tests(certDB, otherTestCA) {
 
   // This stapled response is from a CA that is untrusted and did not issue
   // the server's certificate.
+  let certDB = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  let otherTestCA = constructCertFromFile("ocsp_certs/other-test-ca.pem");
   add_test(function() {
     certDB.setCertTrust(otherTestCA, Ci.nsIX509Cert.CA_CERT,
                         Ci.nsIX509CertDB.UNTRUSTED);
@@ -186,11 +189,6 @@ function check_ocsp_stapling_telemetry() {
 function run_test() {
   do_get_profile();
 
-  let certDB = Cc["@mozilla.org/security/x509certdb;1"]
-                  .getService(Ci.nsIX509CertDB);
-  let otherTestCAFile = do_get_file("tlsserver/other-test-ca.der", false);
-  let otherTestCADER = readFile(otherTestCAFile);
-  let otherTestCA = certDB.constructX509(otherTestCADER, otherTestCADER.length);
 
   let fakeOCSPResponder = new HttpServer();
   fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
@@ -200,9 +198,9 @@ function run_test() {
   });
   fakeOCSPResponder.start(8888);
 
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
-  add_tests(certDB, otherTestCA);
+  add_tests();
 
   add_test(function () {
     fakeOCSPResponder.stop(check_ocsp_stapling_telemetry);
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
index 8a91e050d09..9047a0009d4 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -24,20 +24,20 @@ function add_ocsp_test(aHost, aExpectedResult, aOCSPResponseToServe,
     function() {
       equal(gOCSPRequestCount, aExpectedRequestCount,
             "Should have made " + aExpectedRequestCount +
-            " fallback OCSP request" + aExpectedRequestCount == 1 ? "" : "s");
+            " fallback OCSP request" + (aExpectedRequestCount == 1 ? "" : "s"));
     });
 }
 
 do_get_profile();
 Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
 Services.prefs.setIntPref("security.OCSP.enabled", 1);
-var args = [["good", "localhostAndExampleCom", "unused"],
-             ["expiredresponse", "localhostAndExampleCom", "unused"],
-             ["oldvalidperiod", "localhostAndExampleCom", "unused"],
-             ["revoked", "localhostAndExampleCom", "unused"],
-             ["unknown", "localhostAndExampleCom", "unused"],
+var args = [["good", "default-ee", "unused"],
+             ["expiredresponse", "default-ee", "unused"],
+             ["oldvalidperiod", "default-ee", "unused"],
+             ["revoked", "default-ee", "unused"],
+             ["unknown", "default-ee", "unused"],
             ];
-var ocspResponses = generateOCSPResponses(args, "tlsserver");
+var ocspResponses = generateOCSPResponses(args, "ocsp_certs");
 // Fresh response, certificate is good.
 var ocspResponseGood = ocspResponses[0];
 // Expired response, certificate is good.
@@ -53,9 +53,9 @@ var ocspResponseUnknown = ocspResponses[4];
 var willNotRetry = 1;
 // but sometimes, since a bad response is in the cache, OCSP fetch will be
 // attempted for each validation - in practice, for these test certs, this
-// means 4 requests because various hash algorithm combinations are tried
-// (for sha-1 telemetry)
-var willRetry = 4;
+// means 8 requests because various hash algorithm and key size combinations
+// are tried.
+var willRetry = 8;
 
 function run_test() {
   let ocspResponder = new HttpServer();
@@ -71,7 +71,7 @@ function run_test() {
     gOCSPRequestCount++;
   });
   ocspResponder.start(8888);
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
   // In these tests, the OCSP stapling server gives us a stapled
   // response based on the host name ("ocsp-stapling-expired" or
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
index cbbd149d860..21676f1d5bc 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
@@ -31,7 +31,7 @@ function run_test() {
   });
   ocspResponder.start(8888);
 
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
   add_ocsp_test("ocsp-stapling-with-intermediate.example.com",
                 PRErrorCodeSuccess);
diff --git a/security/manager/ssl/tests/unit/test_ocsp_timeout.js b/security/manager/ssl/tests/unit/test_ocsp_timeout.js
index 3db423b6240..1c480337111 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_timeout.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_timeout.js
@@ -28,7 +28,7 @@ function run_test() {
   do_get_profile();
   Services.prefs.setIntPref("security.OCSP.enabled", 1);
 
-  add_tls_server_setup("OCSPStaplingServer");
+  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
 
   let socket = Cc["@mozilla.org/network/server-socket;1"]
                  .createInstance(Ci.nsIServerSocket);
diff --git a/security/manager/ssl/tests/unit/test_onecrl/moz.build b/security/manager/ssl/tests/unit/test_onecrl/moz.build
new file mode 100644
index 00000000000..0ba72647fa7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+test_certificates = (
+    'same-issuer-ee.pem',
+    'test-int-ee.pem',
+)
+
+for test_certificate in test_certificates:
+    input_file = test_certificate + '.certspec'
+    GENERATED_FILES += [test_certificate]
+    props = GENERATED_FILES[test_certificate]
+    props.script = '../pycert.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_onecrl += ['!%s' % test_certificate]
diff --git a/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec
new file mode 100644
index 00000000000..8b20f03f59e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Another Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec
new file mode 100644
index 00000000000..24792d540a5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test Intermediate
+subject:EE issued by intermediate
+extension:subjectAlternativeName:localhost
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
index b771be2b046..59056475bc3 100644
--- a/security/manager/ssl/tests/unit/test_pinning.js
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -246,10 +246,10 @@ function check_pinning_telemetry() {
 }
 
 function run_test() {
-  add_tls_server_setup("BadCertServer");
+  add_tls_server_setup("BadCertServer", "bad_certs");
 
   // Add a user-specified trust anchor.
-  addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+  addCertFromFile(certdb, "bad_certs/other-test-ca.pem", "CTu,u,u");
 
   test_strict();
   test_mitm();
diff --git a/security/manager/ssl/tests/unit/tlsserver/cert9.db b/security/manager/ssl/tests/unit/tlsserver/cert9.db
deleted file mode 100644
index ecc9900149d..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/cert9.db and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
index b980e443bb2..adcfa2461b4 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -27,7 +27,7 @@ struct BadCertHost
 // Hostname, cert nickname pairs.
 const BadCertHost sBadCertHosts[] =
 {
-  { "expired.example.com", "expired" },
+  { "expired.example.com", "expired-ee" },
   { "notyetvalid.example.com", "notYetValid" },
   { "before-epoch.example.com", "beforeEpoch" },
   { "selfsigned.example.com", "selfsigned" },
@@ -38,7 +38,7 @@ const BadCertHost sBadCertHosts[] =
   { "notyetvalidissuer.example.com", "notYetValidIssuer" },
   { "before-epoch-issuer.example.com", "beforeEpochIssuer" },
   { "md5signature.example.com", "md5signature" },
-  { "untrusted.example.com", "localhostAndExampleCom" },
+  { "untrusted.example.com", "default-ee" },
   { "untrustedissuer.example.com", "untrustedissuer" },
   { "mismatch-expired.example.com", "mismatch-expired" },
   { "mismatch-notYetValid.example.com", "mismatch-notYetValid" },
@@ -46,23 +46,23 @@ const BadCertHost sBadCertHosts[] =
   { "untrusted-expired.example.com", "untrusted-expired" },
   { "md5signature-expired.example.com", "md5signature-expired" },
   { "mismatch-untrusted-expired.example.com", "mismatch-untrusted-expired" },
-  { "inadequatekeyusage.example.com", "inadequatekeyusage" },
+  { "inadequatekeyusage.example.com", "inadequatekeyusage-ee" },
   { "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" },
   { "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" },
   { "ca-used-as-end-entity.example.com", "ca-used-as-end-entity" },
   { "ca-used-as-end-entity-name-mismatch.example.com", "ca-used-as-end-entity" },
   // All of include-subdomains.pinning.example.com is pinned to End Entity
-  // Test Cert with nick localhostAndExampleCom. Any other nick will only
+  // Test Cert with nick default-ee. Any other nick will only
   // pass pinning when security.cert_pinning.enforcement.level != strict and
   // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
-  { "include-subdomains.pinning.example.com", "localhostAndExampleCom" },
-  { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" },
-  { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" },
-  { "bad.include-subdomains.pinning.example.com.", "otherIssuerEE" },
-  { "bad.include-subdomains.pinning.example.com..", "otherIssuerEE" },
-  { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" },
-  { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" },
-  { "test-mode.pinning.example.com", "otherIssuerEE" },
+  { "include-subdomains.pinning.example.com", "default-ee" },
+  { "good.include-subdomains.pinning.example.com", "default-ee" },
+  { "bad.include-subdomains.pinning.example.com", "other-issuer-ee" },
+  { "bad.include-subdomains.pinning.example.com.", "other-issuer-ee" },
+  { "bad.include-subdomains.pinning.example.com..", "other-issuer-ee" },
+  { "exclude-subdomains.pinning.example.com", "default-ee" },
+  { "sub.exclude-subdomains.pinning.example.com", "other-issuer-ee" },
+  { "test-mode.pinning.example.com", "other-issuer-ee" },
   { "unknownissuer.include-subdomains.pinning.example.com", "unknownissuer" },
   { "unknownissuer.test-mode.pinning.example.com", "unknownissuer" },
   { "nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical" },
@@ -78,13 +78,37 @@ const BadCertHost sBadCertHosts[] =
 };
 
 int32_t
-DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr,
-                  uint32_t aSrvNameArrSize, void *aArg)
+DoSNISocketConfigBySubjectCN(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+                             uint32_t aSrvNameArrSize)
 {
-  const BadCertHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
+  for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+    ScopedPORTString name((char*)PORT_ZAlloc(aSrvNameArr[i].len + 1));
+    if (name) {
+      PORT_Memcpy(name, aSrvNameArr[i].data, aSrvNameArr[i].len);
+      if (SECSuccess == ConfigSecureServerWithNamedCert(aFd, name,
+                                                        nullptr, nullptr)) {
+        return 0;
+      }
+    }
+  }
+
+  return SSL_SNI_SEND_ALERT;
+}
+
+int32_t
+DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+                  uint32_t aSrvNameArrSize, void* aArg)
+{
+  const BadCertHost* host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
                                           sBadCertHosts);
   if (!host) {
-    return SSL_SNI_SEND_ALERT;
+    // No static cert <-> hostname mapping found. This happens when we use a
+    // collection of certificates in a given directory and build a cert DB at
+    // runtime, rather than using an NSS cert DB populated at build time.
+    // (This will be the default in the future.)
+    // For all given server names, check if the runtime-built cert DB contains
+    // a certificate with a matching subject CN.
+    return DoSNISocketConfigBySubjectCN(aFd, aSrvNameArr, aSrvNameArrSize);
   }
 
   if (gDebugLevel >= DEBUG_VERBOSE) {
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
index 96001e781d6..19a7e2660f0 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
@@ -110,219 +110,6 @@ WriteResponse(const char* filename, const SECItem* item)
   return true;
 }
 
-template 
-SECStatus
-ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N])
-{
-  static_assert(N > 0, "input buffer too small for ReadFileToBuffer");
-  if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) {
-    PrintPRError("PR_snprintf failed");
-    return SECFailure;
-  }
-  ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0));
-  if (!fd) {
-    PrintPRError("PR_Open failed");
-    return SECFailure;
-  }
-  int32_t fileSize = PR_Available(fd);
-  if (fileSize < 0) {
-    PrintPRError("PR_Available failed");
-    return SECFailure;
-  }
-  if (static_cast(fileSize) > N - 1) {
-    PR_fprintf(PR_STDERR, "file too large - not reading\n");
-    return SECFailure;
-  }
-  int32_t bytesRead = PR_Read(fd, buf, fileSize);
-  if (bytesRead != fileSize) {
-    PrintPRError("PR_Read failed");
-    return SECFailure;
-  }
-  buf[bytesRead] = 0;
-  return SECSuccess;
-}
-
-namespace mozilla {
-
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir);
-MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, unsigned char, PORT_Free);
-
-};
-
-void
-AddKeyFromFile(const char* basePath, const char* filename)
-{
-  const char* PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";
-  const char* PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
-
-  char buf[16384] = { 0 };
-  SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
-  if (rv != SECSuccess) {
-    return;
-  }
-  if (strncmp(buf, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER)) != 0) {
-    PR_fprintf(PR_STDERR, "invalid key - not importing\n");
-    return;
-  }
-  const char* bufPtr = buf + strlen(PRIVATE_KEY_HEADER);
-  size_t bufLen = strlen(buf);
-  char base64[16384] = { 0 };
-  char* base64Ptr = base64;
-  while (bufPtr < buf + bufLen) {
-    if (strncmp(bufPtr, PRIVATE_KEY_FOOTER, strlen(PRIVATE_KEY_FOOTER)) == 0) {
-      break;
-    }
-    if (*bufPtr != '\r' && *bufPtr != '\n') {
-      *base64Ptr = *bufPtr;
-      base64Ptr++;
-    }
-    bufPtr++;
-  }
-
-  unsigned int binLength;
-  ScopedPORTString bin(ATOB_AsciiToData(base64, &binLength));
-  if (!bin || binLength == 0) {
-    PrintPRError("ATOB_AsciiToData failed");
-    return;
-  }
-  ScopedSECItem secitem(SECITEM_AllocItem(nullptr, nullptr, binLength));
-  if (!secitem) {
-    PrintPRError("SECITEM_AllocItem failed");
-    return;
-  }
-  memcpy(secitem->data, bin, binLength);
-  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
-  if (!slot) {
-    PrintPRError("PK11_GetInternalKeySlot failed");
-    return;
-  }
-  if (PK11_NeedUserInit(slot)) {
-    if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) {
-      PrintPRError("PK11_InitPin failed");
-      return;
-    }
-  }
-  SECKEYPrivateKey* privateKey;
-  if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr,
-                                               true, false, KU_ALL,
-                                               &privateKey, nullptr)
-        != SECSuccess) {
-    PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
-    return;
-  }
-  SECKEY_DestroyPrivateKey(privateKey);
-}
-
-SECStatus
-DecodeCertCallback(void* arg, SECItem** certs, int numcerts)
-{
-  if (numcerts != 1) {
-    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
-    return SECFailure;
-  }
-  SECItem* certDEROut = static_cast(arg);
-  return SECITEM_CopyItem(nullptr, certDEROut, *certs);
-}
-
-void
-AddCertificateFromFile(const char* basePath, const char* filename)
-{
-  char buf[16384] = { 0 };
-  SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
-  if (rv != SECSuccess) {
-    return;
-  }
-  SECItem certDER;
-  rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER);
-  if (rv != SECSuccess) {
-    PrintPRError("CERT_DecodeCertPackage failed");
-    return;
-  }
-  ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
-                                                     &certDER, nullptr, false,
-                                                     true));
-  PORT_Free(certDER.data);
-  if (!cert) {
-    PrintPRError("CERT_NewTempCertificate failed");
-    return;
-  }
-  const char* extension = strstr(filename, ".pem");
-  if (!extension) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return;
-  }
-  size_t nicknameLength = extension - filename;
-  memset(buf, 0, sizeof(buf));
-  memcpy(buf, filename, nicknameLength);
-  buf[nicknameLength] = 0;
-  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
-  if (!slot) {
-    PrintPRError("PK11_GetInternalKeySlot failed");
-    return;
-  }
-  rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, buf, false);
-  if (rv != SECSuccess) {
-    PrintPRError("PK11_ImportCert failed");
-  }
-}
-
-SECStatus
-InitializeNSS(const char* nssCertDBDir)
-{
-  // First attempt to initialize NSS in read-only mode, in case the specified
-  // directory contains NSS DBs that are tracked by revision control.
-  // If this succeeds, we're done.
-  if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, NSS_INIT_READONLY)
-        == SECSuccess) {
-    return SECSuccess;
-  }
-  // Otherwise, create a new read-write DB and load all .pem and .key files.
-  if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, 0) != SECSuccess) {
-    PrintPRError("NSS_Initialize failed");
-    return SECFailure;
-  }
-  const char* basePath = nssCertDBDir;
-  // The NSS cert DB path could have been specified as "sql:path". Trim off
-  // the leading "sql:" if so.
-  if (strncmp(basePath, "sql:", 4) == 0) {
-    basePath = basePath + 4;
-  }
-  ScopedPRDir fdDir(PR_OpenDir(basePath));
-  if (!fdDir) {
-    PrintPRError("PR_OpenDir failed");
-    return SECFailure;
-  }
-  // On the B2G ICS emulator, operations taken in AddCertificateFromFile or
-  // AddKeyFromFile appear to interact poorly with readdir (more specifically,
-  // something is causing readdir to never return null - it indefinitely loops
-  // through every file in the directory, which causes timeouts). Rather than
-  // waste more time chasing this down, loading certificates and keys happens in
-  // two phases: filename collection and then loading. (This is probably a good
-  // idea anyway because readdir isn't reentrant. Something could change later
-  // such that it gets called as a result of calling AddCertificateFromFile or
-  // AddKeyFromFile.)
-  std::vector certificates;
-  std::vector keys;
-  for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry;
-       dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) {
-    size_t nameLength = strlen(dirEntry->name);
-    if (nameLength > 4) {
-      if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
-        certificates.push_back(dirEntry->name);
-      } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
-        keys.push_back(dirEntry->name);
-      }
-    }
-  }
-  for (std::string& certificate : certificates) {
-    AddCertificateFromFile(basePath, certificate.c_str());
-  }
-  for (std::string& key : keys) {
-    AddKeyFromFile(basePath, key.c_str());
-  }
-  return SECSuccess;
-}
-
 int
 main(int argc, char* argv[])
 {
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
index c0c1ee6947a..0d61fdef0f0 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
@@ -27,7 +27,7 @@ const OCSPHost sOCSPHosts[] =
   { "ocsp-stapling-unknown.example.com", ORTUnknown, nullptr },
   { "ocsp-stapling-unknown-old.example.com", ORTUnknownOld, nullptr },
   { "ocsp-stapling-good-other.example.com", ORTGoodOtherCert, "ocspOtherEndEntity" },
-  { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "otherCA" },
+  { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "other-test-ca" },
   { "ocsp-stapling-expired.example.com", ORTExpired, nullptr },
   { "ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr },
   { "ocsp-stapling-none.example.com", ORTNone, nullptr },
@@ -52,7 +52,7 @@ const OCSPHost sOCSPHosts[] =
   { "ocsp-stapling-delegated-keyUsage-crlSigning.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning" },
   { "ocsp-stapling-delegated-wrong-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage" },
   { "ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired, nullptr},
-  { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "rsa-1008-keysizeDelegatedSigner" },
+  { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "rsa-1016-keysizeDelegatedSigner" },
   { "revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked, "ca-used-as-end-entity" },
   { nullptr, ORTNull, nullptr }
 };
diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
index 7f89b7e37c3..3a9b8fa9bc0 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/default-ee.der and b/security/manager/ssl/tests/unit/tlsserver/default-ee.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/expired-ee.der b/security/manager/ssl/tests/unit/tlsserver/expired-ee.der
deleted file mode 100644
index fd9e7e9e940..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/expired-ee.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
deleted file mode 100755
index 8f7ba11b62f..00000000000
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
+++ /dev/null
@@ -1,354 +0,0 @@
-#!/bin/bash
-#
-# 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/.
-#
-# Usage: ./generate_certs.sh   [--clobber]
-# e.g. (from the root of mozilla-central)
-# `./security/manager/ssl/tests/unit/tlsserver/generate_certs.sh \
-#  obj-x86_64-unknown-linux-gnu/ \
-#  security/manager/ssl/tests/unit/tlsserver/`
-#
-# The --clobber switch is optional. If specified, the existing database of
-# keys and certificates is removed and repopulated. By default, existing
-# databases are preserved and only keys and certificates that don't already
-# exist in the database are added.
-# NB: If --clobber is specified, the following files to be overwritten if they
-# are in the output directory:
-#  cert9.db, key4.db, pkcs11.txt, test-ca.der, other-test-ca.der, default-ee.der
-# (if --clobber is not specified, then only cert9.db and key4.db are modified)
-# NB: If --clobber is specified, you must run genHPKPStaticPins.js after
-# running this file, since its output (StaticHPKPins.h) depends on
-# default-ee.der
-
-set -x
-set -e
-
-if [ $# -lt 2 ]; then
-  echo "Usage: `basename ${0}`   [--clobber]"
-  exit $E_BADARGS
-fi
-
-OBJDIR=${1}
-OUTPUT_DIR=${2}
-CLOBBER=0
-if [ "${3}" == "--clobber" ]; then
-  CLOBBER=1
-fi
-# Use the SQL DB so we can run tests on Android.
-DB_ARGUMENT="sql:$OUTPUT_DIR"
-RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
-CERTUTIL="$OBJDIR/dist/bin/certutil"
-# On BSD, mktemp requires either a template or a prefix.
-MKTEMP="mktemp temp.XXXX"
-
-NOISE_FILE=`$MKTEMP`
-# Make a good effort at putting something unique in the noise file.
-date +%s%N  > "$NOISE_FILE"
-PASSWORD_FILE=`$MKTEMP`
-
-function cleanup {
-  rm -f "$NOISE_FILE" "$PASSWORD_FILE"
-}
-
-if [ ! -f "$RUN_MOZILLA" ]; then
-  echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\' - I'll try without it"
-  RUN_MOZILLA=""
-fi
-
-if [ ! -f "$CERTUTIL" ]; then
-  echo "Could not find certutil at \'$CERTUTIL\'"
-  exit $E_BADARGS
-fi
-
-if [ ! -d "$OUTPUT_DIR" ]; then
-  echo "Could not find output directory at \'$OUTPUT_DIR\'"
-  exit $E_BADARGS
-fi
-
-if [ -f "$OUTPUT_DIR/cert9.db" -o -f "$OUTPUT_DIR/key4.db" -o -f "$OUTPUT_DIR/pkcs11.txt" ]; then
-  if [ $CLOBBER -eq 1 ]; then
-    echo "Found pre-existing NSS DBs. Clobbering old certificates."
-    rm -f "$OUTPUT_DIR/cert9.db" "$OUTPUT_DIR/key4.db" "$OUTPUT_DIR/pkcs11.txt"
-    $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -N -f $PASSWORD_FILE
-  else
-    echo "Found pre-existing NSS DBs. Only generating newly added certificates."
-    echo "(re-run with --clobber to remove and regenerate old certificates)"
-  fi
-else
-  echo "No pre-existing NSS DBs found. Creating new ones."
-  $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -N -f $PASSWORD_FILE
-fi
-
-COMMON_ARGS="-v 360 -w -1 -2 -z $NOISE_FILE"
-
-function export_cert {
-  NICKNAME="${1}"
-  DERFILE="${2}"
-
-  $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -L -n $NICKNAME -r > $OUTPUT_DIR/$DERFILE
-}
-
-# Bash doesn't actually allow return values in a sane way, so just use a
-# global variable.
-function cert_already_exists {
-  NICKNAME="${1}"
-  ALREADY_EXISTS=1
-  $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -L -n $NICKNAME || ALREADY_EXISTS=0
-}
-
-function make_CA {
-  CA_RESPONSES="y\n1\ny"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  DERFILE="${3}"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  echo -e "$CA_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                                                   -n $NICKNAME \
-                                                   -s "$SUBJECT" \
-                                                   -t "CT,," \
-                                                   -x $COMMON_ARGS
-  export_cert $NICKNAME $DERFILE
-}
-
-SERIALNO=$RANDOM
-
-function make_INT {
-  INT_RESPONSES="y\n0\ny\n2\n7\nhttp://localhost:8888/\n\nn\nn\n"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-  EXTRA_ARGS="${4}"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  echo -e "$INT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                                                    -n $NICKNAME \
-                                                    -s "$SUBJECT" \
-                                                    -c $CA \
-                                                    -t ",," \
-                                                    -m $SERIALNO \
-                                                    --extAIA \
-                                                    $COMMON_ARGS \
-                                                    $EXTRA_ARGS
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-# This creates an X.509 version 1 certificate (note --certVersion 1 and a lack
-# of extensions).
-function make_V1 {
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                         -n $NICKNAME \
-                         -s "$SUBJECT" \
-                         -c $CA \
-                         -t ",," \
-                         -m $SERIALNO \
-                         --certVersion 1 \
-                         -v 360 -w -1 -z $NOISE_FILE
-
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-function make_EE {
-  CERT_RESPONSES="n\n\ny\n2\n7\nhttp://localhost:8888/\n\nn\nn\n"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-  SUBJECT_ALT_NAME="${4}"
-  EXTRA_ARGS="${5} ${6}"
-
-  [ -z "$SUBJECT_ALT_NAME" ] && SUBJECT_ALT_NAME_PART="" || SUBJECT_ALT_NAME_PART="-8 $SUBJECT_ALT_NAME"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                                                     -n $NICKNAME \
-                                                     -s "$SUBJECT" \
-                                                     $SUBJECT_ALT_NAME_PART \
-                                                     -c $CA \
-                                                     -t ",," \
-                                                     -m $SERIALNO \
-                                                     --extAIA \
-                                                     $COMMON_ARGS \
-                                                     $EXTRA_ARGS
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-function make_EE_with_nsCertType {
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-  SUBJECT_ALT_NAME="${4}"
-  NS_CERT_TYPE_CRITICAL="${5}"
-  EXTRA_ARGS="${6}"
-  # This adds the Netscape certificate type extension with the "sslServer"
-  # bit asserted. Its criticality depends on if "y" or "n" was passed as
-  # an argument to this function.
-  CERT_RESPONSES="n\n\ny\n1\n8\n$NS_CERT_TYPE_CRITICAL\n"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                                                     -n $NICKNAME \
-                                                     -s "$SUBJECT" \
-                                                     -8 $SUBJECT_ALT_NAME \
-                                                     -c $CA \
-                                                     -t ",," \
-                                                     -m $SERIALNO \
-                                                     -5 \
-                                                     $COMMON_ARGS \
-                                                     $EXTRA_ARGS
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-function make_delegated {
-  CERT_RESPONSES="n\n\ny\n"
-  NICKNAME="${1}"
-  SUBJECT="${2}"
-  CA="${3}"
-  EXTRA_ARGS="${4}"
-
-  cert_already_exists $NICKNAME
-  if [ $ALREADY_EXISTS -eq 1 ]; then
-    echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)"
-    return
-  fi
-
-  echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \
-                                                     -n $NICKNAME \
-                                                     -s "$SUBJECT" \
-                                                     -c $CA \
-                                                     -t ",," \
-                                                     -m $SERIALNO \
-                                                     $COMMON_ARGS \
-                                                     $EXTRA_ARGS
-  SERIALNO=$(($SERIALNO + 1))
-}
-
-make_CA testCA 'CN=Test CA' test-ca.der
-make_CA otherCA 'CN=Other test CA' other-test-ca.der
-
-make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com"
-# Make another EE cert using testCA for subject / pubkey revocation
-make_EE sameIssuerEE 'CN=Another Test End-entity' testCA "localhost,*.example.com"
-# Make an EE cert issued by otherCA
-make_EE otherIssuerEE 'CN=Wrong CA Pin Test End-Entity' otherCA "*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com"
-
-export_cert localhostAndExampleCom default-ee.der
-export_cert sameIssuerEE same-issuer-ee.der
-export_cert otherIssuerEE other-issuer-ee.der
-
-# A cert that is like localhostAndExampleCom, but with a different serial number for
-# testing the "OCSP response is from the right issuer, but it is for the wrong cert"
-# case.
-make_EE ocspOtherEndEntity 'CN=Other Cert' testCA "localhost,*.example.com"
-
-make_INT testINT 'CN=Test Intermediate' testCA
-export_cert testINT test-int.der
-make_EE ocspEEWithIntermediate 'CN=Test End-entity with Intermediate' testINT "localhost,*.example.com"
-make_EE expired 'CN=Expired Test End-entity' testCA "expired.example.com" "-w -400"
-export_cert expired expired-ee.der
-make_EE notYetValid 'CN=Not Yet Valid Test End-entity' testCA "notyetvalid.example.com" "-w 400"
-make_EE mismatch 'CN=Mismatch Test End-entity' testCA "doesntmatch.example.com,*.alsodoesntmatch.example.com"
-make_EE mismatchCN 'CN=doesntmatch.example.com' testCA
-make_EE ipAddressAsDNSNameInSAN 'CN=127.0.0.1' testCA "127.0.0.1"
-make_EE noValidNames 'CN=End-entity with no valid names' testCA
-make_EE selfsigned 'CN=Self-signed Test End-entity' testCA "selfsigned.example.com" "-x"
-# If the certificate 'CN=Test Intermediate' isn't loaded into memory,
-# this certificate will have an unknown issuer.
-# deletedINT is never kept in the database, so it always gets regenerated.
-# That's ok, because if unknownissuer was already in the database, it won't
-# get regenerated. Either way, deletedINT will then be removed again.
-make_INT deletedINT 'CN=Test Intermediate to delete' testCA
-make_EE unknownissuer 'CN=Test End-entity from unknown issuer' deletedINT "unknownissuer.example.com,unknownissuer.include-subdomains.pinning.example.com,unknownissuer.test-mode.pinning.example.com"
-export_cert unknownissuer unknown-issuer.der
-
-$RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -D -n deletedINT
-
-# certutil doesn't expose a way to directly specify a notBefore time.
-# Workaround this by just providing a large enough warp that the notBefore time
-# falls before the UNIX Epoch.
-make_EE beforeEpoch 'CN=Before UNIX Epoch Test End-entity' testCA "before-epoch.example.com" "-w -720 -v 960"
-make_INT beforeEpochINT 'CN=Before UNIX Epoch Test Intermediate' testCA "-w -720 -v 960"
-make_EE beforeEpochIssuer 'CN=Test End-entity with Before UNIX Epoch issuer' beforeEpochINT "before-epoch-issuer.example.com"
-
-make_INT expiredINT 'CN=Expired Test Intermediate' testCA "-w -400"
-make_EE expiredissuer 'CN=Test End-entity with expired issuer' expiredINT "expiredissuer.example.com"
-make_INT notYetValidINT 'CN=Not Yet Valid Test Intermediate' testCA "-w 400"
-make_EE notYetValidIssuer 'CN=Test End-entity with not yet valid issuer' notYetValidINT "notyetvalidissuer.example.com"
-NSS_ALLOW_WEAK_SIGNATURE_ALG=1 make_EE md5signature 'CN=Test End-entity with MD5 signature' testCA "md5signature.example.com" "-Z MD5"
-make_EE untrustedissuer 'CN=Test End-entity with untrusted issuer' otherCA "untrustedissuer.example.com"
-
-make_EE mismatch-expired 'CN=Mismatch-Expired Test End-entity' testCA "doesntmatch.example.com" "-w -400"
-make_EE mismatch-notYetValid 'CN=Mismatch-Not Yet Valid Test End-entity' testCA "doesntmatch.example.com" "-w 400"
-make_EE mismatch-untrusted 'CN=Mismatch-Untrusted Test End-entity' otherCA "doesntmatch.example.com"
-make_EE untrusted-expired 'CN=Untrusted-Expired Test End-entity' otherCA "untrusted-expired.example.com" "-w -400"
-make_EE mismatch-untrusted-expired 'CN=Mismatch-Untrusted-Expired Test End-entity' otherCA "doesntmatch.example.com" "-w -400"
-NSS_ALLOW_WEAK_SIGNATURE_ALG=1 make_EE md5signature-expired 'CN=Test MD5Signature-Expired End-entity' testCA "md5signature-expired.example.com" "-Z MD5" "-w -400"
-
-make_EE inadequatekeyusage 'CN=Inadequate Key Usage Test End-entity' testCA "inadequatekeyusage.example.com" "--keyUsage crlSigning"
-export_cert inadequatekeyusage inadequatekeyusage-ee.der
-make_EE selfsigned-inadequateEKU 'CN=Self-signed Inadequate EKU Test End-entity' unused "selfsigned-inadequateEKU.example.com" "--keyUsage keyEncipherment,dataEncipherment --extKeyUsage serverAuth" "-x"
-
-make_delegated delegatedSigner 'CN=Test Delegated Responder' testCA "--extKeyUsage ocspResponder"
-make_delegated delegatedSHA1Signer 'CN=Test SHA1 Delegated Responder' testCA "--extKeyUsage ocspResponder -Z SHA1"
-make_delegated invalidDelegatedSignerNoExtKeyUsage 'CN=Test Invalid Delegated Responder No extKeyUsage' testCA
-make_delegated invalidDelegatedSignerFromIntermediate 'CN=Test Invalid Delegated Responder From Intermediate' testINT "--extKeyUsage ocspResponder"
-make_delegated invalidDelegatedSignerKeyUsageCrlSigning 'CN=Test Invalid Delegated Responder keyUsage crlSigning' testCA "--keyUsage crlSigning"
-make_delegated invalidDelegatedSignerWrongExtKeyUsage 'CN=Test Invalid Delegated Responder Wrong extKeyUsage' testCA "--extKeyUsage codeSigning"
-
-make_INT self-signed-EE-with-cA-true 'CN=Test Self-signed End-entity with CA true' unused "-x -8 self-signed-end-entity-with-cA-true.example.com"
-make_INT ca-used-as-end-entity 'CN=Test Intermediate used as End-Entity' testCA "-8 ca-used-as-end-entity.example.com"
-
-make_delegated rsa-1008-keysizeDelegatedSigner 'CN=RSA 1008 Key Size Test Delegated Responder' testCA "--extKeyUsage ocspResponder -g 1008"
-make_EE inadequateKeySizeEE 'CN=Inadequate Key Size End-Entity' testINT "inadequate-key-size-ee.example.com" "-g 1008"
-
-make_EE_with_nsCertType nsCertTypeCritical 'CN=nsCertType Critical' testCA "localhost,*.example.com" "y"
-make_EE_with_nsCertType nsCertTypeNotCritical 'CN=nsCertType Not Critical' testCA "localhost,*.example.com" "n"
-make_EE_with_nsCertType nsCertTypeCriticalWithExtKeyUsage 'CN=nsCertType Critical With extKeyUsage' testCA "localhost,*.example.com" "y" "--extKeyUsage serverAuth"
-
-# Make an X.509 version 1 certificate that will issue another certificate.
-# By default, this causes an error in verification that we allow overrides for.
-# However, if the v1 certificate is a trust anchor, then verification succeeds.
-make_V1 v1Cert 'CN=V1 Cert' testCA
-export_cert v1Cert v1Cert.der
-make_EE eeIssuedByV1Cert 'CN=EE Issued by V1 Cert' v1Cert "localhost,*.example.com"
-
-make_EE eeIssuedByNonCA 'CN=EE Issued by non-CA' localhostAndExampleCom "localhost,*.example.com"
-
-# Make a valid EE using testINT to test OneCRL revocation of testINT
-make_EE eeIssuedByIntermediate 'CN=EE issued by intermediate' testINT "localhost"
-export_cert eeIssuedByIntermediate test-int-ee.der
-
-make_EE badSubjectAltNames 'CN=EE with bad subjectAltNames' testCA "*.*.example.com"
-
-cleanup
diff --git a/security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der b/security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der
deleted file mode 100644
index 4daa9a5eaa1..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/key4.db b/security/manager/ssl/tests/unit/tlsserver/key4.db
deleted file mode 100644
index c3e06ff7099..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/key4.db and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
index 2a10ec44425..3bc28b44cfa 100644
--- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
@@ -5,7 +5,11 @@
 #include "TLSServer.h"
 
 #include 
+#include 
+#include 
+
 #include "ScopedNSSTypes.h"
+#include "base64.h"
 #include "nspr.h"
 #include "nss.h"
 #include "plarenas.h"
@@ -22,7 +26,7 @@ static const uint16_t LISTEN_PORT = 8443;
 DebugLevel gDebugLevel = DEBUG_ERRORS;
 uint16_t gCallbackPort = 0;
 
-const char DEFAULT_CERT_NICKNAME[] = "localhostAndExampleCom";
+const char DEFAULT_CERT_NICKNAME[] = "default-ee";
 
 struct Connection
 {
@@ -60,6 +64,222 @@ PrintPRError(const char *aPrefix)
   }
 }
 
+template 
+SECStatus
+ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N])
+{
+  static_assert(N > 0, "input buffer too small for ReadFileToBuffer");
+  if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) {
+    PrintPRError("PR_snprintf failed");
+    return SECFailure;
+  }
+  ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0));
+  if (!fd) {
+    PrintPRError("PR_Open failed");
+    return SECFailure;
+  }
+  int32_t fileSize = PR_Available(fd);
+  if (fileSize < 0) {
+    PrintPRError("PR_Available failed");
+    return SECFailure;
+  }
+  if (static_cast(fileSize) > N - 1) {
+    PR_fprintf(PR_STDERR, "file too large - not reading\n");
+    return SECFailure;
+  }
+  int32_t bytesRead = PR_Read(fd, buf, fileSize);
+  if (bytesRead != fileSize) {
+    PrintPRError("PR_Read failed");
+    return SECFailure;
+  }
+  buf[bytesRead] = 0;
+  return SECSuccess;
+}
+
+SECStatus
+AddKeyFromFile(const char* basePath, const char* filename)
+{
+  const char* PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";
+  const char* PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
+
+  char buf[16384] = { 0 };
+  SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  if (strncmp(buf, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER)) != 0) {
+    PR_fprintf(PR_STDERR, "invalid key - not importing\n");
+    return SECFailure;
+  }
+  const char* bufPtr = buf + strlen(PRIVATE_KEY_HEADER);
+  size_t bufLen = strlen(buf);
+  char base64[16384] = { 0 };
+  char* base64Ptr = base64;
+  while (bufPtr < buf + bufLen) {
+    if (strncmp(bufPtr, PRIVATE_KEY_FOOTER, strlen(PRIVATE_KEY_FOOTER)) == 0) {
+      break;
+    }
+    if (*bufPtr != '\r' && *bufPtr != '\n') {
+      *base64Ptr = *bufPtr;
+      base64Ptr++;
+    }
+    bufPtr++;
+  }
+
+  unsigned int binLength;
+  ScopedPORTString bin((char*)ATOB_AsciiToData(base64, &binLength));
+  if (!bin || binLength == 0) {
+    PrintPRError("ATOB_AsciiToData failed");
+    return SECFailure;
+  }
+  ScopedSECItem secitem(::SECITEM_AllocItem(nullptr, nullptr, binLength));
+  if (!secitem) {
+    PrintPRError("SECITEM_AllocItem failed");
+    return SECFailure;
+  }
+  PORT_Memcpy(secitem->data, bin, binLength);
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (!slot) {
+    PrintPRError("PK11_GetInternalKeySlot failed");
+    return SECFailure;
+  }
+  if (PK11_NeedUserInit(slot)) {
+    if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) {
+      PrintPRError("PK11_InitPin failed");
+      return SECFailure;
+    }
+  }
+  SECKEYPrivateKey* privateKey;
+  if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr,
+                                               true, false, KU_ALL,
+                                               &privateKey, nullptr)
+        != SECSuccess) {
+    PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
+    return SECFailure;
+  }
+  SECKEY_DestroyPrivateKey(privateKey);
+  return SECSuccess;
+}
+
+SECStatus
+DecodeCertCallback(void* arg, SECItem** certs, int numcerts)
+{
+  if (numcerts != 1) {
+    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
+    return SECFailure;
+  }
+
+  SECItem* certDEROut = static_cast(arg);
+  return SECITEM_CopyItem(nullptr, certDEROut, *certs);
+}
+
+SECStatus
+AddCertificateFromFile(const char* basePath, const char* filename)
+{
+  char buf[16384] = { 0 };
+  SECStatus rv = ReadFileToBuffer(basePath, filename, buf);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  SECItem certDER;
+  rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER);
+  if (rv != SECSuccess) {
+    PrintPRError("CERT_DecodeCertPackage failed");
+    return rv;
+  }
+  ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+                                                     &certDER, nullptr, false,
+                                                     true));
+  PORT_Free(certDER.data);
+  if (!cert) {
+    PrintPRError("CERT_NewTempCertificate failed");
+    return SECFailure;
+  }
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (!slot) {
+    PrintPRError("PK11_GetInternalKeySlot failed");
+    return SECFailure;
+  }
+  // The nickname is the filename without '.pem'.
+  std::string nickname(filename, strlen(filename) - 4);
+  rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, nickname.c_str(), false);
+  if (rv != SECSuccess) {
+    PrintPRError("PK11_ImportCert failed");
+    return rv;
+  }
+  return SECSuccess;
+}
+
+SECStatus
+LoadCertificatesAndKeys(const char* basePath)
+{
+  // The NSS cert DB path could have been specified as "sql:path". Trim off
+  // the leading "sql:" if so.
+  if (strncmp(basePath, "sql:", 4) == 0) {
+    basePath = basePath + 4;
+  }
+
+  ScopedPRDir fdDir(PR_OpenDir(basePath));
+  if (!fdDir) {
+    PrintPRError("PR_OpenDir failed");
+    return SECFailure;
+  }
+  // On the B2G ICS emulator, operations taken in AddCertificateFromFile
+  // appear to interact poorly with readdir (more specifically, something is
+  // causing readdir to never return null - it indefinitely loops through every
+  // file in the directory, which causes timeouts). Rather than waste more time
+  // chasing this down, loading certificates and keys happens in two phases:
+  // filename collection and then loading. (This is probably a good
+  // idea anyway because readdir isn't reentrant. Something could change later
+  // such that it gets called as a result of calling AddCertificateFromFile or
+  // AddKeyFromFile.)
+  std::vector certificates;
+  std::vector keys;
+  for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry;
+       dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) {
+    size_t nameLength = strlen(dirEntry->name);
+    if (nameLength > 4) {
+      if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
+        certificates.push_back(dirEntry->name);
+      } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
+        keys.push_back(dirEntry->name);
+      }
+    }
+  }
+  SECStatus rv;
+  for (std::string& certificate : certificates) {
+    rv = AddCertificateFromFile(basePath, certificate.c_str());
+    if (rv != SECSuccess) {
+      return rv;
+    }
+  }
+  for (std::string& key : keys) {
+    rv = AddKeyFromFile(basePath, key.c_str());
+    if (rv != SECSuccess) {
+      return rv;
+    }
+  }
+  return SECSuccess;
+}
+
+SECStatus
+InitializeNSS(const char* nssCertDBDir)
+{
+  // Try initializing an existing DB.
+  if (NSS_Init(nssCertDBDir) == SECSuccess) {
+    return SECSuccess;
+  }
+
+  // Create a new DB if there is none...
+  SECStatus rv = NSS_Initialize(nssCertDBDir, nullptr, nullptr, nullptr, 0);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+
+  // ...and load all certificates into it.
+  return LoadCertificatesAndKeys(nssCertDBDir);
+}
+
 nsresult
 SendAll(PRFileDesc *aSocket, const char *aData, size_t aDataLen)
 {
@@ -239,9 +459,10 @@ ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName,
     certList->len = 2;
   }
 
-  ScopedSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert, nullptr));
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  ScopedSECKEYPrivateKey key(PK11_FindKeyByDERCert(slot, cert, nullptr));
   if (!key) {
-    PrintPRError("PK11_FindKeyByAnyCert failed");
+    PrintPRError("PK11_FindKeyByDERCert failed");
     return SECFailure;
   }
 
@@ -286,8 +507,8 @@ StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig,
     gCallbackPort = atoi(callbackPort);
   }
 
-  if (NSS_Init(nssCertDBDir) != SECSuccess) {
-    PrintPRError("NSS_Init failed");
+  if (InitializeNSS(nssCertDBDir) != SECSuccess) {
+    PR_fprintf(PR_STDERR, "InitializeNSS failed");
     return 1;
   }
 
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
index b721d4cd884..8dab6fd9387 100644
--- a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
@@ -19,6 +19,13 @@
 #include "secerr.h"
 #include "ssl.h"
 
+namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir);
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, char, PORT_Free);
+
+} // namespace mozilla
+
 namespace mozilla { namespace test {
 
 enum DebugLevel
@@ -42,6 +49,9 @@ ConfigSecureServerWithNamedCert(PRFileDesc *fd, const char *certName,
                                 /*optional*/ ScopedCERTCertificate *cert,
                                 /*optional*/ SSLKEAType *kea);
 
+SECStatus
+InitializeNSS(const char* nssCertDBDir);
+
 int
 StartServer(const char *nssCertDBDir, SSLSNISocketConfig sniSocketConfig,
             void *sniSocketConfigArg);
diff --git a/security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.der b/security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.der
deleted file mode 100644
index a82b2910c5c..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
deleted file mode 100644
index 4da6f3bf6b2..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/pkcs11.txt b/security/manager/ssl/tests/unit/tlsserver/pkcs11.txt
deleted file mode 100644
index 02fd3a9ee6b..00000000000
--- a/security/manager/ssl/tests/unit/tlsserver/pkcs11.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-library=
-name=NSS Internal PKCS #11 Module
-parameters=configdir='sql:security/manager/ssl/tests/unit/tlsserver' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription='' 
-NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30})
-
diff --git a/security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.der b/security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.der
deleted file mode 100644
index 62d959834b0..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-ca.der b/security/manager/ssl/tests/unit/tlsserver/test-ca.der
deleted file mode 100644
index a46410db966..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-ca.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-int-ee.der b/security/manager/ssl/tests/unit/tlsserver/test-int-ee.der
deleted file mode 100644
index d1c019f78d3..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-int-ee.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-int.der b/security/manager/ssl/tests/unit/tlsserver/test-int.der
deleted file mode 100644
index afac61f5174..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-int.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der b/security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der
deleted file mode 100644
index 7457064ebd9..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/v1Cert.der b/security/manager/ssl/tests/unit/tlsserver/v1Cert.der
deleted file mode 100644
index 3a69e645a10..00000000000
Binary files a/security/manager/ssl/tests/unit/tlsserver/v1Cert.der and /dev/null differ
diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini
index aecf6f8ad70..5a3042b5949 100644
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -3,6 +3,7 @@ head = head_psm.js
 tail =
 tags = psm
 support-files =
+  ocsp_common/**
   test_cert_keyUsage/**
   test_signed_apps/**
   tlsserver/**
diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js
index 6e81d159790..70763f3fa07 100644
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -8,13 +8,13 @@
 // 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
 //                                  [path to]/genHPKPStaticpins.js \
 //                                  [absolute path to]/PreloadedHPKPins.json \
-//                                  [absolute path to]/default-ee.der \
+//                                  [an unused argument - see bug 1205406] \
 //                                  [absolute path to]/StaticHPKPins.h
 
 if (arguments.length != 3) {
   throw "Usage: genHPKPStaticPins.js " +
         " " +
-        " " +
+        " " +
         "";
 }
 
@@ -68,7 +68,8 @@ const PINSETDEF = "/* Pinsets are each an ordered list by the actual value of th
 
 // Command-line arguments
 var gStaticPins = parseJson(arguments[0]);
-var gTestCertFile = arguments[1];
+
+// arguments[1] is ignored for now. See bug 1205406.
 
 // Open the output file.
 var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
@@ -350,7 +351,7 @@ function downloadAndParseChromePins(filename,
 
 // Returns a pair of maps [certNameToSKD, certSKDToName] between cert
 // nicknames and digests of the SPKInfo for the mozilla trust store
-function loadNSSCertinfo(derTestFile, extraCertificates) {
+function loadNSSCertinfo(extraCertificates) {
   let allCerts = gCertDB.getCerts();
   let enumerator = allCerts.getEnumerator();
   let certNameToSKD = {};
@@ -374,13 +375,10 @@ function loadNSSCertinfo(derTestFile, extraCertificates) {
   }
 
   {
-    // A certificate for *.example.com.
-    let der = readFileToString(derTestFile);
-    let testCert = gCertDB.constructX509(der, der.length);
-    // We can't include this cert in the previous loop, because it skips
-    // non-builtin certs and the nickname is not built-in to the cert.
+    // This is the pinning test certificate. The key hash identifies the
+    // default RSA key from pykey.
     let name = "End Entity Test Cert";
-    let SKD  = testCert.sha256SubjectPublicKeyInfoDigest;
+    let SKD = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
     certNameToSKD[name] = SKD;
     certSKDToName[SKD] = name;
   }
@@ -599,8 +597,7 @@ function loadExtraCertificates(certStringList) {
 }
 
 var extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
-var [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(gTestCertFile,
-                                                       extraCertificates);
+var [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(extraCertificates);
 var [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts(
   gStaticPins.chromium_data.cert_file_url, certSKDToName);
 var [ chromeImportedPinsets, chromeImportedEntries ] =
diff --git a/testing/marionette/client/marionette/__init__.py b/testing/marionette/client/marionette/__init__.py
index a8d7f241502..1ea66c3147f 100644
--- a/testing/marionette/client/marionette/__init__.py
+++ b/testing/marionette/client/marionette/__init__.py
@@ -3,7 +3,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-__version__ = '0.19'
+__version__ = '0.20'
 
 from .marionette_test import MarionetteTestCase, MarionetteJSTestCase, CommonTestCase, expectedFailure, skip, SkipTest
 from .runner import (
diff --git a/testing/marionette/driver/marionette_driver/__init__.py b/testing/marionette/driver/marionette_driver/__init__.py
index bbafa06a961..57d77f49808 100644
--- a/testing/marionette/driver/marionette_driver/__init__.py
+++ b/testing/marionette/driver/marionette_driver/__init__.py
@@ -3,7 +3,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-__version__ = '0.13'
+__version__ = '0.2.0'
 
 
 from marionette_driver import ( errors, by, decorators, expected, geckoinstance,
diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
index c8bed4c9335..4b7e03c80ae 100644
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -372,11 +372,14 @@ this.BrowserTestUtils = {
    *  event has fired and completed. Instead of a window, a browser is required
    *  to be passed to this function.
    *
-   * @param {string} target
-   *        A selector that identifies the element to target. The syntax is as
-   *        for querySelector. This may also be a CPOW element for easier
-   *        test-conversion. If this is null, then the offset is from the
-   *        content document's edge.
+   * @param target
+   *        One of the following:
+   *        - a selector string that identifies the element to target. The syntax is as
+   *        for querySelector.
+   *        - a CPOW element (for easier test-conversion).
+   *        - a function to be run in the content process that returns the element to
+   *        target
+   *        - null, in which case the offset is from the content document's edge.
    * @param {integer} offsetX
    *        x offset from target's left bounding edge
    * @param {integer} offsetY
@@ -399,13 +402,17 @@ this.BrowserTestUtils = {
       });
 
       let cpowObject = null;
-      if (typeof target != "string") {
+      let targetFn = null;
+      if (typeof target == "function") {
+        targetFn = target.toString();
+        target = null;
+      } else if (typeof target != "string") {
         cpowObject = target;
         target = null;
       }
 
       mm.sendAsyncMessage("Test:SynthesizeMouse",
-                          {target, target, x: offsetX, y: offsetY, event: event},
+                          {target, targetFn, x: offsetX, y: offsetY, event: event},
                           {object: cpowObject});
     });
   },
diff --git a/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js b/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js
index ad2f0ac5fff..3c63f7042e2 100644
--- a/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js
+++ b/testing/mochitest/tests/SimpleTest/AsyncUtilsContent.js
@@ -19,6 +19,13 @@ addMessageListener("Test:SynthesizeMouse", (message) => {
   if (typeof target == "string") {
     target = content.document.querySelector(target);
   }
+  else if (typeof data.targetFn == "string") {
+    let runnablestr = `
+      (() => {
+        return (${data.targetFn});
+      })();`
+    target = eval(runnablestr)();
+  }
   else {
     target = message.objects.object;
   }
diff --git a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py
index 6ccf39f213f..61d137af1a4 100644
--- a/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py
+++ b/testing/mozharness/configs/builds/releng_sub_mac_configs/64_stat_and_debug.py
@@ -11,7 +11,6 @@ config = {
         'build',
         'upload-files',
         'sendchange',
-        'check-test',
         # 'generate-build-stats',
         'update',  # decided by query_is_nightly()
     ],
diff --git a/testing/taskcluster/tasks/branches/base_job_flags.yml b/testing/taskcluster/tasks/branches/base_job_flags.yml
index 55b5cf05c0d..6560eedb702 100644
--- a/testing/taskcluster/tasks/branches/base_job_flags.yml
+++ b/testing/taskcluster/tasks/branches/base_job_flags.yml
@@ -35,7 +35,7 @@ flags:
     - aries-eng
     - aries-dogfood
     - android-api-11
-    - linux32
+    - linux
     - linux64
     - linux64-st-an
     - macosx64
diff --git a/testing/taskcluster/tasks/branches/base_jobs.yml b/testing/taskcluster/tasks/branches/base_jobs.yml
index ba1dcdf270b..4ee1a3e98c0 100644
--- a/testing/taskcluster/tasks/branches/base_jobs.yml
+++ b/testing/taskcluster/tasks/branches/base_jobs.yml
@@ -122,6 +122,14 @@ builds:
     types:
       opt:
         task: tasks/builds/b2g_nexus_5l_eng.yml
+  linux:
+    platforms:
+      - Linux
+    types:
+      opt:
+        task: tasks/builds/opt_linux32.yml
+      debug:
+        task: tasks/builds/dbg_linux32.yml
   linux64:
     platforms:
       - Linux64
diff --git a/testing/taskcluster/tasks/branches/try/job_flags.yml b/testing/taskcluster/tasks/branches/try/job_flags.yml
index 034a64427d9..475efbddbb3 100644
--- a/testing/taskcluster/tasks/branches/try/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/try/job_flags.yml
@@ -129,14 +129,6 @@ builds:
     types:
       opt:
         task: tasks/builds/android_api_11.yml
-  linux64:
-    platforms:
-      - Linux64
-    types:
-      opt:
-        task: tasks/builds/opt_linux64_clobber.yml
-      debug:
-        task: tasks/builds/dbg_linux64_clobber.yml
   linux:
     platforms:
       - Linux
@@ -145,12 +137,6 @@ builds:
         task: tasks/builds/opt_linux32_clobber.yml
       debug:
         task: tasks/builds/dbg_linux32_clobber.yml
-  macosx64:
-    platforms:
-      - MacOSX64
-    types:
-      debug:
-        task: tasks/builds/dbg_macosx64.yml
   sm-plain:
     platforms:
       - Linux64
diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json
index d63a64f75e9..2a9882e6279 100644
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -709,10 +709,42 @@
         "path": "html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html",
         "url": "/html/semantics/forms/attributes-common-to-form-controls/dirname-rtl-manual.html"
       },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-email-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-password-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-search-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-text-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-input-url-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html"
+      },
+      {
+        "path": "html/semantics/forms/constraints/tooLong-textarea-delete-manual.html",
+        "url": "/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html"
+      },
       {
         "path": "html/semantics/forms/the-input-element/file-manual.html",
         "url": "/html/semantics/forms/the-input-element/file-manual.html"
       },
+      {
+        "path": "html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html",
+        "url": "/html/semantics/forms/the-optgroup-element/optgroup-disabled-manual.html"
+      },
       {
         "path": "html/semantics/forms/the-option-element/option-disabled-manual.html",
         "url": "/html/semantics/forms/the-option-element/option-disabled-manual.html"
@@ -733,6 +765,10 @@
         "path": "html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html",
         "url": "/html/semantics/grouping-content/the-li-element/grouping-li-novalue-manual.html"
       },
+      {
+        "path": "html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html",
+        "url": "/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html"
+      },
       {
         "path": "html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html",
         "url": "/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html"
@@ -10323,10 +10359,6 @@
         "path": "DOMEvents/tests/approved/stopPropagation.deferred.effect.html",
         "url": "/DOMEvents/tests/approved/stopPropagation.deferred.effect.html"
       },
-      {
-        "path": "DOMEvents/tests/approved/stopPropagation.dispatchEvent.html",
-        "url": "/DOMEvents/tests/approved/stopPropagation.dispatchEvent.html"
-      },
       {
         "path": "DOMEvents/tests/submissions/Microsoft/converted/DOM.event.flow.html",
         "url": "/DOMEvents/tests/submissions/Microsoft/converted/DOM.event.flow.html"
@@ -10367,10 +10399,6 @@
         "path": "DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.deferred.effect.html",
         "url": "/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.deferred.effect.html"
       },
-      {
-        "path": "DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html",
-        "url": "/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html"
-      },
       {
         "path": "DOMEvents/throwing-in-listener-and-window-error-event.html",
         "url": "/DOMEvents/throwing-in-listener-and-window-error-event.html"
@@ -12231,6 +12259,10 @@
         "path": "content-security-policy/blink-contrib-2/scripthash-basic-blocked.sub.html",
         "url": "/content-security-policy/blink-contrib-2/scripthash-basic-blocked.sub.html"
       },
+      {
+        "path": "content-security-policy/blink-contrib-2/scripthash-default-src.sub.html",
+        "url": "/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html"
+      },
       {
         "path": "content-security-policy/blink-contrib-2/scripthash-ignore-unsafeinline.sub.html",
         "url": "/content-security-policy/blink-contrib-2/scripthash-ignore-unsafeinline.sub.html"
@@ -12395,6 +12427,22 @@
         "path": "content-security-policy/blink-contrib/filesystem-urls-match-filesystem.sub.html",
         "url": "/content-security-policy/blink-contrib/filesystem-urls-match-filesystem.sub.html"
       },
+      {
+        "path": "content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html",
+        "url": "/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html"
+      },
+      {
+        "path": "content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html",
+        "url": "/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html"
+      },
+      {
+        "path": "content-security-policy/blink-contrib/frame-src-allowed.sub.html",
+        "url": "/content-security-policy/blink-contrib/frame-src-allowed.sub.html"
+      },
+      {
+        "path": "content-security-policy/blink-contrib/frame-src-blocked.sub.html",
+        "url": "/content-security-policy/blink-contrib/frame-src-blocked.sub.html"
+      },
       {
         "path": "content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html",
         "url": "/content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html"
@@ -12527,6 +12575,10 @@
         "path": "content-security-policy/blink-contrib/script-src-overrides-default-src.sub.html",
         "url": "/content-security-policy/blink-contrib/script-src-overrides-default-src.sub.html"
       },
+      {
+        "path": "content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html",
+        "url": "/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html"
+      },
       {
         "path": "content-security-policy/blink-contrib/shared-worker-connect-src-allowed.sub.html",
         "url": "/content-security-policy/blink-contrib/shared-worker-connect-src-allowed.sub.html"
@@ -12543,6 +12595,10 @@
         "path": "content-security-policy/blink-contrib/srcdoc-doesnt-bypass-script-src.sub.html",
         "url": "/content-security-policy/blink-contrib/srcdoc-doesnt-bypass-script-src.sub.html"
       },
+      {
+        "path": "content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html",
+        "url": "/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html"
+      },
       {
         "path": "content-security-policy/blink-contrib/style-allowed.sub.html",
         "url": "/content-security-policy/blink-contrib/style-allowed.sub.html"
@@ -12563,6 +12619,10 @@
         "path": "content-security-policy/blink-contrib/worker-eval-blocked.sub.html",
         "url": "/content-security-policy/blink-contrib/worker-eval-blocked.sub.html"
       },
+      {
+        "path": "content-security-policy/blink-contrib/worker-from-guid.sub.html",
+        "url": "/content-security-policy/blink-contrib/worker-from-guid.sub.html"
+      },
       {
         "path": "content-security-policy/blink-contrib/worker-function-function-blocked.sub.html",
         "url": "/content-security-policy/blink-contrib/worker-function-function-blocked.sub.html"
@@ -12579,6 +12639,38 @@
         "path": "content-security-policy/blink-contrib/worker-set-timeout-blocked.sub.html",
         "url": "/content-security-policy/blink-contrib/worker-set-timeout-blocked.sub.html"
       },
+      {
+        "path": "content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html",
+        "url": "/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html",
+        "url": "/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-allowed.sub.html",
+        "url": "/content-security-policy/child-src/child-src-allowed.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-blocked.sub.html",
+        "url": "/content-security-policy/child-src/child-src-blocked.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-conflicting-frame-src.sub.html",
+        "url": "/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-cross-origin-load.sub.html",
+        "url": "/content-security-policy/child-src/child-src-cross-origin-load.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-worker-allowed.sub.html",
+        "url": "/content-security-policy/child-src/child-src-worker-allowed.sub.html"
+      },
+      {
+        "path": "content-security-policy/child-src/child-src-worker-blocked.sub.html",
+        "url": "/content-security-policy/child-src/child-src-worker-blocked.sub.html"
+      },
       {
         "path": "content-security-policy/frame-ancestors/deep-allows-none.sub.html",
         "url": "/content-security-policy/frame-ancestors/deep-allows-none.sub.html"
@@ -12671,6 +12763,14 @@
         "path": "content-security-policy/media-src/media-src-7_3_2.html",
         "url": "/content-security-policy/media-src/media-src-7_3_2.html"
       },
+      {
+        "path": "content-security-policy/meta/meta-img-src.html",
+        "url": "/content-security-policy/meta/meta-img-src.html"
+      },
+      {
+        "path": "content-security-policy/meta/meta-modified.html",
+        "url": "/content-security-policy/meta/meta-modified.html"
+      },
       {
         "path": "content-security-policy/script-src/script-src-1_1.html",
         "url": "/content-security-policy/script-src/script-src-1_1.html"
@@ -12803,10 +12903,6 @@
         "path": "cors/status.htm",
         "url": "/cors/status.htm"
       },
-      {
-        "path": "csp/csp-inline-script.html",
-        "url": "/csp/csp-inline-script.html"
-      },
       {
         "path": "custom-elements/concepts/custom-elements-type-allowed-chars-first-char.html",
         "url": "/custom-elements/concepts/custom-elements-type-allowed-chars-first-char.html"
@@ -13071,6 +13167,10 @@
         "path": "dom/events/Event-dispatch-omitted-capture.html",
         "url": "/dom/events/Event-dispatch-omitted-capture.html"
       },
+      {
+        "path": "dom/events/Event-dispatch-propagation-stopped.html",
+        "url": "/dom/events/Event-dispatch-propagation-stopped.html"
+      },
       {
         "path": "dom/events/Event-dispatch-redispatch.html",
         "url": "/dom/events/Event-dispatch-redispatch.html"
@@ -14611,6 +14711,10 @@
         "path": "html/browsers/history/the-location-interface/location_protocol.html",
         "url": "/html/browsers/history/the-location-interface/location_protocol.html"
       },
+      {
+        "path": "html/browsers/history/the-location-interface/location_reload.html",
+        "url": "/html/browsers/history/the-location-interface/location_reload.html"
+      },
       {
         "path": "html/browsers/history/the-location-interface/location_replace.html",
         "url": "/html/browsers/history/the-location-interface/location_replace.html"
@@ -15551,6 +15655,10 @@
         "path": "html/infrastructure/urls/terminology-0/multiple-base.sub.html",
         "url": "/html/infrastructure/urls/terminology-0/multiple-base.sub.html"
       },
+      {
+        "path": "html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html",
+        "url": "/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html"
+      },
       {
         "path": "html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html",
         "url": "/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-color-01.html"
@@ -17851,6 +17959,10 @@
         "path": "html/webappapis/atob/base64.html",
         "url": "/html/webappapis/atob/base64.html"
       },
+      {
+        "path": "html/webappapis/scripting/event-loops/task_microtask_ordering.html",
+        "url": "/html/webappapis/scripting/event-loops/task_microtask_ordering.html"
+      },
       {
         "path": "html/webappapis/scripting/events/body-onload.html",
         "url": "/html/webappapis/scripting/events/body-onload.html"
@@ -18107,10 +18219,6 @@
         "path": "js/builtins/WeakMap.prototype-properties.html",
         "url": "/js/builtins/WeakMap.prototype-properties.html"
       },
-      {
-        "path": "media-source/SourceBuffer-abort-readyState.html",
-        "url": "/media-source/SourceBuffer-abort-readyState.html"
-      },
       {
         "path": "media-source/SourceBuffer-abort-removed.html",
         "url": "/media-source/SourceBuffer-abort-removed.html"
@@ -28817,6 +28925,11 @@
         "timeout": "long",
         "url": "/html/syntax/parsing/html5lib_webkit02.html?run_type=write_single"
       },
+      {
+        "path": "media-source/SourceBuffer-abort-readyState.html",
+        "timeout": "long",
+        "url": "/media-source/SourceBuffer-abort-readyState.html"
+      },
       {
         "path": "media-source/mediasource-redundant-seek.html",
         "timeout": "long",
@@ -29106,13 +29219,6 @@
     "deleted": [],
     "items": {
       "testharness": {
-        "media-source/SourceBuffer-abort-readyState.html": [
-          {
-            "path": "media-source/SourceBuffer-abort-readyState.html",
-            "timeout": "long",
-            "url": "/media-source/SourceBuffer-abort-readyState.html"
-          }
-        ],
         "webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html": [
           {
             "path": "webaudio/the-audio-api/the-audioparam-interface/retrospective-setValueAtTime.html",
@@ -31740,18 +31846,6 @@
         "url": "/html/semantics/text-level-semantics/the-br-element/br-bidi.html"
       }
     ],
-    "html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html": [
-      {
-        "path": "html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html",
-        "references": [
-          [
-            "/html/semantics/text-level-semantics/the-wbr-element/wbr-element-notref.html",
-            "!="
-          ]
-        ],
-        "url": "/html/semantics/text-level-semantics/the-wbr-element/wbr-element-ref.html"
-      }
-    ],
     "html/semantics/text-level-semantics/the-wbr-element/wbr-element.html": [
       {
         "path": "html/semantics/text-level-semantics/the-wbr-element/wbr-element.html",
@@ -34525,7 +34619,7 @@
       }
     ]
   },
-  "rev": "3f82dd41c8e3697bfe5fa5c57321c842b65ba2aa",
+  "rev": "2869a58fd81da41f9f904441ff39f9398fba53bc",
   "url_base": "/",
   "version": 2
-}
\ No newline at end of file
+}
diff --git a/testing/web-platform/meta/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.ini b/testing/web-platform/meta/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.ini
new file mode 100644
index 00000000000..ae7224bf069
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.ini
@@ -0,0 +1,5 @@
+[scripthash-default-src.sub.html]
+  type: testharness
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.ini b/testing/web-platform/meta/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.ini
index 1c44e77a8e4..c294d5c42c3 100644
--- a/testing/web-platform/meta/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.ini
+++ b/testing/web-platform/meta/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.ini
@@ -3,6 +3,3 @@
   [Expecting logs: ["TEST COMPLETE"\]]
     expected: FAIL
 
-  [Violation report status OK.]
-    expected: FAIL
-
diff --git a/testing/web-platform/meta/content-security-policy/blink-contrib/frame-src-blocked.sub.html.ini b/testing/web-platform/meta/content-security-policy/blink-contrib/frame-src-blocked.sub.html.ini
new file mode 100644
index 00000000000..4bfa1ea92d4
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/blink-contrib/frame-src-blocked.sub.html.ini
@@ -0,0 +1,5 @@
+[frame-src-blocked.sub.html]
+  type: testharness
+  [Expecting logs: ["PASS IFrame #1 generated a load event."\]]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.ini b/testing/web-platform/meta/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.ini
new file mode 100644
index 00000000000..3f1b16a6cdd
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.ini
@@ -0,0 +1,5 @@
+[self-doesnt-match-blob.sub.html]
+  type: testharness
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.ini b/testing/web-platform/meta/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.ini
new file mode 100644
index 00000000000..e2d2873a56e
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.ini
@@ -0,0 +1,5 @@
+[star-doesnt-match-blob.sub.html]
+  type: testharness
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/child-src/child-src-blocked.sub.html.ini b/testing/web-platform/meta/content-security-policy/child-src/child-src-blocked.sub.html.ini
new file mode 100644
index 00000000000..729a721a29e
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/child-src/child-src-blocked.sub.html.ini
@@ -0,0 +1,8 @@
+[child-src-blocked.sub.html]
+  type: testharness
+  [Expecting logs: ["PASS IFrame #1 generated a load event."\]]
+    expected: FAIL
+
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.ini b/testing/web-platform/meta/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.ini
new file mode 100644
index 00000000000..22b871d0361
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.ini
@@ -0,0 +1,5 @@
+[child-src-conflicting-frame-src.sub.html]
+  type: testharness
+  [Expecting logs: ["PASS IFrame #1 generated a load event."\]]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/child-src/child-src-cross-origin-load.sub.html.ini b/testing/web-platform/meta/content-security-policy/child-src/child-src-cross-origin-load.sub.html.ini
new file mode 100644
index 00000000000..5dfe2cd67c9
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/child-src/child-src-cross-origin-load.sub.html.ini
@@ -0,0 +1,13 @@
+[child-src-cross-origin-load.sub.html]
+  type: testharness
+  [Expecting alerts: ["PASS","PASS"\]]
+    expected:
+      if debug and not e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
+      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
+      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
+      if debug and not e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
+      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86_64") and (bits == 64): FAIL
+
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/child-src/child-src-worker-blocked.sub.html.ini b/testing/web-platform/meta/content-security-policy/child-src/child-src-worker-blocked.sub.html.ini
new file mode 100644
index 00000000000..7453700ed0e
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/child-src/child-src-worker-blocked.sub.html.ini
@@ -0,0 +1,8 @@
+[child-src-worker-blocked.sub.html]
+  type: testharness
+  [Expecting alerts: ["PASS"\]]
+    expected: FAIL
+
+  [Violation report status OK.]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_2.html.ini b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_2.html.ini
new file mode 100644
index 00000000000..2b2f6d32e06
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_2.html.ini
@@ -0,0 +1,10 @@
+[media-src-7_2.html]
+  type: testharness
+  [In-policy audio src]
+    expected:
+      if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+
+  [In-policy audio source element]
+    expected:
+      if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_3.html.ini b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_3.html.ini
index bc41ad182c4..cb4ae058347 100644
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_3.html.ini
+++ b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_3.html.ini
@@ -3,8 +3,10 @@
   expected:
     if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT
     if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): TIMEOUT
   [In-policy track element]
     expected:
       if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): NOTRUN
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): NOTRUN
+      if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): NOTRUN
 
diff --git a/testing/web-platform/meta/content-security-policy/meta/meta-img-src.html.ini b/testing/web-platform/meta/content-security-policy/meta/meta-img-src.html.ini
new file mode 100644
index 00000000000..1871323326c
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/meta/meta-img-src.html.ini
@@ -0,0 +1,5 @@
+[meta-img-src.html]
+  type: testharness
+  [Expecting logs: ["PASS","TEST COMPLETE"\]]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/content-security-policy/meta/meta-modified.html.ini b/testing/web-platform/meta/content-security-policy/meta/meta-modified.html.ini
new file mode 100644
index 00000000000..c31e932ca47
--- /dev/null
+++ b/testing/web-platform/meta/content-security-policy/meta/meta-modified.html.ini
@@ -0,0 +1,5 @@
+[meta-modified.html]
+  type: testharness
+  [Expecting logs: ["PASS", "PASS","TEST COMPLETE"\]]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/dom/interfaces.html.ini b/testing/web-platform/meta/dom/interfaces.html.ini
index 5b579816191..a0dd4e375db 100644
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -261,3 +261,15 @@
   [NodeFilter interface: existence and properties of interface object]
     expected: FAIL
 
+  [Document interface: xmlDoc must inherit property "prepend" with the proper type (33)]
+    expected: FAIL
+
+  [Document interface: xmlDoc must inherit property "append" with the proper type (34)]
+    expected: FAIL
+
+  [Document interface: xmlDoc must inherit property "query" with the proper type (35)]
+    expected: FAIL
+
+  [Document interface: xmlDoc must inherit property "queryAll" with the proper type (36)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/dom/nodes/Document-characterSet-normalization.html.ini b/testing/web-platform/meta/dom/nodes/Document-characterSet-normalization.html.ini
index 2dfcc56bcba..fe1b31b83f2 100644
--- a/testing/web-platform/meta/dom/nodes/Document-characterSet-normalization.html.ini
+++ b/testing/web-platform/meta/dom/nodes/Document-characterSet-normalization.html.ini
@@ -468,3 +468,237 @@
   [Name "replacement" has label "iso-2022-kr" (inputEncoding)]
     expected: FAIL
 
+  [Name "macintosh" has label "csmacintosh" (charset)]
+    expected: FAIL
+
+  [Name "macintosh" has label "mac" (charset)]
+    expected: FAIL
+
+  [Name "macintosh" has label "macintosh" (charset)]
+    expected: FAIL
+
+  [Name "macintosh" has label "x-mac-roman" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "dos-874" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "iso-8859-11" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "iso8859-11" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "iso885911" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "tis-620" (charset)]
+    expected: FAIL
+
+  [Name "windows-874" has label "windows-874" (charset)]
+    expected: FAIL
+
+  [Name "windows-1250" has label "cp1250" (charset)]
+    expected: FAIL
+
+  [Name "windows-1250" has label "windows-1250" (charset)]
+    expected: FAIL
+
+  [Name "windows-1250" has label "x-cp1250" (charset)]
+    expected: FAIL
+
+  [Name "windows-1251" has label "cp1251" (charset)]
+    expected: FAIL
+
+  [Name "windows-1251" has label "windows-1251" (charset)]
+    expected: FAIL
+
+  [Name "windows-1251" has label "x-cp1251" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "ansi_x3.4-1968" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "ascii" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "cp1252" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "cp819" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "csisolatin1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "ibm819" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso-8859-1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso-ir-100" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso8859-1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso88591" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso_8859-1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "iso_8859-1:1987" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "l1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "latin1" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "us-ascii" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "windows-1252" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "x-cp1252" (charset)]
+    expected: FAIL
+
+  [Name "windows-1252" has label "x-user-defined" (charset)]
+    expected: FAIL
+
+  [Name "windows-1253" has label "cp1253" (charset)]
+    expected: FAIL
+
+  [Name "windows-1253" has label "windows-1253" (charset)]
+    expected: FAIL
+
+  [Name "windows-1253" has label "x-cp1253" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "cp1254" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "csisolatin5" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso-8859-9" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso-ir-148" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso8859-9" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso88599" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso_8859-9" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "iso_8859-9:1989" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "l5" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "latin5" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "windows-1254" (charset)]
+    expected: FAIL
+
+  [Name "windows-1254" has label "x-cp1254" (charset)]
+    expected: FAIL
+
+  [Name "windows-1255" has label "cp1255" (charset)]
+    expected: FAIL
+
+  [Name "windows-1255" has label "windows-1255" (charset)]
+    expected: FAIL
+
+  [Name "windows-1255" has label "x-cp1255" (charset)]
+    expected: FAIL
+
+  [Name "windows-1256" has label "cp1256" (charset)]
+    expected: FAIL
+
+  [Name "windows-1256" has label "windows-1256" (charset)]
+    expected: FAIL
+
+  [Name "windows-1256" has label "x-cp1256" (charset)]
+    expected: FAIL
+
+  [Name "windows-1257" has label "cp1257" (charset)]
+    expected: FAIL
+
+  [Name "windows-1257" has label "windows-1257" (charset)]
+    expected: FAIL
+
+  [Name "windows-1257" has label "x-cp1257" (charset)]
+    expected: FAIL
+
+  [Name "windows-1258" has label "cp1258" (charset)]
+    expected: FAIL
+
+  [Name "windows-1258" has label "windows-1258" (charset)]
+    expected: FAIL
+
+  [Name "windows-1258" has label "x-cp1258" (charset)]
+    expected: FAIL
+
+  [Name "x-mac-cyrillic" has label "x-mac-cyrillic" (charset)]
+    expected: FAIL
+
+  [Name "x-mac-cyrillic" has label "x-mac-ukrainian" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "chinese" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "csgb2312" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "csiso58gb231280" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "gb2312" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "gb_2312" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "gb_2312-80" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "gbk" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "iso-ir-58" (charset)]
+    expected: FAIL
+
+  [Name "gbk" has label "x-gbk" (charset)]
+    expected: FAIL
+
+  [Name "gb18030" has label "gb18030" (charset)]
+    expected: FAIL
+
+  [Name "hz-gb-2312" has label "hz-gb-2312" (charset)]
+    expected: FAIL
+
+  [Name "replacement" has label "csiso2022kr" (charset)]
+    expected: FAIL
+
+  [Name "replacement" has label "iso-2022-cn" (charset)]
+    expected: FAIL
+
+  [Name "replacement" has label "iso-2022-cn-ext" (charset)]
+    expected: FAIL
+
+  [Name "replacement" has label "iso-2022-kr" (charset)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/editing/run/delete.html.ini b/testing/web-platform/meta/editing/run/delete.html.ini
index 10b73b1ec48..f373b01057b 100644
--- a/testing/web-platform/meta/editing/run/delete.html.ini
+++ b/testing/web-platform/meta/editing/run/delete.html.ini
@@ -2,7 +2,6 @@
   type: testharness
   disabled:
     if e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205642
-
   [[["stylewithcss","true"\],["delete",""\]\] "foobar[\]baz" compare innerHTML]
     expected: FAIL
 
diff --git a/testing/web-platform/meta/editing/run/forwarddelete.html.ini b/testing/web-platform/meta/editing/run/forwarddelete.html.ini
index e03aada3681..73b25c9c331 100644
--- a/testing/web-platform/meta/editing/run/forwarddelete.html.ini
+++ b/testing/web-platform/meta/editing/run/forwarddelete.html.ini
@@ -2,7 +2,6 @@
   type: testharness
   disabled:
     if e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205642
-
   [[["forwarddelete",""\]\] "foo[\]": execCommand("forwarddelete", false, "") return value]
     expected: FAIL
 
diff --git a/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html.ini b/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html.ini
new file mode 100644
index 00000000000..d79c9ae3733
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-02.html.ini
@@ -0,0 +1,5 @@
+[document.open-02.html]
+  type: testharness
+  expected:
+    if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT
+    if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): TIMEOUT
diff --git a/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html.ini b/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html.ini
new file mode 100644
index 00000000000..1295101ba19
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html.ini
@@ -0,0 +1,7 @@
+[document.open-03.html]
+  type: testharness
+  expected:
+    if not debug and e10s and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): TIMEOUT
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): TIMEOUT
+    if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): TIMEOUT
+    if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT
diff --git a/testing/web-platform/meta/html/dom/interfaces.html.ini b/testing/web-platform/meta/html/dom/interfaces.html.ini
index 7fb70b8b91a..aff06daf7ca 100644
--- a/testing/web-platform/meta/html/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.html.ini
@@ -2629,3 +2629,204 @@
   [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onstorage" with the proper type (13)]
     expected: FAIL
 
+  [Document interface: iframe.contentDocument must inherit property "styleSheetSets" with the proper type (32)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "cssElementMap" with the proper type (53)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "commands" with the proper type (70)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "all" with the proper type (82)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "prepend" with the proper type (88)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "append" with the proper type (89)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "query" with the proper type (90)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "queryAll" with the proper type (91)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "onautocomplete" with the proper type (95)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "onautocompleteerror" with the proper type (96)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "oncancel" with the proper type (98)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "onclose" with the proper type (103)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "oncuechange" with the proper type (105)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "ondragexit" with the proper type (110)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "onmousewheel" with the proper type (136)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "onsort" with the proper type (149)]
+    expected: FAIL
+
+  [Document interface: iframe.contentDocument must inherit property "ontoggle" with the proper type (154)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "styleSheetSets" with the proper type (32)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "domain" with the proper type (35)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "cookie" with the proper type (37)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "body" with the proper type (43)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "head" with the proper type (44)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "images" with the proper type (45)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "embeds" with the proper type (46)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "plugins" with the proper type (47)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "links" with the proper type (48)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "forms" with the proper type (49)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "scripts" with the proper type (50)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "getElementsByName" with the proper type (51)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "getItems" with the proper type (52)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "cssElementMap" with the proper type (53)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "open" with the proper type (56)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "close" with the proper type (57)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "write" with the proper type (58)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "writeln" with the proper type (59)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "designMode" with the proper type (63)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "execCommand" with the proper type (64)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryCommandEnabled" with the proper type (65)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryCommandIndeterm" with the proper type (66)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryCommandState" with the proper type (67)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryCommandSupported" with the proper type (68)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryCommandValue" with the proper type (69)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "commands" with the proper type (70)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "fgColor" with the proper type (72)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "linkColor" with the proper type (73)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "vlinkColor" with the proper type (74)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "alinkColor" with the proper type (75)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "bgColor" with the proper type (76)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "anchors" with the proper type (77)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "applets" with the proper type (78)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "clear" with the proper type (79)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "captureEvents" with the proper type (80)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "releaseEvents" with the proper type (81)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "all" with the proper type (82)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "prepend" with the proper type (88)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "append" with the proper type (89)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "query" with the proper type (90)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "queryAll" with the proper type (91)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onautocomplete" with the proper type (95)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onautocompleteerror" with the proper type (96)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "oncancel" with the proper type (98)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onclose" with the proper type (103)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "oncuechange" with the proper type (105)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ondragexit" with the proper type (110)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onmousewheel" with the proper type (136)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "onsort" with the proper type (149)]
+    expected: FAIL
+
+  [Document interface: document.implementation.createDocument(null, "", null) must inherit property "ontoggle" with the proper type (154)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini
index 9ac5f3ddcf1..3a2ee9f3627 100644
--- a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/relevant-mutations.html.ini
@@ -3,7 +3,7 @@
   expected: TIMEOUT
   disabled:
     if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1193349
-    if os == "linux" and debug and e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205645
+    if (os == "linux") and debug and e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205645
   [src removed]
     expected: TIMEOUT
 
diff --git a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-checkValidity.html.ini b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-checkValidity.html.ini
index a5a68455813..75cdea6a24d 100644
--- a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-checkValidity.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-checkValidity.html.ini
@@ -45,3 +45,6 @@
   [[INPUT in WEEK status\] The week type must be supported.]
     expected: FAIL
 
+  [[select\]  suffering from being missing]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-validity-tooLong.html.ini b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-validity-tooLong.html.ini
index e48de1c26f0..242a0f9c835 100644
--- a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-validity-tooLong.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-validity-tooLong.html.ini
@@ -21,3 +21,66 @@
   [[textarea\]  Dirty value - length of value is greater than maxlength]
     expected: FAIL
 
+  [[INPUT in TEXT status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in TEXT status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in TEXT status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[INPUT in SEARCH status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in SEARCH status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in SEARCH status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[INPUT in TEL status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in TEL status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in TEL status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[INPUT in URL status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in URL status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in URL status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[INPUT in EMAIL status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in EMAIL status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in EMAIL status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[INPUT in PASSWORD status\] Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in PASSWORD status\] Dirty value - length of value(AAA) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[INPUT in PASSWORD status\] Dirty value - value equals to maxlength]
+    expected: FAIL
+
+  [[textarea\]  Dirty value - value is less than maxlength]
+    expected: FAIL
+
+  [[textarea\]  Dirty value - length of value(LF, CRLF) in unicode is less than maxlength]
+    expected: FAIL
+
+  [[textarea\]  Dirty value - length of value equals to maxlength]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-willValidate.html.ini b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-willValidate.html.ini
index 90395ec3015..443b70c7ef4 100644
--- a/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-willValidate.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/constraints/form-validation-willValidate.html.ini
@@ -57,3 +57,12 @@
   [[textarea\]  The willValidate attribute must be true if an elment is mutable]
     expected: FAIL
 
+  [[BUTTON in SUBMIT status\] The willValidate attribute must be false if it has a datalist ancestor]
+    expected: FAIL
+
+  [[select\]  The willValidate attribute must be false if it has a datalist ancestor]
+    expected: FAIL
+
+  [[textarea\]  The willValidate attribute must be false if it has a datalist ancestor]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/semantics/forms/textfieldselection/select-event.html.ini b/testing/web-platform/meta/html/semantics/forms/textfieldselection/select-event.html.ini
index 6dcc1a47b16..7b057891b73 100644
--- a/testing/web-platform/meta/html/semantics/forms/textfieldselection/select-event.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/textfieldselection/select-event.html.ini
@@ -1,30 +1,22 @@
 [select-event.html]
   type: testharness
-
   disabled:
-    if e10s and os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1206011
-
+    if e10s and (os == "linux"): https://bugzilla.mozilla.org/show_bug.cgi?id=1206011
   [select() on textarea queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
   [select() on input type text queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
   [select() on input type search queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
   [select() on input type tel queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
   [select() on input type url queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
   [select() on input type password queues select event]
-    expected:
-      FAIL
+    expected: FAIL
 
diff --git a/testing/web-platform/meta/html/semantics/forms/the-input-element/input-textselection-01.html.ini b/testing/web-platform/meta/html/semantics/forms/the-input-element/input-textselection-01.html.ini
index 48b949e6c05..c5bffee7067 100644
--- a/testing/web-platform/meta/html/semantics/forms/the-input-element/input-textselection-01.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/the-input-element/input-textselection-01.html.ini
@@ -48,3 +48,6 @@
   [Selection attributes should not apply to type reset]
     expected: FAIL
 
+  [Selection attributes should not apply to type email]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/semantics/interfaces.html.ini b/testing/web-platform/meta/html/semantics/interfaces.html.ini
index f2f77f91bee..4423d1bfac3 100644
--- a/testing/web-platform/meta/html/semantics/interfaces.html.ini
+++ b/testing/web-platform/meta/html/semantics/interfaces.html.ini
@@ -24,9 +24,6 @@
   [Interfaces for command]
     expected: FAIL
 
-  [Interfaces for rb]
-    expected: FAIL
-
   [Interfaces for bdi]
     expected: FAIL
 
@@ -60,9 +57,6 @@
   [Interfaces for COMMAND]
     expected: FAIL
 
-  [Interfaces for RB]
-    expected: FAIL
-
   [Interfaces for BDI]
     expected: FAIL
 
diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html.ini
new file mode 100644
index 00000000000..189e2b23ee8
--- /dev/null
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html.ini
@@ -0,0 +1,14 @@
+[template-contents-owner-document-type.html]
+  type: testharness
+  [The template contents owner document type is HTML document (case when document has browsing context and the template is created by HTML parser)]
+    expected: FAIL
+
+  [The template contents owner document type is HTML document (case when document has browsing context and the template is created by createElement())]
+    expected: FAIL
+
+  [The template contents owner document type is HTML document (case when document has no browsing context and the template is created by createElement())]
+    expected: FAIL
+
+  [The template contents owner document type is HTML document (case when document has no browsing context and the template is created via innerHTML)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/html/webappapis/scripting/event-loops/task_microtask_ordering.html.ini b/testing/web-platform/meta/html/webappapis/scripting/event-loops/task_microtask_ordering.html.ini
new file mode 100644
index 00000000000..8d2a0914ab6
--- /dev/null
+++ b/testing/web-platform/meta/html/webappapis/scripting/event-loops/task_microtask_ordering.html.ini
@@ -0,0 +1,5 @@
+[task_microtask_ordering.html]
+  type: testharness
+  [Level 1 bossfight (synthetic click)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/mixed-content/blockable/meta-csp/cross-origin-http/object-tag/top-level/no-redirect/opt-in-blocks.https.html.ini b/testing/web-platform/meta/mixed-content/blockable/meta-csp/cross-origin-http/object-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
index 972cec9ea16..c410120a7d6 100644
--- a/testing/web-platform/meta/mixed-content/blockable/meta-csp/cross-origin-http/object-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/blockable/meta-csp/cross-origin-http/object-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
@@ -2,5 +2,5 @@
   type: testharness
   expected: TIMEOUT
   [opt_in_method: meta-csp\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: object-tag\n                                 expectation: blocked]
-    expected: FAIL
+    expected: NOTRUN
 
diff --git a/testing/web-platform/meta/mozilla-sync b/testing/web-platform/meta/mozilla-sync
index 5891c7ea555..ecefc9dc30a 100644
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1 +1 @@
-a25e8ccf7474a9d4a0ef14c10ab8dcabeb367527
\ No newline at end of file
+66b5a91a4fa541fb8b5cdfb22df0411beab7a240
\ No newline at end of file
diff --git a/testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini b/testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini
deleted file mode 100644
index 2087a607819..00000000000
--- a/testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[subresource-integrity.html]
-  type: testharness
-  [Style: Same-origin with incorrect hash.]
-    expected: FAIL
-
-  [Style: Same-origin with sha256 match, sha512 mismatch]
-    expected: FAIL
-
-  [Style:  with incorrect hash, ACAO: *]
-    expected: FAIL
-
-  [Style:  with incorrect hash CORS-eligible]
-    expected: FAIL
-
-  [Style: Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled]
-    expected: FAIL
diff --git a/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini b/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
index b60455db08a..8e2c152b4db 100644
--- a/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
+++ b/testing/web-platform/meta/webrtc/rtcpeerconnection/rtcpeerconnection-idl.html.ini
@@ -1,3 +1,159 @@
 [rtcpeerconnection-idl.html]
   type: testharness
   expected: ERROR
+  [RTCPeerConnection interface: existence and properties of interface object]
+    expected: FAIL
+
+  [RTCPeerConnection interface object length]
+    expected: FAIL
+
+  [RTCPeerConnection interface: existence and properties of interface prototype object]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation createOffer(RTCOfferOptions)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation createAnswer()]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescription)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescription)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation updateIce(RTCConfiguration)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidate)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute canTrickleIceCandidates]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute onicegatheringstatechange]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation createOffer(RTCSessionDescriptionCallback,RTCPeerConnectionErrorCallback,RTCOfferOptions)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation setLocalDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation createAnswer(RTCSessionDescriptionCallback,RTCPeerConnectionErrorCallback)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation setRemoteDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation addIceCandidate(RTCIceCandidate,VoidFunction,RTCPeerConnectionErrorCallback)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation addTrack(MediaStreamTrack,MediaStream)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute ontrack]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation createDTMFSender(MediaStreamTrack)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation getStats(MediaStreamTrack,RTCStatsCallback,RTCPeerConnectionErrorCallback)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: operation getIdentityAssertion()]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute onidentityresult]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute onpeeridentity]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute onidpassertionerror]
+    expected: FAIL
+
+  [RTCPeerConnection interface: attribute onidpvalidationerror]
+    expected: FAIL
+
+  [Stringification of pc]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling setLocalDescription(RTCSessionDescription) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescription) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "signalingState" with the proper type (6)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling updateIce(RTCConfiguration) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling addIceCandidate(RTCIceCandidate) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "iceGatheringState" with the proper type (9)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "iceConnectionState" with the proper type (10)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "canTrickleIceCandidates" with the proper type (11)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onnegotiationneeded" with the proper type (14)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onicecandidate" with the proper type (15)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onsignalingstatechange" with the proper type (16)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "oniceconnectionstatechange" with the proper type (17)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onicegatheringstatechange" with the proper type (18)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling setLocalDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling setRemoteDescription(RTCSessionDescription,VoidFunction,RTCPeerConnectionErrorCallback) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling addIceCandidate(RTCIceCandidate,VoidFunction,RTCPeerConnectionErrorCallback) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "ontrack" with the proper type (28)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "ondatachannel" with the proper type (30)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "createDTMFSender" with the proper type (31)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling createDTMFSender(MediaStreamTrack) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: calling getStats(MediaStreamTrack,RTCStatsCallback,RTCPeerConnectionErrorCallback) on pc with too few arguments must throw TypeError]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "peerIdentity" with the proper type (35)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onidentityresult" with the proper type (36)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onpeeridentity" with the proper type (37)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onidpassertionerror" with the proper type (38)]
+    expected: FAIL
+
+  [RTCPeerConnection interface: pc must inherit property "onidpvalidationerror" with the proper type (39)]
+    expected: FAIL
+
diff --git a/testing/web-platform/meta/websockets/constructor/014.html.ini b/testing/web-platform/meta/websockets/constructor/014.html.ini
index 3bdb554628a..c075a7ab49b 100644
--- a/testing/web-platform/meta/websockets/constructor/014.html.ini
+++ b/testing/web-platform/meta/websockets/constructor/014.html.ini
@@ -2,4 +2,3 @@
   type: testharness
   disabled:
     if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1090198
-
diff --git a/testing/web-platform/meta/websockets/interfaces/WebSocket/close/close-multiple.html.ini b/testing/web-platform/meta/websockets/interfaces/WebSocket/close/close-multiple.html.ini
index 1c2f737837a..6426257266c 100644
--- a/testing/web-platform/meta/websockets/interfaces/WebSocket/close/close-multiple.html.ini
+++ b/testing/web-platform/meta/websockets/interfaces/WebSocket/close/close-multiple.html.ini
@@ -2,4 +2,3 @@
   type: testharness
   disabled:
     if debug and e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205672
-
diff --git a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini b/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini
deleted file mode 100644
index ffc23eacc23..00000000000
--- a/testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[incoming-message.html]
-  type: testharness
-  disabled:
-    if debug: unstable
-  [close() and incoming message]
-    expected: FAIL
-
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/appcache-ordering-main.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/appcache-ordering-main.https.html.ini
deleted file mode 100644
index 21429a9dfdb..00000000000
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/appcache-ordering-main.https.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[appcache-ordering-main.https.html]
-  type: testharness
-  [serviceworkers take priority over appcaches]
-    expected:
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): FAIL
-
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
index c46e88d74bf..e2a328ef319 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/fetch-request-css-base-url.https.html.ini
@@ -2,4 +2,3 @@
   type: testharness
   disabled:
     if e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1205675
-
diff --git a/testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-then-register-new-script.https.html.ini b/testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-then-register-new-script.https.html.ini
index 01c6dd3d5a5..695918a73ca 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-then-register-new-script.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-then-register-new-script.https.html.ini
@@ -1,5 +1,5 @@
 [unregister-then-register-new-script.https.html]
-
+  type: testharness
   [Registering a new script URL while an unregistered registration is in use]
     expected: FAIL
 
diff --git a/testing/web-platform/tests/DOMEvents/tests/approved/stopPropagation.dispatchEvent.html b/testing/web-platform/tests/DOMEvents/tests/approved/stopPropagation.dispatchEvent.html
deleted file mode 100644
index 5c31a1d1330..00000000000
--- a/testing/web-platform/tests/DOMEvents/tests/approved/stopPropagation.dispatchEvent.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
- Calling stopPropagation() prior to dispatchEvent() 
-
-
-
-
-
- - - - - - - - - - - - - - - - - diff --git a/testing/web-platform/tests/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html b/testing/web-platform/tests/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html deleted file mode 100644 index 70ff9706290..00000000000 --- a/testing/web-platform/tests/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Calling stopPropagation() prior to dispatchEvent() - - - - -
- - - - - - - - - - - - - - - - - diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html new file mode 100644 index 00000000000..a11a224ae11 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html @@ -0,0 +1,15 @@ + + + + script-hash allowed from default-src + + + + + + + +
+ + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.sub.headers new file mode 100644 index 00000000000..d8893af4129 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib-2/scripthash-default-src.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: scripthash-default-src={{$id:uuid()}}; Path=/content-security-policy/blink-contrib-2 +Content-Security-Policy: default-src 'self' 'sha256-sc3CeiHrlck5tH2tTC4MnBYFnI9D5zp8f9odqnmGQjE='; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html new file mode 100644 index 00000000000..683558e2958 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html @@ -0,0 +1,21 @@ + + + + stylehash allowed from default-src + + + + + +

Test

+ + + +
+ + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html.sub.headers new file mode 100644 index 00000000000..8efe9d965ef --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib-2/stylehash-default-src.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: stylehash-default-src={{$id:uuid()}}; Path=/content-security-policy/blink-contrib-2 +Content-Security-Policy: default-src 'self' 'sha256-SXMrww9+PS7ymkxYbv91id+HfXeO7p1uCY0xhNb4MIw='; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html index 3f25b94834f..912a29e0b86 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html @@ -9,7 +9,7 @@ diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html.sub.headers index 7d37ce2b437..cbfc8d4e47a 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html.sub.headers +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/blob-urls-do-not-match-self.sub.html.sub.headers @@ -3,4 +3,4 @@ Cache-Control: no-store, no-cache, must-revalidate Cache-Control: post-check=0, pre-check=0, false Pragma: no-cache Set-Cookie: blob-urls-do-not-match-self={{$id:uuid()}}; Path=/content-security-policy/blink-contrib -Content-Security-Policy: script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} +Content-Security-Policy: script-src 'self' 'unsafe-inline'; connect-src 'self'; child-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html index b0400ffb39f..66b86f195d7 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html @@ -4,7 +4,7 @@ - combine-multiple-policies + combine-header-and-meta-policies diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.sub.headers index eec4b0be352..b1f0e7f012d 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.sub.headers +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/combine-header-and-meta-policies.sub.html.sub.headers @@ -2,5 +2,5 @@ Expires: Mon, 26 Jul 1997 05:00:00 GMT Cache-Control: no-store, no-cache, must-revalidate Cache-Control: post-check=0, pre-check=0, false Pragma: no-cache -Set-Cookie: combine-multiple-policies={{$id:uuid()}}; Path=/content-security-policy/blink-contrib -Content-Security-Policy: script-src 'self' 'unsafe-inline'; connect-src 'self'; style-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} \ No newline at end of file +Set-Cookie: combine-header-and-meta-policies={{$id:uuid()}}; Path=/content-security-policy/blink-contrib +Content-Security-Policy: script-src 'self' 'unsafe-inline'; connect-src 'self'; style-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html index f88b3e57d37..a363ce9116f 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-default.sub.html @@ -1,4 +1,24 @@ + + -These frames should not be blocked by Content-Security-Policy. It's pointle to block about:blank iframes because blocking a frame just results in displaying about:blank anyway! - - + + + frame-src-about-blank-allowed-by-default + + + + +

These frames should not be blocked by Content-Security-Policy. + It's pointless to block about:blank iframes because + blocking a frame just results in displaying about:blank anyway! +

+ + + +
+ + + + \ No newline at end of file diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html index 2aea6e6dd35..e4c47392c25 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-about-blank-allowed-by-scheme.sub.html @@ -1,3 +1,20 @@ + + -This iframe should not be blocked by Content-Security-Policy: - + + + frame-src-about-blank-allowed-by-scheme + + + + +

This frame should not be blocked by Content-Security-Policy. +

+ +
+ + + + \ No newline at end of file diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-allowed.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-allowed.sub.html index a43e4be27c2..1d34679c8d0 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-allowed.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-allowed.sub.html @@ -1 +1,63 @@ - + + + + + frame-src-allowed + + + + + +

+ This iframe should be allowed. +

+ + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-blocked.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-blocked.sub.html index 80427ad5cd7..fe7555aebb5 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-blocked.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-blocked.sub.html @@ -1 +1,61 @@ - + + + + + + frame-src-blocked + + + + + +

+ IFrames blocked by CSP should generate a 'load', not 'error' event, regardless of blocked state. This means they appear to be normal cross-origin loads, thereby not leaking URL information directly to JS. +

+ + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html index 1f684686065..5238e7c0f9d 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/frame-src-cross-origin-load.sub.html @@ -32,13 +32,13 @@ } } assert_unreached('unexpected alert: ' + msg); - t_log.done(); + t_alert.done(); }); }

IFrames blocked by CSP should generate a 'load', not 'error' event, regardless of blocked state. This means they appear to be normal cross-origin loads, thereby not leaking URL information directly to JS. @@ -50,8 +50,6 @@ frame-src 'self' http://localhost:8080; script-src 'self' 'unsafe-inline'; conne function loadEvent() { loads++; log("PASS " + "IFrame #" + loads + " generated a load event."); - if (loads == 3) - log("TEST COMPLETE"); } @@ -62,7 +60,7 @@ frame-src 'self' http://localhost:8080; script-src 'self' 'unsafe-inline'; conne

- + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html new file mode 100644 index 00000000000..487e8881bdd --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html @@ -0,0 +1,45 @@ + + + + + + worker-connect-src-blocked + + + + + + + +

This test loads a worker, from a guid. + The worker should be blocked from loading with a child-src policy of 'self' + as the blob: scheme must be specified explicitly. + A report should be sent to the report-uri specified + with this resource.

+ + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.sub.headers new file mode 100644 index 00000000000..05843484b88 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/self-doesnt-match-blob.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: self-doesnt-match-blob={{$id:uuid()}}; Path=/content-security-policy/blink-contrib +Content-Security-Policy: connect-src 'self'; script-src 'self' 'unsafe-inline'; child-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html new file mode 100644 index 00000000000..57fec43b6bb --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html @@ -0,0 +1,45 @@ + + + + + + worker-connect-src-blocked + + + + + + + +

This test loads a worker, from a guid. + The worker should be blocked from loading with a child-src policy of * + as the blob: scheme must be specified explicitly. + A report should be sent to the report-uri specified + with this resource.

+ + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.sub.headers new file mode 100644 index 00000000000..9f7db5b0f6f --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/star-doesnt-match-blob.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: star-doesnt-match-blob={{$id:uuid()}}; Path=/content-security-policy/blink-contrib +Content-Security-Policy: connect-src 'self'; script-src 'self' 'unsafe-inline'; child-src *; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html new file mode 100644 index 00000000000..b290b82f649 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html @@ -0,0 +1,65 @@ + + + + + + worker-connect-src-blocked + + + + + + + +

This test loads a worker, from a guid. + The worker should be blocked from making an XHR + to www1 as this resource's policy is connect-src 'self + and a guid Worker should inherit is parent's policy. + A report should be sent to the report-uri specified + with this resource.

+ + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html.sub.headers new file mode 100644 index 00000000000..d94d31ace8c --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-from-guid.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: worker-from-guid={{$id:uuid()}}; Path=/content-security-policy/blink-contrib +Content-Security-Policy: connect-src 'self'; script-src 'self' 'unsafe-inline' blob:; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/blink-contrib/worker-script-src.sub.html b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-script-src.sub.html index 9c524e715e7..9caf77224f9 100644 --- a/testing/web-platform/tests/content-security-policy/blink-contrib/worker-script-src.sub.html +++ b/testing/web-platform/tests/content-security-policy/blink-contrib/worker-script-src.sub.html @@ -9,7 +9,7 @@ diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html new file mode 100644 index 00000000000..9222a8ddc95 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html @@ -0,0 +1,24 @@ + + + + + + child-src-about-blank-allowed-by-default + + + + +

These frames should not be blocked by Content-Security-Policy. + It's pointless to block about:blank iframes because + blocking a frame just results in displaying about:blank anyway! +

+ + + +
+ + + + \ No newline at end of file diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html.sub.headers new file mode 100644 index 00000000000..68b2fb2fb89 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-default.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-about-blank-allowed-by-default={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'none'; object-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html new file mode 100644 index 00000000000..d94eff6843c --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html @@ -0,0 +1,20 @@ + + + + + + child-src-about-blank-allowed-by-scheme + + + + +

This frame should not be blocked by Content-Security-Policy. +

+ +
+ + + + \ No newline at end of file diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html.sub.headers new file mode 100644 index 00000000000..9ff84d67dd2 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-about-blank-allowed-by-scheme.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-about-blank-allowed-by-scheme={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src about:; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html new file mode 100644 index 00000000000..12a075adb65 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html @@ -0,0 +1,63 @@ + + + + + child-src-allowed + + + + + +

+ This iframe should be allowed. +

+ + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html.sub.headers new file mode 100644 index 00000000000..7eb8d76f960 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-allowed.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-allowed={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html new file mode 100644 index 00000000000..e32cc0af05e --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html @@ -0,0 +1,61 @@ + + + + + + child-src-blocked + + + + + +

+ IFrames blocked by CSP should generate a 'load', not 'error' event, regardless of blocked state. This means they appear to be normal cross-origin loads, thereby not leaking URL information directly to JS. +

+ + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html.sub.headers new file mode 100644 index 00000000000..961d18a7dd6 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-blocked.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-blocked={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html new file mode 100644 index 00000000000..b681253ae01 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html @@ -0,0 +1,61 @@ + + + + child-src-blocked + + + + + +

+ A more permissive child-src should not relax restrictions from a less- + permissive frame-src. Directives still combine for least privilege, even when + one obsoletes another. +

+ + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.sub.headers new file mode 100644 index 00000000000..9c3ce84263c --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-conflicting-frame-src.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-conflicting-frame-src={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: frame-src 'none'; child-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html new file mode 100644 index 00000000000..b6f3e5164e2 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html @@ -0,0 +1,68 @@ + + + + + + child-src-cross-origin-load + + + + + +

+ IFrames blocked by CSP should generate a 'load', not 'error' event, regardless of blocked state. This means they appear to be normal cross-origin loads, thereby not leaking URL information directly to JS. +

+ + + + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html.sub.headers new file mode 100644 index 00000000000..53527c1adaf --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-cross-origin-load.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-cross-origin-load={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'self' http://www1.{{host}}:{{ports[http][0]}}; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html new file mode 100644 index 00000000000..361d0974287 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html @@ -0,0 +1,32 @@ + + + + + child-src-worker-allowed + + + + + + + + + + +
+ + + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html.sub.headers new file mode 100644 index 00000000000..4ddb39e84b2 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-allowed.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-worker-allowed={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html new file mode 100644 index 00000000000..d8908b17b3d --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html @@ -0,0 +1,31 @@ + + + + + child-src-worker-blocked + + + + + + + + + + +
+ + + diff --git a/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html.sub.headers b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html.sub.headers new file mode 100644 index 00000000000..685d6dcf5ea --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/child-src/child-src-worker-blocked.sub.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: child-src-worker-blocked={{$id:uuid()}}; Path=/content-security-policy/child-src +Content-Security-Policy: child-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/content-security-policy/meta/meta-img-src.html b/testing/web-platform/tests/content-security-policy/meta/meta-img-src.html new file mode 100644 index 00000000000..bc7ffd66a70 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/meta/meta-img-src.html @@ -0,0 +1,33 @@ + + + + + + meta-img-src + + + + + + +

Test passes if the image is blocked.

+ + +
+ + + diff --git a/testing/web-platform/tests/content-security-policy/meta/meta-modified.html b/testing/web-platform/tests/content-security-policy/meta/meta-modified.html new file mode 100644 index 00000000000..d03115f31b0 --- /dev/null +++ b/testing/web-platform/tests/content-security-policy/meta/meta-modified.html @@ -0,0 +1,35 @@ + + + + + + meta-modified + + + + + + +

Test passes if the image is blocked both before and after policy modification.

+ + +
+ + + diff --git a/testing/web-platform/tests/content-security-policy/support/alertAssert.sub.js b/testing/web-platform/tests/content-security-policy/support/alertAssert.sub.js index 1c1b430e017..6ca2849ca31 100644 --- a/testing/web-platform/tests/content-security-policy/support/alertAssert.sub.js +++ b/testing/web-platform/tests/content-security-policy/support/alertAssert.sub.js @@ -9,7 +9,10 @@ if(expected_alerts.length == 0) { var t_alert = async_test('Expecting alerts: {{GET[alerts]}}'); function alert_assert(msg) { t_alert.step(function () { - if (msg.match(/^FAIL/i)) { + if(msg && msg instanceof Error) { + msg = msg.message; + } + if (msg && msg.match(/^FAIL/i)) { assert_unreached(msg); t_alert.done(); } diff --git a/testing/web-platform/tests/csp/assert_undefined.py b/testing/web-platform/tests/csp/assert_undefined.py deleted file mode 100644 index 1f45ee8e60d..00000000000 --- a/testing/web-platform/tests/csp/assert_undefined.py +++ /dev/null @@ -1,8 +0,0 @@ -def main(request, response): - code = """ -test(function() { - assert_equals(self.%s, undefined); -}); -""" % request.GET["varName"] - - return ([("Content-Type", "text/javascript")], code) diff --git a/testing/web-platform/tests/csp/csp-inline-script.html b/testing/web-platform/tests/csp/csp-inline-script.html deleted file mode 100644 index 4caa25a4b8a..00000000000 --- a/testing/web-platform/tests/csp/csp-inline-script.html +++ /dev/null @@ -1,11 +0,0 @@ - - - -No inline script - - -
- - diff --git a/testing/web-platform/tests/csp/csp-inline-script.html.headers b/testing/web-platform/tests/csp/csp-inline-script.html.headers deleted file mode 100644 index 4c6fa3c26a7..00000000000 --- a/testing/web-platform/tests/csp/csp-inline-script.html.headers +++ /dev/null @@ -1 +0,0 @@ -Content-Security-Policy: default-src 'self' diff --git a/testing/web-platform/tests/docs/test-format-guidelines.md b/testing/web-platform/tests/docs/test-format-guidelines.md index cae7cb0e2d4..70511804e0e 100644 --- a/testing/web-platform/tests/docs/test-format-guidelines.md +++ b/testing/web-platform/tests/docs/test-format-guidelines.md @@ -42,7 +42,7 @@ running in automation. In general the following order of preference holds: * WebDriver tests - for testing the webdriver protocol itself or (in the future) for certain tests that require access to privileged APIs. -* Manual tests - as a last resort for anything that can't be tested +* [Manual tests][manual-tests] - as a last resort for anything that can't be tested using one of the above techniques. Some scenarios demand certain test types. For example: @@ -328,6 +328,7 @@ see the [lint-tool documentation][lint-tool]. [lint-tool]: ./lint-tool.html [reftests]: ./reftests.html +[manual-tests]: ./manual-test.html [test-templates]: ./test-templates.html [requirement-flags]: ./test-templates.html#requirement-flags [testharness-documentation]: ./testharness-documentation.html diff --git a/testing/web-platform/tests/dom/events/Event-dispatch-propagation-stopped.html b/testing/web-platform/tests/dom/events/Event-dispatch-propagation-stopped.html new file mode 100644 index 00000000000..889f8cfe114 --- /dev/null +++ b/testing/web-platform/tests/dom/events/Event-dispatch-propagation-stopped.html @@ -0,0 +1,59 @@ + + + + Calling stopPropagation() prior to dispatchEvent() + + + + +
+ + + + + + + + + + + + + + + + + diff --git a/testing/web-platform/tests/dom/interfaces.html b/testing/web-platform/tests/dom/interfaces.html index 69ae1561c9c..cb368b357af 100644 --- a/testing/web-platform/tests/dom/interfaces.html +++ b/testing/web-platform/tests/dom/interfaces.html @@ -223,6 +223,7 @@ interface Document : Node { readonly attribute DOMString origin; readonly attribute DOMString compatMode; readonly attribute DOMString characterSet; + readonly attribute DOMString charset; // legacy alias of .characterSet readonly attribute DOMString inputEncoding; // legacy alias of .characterSet readonly attribute DOMString contentType; diff --git a/testing/web-platform/tests/dom/nodes/DOMImplementation-createDocument.html b/testing/web-platform/tests/dom/nodes/DOMImplementation-createDocument.html index 42ed42eb850..a929436561c 100644 --- a/testing/web-platform/tests/dom/nodes/DOMImplementation-createDocument.html +++ b/testing/web-platform/tests/dom/nodes/DOMImplementation-createDocument.html @@ -103,13 +103,20 @@ test(function() { var doc = document.implementation.createDocument(namespace, qualifiedName, doctype) assert_equals(doc.compatMode, "CSS1Compat") assert_equals(doc.characterSet, "UTF-8") - assert_equals(doc.inputEncoding, "UTF-8") assert_equals(doc.contentType, "application/xml") assert_equals(doc.URL, "about:blank") assert_equals(doc.documentURI, "about:blank") assert_equals(doc.createElement("DIV").localName, "DIV"); }, "createDocument test " + i + ": metadata for " + [namespace, qualifiedName, doctype].map(function(el) { return format_value(el) })) + + test(function() { + var doc = document.implementation.createDocument(namespace, qualifiedName, doctype) + assert_equals(doc.characterSet, "UTF-8", "characterSet"); + assert_equals(doc.charset, "UTF-8", "charset"); + assert_equals(doc.inputEncoding, "UTF-8", "inputEncoding"); + }, "createDocument test " + i + ": characterSet aliases for " + + [namespace, qualifiedName, doctype].map(function(el) { return format_value(el) })) } }) }) diff --git a/testing/web-platform/tests/dom/nodes/DOMImplementation-createHTMLDocument.html b/testing/web-platform/tests/dom/nodes/DOMImplementation-createHTMLDocument.html index 89dc3834739..57f475c9c5f 100644 --- a/testing/web-platform/tests/dom/nodes/DOMImplementation-createHTMLDocument.html +++ b/testing/web-platform/tests/dom/nodes/DOMImplementation-createHTMLDocument.html @@ -69,11 +69,17 @@ test(function() { assert_equals(doc.documentURI, "about:blank"); assert_equals(doc.compatMode, "CSS1Compat"); assert_equals(doc.characterSet, "UTF-8"); - assert_equals(doc.inputEncoding, "UTF-8"); assert_equals(doc.contentType, "text/html"); assert_equals(doc.createElement("DIV").localName, "div"); }, "createHTMLDocument(): metadata") +test(function() { + var doc = document.implementation.createHTMLDocument("test"); + assert_equals(doc.characterSet, "UTF-8", "characterSet"); + assert_equals(doc.charset, "UTF-8", "charset"); + assert_equals(doc.inputEncoding, "UTF-8", "inputEncoding"); +}, "createHTMLDocument(): characterSet aliases") + test(function() { var doc = document.implementation.createHTMLDocument("test"); var a = doc.createElement("a"); diff --git a/testing/web-platform/tests/dom/nodes/Document-characterSet-normalization.html b/testing/web-platform/tests/dom/nodes/Document-characterSet-normalization.html index b78ad45053c..e7a4ab5f838 100644 --- a/testing/web-platform/tests/dom/nodes/Document-characterSet-normalization.html +++ b/testing/web-platform/tests/dom/nodes/Document-characterSet-normalization.html @@ -358,6 +358,8 @@ Object.keys(encodingMap).forEach(function(name) { " has label " + format_value(label) + " (characterSet)"); var t2 = async_test("Name " + format_value(name) + " has label " + format_value(label) + " (inputEncoding)"); + var t3 = async_test("Name " + format_value(name) + + " has label " + format_value(label) + " (charset)"); /* iframe.src = "data:text/html," + ''; @@ -370,9 +372,13 @@ Object.keys(encodingMap).forEach(function(name) { t2.step(function() { assert_equals(iframe.contentDocument.inputEncoding, expected_case(name)); }); + t3.step(function() { + assert_equals(iframe.contentDocument.charset, expected_case(name)); + }); document.body.removeChild(iframe); t.done(); t2.done(); + t3.done(); }; document.body.appendChild(iframe); }); diff --git a/testing/web-platform/tests/dom/nodes/Document-constructor.html b/testing/web-platform/tests/dom/nodes/Document-constructor.html index fb728828cff..11549da4ab4 100644 --- a/testing/web-platform/tests/dom/nodes/Document-constructor.html +++ b/testing/web-platform/tests/dom/nodes/Document-constructor.html @@ -32,11 +32,17 @@ test(function() { assert_equals(doc.documentURI, "about:blank"); assert_equals(doc.compatMode, "CSS1Compat"); assert_equals(doc.characterSet, "UTF-8"); - assert_equals(doc.inputEncoding, "UTF-8"); assert_equals(doc.contentType, "application/xml"); assert_equals(doc.createElement("DIV").localName, "DIV"); }, "new Document(): metadata") +test(function() { + var doc = new Document(); + assert_equals(doc.characterSet, "UTF-8", "characterSet"); + assert_equals(doc.charset, "UTF-8", "charset"); + assert_equals(doc.inputEncoding, "UTF-8", "inputEncoding"); +}, "new Document(): characterSet aliases") + test(function() { var doc = new Document(); var a = doc.createElement("a"); diff --git a/testing/web-platform/tests/dom/nodes/Node-properties.html b/testing/web-platform/tests/dom/nodes/Node-properties.html index 798ba14d544..83723ae1ed3 100644 --- a/testing/web-platform/tests/dom/nodes/Node-properties.html +++ b/testing/web-platform/tests/dom/nodes/Node-properties.html @@ -136,7 +136,6 @@ var expected = { URL: String(location), compatMode: "CSS1Compat", characterSet: "UTF-8", - inputEncoding: "UTF-8", contentType: "text/html", doctype: doctype, //documentElement: , @@ -153,7 +152,6 @@ var expected = { URL: "about:blank", compatMode: "CSS1Compat", characterSet: "UTF-8", - inputEncoding: "UTF-8", contentType: "text/html", //doctype: , //documentElement: , @@ -211,7 +209,6 @@ var expected = { URL: "about:blank", compatMode: "CSS1Compat", characterSet: "UTF-8", - inputEncoding: "UTF-8", contentType: "application/xml", //doctype: , //documentElement: , @@ -640,6 +637,8 @@ for (var node in expected) { expected[node].nextSibling = expected[node].nodeValue = expected[node].textContent = null; expected[node].documentURI = expected[node].URL; + expected[node].charset = expected[node].inputEncoding = + expected[node].characterSet; break; case Node.DOCUMENT_TYPE_NODE: diff --git a/testing/web-platform/tests/domparsing/DOMParser-parseFromString-html.html b/testing/web-platform/tests/domparsing/DOMParser-parseFromString-html.html index e0bcc6bc701..fcaa44a391b 100644 --- a/testing/web-platform/tests/domparsing/DOMParser-parseFromString-html.html +++ b/testing/web-platform/tests/domparsing/DOMParser-parseFromString-html.html @@ -38,6 +38,10 @@ test(function() { assert_equals(doc.inputEncoding, "UTF-8") }, 'inputEncoding'); +test(function() { + assert_equals(doc.charset, "UTF-8") +}, 'charset'); + test(function() { var url = document.URL; assert_equals(doc.documentURI, url, diff --git a/testing/web-platform/tests/domparsing/DOMParser-parseFromString-xml.html b/testing/web-platform/tests/domparsing/DOMParser-parseFromString-xml.html index d6de3081c51..0be58706788 100644 --- a/testing/web-platform/tests/domparsing/DOMParser-parseFromString-xml.html +++ b/testing/web-platform/tests/domparsing/DOMParser-parseFromString-xml.html @@ -10,6 +10,7 @@ function checkMetadata(doc, contentType) { assert_equals(doc.URL, document.URL, "URL"); assert_equals(doc.documentURI, document.URL, "documentURI"); assert_equals(doc.characterSet, "UTF-8", "characterSet"); + assert_equals(doc.charset, "UTF-8", "charset"); assert_equals(doc.inputEncoding, "UTF-8", "inputEncoding"); assert_equals(doc.contentType, contentType, "contentType"); assert_equals(doc.location, null, "location"); diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html new file mode 100644 index 00000000000..95b63d9f5d4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload-iframe.html @@ -0,0 +1,4 @@ + diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html new file mode 100644 index 00000000000..513b60afd00 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location_reload.html @@ -0,0 +1,37 @@ + + + + location_reload + + + + +
+ + + + + + + diff --git a/testing/web-platform/tests/html/dom/interfaces.html b/testing/web-platform/tests/html/dom/interfaces.html index 29d059bdc1e..cc5945f2461 100644 --- a/testing/web-platform/tests/html/dom/interfaces.html +++ b/testing/web-platform/tests/html/dom/interfaces.html @@ -235,6 +235,7 @@ interface Document : Node { readonly attribute DOMString origin; readonly attribute DOMString compatMode; readonly attribute DOMString characterSet; + readonly attribute DOMString charset; // legacy alias of .characterSet readonly attribute DOMString inputEncoding; // legacy alias of .characterSet readonly attribute DOMString contentType; @@ -3222,6 +3223,7 @@ window.onload = function() { 'document.createElement("acronym")', ], HTMLUnknownElement: [ + 'document.createElement("blink")', 'document.createElement("quasit")', 'document.createElement("bgsound")', 'document.createElement("isindex")', diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html new file mode 100644 index 00000000000..37a92bb09f4 --- /dev/null +++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html @@ -0,0 +1,52 @@ + +document.all + + + + +
+ diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html index 79d0cbe17c0..aa787d471d2 100644 --- a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html +++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-tooLong.html @@ -23,7 +23,8 @@ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - value is less than maxlength", dirty: true}, {conditions: {maxLength: "4", value: "\u0041\u0041\u0041"}, expected: false, name: "[target] Dirty value - length of value(AAA) in unicode is less than maxlength", dirty: true}, {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - value equals to maxlength", dirty: true}, - {conditions: {maxLength: "4", value: "abcde"}, expected: true, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true} + // False due to lack of required interactive editing by the user + {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true} ] }, { @@ -39,7 +40,8 @@ {conditions: {maxLength: "4", value: "abc"}, expected: false, name: "[target] Dirty value - value is less than maxlength", dirty: true}, {conditions: {maxLength: "4", value: "\u000D\u000A"}, expected: false, name: "[target] Dirty value - length of value(LF, CRLF) in unicode is less than maxlength", dirty: true}, {conditions: {maxLength: "4", value: "abcd"}, expected: false, name: "[target] Dirty value - length of value equals to maxlength", dirty: true}, - {conditions: {maxLength: "4", value: "abcde"}, expected: true, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true} + // False due to lack of required interactive editing by the user + {conditions: {maxLength: "4", value: "abcde"}, expected: false, name: "[target] Dirty value - length of value is greater than maxlength", dirty: true} ] } ]; diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html index 4554c95a3b4..8b2c01d9b39 100644 --- a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html +++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-validity-valueMissing.html @@ -114,7 +114,7 @@ {conditions: {required: true, value: " 123 "}, expected: true, name: "[target] Value is a number with some white spaces"}, {conditions: {required: true, value: Math.pow(2, 1024)}, expected: true, name: "[target] Value is Math.pow(2, 1024)"}, {conditions: {required: true, value: Math.pow(-2, 1024)}, expected: true, name: "[target] Value is Math.pow(-2, 1024)"}, - {conditions: {required: true, value: "abc"}, expected: true, name: "[target] Value is a string that can not be coverted to a number"}, + {conditions: {required: true, value: "abc"}, expected: true, name: "[target] Value is a string that cannot be converted to a number"}, {conditions: {required: true, value: ""}, expected: true, name: "[target] The value attribute is empty string"} ] }, diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html index 671d269ef58..04300c67706 100644 --- a/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html +++ b/testing/web-platform/tests/html/semantics/forms/constraints/form-validation-willValidate.html @@ -52,7 +52,7 @@ types: ["text", "search", "tel", "url", "email", "password", "datetime", "date", "month", "week", "time", "color", "file", "submit"], testData: [ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is disabled"}, - {conditions: {disabled: false, readOnly: false}, expected: true, name: "[target] The willValidate attribute must be true if an elment is mutable"}, + {conditions: {disabled: false, readOnly: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"}, {conditions: {readOnly: true}, expected: false, name: "[target] Must be barred from the constraint validation if it is readonly"}, {conditions: {disabled: false, readOnly: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"}, ] @@ -62,7 +62,7 @@ types: ["submit"], testData: [ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"}, - {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an elment is mutable"}, + {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"}, {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"} ] }, @@ -71,7 +71,7 @@ types: [], testData: [ {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"}, - {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an elment is mutable"}, + {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"}, {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"} ] }, @@ -80,7 +80,7 @@ types: [], testData: [, {conditions: {disabled: true}, expected: false, name: "[target] Must be barred from the constraint validation"}, - {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an elment is mutable"}, + {conditions: {disabled: false}, expected: true, name: "[target] The willValidate attribute must be true if an element is mutable"}, {conditions: {disabled: false}, expected: false, name: "[target] The willValidate attribute must be false if it has a datalist ancestor", ancestor: "datalist"} ] } diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js b/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js index 12a88c1fc6e..dfd0d08f17c 100644 --- a/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js +++ b/testing/web-platform/tests/html/semantics/forms/constraints/support/validator.js @@ -229,8 +229,8 @@ var validator = { assert_true(ctl.reportValidity(), "The reportValidity method should be true."); assert_false(eventFired, "The invalid event should not be fired."); } else { - assert_true(eventFired, "The invalid event should be fired."); assert_false(ctl.reportValidity(), "The reportValidity method should be false."); + assert_true(eventFired, "The invalid event should be fired."); } }, data.name); @@ -262,8 +262,20 @@ var validator = { }, set_conditions: function (ctl, obj) { - ["required", "pattern", "step", "max", "min", "maxlength", - "value", "multiple", "checked", "selected"].forEach(function(item) { + [ + "checked", + "disabled", + "max", + "maxlength", + "min", + "minlength", + "multiple", + "pattern", + "required", + "selected", + "step", + "value" + ].forEach(function(item) { ctl.removeAttribute(item); }); for (var attr in obj) { @@ -273,14 +285,16 @@ var validator = { }, set_dirty: function(ctl) { - document.disgnMode = "on"; + document.designMode = "on"; ctl.focus(); var old_value = ctl.value; ctl.value = "a"; ctl.value = old_value; - ctl.setSelectionRange(ctl.value.length, ctl.value.length); + if (ctl.type !== 'email') { + ctl.setSelectionRange(ctl.value.length, ctl.value.length); + } document.execCommand("Delete"); - document.disgnMode = "off"; + document.designMode = "off"; }, pre_check: function(ctl, item) { diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html new file mode 100644 index 00000000000..008089f39ae --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-email-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="email"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html new file mode 100644 index 00000000000..353d9466ddf --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-password-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="password"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html new file mode 100644 index 00000000000..73be3b6d839 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-search-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="search"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html new file mode 100644 index 00000000000..bf7682af3ef --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-tel-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="tel"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html new file mode 100644 index 00000000000..2eea2b7245a --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-text-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="text"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html new file mode 100644 index 00000000000..17039a71a8b --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-input-url-delete-manual.html @@ -0,0 +1,30 @@ + + + + + input[type="url"], ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text input:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html new file mode 100644 index 00000000000..2212a1ca90b --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/constraints/tooLong-textarea-delete-manual.html @@ -0,0 +1,30 @@ + + + + + textarea, ValidityState.tooLong and user editing + + + + + + + + +

Delete one character from the following text area:

+ + +
+ + + diff --git a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-textselection-01.html b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-textselection-01.html index df7c52567e8..f4a71612e4a 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-input-element/input-textselection-01.html +++ b/testing/web-platform/tests/html/semantics/forms/the-input-element/input-textselection-01.html @@ -7,10 +7,10 @@
+ + + + +

Click on the inner box:

+
+
+
+ + diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html new file mode 100644 index 00000000000..a5bb59a99b4 --- /dev/null +++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html @@ -0,0 +1,85 @@ + +Task and Microtask Ordering + + + + + + +
+
+
+ + diff --git a/testing/web-platform/tests/subresource-integrity/subresource-integrity.html b/testing/web-platform/tests/subresource-integrity/subresource-integrity.html index fa82868476a..9519fc127fe 100644 --- a/testing/web-platform/tests/subresource-integrity/subresource-integrity.html +++ b/testing/web-platform/tests/subresource-integrity/subresource-integrity.html @@ -286,7 +286,7 @@ true, "Same-origin with correct sha256 hash", { - href: "style.css", + href: "style.css?1", integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=" } ); @@ -296,7 +296,7 @@ true, "Same-origin with correct sha384 hash", { - href: "style.css", + href: "style.css?2", integrity: "sha384-wDAWxH4tOWBwAwHfBn9B7XuNmFxHTMeigAMwn0iVQ0zq3FtmYMLxihcGnU64CwcX" } ); @@ -306,7 +306,7 @@ true, "Same-origin with correct sha512 hash", { - href: "style.css", + href: "style.css?3", integrity: "sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w==" } ); @@ -316,7 +316,7 @@ true, "Same-origin with empty integrity", { - href: "style.css", + href: "style.css?4", integrity: "" } ); @@ -326,7 +326,7 @@ false, "Same-origin with incorrect hash.", { - href: "style.css", + href: "style.css?5", integrity: "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" } ); @@ -336,8 +336,8 @@ true, "Same-origin with multiple sha256 hashes, including correct.", { - href: "style.css", - integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F-SPLT7YZk7gyCWUV4= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" + href: "style.css?6", + integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" } ); @@ -346,8 +346,8 @@ true, "Same-origin with multiple sha256 hashes, including unknown algorithm.", { - href: "style.css", - integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F-SPLT7YZk7gyCWUV4= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" + href: "style.css?7", + integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" } ); @@ -356,7 +356,7 @@ true, "Same-origin with sha256 mismatch, sha512 match", { - href: "style.css", + href: "style.css?8", integrity: "sha512-9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2HSZX4l6w== sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead" } ); @@ -366,7 +366,7 @@ false, "Same-origin with sha256 match, sha512 mismatch", { - href: "style.css", + href: "style.css?9", integrity: "sha512-deadbeef9wXDjd6Wq3H6nPAhI9zOvG7mJkUr03MTxaO+8ztTKnfJif42laL93Be/IF6YYZHHF4esitVYxiwpY2== sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=" } ); @@ -376,7 +376,7 @@ true, " with correct hash, ACAO: *", { - href: xorigin_anon_style, + href: xorigin_anon_style + '?1', integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=", crossorigin: "anonymous" } @@ -387,7 +387,7 @@ false, " with incorrect hash, ACAO: *", { - href: xorigin_anon_style, + href: xorigin_anon_style + '?2', integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk=", crossorigin: "anonymous" } @@ -398,7 +398,7 @@ true, " with correct hash, CORS-eligible", { - href: xorigin_creds_style, + href: xorigin_creds_style + '?1', integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=", crossorigin: "use-credentials" } @@ -409,7 +409,7 @@ false, " with incorrect hash CORS-eligible", { - href: xorigin_creds_style, + href: xorigin_creds_style + '?2', integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk=", crossorigin: "use-credentials" } @@ -420,7 +420,7 @@ false, " with CORS-ineligible resource", { - href: xorigin_ineligible_style, + href: xorigin_ineligible_style + '?1', integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=", crossorigin: "anonymous" } @@ -431,7 +431,7 @@ false, "Cross-origin, not CORS request, with correct hash", { - href: xorigin_anon_style, + href: xorigin_anon_style + '?3', integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=" } ); @@ -441,7 +441,7 @@ false, "Cross-origin, not CORS request, with hash mismatch", { - href: xorigin_anon_style, + href: xorigin_anon_style + '?4', integrity: "sha256-deadbeefCzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk=" } ); @@ -451,7 +451,7 @@ true, "Cross-origin, empty integrity", { - href: xorigin_anon_style, + href: xorigin_anon_style + '?5', integrity: "" } ); @@ -461,7 +461,7 @@ true, "Same-origin with correct hash, options.", { - href: "style.css", + href: "style.css?10", integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=?foo=bar?spam=eggs" } ); @@ -471,7 +471,7 @@ true, "Same-origin with unknown algorithm only.", { - href: "style.css", + href: "style.css?11", integrity: "foo666-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=?foo=bar?spam=eggs" } ); @@ -481,8 +481,8 @@ true, "Same-origin with correct sha256 hash, rel='stylesheet license'", { - href: "style.css", - integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F-SPLT7YZk7gyCWUV4=", + href: "style.css?12", + integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=", rel: "stylesheet license" } ); @@ -492,8 +492,8 @@ true, "Same-origin with correct sha256 hash, rel='license stylesheet'", { - href: "style.css", - integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F-SPLT7YZk7gyCWUV4=", + href: "style.css?13", + integrity: "sha256-CzHgdJ7wOccM8L89n4bhcJMz3F+SPLT7YZk7gyCWUV4=", rel: "license stylesheet" } ); @@ -503,7 +503,7 @@ true, "Same-origin with correct sha256 and sha512 hash, rel='alternate stylesheet' enabled", { - href: "alternate.css", + href: "alternate.css?1", title: "alt", type: "text/css", class: "alternate", @@ -523,7 +523,7 @@ false, "Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled", { - href: "alternate.css", + href: "alternate.css?2", title: "alt", type: "text/css", class: "alternate", diff --git a/testing/web-platform/tests/touch-events/multi-touch-interactions.js b/testing/web-platform/tests/touch-events/multi-touch-interactions.js index ac1b8ef55a8..5248e6a9430 100644 --- a/testing/web-platform/tests/touch-events/multi-touch-interactions.js +++ b/testing/web-platform/tests/touch-events/multi-touch-interactions.js @@ -290,7 +290,7 @@ function run() { // In some cases, when multiple touch points are released simultaneously // the UA would dispatch the "same" touchend event (same changedTouches, same touches, but possibly different targetTouches) - // to each of the elements that are starting elments of the released touch points. + // to each of the elements that are starting elements of the released touch points. // in these situations, the subsequent events are exempt from TA 1.5.3.4 and 1.5.4.2 var same_event_as_last = false; if (last_changedTouches && last_changedTouches.length==ev.changedTouches.length) { diff --git a/testing/web-platform/tests/websockets/README.md b/testing/web-platform/tests/websockets/README.md index 4ad032e089f..f03192230a5 100644 --- a/testing/web-platform/tests/websockets/README.md +++ b/testing/web-platform/tests/websockets/README.md @@ -2,7 +2,7 @@ This directory contains the WebSocket API test suite. The following document contains a list of each test file in the test suite and the results of running the test file on several browsers . -To run this test suite within a browser, go to: . +To run this test suite within a browser, go to: . The latest Editor's Draft of WebSocket API is: . diff --git a/testing/web-platform/tests/webstorage/storage_builtins.html b/testing/web-platform/tests/webstorage/storage_builtins.html index 39730209047..2ae06d55b00 100644 --- a/testing/web-platform/tests/webstorage/storage_builtins.html +++ b/testing/web-platform/tests/webstorage/storage_builtins.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - builtins diff --git a/testing/web-platform/tests/webstorage/storage_clear.html b/testing/web-platform/tests/webstorage/storage_clear.html index d135fdbb31b..1d81f539174 100644 --- a/testing/web-platform/tests/webstorage/storage_clear.html +++ b/testing/web-platform/tests/webstorage/storage_clear.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - clear() diff --git a/testing/web-platform/tests/webstorage/storage_getitem.html b/testing/web-platform/tests/webstorage/storage_getitem.html index 4b07383ceb1..b735b2eaf76 100644 --- a/testing/web-platform/tests/webstorage/storage_getitem.html +++ b/testing/web-platform/tests/webstorage/storage_getitem.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - getItem(key) and named getter diff --git a/testing/web-platform/tests/webstorage/storage_in.html b/testing/web-platform/tests/webstorage/storage_in.html index f607757ead0..8b336047c8a 100644 --- a/testing/web-platform/tests/webstorage/storage_in.html +++ b/testing/web-platform/tests/webstorage/storage_in.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - in operator diff --git a/testing/web-platform/tests/webstorage/storage_indexing.html b/testing/web-platform/tests/webstorage/storage_indexing.html index f4282ab830b..7bce1051437 100644 --- a/testing/web-platform/tests/webstorage/storage_indexing.html +++ b/testing/web-platform/tests/webstorage/storage_indexing.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - indexed getter diff --git a/testing/web-platform/tests/webstorage/storage_key.html b/testing/web-platform/tests/webstorage/storage_key.html index 6cd76c84276..c70b536fb07 100644 --- a/testing/web-platform/tests/webstorage/storage_key.html +++ b/testing/web-platform/tests/webstorage/storage_key.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - key(index) diff --git a/testing/web-platform/tests/webstorage/storage_length.html b/testing/web-platform/tests/webstorage/storage_length.html index fced3c11f78..ecee6ec9b21 100644 --- a/testing/web-platform/tests/webstorage/storage_length.html +++ b/testing/web-platform/tests/webstorage/storage_length.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - length diff --git a/testing/web-platform/tests/webstorage/storage_removeitem.html b/testing/web-platform/tests/webstorage/storage_removeitem.html index c2624e56524..58d66ec3751 100644 --- a/testing/web-platform/tests/webstorage/storage_removeitem.html +++ b/testing/web-platform/tests/webstorage/storage_removeitem.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - removeItem(key) diff --git a/testing/web-platform/tests/webstorage/storage_setitem.html b/testing/web-platform/tests/webstorage/storage_setitem.html index 9f8fd39f66f..fb9c78428ef 100644 --- a/testing/web-platform/tests/webstorage/storage_setitem.html +++ b/testing/web-platform/tests/webstorage/storage_setitem.html @@ -1,5 +1,5 @@ - + WebStorage Test: Storage - setItem(key, value) diff --git a/toolkit/components/startup/tests/browser/browser_bug511456.js b/toolkit/components/startup/tests/browser/browser_bug511456.js index 4eb97a8732c..652a34ea237 100644 --- a/toolkit/components/startup/tests/browser/browser_bug511456.js +++ b/toolkit/components/startup/tests/browser/browser_bug511456.js @@ -6,6 +6,8 @@ const TEST_URL = "http://example.com/browser/toolkit/components/startup/tests/browser/beforeunload.html"; +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + function test() { waitForExplicitFinish(); ignoreAllUncaughtExceptions(); diff --git a/toolkit/components/startup/tests/browser/browser_bug537449.js b/toolkit/components/startup/tests/browser/browser_bug537449.js index 7ddcf17f536..ed70bdbcfa1 100644 --- a/toolkit/components/startup/tests/browser/browser_bug537449.js +++ b/toolkit/components/startup/tests/browser/browser_bug537449.js @@ -11,6 +11,8 @@ // thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null"); +SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]}); + const TEST_URL = "http://example.com/browser/toolkit/components/startup/tests/browser/beforeunload.html"; function test() { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 935845d1ce7..169b45fa0a8 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -7562,6 +7562,14 @@ "n_values": 10, "description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS" }, + "COOKIE_SCHEME_SECURITY": { + "alert_emails": ["seceng@mozilla.org"], + "expires_in_version": "50", + "kind": "enumerated", + "n_values": 10, + "releaseChannelCollection": "opt-out", + "description": "How often are secure cookies set from non-secure origins, and vice-versa? 0=nonsecure/http, 1=nonsecure/https, 2=secure/http, 3=secure/https" + }, "NTLM_MODULE_USED_2": { "expires_in_version": "never", "kind": "enumerated", @@ -9758,5 +9766,18 @@ "expires_in_version": "55", "kind": "count", "description": "Attempt to notify ServiceWorker of push notification resubscription." + }, + "PLUGIN_ACTIVATION_COUNT": { + "alert_emails": ["cpeterson@mozilla.com"], + "expires_in_version": "48", + "kind": "count", + "keyed": true, + "description": "Counts number of times a certain plugin has been activated." + }, + "YOUTUBE_EMBED_SEEN": { + "alert_emails": ["cpeterson@mozilla.com"], + "expires_in_version": "48", + "kind": "flag", + "description": "Flag activated whenever a youtube flash embed is seen during a session." } } diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index ff6b75bf051..0938e5033c7 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -583,11 +583,11 @@ var FindBar = { FAYT_TEXT_KEY: "/".charCodeAt(0), _findMode: 0, - get _findAsYouType() { - return Services.prefs.getBoolPref("accessibility.typeaheadfind"); - }, + _findAsYouType: false, init() { + this._findAsYouType = + Services.prefs.getBoolPref("accessibility.typeaheadfind"); addMessageListener("Findbar:UpdateState", this); Services.els.addSystemEventListener(global, "keypress", this, false); Services.els.addSystemEventListener(global, "mouseup", this, false); diff --git a/toolkit/content/buildconfig.html b/toolkit/content/buildconfig.html index c375bd49b50..c2480d17886 100644 --- a/toolkit/content/buildconfig.html +++ b/toolkit/content/buildconfig.html @@ -54,7 +54,9 @@ @CXX@ @CXX_VERSION@ +#ifndef BUILD_FASTER @CXXFLAGS@ @CPPFLAGS@ +#endif