merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 08 Dec 2016 16:16:48 +0100
changeset 325404 d8f63b2935af0915a6a24b3ea8e27d9a09f66416
parent 325373 64e024fe44719575aa56c432e059ca065071ac95 (current diff)
parent 325403 387b89eeed54d07238a2e0e878e44ca7cdbd26cb (diff)
child 325406 a5c688e01e3bc636ae93c1e64944e3c829073180
child 325436 c65a9a96f48a5c3d7ac132df804cb829619a41b2
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
merge mozilla-inbound to mozilla-central a=merge
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -491,17 +491,19 @@ mozilla::ipc::IPCResult
 DocAccessibleParent::RecvGetWindowedPluginIAccessible(
       const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy)
 {
 #if defined(MOZ_CONTENT_SANDBOX)
   // We don't actually want the accessible object for aHwnd, but rather the
   // one that belongs to its child (see HTMLWin32ObjectAccessible).
   HWND childWnd = ::GetWindow(reinterpret_cast<HWND>(aHwnd), GW_CHILD);
   if (!childWnd) {
-    return IPC_FAIL(this, "GetWindow failed");
+    // We're seeing this in the wild - the plugin is windowed but we no longer
+    // have a window.
+    return IPC_OK();
   }
 
   IAccessible* rawAccPlugin = nullptr;
   HRESULT hr = ::AccessibleObjectFromWindow(childWnd, OBJID_WINDOW,
                                             IID_IAccessible,
                                             (void**)&rawAccPlugin);
   if (FAILED(hr)) {
     // This might happen if the plugin doesn't handle WM_GETOBJECT properly.
--- a/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp
+++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp
@@ -80,20 +80,17 @@ HTMLWin32ObjectAccessible::HTMLWin32Obje
       return;
     }
 #endif
 
     // The plugin is not windowless. In this situation we use 
     // use its inner child owned by the plugin so that we don't get
     // in an infinite loop, where the WM_GETOBJECT's get forwarded
     // back to us and create another HTMLWin32ObjectAccessible
-    HWND childWnd = ::GetWindow((HWND)aHwnd, GW_CHILD);
-    if (childWnd) {
-      mHwnd = childWnd;
-    }
+    mHwnd = ::GetWindow((HWND)aHwnd, GW_CHILD);
   }
 }
 
 void
 HTMLWin32ObjectAccessible::GetNativeInterface(void** aNativeAccessible)
 {
 #if defined(MOZ_CONTENT_SANDBOX)
   if (XRE_IsContentProcess()) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4805,32 +4805,41 @@
                 let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
                 if (fl && fl.tabParent) {
                   fl.tabParent.suppressDisplayport(false);
                   this.mActiveResizeDisplayportSuppression = null;
                 }
               }
               break;
             }
+            case "contextual-identity-updated": {
+              for (let tab of this.tabs) {
+                if (tab.getAttribute("usercontextid") == aData) {
+                  ContextualIdentityService.setTabStyle(tab);
+                }
+              }
+              break;
+            }
             case "nsPref:changed": {
               // This is the only pref observed.
               this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
               break;
             }
           }
         ]]></body>
       </method>
 
       <constructor>
         <![CDATA[
           this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
           this.mCurrentBrowser.permanentKey = {};
 
           Services.obs.addObserver(this, "live-resize-start", false);
           Services.obs.addObserver(this, "live-resize-end", false);
+          Services.obs.addObserver(this, "contextual-identity-updated", false);
 
           this.mCurrentTab = this.tabContainer.firstChild;
           const nsIEventListenerService =
             Components.interfaces.nsIEventListenerService;
           let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
                               .getService(nsIEventListenerService);
           els.addSystemEventListener(document, "keydown", this, false);
           if (this.AppConstants.platform == "macosx") {
@@ -4920,16 +4929,17 @@
           return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
         ]]></body>
       </method>
 
       <destructor>
         <![CDATA[
           Services.obs.removeObserver(this, "live-resize-start", false);
           Services.obs.removeObserver(this, "live-resize-end", false);
+          Services.obs.removeObserver(this, "contextual-identity-updated", false);
 
           for (let tab of this.tabs) {
             let browser = tab.linkedBrowser;
             if (browser.registeredOpenURI) {
               this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI,
                                                        browser.getAttribute("usercontextid") || 0);
               delete browser.registeredOpenURI;
             }
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -346,16 +346,29 @@ OriginAttributes::IsFirstPartyEnabled()
   if (!sCachedFirstPartyPref) {
     sCachedFirstPartyPref = true;
     Preferences::AddBoolVarCache(&sFirstPartyIsolation, "privacy.firstparty.isolate");
   }
 
   return sFirstPartyIsolation;
 }
 
+/* static */
+bool
+OriginAttributes::IsPrivateBrowsing(const nsACString& aOrigin)
+{
+  nsAutoCString dummy;
+  PrincipalOriginAttributes attrs;
+  if (NS_WARN_IF(!attrs.PopulateFromOrigin(aOrigin, dummy))) {
+    return false;
+  }
+
+  return !!attrs.mPrivateBrowsingId;
+}
+
 BasePrincipal::BasePrincipal()
 {}
 
 BasePrincipal::~BasePrincipal()
 {}
 
 NS_IMETHODIMP
 BasePrincipal::GetOrigin(nsACString& aOrigin)
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -63,16 +63,20 @@ public:
   // flags. Once all other flags are removed, this can be removed too.
   void SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing);
 
   void SetFromGenericAttributes(const GenericOriginAttributes& aAttrs);
 
   // check if "privacy.firstparty.isolate" is enabled.
   static bool IsFirstPartyEnabled();
 
+  // returns true if the originAttributes suffix has mPrivateBrowsingId value
+  // different than 0.
+  static bool IsPrivateBrowsing(const nsACString& aOrigin);
+
 protected:
   OriginAttributes() {}
   explicit OriginAttributes(const OriginAttributesDictionary& aOther)
     : OriginAttributesDictionary(aOther) {}
 };
 
 class PrincipalOriginAttributes;
 class DocShellOriginAttributes;
--- a/dom/animation/AnimationUtils.cpp
+++ b/dom/animation/AnimationUtils.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AnimationUtils.h"
 
-#include "nsContentUtils.h" // For nsContentUtils::IsCallerChrome
 #include "nsDebug.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsGlobalWindow.h"
 #include "nsString.h"
 #include "xpcpublic.h" // For xpc::NativeGlobal
 #include "mozilla/Preferences.h"
@@ -74,14 +73,14 @@ AnimationUtils::IsCoreAPIEnabled()
     Preferences::AddBoolVarCache(&sCoreAPIEnabled,
                                  "dom.animations-api.core.enabled");
   }
 
   return sCoreAPIEnabled;
 }
 
 /* static */ bool
-AnimationUtils::IsCoreAPIEnabledForCaller()
+AnimationUtils::IsCoreAPIEnabledForCaller(dom::CallerType aCallerType)
 {
-  return IsCoreAPIEnabled() || nsContentUtils::IsCallerChrome();
+  return IsCoreAPIEnabled() || aCallerType == dom::CallerType::System;
 }
 
 } // namespace mozilla
--- a/dom/animation/AnimationUtils.h
+++ b/dom/animation/AnimationUtils.h
@@ -3,16 +3,17 @@
 /* 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 mozilla_dom_AnimationUtils_h
 #define mozilla_dom_AnimationUtils_h
 
 #include "mozilla/TimeStamp.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsStringFwd.h"
 
 class nsIContent;
 class nsIDocument;
 struct JSContext;
 
 namespace mozilla {
@@ -66,14 +67,14 @@ public:
    * true.
    */
   static bool IsCoreAPIEnabled();
 
   /**
    * Returns true if the preference to enable the core Web Animations API is
    * true or the caller is chrome.
    */
-  static bool IsCoreAPIEnabledForCaller();
+  static bool IsCoreAPIEnabledForCaller(dom::CallerType aCallerType);
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -128,21 +128,22 @@ KeyframeEffect::SetTarget(const Nullable
   } else if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
     // New target is null, so fall back to distribute spacing.
     KeyframeUtils::ApplyDistributeSpacing(mKeyframes);
   }
 }
 
 void
 KeyframeEffect::SetIterationComposite(
-  const IterationCompositeOperation& aIterationComposite)
+  const IterationCompositeOperation& aIterationComposite,
+  CallerType aCallerType)
 {
   // Ignore iterationComposite if the Web Animations API is not enabled,
   // then the default value 'Replace' will be used.
-  if (!AnimationUtils::IsCoreAPIEnabledForCaller()) {
+  if (!AnimationUtils::IsCoreAPIEnabledForCaller(aCallerType)) {
     return;
   }
 
   if (mEffectOptions.mIterationComposite == aIterationComposite) {
     return;
   }
 
   if (mAnimation && mAnimation->IsRelevant()) {
@@ -151,25 +152,27 @@ KeyframeEffect::SetIterationComposite(
 
   mEffectOptions.mIterationComposite = aIterationComposite;
   RequestRestyle(EffectCompositor::RestyleType::Layer);
 }
 
 void
 KeyframeEffect::SetSpacing(JSContext* aCx,
                            const nsAString& aSpacing,
+                           CallerType aCallerType,
                            ErrorResult& aRv)
 {
   SpacingMode spacingMode = SpacingMode::distribute;
   nsCSSPropertyID pacedProperty = eCSSProperty_UNKNOWN;
   nsAutoString invalidPacedProperty;
   KeyframeEffectParams::ParseSpacing(aSpacing,
                                      spacingMode,
                                      pacedProperty,
                                      invalidPacedProperty,
+                                     aCallerType,
                                      aRv);
   if (aRv.Failed()) {
     return;
   }
 
   if (!invalidPacedProperty.IsEmpty()) {
     const char16_t* params[] = { invalidPacedProperty.get() };
     nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aCx);
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -3,16 +3,17 @@
 /* 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 mozilla_dom_KeyframeEffect_h
 #define mozilla_dom_KeyframeEffect_h
 
 #include "nsWrapperCache.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
 #include "mozilla/AnimationTarget.h" // For (Non)OwningAnimationTarget
 #include "mozilla/Maybe.h"
 
 struct JSContext;
 class JSObject;
 class nsIDocument;
 
@@ -66,17 +67,27 @@ public:
 
   // This method calls GetTargetStyleContext which is not safe to use when
   // we are in the middle of updating style. If we need to use this when
   // updating style, we should pass the nsStyleContext into this method and use
   // that to update the properties rather than calling
   // GetStyleContextForElement.
   void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
 
-  void SetSpacing(JSContext* aCx, const nsAString& aSpacing, ErrorResult& aRv);
+  void GetSpacing(nsString& aRetVal, CallerType aCallerType)
+  {
+    KeyframeEffectReadOnly::GetSpacing(aRetVal);
+  }
+  void SetSpacing(JSContext* aCx, const nsAString& aSpacing,
+                  CallerType aCallerType, ErrorResult& aRv);
+  IterationCompositeOperation IterationComposite(CallerType aCallerType)
+  {
+    return KeyframeEffectReadOnly::IterationComposite();
+  }
   void SetIterationComposite(
-    const IterationCompositeOperation& aIterationComposite);
+    const IterationCompositeOperation& aIterationComposite,
+    CallerType aCallerType);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffect_h
--- a/dom/animation/KeyframeEffectParams.cpp
+++ b/dom/animation/KeyframeEffectParams.cpp
@@ -104,23 +104,24 @@ ConsumeIdentToken(RangedPtr<const char16
   }
 }
 
 /* static */ void
 KeyframeEffectParams::ParseSpacing(const nsAString& aSpacing,
                                    SpacingMode& aSpacingMode,
                                    nsCSSPropertyID& aPacedProperty,
                                    nsAString& aInvalidPacedProperty,
+                                   dom::CallerType aCallerType,
                                    ErrorResult& aRv)
 {
   aInvalidPacedProperty.Truncate();
 
   // Ignore spacing if the core API is not enabled since it is not yet ready to
   // ship.
-  if (!AnimationUtils::IsCoreAPIEnabledForCaller()) {
+  if (!AnimationUtils::IsCoreAPIEnabledForCaller(aCallerType)) {
     aSpacingMode = SpacingMode::distribute;
     return;
   }
 
   // Parse spacing.
   // distribute | paced({ident})
   // https://w3c.github.io/web-animations/#dom-keyframeeffectreadonly-spacing
   // 1. distribute spacing.
--- a/dom/animation/KeyframeEffectParams.h
+++ b/dom/animation/KeyframeEffectParams.h
@@ -9,16 +9,17 @@
 
 #include "nsCSSProps.h"
 #include "nsString.h"
 // X11 has a #define for None
 #ifdef None
 #undef None
 #endif
 #include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
+#include "mozilla/dom/BindingDeclarations.h"   // CallerType
 
 namespace mozilla {
 
 class ErrorResult;
 
 enum class SpacingMode
 {
   distribute,
@@ -49,16 +50,17 @@ struct KeyframeEffectParams
    *                                    is not a recognized animatable property,
    *                                    will be set to <ident>.
    * @param [out] aRv The error result.
    */
   static void ParseSpacing(const nsAString& aSpacing,
                            SpacingMode& aSpacingMode,
                            nsCSSPropertyID& aPacedProperty,
                            nsAString& aInvalidPacedProperty,
+                           dom::CallerType aCallerType,
                            ErrorResult& aRv);
 
   dom::IterationCompositeOperation mIterationComposite =
     dom::IterationCompositeOperation::Replace;
   dom::CompositeOperation mComposite = dom::CompositeOperation::Replace;
   SpacingMode mSpacingMode = SpacingMode::distribute;
   nsCSSPropertyID mPacedProperty = eCSSProperty_UNKNOWN;
 };
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -563,30 +563,32 @@ KeyframeEffectOptionsFromUnion(
   MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
   return aOptions.GetAsKeyframeAnimationOptions();
 }
 
 template <class OptionsType>
 static KeyframeEffectParams
 KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
                               nsAString& aInvalidPacedProperty,
+                              CallerType aCallerType,
                               ErrorResult& aRv)
 {
   KeyframeEffectParams result;
   if (!aOptions.IsUnrestrictedDouble()) {
     const KeyframeEffectOptions& options =
       KeyframeEffectOptionsFromUnion(aOptions);
     KeyframeEffectParams::ParseSpacing(options.mSpacing,
                                        result.mSpacingMode,
                                        result.mPacedProperty,
                                        aInvalidPacedProperty,
+                                       aCallerType,
                                        aRv);
     // Ignore iterationComposite if the Web Animations API is not enabled,
     // then the default value 'Replace' will be used.
-    if (AnimationUtils::IsCoreAPIEnabledForCaller()) {
+    if (AnimationUtils::IsCoreAPIEnabledForCaller(aCallerType)) {
       result.mIterationComposite = options.mIterationComposite;
       // FIXME: Bug 1311620: We don't support additive animation yet.
       if (options.mComposite != dom::CompositeOperation::Add) {
         result.mComposite = options.mComposite;
       }
     }
   }
   return result;
@@ -634,17 +636,18 @@ KeyframeEffectReadOnly::ConstructKeyfram
   TimingParams timingParams =
     TimingParams::FromOptionsUnion(aOptions, doc, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   nsAutoString invalidPacedProperty;
   KeyframeEffectParams effectOptions =
-    KeyframeEffectParamsFromUnion(aOptions, invalidPacedProperty, aRv);
+    KeyframeEffectParamsFromUnion(aOptions, invalidPacedProperty,
+                                  aGlobal.CallerType(), aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!invalidPacedProperty.IsEmpty()) {
     const char16_t* params[] = { invalidPacedProperty.get() };
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Animation"),
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -19,16 +19,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/KeyframeEffectParams.h"
 #include "mozilla/ServoBindingTypes.h" // RawServoDeclarationBlock and
                                        // associated RefPtrTraits
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Element.h"
 
 struct JSContext;
 class JSObject;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
 class nsIPresShell;
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -576,17 +576,17 @@ Location::GetPathname(nsAString& aPathna
 {
   aPathname.Truncate();
 
   nsCOMPtr<nsIURI> uri;
   nsresult result = NS_OK;
 
   result = GetURI(getter_AddRefs(uri));
 
-  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
   if (url) {
     nsAutoCString file;
 
     result = url->GetFilePath(file);
 
     if (NS_SUCCEEDED(result)) {
       AppendUTF8toUTF16(file, aPathname);
     }
@@ -599,22 +599,22 @@ NS_IMETHODIMP
 Location::SetPathname(const nsAString& aPathname)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
-  rv = uri->SetPath(NS_ConvertUTF16toUTF8(aPathname));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(uri));
+  if (url && NS_SUCCEEDED(url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)))) {
+    return SetURI(uri);
   }
 
-  return SetURI(uri);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 Location::GetPort(nsAString& aPort)
 {
   aPort.SetLength(0);
 
   nsCOMPtr<nsIURI> uri;
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -79,16 +79,18 @@ struct AllOwningUnionBase {
 };
 
 
 struct EnumEntry {
   const char* value;
   size_t length;
 };
 
+enum class CallerType : uint32_t;
+
 class MOZ_STACK_CLASS GlobalObject
 {
 public:
   GlobalObject(JSContext* aCx, JSObject* aObject);
 
   JSObject* Get() const
   {
     return mGlobalJSObject;
@@ -108,16 +110,20 @@ public:
   {
     return !Get();
   }
 
   // It returns the subjectPrincipal if called on the main-thread, otherwise
   // a nullptr is returned.
   nsIPrincipal* GetSubjectPrincipal() const;
 
+  // Get the caller type.  Note that this needs to be called before anyone has
+  // had a chance to mess with the JSContext.
+  dom::CallerType CallerType() const;
+
 protected:
   JS::Rooted<JSObject*> mGlobalJSObject;
   JSContext* mCx;
   mutable nsISupports* MOZ_UNSAFE_REF("Valid because GlobalObject is a stack "
                                       "class, and mGlobalObject points to the "
                                       "global, so it won't be destroyed as long "
                                       "as GlobalObject lives on the stack") mGlobalObject;
 };
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2310,16 +2310,23 @@ GlobalObject::GetSubjectPrincipal() cons
   }
 
   JSCompartment* compartment = js::GetContextCompartment(mCx);
   MOZ_ASSERT(compartment);
   JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
   return nsJSPrincipals::get(principals);
 }
 
+CallerType
+GlobalObject::CallerType() const
+{
+  return nsContentUtils::ThreadsafeIsSystemCaller(mCx) ?
+    dom::CallerType::System : dom::CallerType::NonSystem;
+}
+
 static bool
 CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
 {
     JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
     bool isInstance;
     if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
       return false;
     }
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -309,42 +309,42 @@ public:
 
     void ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth, GLint stencil);
 
     // -------------------------------------------------------------------------
     // Sampler Objects - WebGL2ContextSamplers.cpp
 
     already_AddRefed<WebGLSampler> CreateSampler();
     void DeleteSampler(WebGLSampler* sampler);
-    bool IsSampler(WebGLSampler* sampler);
+    bool IsSampler(const WebGLSampler* sampler);
     void BindSampler(GLuint unit, WebGLSampler* sampler);
     void SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint param);
     void SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param);
     void GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                              JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Sync objects - WebGL2ContextSync.cpp
 
     already_AddRefed<WebGLSync> FenceSync(GLenum condition, GLbitfield flags);
-    bool IsSync(WebGLSync* sync);
+    bool IsSync(const WebGLSync* sync);
     void DeleteSync(WebGLSync* sync);
     GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout);
     void WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout);
     void GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                           JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Transform Feedback - WebGL2ContextTransformFeedback.cpp
 
     already_AddRefed<WebGLTransformFeedback> CreateTransformFeedback();
     void DeleteTransformFeedback(WebGLTransformFeedback* tf);
-    bool IsTransformFeedback(WebGLTransformFeedback* tf);
+    bool IsTransformFeedback(const WebGLTransformFeedback* tf);
     void BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf);
     void BeginTransformFeedback(GLenum primitiveMode);
     void EndTransformFeedback();
     void PauseTransformFeedback();
     void ResumeTransformFeedback();
     void TransformFeedbackVaryings(WebGLProgram& program,
                                    const dom::Sequence<nsString>& varyings,
                                    GLenum bufferMode);
--- a/dom/canvas/WebGL2ContextPrograms.cpp
+++ b/dom/canvas/WebGL2ContextPrograms.cpp
@@ -14,15 +14,15 @@ namespace mozilla {
 // Programs and shaders
 
 GLint
 WebGL2Context::GetFragDataLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObjectRef("getFragDataLocation: program", prog))
+    if (!ValidateObject("getFragDataLocation: program", prog))
         return -1;
 
     return prog.GetFragDataLocation(name);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextQueries.cpp
+++ b/dom/canvas/WebGL2ContextQueries.cpp
@@ -73,63 +73,48 @@ WebGLContext::CreateQuery(const char* fu
 
 void
 WebGLContext::DeleteQuery(WebGLQuery* query, const char* funcName)
 {
     if (!funcName) {
         funcName = "deleteQuery";
     }
 
-    if (IsContextLost())
-        return;
-
-    if (!query)
-        return;
-
-    if (!ValidateObjectAllowDeleted(funcName, query))
+    if (!ValidateDeleteObject(funcName, query))
         return;
 
     query->DeleteQuery();
 }
 
 bool
 WebGLContext::IsQuery(const WebGLQuery* query, const char* funcName)
 {
     if (!funcName) {
         funcName = "isQuery";
     }
 
-    if (IsContextLost())
-        return false;
-
-    if (!query)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isQuery", query))
+    if (!ValidateIsObject(funcName, query))
         return false;
 
     return query->IsQuery();
 }
 
 void
 WebGLContext::BeginQuery(GLenum target, WebGLQuery& query, const char* funcName)
 {
     if (!funcName) {
         funcName = "beginQuery";
     }
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, &query))
+    if (!ValidateObject(funcName, query))
         return;
 
-    if (query.IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot begin a deleted query.", funcName);
-
     const auto& slot = ValidateQuerySlotByTarget(funcName, target);
     if (!slot)
         return;
 
     if (*slot)
         return ErrorInvalidOperation("%s: Query target already active.", funcName);
 
     ////
@@ -233,18 +218,15 @@ WebGLContext::GetQueryParameter(JSContex
     if (!funcName) {
         funcName = "getQueryParameter";
     }
 
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeleted(funcName, &query))
+    if (!ValidateObject(funcName, query))
         return;
 
-    if (query.IsDeleted())
-        return ErrorInvalidOperation("%s: Query must not be deleted.", funcName);
-
     query.GetQueryParameter(pname, retval);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -21,116 +21,98 @@ WebGL2Context::CreateSampler()
 
     RefPtr<WebGLSampler> globj = new WebGLSampler(this, sampler);
     return globj.forget();
 }
 
 void
 WebGL2Context::DeleteSampler(WebGLSampler* sampler)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteSampler", sampler))
-        return;
-
-    if (!sampler || sampler->IsDeleted())
+    if (!ValidateDeleteObject("deleteSampler", sampler))
         return;
 
     for (int n = 0; n < mGLMaxTextureUnits; n++) {
         if (mBoundSamplers[n] == sampler) {
             mBoundSamplers[n] = nullptr;
 
             InvalidateResolveCacheForTextureWithTexUnit(n);
         }
     }
 
     sampler->RequestDelete();
 }
 
 bool
-WebGL2Context::IsSampler(WebGLSampler* sampler)
+WebGL2Context::IsSampler(const WebGLSampler* sampler)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!sampler)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isSampler", sampler))
-        return false;
-
-    if (sampler->IsDeleted())
+    if (!ValidateIsObject("isSampler", sampler))
         return false;
 
     MakeContextCurrent();
     return gl->fIsSampler(sampler->mGLName);
 }
 
 void
 WebGL2Context::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindSampler", sampler))
+    if (sampler && !ValidateObject("bindSampler", *sampler))
         return;
 
     if (GLint(unit) >= mGLMaxTextureUnits)
         return ErrorInvalidValue("bindSampler: unit must be < %d", mGLMaxTextureUnits);
 
-    if (sampler && sampler->IsDeleted())
-        return ErrorInvalidOperation("bindSampler: binding deleted sampler");
-
     ////
 
     gl->MakeCurrent();
     gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
 
     InvalidateResolveCacheForTextureWithTexUnit(unit);
     mBoundSamplers[unit] = sampler;
 }
 
 void
 WebGL2Context::SamplerParameteri(WebGLSampler& sampler, GLenum pname, GLint paramInt)
 {
     const char funcName[] = "samplerParameteri";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     sampler.SamplerParameter(funcName, pname, paramInt);
 }
 
 void
 WebGL2Context::SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat paramFloat)
 {
     const char funcName[] = "samplerParameterf";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     sampler.SamplerParameter(funcName, pname, WebGLIntOrFloat(paramFloat).AsInt());
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                                    JS::MutableHandleValue retval)
 {
     const char funcName[] = "getSamplerParameter";
     retval.setNull();
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sampler))
+    if (!ValidateObject(funcName, sampler))
         return;
 
     ////
 
     gl->MakeCurrent();
 
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -11,66 +11,60 @@
 namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Sync objects
 
 already_AddRefed<WebGLSync>
 WebGL2Context::FenceSync(GLenum condition, GLbitfield flags)
 {
-   if (IsContextLost())
-       return nullptr;
+    if (IsContextLost())
+        return nullptr;
 
-   if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
-       ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
-       return nullptr;
-   }
+    if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
+        ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
+        return nullptr;
+    }
 
-   if (flags != 0) {
-       ErrorInvalidValue("fenceSync: flags must be 0");
-       return nullptr;
-   }
+    if (flags != 0) {
+        ErrorInvalidValue("fenceSync: flags must be 0");
+        return nullptr;
+    }
 
-   MakeContextCurrent();
-   RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
-   return globj.forget();
+    MakeContextCurrent();
+    RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
+    return globj.forget();
 }
 
 bool
-WebGL2Context::IsSync(WebGLSync* sync)
+WebGL2Context::IsSync(const WebGLSync* sync)
 {
-   if (IsContextLost())
-       return false;
+    if (!ValidateIsObject("isSync", sync))
+        return false;
 
-   return ValidateObjectAllowDeleted("isSync", sync) && !sync->IsDeleted();
+    return true;
 }
 
 void
 WebGL2Context::DeleteSync(WebGLSync* sync)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteSync", sync))
-        return;
-
-    if (!sync || sync->IsDeleted())
+    if (!ValidateDeleteObject("deleteSync", sync))
         return;
 
     sync->RequestDelete();
 }
 
 GLenum
 WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout)
 {
     const char funcName[] = "clientWaitSync";
     if (IsContextLost())
         return LOCAL_GL_WAIT_FAILED;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return LOCAL_GL_WAIT_FAILED;
 
     if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
         ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName);
         return LOCAL_GL_WAIT_FAILED;
     }
 
     MakeContextCurrent();
@@ -79,17 +73,17 @@ WebGL2Context::ClientWaitSync(const WebG
 
 void
 WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout)
 {
     const char funcName[] = "waitSync";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return;
 
     if (flags != 0) {
         ErrorInvalidValue("%s: `flags` must be 0.", funcName);
         return;
     }
 
     if (timeout != -1) {
@@ -105,17 +99,17 @@ void
 WebGL2Context::GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                                 JS::MutableHandleValue retval)
 {
     const char funcName[] = "getSyncParameter";
     retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef(funcName, sync))
+    if (!ValidateObject(funcName, sync))
         return;
 
     ////
 
     gl->MakeCurrent();
 
     GLint result = 0;
     switch (pname) {
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -27,66 +27,54 @@ WebGL2Context::CreateTransformFeedback()
     RefPtr<WebGLTransformFeedback> ret = new WebGLTransformFeedback(this, tf);
     return ret.forget();
 }
 
 void
 WebGL2Context::DeleteTransformFeedback(WebGLTransformFeedback* tf)
 {
     const char funcName[] = "deleteTransformFeedback";
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObject(funcName, tf))
+    if (!ValidateDeleteObject(funcName, tf))
         return;
 
     if (tf->mIsActive) {
         ErrorInvalidOperation("%s: Cannot delete active transform feedbacks.", funcName);
         return;
     }
 
     if (mBoundTransformFeedback == tf) {
         BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, nullptr);
     }
 
     tf->RequestDelete();
 }
 
 bool
-WebGL2Context::IsTransformFeedback(WebGLTransformFeedback* tf)
+WebGL2Context::IsTransformFeedback(const WebGLTransformFeedback* tf)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeletedOrNull("isTransformFeedback", tf))
-        return false;
-
-    if (!tf || tf->IsDeleted())
+    if (!ValidateIsObject("isTransformFeedback", tf))
         return false;
 
     MakeContextCurrent();
     return gl->fIsTransformFeedback(tf->mGLName);
 }
 
 void
 WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
 {
     const char funcName[] = "bindTransformFeedback";
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_TRANSFORM_FEEDBACK)
         return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName);
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, tf))
+    if (tf && !ValidateObject(funcName, *tf))
         return;
 
-    if (tf && tf->IsDeleted())
-        return ErrorInvalidOperation("%s: TFO already deleted.", funcName);
-
     if (mBoundTransformFeedback->mIsActive &&
         !mBoundTransformFeedback->mIsPaused)
     {
         ErrorInvalidOperation("%s: Currently bound transform feedback is active and not"
                               " paused.",
                               funcName);
         return;
     }
@@ -138,27 +126,27 @@ WebGL2Context::ResumeTransformFeedback()
 void
 WebGL2Context::TransformFeedbackVaryings(WebGLProgram& program,
                                          const dom::Sequence<nsString>& varyings,
                                          GLenum bufferMode)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("transformFeedbackVaryings: program", program))
+    if (!ValidateObject("transformFeedbackVaryings: program", program))
         return;
 
     program.TransformFeedbackVaryings(varyings, bufferMode);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGL2Context::GetTransformFeedbackVarying(const WebGLProgram& program, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getTransformFeedbackVarying: program", program))
+    if (!ValidateObject("getTransformFeedbackVarying: program", program))
         return nullptr;
 
     return program.GetTransformFeedbackVarying(index);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -133,17 +133,17 @@ void
 WebGL2Context::GetUniformIndices(const WebGLProgram& program,
                                  const dom::Sequence<nsString>& uniformNames,
                                  dom::Nullable< nsTArray<GLuint> >& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getUniformIndices: program", program))
+    if (!ValidateObject("getUniformIndices: program", program))
         return;
 
     if (!uniformNames.Length())
         return;
 
     program.GetUniformIndices(uniformNames, retval);
 }
 
@@ -174,17 +174,17 @@ WebGL2Context::GetActiveUniforms(JSConte
     const char funcName[] = "getActiveUniforms";
     retval.setNull();
     if (IsContextLost())
         return;
 
     if (!ValidateUniformEnum(this, pname, funcName))
         return;
 
-    if (!ValidateObjectRef("getActiveUniforms: program", program))
+    if (!ValidateObject("getActiveUniforms: program", program))
         return;
 
     const auto& count = uniformIndices.Length();
 
     JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
     UniquePtr<GLint[]> samples(new GLint[count]);
     if (!array || !samples) {
         ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
@@ -226,33 +226,33 @@ WebGL2Context::GetActiveUniforms(JSConte
 
 GLuint
 WebGL2Context::GetUniformBlockIndex(const WebGLProgram& program,
                                     const nsAString& uniformBlockName)
 {
     if (IsContextLost())
         return 0;
 
-    if (!ValidateObjectRef("getUniformBlockIndex: program", program))
+    if (!ValidateObject("getUniformBlockIndex: program", program))
         return 0;
 
     return program.GetUniformBlockIndex(uniformBlockName);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, const WebGLProgram& program,
                                               GLuint uniformBlockIndex, GLenum pname,
                                               JS::MutableHandleValue out_retval,
                                               ErrorResult& out_error)
 {
     out_retval.setNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getActiveUniformBlockParameter: program", program))
+    if (!ValidateObject("getActiveUniformBlockParameter: program", program))
         return;
 
     MakeContextCurrent();
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
@@ -273,28 +273,28 @@ WebGL2Context::GetActiveUniformBlockPara
 void
 WebGL2Context::GetActiveUniformBlockName(const WebGLProgram& program,
                                          GLuint uniformBlockIndex, nsAString& retval)
 {
     retval.SetIsVoid(true);
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getActiveUniformBlockName: program", program))
+    if (!ValidateObject("getActiveUniformBlockName: program", program))
         return;
 
     program.GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
 
 void
 WebGL2Context::UniformBlockBinding(WebGLProgram& program, GLuint uniformBlockIndex,
                                    GLuint uniformBlockBinding)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("uniformBlockBinding: program", program))
+    if (!ValidateObject("uniformBlockBinding: program", program))
         return;
 
     program.UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -8,17 +8,17 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLElementArrayCache.h"
 
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
     , mUsage(LOCAL_GL_STATIC_DRAW)
     , mByteLength(0)
 {
     mContext->mBuffers.insertBack(this);
 }
 
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -17,17 +17,16 @@
 namespace mozilla {
 
 class WebGLElementArrayCache;
 
 class WebGLBuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLTexture;
     friend class WebGLTransformFeedback;
 
 public:
     enum class Kind {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -217,17 +217,17 @@ WebGLContext::~WebGLContext()
     DestroyResourcesAndContext();
     if (NS_IsMainThread()) {
         // XXX mtseng: bug 709490, not thread safe
         WebGLMemoryTracker::RemoveWebGLContext(this);
     }
 }
 
 template<typename T>
-static void
+void
 ClearLinkedList(LinkedList<T>& list)
 {
     while (!list.isEmpty()) {
         list.getLast()->DeleteOnce();
     }
 }
 
 void
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -611,21 +611,21 @@ public:
     {
         retval.set(GetUniform(cx, prog, loc));
     }
 
     already_AddRefed<WebGLUniformLocation>
     GetUniformLocation(const WebGLProgram& prog, const nsAString& name);
 
     void Hint(GLenum target, GLenum mode);
-    bool IsFramebuffer(WebGLFramebuffer* fb);
-    bool IsProgram(WebGLProgram* prog);
-    bool IsRenderbuffer(WebGLRenderbuffer* rb);
-    bool IsShader(WebGLShader* shader);
-    bool IsVertexArray(WebGLVertexArray* vao);
+    bool IsFramebuffer(const WebGLFramebuffer* fb);
+    bool IsProgram(const WebGLProgram* prog);
+    bool IsRenderbuffer(const WebGLRenderbuffer* rb);
+    bool IsShader(const WebGLShader* shader);
+    bool IsVertexArray(const WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
     bool StartVRPresentation();
 protected:
@@ -1623,43 +1623,105 @@ protected:
     bool ConvertImage(size_t width, size_t height, size_t srcStride,
                       size_t dstStride, const uint8_t* src, uint8_t* dst,
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     //////
 public:
-    // Returns false if `object` is null or not valid.
-    template<class ObjectType>
-    bool ValidateObject(const char* info, const ObjectType* object);
+    bool ValidateObjectAllowDeleted(const char* funcName,
+                                    const WebGLContextBoundObject& object)
+    {
+        if (!object.IsCompatibleWithContext(this)) {
+            ErrorInvalidOperation("%s: Object from different WebGL context (or older"
+                                  " generation of this one) passed as argument.",
+                                  funcName);
+            return false;
+        }
+
+        return true;
+    }
+
+    bool ValidateObject(const char* funcName, const WebGLDeletableObject& object,
+                        bool isShaderOrProgram = false)
+    {
+        if (!ValidateObjectAllowDeleted(funcName, object))
+            return false;
 
-    // Returns false if `object` is not valid.
-    template<class ObjectType>
-    bool ValidateObjectRef(const char* info, const ObjectType& object);
-
-    // Returns false if `object` is not valid.  Considers null to be valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowNull(const char* info, const ObjectType* object);
+        if (isShaderOrProgram) {
+            /* GLES 3.0.5 p45:
+             * "Commands that accept shader or program object names will generate the
+             *  error INVALID_VALUE if the provided name is not the name of either a
+             *  shader or program object[.]"
+             * Further, shaders and programs appear to be different from other objects,
+             * in that their lifetimes are better defined. However, they also appear to
+             * allow use of objects marked for deletion, and only reject
+             * actually-destroyed objects.
+             */
+            if (object.IsDeleted()) {
+                ErrorInvalidValue("%s: Shader or program object argument cannot have been"
+                                  " deleted.",
+                                  funcName);
+                return false;
+            }
+        } else {
+            if (object.IsDeleteRequested()) {
+                ErrorInvalidOperation("%s: Object argument cannot have been marked for"
+                                      " deletion.",
+                                      funcName);
+                return false;
+            }
+        }
 
-    // Returns false if `object` is not valid, but considers deleted objects and
-    // null objects valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object);
+        return true;
+    }
+
+    ////
+
+    bool ValidateObject(const char* funcName, const WebGLProgram& object);
+    bool ValidateObject(const char* funcName, const WebGLShader& object);
+
+    ////
+
+    bool ValidateIsObject(const char* funcName,
+                          const WebGLDeletableObject* object) const
+    {
+        if (IsContextLost())
+            return false;
+
+        if (!object)
+            return false;
+
+        if (!object->IsCompatibleWithContext(this))
+            return false;
 
-    // Returns false if `object` is null or not valid, but considers deleted
-    // objects valid.
-    template<class ObjectType>
-    bool ValidateObjectAllowDeleted(const char* info, const ObjectType* object);
+        if (object->IsDeleted())
+            return false;
+
+        return true;
+    }
+
+    bool ValidateDeleteObject(const char* funcName, const WebGLDeletableObject* object) {
+        if (IsContextLost())
+            return false;
 
-private:
-    // Like ValidateObject, but only for cases when `object` is known to not be
-    // null already.
-    template<class ObjectType>
-    bool ValidateObjectAssumeNonNull(const char* info, const ObjectType* object);
+        if (!object)
+            return false;
+
+        if (!ValidateObjectAllowDeleted(funcName, *object))
+            return false;
+
+        if (object->IsDeleteRequested())
+            return false;
+
+        return true;
+    }
+
+    ////
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual WebGLVertexArray* CreateVertexArrayImpl();
 
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
@@ -1948,92 +2010,16 @@ public:
 
 // used by DOM bindings in conjunction with GetParentObject
 inline nsISupports*
 ToSupports(WebGLContext* webgl)
 {
     return static_cast<nsIDOMWebGLRenderingContext*>(webgl);
 }
 
-/**
- ** Template implementations
- **/
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object)
-{
-    if (object && !object->IsCompatibleWithContext(this)) {
-        ErrorInvalidOperation("%s: object from different WebGL context "
-                              "(or older generation of this one) "
-                              "passed as argument", info);
-        return false;
-    }
-
-    return true;
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAssumeNonNull(const char* info, const ObjectType* object)
-{
-    MOZ_ASSERT(object);
-
-    if (!ValidateObjectAllowDeletedOrNull(info, object))
-        return false;
-
-    if (object->IsDeleted()) {
-        ErrorInvalidValue("%s: Deleted object passed as argument.", info);
-        return false;
-    }
-
-    return true;
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowNull(const char* info, const ObjectType* object)
-{
-    if (!object)
-        return true;
-
-    return ValidateObjectAssumeNonNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectAllowDeleted(const char* info, const ObjectType* object)
-{
-    if (!object) {
-        ErrorInvalidValue("%s: null object passed as argument", info);
-        return false;
-    }
-
-    return ValidateObjectAllowDeletedOrNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObject(const char* info, const ObjectType* object)
-{
-    if (!object) {
-        ErrorInvalidValue("%s: null object passed as argument", info);
-        return false;
-    }
-
-    return ValidateObjectAssumeNonNull(info, object);
-}
-
-template<class ObjectType>
-inline bool
-WebGLContext::ValidateObjectRef(const char* info, const ObjectType& object)
-{
-    return ValidateObjectAssumeNonNull(info, &object);
-}
-
 // Returns `value` rounded to the next highest multiple of `multiple`.
 // AKA PadToAlignment, StrideForAlignment.
 template<typename V, typename M>
 V
 RoundUpToMultipleOf(const V& value, const M& multiple)
 {
     return ((value + multiple - 1) / multiple) * multiple;
 }
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -114,22 +114,19 @@ WebGLContext::ValidateIndexedBufferSlot(
 
 void
 WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
 {
     const char funcName[] = "bindBuffer";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     const auto& slot = ValidateBufferSlot(funcName, target);
     if (!slot)
         return;
 
     if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
         return;
 
     gl->MakeCurrent();
@@ -178,22 +175,19 @@ WebGLContext::ValidateIndexedBufferBindi
 
 void
 WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
     const char funcName[] = "bindBufferBase";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
     if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
                                       &indexedBinding))
     {
         return;
     }
 
@@ -229,22 +223,19 @@ WebGLContext::BindBufferBase(GLenum targ
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
     const char funcName[] = "bindBufferRange";
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull(funcName, buffer))
+    if (buffer && !ValidateObject(funcName, *buffer))
         return;
 
-    if (buffer && buffer->IsDeleted())
-        return ErrorInvalidOperation("%s: Cannot bind a deleted object.", funcName);
-
     if (!ValidateNonNegative(funcName, "offset", offset) ||
         !ValidateNonNegative(funcName, "size", size))
     {
         return;
     }
 
     WebGLRefPtr<WebGLBuffer>* genericBinding;
     IndexedBufferBinding* indexedBinding;
@@ -471,23 +462,17 @@ WebGLContext::CreateBuffer()
 
     RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
     return globj.forget();
 }
 
 void
 WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer))
-        return;
-
-    if (!buffer || buffer->IsDeleted())
+    if (!ValidateDeleteObject("deleteBuffer", buffer))
         return;
 
     ////
 
     const auto fnClearIfBuffer = [&](WebGLRefPtr<WebGLBuffer>& bindPoint) {
         if (bindPoint == buffer) {
             bindPoint = nullptr;
         }
@@ -525,22 +510,16 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
     ////
 
     buffer->RequestDelete();
 }
 
 bool
 WebGLContext::IsBuffer(WebGLBuffer* buffer)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isBuffer", buffer))
-        return false;
-
-    if (buffer->IsDeleted())
+    if (!ValidateIsObject("isBuffer", buffer))
         return false;
 
     MakeContextCurrent();
     return gl->fIsBuffer(buffer->mGLName);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -52,16 +52,28 @@
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 
+bool
+WebGLContext::ValidateObject(const char* funcName, const WebGLProgram& object)
+{
+    return ValidateObject(funcName, object, true);
+}
+
+bool
+WebGLContext::ValidateObject(const char* funcName, const WebGLShader& object)
+{
+    return ValidateObject(funcName, object, true);
+}
+
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 //
 //  WebGL API
 //
 
@@ -87,53 +99,50 @@ WebGLContext::ActiveTexture(GLenum textu
 }
 
 void
 WebGLContext::AttachShader(WebGLProgram& program, WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("attachShader: program", program) ||
-        !ValidateObjectRef("attachShader: shader", shader))
+    if (!ValidateObject("attachShader: program", program) ||
+        !ValidateObject("attachShader: shader", shader))
     {
         return;
     }
 
     program.AttachShader(&shader);
 }
 
 void
 WebGLContext::BindAttribLocation(WebGLProgram& prog, GLuint location,
                                  const nsAString& name)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("bindAttribLocation: program", prog))
+    if (!ValidateObject("bindAttribLocation: program", prog))
         return;
 
     prog.BindAttribLocation(location, name);
 }
 
 void
 WebGLContext::BindFramebuffer(GLenum target, WebGLFramebuffer* wfb)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateFramebufferTarget(target, "bindFramebuffer"))
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindFramebuffer", wfb))
+    if (wfb && !ValidateObject("bindFramebuffer", *wfb))
         return;
 
-    if (wfb && wfb->IsDeleted())
-        return ErrorInvalidOperation("bindFramebuffer: Cannot bind a deleted object.");
-
     MakeContextCurrent();
 
     if (!wfb) {
         gl->fBindFramebuffer(target, 0);
     } else {
         GLuint framebuffername = wfb->mGLName;
         gl->fBindFramebuffer(target, framebuffername);
 #ifdef ANDROID
@@ -161,22 +170,19 @@ void
 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
 {
     if (IsContextLost())
         return;
 
     if (target != LOCAL_GL_RENDERBUFFER)
         return ErrorInvalidEnumInfo("bindRenderbuffer: target", target);
 
-    if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
+    if (wrb && !ValidateObject("bindRenderbuffer", *wrb))
         return;
 
-    if (wrb && wrb->IsDeleted())
-        return ErrorInvalidOperation("bindRenderbuffer: Cannot bind a deleted object.");
-
     // Usually, we would now call into glBindRenderbuffer. However, since we have to
     // potentially emulate packed-depth-stencil, there's not a specific renderbuffer that
     // we know we should bind here.
     // Instead, we do all renderbuffer binding lazily.
 
     if (wrb) {
         wrb->mHasBeenBound = true;
     }
@@ -315,23 +321,17 @@ WebGLContext::CullFace(GLenum face)
 
     MakeContextCurrent();
     gl->fCullFace(face);
 }
 
 void
 WebGLContext::DeleteFramebuffer(WebGLFramebuffer* fbuf)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteFramebuffer", fbuf))
-        return;
-
-    if (!fbuf || fbuf->IsDeleted())
+    if (!ValidateDeleteObject("deleteFramebuffer", fbuf))
         return;
 
     fbuf->RequestDelete();
 
     if (mBoundReadFramebuffer == mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer == fbuf) {
             BindFramebuffer(LOCAL_GL_FRAMEBUFFER,
                             static_cast<WebGLFramebuffer*>(nullptr));
@@ -343,23 +343,17 @@ WebGLContext::DeleteFramebuffer(WebGLFra
         BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
                         static_cast<WebGLFramebuffer*>(nullptr));
     }
 }
 
 void
 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteRenderbuffer", rbuf))
-        return;
-
-    if (!rbuf || rbuf->IsDeleted())
+    if (!ValidateDeleteObject("deleteRenderbuffer", rbuf))
         return;
 
     if (mBoundDrawFramebuffer)
         mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
 
     if (mBoundReadFramebuffer)
         mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
 
@@ -369,23 +363,17 @@ WebGLContext::DeleteRenderbuffer(WebGLRe
         BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
 
     rbuf->RequestDelete();
 }
 
 void
 WebGLContext::DeleteTexture(WebGLTexture* tex)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteTexture", tex))
-        return;
-
-    if (!tex || tex->IsDeleted())
+    if (!ValidateDeleteObject("deleteTexture", tex))
         return;
 
     if (mBoundDrawFramebuffer)
         mBoundDrawFramebuffer->DetachTexture(tex);
 
     if (mBoundReadFramebuffer)
         mBoundReadFramebuffer->DetachTexture(tex);
 
@@ -403,53 +391,41 @@ WebGLContext::DeleteTexture(WebGLTexture
     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
 WebGLContext::DeleteProgram(WebGLProgram* prog)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteProgram", prog))
-        return;
-
-    if (!prog || prog->IsDeleted())
+    if (!ValidateDeleteObject("deleteProgram", prog))
         return;
 
     prog->RequestDelete();
 }
 
 void
 WebGLContext::DeleteShader(WebGLShader* shader)
 {
-    if (IsContextLost())
-        return;
-
-    if (!ValidateObjectAllowDeletedOrNull("deleteShader", shader))
-        return;
-
-    if (!shader || shader->IsDeleted())
+    if (!ValidateDeleteObject("deleteShader", shader))
         return;
 
     shader->RequestDelete();
 }
 
 void
 WebGLContext::DetachShader(WebGLProgram& program, const WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
     // It's valid to attempt to detach a deleted shader, since it's still a
     // shader.
-    if (!ValidateObjectRef("detachShader: program", program) ||
-        !ValidateObjectAllowDeleted("detachShader: shader", &shader))
+    if (!ValidateObject("detachShader: program", program) ||
+        !ValidateObjectAllowDeleted("detachShader: shader", shader))
     {
         return;
     }
 
     program.DetachShader(&shader);
 }
 
 void
@@ -564,55 +540,55 @@ WebGLContext::FrontFace(GLenum mode)
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveAttrib(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getActiveAttrib: program", prog))
+    if (!ValidateObject("getActiveAttrib: program", prog))
         return nullptr;
 
     return prog.GetActiveAttrib(index);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLContext::GetActiveUniform(const WebGLProgram& prog, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getActiveUniform: program", prog))
+    if (!ValidateObject("getActiveUniform: program", prog))
         return nullptr;
 
     return prog.GetActiveUniform(index);
 }
 
 void
 WebGLContext::GetAttachedShaders(const WebGLProgram& prog,
                                  dom::Nullable<nsTArray<RefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getAttachedShaders", prog))
+    if (!ValidateObject("getAttachedShaders", prog))
         return;
 
     prog.GetAttachedShaders(&retval.SetValue());
 }
 
 GLint
 WebGLContext::GetAttribLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return -1;
 
-    if (!ValidateObjectRef("getAttribLocation: program", prog))
+    if (!ValidateObject("getAttribLocation: program", prog))
         return -1;
 
     return prog.GetAttribLocation(name);
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
@@ -905,62 +881,62 @@ WebGLContext::GetError()
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(const WebGLProgram& prog, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectAllowDeleted("getProgramParameter: program", &prog))
+    if (!ValidateObjectAllowDeleted("getProgramParameter: program", prog))
         return JS::NullValue();
 
     return prog.GetProgramParameter(pname);
 }
 
 void
 WebGLContext::GetProgramInfoLog(const WebGLProgram& prog, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getProgramInfoLog: program", prog))
+    if (!ValidateObject("getProgramInfoLog: program", prog))
         return;
 
     prog.GetProgramInfoLog(&retval);
 }
 
 JS::Value
 WebGLContext::GetUniform(JSContext* js, const WebGLProgram& prog,
                          const WebGLUniformLocation& loc)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getUniform: `program`", prog))
+    if (!ValidateObject("getUniform: `program`", prog))
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getUniform: `location`", loc))
+    if (!ValidateObjectAllowDeleted("getUniform: `location`", loc))
         return JS::NullValue();
 
     if (!loc.ValidateForProgram(&prog, "getUniform"))
         return JS::NullValue();
 
     return loc.GetUniform(js);
 }
 
 already_AddRefed<WebGLUniformLocation>
 WebGLContext::GetUniformLocation(const WebGLProgram& prog, const nsAString& name)
 {
     if (IsContextLost())
         return nullptr;
 
-    if (!ValidateObjectRef("getUniformLocation: program", prog))
+    if (!ValidateObject("getUniformLocation: program", prog))
         return nullptr;
 
     return prog.GetUniformLocation(name);
 }
 
 void
 WebGLContext::Hint(GLenum target, GLenum mode)
 {
@@ -990,80 +966,67 @@ WebGLContext::Hint(GLenum target, GLenum
     if (!isValid)
         return ErrorInvalidEnum("hint: invalid hint");
 
     MakeContextCurrent();
     gl->fHint(target, mode);
 }
 
 bool
-WebGLContext::IsFramebuffer(WebGLFramebuffer* fb)
+WebGLContext::IsFramebuffer(const WebGLFramebuffer* fb)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isFramebuffer", fb))
-        return false;
-
-    if (fb->IsDeleted())
+    if (!ValidateIsObject("isFramebuffer", fb))
         return false;
 
 #ifdef ANDROID
     if (gl->WorkAroundDriverBugs() &&
         gl->Renderer() == GLRenderer::AndroidEmulator)
     {
         return fb->mIsFB;
     }
 #endif
 
     MakeContextCurrent();
     return gl->fIsFramebuffer(fb->mGLName);
 }
 
 bool
-WebGLContext::IsProgram(WebGLProgram* prog)
+WebGLContext::IsProgram(const WebGLProgram* prog)
 {
-    if (IsContextLost())
+    if (!ValidateIsObject("isProgram", prog))
         return false;
 
-    return ValidateObjectAllowDeleted("isProgram", prog) && !prog->IsDeleted();
+    return true;
 }
 
 bool
-WebGLContext::IsRenderbuffer(WebGLRenderbuffer* rb)
+WebGLContext::IsRenderbuffer(const WebGLRenderbuffer* rb)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isRenderBuffer", rb))
-        return false;
-
-    if (rb->IsDeleted())
+    if (!ValidateIsObject("isRenderbuffer", rb))
         return false;
 
     return rb->mHasBeenBound;
 }
 
 bool
-WebGLContext::IsShader(WebGLShader* shader)
+WebGLContext::IsShader(const WebGLShader* shader)
 {
-    if (IsContextLost())
+    if (!ValidateIsObject("isShader", shader))
         return false;
 
-    return ValidateObjectAllowDeleted("isShader", shader) &&
-        !shader->IsDeleted();
+    return true;
 }
 
 void
 WebGLContext::LinkProgram(WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("linkProgram", prog))
+    if (!ValidateObject("linkProgram", prog))
         return;
 
     prog.LinkProgram();
 
     if (!prog.IsLinked()) {
         // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
         // null out mActiveProgramLinkInfo.
         return;
@@ -2096,32 +2059,32 @@ WebGLContext::UseProgram(WebGLProgram* p
         return;
 
     if (!prog) {
         mCurrentProgram = nullptr;
         mActiveProgramLinkInfo = nullptr;
         return;
     }
 
-    if (!ValidateObject("useProgram", prog))
+    if (!ValidateObject("useProgram", *prog))
         return;
 
     if (prog->UseProgram()) {
         mCurrentProgram = prog;
         mActiveProgramLinkInfo = mCurrentProgram->LinkInfo();
     }
 }
 
 void
 WebGLContext::ValidateProgram(const WebGLProgram& prog)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("validateProgram", prog))
+    if (!ValidateObject("validateProgram", prog))
         return;
 
     prog.ValidateProgram();
 }
 
 already_AddRefed<WebGLFramebuffer>
 WebGLContext::CreateFramebuffer()
 {
@@ -2166,43 +2129,43 @@ WebGLContext::Viewport(GLint x, GLint y,
 }
 
 void
 WebGLContext::CompileShader(WebGLShader& shader)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("compileShader", shader))
+    if (!ValidateObject("compileShader", shader))
         return;
 
     shader.CompileShader();
 }
 
 JS::Value
 WebGLContext::GetShaderParameter(const WebGLShader& shader, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateObjectRef("getShaderParameter: shader", shader))
+    if (!ValidateObjectAllowDeleted("getShaderParameter: shader", shader))
         return JS::NullValue();
 
     return shader.GetShaderParameter(pname);
 }
 
 void
 WebGLContext::GetShaderInfoLog(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getShaderInfoLog: shader", shader))
+    if (!ValidateObject("getShaderInfoLog: shader", shader))
         return;
 
     shader.GetShaderInfoLog(&retval);
 }
 
 already_AddRefed<WebGLShaderPrecisionFormat>
 WebGLContext::GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype)
 {
@@ -2254,29 +2217,29 @@ WebGLContext::GetShaderPrecisionFormat(G
 void
 WebGLContext::GetShaderSource(const WebGLShader& shader, nsAString& retval)
 {
     retval.SetIsVoid(true);
 
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("getShaderSource: shader", shader))
+    if (!ValidateObject("getShaderSource: shader", shader))
         return;
 
     shader.GetShaderSource(&retval);
 }
 
 void
 WebGLContext::ShaderSource(WebGLShader& shader, const nsAString& source)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectRef("shaderSource: shader", shader))
+    if (!ValidateObject("shaderSource: shader", shader))
         return;
 
     shader.ShaderSource(source);
 }
 
 void
 WebGLContext::LoseContext()
 {
--- a/dom/canvas/WebGLContextTextures.cpp
+++ b/dom/canvas/WebGLContextTextures.cpp
@@ -204,17 +204,17 @@ WebGLContext::InvalidateResolveCacheForT
 // GL calls
 
 void
 WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex)
 {
     if (IsContextLost())
         return;
 
-     if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex))
+     if (newTex && !ValidateObject("bindTexture", *newTex))
         return;
 
     // Need to check rawTarget first before comparing against newTex->Target() as
     // newTex->Target() returns a TexTarget, which will assert on invalid value.
     WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr;
     switch (rawTarget) {
     case LOCAL_GL_TEXTURE_2D:
         currentTexPtr = &mBound2DTextures[mActiveTexture];
@@ -285,20 +285,17 @@ WebGLContext::GetTexParameter(GLenum raw
     }
 
     return tex->GetTexParameter(texTarget, pname);
 }
 
 bool
 WebGLContext::IsTexture(WebGLTexture* tex)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isTexture", tex))
+    if (!ValidateIsObject("isTexture", tex))
         return false;
 
     return tex->IsTexture();
 }
 
 void
 WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeIntParam,
                                 GLfloat* maybeFloatParam)
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -202,17 +202,17 @@ WebGLContext::ValidateUniformLocation(We
     /* GLES 2.0.25, p38:
      *   If the value of location is -1, the Uniform* commands will silently
      *   ignore the data passed in, and the current uniform values will not be
      *   changed.
      */
     if (!loc)
         return false;
 
-    if (!ValidateObject(funcName, loc))
+    if (!ValidateObjectAllowDeleted(funcName, *loc))
         return false;
 
     if (!mCurrentProgram) {
         ErrorInvalidOperation("%s: No program is currently bound.", funcName);
         return false;
     }
 
     return loc->ValidateForProgram(mCurrentProgram, funcName);
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -13,30 +13,19 @@
 namespace mozilla {
 
 void
 WebGLContext::BindVertexArray(WebGLVertexArray* array)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateObjectAllowDeletedOrNull("bindVertexArrayObject", array))
+    if (array && !ValidateObject("bindVertexArrayObject", *array))
         return;
 
-    if (array && array->IsDeleted()) {
-        /* http://www.khronos.org/registry/gles/extensions/OES/OES_vertex_array_object.txt
-         * BindVertexArrayOES fails and an INVALID_OPERATION error is
-         * generated if array is not a name returned from a previous call to
-         * GenVertexArraysOES, or if such a name has since been deleted with
-         * DeleteVertexArraysOES
-         */
-        ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
-        return;
-    }
-
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
     if (array == nullptr) {
         array = mDefaultVertexArray;
     }
 
@@ -63,43 +52,28 @@ WebGLVertexArray*
 WebGLContext::CreateVertexArrayImpl()
 {
     return WebGLVertexArray::Create(this);
 }
 
 void
 WebGLContext::DeleteVertexArray(WebGLVertexArray* array)
 {
-    if (IsContextLost())
-        return;
-
-    if (array == nullptr)
-        return;
-
-    if (array->IsDeleted())
+    if (!ValidateDeleteObject("deleteVertexArray", array))
         return;
 
     if (mBoundVertexArray == array)
         BindVertexArray(static_cast<WebGLVertexArray*>(nullptr));
 
     array->RequestDelete();
 }
 
 bool
-WebGLContext::IsVertexArray(WebGLVertexArray* array)
+WebGLContext::IsVertexArray(const WebGLVertexArray* array)
 {
-    if (IsContextLost())
-        return false;
-
-    if (!array)
-        return false;
-
-    if (!ValidateObjectAllowDeleted("isVertexArray", array))
-        return false;
-
-    if (array->IsDeleted())
+    if (!ValidateIsObject("isVertexArray", array))
         return false;
 
     MakeContextCurrent();
     return array->IsVertexArray();
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDebugShaders.cpp
+++ b/dom/canvas/WebGLExtensionDebugShaders.cpp
@@ -33,17 +33,17 @@ WebGLExtensionDebugShaders::GetTranslate
         mContext->ErrorInvalidOperation("%s: Extension is lost.",
                                         "getTranslatedShaderSource");
         return;
     }
 
     if (mContext->IsContextLost())
         return;
 
-    if (!mContext->ValidateObjectRef("getShaderTranslatedSource: shader", shader))
+    if (!mContext->ValidateObject("getShaderTranslatedSource: shader", shader))
         return;
 
     shader.GetShaderTranslatedSource(&retval);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDebugShaders, WEBGL_debug_shaders)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -78,17 +78,17 @@ WebGLExtensionDisjointTimerQuery::EndQue
 
 void
 WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery& query, GLenum target) const
 {
     const char funcName[] = "queryCounterEXT";
     if (mIsLost)
         return;
 
-    if (!mContext->ValidateObjectRef(funcName, query))
+    if (!mContext->ValidateObject(funcName, query))
         return;
 
     query.QueryCounter(funcName, target);
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
                                               JS::MutableHandleValue retval) const
--- a/dom/canvas/WebGLExtensionVertexArray.cpp
+++ b/dom/canvas/WebGLExtensionVertexArray.cpp
@@ -36,17 +36,17 @@ WebGLExtensionVertexArray::DeleteVertexA
 {
     if (mIsLost)
         return;
 
     mContext->DeleteVertexArray(array);
 }
 
 bool
-WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
+WebGLExtensionVertexArray::IsVertexArrayOES(const WebGLVertexArray* array)
 {
     if (mIsLost)
         return false;
 
     return mContext->IsVertexArray(array);
 }
 
 void
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -339,17 +339,17 @@ class WebGLExtensionVertexArray
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionVertexArray(WebGLContext* webgl);
     virtual ~WebGLExtensionVertexArray();
 
     already_AddRefed<WebGLVertexArray> CreateVertexArrayOES();
     void DeleteVertexArrayOES(WebGLVertexArray* array);
-    bool IsVertexArrayOES(WebGLVertexArray* array);
+    bool IsVertexArrayOES(const WebGLVertexArray* array);
     void BindVertexArrayOES(WebGLVertexArray* array);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
 class WebGLExtensionInstancedArrays
     : public WebGLExtensionBase
 {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -599,17 +599,17 @@ WebGLFBAttachPoint::GetParameter(const c
     return JS::Int32Value(ret);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(fbo)
 #ifdef ANDROID
     , mIsFB(false)
 #endif
     , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
 {
@@ -1296,17 +1296,17 @@ WebGLFramebuffer::FramebufferRenderbuffe
 
     // `rbTarget`
     if (rbtarget != LOCAL_GL_RENDERBUFFER) {
         mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: rbtarget:", rbtarget);
         return;
     }
 
     // `rb`
-    if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: rb", rb))
+    if (rb && !mContext->ValidateObject("framebufferRenderbuffer: rb", *rb))
         return;
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
         mDepthAttachment.SetRenderbuffer(rb);
         mStencilAttachment.SetRenderbuffer(rb);
     } else {
@@ -1338,20 +1338,20 @@ WebGLFramebuffer::FramebufferTexture2D(c
          texImageTarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
     {
         mContext->ErrorInvalidEnumInfo("framebufferTexture2D: texImageTarget:",
                                        texImageTarget);
         return;
     }
 
     // `texture`
-    if (!mContext->ValidateObjectAllowNull("framebufferTexture2D: texture", tex))
-        return;
+    if (tex) {
+        if (!mContext->ValidateObject("framebufferTexture2D: texture", *tex))
+            return;
 
-    if (tex) {
         if (!tex->HasEverBeenBound()) {
             mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
                                             funcName);
             return;
         }
 
         const TexTarget destTexTarget = TexImageTargetToTexTarget(texImageTarget);
         if (tex->Target() != destTexTarget) {
@@ -1414,34 +1414,35 @@ WebGLFramebuffer::FramebufferTextureLaye
     // `attachment`
     const auto maybeAttach = GetAttachPoint(attachEnum);
     if (!maybeAttach) {
         mContext->ErrorInvalidEnum("%s: Bad `attachment`: 0x%x.", funcName, attachEnum);
         return;
     }
     const auto& attach = maybeAttach.value();
 
-    // `texture`
-    if (!mContext->ValidateObjectAllowNull("framebufferTextureLayer: texture", tex))
-        return;
-
-    if (tex && !tex->HasEverBeenBound()) {
-        mContext->ErrorInvalidOperation("%s: `texture` has never been bound.", funcName);
-        return;
-    }
-
     // `level`, `layer`
     if (layer < 0)
         return mContext->ErrorInvalidValue("%s: `layer` must be >= 0.", funcName);
 
     if (level < 0)
         return mContext->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
 
+    // `texture`
     TexImageTarget texImageTarget = LOCAL_GL_TEXTURE_3D;
     if (tex) {
+        if (!mContext->ValidateObject("framebufferTextureLayer: texture", *tex))
+            return;
+
+        if (!tex->HasEverBeenBound()) {
+            mContext->ErrorInvalidOperation("%s: `texture` has never been bound.",
+                                            funcName);
+            return;
+        }
+
         texImageTarget = tex->Target().get();
         switch (texImageTarget.get()) {
         case LOCAL_GL_TEXTURE_3D:
             if (uint32_t(layer) >= mContext->mImplMax3DTextureSize) {
                 mContext->ErrorInvalidValue("%s: `layer` must be < %s.", funcName,
                                             "MAX_3D_TEXTURE_SIZE");
                 return;
             }
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -127,17 +127,16 @@ public:
         }
     };
 };
 
 class WebGLFramebuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
-    , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
 {
     friend class WebGLContext;
 
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     const GLuint mGLName;
--- a/dom/canvas/WebGLObjectModel.h
+++ b/dom/canvas/WebGLObjectModel.h
@@ -7,18 +7,65 @@
 #define WEBGLOBJECTMODEL_H_
 
 #include "nsCycleCollectionNoteChild.h"
 
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
+template<typename> class LinkedList;
 class WebGLContext;
 
+////
+
+// This class is a mixin for objects that are tied to a specific
+// context (which is to say, all of them).  They provide initialization
+// as well as comparison with the current context.
+class WebGLContextBoundObject
+{
+public:
+    WebGLContext* const mContext;
+private:
+    const uint32_t mContextGeneration;
+
+public:
+    explicit WebGLContextBoundObject(WebGLContext* webgl);
+
+    bool IsCompatibleWithContext(const WebGLContext* other) const;
+};
+
+////
+
+class WebGLDeletableObject : public WebGLContextBoundObject
+{
+    template<typename> friend class WebGLRefCountedObject;
+
+private:
+    enum DeletionStatus { Default, DeleteRequested, Deleted };
+
+    DeletionStatus mDeletionStatus;
+
+    ////
+
+    explicit WebGLDeletableObject(WebGLContext* webgl)
+      : WebGLContextBoundObject(webgl)
+      , mDeletionStatus(Default)
+    { }
+
+    ~WebGLDeletableObject() {
+        MOZ_ASSERT(mDeletionStatus == Deleted,
+                   "Derived class destructor must call DeleteOnce().");
+    }
+
+public:
+    bool IsDeleted() const { return mDeletionStatus == Deleted; }
+    bool IsDeleteRequested() const { return mDeletionStatus != Default; }
+};
+
 /* Each WebGL object class WebGLFoo wants to:
  *  - inherit WebGLRefCountedObject<WebGLFoo>
  *  - implement a Delete() method
  *  - have its destructor call DeleteOnce()
  *
  * This base class provides two features to WebGL object types:
  * 1. support for OpenGL object reference counting
  * 2. support for OpenGL deletion statuses
@@ -86,32 +133,35 @@ class WebGLContext;
  * derived class were final, but that would be impossible to enforce and would
  * lead to strange bugs if it were subclassed.
  *
  * This WebGLRefCountedObject class takes the Derived type as template
  * parameter, as a means to allow DeleteOnce to call Delete() on the Derived
  * class, without either method being virtual. This is a common C++ pattern
  * known as the "curiously recursive template pattern (CRTP)".
  */
+
 template<typename Derived>
-class WebGLRefCountedObject
+class WebGLRefCountedObject : public WebGLDeletableObject
 {
+    friend class WebGLContext;
+    template<typename T> friend void ClearLinkedList(LinkedList<T>& list);
+
+private:
+    nsAutoRefCnt mWebGLRefCnt;
+
 public:
-    enum DeletionStatus { Default, DeleteRequested, Deleted };
-
-    WebGLRefCountedObject()
-      : mDeletionStatus(Default)
-    {}
+    explicit WebGLRefCountedObject(WebGLContext* webgl)
+        : WebGLDeletableObject(webgl)
+    { }
 
     ~WebGLRefCountedObject() {
         MOZ_ASSERT(mWebGLRefCnt == 0,
                    "Destroying WebGL object still referenced by other WebGL"
                    " objects.");
-        MOZ_ASSERT(mDeletionStatus == Deleted,
-                   "Derived class destructor must call DeleteOnce().");
     }
 
     // called by WebGLRefPtr
     void WebGLAddRef() {
         ++mWebGLRefCnt;
     }
 
     // called by WebGLRefPtr
@@ -124,43 +174,32 @@ public:
 
     // this is the function that WebGL.deleteXxx() functions want to call
     void RequestDelete() {
         if (mDeletionStatus == Default)
             mDeletionStatus = DeleteRequested;
         MaybeDelete();
     }
 
-    bool IsDeleted() const {
-        return mDeletionStatus == Deleted;
-    }
-
-    bool IsDeleteRequested() const {
-        return mDeletionStatus != Default;
-    }
-
+protected:
     void DeleteOnce() {
         if (mDeletionStatus != Deleted) {
             static_cast<Derived*>(this)->Delete();
             mDeletionStatus = Deleted;
         }
     }
 
 private:
     void MaybeDelete() {
         if (mWebGLRefCnt == 0 &&
             mDeletionStatus == DeleteRequested)
         {
             DeleteOnce();
         }
     }
-
-protected:
-    nsAutoRefCnt mWebGLRefCnt;
-    DeletionStatus mDeletionStatus;
 };
 
 /* This WebGLRefPtr class is meant to be used for references between WebGL
  * objects. For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's
  * attached to it.
  *
  * Why the need for a separate refptr class? The only special thing that
  * WebGLRefPtr does is that it increments and decrements the WebGL refcount of
@@ -254,31 +293,16 @@ private:
         mRawPtr = newPtr;
         ReleasePtr(oldPtr);
     }
 
 protected:
     T* mRawPtr;
 };
 
-// This class is a mixin for objects that are tied to a specific
-// context (which is to say, all of them).  They provide initialization
-// as well as comparison with the current context.
-class WebGLContextBoundObject
-{
-public:
-    explicit WebGLContextBoundObject(WebGLContext* webgl);
-
-    bool IsCompatibleWithContext(const WebGLContext* other) const;
-
-    WebGLContext* const mContext;
-protected:
-    const uint32_t mContextGeneration;
-};
-
 // this class is a mixin for GL objects that have dimensions
 // that we need to track.
 class WebGLRectangleObject
 {
 public:
     WebGLRectangleObject()
         : mWidth(0)
         , mHeight(0)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -438,17 +438,17 @@ webgl::LinkedProgramInfo::~LinkedProgram
 static GLuint
 CreateProgram(gl::GLContext* gl)
 {
     gl->MakeCurrent();
     return gl->fCreateProgram();
 }
 
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(CreateProgram(webgl->GL()))
     , mNumActiveTFOs(0)
     , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_SEPARATE_ATTRIBS)
 {
     mContext->mPrograms.insertBack(this);
 }
 
 WebGLProgram::~WebGLProgram()
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -125,17 +125,16 @@ struct LinkedProgramInfo final
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
-    , public WebGLContextBoundObject
 {
     friend class WebGLTransformFeedback;
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLProgram)
 
     explicit WebGLProgram(WebGLContext* webgl);
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -35,17 +35,17 @@ GenQuery(gl::GLContext* gl)
     gl->MakeCurrent();
 
     GLuint ret = 0;
     gl->fGenQueries(1, &ret);
     return ret;
 }
 
 WebGLQuery::WebGLQuery(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(GenQuery(mContext->gl))
     , mTarget(0)
     , mActiveSlot(nullptr)
     , mCanBeAvailable(false)
 {
     mContext->mQueries.insertBack(this);
 }
 
@@ -206,30 +206,28 @@ WebGLQuery::GetQueryParameter(GLenum pna
     default:
         MOZ_CRASH("Bad `pname`.");
     }
 }
 
 bool
 WebGLQuery::IsQuery() const
 {
-    if (IsDeleted())
-        return false;
+    MOZ_ASSERT(!IsDeleted());
 
     if (!mTarget)
         return false;
 
     return true;
 }
 
 void
 WebGLQuery::DeleteQuery()
 {
-    if (IsDeleted())
-        return;
+    MOZ_ASSERT(!IsDeleteRequested());
 
     if (mActiveSlot) {
         EndQuery();
     }
 
     RequestDelete();
 }
 
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -13,17 +13,16 @@
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 class WebGLQuery final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLQuery>
     , public LinkedListElement<WebGLQuery>
-    , public WebGLContextBoundObject
 {
     friend class AvailableRunnable;
     friend class WebGLRefCountedObject<WebGLQuery>;
 
 public:
     const GLuint mGLName;
 private:
     GLenum mTarget;
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -42,17 +42,17 @@ DoCreateRenderbuffer(gl::GLContext* gl)
 
 static bool
 EmulatePackedDepthStencil(gl::GLContext* gl)
 {
     return !gl->IsSupported(gl::GLFeature::packed_depth_stencil);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mPrimaryRB( DoCreateRenderbuffer(webgl->gl) )
     , mEmulatePackedDepthStencil( EmulatePackedDepthStencil(webgl->gl) )
     , mSecondaryRB(0)
     , mFormat(nullptr)
     , mSamples(0)
     , mImageDataStatus(WebGLImageDataStatus::NoImageData)
     , mHasBeenBound(false)
 {
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -18,17 +18,16 @@ namespace webgl {
 struct FormatUsageInfo;
 }
 
 class WebGLRenderbuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
-    , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
     friend class WebGLContext;
     friend class WebGLFramebuffer;
     friend class WebGLFBAttachPoint;
 
 public:
     const GLuint mPrimaryRB;
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSampler::WebGLSampler(WebGLContext* webgl, GLuint sampler)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(sampler)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mWrapR(LOCAL_GL_REPEAT)
     , mMinLod(-1000)
     , mMaxLod(1000)
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -12,17 +12,16 @@
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 class WebGLSampler final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
     friend class WebGLTexture;
 
 public:
     WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
     const GLuint mGLName;
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -136,17 +136,17 @@ GetCompilationStatusAndLog(gl::GLContext
 static GLuint
 CreateShader(gl::GLContext* gl, GLenum type)
 {
     gl->MakeCurrent();
     return gl->fCreateShader(type);
 }
 
 WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(CreateShader(webgl->GL(), type))
     , mType(type)
     , mTranslationSuccessful(false)
     , mCompilationSuccessful(false)
 {
     mContext->mShaders.insertBack(this);
 }
 
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -23,17 +23,16 @@ namespace mozilla {
 namespace webgl {
 class ShaderValidator;
 } // namespace webgl
 
 class WebGLShader final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLShader>
     , public LinkedListElement<WebGLShader>
-    , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGLProgram;
 
 public:
     WebGLShader(WebGLContext* webgl, GLenum type);
 
 protected:
--- a/dom/canvas/WebGLSync.cpp
+++ b/dom/canvas/WebGLSync.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSync::WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
 {
    mContext->mSyncs.insertBack(this);
    mGLName = mContext->gl->fFenceSync(condition, flags);
 }
 
 WebGLSync::~WebGLSync()
 {
     DeleteOnce();
--- a/dom/canvas/WebGLSync.h
+++ b/dom/canvas/WebGLSync.h
@@ -11,17 +11,16 @@
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLSync final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSync>
     , public LinkedListElement<WebGLSync>
-    , public WebGLContextBoundObject
 {
     friend class WebGL2Context;
 
 public:
     WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
 
     void Delete();
     WebGLContext* GetParentObject() const;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -122,17 +122,17 @@ WebGLTexture::ImageInfo::SetIsDataInitia
 ////////////////////////////////////////
 
 JSObject*
 WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
     return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(tex)
     , mTarget(LOCAL_GL_NONE)
     , mFaceCount(0)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mImmutable(false)
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -52,17 +52,16 @@ DoesTargetMatchDimensions(WebGLContext* 
 
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
-    , public WebGLContextBoundObject
 {
     // Friends
     friend class WebGLContext;
     friend class WebGLFramebuffer;
 
     ////////////////////////////////////
     // Members
 public:
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -7,17 +7,17 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGL2Context.h"
 
 namespace mozilla {
 
 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(tf)
     , mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
     , mIsPaused(false)
     , mIsActive(false)
     , mBuffersForTF_Dirty(true)
 {
     mContext->mTransformFeedbacks.insertBack(this);
 }
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -11,17 +11,16 @@
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLTransformFeedback final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
-    , public WebGLContextBoundObject
 {
     friend class ScopedDrawWithTransformFeedback;
     friend class WebGLContext;
     friend class WebGL2Context;
     friend class WebGLProgram;
 
 public:
     const GLuint mGLName;
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -16,17 +16,17 @@ namespace mozilla {
 
 JSObject*
 WebGLVertexArray::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLVertexArrayObjectBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
-    : WebGLContextBoundObject(webgl)
+    : WebGLRefCountedObject(webgl)
     , mGLName(0)
 {
     mContext->mVertexArrays.insertBack(this);
 }
 
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* webgl)
 {
@@ -45,17 +45,17 @@ WebGLVertexArray::Delete()
     DeleteImpl();
 
     LinkedListElement<WebGLVertexArray>::removeFrom(mContext->mVertexArrays);
     mElementArrayBuffer = nullptr;
     mAttribs.Clear();
 }
 
 bool
-WebGLVertexArray::IsVertexArray()
+WebGLVertexArray::IsVertexArray() const
 {
     return IsVertexArrayImpl();
 }
 
 void
 WebGLVertexArray::EnsureAttrib(GLuint index)
 {
     MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs));
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -18,17 +18,16 @@
 namespace mozilla {
 
 class WebGLVertexArrayFake;
 
 class WebGLVertexArray
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
-    , public WebGLContextBoundObject
 {
 public:
     static WebGLVertexArray* Create(WebGLContext* webgl);
 
     void BindVertexArray() {
         // Bind to dummy value to signal that this vertex array has ever been
         // bound.
         BindVertexArrayImpl();
@@ -39,17 +38,17 @@ public:
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) const {
         return HasAttrib(index) && mAttribs[index].mEnabled;
     }
 
     // Implement parent classes:
     void Delete();
-    bool IsVertexArray();
+    bool IsVertexArray() const;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
@@ -62,17 +61,17 @@ protected:
 
     virtual ~WebGLVertexArray() {
         MOZ_ASSERT(IsDeleted());
     }
 
     virtual void GenVertexArray() = 0;
     virtual void BindVertexArrayImpl() = 0;
     virtual void DeleteImpl() = 0;
-    virtual bool IsVertexArrayImpl() = 0;
+    virtual bool IsVertexArrayImpl() const = 0;
 
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
     friend class WebGLContext;
     friend class WebGLVertexArrayFake;
     friend class WebGL2Context;
--- a/dom/canvas/WebGLVertexArrayFake.cpp
+++ b/dom/canvas/WebGLVertexArrayFake.cpp
@@ -57,14 +57,14 @@ WebGLVertexArrayFake::BindVertexArrayImp
 
 void
 WebGLVertexArrayFake::DeleteImpl()
 {
     mIsVAO = false;
 }
 
 bool
-WebGLVertexArrayFake::IsVertexArrayImpl()
+WebGLVertexArrayFake::IsVertexArrayImpl() const
 {
     return mIsVAO;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLVertexArrayFake.h
+++ b/dom/canvas/WebGLVertexArrayFake.h
@@ -14,17 +14,17 @@ class WebGLVertexArrayFake final
     : public WebGLVertexArray
 {
     friend class WebGLVertexArray;
 
 protected:
     virtual void BindVertexArrayImpl() override;
     virtual void DeleteImpl() override;
     virtual void GenVertexArray() override {};
-    virtual bool IsVertexArrayImpl() override;
+    virtual bool IsVertexArrayImpl() const override;
 
 private:
     explicit WebGLVertexArrayFake(WebGLContext* webgl);
 
     ~WebGLVertexArrayFake() {
         DeleteOnce();
     }
 
--- a/dom/canvas/WebGLVertexArrayGL.cpp
+++ b/dom/canvas/WebGLVertexArrayGL.cpp
@@ -42,17 +42,17 @@ WebGLVertexArrayGL::BindVertexArrayImpl(
 
 void
 WebGLVertexArrayGL::GenVertexArray()
 {
     mContext->gl->fGenVertexArrays(1, &mGLName);
 }
 
 bool
-WebGLVertexArrayGL::IsVertexArrayImpl()
+WebGLVertexArrayGL::IsVertexArrayImpl() const
 {
     gl::GLContext* gl = mContext->gl;
     if (gl->WorkAroundDriverBugs())
     {
         return mIsVAO;
     }
 
     mContext->MakeContextCurrent();
--- a/dom/canvas/WebGLVertexArrayGL.h
+++ b/dom/canvas/WebGLVertexArrayGL.h
@@ -14,17 +14,17 @@ class WebGLVertexArrayGL
     : public WebGLVertexArray
 {
     friend class WebGLVertexArray;
 
 public:
     virtual void DeleteImpl() override;
     virtual void BindVertexArrayImpl() override;
     virtual void GenVertexArray() override;
-    virtual bool IsVertexArrayImpl() override;
+    virtual bool IsVertexArrayImpl() const override;
 
 protected:
     explicit WebGLVertexArrayGL(WebGLContext* webgl);
     ~WebGLVertexArrayGL();
 
     // Bug 1140459: Some drivers (including our test slaves!) don't
     // give reasonable answers for IsVertexArray, maybe others.
     //
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -4,17 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += [
     'gtest'
 ]
 
 # Change the following line(s) to avoid bug 1081323 (clobber after changing a manifest):
-# * Implement ReadPixel with PBOs.
+# * Adjust failure errata for webgl-conf.
 
 MOCHITEST_MANIFESTS += [
     'test/crash/mochitest.ini',
     'test/crossorigin/mochitest.ini',
     'test/mochitest.ini',
     'test/webgl-conf/generated-mochitest.ini',
     'test/webgl-mochitest/mochitest.ini',
 ]
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html
@@ -121,33 +121,33 @@ function runSupportedTest(extensionEnabl
             testPassed("OES_vertex_array_object not listed as supported and getExtension failed -- this is legal");
         }
     }
 }
 
 function runBindingTestDisabled() {
     debug("");
     debug("Testing binding enum with extension disabled");
-    
+
     // Use the constant directly as we don't have the extension
     var VERTEX_ARRAY_BINDING_OES = 0x85B5;
-    
+
     gl.getParameter(VERTEX_ARRAY_BINDING_OES);
     wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "VERTEX_ARRAY_BINDING_OES should not be queryable if extension is disabled");
 }
 
 function runBindingTestEnabled() {
     debug("");
     debug("Testing binding enum with extension enabled");
-    
+
     shouldBe("ext.VERTEX_ARRAY_BINDING_OES", "0x85B5");
-    
+
     gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES);
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "VERTEX_ARRAY_BINDING_OES query should succeed if extension is enabled");
-    
+
     // Default value is null
     if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) === null) {
         testPassed("Default value of VERTEX_ARRAY_BINDING_OES is null");
     } else {
         testFailed("Default value of VERTEX_ARRAY_BINDING_OES is not null");
     }
 
     debug("");
@@ -174,88 +174,88 @@ function runBindingTestEnabled() {
     ext.bindVertexArrayOES(null);
     shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
     ext.deleteVertexArrayOES(vao1);
 }
 
 function runObjectTest() {
     debug("");
     debug("Testing object creation");
-    
+
     vao = ext.createVertexArrayOES();
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createVertexArrayOES should not set an error");
     shouldBeNonNull("vao");
-    
+
     // Expect false if never bound
     shouldBeFalse("ext.isVertexArrayOES(vao)");
     ext.bindVertexArrayOES(vao);
     shouldBeTrue("ext.isVertexArrayOES(vao)");
     ext.bindVertexArrayOES(null);
     shouldBeTrue("ext.isVertexArrayOES(vao)");
-    
+
     shouldBeFalse("ext.isVertexArrayOES(null)");
-    
+
     ext.deleteVertexArrayOES(vao);
     vao = null;
 }
 
 function runAttributeTests() {
     debug("");
     debug("Testing attributes work across bindings");
-    
+
     var states = [];
-    
+
     var attrCount = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
     for (var n = 0; n < attrCount; n++) {
         gl.bindBuffer(gl.ARRAY_BUFFER, null);
         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-        
+
         var state = {};
         states.push(state);
-        
+
         var vao = state.vao = ext.createVertexArrayOES();
         ext.bindVertexArrayOES(vao);
-        
+
         var enableArray = (n % 2 == 0);
         if (enableArray) {
             gl.enableVertexAttribArray(n);
         } else {
             gl.disableVertexAttribArray(n);
         }
-        
+
         if (enableArray) {
             var buffer = state.buffer = gl.createBuffer();
             gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
             gl.bufferData(gl.ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
-            
+
             gl.vertexAttribPointer(n, 1 + n % 4, gl.FLOAT, true, n * 4, n * 4);
         }
-        
+
         if (enableArray) {
             var elbuffer = state.elbuffer = gl.createBuffer();
             gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elbuffer);
             gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
         }
-        
+
         ext.bindVertexArrayOES(null);
     }
-    
+
     var anyMismatch = false;
     for (var n = 0; n < attrCount; n++) {
         var state = states[n];
-        
+
         ext.bindVertexArrayOES(state.vao);
-        
+
         var shouldBeEnabled = (n % 2 == 0);
         var isEnabled = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_ENABLED);
         if (shouldBeEnabled != isEnabled) {
             testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved");
             anyMismatch = true;
         }
-        
+
         var buffer = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
         if (shouldBeEnabled) {
             if (buffer == state.buffer) {
                 // Matched
                 if ((gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_SIZE) == 1 + n % 4) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_TYPE) == gl.FLOAT) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) == true) &&
                     (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_STRIDE) == n * 4) &&
@@ -271,17 +271,17 @@ function runAttributeTests() {
             }
         } else {
             // GL_CURRENT_VERTEX_ATTRIB is not preserved
             if (buffer) {
                 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
                 anyMismatch = true;
             }
         }
-        
+
         var elbuffer = gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING);
         if (shouldBeEnabled) {
             if (elbuffer == state.elbuffer) {
                 // Matched
             } else {
                 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
                 anyMismatch = true;
             }
@@ -293,81 +293,81 @@ function runAttributeTests() {
                 anyMismatch = true;
             }
         }
     }
     ext.bindVertexArrayOES(null);
     if (!anyMismatch) {
         testPassed("All attributes preserved across bindings");
     }
-    
+
     for (var n = 0; n < attrCount; n++) {
         var state = states[n];
         ext.deleteVertexArrayOES(state.vao);
     }
 }
 
 function runAttributeValueTests() {
     debug("");
     debug("Testing that attribute values are not attached to bindings");
-    
+
     var v;
     var vao0 = ext.createVertexArrayOES();
     var anyFailed = false;
-    
+
     ext.bindVertexArrayOES(null);
     gl.vertexAttrib4f(0, 0, 1, 2, 3);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
         testFailed("Vertex attrib value not round-tripped?");
         anyFailed = true;
     }
-    
+
     ext.bindVertexArrayOES(vao0);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
         testFailed("Vertex attrib value reset across bindings");
         anyFailed = true;
     }
-    
+
     gl.vertexAttrib4f(0, 4, 5, 6, 7);
     ext.bindVertexArrayOES(null);
-    
+
     v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
     if (!(v[0] == 4 && v[1] == 5 && v[2] == 6 && v[3] == 7)) {
         testFailed("Vertex attrib value bound to buffer");
         anyFailed = true;
     }
-    
+
     if (!anyFailed) {
         testPassed("Vertex attribute values are not attached to bindings")
     }
-    
+
     ext.bindVertexArrayOES(null);
     ext.deleteVertexArrayOES(vao0);
 }
 
 function runDrawTests() {
     debug("");
     debug("Testing draws with various VAO bindings");
-    
+
     canvas.width = 50; canvas.height = 50;
     gl.viewport(0, 0, canvas.width, canvas.height);
-    
+
     var vao0 = ext.createVertexArrayOES();
     var vao1 = ext.createVertexArrayOES();
     var vao2 = ext.createVertexArrayOES();
 
     var positionLocation = 0;
     var colorLocation = 1;
-    
+
     var program = wtu.setupSimpleVertexColorProgram(gl, positionLocation, colorLocation);
-    
+
     function setupQuad(s, colorsInArray) {
         var vertexObject = gl.createBuffer();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
              1.0 * s,  1.0 * s, 0.0,
             -1.0 * s,  1.0 * s, 0.0,
             -1.0 * s, -1.0 * s, 0.0,
              1.0 * s,  1.0 * s, 0.0,
@@ -388,17 +388,17 @@ function runDrawTests() {
                 0.0, 0.0, 0.0, 1.0,
                 0.0, 0.0, 0.0, 1.0]), gl.STATIC_DRAW);
             gl.enableVertexAttribArray(colorLocation);
             gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
         } else {
             gl.disableVertexAttribArray(colorLocation);
         }
     };
-    
+
     function verifyDiagonalPixels(s, expectedInside, drawDescription) {
         // Tests pixels along a diagonal running from the center of the canvas to the (0, 0) corner.
         // Values on the points list indicate relative position along this diagonal.
         var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
         for (var n = 0; n < points.length; n++) {
             var expected = points[n] <= s ? expectedInside : 255;
             var x = Math.round((1 - points[n]) * canvas.width / 2);
             var y = Math.round((1 - points[n]) * canvas.height / 2);
@@ -406,28 +406,28 @@ function runDrawTests() {
                 "Drawing " + drawDescription + " should pass", 2);
         }
     };
     function verifyDraw(drawDescription, s, colorsInArray) {
         wtu.clearAndDrawUnitQuad(gl);
         var expectedInside = colorsInArray ? 0 : 128;
         verifyDiagonalPixels(s, expectedInside, drawDescription);
     };
-    
+
     // Setup all bindings
     setupQuad(1, true);
     ext.bindVertexArrayOES(vao0);
     setupQuad(0.5, true);
     ext.bindVertexArrayOES(vao1);
     setupQuad(0.25, true);
     ext.bindVertexArrayOES(vao2);
     setupQuad(0.75, false);
 
     gl.vertexAttrib4f(colorLocation, 0.5, 0.5, 0.5, 1);
-    
+
     // Verify drawing
     ext.bindVertexArrayOES(null);
     verifyDraw("with the default VAO", 1, true);
     ext.bindVertexArrayOES(vao0);
     verifyDraw("with VAO #0", 0.5, true);
     ext.bindVertexArrayOES(vao1);
     verifyDraw("with VAO #1", 0.25, true);
     ext.bindVertexArrayOES(vao2);
@@ -619,35 +619,41 @@ function runBoundDeleteTests() {
 
     // delete the color buffers AND the position buffer, that are bound to the current VAO
     for (var ii = 0; ii < vaos.length; ++ii) {
         ext.bindVertexArrayOES(vaos[ii]);
 
         gl.deleteBuffer(colorBuffer);
         gl.deleteBuffer(positionBuffer);
 
-        // The buffers should not be accessible at this point. Deleted objects that are bound
-        // in the current context undergo an automatic unbinding
+        var expectRetained = (ii != 0);
+        var shouldBeStr = (expectRetained ? "retained" : "cleared");
+
         var boundPositionBuffer = gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
-        if(boundPositionBuffer == positionBuffer) {
-            testFailed("Position buffer should be automatically unbound when deleted");
+        if (expectRetained != (boundPositionBuffer == positionBuffer)) {
+            testFailed("Position attrib stored buffer should be " + shouldBeStr + ".");
         }
+
         var boundColorBuffer = gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
-        if(boundColorBuffer == colorBuffer) {
-            testFailed("Color buffer should be automatically unbound when deleted");
+        if (expectRetained != (boundColorBuffer == colorBuffer)) {
+            testFailed("Color attrib stored buffer should be " + shouldBeStr + ".");
         }
 
+        // If retained, everything should still work. If cleared, drawing should now fail.
         gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
-        wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Draw call should fail with unbound position and color buffers");
+        var expectedError = (expectRetained ? gl.NO_ERROR : gl.INVALID_OPERATION);
+        wtu.glErrorShouldBe(gl, expectedError,
+                            "Draw call should " + (expectRetained ? "not " : "") + "fail.");
 
-        var isPositionBuffer = gl.isBuffer(positionBuffer);
-        var isColorBuffer    = gl.isBuffer(colorBuffer);
-
-        if(isPositionBuffer)  testFailed("Position buffer should no longer exist after last ref removed");
-        if(isColorBuffer)     testFailed("Color buffer should no longer exist after last ref removed");
+        if (!gl.isBuffer(positionBuffer)) {
+            testFailed("Position buffer should count for isBuffer.");
+        }
+        if (!gl.isBuffer(colorBuffer)) {
+            testFailed("Color buffer should count for isBuffer.");
+        }
     }
 }
 
 function runArrayBufferBindTests() {
     debug("");
     debug("Testing that buffer bindings on VAOs don't affect default VAO ARRAY_BUFFER binding.");
 
     ext.bindVertexArrayOES(null);
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -6023,17 +6023,16 @@ fail-if = (os == 'android')
 [generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
 skip-if = (os == 'win' && os_version == '6.1')
 fail-if = (os == 'android')
 [generated/test_conformance__extensions__oes-texture-half-float.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__extensions__oes-vertex-array-object-bufferData.html]
 [generated/test_conformance__extensions__oes-vertex-array-object.html]
 skip-if = (os == 'mac' && os_version == '10.6')
-fail-if = (os == 'win') || (os == 'mac') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__extensions__webgl-compressed-texture-atc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-pvrtc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
 [generated/test_conformance__extensions__webgl-compressed-texture-size-limit.html]
 skip-if = (os == 'win')
 [generated/test_conformance__extensions__webgl-debug-renderer-info.html]
 [generated/test_conformance__extensions__webgl-debug-shaders.html]
 [generated/test_conformance__extensions__webgl-depth-texture.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -124,17 +124,16 @@ skip-if = (os == 'android') || (os == 'b
 fail-if = (os == 'android')
 # void mozilla::gl::GLContext::fDetachShader(GLuint, GLuint): Generated unexpected GL_INVALID_VALUE error. (0x0501)
 skip-if = (os == 'android' && debug)
 
 [generated/test_conformance__extensions__oes-vertex-array-object.html]
 # 10.6 crash:
 # PROCESS-CRASH | dom/canvas/test/webgl-conf/generated/test_conformance__extensions__oes-vertex-array-object.html | application crashed [@ gleRunVertexSubmitImmediate + 0xf24]
 skip-if = (os == 'mac' && os_version == '10.6')
-fail-if = (os == 'win') || (os == 'mac') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__textures__misc__texture-size.html]
 # application crashed [@ mozilla::gl::GLContext::AfterGLCall]
 skip-if = (os == 'android') || (os == 'win')
 
 [generated/test_2_conformance__textures__misc__cube-incomplete-fbo.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'win')
 [generated/test_2_conformance__extensions__webgl-compressed-texture-s3tc.html]
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -195,17 +195,18 @@ public:
     }
 
     RefPtr<MediaStreamTrack> track =
       mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
     NS_ASSERTION(track, "Owned MediaStreamTracks must be known by the DOMMediaStream");
     if (track) {
       LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.",
                             mStream, track.get()));
-      track->OverrideEnded();
+      NS_DispatchToMainThread(NewRunnableMethod(
+        track, &MediaStreamTrack::OverrideEnded));
     }
   }
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
@@ -265,17 +266,18 @@ public:
   void DoNotifyFinished()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!mStream) {
       return;
     }
 
-    mStream->NotifyFinished();
+    NS_DispatchToMainThread(NewRunnableMethod(
+      mStream, &DOMMediaStream::NotifyFinished));
   }
 
   // The methods below are called on the MediaStreamGraph thread.
 
   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
   {
     nsCOMPtr<nsIRunnable> runnable =
       NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation);
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1225,20 +1225,20 @@ public:
         new MediaOperationTask(MEDIA_START, mListener, domStream,
                                tracksAvailableCallback,
                                mAudioDevice, mVideoDevice,
                                false, mWindowID, mOnFailure.forget());
     MediaManager::PostTask(mediaOperation.forget());
     // We won't need mOnFailure now.
     mOnFailure = nullptr;
 
-    if (!MediaManager::IsPrivateBrowsing(window)) {
+    if (!OriginAttributes::IsPrivateBrowsing(mOrigin)) {
       // Call GetOriginKey again, this time w/persist = true, to promote
       // deviceIds to persistent, in case they're not already. Fire'n'forget.
-      RefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, false, true);
+      RefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, true);
     }
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   MediaStreamConstraints mConstraints;
@@ -1929,23 +1929,16 @@ MediaManager::NotifyRecordingStatusChang
                                                                    requestURL,
                                                                    aIsAudio,
                                                                    aIsVideo);
   }
 
   return NS_OK;
 }
 
-bool MediaManager::IsPrivateBrowsing(nsPIDOMWindowInner* window)
-{
-  nsCOMPtr<nsIDocument> doc = window->GetDoc();
-  nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
-  return loadContext && loadContext->UsePrivateBrowsing();
-}
-
 int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
 {
   bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
   MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
     RefPtr<MediaManager> manager = MediaManager_GetInstance();
     manager->GetBackend(0)->AddDeviceChangeCallback(manager);
     if (fakeDeviceChangeEventOn)
       manager->GetBackend(0)->SetFakeDeviceChangeEvents();
@@ -2492,33 +2485,31 @@ MediaManager::EnumerateDevicesImpl(uint6
   RefPtr<PledgeSourceSet> pledge = new PledgeSourceSet();
   uint32_t id = mOutstandingPledges.Append(*pledge);
 
   // To get a device list anonymized for a particular origin, we must:
   // 1. Get an origin-key (for either regular or private browsing)
   // 2. Get the raw devices list
   // 3. Anonymize the raw list with the origin-key.
 
-  bool privateBrowsing = IsPrivateBrowsing(window);
   nsCOMPtr<nsIPrincipal> principal =
     nsGlobalWindow::Cast(window)->GetPrincipal();
   MOZ_ASSERT(principal);
 
   nsAutoCString origin;
   principal->GetOrigin(origin);
 
   bool persist = IsActivelyCapturingOrHasAPermission(aWindowId);
 
   // GetOriginKey is an async API that returns a pledge (a promise-like
   // pattern). We use .Then() to pass in a lambda to run back on this same
   // thread later once GetOriginKey resolves. Needed variables are "captured"
   // (passed by value) safely into the lambda.
 
-  RefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing,
-                                                      persist);
+  RefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, persist);
   p->Then([id, aWindowId, aVideoType, aAudioType,
            aFake](const nsCString& aOriginKey) mutable {
     MOZ_ASSERT(NS_IsMainThread());
     RefPtr<MediaManager> mgr = MediaManager_GetInstance();
 
     RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aVideoType,
                                                          aAudioType, aFake);
     p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -252,17 +252,16 @@ public:
 
   nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow, dom::Promise& aPromise);
   void OnNavigation(uint64_t aWindowID);
   bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<RefPtr<MediaDevice>> SourceSet;
-  static bool IsPrivateBrowsing(nsPIDOMWindowInner* window);
 
   virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
   virtual void OnDeviceChange() override;
 private:
   typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
   typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
   typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
 
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -90,18 +90,17 @@ public:
   {}
 
   // Main thread only.
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   virtual void Init(PromiseId aPromiseId,
                     const nsAString& aOrigin,
                     const nsAString& aTopLevelOrigin,
-                    const nsAString& aName,
-                    bool aInPrivateBrowsing) = 0;
+                    const nsAString& aName) = 0;
 
   virtual void OnSetDecryptorId(uint32_t aId) {}
 
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   virtual void CreateSession(uint32_t aCreateSessionToken,
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -404,41 +404,36 @@ MediaKeys::Init(ErrorResult& aRv)
   nsAutoCString topLevelOrigin;
   rv = mTopLevelPrincipal->GetOrigin(topLevelOrigin);
   if (NS_FAILED(rv)) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                          NS_LITERAL_CSTRING("Couldn't get top-level principal origin string in MediaKeys::Init"));
     return promise.forget();
   }
 
-  nsIDocument* doc = window->GetExtantDoc();
-  const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
-
-  EME_LOG("MediaKeys[%p]::Create() (%s, %s), %s",
+  EME_LOG("MediaKeys[%p]::Create() (%s, %s)",
           this,
           origin.get(),
-          topLevelOrigin.get(),
-          (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
+          topLevelOrigin.get());
 
   // The CDMProxy's initialization is asynchronous. The MediaKeys is
   // refcounted, and its instance is returned to JS by promise once
   // it's been initialized. No external refs exist to the MediaKeys while
   // we're waiting for the promise to be resolved, so we must hold a
   // reference to the new MediaKeys object until it's been created,
   // or its creation has failed. Store the id of the promise returned
   // here, and hold a self-reference until that promise is resolved or
   // rejected.
   MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
   mCreatePromiseId = StorePromise(promise);
   AddRef();
   mProxy->Init(mCreatePromiseId,
                NS_ConvertUTF8toUTF16(origin),
                NS_ConvertUTF8toUTF16(topLevelOrigin),
-               KeySystemToGMPName(mKeySystem),
-               inPrivateBrowsing);
+               KeySystemToGMPName(mKeySystem));
 
   return promise.forget();
 }
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const uint32_t aPluginId)
 {
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
@@ -41,26 +41,24 @@ MediaDrmCDMProxy::~MediaDrmCDMProxy()
 {
   MOZ_COUNT_DTOR(MediaDrmCDMProxy);
 }
 
 void
 MediaDrmCDMProxy::Init(PromiseId aPromiseId,
                        const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
-                       const nsAString& aName,
-                       bool aInPrivateBrowsing)
+                       const nsAString& aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   EME_LOG("MediaDrmCDMProxy::Init (%s, %s) %s",
           NS_ConvertUTF16toUTF8(aOrigin).get(),
-          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
-          (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
+          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get());
 
   // Create a thread to work with cdm.
   if (!mOwnerThread) {
     nsresult rv = NS_NewNamedThread("MDCDMThread", getter_AddRefs(mOwnerThread));
     if (NS_FAILED(rv)) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                     NS_LITERAL_CSTRING("Couldn't create CDM thread MediaDrmCDMProxy::Init"));
       return;
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
@@ -32,18 +32,17 @@ public:
   MediaDrmCDMProxy(dom::MediaKeys* aKeys,
                    const nsAString& aKeySystem,
                    bool aDistinctiveIdentifierRequired,
                    bool aPersistentStateRequired);
 
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
-            const nsAString& aGMPName,
-            bool aInPrivateBrowsing) override;
+            const nsAString& aGMPName) override;
 
   void CreateSession(uint32_t aCreateSessionToken,
                      MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -49,26 +49,24 @@ GMPCDMProxy::~GMPCDMProxy()
 {
   MOZ_COUNT_DTOR(GMPCDMProxy);
 }
 
 void
 GMPCDMProxy::Init(PromiseId aPromiseId,
                   const nsAString& aOrigin,
                   const nsAString& aTopLevelOrigin,
-                  const nsAString& aGMPName,
-                  bool aInPrivateBrowsing)
+                  const nsAString& aGMPName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
-  EME_LOG("GMPCDMProxy::Init (%s, %s) %s",
+  EME_LOG("GMPCDMProxy::Init (%s, %s)",
           NS_ConvertUTF16toUTF8(aOrigin).get(),
-          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
-          (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
+          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get());
 
   nsCString pluginVersion;
   if (!mOwnerThread) {
     nsCOMPtr<mozIGeckoMediaPluginService> mps =
       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     if (!mps) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                     NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::Init"));
@@ -88,17 +86,16 @@ GMPCDMProxy::Init(PromiseId aPromiseId,
     return;
   }
 
   UniquePtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
   data->mGMPName = aGMPName;
-  data->mInPrivateBrowsing = aInPrivateBrowsing;
   data->mCrashHelper = mCrashHelper;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<UniquePtr<InitData>&&>(this,
                                              &GMPCDMProxy::gmp_Init,
                                              Move(data)));
   mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
@@ -205,17 +202,16 @@ GMPCDMProxy::gmp_Init(UniquePtr<InitData
   // Make a copy before we transfer ownership of aData to the
   // gmp_InitGetGMPDecryptorCallback.
   InitData data(*aData);
   UniquePtr<GetNodeIdCallback> callback(
     new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
   nsresult rv = mps->GetNodeId(data.mOrigin,
                                data.mTopLevelOrigin,
                                data.mGMPName,
-                               data.mInPrivateBrowsing,
                                Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
   }
 }
 
 void
@@ -236,20 +232,19 @@ GMPCDMProxy::gmp_InitGetGMPDecryptor(nsr
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_InitGetGMPDecryptor"));
     return;
   }
 
-  EME_LOG("GMPCDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
+  EME_LOG("GMPCDMProxy::gmp_Init (%s, %s) NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
-          (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
   // Note: must capture helper refptr here, before the Move()
   // when we create the GetGMPDecryptorCallback below.
   RefPtr<GMPCrashHelper> crashHelper = Move(aData->mCrashHelper);
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -26,18 +26,17 @@ public:
               const nsAString& aKeySystem,
               GMPCrashHelper* aCrashHelper,
               bool aDistinctiveIdentifierRequired,
               bool aPersistentStateRequired);
 
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
-            const nsAString& aGMPName,
-            bool aInPrivateBrowsing) override;
+            const nsAString& aGMPName) override;
 
   void OnSetDecryptorId(uint32_t aId) override;
 
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
@@ -118,17 +117,16 @@ private:
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsString mOrigin;
     nsString mTopLevelOrigin;
     nsString mGMPName;
     RefPtr<GMPCrashHelper> mCrashHelper;
-    bool mInPrivateBrowsing;
   };
 
   // GMP thread only.
   void gmp_Init(UniquePtr<InitData>&& aData);
   void gmp_InitDone(GMPDecryptorProxy* aCDM, UniquePtr<InitData>&& aData);
   void gmp_InitGetGMPDecryptor(nsresult aResult,
                                const nsACString& aNodeId,
                                UniquePtr<InitData>&& aData);
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -230,34 +230,31 @@ GeckoMediaPluginServiceChild::HasPluginF
   *aHasPlugin = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
                                         const nsAString& aTopLevelOrigin,
                                         const nsAString& aGMPName,
-                                        bool aInPrivateBrowsing,
                                         UniquePtr<GetNodeIdCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   GetNodeIdCallback* rawCallback = aCallback.release();
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   nsString origin(aOrigin);
   nsString topLevelOrigin(aTopLevelOrigin);
   nsString gmpName(aGMPName);
-  bool pb = aInPrivateBrowsing;
   GetServiceChild()->Then(thread, __func__,
-    [rawCallback, origin, topLevelOrigin, gmpName, pb](GMPServiceChild* child) {
+    [rawCallback, origin, topLevelOrigin, gmpName](GMPServiceChild* child) {
       UniquePtr<GetNodeIdCallback> callback(rawCallback);
       nsCString outId;
       if (!child->SendGetGMPNodeId(origin, topLevelOrigin,
-                                   gmpName,
-                                   pb, &outId)) {
+                                   gmpName, &outId)) {
         callback->Done(NS_ERROR_FAILURE, EmptyCString());
         return;
       }
 
       callback->Done(NS_OK, outId);
     },
     [rawCallback](nsresult rv) {
       UniquePtr<GetNodeIdCallback> callback(rawCallback);
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -27,17 +27,16 @@ public:
   static already_AddRefed<GeckoMediaPluginServiceChild> GetSingleton();
 
   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
                              nsTArray<nsCString>* aTags,
                              bool *aRetVal) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
                        const nsAString& aGMPName,
-                       bool aInPrivateBrowsingMode,
                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
 
   NS_DECL_NSIOBSERVER
 
   void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
 
   void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1383,24 +1383,22 @@ GeckoMediaPluginServiceParent::IsPersist
                  mPersistentStorageAllowed.Get(aNodeId);
   return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
                                          const nsAString& aTopLevelOrigin,
                                          const nsAString& aGMPName,
-                                         bool aInPrivateBrowsing,
                                          nsACString& aOutId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
+  LOGD(("%s::%s: (%s, %s)", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
-       NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
-       (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
+       NS_ConvertUTF16toUTF8(aTopLevelOrigin).get()));
 
   nsresult rv;
 
   if (aOrigin.EqualsLiteral("null") ||
       aOrigin.IsEmpty() ||
       aTopLevelOrigin.EqualsLiteral("null") ||
       aTopLevelOrigin.IsEmpty()) {
     // (origin, topLevelOrigin) is null or empty; this is for an anonymous
@@ -1416,17 +1414,17 @@ GeckoMediaPluginServiceParent::GetNodeId
     aOutId = salt;
     mPersistentStorageAllowed.Put(salt, false);
     return NS_OK;
   }
 
   const uint32_t hash = AddToHash(HashString(aOrigin),
                                   HashString(aTopLevelOrigin));
 
-  if (aInPrivateBrowsing) {
+  if (OriginAttributes::IsPrivateBrowsing(NS_ConvertUTF16toUTF8(aOrigin))) {
     // For PB mode, we store the node id, indexed by the origin pair and GMP name,
     // so that if the same origin pair is opened for the same GMP in this session,
     // it gets the same node id.
     const uint32_t pbHash = AddToHash(HashString(aGMPName), hash);
     nsCString* salt = nullptr;
     if (!(salt = mTempNodeIds.Get(pbHash))) {
       // No salt stored, generate and temporarily store some for this id.
       nsAutoCString newSalt;
@@ -1546,21 +1544,20 @@ GeckoMediaPluginServiceParent::GetNodeId
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
                                          const nsAString& aTopLevelOrigin,
                                          const nsAString& aGMPName,
-                                         bool aInPrivateBrowsing,
                                          UniquePtr<GetNodeIdCallback>&& aCallback)
 {
   nsCString nodeId;
-  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, nodeId);
+  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, nodeId);
   aCallback->Done(rv, nodeId);
   return rv;
 }
 
 static bool
 ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
 {
   nsCString str;
@@ -2005,21 +2002,19 @@ GMPServiceParent::RecvLaunchGMP(const ns
   *aOutRv = NS_OK;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
                                    const nsString& aTopLevelOrigin,
                                    const nsString& aGMPName,
-                                   const bool& aInPrivateBrowsing,
                                    nsCString* aID)
 {
-  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName,
-                                    aInPrivateBrowsing, *aID);
+  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, *aID);
   if (!NS_SUCCEEDED(rv)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 class DeleteGMPServiceParent : public mozilla::Runnable
 {
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -39,17 +39,16 @@ public:
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
                              nsTArray<nsCString>* aTags,
                              bool *aRetVal) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
                        const nsAString& aGMPName,
-                       bool aInPrivateBrowsingMode,
                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
 
   NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
   NS_DECL_NSIOBSERVER
 
   void AsyncShutdownNeeded(GMPParent* aParent);
   void AsyncShutdownComplete(GMPParent* aParent);
 
@@ -85,18 +84,17 @@ private:
                                                  const nsTArray<nsCString>& aTags);
 
   already_AddRefed<GMPParent> FindPluginForAPIFrom(size_t aSearchStartIndex,
                                                    const nsCString& aAPI,
                                                    const nsTArray<nsCString>& aTags,
                                                    size_t* aOutPluginIndex);
 
   nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
-                     const nsAString& aGMPName,
-                     bool aInPrivateBrowsing, nsACString& aOutId);
+                     const nsAString& aGMPName, nsACString& aOutId);
 
   void UnloadPlugins();
   void CrashPlugins();
   void NotifySyncShutdownComplete();
   void NotifyAsyncShutdownComplete();
 
   void ProcessPossiblePlugin(nsIFile* aDir);
 
@@ -245,17 +243,16 @@ public:
   {
     mService->ServiceUserCreated();
   }
   virtual ~GMPServiceParent();
 
   mozilla::ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
                                            const nsString& aTopLevelOrigin,
                                            const nsString& aGMPName,
-                                           const bool& aInPrivateBrowsing,
                                            nsCString* aID) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static PGMPServiceParent* Create(Transport* aTransport, ProcessId aOtherPid);
 
   mozilla::ipc::IPCResult RecvLaunchGMP(const nsCString& aNodeId,
                                         const nsCString& aAPI,
                                         nsTArray<nsCString>&& aTags,
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -17,16 +17,14 @@ sync protocol PGMPService
 parent:
 
   sync LaunchGMP(nsCString nodeId,
                  nsCString api,
                  nsCString[] tags,
                  ProcessId[] alreadyBridgedTo)
     returns (uint32_t pluginId, ProcessId id, nsCString displayName, nsresult aResult);
 
-  sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
-                    nsString gmpName,
-                    bool inPrivateBrowsing)
+  sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName)
     returns (nsCString id);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -156,17 +156,16 @@ interface mozIGeckoMediaPluginService : 
    */
   [noscript]
   void getGMPDecryptor(in GMPCrashHelperPtr helper,
                        in TagArray tags,
                        in ACString nodeId,
                        in GetGMPDecryptorCallback callback);
 
   /**
-   * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
+   * Gets the NodeId for a (origin, urlbarOrigin) pair.
    */
   [noscript]
   void getNodeId(in AString origin,
                  in AString topLevelOrigin,
                  in AString gmpName,
-                 in bool inPrivateBrowsingMode,
                  in GetNodeIdCallback callback);
 };
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -468,22 +468,36 @@ GetNodeId(const nsAString& aOrigin,
 {
   RefPtr<GeckoMediaPluginServiceParent> service =
     GeckoMediaPluginServiceParent::GetSingleton();
   EXPECT_TRUE(service);
   nsCString nodeId;
   nsresult result;
   UniquePtr<GetNodeIdCallback> callback(new TestGetNodeIdCallback(nodeId,
                                                                   result));
+
+  PrincipalOriginAttributes attrs;
+  attrs.mPrivateBrowsingId = aInPBMode ? 1 : 0;
+
+  nsAutoCString suffix;
+  attrs.CreateSuffix(suffix);
+
+  nsAutoString origin;
+  origin.Assign(aOrigin);
+  origin.Append(NS_ConvertUTF8toUTF16(suffix));
+
+  nsAutoString topLevelOrigin;
+  topLevelOrigin.Assign(aTopLevelOrigin);
+  topLevelOrigin.Append(NS_ConvertUTF8toUTF16(suffix));
+
   // We rely on the fact that the GetNodeId implementation for
   // GeckoMediaPluginServiceParent is synchronous.
-  nsresult rv = service->GetNodeId(aOrigin,
-                                   aTopLevelOrigin,
+  nsresult rv = service->GetNodeId(origin,
+                                   topLevelOrigin,
                                    NS_LITERAL_STRING("gmp-fake"),
-                                   aInPBMode,
                                    Move(callback));
   EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result));
   return nodeId;
 }
 
 static bool
 IsGMPStorageIsEmpty()
 {
--- a/dom/media/systemservices/MediaChild.cpp
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -15,29 +15,28 @@
 #undef LOG
 mozilla::LazyLogModule gMediaChildLog("MediaChild");
 #define LOG(args) MOZ_LOG(gMediaChildLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace media {
 
 already_AddRefed<Pledge<nsCString>>
-GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist)
+GetOriginKey(const nsCString& aOrigin, bool aPersist)
 {
   RefPtr<MediaManager> mgr = MediaManager::GetInstance();
   MOZ_ASSERT(mgr);
 
   RefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
   uint32_t id = mgr->mGetOriginKeyPledges.Append(*p);
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing,
-                                              aPersist);
+    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPersist);
   } else {
-    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing, aPersist);
+    Child::Get()->SendGetOriginKey(id, aOrigin, aPersist);
   }
   return p.forget();
 }
 
 void
 SanitizeOriginKeys(const uint64_t& aSinceWhen, bool aOnlyPrivateBrowsing)
 {
   LOG(("SanitizeOriginKeys since %llu %s", aSinceWhen,
--- a/dom/media/systemservices/MediaChild.h
+++ b/dom/media/systemservices/MediaChild.h
@@ -20,17 +20,17 @@ namespace media {
 //
 // GetOriginKey() - get a cookie-like persisted unique key for a given origin.
 // SanitizeOriginKeys() - reset persisted unique keys.
 
 // GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
 // (promise-like objects) with the future value. Use pledge.Then(func) to access.
 
 already_AddRefed<Pledge<nsCString>>
-GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist);
+GetOriginKey(const nsCString& aOrigin, bool aPersist);
 
 void
 SanitizeOriginKeys(const uint64_t& aSinceWhen, bool aOnlyPrivateBrowsing);
 
 class Child : public PMediaChild
 {
 public:
   static Child* Get();
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -364,17 +364,16 @@ bool NonE10s::SendGetOriginKeyResponse(c
     pledge->Resolve(aKey);
   }
   return true;
 }
 
 template<class Super> mozilla::ipc::IPCResult
 Parent<Super>::RecvGetOriginKey(const uint32_t& aRequestId,
                                 const nsCString& aOrigin,
-                                const bool& aPrivateBrowsing,
                                 const bool& aPersist)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // First, get profile dir.
 
   MOZ_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIFile> profileDir;
@@ -390,21 +389,22 @@ Parent<Super>::RecvGetOriginKey(const ui
   RefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
   uint32_t id = mOutstandingPledges.Append(*p);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(sts);
   RefPtr<Parent<Super>> that(this);
 
   rv = sts->Dispatch(NewRunnableFrom([this, that, id, profileDir, aOrigin,
-                                      aPrivateBrowsing, aPersist]() -> nsresult {
+                                      aPersist]() -> nsresult {
     MOZ_ASSERT(!NS_IsMainThread());
     mOriginKeyStore->mOriginKeys.SetProfileDir(profileDir);
-    nsCString result;
-    if (aPrivateBrowsing) {
+
+    nsAutoCString result;
+    if (OriginAttributes::IsPrivateBrowsing(aOrigin)) {
       mOriginKeyStore->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
     } else {
       mOriginKeyStore->mOriginKeys.GetOriginKey(aOrigin, result, aPersist);
     }
 
     // Pass result back to main thread.
     nsresult rv;
     rv = NS_DispatchToMainThread(NewRunnableFrom([this, that, id,
--- a/dom/media/systemservices/MediaParent.h
+++ b/dom/media/systemservices/MediaParent.h
@@ -24,17 +24,16 @@ class NonE10s
 {
   typedef mozilla::ipc::IProtocol::ActorDestroyReason
       ActorDestroyReason;
 public:
   virtual ~NonE10s() {}
 protected:
   virtual mozilla::ipc::IPCResult RecvGetOriginKey(const uint32_t& aRequestId,
                                                    const nsCString& aOrigin,
-                                                   const bool& aPrivateBrowsing,
                                                    const bool& aPersist) = 0;
   virtual mozilla::ipc::IPCResult RecvSanitizeOriginKeys(const uint64_t& aSinceWhen,
                                                          const bool& aOnlyPrivateBrowsing) = 0;
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) = 0;
 
   bool SendGetOriginKeyResponse(const uint32_t& aRequestId,
                                 nsCString aKey);
@@ -47,17 +46,16 @@ class Parent : public Super
 {
   typedef mozilla::ipc::IProtocol::ActorDestroyReason
       ActorDestroyReason;
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Parent<Super>)
 
   virtual mozilla::ipc::IPCResult RecvGetOriginKey(const uint32_t& aRequestId,
                                                    const nsCString& aOrigin,
-                                                   const bool& aPrivateBrowsing,
                                                    const bool& aPersist) override;
   virtual mozilla::ipc::IPCResult RecvSanitizeOriginKeys(const uint64_t& aSinceWhen,
                                                          const bool& aOnlyPrivateBrowsing) override;
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   Parent();
 private:
   virtual ~Parent();
--- a/dom/media/systemservices/PMedia.ipdl
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -13,28 +13,29 @@ protocol PMedia
 
 parent:
   /**
    * Requests a potentially persistent unique secret key for each origin.
    * Has no expiry, but is cleared by age along with cookies.
    * This is needed by mediaDevices.enumerateDevices() to produce persistent
    * deviceIds that wont work cross-origin.
    *
-   * If aPrivateBrowsing is false, a key for this origin is returned from a
-   * primary pool of temporal in-memory keys and persistent keys read from disk.
-   * If no key exists, a temporal one is created.
+   * The origin string must contain the OriginAttributes suffix.
+   * If this OriginAttributes dictionary has the privateBrowsing flag set to
+   * false, a key for this origin is returned from a primary pool of temporal
+   * in-memory keys and persistent keys read from disk. If no key exists, a
+   * temporal one is created.
    * If aPersist is true and key is temporal, the key is promoted to persistent.
    * Once persistent, a key cannot become temporal again.
    *
-   * If aPrivateBrowsing is true, a different key for this origin is returned
-   * from a secondary pool that is never persisted to disk, and aPersist is
-   * ignored.
+   * If the OriginAttributes dictionary has the privateBrowsing flag set to
+   * true, a different key for this origin is returned from a secondary pool
+   * that is never persisted to disk, and aPersist is ignored.
    */
-  async GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing,
-                     bool aPersist);
+  async GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPersist);
 
   /**
    * Clear per-orgin list of persistent deviceIds stored for enumerateDevices
    * Fire and forget.
    *
    * aSinceTime - milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all
    *
    * aOnlyPrivateBrowsing - if true then only purge the separate in-memory
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -324,39 +324,43 @@ MediaEngineTabVideoSource::Draw() {
       return;
     }
     presShell = presContext->PresShell();
   }
 
   RefPtr<layers::ImageContainer> container =
     layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS);
   RefPtr<DrawTarget> dt =
-    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+    Factory::CreateDrawTargetForData(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
                                      mData.get(),
                                      size,
                                      stride,
-                                     SurfaceFormat::B8G8R8X8);
+                                     SurfaceFormat::B8G8R8X8,
+                                     true);
   if (!dt || !dt->IsValid()) {
     return;
   }
-  RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
-  MOZ_ASSERT(context); // already checked the draw target above
-  context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth),
-                                                    (((float) size.height)/mViewportHeight)));
 
   if (mWindow) {
+    RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+    MOZ_ASSERT(context); // already checked the draw target above
+    context->SetMatrix(context->CurrentMatrix().Scale((((float) size.width)/mViewportWidth),
+                                                      (((float) size.height)/mViewportHeight)));
+
     nscolor bgColor = NS_RGB(255, 255, 255);
     uint32_t renderDocFlags = mScrollWithPage? 0 :
       (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
        nsIPresShell::RENDER_DOCUMENT_RELATIVE);
     nsRect r(nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetX),
              nsPresContext::CSSPixelsToAppUnits((float)mViewportOffsetY),
              nsPresContext::CSSPixelsToAppUnits((float)mViewportWidth),
              nsPresContext::CSSPixelsToAppUnits((float)mViewportHeight));
     NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
+  } else {
+    dt->ClearRect(Rect(0, 0, size.width, size.height));
   }
 
   RefPtr<SourceSurface> surface = dt->Snapshot();
   if (!surface) {
     return;
   }
 
   RefPtr<layers::SourceSurfaceImage> image = new layers::SourceSurfaceImage(size, surface);
--- a/dom/url/tests/mochitest.ini
+++ b/dom/url/tests/mochitest.ini
@@ -13,8 +13,9 @@ support-files =
 [test_urlSearchParams.html]
 [test_urlSearchParams_utf8.html]
 [test_urlutils_stringify.html]
 [test_worker_url.html]
 [test_worker_urlApi.html]
 [test_worker_url_exceptions.html]
 [test_worker_urlSearchParams.html]
 [test_unknown_url_origin.html]
+[test_bloburl_location.html]
new file mode 100644
--- /dev/null
+++ b/dom/url/tests/test_bloburl_location.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for blobURL in location</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <script type="application/javascript">
+
+var expectedData = null;
+onmessage = function(e) {
+  if (expectedData === null) {
+    expectedData = e.data;
+  } else {
+    is(e.data, expectedData, "Pathname should be not be changed");
+    SimpleTest.finish();
+  }
+}
+
+var ifr = document.createElement('iframe');
+document.body.appendChild(ifr);
+
+ifr.src = "data:html,<script>location=URL.createObjectURL(new%20Blob(['<script>parent.postMessage(location.pathname,\"*\");location.pathname=\"foo\";parent.postMessage(location.pathname,\"*\");<\/s' +'cript>'], {type:\"text/html\"}));<\/script>";
+
+SimpleTest.waitForExplicitFinish();
+
+  </script>
+</body>
+</html>
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -63,16 +63,17 @@ partial interface KeyframeEffectReadOnly
 
 [Func="nsDocument::IsWebAnimationsEnabled",
  Constructor ((Element or CSSPseudoElement)? target,
               object? keyframes,
               optional (unrestricted double or KeyframeEffectOptions) options),
  Constructor (KeyframeEffectReadOnly source)]
 interface KeyframeEffect : KeyframeEffectReadOnly {
   inherit attribute (Element or CSSPseudoElement)? target;
+  [NeedsCallerType]
   inherit attribute IterationCompositeOperation    iterationComposite;
   // Bug 1216844 - implement additive animation
   // inherit attribute CompositeOperation          composite;
-  [SetterThrows]
+  [SetterThrows, NeedsCallerType]
   inherit attribute DOMString                   spacing;
   [Throws]
   void setKeyframes (object? keyframes);
 };
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -499,18 +499,17 @@ nsWindowWatcher::CreateChromeWindow(cons
   int retval = WinHasOption(aFeatures, "mozDisplayId", 0, nullptr);
   windowCreator2->SetScreenId(retval);
 #endif
 
   bool cancel = false;
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
   nsresult rv =
     windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags,
-                                        0 /* contextFlag */,  aOpeningTabParent,
-                                        aOpener, &cancel,
+                                        aOpeningTabParent, aOpener, &cancel,
                                         getter_AddRefs(newWindowChrome));
 
   if (NS_SUCCEEDED(rv) && cancel) {
     newWindowChrome = nullptr;
     return NS_ERROR_ABORT;
   }
 
   newWindowChrome.forget(aResult);
--- a/embedding/nsIWindowCreator2.idl
+++ b/embedding/nsIWindowCreator2.idl
@@ -21,41 +21,35 @@ interface nsITabParent;
 interface nsIURI;
 interface nsIWebBrowserChrome;
 interface mozIDOMWindowProxy;
 
 [scriptable, uuid(b6c44689-f97e-4f32-a723-29eeddfbdc53)]
 
 interface nsIWindowCreator2 : nsIWindowCreator {
 
-  /**
-   * Definitions for contextFlags
-   */
-
   /** Create a new window. Gecko will/may call this method, if made
       available to it, to create new windows.
       @param parent Parent window, if any. Null if not. The newly created
                     window should be made a child/dependent window of
                     the parent, if any (and if the concept applies
                     to the underlying OS).
       @param chromeFlags Chrome features from nsIWebBrowserChrome
-      @param contextFlags Flags about the context of the window being created.
       @param aOpeningTab The TabParent that is trying to open this new chrome
                          window. Can be nullptr.
       @param aOpener The window which is trying to open this new chrome window.
                      Can be nullptr
       @param cancel Return |true| to reject window creation. If true the
                     implementation has determined the window should not
                     be created at all. The caller should not default
                     to any possible backup scheme for creating the window.
       @return the new window. Will be null if canceled or an error occurred.
   */
   nsIWebBrowserChrome createChromeWindow2(in nsIWebBrowserChrome parent,
                                           in uint32_t chromeFlags,
-                                          in uint32_t contextFlags,
                                           in nsITabParent aOpeningTab,
                                           in mozIDOMWindowProxy aOpener,
                                           out boolean cancel);
 
   /**
    * B2G multi-screen support. When open another top-level window on b2g,
    * a screen ID is needed for identifying which screen this window is
    * opened to.
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -4145,22 +4145,25 @@ class _GenerateProtocolActorCode(ipdl.as
         actor = md.actorDecl()
         actorvar = actor.var()
         method = MethodDefn(self.makeDtorMethodDecl(md))
 
         method.addstmts(self.dtorPrologue(actorvar))
 
         msgvar, stmts = self.makeMessage(md, errfnSendDtor, actorvar)
         sendok, sendstmts = self.sendAsync(md, msgvar, actorvar)
+        failif = StmtIf(ExprNot(sendok))
+        failif.addifstmt(StmtReturn.FALSE)
+
         method.addstmts(
             stmts
             + self.genVerifyMessage(md.decl.type.verify, md.params,
                                     errfnSendDtor, ExprVar('msg__'))
             + sendstmts
-            + [ Whitespace.NL ]
+            + [ failif, Whitespace.NL ]
             + self.dtorEpilogue(md, actor.var())
             + [ StmtReturn(sendok) ])
 
         lbl = CaseLabel(md.pqReplyId())
         case = StmtBlock()
         case.addstmt(StmtReturn(_Result.Processed))
         # TODO if the dtor is "inherently racy", keep the actor alive
         # until the other side acks
--- a/js/src/ds/MemoryProtectionExceptionHandler.cpp
+++ b/js/src/ds/MemoryProtectionExceptionHandler.cpp
@@ -98,16 +98,19 @@ MemoryProtectionExceptionHandler::isDisa
 #if defined(XP_WIN) && defined(MOZ_ASAN)
     // Under Windows ASan, WasmFaultHandler registers itself at 'last' priority
     // in order to let ASan's ShadowExceptionHandler stay at 'first' priority.
     // Unfortunately that results in spurious wasm faults passing through the
     // MemoryProtectionExceptionHandler, which breaks its assumption that any
     // faults it sees are fatal. Just disable this handler in that case, as the
     // crash annotations provided here are not critical for ASan builds.
     return true;
+#elif defined(RELEASE_OR_BETA)
+    // Disable the exception handler for Beta and Release builds.
+    return true;
 #else
     return false;
 #endif
 }
 
 void
 MemoryProtectionExceptionHandler::addRegion(void* addr, size_t size)
 {
--- a/js/src/ds/PageProtectingVector.h
+++ b/js/src/ds/PageProtectingVector.h
@@ -68,18 +68,20 @@ class PageProtectingVector final
 
     bool protectionEnabled;
     bool regionUnprotected;
 
     void updateOffsetToPage() {
         unprotectedBytes += offsetToPage;
         offsetToPage = (pageSize - (uintptr_t(vector.begin()) & pageMask)) & pageMask;
         unprotectedBytes -= offsetToPage;
+#ifndef RELEASE_OR_BETA
         protectionEnabled = vector.capacity() >= protectionLowerBound &&
                             vector.capacity() >= pageSize + offsetToPage;
+#endif
     }
 
     void protect() {
         if (!regionUnprotected && protectionEnabled && unprotectedBytes >= intptr_t(pageSize)) {
             size_t toProtect = size_t(unprotectedBytes) & ~pageMask;
             uintptr_t addr = uintptr_t(vector.begin()) + offsetToPage + protectedBytes;
             gc::MakePagesReadOnly(reinterpret_cast<void*>(addr), toProtect);
             unprotectedBytes -= toProtect;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -483,19 +483,23 @@ js::TenuringTracer::TenuringTracer(JSRun
   : JSTracer(rt, JSTracer::TracerKindTag::Tenuring, TraceWeakMapKeysValues)
   , nursery_(*nursery)
   , tenuredSize(0)
   , head(nullptr)
   , tail(&head)
 {
 }
 
-/* static */ void
+void
 js::Nursery::printProfileHeader()
 {
+    if (!enableProfiling_)
+        return;
+
+    fprintf(stderr, "MinorGC:               Reason  PRate Size ");
 #define PRINT_HEADER(name, text)                                              \
     fprintf(stderr, " %6s", text);
 FOR_EACH_NURSERY_PROFILE_TIME(PRINT_HEADER)
 #undef PRINT_HEADER
     fprintf(stderr, "\n");
 }
 
 /* static */ void
@@ -625,21 +629,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
         rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason);
     rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted());
     rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount);
 
     rt->gc.stats.endNurseryCollection(reason);
     TraceMinorGCEnd();
 
     if (enableProfiling_ && totalTime >= profileThreshold_) {
-        static int printedHeader = 0;
-        if ((printedHeader++ % 200) == 0) {
-            fprintf(stderr, "MinorGC:               Reason  PRate Size ");
-            printProfileHeader();
-        }
+        rt->gc.stats.maybePrintProfileHeaders();
 
         fprintf(stderr, "MinorGC: %20s %5.1f%% %4u ",
                 JS::gcreason::ExplainReason(reason),
                 promotionRate * 100,
                 numChunks());
         printProfileTimes(profileTimes_);
 
         if (reportTenurings_) {
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -252,16 +252,19 @@ class Nursery
                (numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize;
     }
 
 #ifdef JS_GC_ZEAL
     void enterZealMode();
     void leaveZealMode();
 #endif
 
+    /* Print header line for profile times. */
+    void printProfileHeader();
+
     /* Print total profile times on shutdown. */
     void printTotalProfileTimes();
 
   private:
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunk {
@@ -453,17 +456,16 @@ class Nursery
     void shrinkAllocableSpace();
     void minimizeAllocableSpace();
 
     /* Profile recording and printing. */
     void startProfile(ProfileKey key);
     void endProfile(ProfileKey key);
     void maybeStartProfile(ProfileKey key);
     void maybeEndProfile(ProfileKey key);
-    static void printProfileHeader();
     static void printProfileTimes(const ProfileTimes& times);
 
     friend class TenuringTracer;
     friend class gc::MinorCollectionTracer;
     friend class jit::MacroAssembler;
 };
 
 } /* namespace js */
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -1321,19 +1321,33 @@ Statistics::computeMMU(int64_t window) c
             cur -= (slices[endIndex].end - slices[startIndex].start - window);
         if (cur > gcMax)
             gcMax = cur;
     }
 
     return double(window - gcMax) / window;
 }
 
-/* static */ void
+void
+Statistics::maybePrintProfileHeaders()
+{
+    static int printedHeader = 0;
+    if ((printedHeader++ % 200) == 0) {
+        printProfileHeader();
+        runtime->gc.nursery.printProfileHeader();
+    }
+}
+
+void
 Statistics::printProfileHeader()
 {
+    if (!enableProfiling_)
+        return;
+
+    fprintf(stderr, "MajorGC:               Reason States      ");
     fprintf(stderr, " %6s", "total");
 #define PRINT_PROFILE_HEADER(name, text, phase)                               \
     fprintf(stderr, " %6s", text);
 FOR_EACH_GC_PROFILE_TIME(PRINT_PROFILE_HEADER)
 #undef PRINT_PROFILE_HEADER
     fprintf(stderr, "\n");
 }
 
@@ -1345,21 +1359,17 @@ Statistics::printProfileTimes(const Prof
     fprintf(stderr, "\n");
 }
 
 void
 Statistics::printSliceProfile()
 {
     const SliceData& slice = slices.back();
 
-    static int printedHeader = 0;
-    if ((printedHeader++ % 200) == 0) {
-        fprintf(stderr, "MajorGC:               Reason States      ");
-        printProfileHeader();
-    }
+    maybePrintProfileHeaders();
 
     fprintf(stderr, "MajorGC: %20s %1d -> %1d      ",
             ExplainReason(slice.reason), int(slice.initialState), int(slice.finalState));
 
     ProfileTimes times;
     times[ProfileKey::Total] = slice.duration();
     totalTimes_[ProfileKey::Total] += times[ProfileKey::Total];
 
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -136,27 +136,27 @@ struct ZoneGCStats
 
     ZoneGCStats()
       : collectedZoneCount(0), zoneCount(0), sweptZoneCount(0),
         collectedCompartmentCount(0), compartmentCount(0), sweptCompartmentCount(0)
     {}
 };
 
 #define FOR_EACH_GC_PROFILE_TIME(_)                                           \
-    _(BeginCallback, "beginCB",  PHASE_GC_BEGIN)                              \
-    _(WaitBgThread,  "waitBG",   PHASE_WAIT_BACKGROUND_THREAD)                \
-    _(DiscardCode,   "discard",  PHASE_MARK_DISCARD_CODE)                     \
-    _(RelazifyFunc,  "relazify", PHASE_RELAZIFY_FUNCTIONS)                    \
-    _(PurgeTables,   "purgeTables", PHASE_PURGE_SHAPE_TABLES)                 \
-    _(Purge,         "purge",    PHASE_PURGE)                                 \
-    _(Mark,          "mark",     PHASE_MARK)                                  \
-    _(Sweep,         "sweep",    PHASE_SWEEP)                                 \
-    _(Compact,       "compact",  PHASE_COMPACT)                               \
-    _(EndCallback,   "endCB",    PHASE_GC_END)                                \
-    _(Barriers,      "barriers", PHASE_BARRIER)
+    _(BeginCallback, "bgnCB",  PHASE_GC_BEGIN)                                \
+    _(WaitBgThread,  "waitBG", PHASE_WAIT_BACKGROUND_THREAD)                  \
+    _(DiscardCode,   "discrd", PHASE_MARK_DISCARD_CODE)                       \
+    _(RelazifyFunc,  "relzfy", PHASE_RELAZIFY_FUNCTIONS)                      \
+    _(PurgeTables,   "prgTbl", PHASE_PURGE_SHAPE_TABLES)                      \
+    _(Purge,         "purge",  PHASE_PURGE)                                   \
+    _(Mark,          "mark",   PHASE_MARK)                                    \
+    _(Sweep,         "sweep",  PHASE_SWEEP)                                   \
+    _(Compact,       "cmpct",  PHASE_COMPACT)                                 \
+    _(EndCallback,   "endCB",  PHASE_GC_END)                                  \
+    _(Barriers,      "brrier", PHASE_BARRIER)
 
 const char* ExplainAbortReason(gc::AbortReason reason);
 const char* ExplainInvocationKind(JSGCInvocationKind gckind);
 
 /*
  * Struct for collecting timing statistics on a "phase tree". The tree is
  * specified as a limited DAG, but the timings are collected for the whole tree
  * that you would get by expanding out the DAG by duplicating subtrees rooted
@@ -308,16 +308,22 @@ struct Statistics
     };
 
     typedef Vector<SliceData, 8, SystemAllocPolicy> SliceDataVector;
     typedef SliceDataVector::ConstRange SliceRange;
 
     SliceRange sliceRange() const { return slices.all(); }
     size_t slicesLength() const { return slices.length(); }
 
+    /* Occasionally print header lines for profiling information. */
+    void maybePrintProfileHeaders();
+
+    /* Print header line for profile times. */
+    void printProfileHeader();
+
     /* Print total profile times on shutdown. */
     void printTotalProfileTimes();
 
   private:
     JSRuntime* runtime;
 
     int64_t startupTime;
 
@@ -424,17 +430,16 @@ FOR_EACH_GC_PROFILE_TIME(DEFINE_TIME_KEY
 
     UniqueChars formatJsonDescription(uint64_t timestamp);
     UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice);
     UniqueChars formatJsonPhaseTimes(const PhaseTimeTable phaseTimes);
 
     double computeMMU(int64_t resolution) const;
 
     void printSliceProfile();
-    static void printProfileHeader();
     static void printProfileTimes(const ProfileTimes& times);
 };
 
 struct MOZ_RAII AutoGCSlice
 {
     AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                 SliceBudget budget, JS::gcreason::Reason reason)
       : stats(stats)
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -1832,35 +1832,33 @@ template class irregexp::RegExpParser<La
 template class irregexp::RegExpParser<char16_t>;
 
 template <typename CharT>
 static bool
 ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length,
              bool multiline, bool match_only, bool unicode, bool ignore_case,
              bool global, bool sticky, RegExpCompileData* data)
 {
-    if (match_only) {
+    // We shouldn't strip pattern for exec, or test with global/sticky,
+    // to reflect correct match position and lastIndex.
+    if (match_only && !global && !sticky) {
         // Try to strip a leading '.*' from the RegExp, but only if it is not
         // followed by a '?' (which will affect how the .* is parsed). This
         // pattern will affect the captures produced by the RegExp, but not
         // whether there is a match or not.
         if (length >= 3 && chars[0] == '.' && chars[1] == '*' && chars[2] != '?') {
             chars += 2;
             length -= 2;
         }
 
         // Try to strip a trailing '.*' from the RegExp, which as above will
         // affect the captures but not whether there is a match. Only do this
-        // when the following conditions are met:
-        //   1. there are no other meta characters in the RegExp, so that we
-        //      are sure this will not affect how the RegExp is parsed
-        //   2. global and sticky flags are not set, as lastIndex needs to be
-        //      set properly on global or sticky match
+        // when there are no other meta characters in the RegExp, so that we
+        // are sure this will not affect how the RegExp is parsed.
         if (length >= 3 && !HasRegExpMetaChars(chars, length - 2) &&
-            !global && !sticky &&
             chars[length - 2] == '.' && chars[length - 1] == '*')
         {
             length -= 2;
         }
     }
 
     RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, multiline, unicode, ignore_case);
     data->tree = parser.ParsePattern();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/test-emptyMatch.js
@@ -0,0 +1,23 @@
+var BUGNUMBER = 1322035;
+var summary = 'RegExp.prototype.test should update lastIndex to correct position even if pattern starts with .*';
+
+print(BUGNUMBER + ": " + summary);
+
+var regExp = /.*x?/g;
+regExp.test('12345');
+assertEq(regExp.lastIndex, 5);
+
+regExp = /.*x*/g;
+regExp.test('12345');
+assertEq(regExp.lastIndex, 5);
+
+regExp = /.*()/g;
+regExp.test('12345');
+assertEq(regExp.lastIndex, 5);
+
+regExp = /.*(x|)/g;
+regExp.test('12345');
+assertEq(regExp.lastIndex, 5);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -400,29 +400,33 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext
 
     // There is an extra AddRef to support weak references to wrappers
     // that are subject to finalization. See the top of the file for more
     // details.
     NS_ADDREF_THIS();
 
     if (IsRootWrapper()) {
         MOZ_ASSERT(!IsMultiCompartment(), "mNext is always nullptr here");
-        xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this);
+        if (!xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this)) {
+            *rv = NS_ERROR_OUT_OF_MEMORY;
+        }
     } else {
         NS_ADDREF(mRoot);
         mNext = mRoot->mNext;
         mRoot->mNext = this;
 
         // We always start wrappers in the per-compartment table. If adding
         // this wrapper to the chain causes it to cross compartments, we need
         // to migrate the chain to the global table on the XPCJSContext.
         if (mRoot->IsMultiCompartment()) {
             xpc::CompartmentPrivate::Get(mRoot->mJSObj)->GetWrappedJSMap()->Remove(mRoot);
-            MOZ_RELEASE_ASSERT(nsXPConnect::GetContextInstance()->
-                    GetMultiCompartmentWrappedJSMap()->Add(cx, mRoot));
+            auto destMap = nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap();
+            if (!destMap->Add(cx, mRoot)) {
+                *rv = NS_ERROR_OUT_OF_MEMORY;
+            }
         }
     }
 }
 
 nsXPCWrappedJS::~nsXPCWrappedJS()
 {
     Destroy();
 }
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -549,17 +549,19 @@ nsXPCWrappedJSClass::DelegatedQueryInter
     nsCOMPtr<nsIInterfaceInfo> info;
     nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
     if (info && NS_SUCCEEDED(info->IsFunction(&isFunc)) && isFunc) {
         RefPtr<nsXPCWrappedJS> wrapper;
         RootedObject obj(RootingCx(), self->GetJSObject());
         nsresult rv = nsXPCWrappedJS::GetNewOrUsed(obj, aIID, getter_AddRefs(wrapper));
 
         // Do the same thing we do for the "check for any existing wrapper" case above.
-        *aInstancePtr = wrapper.forget().take()->GetXPTCStub();
+        if (NS_SUCCEEDED(rv) && wrapper) {
+            *aInstancePtr = wrapper.forget().take()->GetXPTCStub();
+        }
         return rv;
     }
 
     // else we do the more expensive stuff...
 
     // check if the JSObject claims to implement this interface
     RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(),
                                                          aIID));
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -9,25 +9,26 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/SRIMetadata.h"
+#include "nsIConsoleService.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 
-// Includes for the crash report annotation in ErrorLoadingBuiltinSheet.
+// Includes for the crash report annotation in ErrorLoadingSheet.
 #ifdef MOZ_CRASHREPORTER
 #include "mozilla/Omnijar.h"
 #include "nsDirectoryService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExceptionHandler.h"
 #include "nsIChromeRegistry.h"
 #include "nsISimpleEnumerator.h"
 #include "nsISubstitutingProtocolHandler.h"
@@ -70,44 +71,44 @@ nsLayoutStylesheetCache::Observe(nsISupp
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::ScrollbarsSheet()
 {
   if (!mScrollbarsSheet) {
     // Scrollbars don't need access to unsafe rules
     LoadSheetURL("chrome://global/skin/scrollbars.css",
-                 &mScrollbarsSheet, eAuthorSheetFeatures);
+                 &mScrollbarsSheet, eAuthorSheetFeatures, eCrash);
   }
 
   return mScrollbarsSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::FormsSheet()
 {
   if (!mFormsSheet) {
     // forms.css needs access to unsafe rules
     LoadSheetURL("resource://gre-resources/forms.css",
-                 &mFormsSheet, eAgentSheetFeatures);
+                 &mFormsSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mFormsSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::NumberControlSheet()
 {
   if (!sNumberControlEnabled) {
     return nullptr;
   }
 
   if (!mNumberControlSheet) {
     LoadSheetURL("resource://gre-resources/number-control.css",
-                 &mNumberControlSheet, eAgentSheetFeatures);
+                 &mNumberControlSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mNumberControlSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::UserContentSheet()
 {
@@ -120,17 +121,17 @@ nsLayoutStylesheetCache::UserChromeSheet
   return mUserChromeSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::UASheet()
 {
   if (!mUASheet) {
     LoadSheetURL("resource://gre-resources/ua.css",
-                 &mUASheet, eAgentSheetFeatures);
+                 &mUASheet, eAgentSheetFeatures, eCrash);
   }
 
   return mUASheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::HTMLSheet()
 {
@@ -161,45 +162,45 @@ nsLayoutStylesheetCache::SVGSheet()
   return mSVGSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::MathMLSheet()
 {
   if (!mMathMLSheet) {
     LoadSheetURL("resource://gre-resources/mathml.css",
-                 &mMathMLSheet, eAgentSheetFeatures);
+                 &mMathMLSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mMathMLSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::CounterStylesSheet()
 {
   return mCounterStylesSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::NoScriptSheet()
 {
   if (!mNoScriptSheet) {
     LoadSheetURL("resource://gre-resources/noscript.css",
-                 &mNoScriptSheet, eAgentSheetFeatures);
+                 &mNoScriptSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mNoScriptSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::NoFramesSheet()
 {
   if (!mNoFramesSheet) {
     LoadSheetURL("resource://gre-resources/noframes.css",
-                 &mNoFramesSheet, eAgentSheetFeatures);
+                 &mNoFramesSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mNoFramesSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::ChromePreferenceSheet(nsPresContext* aPresContext)
 {
@@ -220,28 +221,28 @@ nsLayoutStylesheetCache::ContentPreferen
   return mContentPreferenceSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::ContentEditableSheet()
 {
   if (!mContentEditableSheet) {
     LoadSheetURL("resource://gre/res/contenteditable.css",
-                 &mContentEditableSheet, eAgentSheetFeatures);
+                 &mContentEditableSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mContentEditableSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::DesignModeSheet()
 {
   if (!mDesignModeSheet) {
     LoadSheetURL("resource://gre/res/designmode.css",
-                 &mDesignModeSheet, eAgentSheetFeatures);
+                 &mDesignModeSheet, eAgentSheetFeatures, eCrash);
   }
 
   return mDesignModeSheet;
 }
 
 void
 nsLayoutStylesheetCache::Shutdown()
 {
@@ -323,31 +324,31 @@ nsLayoutStylesheetCache::nsLayoutStylesh
     obsSvc->AddObserver(this, "chrome-flush-caches", false);
   }
 
   InitFromProfile();
 
   // And make sure that we load our UA sheets.  No need to do this
   // per-profile, since they're profile-invariant.
   LoadSheetURL("resource://gre-resources/counterstyles.css",
-               &mCounterStylesSheet, eAgentSheetFeatures);
+               &mCounterStylesSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("resource://gre-resources/html.css",
-               &mHTMLSheet, eAgentSheetFeatures);
+               &mHTMLSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("chrome://global/content/minimal-xul.css",
-               &mMinimalXULSheet, eAgentSheetFeatures);
+               &mMinimalXULSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("resource://gre-resources/quirk.css",
-               &mQuirkSheet, eAgentSheetFeatures);
+               &mQuirkSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("resource://gre/res/svg.css",
-               &mSVGSheet, eAgentSheetFeatures);
+               &mSVGSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("chrome://global/content/xul.css",
-               &mXULSheet, eAgentSheetFeatures);
+               &mXULSheet, eAgentSheetFeatures, eCrash);
 
   if (gUserContentSheetURL) {
     MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
-    LoadSheet(gUserContentSheetURL, &mUserContentSheet, eUserSheetFeatures);
+    LoadSheet(gUserContentSheetURL, &mUserContentSheet, eUserSheetFeatures, eLogToConsole);
     gUserContentSheetURL = nullptr;
   }
 
   // The remaining sheets are created on-demand do to their use being rarer
   // (which helps save memory for Firefox OS apps) or because they need to
   // be re-loadable in DependentPrefChanged.
 }
 
@@ -417,47 +418,49 @@ nsLayoutStylesheetCache::InitFromProfile
   }
 
   contentFile->Clone(getter_AddRefs(chromeFile));
   if (!chromeFile) return;
 
   contentFile->Append(NS_LITERAL_STRING("userContent.css"));
   chromeFile->Append(NS_LITERAL_STRING("userChrome.css"));
 
-  LoadSheetFile(contentFile, &mUserContentSheet, eUserSheetFeatures);
-  LoadSheetFile(chromeFile, &mUserChromeSheet, eUserSheetFeatures);
+  LoadSheetFile(contentFile, &mUserContentSheet, eUserSheetFeatures, eLogToConsole);
+  LoadSheetFile(chromeFile, &mUserChromeSheet, eUserSheetFeatures, eLogToConsole);
 }
 
 void
 nsLayoutStylesheetCache::LoadSheetURL(const char* aURL,
                                       RefPtr<StyleSheet>* aSheet,
-                                      SheetParsingMode aParsingMode)
+                                      SheetParsingMode aParsingMode,
+                                      FailureAction aFailureAction)
 {
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), aURL);
-  LoadSheet(uri, aSheet, aParsingMode);
+  LoadSheet(uri, aSheet, aParsingMode, aFailureAction);
   if (!aSheet) {
     NS_ERROR(nsPrintfCString("Could not load %s", aURL).get());
   }
 }
 
 void
 nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile,
                                        RefPtr<StyleSheet>* aSheet,
-                                       SheetParsingMode aParsingMode)
+                                       SheetParsingMode aParsingMode,
+                                       FailureAction aFailureAction)
 {
   bool exists = false;
   aFile->Exists(&exists);
 
   if (!exists) return;
 
   nsCOMPtr<nsIURI> uri;
   NS_NewFileURI(getter_AddRefs(uri), aFile);
 
-  LoadSheet(uri, aSheet, aParsingMode);
+  LoadSheet(uri, aSheet, aParsingMode, aFailureAction);
 }
 
 #ifdef MOZ_CRASHREPORTER
 static inline nsresult
 ComputeCRC32(nsIFile* aFile, uint32_t* aResult)
 {
   PRFileDesc* fd;
   nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
@@ -725,56 +728,66 @@ AnnotateCrashReport(nsIURI* aURI)
   }
 
   CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"),
                                      NS_ConvertUTF16toUTF8(annotation));
 }
 #endif
 
 static void
-ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg)
+ErrorLoadingSheet(nsIURI* aURI, const char* aMsg, FailureAction aFailureAction)
 {
+  nsPrintfCString errorMessage("%s loading built-in stylesheet '%s'",
+                               aMsg,
+                               aURI ? aURI->GetSpecOrDefault().get() : "");
+  if (aFailureAction == eLogToConsole) {
+    nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    if (cs) {
+      cs->LogStringMessage(NS_ConvertUTF8toUTF16(errorMessage).get());
+      return;
+    }
+  }
+
 #ifdef MOZ_CRASHREPORTER
   AnnotateCrashReport(aURI);
 #endif
-
-  NS_RUNTIMEABORT(
-    nsPrintfCString("%s loading built-in stylesheet '%s'",
-                    aMsg, aURI ? aURI->GetSpecOrDefault().get() : "").get());
+  NS_RUNTIMEABORT(errorMessage.get());
 }
 
 void
 nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI,
                                    RefPtr<StyleSheet>* aSheet,
-                                   SheetParsingMode aParsingMode)
+                                   SheetParsingMode aParsingMode,
+                                   FailureAction aFailureAction)
 {
   if (!aURI) {
-    ErrorLoadingBuiltinSheet(aURI, "null URI");
+    ErrorLoadingSheet(aURI, "null URI", eCrash);
     return;
   }
 
   auto& loader = mBackendType == StyleBackendType::Gecko ?
     gCSSLoader_Gecko :
     gCSSLoader_Servo;
 
   if (!loader) {
     loader = new mozilla::css::Loader(mBackendType);
     if (!loader) {
-      ErrorLoadingBuiltinSheet(aURI, "no Loader");
+      ErrorLoadingSheet(aURI, "no Loader", eCrash);
       return;
     }
   }
 
 #ifdef MOZ_CRASHREPORTER
   nsZipArchive::sFileCorruptedReason = nullptr;
 #endif
   nsresult rv = loader->LoadSheetSync(aURI, aParsingMode, true, aSheet);
   if (NS_FAILED(rv)) {
-    ErrorLoadingBuiltinSheet(aURI,
-      nsPrintfCString("LoadSheetSync failed with error %x", rv).get());
+    ErrorLoadingSheet(aURI,
+      nsPrintfCString("LoadSheetSync failed with error %x", rv).get(),
+      aFailureAction);
   }
 }
 
 /* static */ void
 nsLayoutStylesheetCache::InvalidateSheet(RefPtr<StyleSheet>* aGeckoSheet,
                                          RefPtr<StyleSheet>* aServoSheet)
 {
   MOZ_ASSERT(gCSSLoader_Gecko || gCSSLoader_Servo,
--- a/layout/style/nsLayoutStylesheetCache.h
+++ b/layout/style/nsLayoutStylesheetCache.h
@@ -17,16 +17,28 @@
 
 class nsIFile;
 class nsIURI;
 
 namespace mozilla {
 class CSSStyleSheet;
 } // namespace mozilla
 
+namespace mozilla {
+namespace css {
+
+// Enum defining how error should be handled.
+enum FailureAction {
+  eCrash = 0,
+  eLogToConsole
+};
+
+}
+}
+
 class nsLayoutStylesheetCache final
  : public nsIObserver
  , public nsIMemoryReporter
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIMEMORYREPORTER
 
@@ -73,22 +85,25 @@ class nsLayoutStylesheetCache final
 private:
   explicit nsLayoutStylesheetCache(mozilla::StyleBackendType aImpl);
   ~nsLayoutStylesheetCache();
 
   void InitFromProfile();
   void InitMemoryReporter();
   void LoadSheetURL(const char* aURL,
                     RefPtr<mozilla::StyleSheet>* aSheet,
-                    mozilla::css::SheetParsingMode aParsingMode);
+                    mozilla::css::SheetParsingMode aParsingMode,
+                    mozilla::css::FailureAction aFailureAction);
   void LoadSheetFile(nsIFile* aFile,
                      RefPtr<mozilla::StyleSheet>* aSheet,
-                     mozilla::css::SheetParsingMode aParsingMode);
+                     mozilla::css::SheetParsingMode aParsingMode,
+                     mozilla::css::FailureAction aFailureAction);
   void LoadSheet(nsIURI* aURI, RefPtr<mozilla::StyleSheet>* aSheet,
-                 mozilla::css::SheetParsingMode aParsingMode);
+                 mozilla::css::SheetParsingMode aParsingMode,
+                 mozilla::css::FailureAction aFailureAction);
   static void InvalidateSheet(RefPtr<mozilla::StyleSheet>* aGeckoSheet,
                               RefPtr<mozilla::StyleSheet>* aServoSheet);
   static void DependentPrefChanged(const char* aPref, void* aData);
   void BuildPreferenceSheet(RefPtr<mozilla::StyleSheet>* aSheet,
                             nsPresContext* aPresContext);
 
   static mozilla::StaticRefPtr<nsLayoutStylesheetCache> gStyleCache_Gecko;
   static mozilla::StaticRefPtr<nsLayoutStylesheetCache> gStyleCache_Servo;
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -109,8 +109,83 @@ win64/pgo:
         max-run-time: 10800
     run:
         using: mozharness
         options: [enable-pgo]
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_64_opt.py
 
+win32-clang/debug:
+    description: "Win32 Clang-cl Debug"
+    index:
+        product: firefox
+        job-name: win32-clang-debug
+    treeherder:
+        platform: windows2012-32/debug
+        symbol: tc(Bcl)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        implementation: generic-worker
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win32_clang_debug.py
+
+win32-clang/opt:
+    description: "Win32 Clang-cl Opt"
+    index:
+        product: firefox
+        job-name: win32-clang-opt
+    treeherder:
+        platform: windows2012-32/opt
+        symbol: tc(Bcl)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        implementation: generic-worker
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win32_clang.py
+
+win64-clang/debug:
+    description: "Win64 Clang-cl Debug"
+    index:
+        product: firefox
+        job-name: win64-clang-debug
+    treeherder:
+        platform: windows2012-64/debug
+        symbol: tc(Bcl)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        implementation: generic-worker
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win64_clang_debug.py
+
+win64-clang/opt:
+    description: "Win64 Clang-cl Opt"
+    index:
+        product: firefox
+        job-name: win64-clang-opt
+    treeherder:
+        platform: windows2012-64/opt
+        symbol: tc(Bcl)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
+    worker:
+        implementation: generic-worker
+        max-run-time: 7200
+    run:
+        using: mozharness
+        script: mozharness/scripts/fx_desktop_build.py
+        config:
+            - builds/taskcluster_firefox_win64_clang.py
--- a/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
+++ b/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
@@ -58,19 +58,23 @@ JOB_NAME_WHITELIST = set([
     'sm-mozjs-sys-debug',
     'sm-msan-opt',
     'sm-nonunified-debug',
     'sm-package-opt',
     'sm-plaindebug-debug',
     'sm-plain-opt',
     'sm-rootanalysis-debug',
     'sm-tsan-opt',
+    'win32-clang-debug',
+    'win32-clang-opt',
     'win32-debug',
     'win32-opt',
     'win32-pgo',
+    'win64-clang-debug',
+    'win64-clang-opt',
     'win64-debug',
     'win64-opt',
     'win64-pgo',
 ])
 
 JOB_NAME_WHITELIST_ERROR = """\
 The gecko-v2 job name {} is not in the whitelist in gecko_v2_whitelist.py.
 If this job runs on Buildbot, please ensure that the job names match between
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win32_clang.py
@@ -0,0 +1,93 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 32 bit keys/values please add them
+    # below under the '32 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'check-test',
+    ],
+    'exes': {
+        'python2.7': sys.executable,
+        'make': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'build', 'pymake', 'make.py'
+            )
+        ],
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+        'mach-build': [
+            os.path.join(os.environ['MOZILLABUILD'], 'msys', 'bin', 'bash.exe'),
+            os.path.join(os.getcwd(), 'build', 'src', 'mach'),
+            '--log-no-times', 'build', '-v'
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'enable_ccache': False,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 32 bit specific ######
+    'base_name': 'WINNT_5.2_%(branch)s',
+    'platform': 'win32',
+    'stage_platform': 'win32',
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'BINSCOPE': os.path.join(
+            os.environ['ProgramFiles(x86)'], 'Microsoft', 'SDL BinScope', 'BinScope.exe'
+        ),
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'MOZ_AUTOMATION': '1',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': '/c/Program Files (x86)/Windows Kits/10/Debuggers/x86/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/c/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win32\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'enable_pymake': True,
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win32\\clang',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win32\\clang.manifest',
+    #########################################################################
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win32_clang_debug.py
@@ -0,0 +1,95 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 32 bit keys/values please add them
+    # below under the '32 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'check-test',
+    ],
+    'exes': {
+        'python2.7': sys.executable,
+        'make': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'build', 'pymake', 'make.py'
+            )
+        ],
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+        'mach-build': [
+            os.path.join(os.environ['MOZILLABUILD'], 'msys', 'bin', 'bash.exe'),
+            os.path.join(os.getcwd(), 'build', 'src', 'mach'),
+            '--log-no-times', 'build', '-v'
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'enable_ccache': False,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 32 bit specific ######
+    'base_name': 'WINNT_5.2_%(branch)s',
+    'platform': 'win32',
+    'stage_platform': 'win32-debug',
+    'debug_build': True,
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'BINSCOPE': os.path.join(
+            os.environ['ProgramFiles(x86)'], 'Microsoft', 'SDL BinScope', 'BinScope.exe'
+        ),
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
+        'MOZ_AUTOMATION': '1',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': '/c/Program Files (x86)/Windows Kits/10/Debuggers/x86/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/c/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win32\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'enable_pymake': True,
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win32\\clang-debug',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win32\\clang.manifest',
+    #########################################################################
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win64_clang.py
@@ -0,0 +1,89 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 64 bit keys/values please add them
+    # below under the '64 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'check-test',
+    ],
+    'exes': {
+        'python2.7': sys.executable,
+        'make': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'build', 'pymake', 'make.py'
+            )
+        ],
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+        'mach-build': [
+            os.path.join(os.environ['MOZILLABUILD'], 'msys', 'bin', 'bash.exe'),
+            os.path.join(os.getcwd(), 'build', 'src', 'mach'),
+            '--log-no-times', 'build', '-v'
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'enable_ccache': False,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 64 bit specific ######
+    'base_name': 'WINNT_6.1_x86-64_%(branch)s',
+    'platform': 'win64',
+    'stage_platform': 'win64',
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZ_AUTOMATION': '1',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': '/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/c/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win64\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'enable_pymake': True,
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win64\\clang',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win64\\clang.manifest',
+    #########################################################################
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_win64_clang_debug.py
@@ -0,0 +1,91 @@
+import os
+import sys
+
+config = {
+    #########################################################################
+    ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
+    # if you are updating this with custom 64 bit keys/values please add them
+    # below under the '64 bit specific' code block otherwise, update in this
+    # code block and also make sure this is synced between:
+    # - taskcluster_firefox_win32_debug
+    # - taskcluster_firefox_win32_opt
+    # - taskcluster_firefox_win64_debug
+    # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
+
+    'default_actions': [
+        'clone-tools',
+        'build',
+        'check-test',
+    ],
+    'exes': {
+        'python2.7': sys.executable,
+        'make': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'build', 'pymake', 'make.py'
+            )
+        ],
+        'virtualenv': [
+            sys.executable,
+            os.path.join(
+                os.getcwd(), 'build', 'src', 'python', 'virtualenv', 'virtualenv.py'
+            )
+        ],
+        'mach-build': [
+            os.path.join(os.environ['MOZILLABUILD'], 'msys', 'bin', 'bash.exe'),
+            os.path.join(os.getcwd(), 'build', 'src', 'mach'),
+            '--log-no-times', 'build', '-v'
+        ],
+    },
+    'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
+    # decides whether we want to use moz_sign_cmd in env
+    'enable_signing': True,
+    'enable_ccache': False,
+    'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
+    'objdir': 'obj-firefox',
+    'tooltool_script': [
+      sys.executable,
+      os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
+    ],
+    'tooltool_bootstrap': 'setup.sh',
+    'enable_count_ctors': False,
+    'max_build_output_timeout': 60 * 80,
+    #########################################################################
+
+
+     #########################################################################
+     ###### 64 bit specific ######
+    'base_name': 'WINNT_6.1_x86-64_%(branch)s',
+    'platform': 'win64',
+    'stage_platform': 'win64-debug',
+    'debug_build': True,
+    'publish_nightly_en_US_routes': True,
+    'env': {
+        'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
+        'MOZ_AUTOMATION': '1',
+        'MOZ_CRASHREPORTER_NO_REPORT': '1',
+        'MOZ_OBJDIR': 'obj-firefox',
+        'PDBSTR_PATH': '/c/Program Files (x86)/Windows Kits/10/Debuggers/x64/srcsrv/pdbstr.exe',
+        'TINDERBOX_OUTPUT': '1',
+        'TOOLTOOL_CACHE': '/c/builds/tooltool_cache',
+        'TOOLTOOL_HOME': '/c/builds',
+        'XPCOM_DEBUG_BREAK': 'stack-and-abort',
+        'MSYSTEM': 'MINGW32',
+    },
+    'upload_env': {
+        'UPLOAD_HOST': 'localhost',
+        'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
+    },
+    "check_test_env": {
+        'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win64\\minidump_stackwalk.exe',
+        'MINIDUMP_SAVE_PATH': '%(base_work_dir)s\\minidumps',
+    },
+    'enable_pymake': True,
+    'src_mozconfig': 'browser\\config\\mozconfigs\\win64\\clang-debug',
+    'tooltool_manifest_src': 'browser\\config\\tooltool-manifests\\win64\\clang.manifest',
+    #########################################################################
+}
--- a/testing/mozharness/configs/builds/taskcluster_firefox_windows_32_debug.py
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_windows_32_debug.py
@@ -6,16 +6,20 @@ config = {
     ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
     # if you are updating this with custom 32 bit keys/values please add them
     # below under the '32 bit specific' code block otherwise, update in this
     # code block and also make sure this is synced between:
     # - taskcluster_firefox_win32_debug
     # - taskcluster_firefox_win32_opt
     # - taskcluster_firefox_win64_debug
     # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
 
     'default_actions': [
         'clone-tools',
         'build',
         'check-test',
         'generate-build-stats'
     ],
     'exes': {
--- a/testing/mozharness/configs/builds/taskcluster_firefox_windows_32_opt.py
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_windows_32_opt.py
@@ -6,16 +6,20 @@ config = {
     ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
     # if you are updating this with custom 32 bit keys/values please add them
     # below under the '32 bit specific' code block otherwise, update in this
     # code block and also make sure this is synced between:
     # - taskcluster_firefox_win32_debug
     # - taskcluster_firefox_win32_opt
     # - taskcluster_firefox_win64_debug
     # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
 
     'default_actions': [
         'clone-tools',
         'build',
         'check-test',
         'generate-build-stats'
     ],
     'exes': {
--- a/testing/mozharness/configs/builds/taskcluster_firefox_windows_64_debug.py
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_windows_64_debug.py
@@ -6,16 +6,20 @@ config = {
     ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
     # if you are updating this with custom 64 bit keys/values please add them
     # below under the '64 bit specific' code block otherwise, update in this
     # code block and also make sure this is synced between:
     # - taskcluster_firefox_win32_debug
     # - taskcluster_firefox_win32_opt
     # - taskcluster_firefox_win64_debug
     # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
 
     'default_actions': [
         'clone-tools',
         'build',
         'check-test',
         'generate-build-stats'
     ],
     'exes': {
--- a/testing/mozharness/configs/builds/taskcluster_firefox_windows_64_opt.py
+++ b/testing/mozharness/configs/builds/taskcluster_firefox_windows_64_opt.py
@@ -6,16 +6,20 @@ config = {
     ######## WINDOWS GENERIC CONFIG KEYS/VAlUES
     # if you are updating this with custom 64 bit keys/values please add them
     # below under the '64 bit specific' code block otherwise, update in this
     # code block and also make sure this is synced between:
     # - taskcluster_firefox_win32_debug
     # - taskcluster_firefox_win32_opt
     # - taskcluster_firefox_win64_debug
     # - taskcluster_firefox_win64_opt
+    # - taskcluster_firefox_win32_clang
+    # - taskcluster_firefox_win32_clang_debug
+    # - taskcluster_firefox_win64_clang
+    # - taskcluster_firefox_win64_clang_debug
 
     'default_actions': [
         'clone-tools',
         'build',
         'check-test',
         'generate-build-stats'
     ],
     'exes': {
--- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm
+++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm
@@ -188,16 +188,18 @@ function _ContextualIdentityService(path
     let identity = this._identities.find(identity => identity.userContextId == userContextId &&
                                          identity.public);
     if (identity && name) {
       identity.name = name;
       identity.color = color;
       identity.icon = icon;
       delete identity.l10nID;
       delete identity.accessKey;
+
+      Services.obs.notifyObservers(null, "contextual-identity-updated", userContextId);
       this.saveSoon();
     }
 
     return !!identity;
   },
 
   remove(userContextId) {
     let index = this._identities.findIndex(i => i.userContextId == userContextId && i.public);
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -606,17 +606,17 @@ nsAppStartup::GetInterrupted(bool *aInte
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
                                  uint32_t aChromeFlags,
                                  nsIWebBrowserChrome **_retval)
 {
   bool cancel;
-  return CreateChromeWindow2(aParent, aChromeFlags, 0, nullptr, nullptr, &cancel, _retval);
+  return CreateChromeWindow2(aParent, aChromeFlags, nullptr, nullptr, &cancel, _retval);
 }
 
 
 //
 // nsAppStartup->nsIWindowCreator2
 //
 
 NS_IMETHODIMP
@@ -628,17 +628,16 @@ nsAppStartup::SetScreenId(uint32_t aScre
   }
 
   return appShell->SetScreenId(aScreenId);
 }
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
                                   uint32_t aChromeFlags,
-                                  uint32_t aContextFlags,
                                   nsITabParent *aOpeningTab,
                                   mozIDOMWindowProxy* aOpener,
                                   bool *aCancel,
                                   nsIWebBrowserChrome **_retval)
 {
   NS_ENSURE_ARG_POINTER(aCancel);
   NS_ENSURE_ARG_POINTER(_retval);
   *aCancel = false;
@@ -673,17 +672,16 @@ nsAppStartup::CreateChromeWindow2(nsIWeb
                                    nsIAppShellService::SIZE_TO_CONTENT,
                                    nsIAppShellService::SIZE_TO_CONTENT,
                                    aOpeningTab, aOpener,
                                    getter_AddRefs(newWindow));
   }
 
   // if anybody gave us anything to work with, use it
   if (newWindow) {
-    newWindow->SetContextFlags(aContextFlags);
     nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
     if (thing)
       CallGetInterface(thing.get(), _retval);
   }
 
   return *_retval ? NS_OK : NS_ERROR_FAILURE;
 }
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1714,296 +1714,296 @@
     "alert_emails": ["ckerschbaumer@mozilla.com"],
     "bug_numbers": [1272345, 1296287],
     "expires_in_version": "56",
     "kind": "enumerated",
     "n_values": 12,
     "description": "Whether the URL gets redirected?  (0=200, 1=301, 2=302, 3=304, 4=307, 5=308, 6=400, 7=401, 8=403, 9=404, 10=500, 11=other)"
   },
   "HTTP_NET_VS_CACHE_ONSTART_ISIMG": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for images. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_NOTIMG": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for non-images. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_ISIMG": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for images. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_NOTIMG": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for non-images. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and small queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and medium queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a normal priority and large queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and small queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and medium queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for requests with a high priority and large queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and small queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and medium queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a normal priority and large queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and small queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and medium queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for requests with a high priority and large queue. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_SMALL_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a small size (<32K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_MED_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a medium size (<256K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_LARGE_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a large size (>256K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_SMALL_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a small size (<32K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_MED_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a medium size (<256K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_LARGE_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) for cache files with a large size (>256K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_SMALL_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a small size (<32K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_MED_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a medium size (<256K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_LARGE_NORMALPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a large size (>256K) and normal priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_SMALL_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a small size (<32K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_MED_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a medium size (<256K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_LARGE_HIGHPRI": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) for cache files with a large size (>256K) and high priority. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_REVALIDATED": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) revalidated cache entries. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStartRequest) difference (ms) not revalidated cache entries. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) revalidated cache entries. Offset by 500 ms."
   },
   "HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED": {
-    "expires_in_version": "never",
+    "expires_in_version": "58",
       "alert_emails": ["necko@mozilla.com"],
       "bug_numbers": [1313095],
       "kind": "linear",
       "high": 1000,
       "n_buckets": 100,
       "description": "Network vs cache time load (OnStopRequest) difference (ms) not revalidated cache entries. Offset by 500 ms."
   },
   "HTTP_AUTH_DIALOG_STATS": {
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -1287,179 +1287,16 @@
 
     !undef _MOZFUNC_UN
     !define _MOZFUNC_UN
     !verbose pop
   !endif
 !macroend
 
 /**
- * Writes common registry values for a handler that uses DDE using SHCTX.
- *
- * @param   _KEY
- *          The key name in relation to the HKCR root. SOFTWARE\Classes is
- *          prefixed to this value when using SHCTX.
- * @param   _VALOPEN
- *          The path and args to launch the application.
- * @param   _VALICON
- *          The path to the binary that contains the icon group for the default icon
- *          followed by a comma and either the icon group's resource index or the icon
- *          group's resource id prefixed with a minus sign
- * @param   _DISPNAME
- *          The display name for the handler. If emtpy no value will be set.
- * @param   _ISPROTOCOL
- *          Sets protocol handler specific registry values when "true".
- *          Deletes protocol handler specific registry values when "delete".
- *          Otherwise doesn't touch handler specific registry values.
- * @param   _DDE_APPNAME
- *          Sets DDE specific registry values when not an empty string.
- *
- * $R0 = storage for SOFTWARE\Classes
- * $R1 = string value of the current registry key path.
- * $R2 = _KEY
- * $R3 = _VALOPEN
- * $R4 = _VALICON
- * $R5 = _DISPNAME
- * $R6 = _ISPROTOCOL
- * $R7 = _DDE_APPNAME
- * $R8 = _DDE_DEFAULT
- * $R9 = _DDE_TOPIC
- */
-!macro AddDDEHandlerValues
-
-  !ifndef ${_MOZFUNC_UN}AddDDEHandlerValues
-    !verbose push
-    !verbose ${_MOZFUNC_VERBOSE}
-    !define ${_MOZFUNC_UN}AddDDEHandlerValues "!insertmacro ${_MOZFUNC_UN}AddDDEHandlerValuesCall"
-
-    Function ${_MOZFUNC_UN}AddDDEHandlerValues
-      Exch $R9
-      Exch 1
-      Exch $R8
-      Exch 2
-      Exch $R7
-      Exch 3
-      Exch $R6
-      Exch 4
-      Exch $R5
-      Exch 5
-      Exch $R4
-      Exch 6
-      Exch $R3
-      Exch 7
-      Exch $R2
-      Push $R1
-      Push $R0
-
-      StrCpy $R0 "SOFTWARE\Classes"
-      StrCmp "$R5" "" +6 +1
-      ReadRegStr $R1 SHCTX "$R2" "FriendlyTypeName"
-
-      StrCmp "$R1" "" +1 +3
-      WriteRegStr SHCTX "$R0\$R2" "" "$R5"
-      WriteRegStr SHCTX "$R0\$R2" "FriendlyTypeName" "$R5"
-
-      StrCmp "$R6" "true" +1 +2
-      WriteRegStr SHCTX "$R0\$R2" "URL Protocol" ""
-      StrCmp "$R6" "delete" +1 +2
-      DeleteRegValue SHCTX "$R0\$R2" "URL Protocol"
-      StrCpy $R1 ""
-      ReadRegDWord $R1 SHCTX "$R0\$R2" "EditFlags"
-      StrCmp $R1 "" +1 +3  ; Only add EditFlags if a value doesn't exist
-      DeleteRegValue SHCTX "$R0\$R2" "EditFlags"
-      WriteRegDWord SHCTX "$R0\$R2" "EditFlags" 0x00000002
-
-      StrCmp "$R4" "" +2 +1
-      WriteRegStr SHCTX "$R0\$R2\DefaultIcon" "" "$R4"
-
-      WriteRegStr SHCTX "$R0\$R2\shell" "" "open"
-      WriteRegStr SHCTX "$R0\$R2\shell\open\command" "" "$R3"
-
-      WriteRegStr SHCTX "$R0\$R2\shell\open\ddeexec" "" "$R8"
-      WriteRegStr SHCTX "$R0\$R2\shell\open\ddeexec" "NoActivateHandler" ""
-      WriteRegStr SHCTX "$R0\$R2\shell\open\ddeexec\Application" "" "$R7"
-      WriteRegStr SHCTX "$R0\$R2\shell\open\ddeexec\Topic" "" "$R9"
-
-      ; The ifexec key may have been added by another application so try to
-      ; delete it to prevent it from breaking this app's shell integration.
-      ; Also, IE 6 and below doesn't remove this key when it sets itself as the
-      ; default handler and if this key exists IE's shell integration breaks.
-      DeleteRegKey HKLM "$R0\$R2\shell\open\ddeexec\ifexec"
-      DeleteRegKey HKCU "$R0\$R2\shell\open\ddeexec\ifexec"
-      ClearErrors
-
-      Pop $R0
-      Pop $R1
-      Exch $R2
-      Exch 7
-      Exch $R3
-      Exch 6
-      Exch $R4
-      Exch 5
-      Exch $R5
-      Exch 4
-      Exch $R6
-      Exch 3
-      Exch $R7
-      Exch 2
-      Exch $R8
-      Exch 1
-      Exch $R9
-    FunctionEnd
-
-    !verbose pop
-  !endif
-!macroend
-
-!macro AddDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _DDE_APPNAME _DDE_DEFAULT _DDE_TOPIC
-  !verbose push
-  !verbose ${_MOZFUNC_VERBOSE}
-  Push "${_KEY}"
-  Push "${_VALOPEN}"
-  Push "${_VALICON}"
-  Push "${_DISPNAME}"
-  Push "${_ISPROTOCOL}"
-  Push "${_DDE_APPNAME}"
-  Push "${_DDE_DEFAULT}"
-  Push "${_DDE_TOPIC}"
-  Call AddDDEHandlerValues
-  !verbose pop
-!macroend
-
-!macro un.AddDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _DDE_APPNAME _DDE_DEFAULT _DDE_TOPIC
-  !verbose push
-  !verbose ${_MOZFUNC_VERBOSE}
-  Push "${_KEY}"
-  Push "${_VALOPEN}"
-  Push "${_VALICON}"
-  Push "${_DISPNAME}"
-  Push "${_ISPROTOCOL}"
-  Push "${_DDE_APPNAME}"
-  Push "${_DDE_DEFAULT}"
-  Push "${_DDE_TOPIC}"
-  Call un.AddDDEHandlerValues
-  !verbose pop
-!macroend
-
-!macro un.AddDDEHandlerValues
-  !ifndef un.AddDDEHandlerValues
-    !verbose push
-    !verbose ${_MOZFUNC_VERBOSE}
-    !undef _MOZFUNC_UN
-    !define _MOZFUNC_UN "un."
-
-    !insertmacro AddDDEHandlerValues
-
-    !undef _MOZFUNC_UN
-    !define _MOZFUNC_UN
-    !verbose pop
-  !endif
-!macroend
-
-/**
  * Writes common registry values for a handler that DOES NOT use DDE using SHCTX.
  *
  * @param   _KEY
  *          The key name in relation to the HKCR root. SOFTWARE\Classes is
  *          prefixed to this value when using SHCTX.
  * @param   _VALOPEN
  *          The path and args to launch the application.
  * @param   _VALICON
@@ -6123,18 +5960,20 @@
       ${ElseIf} ${IsWinVista}
         ${LogMsg} "OS Name    : Windows Vista"
       ${ElseIf} ${IsWin7}
         ${LogMsg} "OS Name    : Windows 7"
       ${ElseIf} ${IsWin8}
         ${LogMsg} "OS Name    : Windows 8"
       ${ElseIf} ${IsWin8.1}
         ${LogMsg} "OS Name    : Windows 8.1"
-      ${ElseIf} ${AtLeastWin8.1}
-        ${LogMsg} "OS Name    : Above Windows 8.1"
+      ${ElseIf} ${IsWin10}
+        ${LogMsg} "OS Name    : Windows 10"
+      ${ElseIf} ${AtLeastWin10}
+        ${LogMsg} "OS Name    : Above Windows 10"
       ${Else}
         ${LogMsg} "OS Name    : Unable to detect"
       ${EndIf}
 
       !ifdef HAVE_64BIT_BUILD
         ${LogMsg} "Target CPU : x64"
       !else
         ${LogMsg} "Target CPU : x86"
--- a/toolkit/mozapps/installer/windows/nsis/overrides.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/overrides.nsh
@@ -43,16 +43,21 @@
   !insertmacro __MOZ__WinVer_DefineOSTests 2012
 !endif
 
 !ifndef WINVER_2012R2
   !define WINVER_2012R2    0x06030001 ;6.03.9600
   !insertmacro __MOZ__WinVer_DefineOSTests 2012R2
 !endif
 
+!ifndef WINVER_10
+  !define WINVER_10        0x0A000000 ;10.0.10240
+  !insertmacro __MOZ__WinVer_DefineOSTests 10
+!endif
+
 !verbose push
 !verbose 3
 !ifndef _OVERRIDE_VERBOSE
   !define _OVERRIDE_VERBOSE 3
 !endif
 !verbose ${_OVERRIDE_VERBOSE}
 !define OVERRIDE_VERBOSE `!insertmacro OVERRIDE_VERBOSE`
 !define _OVERRIDE_UN
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -98,21 +98,16 @@ interface nsIXULWindow : nsISupports
   const unsigned long lowestZ = 0;
   const unsigned long loweredZ = 4;  /* "alwaysLowered" attribute */
   const unsigned long normalZ = 5;
   const unsigned long raisedZ = 6;   /* "alwaysRaised" attribute */
   const unsigned long highestZ = 9;
 
   attribute unsigned long zLevel;
 
-  /**
-   * contextFlags are from nsIWindowCreator2
-   */
-  attribute uint32_t contextFlags;
-
   attribute uint32_t chromeFlags;
 
   /**
    * Begin assuming |chromeFlags| don't change hereafter, and assert
    * if they do change.  The state change is one-way and idempotent.
    */
   void assumeChromeFlagsAreFrozen();
 
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -100,17 +100,16 @@ nsXULWindow::nsXULWindow(uint32_t aChrom
     mIsHiddenWindow(false),
     mLockedUntilChromeLoad(false),
     mIgnoreXULSize(false),
     mIgnoreXULPosition(false),
     mChromeFlagsFrozen(false),
     mIgnoreXULSizeMode(false),
     mDestroying(false),
     mRegistered(false),
-    mContextFlags(0),
     mPersistentAttributesDirty(0),
     mPersistentAttributesMask(0),
     mChromeFlags(aChromeFlags)
 {
 }
 
 nsXULWindow::~nsXULWindow()
 {
@@ -249,29 +248,16 @@ NS_IMETHODIMP nsXULWindow::SetZLevel(uin
         bool defaultActionEnabled;
         doc->DispatchEvent(event, &defaultActionEnabled);
       }
     }
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP nsXULWindow::GetContextFlags(uint32_t *aContextFlags)
-{
-  NS_ENSURE_ARG_POINTER(aContextFlags);
-  *aContextFlags = mContextFlags;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsXULWindow::SetContextFlags(uint32_t aContextFlags)
-{
-  mContextFlags = aContextFlags;
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsXULWindow::GetChromeFlags(uint32_t *aChromeFlags)
 {
   NS_ENSURE_ARG_POINTER(aChromeFlags);
   *aChromeFlags = mChromeFlags;
   /* mChromeFlags is kept up to date, except for scrollbar visibility.
      That can be changed directly by the content DOM window, which
      doesn't know to update the chrome window. So that we must check
      separately. */
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -158,17 +158,16 @@ protected:
    bool                    mIgnoreXULSize;
    bool                    mIgnoreXULPosition;
    bool                    mChromeFlagsFrozen;
    bool                    mIgnoreXULSizeMode;
    // mDestroying is used to prevent reentry into into Destroy(), which can
    // otherwise happen due to script running as we tear down various things.
    bool                    mDestroying;
    bool                    mRegistered;
-   uint32_t                mContextFlags;
    uint32_t                mPersistentAttributesDirty; // persistentAttributes
    uint32_t                mPersistentAttributesMask;
    uint32_t                mChromeFlags;
    nsString                mTitle;
    nsIntRect               mOpenerScreenRect; // the screen rect of the opener
 
    nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only