Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 15 Jul 2015 13:30:09 -0400
changeset 253059 fca08fd490462b1f7102fc5fecc9198d72919273
parent 253058 83c2b44e598e455b6f3b999a7d7e74d9bec6ab3a (current diff)
parent 253022 49683d4e9ebd1db5de2aad70df5d43b034321488 (diff)
child 253060 ab40b1138e7ae4ac0b9c6a677b5dd318df15ccd9
push id29056
push userkwierso@gmail.com
push dateWed, 15 Jul 2015 23:34:03 +0000
treeherdermozilla-central@9a42bc345feb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
intl/unicharutil/tools/gentransliterate.pl
ipc/ril/RilSocket.cpp
ipc/ril/RilSocket.h
ipc/ril/RilSocketConsumer.cpp
ipc/ril/RilSocketConsumer.h
testing/web-platform/mozilla/meta/service-workers/service-worker/postmessage-msgport-to-client.https.html.ini
toolkit/devtools/server/tests/unit/test_originAttributesToCookieJar.js
--- a/accessible/mac/mozAccessible.h
+++ b/accessible/mac/mozAccessible.h
@@ -13,36 +13,50 @@
 @class mozRootAccessible;
 
 /**
  * All mozAccessibles are either abstract objects (that correspond to XUL
  * widgets, HTML frames, etc) or are attached to a certain view; for example
  * a document view. When we hand an object off to an AT, we always want
  * to give it the represented view, in the latter case.
  */
+
+namespace mozilla {
+namespace a11y {
+
 inline id <mozAccessible>
 GetObjectOrRepresentedView(id <mozAccessible> aObject)
 {
   return [aObject hasRepresentedView] ? [aObject representedView] : aObject;
 }
 
 inline mozAccessible*
-GetNativeFromGeckoAccessible(mozilla::a11y::Accessible* aAccessible)
+GetNativeFromGeckoAccessible(Accessible* aAccessible)
 {
   mozAccessible* native = nil;
   aAccessible->GetNativeInterface((void**)&native);
   return native;
 }
 
 inline mozAccessible*
-GetNativeFromProxy(mozilla::a11y::ProxyAccessible* aProxy)
+GetNativeFromProxy(const ProxyAccessible* aProxy)
 {
   return reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
 }
 
+ProxyAccessible* GetProxyUnignoredParent(const ProxyAccessible* aProxy);
+
+void GetProxyUnignoredChildren(const ProxyAccessible* aProxy,
+                               nsTArray<ProxyAccessible*>* aChildrenArray);
+
+BOOL IsProxyIgnored(const ProxyAccessible* aProxy);
+
+} // a11y
+} // mozilla
+
 // This is OR'd with the Accessible owner to indicate the wrap-ee is a proxy.
 static const uintptr_t IS_PROXY = 1;
 
 @interface mozAccessible : NSObject <mozAccessible>
 {
   /**
    * Weak reference; it owns us.
    */
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -71,34 +71,93 @@ GetClosestInterestingAccessible(id anObj
   if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)])
     return GetObjectOrRepresentedView(unignoredObject);
 
   return unignoredObject;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
+ProxyAccessible*
+a11y::GetProxyUnignoredParent(const ProxyAccessible* aProxy)
+{
+  ProxyAccessible* parent = aProxy->Parent();
+  while (parent && IsProxyIgnored(aProxy))
+    parent = parent->Parent();
+
+  return parent;
+}
+
+void
+a11y::GetProxyUnignoredChildren(const ProxyAccessible* aProxy,
+                                nsTArray<ProxyAccessible*>* aChildrenArray)
+{
+  if (aProxy->MustPruneChildren())
+    return;
+
+  uint32_t childCount = aProxy->ChildrenCount();
+  for (size_t childIdx = 0; childIdx < childCount; childIdx++) {
+    ProxyAccessible* childProxy = aProxy->ChildAt(childIdx);
+
+    // If element is ignored, then add its children as substitutes.
+    if (IsProxyIgnored(childProxy)) {
+      GetProxyUnignoredChildren(aProxy, aChildrenArray);
+      continue;
+    }
+
+    aChildrenArray->AppendElement(childProxy);
+  }
+}
+
+BOOL
+a11y::IsProxyIgnored(const ProxyAccessible* aProxy)
+{
+  mozAccessible* nativeObject = GetNativeFromProxy(aProxy);
+  if (!nativeObject)
+   return true;
+
+  return [nativeObject accessibilityIsIgnored];
+}
+
 // convert an array of Gecko accessibles to an NSArray of native accessibles
 static inline NSMutableArray*
 ConvertToNSArray(nsTArray<Accessible*>& aArray)
 {
   NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
 
   // iterate through the list, and get each native accessible.
-  uint32_t totalCount = aArray.Length();
-  for (uint32_t i = 0; i < totalCount; i++) {
+  size_t totalCount = aArray.Length();
+  for (size_t i = 0; i < totalCount; i++) {
     Accessible* curAccessible = aArray.ElementAt(i);
     mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible);
     if (curNative)
       [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
   }
 
   return nativeArray;
 }
 
+// convert an array of Gecko proxy accessibles to an NSArray of native accessibles
+static inline NSMutableArray*
+ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
+{
+  NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+
+  // iterate through the list, and get each native accessible.
+  size_t totalCount = aArray.Length();
+  for (size_t i = 0; i < totalCount; i++) {
+    ProxyAccessible* curAccessible = aArray.ElementAt(i);
+    mozAccessible* curNative = GetNativeFromProxy(curAccessible);
+    if (curNative)
+      [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+  }
+
+  return nativeArray;
+}
+
 #pragma mark -
 
 @implementation mozAccessible
 
 - (id)initWithAccessible:(uintptr_t)aGeckoAccessible
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
@@ -607,31 +666,44 @@ ConvertToNSArray(nsTArray<Accessible*>& 
 }
 
 #pragma mark -
 
 - (id <mozAccessible>)parent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-  AccessibleWrap* accWrap = [self getGeckoAccessible];
-  Accessible* accessibleParent = accWrap->GetUnignoredParent();
-  if (accessibleParent) {
-    id nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
+  id nativeParent = nil;
+  if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+    Accessible* accessibleParent = accWrap->GetUnignoredParent();
+    if (accessibleParent)
+      nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
     if (nativeParent)
       return GetClosestInterestingAccessible(nativeParent);
+    // GetUnignoredParent() returns null when there is no unignored accessible all the way up to
+    // the root accessible. so we'll have to return whatever native accessible is above our root accessible
+    // (which might be the owning NSWindow in the application, for example).
+    //
+    // get the native root accessible, and tell it to return its first parent unignored accessible.
+    nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible());
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    // Go up the chain to find a parent that is not ignored.
+    ProxyAccessible* accessibleParent = GetProxyUnignoredParent(proxy);
+    if (accessibleParent)
+      nativeParent = GetNativeFromProxy(accessibleParent);
+    if (nativeParent)
+      return GetClosestInterestingAccessible(nativeParent);
+
+    Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+    nativeParent = outerDoc ?
+      GetNativeFromGeckoAccessible(outerDoc->RootAccessible()) : nil;
+  } else {
+    return nil;
   }
 
-  // GetUnignoredParent() returns null when there is no unignored accessible all the way up to
-  // the root accessible. so we'll have to return whatever native accessible is above our root accessible
-  // (which might be the owning NSWindow in the application, for example).
-  //
-  // get the native root accessible, and tell it to return its first parent unignored accessible.
-  id nativeParent =
-    GetNativeFromGeckoAccessible(accWrap->RootAccessible());
   NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
 
   return GetClosestInterestingAccessible(nativeParent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)hasRepresentedView
@@ -651,23 +723,29 @@ ConvertToNSArray(nsTArray<Accessible*>& 
 
 // gets our native children lazily.
 // returns nil when there are no children.
 - (NSArray*)children
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   AccessibleWrap* accWrap = [self getGeckoAccessible];
-  if (mChildren || !accWrap->AreChildrenCached())
+  if (mChildren || (accWrap && !accWrap->AreChildrenCached()))
     return mChildren;
 
   // get the array of children.
-  nsAutoTArray<Accessible*, 10> childrenArray;
-  accWrap->GetUnignoredChildren(&childrenArray);
-  mChildren = ConvertToNSArray(childrenArray);
+  if (accWrap) {
+    nsAutoTArray<Accessible*, 10> childrenArray;
+    accWrap->GetUnignoredChildren(&childrenArray);
+    mChildren = ConvertToNSArray(childrenArray);
+  } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+    nsAutoTArray<ProxyAccessible*, 10> childrenArray;
+    GetProxyUnignoredChildren(proxy, &childrenArray);
+    mChildren = ConvertToNSArray(childrenArray);
+  }
 
 #ifdef DEBUG_hakan
   // make sure we're not returning any ignored accessibles.
   NSEnumerator *e = [mChildren objectEnumerator];
   mozAccessible *m = nil;
   while ((m = [e nextObject])) {
     NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m);
   }
--- a/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
+++ b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp
@@ -8,32 +8,33 @@
 #include "ApplicationAccessibleWrap.h"
 
 #include "AccessibleApplication_i.c"
 #include "IUnknownImpl.h"
 
 #include "nsIGfxInfo.h"
 #include "nsIPersistentProperties2.h"
 #include "nsServiceManagerUtils.h"
+#include "mozilla/Services.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 NS_IMPL_ISUPPORTS_INHERITED0(ApplicationAccessibleWrap,
                              ApplicationAccessible)
 
 already_AddRefed<nsIPersistentProperties>
 ApplicationAccessibleWrap::NativeAttributes()
 {
   nsCOMPtr<nsIPersistentProperties> attributes =
     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
 
-  nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     bool isD2DEnabled = false;
     gfxInfo->GetD2DEnabled(&isD2DEnabled);
     nsAutoString unused;
     attributes->SetStringProperty(
       NS_LITERAL_CSTRING("D2D"),
       isD2DEnabled ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"),
         unused);
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -328,17 +328,16 @@ pref("media.eme.enabled", true);
 pref("media.eme.apiVisible", true);
 
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.downscale-during-decode.enabled", true);
-pref("image.decode-only-on-draw.enabled", false);
 pref("image.mem.allow_locking_in_content_processes", true);
 pref("image.decode.retry-on-alloc-failure", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "c6ef08964711f461a8e6326eae911789d1ec220c", 
+        "git_revision": "b9968cdc4a1dee49848fed6159a59c378cea062d", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "422a64c4639a399fda83fb6ba9af46254a659421", 
+    "revision": "275f9f64b346b08c91bbe10bfc786827a5bd0531", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b9968cdc4a1dee49848fed6159a59c378cea062d"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>
--- a/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
+++ b/browser/components/sessionstore/test/browser_615394-SSWindowState_events.js
@@ -37,17 +37,17 @@ function getOuterWindowID(aWindow) {
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
          getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
 }
 
 function test() {
   /** Test for Bug 615394 - Session Restore should notify when it is beginning and ending a restore **/
   waitForExplicitFinish();
   // Preemptively extend the timeout to prevent [orange]
-  requestLongerTimeout(2);
+  requestLongerTimeout(4);
   runNextTest();
 }
 
 
 let tests = [
   test_setTabState,
   test_duplicateTab,
   test_undoCloseTab,
--- a/browser/config/mozconfigs/linux32/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux32/l10n-mozconfig
@@ -1,9 +1,8 @@
-no_tooltool=1
 no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/unix/mozconfig.linux32
--- a/browser/config/mozconfigs/linux64/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux64/l10n-mozconfig
@@ -1,9 +1,8 @@
-no_tooltool=1
 no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/unix/mozconfig.linux
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/opt-tsan
@@ -0,0 +1,8 @@
+ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
+
+. $topsrcdir/build/unix/mozconfig.tsan
+
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL=tsan
+
+. "$topsrcdir/build/mozconfig.common.override"
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -0,0 +1,12 @@
+[
+{
+"clang_version": "r241773"
+}, 
+{
+"size": 89690541, 
+"digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2",
+"unpack": true
+}
+]
--- a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
@@ -177,17 +177,17 @@ function test() {
         is(allSwatches[1].textContent, "rgba(33,180,226,1)");
         is(allSwatches[2].textContent, "rgba(31,170,217,.5)");
         is(allSwatches[3].textContent, "#F06");
         is(allSwatches[4].textContent, "red");
       }
     },
     {
       name: "background",
-      value: "radial-gradient(circle closest-side at center, orange 0%, red 100%)",
+      value: "-moz-radial-gradient(center 45deg, circle closest-side, orange 0%, red 100%)",
       test: fragment => {
         is(countAll(fragment), 4);
         let allSwatches = fragment.querySelectorAll("." + COLOR_CLASS);
         is(allSwatches.length, 2);
         is(allSwatches[0].textContent, "orange");
         is(allSwatches[1].textContent, "red");
       }
     },
new file mode 100644
--- /dev/null
+++ b/build/unix/build-clang/clang-3.6.json
@@ -0,0 +1,8 @@
+{
+    "llvm_revision": "241773",
+    "llvm_repo": "http://llvm.org/svn/llvm-project/llvm/branches/release_36",
+    "clang_repo": "http://llvm.org/svn/llvm-project/cfe/branches/release_36",
+    "compiler_repo": "http://llvm.org/svn/llvm-project/compiler-rt/branches/release_36",
+    "patches": {
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/unix/mozconfig.tsan
@@ -0,0 +1,34 @@
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. "$topsrcdir/build/mozconfig.common"
+
+# Use Clang as specified in manifest
+export CC="$topsrcdir/clang/bin/clang"
+export CXX="$topsrcdir/clang/bin/clang++"
+export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
+
+# Mandatory flag for TSan
+export CFLAGS="-fsanitize=thread"
+export CXXFLAGS="-fsanitize=thread"
+export LDFLAGS="-fsanitize=thread"
+
+# Enable TSan specific code and build workarounds
+ac_add_options --enable-thread-sanitizer
+
+# The ThreadSanitizer is not compatible with sanboxing
+# (see bug 1182565)
+ac_add_options --disable-sandbox
+
+# These are required by TSan
+ac_add_options --disable-jemalloc
+ac_add_options --disable-crashreporter
+ac_add_options --disable-elf-hack
+ac_add_options --enable-pie
+
+# Keep symbols to symbolize TSan traces
+ac_add_options --disable-install-strip
+# -gline-tables-only results in significantly smaller binaries.
+ac_add_options --enable-debug-symbols="-gline-tables-only"
+
+# Avoid dependency on libstdc++ 4.7
+ac_add_options --enable-stdcxx-compat
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -140,22 +140,16 @@ OriginAttributes::PopulateFromOrigin(con
     aOriginNoSuffix = origin;
     return true;
   }
 
   aOriginNoSuffix = Substring(origin, 0, pos);
   return PopulateFromSuffix(Substring(origin, pos));
 }
 
-void
-OriginAttributes::CookieJar(nsACString& aStr)
-{
-  mozilla::GetJarPrefix(mAppId, mInBrowser, aStr);
-}
-
 BasePrincipal::BasePrincipal()
 {}
 
 BasePrincipal::~BasePrincipal()
 {}
 
 NS_IMETHODIMP
 BasePrincipal::GetOrigin(nsACString& aOrigin)
@@ -254,17 +248,17 @@ BasePrincipal::GetIsNullPrincipal(bool* 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
   MOZ_ASSERT(AppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  mOriginAttributes.CookieJar(aJarPrefix);
+  mozilla::GetJarPrefix(mOriginAttributes.mAppId, mOriginAttributes.mInBrowser, aJarPrefix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
 {
   if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
     return NS_ERROR_FAILURE;
@@ -275,23 +269,16 @@ BasePrincipal::GetOriginAttributes(JSCon
 NS_IMETHODIMP
 BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes)
 {
   mOriginAttributes.CreateSuffix(aOriginAttributes);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-BasePrincipal::GetCookieJar(nsACString& aCookieJar)
-{
-  mOriginAttributes.CookieJar(aCookieJar);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 BasePrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   if (AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     NS_WARNING("Asking for app status on a principal with an unknown app id");
     *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
     return NS_OK;
   }
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -43,24 +43,55 @@ public:
   }
 
   // Serializes/Deserializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
   // returns an empty string.
   void CreateSuffix(nsACString& aStr) const;
   bool PopulateFromSuffix(const nsACString& aStr);
 
-  void CookieJar(nsACString& aStr);
-
   // Populates the attributes from a string like
   // |uri!key1=value1&key2=value2| and returns the uri without the suffix.
   bool PopulateFromOrigin(const nsACString& aOrigin,
                           nsACString& aOriginNoSuffix);
 };
 
+class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary
+{
+public:
+  // To convert a JSON string to an OriginAttributesPattern, do the following:
+  //
+  // OriginAttributesPattern pattern;
+  // if (!pattern.Init(aJSONString)) {
+  //   ... // handle failure.
+  // }
+  OriginAttributesPattern() {}
+
+  explicit OriginAttributesPattern(const OriginAttributesPatternDictionary& aOther)
+    : OriginAttributesPatternDictionary(aOther) {}
+
+  // Performs a match of |aAttrs| against this pattern.
+  bool Matches(const OriginAttributes& aAttrs) const
+  {
+    if (mAppId.WasPassed() && mAppId.Value() != aAttrs.mAppId) {
+      return false;
+    }
+
+    if (mInBrowser.WasPassed() && mInBrowser.Value() != aAttrs.mInBrowser) {
+      return false;
+    }
+
+    if (mAddonId.WasPassed() && mAddonId.Value() != aAttrs.mAddonId) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
 /*
  * Base class from which all nsIPrincipal implementations inherit. Use this for
  * default implementations and other commonalities between principal
  * implementations.
  *
  * We should merge nsJSPrincipals into this class at some point.
  */
 class BasePrincipal : public nsJSPrincipals
@@ -79,17 +110,16 @@ public:
   NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
   NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
   NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) override;
   NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix) final;
   NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
   NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
-  NS_IMETHOD GetCookieJar(nsACString& aCookieJar) final;
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
   NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) final;
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
 
   virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
 
   virtual bool IsCodebasePrincipal() const { return false; };
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -15,17 +15,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, builtinclass, uuid(49c2faf0-b6de-4640-8d0f-e0217baa8627)]
+[scriptable, builtinclass, uuid(7a9aa074-7565-4567-af2f-9e3704c7af9e)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Returns whether the other principal is equivalent to this principal.
      * Principals are considered equal if they are the same principal, or
      * they have the same origin.
      */
     boolean equals(in nsIPrincipal other);
@@ -200,40 +200,16 @@ interface nsIPrincipal : nsISerializable
      * special requirements must inspect and compare .originSuffix manually.
      *
      * originsuffix are intended to be a replacement for jarPrefix, which will
      * eventually be removed.
      */
     readonly attribute AUTF8String originSuffix;
 
     /**
-     * Opaque string token representing the "cookie jar" associated with this
-     * principal. Cookie jars are intended to be a tag associated with persistent
-     * data (like cookies, localStorage data, etc) such that all data associated
-     * with a given cookie jar can be quickly located and (for example) deleted.
-     * Code from many origins may share a given cookie jar, so callers still need
-     * to consult .origin (or equivalent) to compartmentalize data - the cookie
-     * jar should _only_ be used as a tag in the manner described above.
-     *
-     * If two principals are in different cookie jars, they must be cross-origin.
-     * As such, the information making up the cookie jar token must be contained
-     * in the originAttributes (i.e. cookieJar must be a function of / derivable
-     * from originAttributes). Long term, the intention is for the cookie jar
-     * identifier to simply be an origin attribute. But we don't have that
-     * attribute yet, and we also need to concatenate the appId and inBrowser
-     * attributes until those go away.
-     *
-     * This getter is designed to hide these details from consumers so that they
-     * don't need to be updated when we swap out the implementation. For that
-     * reason, callers should treat the string as opaque and not rely on the
-     * current format.
-     */
-    readonly attribute ACString cookieJar;
-
-    /**
      * The base domain of the codebase URI to which this principal pertains
      * (generally the document URI), handling null principals and
      * non-hierarchical schemes correctly.
      */
     readonly attribute ACString baseDomain;
 
     const short APP_STATUS_NOT_INSTALLED = 0;
     const short APP_STATUS_INSTALLED     = 1;
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -15,19 +15,16 @@ function checkThrows(f) {
 
 function checkCrossOrigin(a, b) {
   do_check_false(a.equals(b));
   do_check_false(a.equalsConsideringDomain(b));
   do_check_false(a.subsumes(b));
   do_check_false(a.subsumesConsideringDomain(b));
   do_check_false(b.subsumes(a));
   do_check_false(b.subsumesConsideringDomain(a));
-  do_check_eq(a.cookieJar === b.cookieJar,
-              a.originAttributes.appId == b.originAttributes.appId &&
-              a.originAttributes.inBrowser == b.originAttributes.inBrowser);
 }
 
 function checkOriginAttributes(prin, attrs, suffix) {
   attrs = attrs || {};
   do_check_eq(prin.originAttributes.appId, attrs.appId || 0);
   do_check_eq(prin.originAttributes.inBrowser, attrs.inBrowser || false);
   do_check_eq(prin.originSuffix, suffix || '');
   if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -4846,32 +4846,26 @@ this.DOMApplicationRegistry = {
   },
 
   _clearPrivateData: function(appId, browserOnly, msg) {
     let subject = {
       appId: appId,
       browserOnly: browserOnly,
       QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
     };
-    this._clearCookieJarData(appId, browserOnly);
+    this._clearOriginData(appId, browserOnly);
     this._notifyCategoryAndObservers(subject, "webapps-clear-data", null, msg);
   },
 
-  _clearCookieJarData: function(appId, browserOnly) {
-    let browserCookieJar =
-      ChromeUtils.originAttributesToCookieJar({appId: appId,
-                                               inBrowser: true});
-    this._notifyCategoryAndObservers(null, "clear-cookiejar-data", browserCookieJar);
-
-    if (!browserOnly) {
-      let appCookieJar =
-        ChromeUtils.originAttributesToCookieJar({appId: appId,
-                                                 inBrowser: false});
-      this._notifyCategoryAndObservers(null, "clear-cookiejar-data", appCookieJar);
+  _clearOriginData: function(appId, browserOnly) {
+    let attributes = {appId: appId};
+    if (browserOnly) {
+      attributes.inBrowser = true;
     }
+    this._notifyCategoryAndObservers(null, "clear-origin-data", JSON.stringify(attributes));
   }
 };
 
 /**
  * Appcache download observer
  */
 let AppcacheObserver = function(aApp) {
   debug("Creating AppcacheObserver for " + aApp.origin +
--- a/dom/apps/tests/test_bug_1168300.html
+++ b/dom/apps/tests/test_bug_1168300.html
@@ -47,44 +47,41 @@ https://bugzilla.mozilla.org/show_bug.cg
     yield undefined;
 
     let request = navigator.mozApps.install(url);
     request.onerror = mozAppsError;
     request.onsuccess = continueTest;
     yield undefined;
     let app = request.result;
 
-    let _topic = "clear-cookiejar-data";
+    let _topic = "clear-origin-data";
     let observer = new Observer(_topic);
     observer.onobserve = function(subject, topic, data, count) {
       ok(topic == _topic, "unknown topic " + topic);
 
-      let cookieJar = data;
-      ok(cookieJar, "params.cookieJar should have value for an app.");
+      let props = Object.getOwnPropertyNames(JSON.parse(data));
+      is(props.length, 1, "pattern should have one property");
+      is(props[0], 'appId', "pattern property should be appId");
 
-      // should receive 2 notifications when an app is uninstalled.
-      if (count == 2) {
-        ok(true);
-        observer.shutdown();
-        continueTest();
-      }
+      observer.shutdown();
+      continueTest();
     };
 
     request = navigator.mozApps.mgmt.uninstall(app);
     request.onerror = mozAppsError;
     navigator.mozApps.mgmt.onuninstall = function(evt) {
       if (evt.application.manifestURL != url) {
         return;
       }
 
       ok(true, "got onuninstall event");
       continueTest();
     };
 
-    // we now wait for "clear-cookiejar-data" notifications and onuninstall
+    // we now wait for "clear-origin-data" notifications and onuninstall
     // callback.
     yield undefined;
     yield undefined;
 
     navigator.mozApps.mgmt.onuninstall = null;
   }
 
   function Observer(topic) {
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -180,17 +180,17 @@ void
 AudioChannelService::Shutdown()
 {
   if (gAudioChannelService) {
     if (IsParentProcess()) {
       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
       if (obs) {
         obs->RemoveObserver(gAudioChannelService, "ipc:content-shutdown");
         obs->RemoveObserver(gAudioChannelService, "xpcom-shutdown");
-        obs->RemoveObserver(gAudioChannelService, "inner-window-destroyed");
+        obs->RemoveObserver(gAudioChannelService, "outer-window-destroyed");
 #ifdef MOZ_WIDGET_GONK
         // To monitor the volume settings based on audio channel.
         obs->RemoveObserver(gAudioChannelService, "mozsettings-changed");
 #endif
       }
     }
 
     gAudioChannelService = nullptr;
@@ -213,17 +213,17 @@ AudioChannelService::AudioChannelService
   , mContentOrNormalChannel(false)
   , mAnyChannel(false)
 {
   if (IsParentProcess()) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
       obs->AddObserver(this, "xpcom-shutdown", false);
-      obs->AddObserver(this, "inner-window-destroyed", false);
+      obs->AddObserver(this, "outer-window-destroyed", false);
 #ifdef MOZ_WIDGET_GONK
       // To monitor the volume settings based on audio channel.
       obs->AddObserver(this, "mozsettings-changed", false);
 #endif
     }
   }
 
   Preferences::AddBoolVarCache(&sAudioChannelMutedByDefault,
@@ -503,33 +503,33 @@ AudioChannelService::Observe(nsISupports
       // We didn't use MOZ_CRASH or MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE here
       // because any web content who has permission of mozSettings can set any
       // names then it can be easy to crash the B2G.
       NS_WARNING("unexpected audio channel for volume control");
     }
   }
 #endif
 
-  else if (!strcmp(aTopic, "inner-window-destroyed")) {
+  else if (!strcmp(aTopic, "outer-window-destroyed")) {
     nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
-    uint64_t innerID;
-    nsresult rv = wrapper->GetData(&innerID);
+    uint64_t outerID;
+    nsresult rv = wrapper->GetData(&outerID);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsAutoPtr<AudioChannelWindow> winData;
     {
       nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
         iter(mWindows);
       while (iter.HasMore()) {
         nsAutoPtr<AudioChannelWindow>& next = iter.GetNext();
-        if (next->mWindowID == innerID) {
+        if (next->mWindowID == outerID) {
           uint32_t pos = mWindows.IndexOf(next);
           winData = next.forget();
           mWindows.RemoveElementAt(pos);
           break;
         }
       }
     }
 
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -6,28 +6,29 @@
 #include "ChromeUtils.h"
 
 #include "mozilla/BasePrincipal.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ void
-ChromeUtils::OriginAttributesToCookieJar(GlobalObject& aGlobal,
-                                         const OriginAttributesDictionary& aAttrs,
-                                         nsCString& aCookieJar)
-{
-  OriginAttributes attrs(aAttrs);
-  attrs.CookieJar(aCookieJar);
-}
-
-/* static */ void
 ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
                                       const dom::OriginAttributesDictionary& aAttrs,
                                       nsCString& aSuffix)
 
 {
   OriginAttributes attrs(aAttrs);
   attrs.CreateSuffix(aSuffix);
 }
 
+/* static */ bool
+ChromeUtils::OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
+                                          const dom::OriginAttributesDictionary& aAttrs,
+                                          const dom::OriginAttributesPatternDictionary& aPattern)
+{
+  OriginAttributes attrs(aAttrs);
+  OriginAttributesPattern pattern(aPattern);
+  return pattern.Matches(attrs);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -36,22 +36,22 @@ public:
                                                                    const nsAString& filePath,
                                                                    ErrorResult& rv);
 };
 
 class ChromeUtils : public ThreadSafeChromeUtils
 {
 public:
   static void
-  OriginAttributesToCookieJar(dom::GlobalObject& aGlobal,
-                              const dom::OriginAttributesDictionary& aAttrs,
-                              nsCString& aCookieJar);
-
-  static void
   OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
                            const dom::OriginAttributesDictionary& aAttrs,
                            nsCString& aSuffix);
+
+  static bool
+  OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
+                               const dom::OriginAttributesDictionary& aAttrs,
+                               const dom::OriginAttributesPatternDictionary& aPattern);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -795,17 +795,20 @@ nsFrameMessageManager::DispatchAsyncMess
     int32_t len = mChildManagers.Count();
     for (int32_t i = 0; i < len; ++i) {
       static_cast<nsFrameMessageManager*>(mChildManagers[i])->
          DispatchAsyncMessageInternal(aCx, aMessage, aData, aCpows, aPrincipal);
     }
     return NS_OK;
   }
 
-  NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
+  if (!mCallback) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
   if (!mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName,
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2108,20 +2108,16 @@ GK_ATOM(x_guru, "x-guru")
 GK_ATOM(x_khmr, "x-khmr")
 GK_ATOM(x_knda, "x-knda")
 GK_ATOM(x_mlym, "x-mlym")
 GK_ATOM(x_orya, "x-orya")
 GK_ATOM(x_sinh, "x-sinh")
 GK_ATOM(x_telu, "x-telu")
 GK_ATOM(x_tibt, "x-tibt")
 
-// used in gfxGDIFontList.h
-GK_ATOM(ko_xxx, "ko-xxx")
-GK_ATOM(x_symbol, "x-symbol")
-
 // additional languages that have special case transformations
 GK_ATOM(az, "az")
 GK_ATOM(ba, "ba")
 GK_ATOM(crh, "crh")
 GK_ATOM(el, "el")
 GK_ATOM(ga, "ga")
 GK_ATOM(nl, "nl")
 
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -1124,17 +1124,19 @@ nsRange::SetStart(nsIDOMNode* aParent, i
   SetStart(*parent, aOffset, rv);
   return rv.StealNSResult();
 }
 
 /* virtual */ nsresult
 nsRange::SetStart(nsINode* aParent, int32_t aOffset)
 {
   nsINode* newRoot = IsValidBoundary(aParent);
-  NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
+  if (!newRoot) {
+    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+  }
 
   if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, if positioned in another doc or
   // if the new start is after end.
   if (!mIsPositioned || newRoot != mRoot ||
@@ -1223,17 +1225,19 @@ nsRange::SetEnd(nsIDOMNode* aParent, int
   SetEnd(*parent, aOffset, rv);
   return rv.StealNSResult();
 }
 
 /* virtual */ nsresult
 nsRange::SetEnd(nsINode* aParent, int32_t aOffset)
 {
   nsINode* newRoot = IsValidBoundary(aParent);
-  NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
+  if (!newRoot) {
+    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
+  }
 
   if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, if positioned in another doc or
   // if the new end is before start.
   if (!mIsPositioned || newRoot != mRoot ||
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -30,17 +30,16 @@
 #include "mozilla/MemoryReporting.h"
 #include "nsIGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsISupportsImpl.h"
 #include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
-#include "pldhash.h" // For PLDHashOperator
 
 #include "nsWrapperCacheInlines.h"
 
 class nsIJSID;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
@@ -2141,27 +2140,23 @@ public:
         T& val = seqp->Value();
         T* ptr = &val;
         SequenceTracer<T>::TraceSequence(trc, ptr, ptr+1);
       }
     }
   }
 };
 
-// XXXbz It's not clear whether it's better to add a pldhash dependency here
-// (for PLDHashOperator) or add a BindingUtils.h dependency (for
-// SequenceTracer) to MozMap.h...
 template<typename T>
-static PLDHashOperator
+static void
 TraceMozMapValue(T* aValue, void* aClosure)
 {
   JSTracer* trc = static_cast<JSTracer*>(aClosure);
   // Act like it's a one-element sequence to leverage all that infrastructure.
   SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1);
-  return PL_DHASH_NEXT;
 }
 
 template<typename T>
 void TraceMozMap(JSTracer* trc, MozMap<T>& map)
 {
   map.EnumerateValues(TraceMozMapValue<T>, trc);
 }
 
--- a/dom/bindings/MozMap.h
+++ b/dom/bindings/MozMap.h
@@ -84,57 +84,38 @@ public:
     const EntryType* ent = this->GetEntry(aKey);
     if (!ent) {
       return nullptr;
     }
     return &ent->mData;
   }
 
   void GetKeys(nsTArray<nsString>& aKeys) const {
-    // Sadly, EnumerateEntries is not a const method
-    const_cast<SelfType*>(this)->EnumerateEntries(KeyEnumerator, &aKeys);
+    for (auto iter = this->ConstIter(); !iter.Done(); iter.Next()) {
+      aKeys.AppendElement(iter.Get()->GetKey());
+    }
   }
 
   // XXXbz we expose this generic enumerator for tracing.  Otherwise we'd end up
   // with a dependency on BindingUtils.h here for the SequenceTracer bits.
-  typedef PLDHashOperator (* Enumerator)(DataType* aValue, void* aClosure);
+  typedef void (* Enumerator)(DataType* aValue, void* aClosure);
   void EnumerateValues(Enumerator aEnumerator, void *aClosure)
   {
-    ValueEnumClosure args = { aEnumerator, aClosure };
-    this->EnumerateEntries(ValueEnumerator, &args);
+    for (auto iter = this->Iter(); !iter.Done(); iter.Next()) {
+      aEnumerator(&iter.Get()->mData, aClosure);
+    }
   }
 
   MOZ_WARN_UNUSED_RESULT
   DataType* AddEntry(const nsAString& aKey)
   {
     EntryType* ent = this->PutEntry(aKey, fallible);
     if (!ent) {
       return nullptr;
     }
     return &ent->mData;
   }
-
-private:
-  static PLDHashOperator
-  KeyEnumerator(EntryType* aEntry, void* aClosure)
-  {
-    nsTArray<nsString>& keys = *static_cast<nsTArray<nsString>*>(aClosure);
-    keys.AppendElement(aEntry->GetKey());
-    return PL_DHASH_NEXT;
-  }
-
-  struct ValueEnumClosure {
-    Enumerator mEnumerator;
-    void* mClosure;
-  };
-
-  static PLDHashOperator
-  ValueEnumerator(EntryType* aEntry, void* aClosure)
-  {
-    ValueEnumClosure* enumClosure = static_cast<ValueEnumClosure*>(aClosure);
-    return enumClosure->mEnumerator(&aEntry->mData, enumClosure->mClosure);
-  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MozMap_h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -111,16 +111,17 @@
 #include "SVGContentUtils.h"
 #include "SVGImageContext.h"
 #include "nsIScreenManager.h"
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 #include "Units.h"
+#include "CanvasUtils.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
 #include "SurfaceTypes.h"
 #include "GLBlitHelper.h"
 #endif
@@ -2575,17 +2576,16 @@ CanvasRenderingContext2D::BeginPath()
   mPathBuilder = nullptr;
   mDSPathBuilder = nullptr;
   mPathTransformWillUpdate = false;
 }
 
 void
 CanvasRenderingContext2D::Fill(const CanvasWindingRule& winding)
 {
-  EnsureTarget();
   EnsureUserSpacePath(winding);
 
   if (!mPath) {
     return;
   }
 
   mgfx::Rect bounds;
 
@@ -2621,17 +2621,16 @@ void CanvasRenderingContext2D::Fill(cons
          DrawOptions(CurrentState().globalAlpha, UsedOperation()));
 
   Redraw();
 }
 
 void
 CanvasRenderingContext2D::Stroke()
 {
-  EnsureTarget();
   EnsureUserSpacePath();
 
   if (!mPath) {
     return;
   }
 
   const ContextState &state = CurrentState();
 
@@ -2757,18 +2756,16 @@ bool CanvasRenderingContext2D::DrawCusto
   }
 
   return false;
 }
 
 void
 CanvasRenderingContext2D::Clip(const CanvasWindingRule& winding)
 {
-  EnsureTarget();
-
   EnsureUserSpacePath(winding);
 
   if (!mPath) {
     return;
   }
 
   mTarget->PushClip(mPath);
   CurrentState().clipsPushed.push_back(mPath);
@@ -2900,16 +2897,18 @@ CanvasRenderingContext2D::Rect(double x,
     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
     mDSPathBuilder->Close();
   }
 }
 
 void
 CanvasRenderingContext2D::EnsureWritablePath()
 {
+  EnsureTarget();
+
   if (mDSPathBuilder) {
     return;
   }
 
   FillRule fillRule = CurrentState().fillRule;
 
   if (mPathBuilder) {
     if (mPathTransformWillUpdate) {
@@ -2918,17 +2917,16 @@ CanvasRenderingContext2D::EnsureWritable
         mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
       mPath = nullptr;
       mPathBuilder = nullptr;
       mPathTransformWillUpdate = false;
     }
     return;
   }
 
-  EnsureTarget();
   if (!mPath) {
     NS_ASSERTION(!mPathTransformWillUpdate, "mPathTransformWillUpdate should be false, if all paths are null");
     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
   } else if (!mPathTransformWillUpdate) {
     mPathBuilder = mPath->CopyToBuilder(fillRule);
   } else {
     mDSPathBuilder =
       mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
@@ -2939,18 +2937,19 @@ CanvasRenderingContext2D::EnsureWritable
 
 void
 CanvasRenderingContext2D::EnsureUserSpacePath(const CanvasWindingRule& winding)
 {
   FillRule fillRule = CurrentState().fillRule;
   if(winding == CanvasWindingRule::Evenodd)
     fillRule = FillRule::FILL_EVEN_ODD;
 
+  EnsureTarget();
+
   if (!mPath && !mPathBuilder && !mDSPathBuilder) {
-    EnsureTarget();
     mPathBuilder = mTarget->CreatePathBuilder(fillRule);
   }
 
   if (mPathBuilder) {
     mPath = mPathBuilder->Finish();
     mPathBuilder = nullptr;
   }
 
@@ -3207,16 +3206,17 @@ CanvasRenderingContext2D::MeasureText(co
   return new TextMetrics(width);
 }
 
 void
 CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& options, ErrorResult& error)
 {
   RefPtr<gfx::Path> path;
   if (options.mPath) {
+    EnsureTarget();
     path = options.mPath->GetPath(CanvasWindingRule::Nonzero, mTarget);
   }
 
   if (!path) {
     // check if the path is valid
     EnsureUserSpacePath(CanvasWindingRule::Nonzero);
     path = mPath;
   }
@@ -5273,22 +5273,22 @@ CanvasRenderingContext2D::PutImageData_e
     return NS_ERROR_FAILURE;
   }
 
   uint32_t copyX = dirtyRect.x - x;
   uint32_t copyY = dirtyRect.y - y;
   //uint8_t *src = aArray->Data();
   uint8_t *dst = imgsurf->Data();
   uint8_t* srcLine = aArray->Data() + copyY * (w * 4) + copyX * 4;
-#if 0  
+#if 0
   printf("PutImageData_explicit: dirty x=%d y=%d w=%d h=%d copy x=%d y=%d w=%d h=%d ext x=%d y=%d w=%d h=%d\n",
 	     dirtyRect.x, dirtyRect.y, copyWidth, copyHeight,
 	     copyX, copyY, copyWidth, copyHeight,
 	     x, y, w, h);
-#endif	     
+#endif
   for (uint32_t j = 0; j < copyHeight; j++) {
     uint8_t *src = srcLine;
     for (uint32_t i = 0; i < copyWidth; i++) {
       uint8_t r = *src++;
       uint8_t g = *src++;
       uint8_t b = *src++;
       uint8_t a = *src++;
 
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -8,17 +8,16 @@
 #include "mozilla/Attributes.h"
 #include <vector>
 #include "nsIDOMCanvasRenderingContext2D.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/RefPtr.h"
 #include "nsColor.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
-#include "CanvasUtils.h"
 #include "gfxTextRun.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
--- a/dom/canvas/WebGL1Context.cpp
+++ b/dom/canvas/WebGL1Context.cpp
@@ -23,19 +23,19 @@ WebGL1Context::WebGL1Context()
     mFormatUsage = Move(webgl::FormatUsageAuthority::CreateForWebGL1());
 }
 
 WebGL1Context::~WebGL1Context()
 {
 }
 
 JSObject*
-WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLRenderingContextBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLRenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
 bool
 WebGL1Context::ValidateQueryTarget(GLenum target, const char* info)
 {
     // TODO: Implement this for EXT_disjoint_timer
     return false;
 }
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -22,17 +22,17 @@ private:
 public:
     virtual ~WebGL1Context();
 
     virtual bool IsWebGL2() const override {
         return false;
     }
 
     // nsWrapperCache
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 private:
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
--- a/dom/canvas/WebGL1ContextBuffers.cpp
+++ b/dom/canvas/WebGL1ContextBuffers.cpp
@@ -1,19 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL1Context.h"
-#include "WebGLBuffer.h"
-#include "GLContext.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Buffer objects
 
 /** Target validation for BindBuffer, etc */
 bool
 WebGL1Context::ValidateBufferTarget(GLenum target, const char* info)
 {
@@ -45,8 +42,10 @@ WebGL1Context::ValidateBufferUsageEnum(G
         return true;
     default:
         break;
     }
 
     ErrorInvalidEnumInfo(info, usage);
     return false;
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -38,19 +38,19 @@ WebGL2Context::IsSupported()
 
 /*static*/ WebGL2Context*
 WebGL2Context::Create()
 {
     return new WebGL2Context();
 }
 
 JSObject*
-WebGL2Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGL2Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGL2RenderingContextBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGL2RenderingContextBinding::Wrap(cx, this, givenProto);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGL 2 initialisation
 
 // These WebGL 1 extensions are natively supported by WebGL 2.
 static const WebGLExtensionID kNativelySupportedExtensions[] = {
     WebGLExtensionID::ANGLE_instanced_arrays,
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -33,17 +33,17 @@ public:
     virtual bool IsWebGL2() const override
     {
         return true;
     }
 
     // -------------------------------------------------------------------------
     // IMPLEMENT nsWrapperCache
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     // -------------------------------------------------------------------------
     // Buffer objects - WebGL2ContextBuffers.cpp
 
     void CopyBufferSubData(GLenum readTarget, GLenum writeTarget,
                            GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
     void GetBufferSubData(GLenum target, GLintptr offset,
                           const dom::Nullable<dom::ArrayBuffer>& maybeData);
@@ -53,39 +53,39 @@ public:
     // Framebuffer objects - WebGL2ContextFramebuffers.cpp
 
     void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                          GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                          GLbitfield mask, GLenum filter);
     void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
     void GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval);
     void InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
-                               ErrorResult& aRv);
+                               ErrorResult& rv);
     void InvalidateSubFramebuffer (GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y,
-                                   GLsizei width, GLsizei height, ErrorResult& aRv);
+                                   GLsizei width, GLsizei height, ErrorResult& rv);
     void ReadBuffer(GLenum mode);
     void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat,
                                         GLsizei width, GLsizei height);
 
 
     // -------------------------------------------------------------------------
     // Texture objects - WebGL2ContextTextures.cpp
 
     void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
     void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
                       GLsizei depth);
     void TexImage3D(GLenum target, GLint level, GLenum internalformat,
                     GLsizei width, GLsizei height, GLsizei depth,
                     GLint border, GLenum format, GLenum type,
-                    const Nullable<dom::ArrayBufferView> &pixels,
+                    const dom::Nullable<dom::ArrayBufferView>& pixels,
                     ErrorResult& rv);
     void TexSubImage3D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth,
-                       GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
+                       GLenum format, GLenum type, const dom::Nullable<dom::ArrayBufferView>& pixels,
                        ErrorResult& rv);
     void TexSubImage3D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLint zoffset,
                        GLenum format, GLenum type, dom::ImageData* data,
                        ErrorResult& rv);
     template<class ElementType>
     void TexSubImage3D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLint zoffset,
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -2,19 +2,19 @@
 /* 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 "WebGL2Context.h"
 
 #include "GLContext.h"
 #include "WebGLBuffer.h"
+#include "WebGLTransformFeedback.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 bool
 WebGL2Context::ValidateBufferTarget(GLenum target, const char* info)
 {
     switch (target) {
     case LOCAL_GL_ARRAY_BUFFER:
     case LOCAL_GL_COPY_READ_BUFFER:
     case LOCAL_GL_COPY_WRITE_BUFFER:
@@ -219,8 +219,10 @@ WebGL2Context::GetBufferSubData(GLenum t
     void* ptr = gl->fMapBufferRange(target, offset, data.Length(), LOCAL_GL_MAP_READ_BIT);
     memcpy(data.Data(), ptr, data.Length());
     gl->fUnmapBuffer(target);
 
     if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER && currentTF) {
         BindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, currentTF);
     }
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextDraw.cpp
+++ b/dom/canvas/WebGL2ContextDraw.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL2Context.h"
-#include "GLContext.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Writing to the drawing buffer
 
 void
 WebGL2Context::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset)
 {
     MOZ_CRASH("Not Implemented.");
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL2Context.h"
 
 #include "GLContext.h"
+#include "GLScreenBuffer.h"
 #include "WebGLContextUtils.h"
+#include "WebGLFramebuffer.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // Returns one of FLOAT, INT, UNSIGNED_INT.
 // Fixed-points (normalized ints) are considered FLOAT.
 static GLenum
 ValueTypeForFormat(GLenum internalFormat)
 {
     switch (internalFormat) {
     // Fixed-point
@@ -369,17 +370,17 @@ TranslateDefaultAttachments(const dom::S
     }
 
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
-                                     ErrorResult& aRv)
+                                     ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
         return;
@@ -415,30 +416,30 @@ WebGL2Context::InvalidateFramebuffer(GLe
     // drivers.
     static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
     if (!invalidateFBSupported)
         return;
 
     if (!fb && !isDefaultFB) {
         dom::Sequence<GLenum> tmpAttachments;
         if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
-            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+            rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
     } else {
         gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
     }
 }
 
 void
 WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                         GLint x, GLint y, GLsizei width, GLsizei height,
-                                        ErrorResult& aRv)
+                                        ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
     if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
         return;
@@ -474,17 +475,17 @@ WebGL2Context::InvalidateSubFramebuffer(
     // drivers.
     static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
     if (!invalidateFBSupported)
         return;
 
     if (!fb && !isDefaultFB) {
         dom::Sequence<GLenum> tmpAttachments;
         if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
-            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+            rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(),
                                       x, y, width, height);
     } else {
         gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(),
                                       x, y, width, height);
@@ -531,8 +532,10 @@ WebGL2Context::ReadBuffer(GLenum mode)
 void
 WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
                                               GLenum internalFormat,
                                               GLsizei width, GLsizei height)
 {
     RenderbufferStorage_base("renderbufferStorageMultisample", target, samples,
                               internalFormat, width, height);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextMRTs.cpp
+++ b/dom/canvas/WebGL2ContextMRTs.cpp
@@ -1,18 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL2Context.h"
 #include "GLContext.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 bool WebGL2Context::ValidateClearBuffer(const char* info, GLenum buffer, GLint drawbuffer, size_t elemCount)
 {
     size_t requiredElements = -1;
     GLint maxDrawbuffer = -1;
     switch (buffer) {
       case LOCAL_GL_COLOR:
       case LOCAL_GL_FRONT:
@@ -135,8 +134,10 @@ void
 WebGL2Context::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
 {
     if (buffer != LOCAL_GL_DEPTH_STENCIL) {
         return ErrorInvalidEnumInfo("clearBufferfi: buffer", buffer);
     }
     MakeContextCurrent();
     gl->fClearBufferfi(buffer, drawbuffer, depth, stencil);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -2,18 +2,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/. */
 
 #include "WebGL2Context.h"
 #include "WebGLSampler.h"
 #include "GLContext.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 already_AddRefed<WebGLSampler>
 WebGL2Context::CreateSampler()
 {
     if (IsContextLost())
         return nullptr;
 
     GLuint sampler;
@@ -220,8 +219,10 @@ WebGL2Context::GetSamplerParameter(JSCon
 
     case LOCAL_GL_TEXTURE_MIN_LOD:
     case LOCAL_GL_TEXTURE_MAX_LOD:
         retval.set(JS::Float32Value(
             WebGLContextUnchecked::GetSamplerParameterfv(sampler, pname)));
         return;
     }
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -1,16 +1,23 @@
 /* -*- 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 "WebGL2Context.h"
+
+#include "GLContext.h"
+#include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
+#include "WebGLFramebuffer.h"
+#include "WebGLSampler.h"
+#include "WebGLTransformFeedback.h"
+#include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 JS::Value
 WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
 {
   // The following cases are handled in WebGLContext::GetParameter():
   //     case LOCAL_GL_MAX_COLOR_ATTACHMENTS:
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -3,18 +3,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/. */
 
 #include "WebGL2Context.h"
 
 #include "GLContext.h"
 #include "WebGLSync.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Sync objects
 
 already_AddRefed<WebGLSync>
 WebGL2Context::FenceSync(GLenum condition, GLbitfield flags)
 {
    if (IsContextLost())
@@ -126,8 +125,10 @@ WebGL2Context::GetSyncParameter(JSContex
         MakeContextCurrent();
         gl->fGetSynciv(sync->mGLName, pname, 1, nullptr, &result);
         retval.set(JS::Int32Value(result));
         break;
     }
 
     ErrorInvalidEnum("getSyncParameter: Invalid pname 0x%04x", pname);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "GLContext.h"
 #include "WebGL2Context.h"
 #include "WebGLContextUtils.h"
-#include "GLContext.h"
+#include "WebGLTexture.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 bool
 WebGL2Context::ValidateSizedInternalFormat(GLenum internalformat, const char* info)
 {
     switch (internalformat) {
         // Sized Internal Formats
         // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
     case LOCAL_GL_R8:
@@ -200,31 +200,31 @@ WebGL2Context::TexStorage3D(GLenum targe
         d = std::max(1, d >> 1);
     }
 }
 
 void
 WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat,
                           GLsizei width, GLsizei height, GLsizei depth,
                           GLint border, GLenum format, GLenum type,
-                          const Nullable<dom::ArrayBufferView> &pixels,
+                          const dom::Nullable<dom::ArrayBufferView> &pixels,
                           ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     void* data;
     size_t dataLength;
     js::Scalar::Type jsArrayType;
     if (pixels.IsNull()) {
         data = nullptr;
         dataLength = 0;
         jsArrayType = js::Scalar::MaxTypedArrayViewType;
     } else {
-        const ArrayBufferView& view = pixels.Value();
+        const dom::ArrayBufferView& view = pixels.Value();
         view.ComputeLengthAndData();
 
         data = view.Data();
         dataLength = view.Length();
         jsArrayType = view.Type();
     }
 
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
@@ -313,26 +313,27 @@ WebGL2Context::TexImage3D(GLenum target,
                       data ? WebGLImageDataStatus::InitializedImageData
                            : WebGLImageDataStatus::UninitializedImageData);
 }
 
 void
 WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level,
                              GLint xoffset, GLint yoffset, GLint zoffset,
                              GLsizei width, GLsizei height, GLsizei depth,
-                             GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
+                             GLenum format, GLenum type,
+                             const dom::Nullable<dom::ArrayBufferView>& pixels,
                              ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (pixels.IsNull())
         return ErrorInvalidValue("texSubImage3D: pixels must not be null!");
 
-    const ArrayBufferView& view = pixels.Value();
+    const dom::ArrayBufferView& view = pixels.Value();
     view.ComputeLengthAndData();
 
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
     const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
 
     if (!ValidateTexImageTarget(rawTarget, func, dims))
         return;
 
@@ -484,8 +485,10 @@ WebGL2Context::GetTexParameterInternal(c
             GLfloat f = 0.0f;
             gl->fGetTexParameterfv(target.get(), pname, &f);
             return JS::NumberValue(float(f));
         }
     }
 
     return WebGLContext::GetTexParameterInternal(target, pname);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -4,18 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLProgram.h"
 #include "WebGLTransformFeedback.h"
 #include "GLContext.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Transform Feedback
 
 already_AddRefed<WebGLTransformFeedback>
 WebGL2Context::CreateTransformFeedback()
 {
     if (IsContextLost())
@@ -211,8 +210,10 @@ WebGL2Context::GetTransformFeedbackVaryi
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getTransformFeedbackVarying: program", program))
         return nullptr;
 
     return program->GetTransformFeedbackVarying(index);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -1,20 +1,23 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGL2Context.h"
+
 #include "GLContext.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "nsRefPtr.h"
+#include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
-#include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
 namespace mozilla {
 
 typedef union { GLint i; GLfloat f; GLuint u; } fi_t;
 
 static inline
 GLfloat PuntToFloat(GLint i)
 {
@@ -538,17 +541,17 @@ WebGL2Context::GetUniformBlockIndex(WebG
         return 0;
 
     return program->GetUniformBlockIndex(uniformBlockName);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, WebGLProgram* program,
                                               GLuint uniformBlockIndex, GLenum pname,
-                                              Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
+                                              dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
                                               ErrorResult& rv)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockParameter: program", program))
         return;
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -86,19 +86,19 @@ WebGLActiveInfo::WebGLActiveInfo(WebGLCo
     , mIsArray(isArray)
     , mElemSize(ElemSizeFromType(elemType))
     , mBaseMappedName(baseMappedName)
 { }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
-WebGLActiveInfo::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
+WebGLActiveInfo::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLActiveInfoBinding::Wrap(js, this, aGivenProto);
+    return dom::WebGLActiveInfoBinding::Wrap(js, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLActiveInfo)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLActiveInfo, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLActiveInfo, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -2,17 +2,16 @@
 /* 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 WEBGL_ACTIVE_INFO_H_
 #define WEBGL_ACTIVE_INFO_H_
 
 #include "GLDefs.h"
-#include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS
 #include "nsISupportsImpl.h" // NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 
@@ -20,17 +19,17 @@ class WebGLContext;
 
 class WebGLActiveInfo final
     : public nsWrapperCache
 {
 public:
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLActiveInfo)
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo)
 
-    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     WebGLContext* GetParentObject() const {
         return mWebGL;
     }
 
 
     WebGLContext* const mWebGL;
 
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -99,19 +99,19 @@ WebGLBuffer::Validate(GLenum type, uint3
 
 bool
 WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
 {
     return mCache->BeenUsedWithMultipleTypes();
 }
 
 JSObject*
-WebGLBuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLBuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLBufferBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLBufferBinding::Wrap(cx, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLBuffer, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLBuffer, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -3,20 +3,20 @@
  * 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 WEBGL_BUFFER_H_
 #define WEBGL_BUFFER_H_
 
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
-#include "mozilla/MemoryReporting.h"
+#include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
-#include "WebGLStrongTypes.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLElementArrayCache;
 
 class WebGLBuffer final
     : public nsWrapperCache
@@ -53,17 +53,17 @@ public:
                   uint32_t* const out_upperBound);
 
     bool IsElementArrayUsedWithMultipleTypes() const;
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     const GLenum mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1,73 +1,84 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
 
+#include <queue>
+
 #include "AccessCheck.h"
-#include "CanvasUtils.h"
 #include "gfxContext.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxPattern.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "GLReadTexImageHelper.h"
+#include "GLScreenBuffer.h"
 #include "ImageContainer.h"
 #include "ImageEncoder.h"
 #include "Layers.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
-#include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/EnumeratedArrayCycleCollection.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessPriorityManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsError.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIConsoleService.h"
+#include "nsIDOMEvent.h"
 #include "nsIGfxInfo.h"
 #include "nsIObserverService.h"
-#include "nsIDOMEvent.h"
 #include "nsIVariant.h"
 #include "nsIWidget.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSVGEffects.h"
 #include "prenv.h"
-#include <queue>
 #include "ScopedGLHelpers.h"
+
+#ifdef MOZ_WIDGET_GONK
+#include "mozilla/layers/ShadowLayers.h"
+#endif
+
+// Local
+#include "CanvasUtils.h"
 #include "WebGL1Context.h"
+#include "WebGLActiveInfo.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextLossHandler.h"
 #include "WebGLContextUtils.h"
 #include "WebGLExtensions.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLMemoryTracker.h"
 #include "WebGLObjectModel.h"
+#include "WebGLProgram.h"
 #include "WebGLQuery.h"
 #include "WebGLSampler.h"
+#include "WebGLShader.h"
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
-#ifdef MOZ_WIDGET_GONK
-#include "mozilla/layers/ShadowLayers.h"
-#endif
+// Generated
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
 
-using namespace mozilla;
+
+namespace mozilla {
+
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 
 WebGLObserver::WebGLObserver(WebGLContext* webgl)
     : mWebGL(webgl)
 {
@@ -724,17 +735,17 @@ CreateOffscreen(GLContext* gl, const Web
     }
 
     return created;
 }
 
 bool
 WebGLContext::CreateOffscreenGL(bool forceEnabled)
 {
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
 
     layers::ISurfaceAllocator* surfAllocator = nullptr;
 #ifdef MOZ_WIDGET_GONK
     nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
     if (docWidget) {
         layers::LayerManager* layerManager = docWidget->GetLayerManager();
         if (layerManager) {
             // XXX we really want "AsSurfaceAllocator" here for generality
@@ -898,22 +909,22 @@ WebGLContext::SetDimensions(int32_t sign
     // TODO: When we have software webgl support we should use that instead.
     disabled |= gfxPlatform::InSafeMode();
 
     if (disabled) {
         GenerateWarning("WebGL creation is disabled, and so disallowed here.");
         return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     bool failIfMajorPerformanceCaveat =
                     !gfxPrefs::WebGLDisableFailIfMajorPerformanceCaveat() &&
                     !HasAcceleratedLayers(gfxInfo);
     if (failIfMajorPerformanceCaveat) {
-        Nullable<dom::WebGLContextAttributes> contextAttributes;
+        dom::Nullable<dom::WebGLContextAttributes> contextAttributes;
         this->GetContextAttributes(contextAttributes);
         if (contextAttributes.Value().mFailIfMajorPerformanceCaveat) {
             return NS_ERROR_FAILURE;
         }
     }
 
     // Alright, now let's start trying.
     bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
@@ -1177,18 +1188,16 @@ WebGLContext::UpdateLastUseIndex()
     if (!sIndex.isValid())
         NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
 
     mLastUseIndex = sIndex.value();
 }
 
 static uint8_t gWebGLLayerUserData;
 
-namespace mozilla {
-
 class WebGLContextUserData : public LayerUserData
 {
 public:
     explicit WebGLContextUserData(HTMLCanvasElement* canvas)
         : mCanvas(canvas)
     {}
 
     /* PreTransactionCallback gets called by the Layers code every time the
@@ -1217,18 +1226,16 @@ public:
 
         webgl->UpdateLastUseIndex();
     }
 
 private:
     nsRefPtr<HTMLCanvasElement> mCanvas;
 };
 
-} // end namespace mozilla
-
 already_AddRefed<layers::CanvasLayer>
 WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
                              CanvasLayer* oldLayer,
                              LayerManager* manager)
 {
     if (IsContextLost())
         return nullptr;
 
@@ -1278,17 +1285,17 @@ WebGLContext::GetCanvasLayer(nsDisplayLi
     canvasLayer->Updated();
 
     mResetLayer = false;
 
     return canvasLayer.forget();
 }
 
 void
-WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes>& retval)
+WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
 
     result.mAlpha.Construct(mOptions.alpha);
@@ -1897,17 +1904,79 @@ WebGLContext::TexImageFromVideoElement(c
                           srcImage->GetSize().height, 1,
                           effectiveInternalFormat,
                           WebGLImageDataStatus::InitializedImageData);
         tex->Bind(TexImageTargetToTexTarget(texImageTarget));
     }
     return ok;
 }
 
-size_t mozilla::RoundUpToMultipleOf(size_t value, size_t multiple)
+void
+WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
+                            GLint yoffset, GLenum format, GLenum type,
+                            dom::Element* elt, ErrorResult* const out_rv)
+{
+    // TODO: Consolidate all the parameter validation
+    // checks. Instead of spreading out the cheks in multple
+    // places, consolidate into one spot.
+
+    if (IsContextLost())
+        return;
+
+    if (!ValidateTexImageTarget(rawTexImageTarget,
+                                WebGLTexImageFunc::TexSubImage,
+                                WebGLTexDimensions::Tex2D))
+    {
+        ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
+        return;
+    }
+
+    const TexImageTarget texImageTarget(rawTexImageTarget);
+
+    if (level < 0)
+        return ErrorInvalidValue("texSubImage2D: level is negative");
+
+    const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
+    if (level > maxLevel) {
+        ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d",
+                          level, maxLevel);
+        return;
+    }
+
+    WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
+    if (!tex)
+        return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
+
+    const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
+    const TexInternalFormat internalFormat = imageInfo.EffectiveInternalFormat();
+
+    // Trying to handle the video by GPU directly first
+    if (TexImageFromVideoElement(texImageTarget, level,
+                                 internalFormat.get(), format, type, *elt))
+    {
+        return;
+    }
+
+    RefPtr<gfx::DataSourceSurface> data;
+    WebGLTexelFormat srcFormat;
+    nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(*elt);
+    *out_rv = SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
+    if (out_rv->Failed() || !data)
+        return;
+
+    gfx::IntSize size = data->GetSize();
+    uint32_t byteLength = data->Stride() * size.height;
+    TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset, size.width,
+                       size.height, data->Stride(), format, type, data->GetData(),
+                       byteLength, js::Scalar::MaxTypedArrayViewType, srcFormat,
+                       mPixelStorePremultiplyAlpha);
+}
+
+size_t
+RoundUpToMultipleOf(size_t value, size_t multiple)
 {
     size_t overshoot = value + multiple - 1;
     return overshoot - (overshoot % multiple);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
@@ -1972,8 +2041,10 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
     NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
     NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     // If the exact way we cast to nsISupports here ever changes, fix our
     // ToSupports() method.
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext)
 NS_INTERFACE_MAP_END
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1,55 +1,50 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGLCONTEXT_H_
 #define WEBGLCONTEXT_H_
 
+#include <stdarg.h>
+
+#include "GLDefs.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/dom/TypedArray.h"
 #include "mozilla/EnumeratedArray.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
-
-#include "GLDefs.h"
-#include "WebGLActiveInfo.h"
-#include "WebGLContextUnchecked.h"
-#include "WebGLFormats.h"
-#include "WebGLObjectModel.h"
-#include "WebGLRenderbuffer.h"
-#include "WebGLTexture.h"
-#include "WebGLShaderValidator.h"
-#include "WebGLStrongTypes.h"
-#include <stdarg.h>
-
+#include "nsCycleCollectionNoteChild.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsLayoutUtils.h"
 #include "nsTArray.h"
-#include "nsCycleCollectionNoteChild.h"
-
-#include "nsIDOMWebGLRenderingContext.h"
-#include "nsICanvasRenderingContextInternal.h"
-#include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsWrapperCache.h"
-#include "nsIObserver.h"
-#include "nsIDOMEventListener.h"
-#include "nsLayoutUtils.h"
-
-#include "GLContextProvider.h"
-
-#include "mozilla/gfx/2D.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
-#include "mozilla/dom/TypedArray.h"
-#include "mozilla/ErrorResult.h"
+// Local
+#include "WebGLContextUnchecked.h"
+#include "WebGLFormats.h"
+#include "WebGLObjectModel.h"
+#include "WebGLStrongTypes.h"
+
+// Generated
+#include "nsIDOMEventListener.h"
+#include "nsIDOMWebGLRenderingContext.h"
+#include "nsIObserver.h"
+
 
 class nsIDocShell;
 
 /*
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
  *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
  *
  * Exceptions: some of the following values are set to higher values than in the spec because
@@ -103,16 +98,17 @@ template<typename> struct Nullable;
 } // namespace dom
 
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
+class ShaderValidator;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
@@ -196,24 +192,28 @@ class WebGLContext
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
         UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
 public:
     WebGLContext();
 
+protected:
+    virtual ~WebGLContext();
+
+public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLContext)
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsIDOMWebGLRenderingContext)
 
-    virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
 
     // nsICanvasRenderingContextInternal
 #ifdef DEBUG
     virtual int32_t GetWidth() const override;
     virtual int32_t GetHeight() const override;
 #endif
@@ -537,17 +537,17 @@ public:
     bool IsTexture(WebGLTexture* tex);
     bool IsVertexArray(WebGLVertexArray* vao);
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram* prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
     void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type,
-                    const Nullable<dom::ArrayBufferView>& pixels,
+                    const dom::Nullable<dom::ArrayBufferView>& pixels,
                     ErrorResult& rv);
     void RenderbufferStorage(GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height);
 protected:
     void RenderbufferStorage_base(const char* funcName, GLenum target,
                                   GLsizei samples, GLenum internalformat,
                                   GLsizei width, GLsizei height);
 public:
@@ -558,17 +558,17 @@ public:
     void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
     void StencilMask(GLuint mask);
     void StencilMaskSeparate(GLenum face, GLuint mask);
     void StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
     void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
                            GLenum dppass);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLsizei width, GLsizei height, GLint border, GLenum format,
-                    GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
+                    GLenum type, const dom::Nullable<dom::ArrayBufferView>& pixels,
                     ErrorResult& rv);
     void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat,
                     GLenum format, GLenum type, dom::ImageData* pixels,
                     ErrorResult& rv);
     // Allow whatever element types the bindings are willing to pass
     // us in TexImage2D
     bool TexImageFromVideoElement(TexImageTarget texImageTarget, GLint level,
                                   GLenum internalFormat, GLenum format,
@@ -635,83 +635,35 @@ public:
     }
     void TexParameteri(GLenum target, GLenum pname, GLint param) {
         TexParameter_base(target, pname, &param, nullptr);
     }
 
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset,
                        GLint yoffset, GLsizei width, GLsizei height,
                        GLenum format, GLenum type,
-                       const Nullable<dom::ArrayBufferView>& pixels,
+                       const dom::Nullable<dom::ArrayBufferView>& pixels,
                        ErrorResult& rv);
     void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xoffset,
                        GLint yoffset, GLenum format, GLenum type,
                        dom::ImageData* pixels, ErrorResult& rv);
+
+    void TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
+                       GLint yoffset, GLenum format, GLenum type,
+                       dom::Element* elt, ErrorResult* const out_rv);
+
     // Allow whatever element types the bindings are willing to pass
     // us in TexSubImage2D
     template<class ElementType>
     void TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
                        GLint yoffset, GLenum format, GLenum type,
-                       ElementType& elt, ErrorResult& rv)
+                       ElementType& elt, ErrorResult& out_rv)
     {
-        // TODO: Consolidate all the parameter validation
-        // checks. Instead of spreading out the cheks in multple
-        // places, consolidate into one spot.
-
-        if (IsContextLost())
-            return;
-
-        if (!ValidateTexImageTarget(rawTexImageTarget,
-                                    WebGLTexImageFunc::TexSubImage,
-                                    WebGLTexDimensions::Tex2D))
-        {
-            ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
-            return;
-        }
-
-        const TexImageTarget texImageTarget(rawTexImageTarget);
-
-        if (level < 0)
-            return ErrorInvalidValue("texSubImage2D: level is negative");
-
-        const int32_t maxLevel = MaxTextureLevelForTexImageTarget(texImageTarget);
-        if (level > maxLevel) {
-            ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d",
-                              level, maxLevel);
-            return;
-        }
-
-        WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
-        if (!tex)
-            return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
-
-        const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
-        const TexInternalFormat internalFormat = imageInfo.EffectiveInternalFormat();
-
-        // Trying to handle the video by GPU directly first
-        if (TexImageFromVideoElement(texImageTarget, level,
-                                     internalFormat.get(), format, type, elt))
-        {
-            return;
-        }
-
-        RefPtr<gfx::DataSourceSurface> data;
-        WebGLTexelFormat srcFormat;
-        nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
-        rv = SurfaceFromElementResultToImageSurface(res, data, &srcFormat);
-        if (rv.Failed() || !data)
-            return;
-
-        gfx::IntSize size = data->GetSize();
-        uint32_t byteLength = data->Stride() * size.height;
-        return TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset,
-                                  size.width, size.height, data->Stride(),
-                                  format, type, data->GetData(), byteLength,
-                                  js::Scalar::MaxTypedArrayViewType, srcFormat,
-                                  mPixelStorePremultiplyAlpha);
+        TexSubImage2D(rawTexImageTarget, level, xoffset, yoffset, format, type, &elt,
+                      &out_rv);
     }
 
     void Uniform1i(WebGLUniformLocation* loc, GLint x);
     void Uniform2i(WebGLUniformLocation* loc, GLint x, GLint y);
     void Uniform3i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z);
     void Uniform4i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z,
                    GLint w);
 
@@ -912,28 +864,28 @@ public:
 private:
     void BufferDataUnchecked(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
     void BufferData(GLenum target, WebGLsizeiptr size, void* data, GLenum usage);
 
 public:
     void BufferData(GLenum target, WebGLsizeiptr size, GLenum usage);
     void BufferData(GLenum target, const dom::ArrayBufferView& data,
                     GLenum usage);
-    void BufferData(GLenum target, const Nullable<dom::ArrayBuffer>& maybeData,
+    void BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeData,
                     GLenum usage);
 
 private:
     void BufferSubDataUnchecked(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
     void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
 
 public:
     void BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                        const dom::ArrayBufferView& data);
     void BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
-                       const Nullable<dom::ArrayBuffer>& maybeData);
+                       const dom::Nullable<dom::ArrayBuffer>& maybeData);
     already_AddRefed<WebGLBuffer> CreateBuffer();
     void DeleteBuffer(WebGLBuffer* buf);
     bool IsBuffer(WebGLBuffer* buf);
 
 protected:
     // bound buffer state
     WebGLRefPtr<WebGLBuffer> mBoundArrayBuffer;
     WebGLRefPtr<WebGLBuffer> mBoundCopyReadBuffer;
@@ -1082,18 +1034,16 @@ private:
                               const GLfloat* ptr);
 
     bool ValidateBufferFetching(const char* info);
     bool BindArrayAttribToLocation0(WebGLProgram* prog);
 
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
-    virtual ~WebGLContext();
-
     void SetFakeBlackStatus(WebGLContextFakeBlackStatus x) {
         mFakeBlackStatus = x;
     }
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLContextFakeBlackStatus ResolvedFakeBlackStatus();
 
     void BindFakeBlackTextures();
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -12,19 +12,17 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
-using namespace mozilla::gl;
+namespace mozilla {
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
 bool
 WebGLContext::DrawInstanced_check(const char* info)
 {
     // This restriction was removed in GLES3, so WebGL2 shouldn't have it.
@@ -743,17 +741,17 @@ WebGLContext::UnbindFakeBlackTextures()
             gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
             gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->mGLName);
         }
     }
 
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
 }
 
-WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext* gl, TexTarget target, GLenum format)
+WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format)
     : mGL(gl)
     , mGLName(0)
 {
   MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
 
   mGL->MakeCurrent();
   GLuint formerBinding = 0;
   gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
@@ -783,8 +781,10 @@ WebGLContext::FakeBlackTexture::FakeBlac
 
 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
 {
   if (mGL) {
       mGL->MakeCurrent();
       mGL->fDeleteTextures(1, &mGLName);
   }
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -388,17 +388,17 @@ WebGLContext::EnableExtension(WebGLExten
         MOZ_ASSERT(false, "should not get there.");
     }
 
     mExtensions[ext] = obj;
 }
 
 void
 WebGLContext::GetSupportedExtensions(JSContext* cx,
-                                     Nullable< nsTArray<nsString> >& retval)
+                                     dom::Nullable< nsTArray<nsString> >& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     nsTArray<nsString>& arr = retval.SetValue();
 
     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) {
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLFramebuffer.h"
 #include "GLContext.h"
 
-using namespace mozilla;
+namespace mozilla {
 
 void
 WebGLContext::Clear(GLbitfield mask)
 {
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
@@ -245,11 +245,9 @@ WebGLContext::StencilMaskSeparate(GLenum
             mStencilWriteMaskBack = mask;
             break;
     }
 
     MakeContextCurrent();
     gl->fStencilMaskSeparate(face, mask);
 }
 
-
-
-
+} // namespace mozilla
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1,30 +1,33 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
+
+#include "WebGLActiveInfo.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShaderPrecisionFormat.h"
 #include "WebGLTexture.h"
 #include "WebGLExtensions.h"
 #include "WebGLVertexArray.h"
 
-#include "nsString.h"
 #include "nsDebug.h"
 #include "nsReadableUtils.h"
+#include "../../xpcom/base/nsRefPtr.h"
+#include "nsString.h"
 
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "GLContext.h"
 
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
@@ -44,17 +47,18 @@
 #endif
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Endian.h"
 
-using namespace mozilla;
+namespace mozilla {
+
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 static const WebGLRectangleObject*
 CurValidFBRectObject(const WebGLContext* webgl,
                      const WebGLFramebuffer* boundFB)
 {
@@ -1018,17 +1022,17 @@ WebGLContext::GetActiveUniform(WebGLProg
     if (!ValidateObject("getActiveUniform: program", prog))
         return nullptr;
 
     return prog->GetActiveUniform(index);
 }
 
 void
 WebGLContext::GetAttachedShaders(WebGLProgram* prog,
-                                 Nullable<nsTArray<nsRefPtr<WebGLShader>>>& retval)
+                                 dom::Nullable<nsTArray<nsRefPtr<WebGLShader>>>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     if (!prog) {
         ErrorInvalidValue("getAttachedShaders: Invalid program.");
         return;
@@ -1135,17 +1139,17 @@ WebGLContext::GetFramebufferAttachmentPa
         attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
         attachment <= LOCAL_GL_COLOR_ATTACHMENT15)
     {
         fb->EnsureColorAttachPoints(attachment - LOCAL_GL_COLOR_ATTACHMENT0);
     }
 
     MakeContextCurrent();
 
-    const WebGLFramebuffer::AttachPoint& fba = fb->GetAttachPoint(attachment);
+    const WebGLFBAttachPoint& fba = fb->GetAttachPoint(attachment);
 
     if (fba.Renderbuffer()) {
         switch (pname) {
             case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT:
                 if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) {
                     const GLenum internalFormat = fba.Renderbuffer()->InternalFormat();
                     return (internalFormat == LOCAL_GL_SRGB_EXT ||
                             internalFormat == LOCAL_GL_SRGB_ALPHA_EXT ||
@@ -1967,17 +1971,17 @@ IsFormatAndTypeUnpackable(GLenum format,
     default:
         return false;
     }
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
                          GLsizei height, GLenum format,
-                         GLenum type, const Nullable<ArrayBufferView> &pixels,
+                         GLenum type, const dom::Nullable<ArrayBufferView>& pixels,
                          ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
         GenerateWarning("readPixels: Not allowed");
         return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
@@ -2082,19 +2086,19 @@ WebGLContext::ReadPixels(GLint x, GLint 
 
     bool isSourceTypeFloat;
     if (mBoundReadFramebuffer) {
         TexInternalFormat srcFormat;
         if (!mBoundReadFramebuffer->ValidateForRead("readPixels", &srcFormat))
             return;
 
         MOZ_ASSERT(srcFormat != LOCAL_GL_NONE);
-        TexType type = TypeFromInternalFormat(srcFormat);
-        isSourceTypeFloat = (type == LOCAL_GL_FLOAT ||
-                             type == LOCAL_GL_HALF_FLOAT);
+        TexType texType = TypeFromInternalFormat(srcFormat);
+        isSourceTypeFloat = (texType == LOCAL_GL_FLOAT ||
+                             texType == LOCAL_GL_HALF_FLOAT);
     } else {
         ClearBackbufferIfNeeded();
 
         isSourceTypeFloat = false;
     }
 
     // Check the format and type params to assure they are an acceptable pair (as per spec)
 
@@ -3402,17 +3406,18 @@ WebGLContext::TexImage2D_base(TexImageTa
     tex->SetImageInfo(texImageTarget, level, width, height, 1,
                       effectiveInternalFormat, imageInfoStatusIfSuccess);
 }
 
 void
 WebGLContext::TexImage2D(GLenum rawTarget, GLint level,
                          GLenum internalformat, GLsizei width,
                          GLsizei height, GLint border, GLenum format,
-                         GLenum type, const Nullable<ArrayBufferView> &pixels, ErrorResult& rv)
+                         GLenum type, const dom::Nullable<ArrayBufferView>& pixels,
+                         ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     void* data;
     uint32_t length;
     js::Scalar::Type jsArrayType;
     if (pixels.IsNull()) {
@@ -3603,17 +3608,17 @@ WebGLContext::TexSubImage2D_base(GLenum 
     gl->fTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, width, height, driverFormat, driverType, pixels);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum rawTarget, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLsizei width, GLsizei height,
                             GLenum format, GLenum type,
-                            const Nullable<ArrayBufferView> &pixels,
+                            const dom::Nullable<ArrayBufferView>& pixels,
                             ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
     if (pixels.IsNull())
         return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
 
@@ -3679,17 +3684,17 @@ WebGLContext::RestoreContext()
 
     if (!mAllowContextRestore)
         return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
 
     ForceRestoreContext();
 }
 
 WebGLTexelFormat
-mozilla::GetWebGLTexelFormat(TexInternalFormat effectiveInternalFormat)
+GetWebGLTexelFormat(TexInternalFormat effectiveInternalFormat)
 {
     switch (effectiveInternalFormat.get()) {
         case LOCAL_GL_RGBA8:                  return WebGLTexelFormat::RGBA8;
         case LOCAL_GL_SRGB8_ALPHA8:           return WebGLTexelFormat::RGBA8;
         case LOCAL_GL_RGB8:                   return WebGLTexelFormat::RGB8;
         case LOCAL_GL_SRGB8:                  return WebGLTexelFormat::RGB8;
         case LOCAL_GL_ALPHA8:                 return WebGLTexelFormat::A8;
         case LOCAL_GL_LUMINANCE8:             return WebGLTexelFormat::R8;
@@ -3763,8 +3768,10 @@ WebGLContext::PolygonOffset(GLfloat fact
 
 void
 WebGLContext::SampleCoverage(GLclampf value, WebGLboolean invert) {
     if (IsContextLost())
         return;
     MakeContextCurrent();
     gl->fSampleCoverage(value, invert);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContextReporter.cpp
+++ b/dom/canvas/WebGLContextReporter.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLMemoryTracker.h"
 
-using namespace mozilla;
+namespace mozilla {
 
 NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
 
 NS_IMETHODIMP
 WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
                                    nsISupports* data, bool)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                         \
@@ -146,8 +147,10 @@ WebGLMemoryTracker::GetShaderSize()
              shader;
              shader = shader->getNext())
         {
             result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
         }
     }
     return result;
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -10,18 +10,17 @@
 #include "WebGLProgram.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "GLContext.h"
 #include "mozilla/dom/ToJSValue.h"
 
-using namespace mozilla;
-using namespace dom;
+namespace mozilla {
 
 void
 WebGLContext::Disable(GLenum cap)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateCapabilityEnum(cap, "disable"))
@@ -348,17 +347,18 @@ WebGLContext::GetParameter(JSContext* cx
 
         case LOCAL_GL_MAX_VARYING_VECTORS:
             return JS::Int32Value(mGLMaxVaryingVectors);
 
         case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
             return JS::Int32Value(0);
         case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: {
             uint32_t length = mCompressedTextureFormats.Length();
-            JSObject* obj = Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements());
+            JSObject* obj = dom::Uint32Array::Create(cx, this, length,
+                                                     mCompressedTextureFormats.Elements());
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // unsigned int. here we may have to return very large values like 2^32-1 that can't be represented as
         // javascript integer values. We just return them as doubles and javascript doesn't care.
@@ -414,66 +414,66 @@ WebGLContext::GetParameter(JSContext* cx
         // Complex values
 
         // 2 floats
         case LOCAL_GL_DEPTH_RANGE:
         case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
         case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
             GLfloat fv[2] = { 0 };
             gl->fGetFloatv(pname, fv);
-            JSObject* obj = Float32Array::Create(cx, this, 2, fv);
+            JSObject* obj = dom::Float32Array::Create(cx, this, 2, fv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // 4 floats
         case LOCAL_GL_COLOR_CLEAR_VALUE:
         case LOCAL_GL_BLEND_COLOR: {
             GLfloat fv[4] = { 0 };
             gl->fGetFloatv(pname, fv);
-            JSObject* obj = Float32Array::Create(cx, this, 4, fv);
+            JSObject* obj = dom::Float32Array::Create(cx, this, 4, fv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // 2 ints
         case LOCAL_GL_MAX_VIEWPORT_DIMS: {
             GLint iv[2] = { 0 };
             gl->fGetIntegerv(pname, iv);
-            JSObject* obj = Int32Array::Create(cx, this, 2, iv);
+            JSObject* obj = dom::Int32Array::Create(cx, this, 2, iv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // 4 ints
         case LOCAL_GL_SCISSOR_BOX:
         case LOCAL_GL_VIEWPORT: {
             GLint iv[4] = { 0 };
             gl->fGetIntegerv(pname, iv);
-            JSObject* obj = Int32Array::Create(cx, this, 4, iv);
+            JSObject* obj = dom::Int32Array::Create(cx, this, 4, iv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         // 4 bools
         case LOCAL_GL_COLOR_WRITEMASK: {
             realGLboolean gl_bv[4] = { 0 };
             gl->fGetBooleanv(pname, gl_bv);
             bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]),
                              bool(gl_bv[2]), bool(gl_bv[3]) };
             JS::Rooted<JS::Value> arr(cx);
-            if (!ToJSValue(cx, vals, &arr)) {
+            if (!dom::ToJSValue(cx, vals, &arr)) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return arr;
         }
 
         case LOCAL_GL_ARRAY_BUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv);
         }
@@ -588,8 +588,10 @@ WebGLContext::GetStateTrackingSlot(GLenu
         case LOCAL_GL_SCISSOR_TEST:
             return &mScissorTestEnabled;
         case LOCAL_GL_STENCIL_TEST:
             return &mStencilTestEnabled;
     }
 
     return nullptr;
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -22,18 +22,16 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLContextUtils.h"
 
 namespace mozilla {
 
-using namespace gl;
-
 bool
 IsGLDepthFormat(TexInternalFormat internalformat)
 {
     TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
     return unsizedformat == LOCAL_GL_DEPTH_COMPONENT;
 }
 
 bool
@@ -491,17 +489,17 @@ WebGLContext::GenerateWarning(const char
     PR_vsnprintf(buf, 1024, fmt, ap);
 
     // no need to print to stderr, as JS_ReportWarning takes care of this for us.
 
     if (!mCanvasElement) {
         return;
     }
 
-    AutoJSAPI api;
+    dom::AutoJSAPI api;
     if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) {
         return;
     }
 
     JSContext* cx = api.cx();
     JS_ReportWarning(cx, "WebGL: %s", buf);
     if (!ShouldGenerateWarnings()) {
         JS_ReportWarning(cx,
--- a/dom/canvas/WebGLContextUtils.h
+++ b/dom/canvas/WebGLContextUtils.h
@@ -2,20 +2,22 @@
 /* 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 WEBGL_CONTEXT_UTILS_H_
 #define WEBGL_CONTEXT_UTILS_H_
 
 #include "WebGLContext.h"
-#include "WebGLStrongTypes.h"
+
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingUtils.h"
 
+#include "WebGLStrongTypes.h"
+
 namespace mozilla {
 
 bool IsGLDepthFormat(TexInternalFormat webGLFormat);
 bool IsGLDepthStencilFormat(TexInternalFormat webGLFormat);
 bool FormatHasAlpha(TexInternalFormat webGLFormat);
 
 void
 DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl,
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -9,21 +9,23 @@
 #include "angle/ShaderLang.h"
 #include "CanvasUtils.h"
 #include "GLContext.h"
 #include "jsfriendapi.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
+#include "WebGLActiveInfo.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
+#include "WebGLSampler.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLValidateStrings.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #if defined(MOZ_WIDGET_COCOA)
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -11,18 +11,17 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
-using namespace mozilla;
-using namespace dom;
+namespace mozilla {
 
 void
 WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttrib1f"))
@@ -301,17 +300,17 @@ WebGLContext::GetVertexAttrib(JSContext*
             if (index) {
                 gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, &vec[0]);
             } else {
                 vec[0] = mVertexAttrib0Vector[0];
                 vec[1] = mVertexAttrib0Vector[1];
                 vec[2] = mVertexAttrib0Vector[2];
                 vec[3] = mVertexAttrib0Vector[3];
             }
-            JSObject* obj = Float32Array::Create(cx, this, 4, vec);
+            JSObject* obj = dom::Float32Array::Create(cx, this, 4, vec);
             if (!obj) {
                 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             }
             return JS::ObjectOrNullValue(obj);
         }
 
         case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
         {
@@ -405,8 +404,10 @@ WebGLContext::VertexAttribDivisor(GLuint
     vd.divisor = divisor;
 
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
     gl->fVertexAttribDivisor(index, divisor);
 }
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensionBase.cpp
+++ b/dom/canvas/WebGLExtensionBase.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
 #include "WebGLExtensions.h"
 
-using namespace mozilla;
+namespace mozilla {
 
 WebGLExtensionBase::WebGLExtensionBase(WebGLContext* context)
     : WebGLContextBoundObject(context)
     , mIsLost(false)
 {
 }
 
 WebGLExtensionBase::~WebGLExtensionBase()
@@ -23,8 +22,10 @@ WebGLExtensionBase::MarkLost()
 {
     mIsLost = true;
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLExtensionBase)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLExtensionBase, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLExtensionBase, Release)
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -1,24 +1,30 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_EXTENSIONS_H_
 #define WEBGL_EXTENSIONS_H_
 
-#include "jsapi.h"
+#include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
+namespace dom {
+template<typename T>
+class Sequence;
+} // namespace dom
+
 class WebGLContext;
 class WebGLShader;
 class WebGLQuery;
 class WebGLTimerQuery;
 class WebGLVertexArray;
 
 class WebGLExtensionBase
     : public nsWrapperCache
@@ -38,22 +44,22 @@ public:
 
 protected:
     virtual ~WebGLExtensionBase();
 
     bool mIsLost;
 };
 
 #define DECL_WEBGL_EXTENSION_GOOP \
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 #define IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionType, WebGLBindingType)\
     JSObject*                                                    \
-    WebGLExtensionType::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) {              \
-        return dom::WebGLBindingType##Binding::Wrap(cx, this, aGivenProto); \
+    WebGLExtensionType::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {              \
+        return dom::WebGLBindingType##Binding::Wrap(cx, this, givenProto); \
     }
 
 class WebGLExtensionCompressedTextureATC
     : public WebGLExtensionBase
 {
 public:
     explicit WebGLExtensionCompressedTextureATC(WebGLContext*);
     virtual ~WebGLExtensionCompressedTextureATC();
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLFormats.h"
 
+#include "GLDefs.h"
 #include "mozilla/StaticMutex.h"
 
 namespace mozilla {
 namespace webgl {
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 std::map<EffectiveFormat, const CompressedFormatInfo> gCompressedFormatInfoMap;
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -10,46 +10,46 @@
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLExtensions.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
-WebGLFramebuffer::AttachPoint::AttachPoint(WebGLFramebuffer* fb,
-                                           FBAttachment attachmentPoint)
+WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb,
+                                       FBAttachment attachmentPoint)
     : mFB(fb)
     , mAttachmentPoint(attachmentPoint)
     , mTexImageTarget(LOCAL_GL_NONE)
 { }
 
-WebGLFramebuffer::AttachPoint::~AttachPoint()
+WebGLFBAttachPoint::~WebGLFBAttachPoint()
 {
     MOZ_ASSERT(!mRenderbufferPtr);
     MOZ_ASSERT(!mTexturePtr);
 }
 
 bool
-WebGLFramebuffer::AttachPoint::IsDeleteRequested() const
+WebGLFBAttachPoint::IsDeleteRequested() const
 {
     return Texture() ? Texture()->IsDeleteRequested()
          : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
          : false;
 }
 
 bool
-WebGLFramebuffer::AttachPoint::IsDefined() const
+WebGLFBAttachPoint::IsDefined() const
 {
     return Renderbuffer() ||
            (Texture() && Texture()->HasImageInfoAt(ImageTarget(), 0));
 }
 
 bool
-WebGLFramebuffer::AttachPoint::HasAlpha() const
+WebGLFBAttachPoint::HasAlpha() const
 {
     MOZ_ASSERT(HasImage());
 
     if (Texture() &&
         Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
     {
         return FormatHasAlpha(Texture()->ImageInfoAt(mTexImageTarget,
                                                      mTexImageLevel).EffectiveInternalFormat());
@@ -57,17 +57,17 @@ WebGLFramebuffer::AttachPoint::HasAlpha(
 
     if (Renderbuffer())
         return FormatHasAlpha(Renderbuffer()->InternalFormat());
 
     return false;
 }
 
 GLenum
-WebGLFramebuffer::GetFormatForAttachment(const WebGLFramebuffer::AttachPoint& attachment) const
+WebGLFramebuffer::GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const
 {
     MOZ_ASSERT(attachment.IsDefined());
     MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer());
 
     if (attachment.Texture()) {
         const WebGLTexture& tex = *attachment.Texture();
         MOZ_ASSERT(tex.HasImageInfoAt(attachment.ImageTarget(), 0));
 
@@ -78,86 +78,86 @@ WebGLFramebuffer::GetFormatForAttachment
 
     if (attachment.Renderbuffer())
         return attachment.Renderbuffer()->InternalFormat();
 
     return LOCAL_GL_NONE;
 }
 
 TexInternalFormat
-WebGLFramebuffer::AttachPoint::EffectiveInternalFormat() const
+WebGLFBAttachPoint::EffectiveInternalFormat() const
 {
     const WebGLTexture* tex = Texture();
     if (tex && tex->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) {
         return tex->ImageInfoAt(mTexImageTarget,
                                 mTexImageLevel).EffectiveInternalFormat();
     }
 
     const WebGLRenderbuffer* rb = Renderbuffer();
     if (rb)
         return rb->InternalFormat();
 
     return LOCAL_GL_NONE;
 }
 
 bool
-WebGLFramebuffer::AttachPoint::IsReadableFloat() const
+WebGLFBAttachPoint::IsReadableFloat() const
 {
     TexInternalFormat internalformat = EffectiveInternalFormat();
     MOZ_ASSERT(internalformat != LOCAL_GL_NONE);
     TexType type = TypeFromInternalFormat(internalformat);
     return type == LOCAL_GL_FLOAT ||
            type == LOCAL_GL_HALF_FLOAT_OES ||
            type == LOCAL_GL_HALF_FLOAT;
 }
 
 static void
-UnmarkAttachment(WebGLFramebuffer::AttachPoint& attachment)
+UnmarkAttachment(WebGLFBAttachPoint& attachment)
 {
     WebGLFramebufferAttachable* maybe = attachment.Texture();
     if (!maybe)
         maybe = attachment.Renderbuffer();
 
     if (maybe)
         maybe->UnmarkAttachment(attachment);
 }
 
 void
-WebGLFramebuffer::AttachPoint::SetTexImage(WebGLTexture* tex, TexImageTarget target,
+WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex, TexImageTarget target,
                                           GLint level)
 {
     mFB->InvalidateFramebufferStatus();
 
     UnmarkAttachment(*this);
 
     mTexturePtr = tex;
     mRenderbufferPtr = nullptr;
     mTexImageTarget = target;
     mTexImageLevel = level;
 
     if (tex)
         tex->MarkAttachment(*this);
 }
 
 void
-WebGLFramebuffer::AttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb)
+WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb)
 {
     mFB->InvalidateFramebufferStatus();
 
     UnmarkAttachment(*this);
 
     mTexturePtr = nullptr;
     mRenderbufferPtr = rb;
 
     if (rb)
         rb->MarkAttachment(*this);
 }
 
 bool
-WebGLFramebuffer::AttachPoint::HasUninitializedImageData() const
+WebGLFBAttachPoint::HasUninitializedImageData() const
 {
     if (!HasImage())
         return false;
 
     if (Renderbuffer())
         return Renderbuffer()->HasUninitializedImageData();
 
     if (Texture()) {
@@ -166,17 +166,17 @@ WebGLFramebuffer::AttachPoint::HasUninit
                                       mTexImageLevel).HasUninitializedImageData();
     }
 
     MOZ_ASSERT(false, "Should not get here.");
     return false;
 }
 
 void
-WebGLFramebuffer::AttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus)
+WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus)
 {
     if (!HasImage())
         return;
 
     if (Renderbuffer()) {
         Renderbuffer()->SetImageDataStatus(newStatus);
         return;
     }
@@ -186,29 +186,29 @@ WebGLFramebuffer::AttachPoint::SetImageD
                                       newStatus);
         return;
     }
 
     MOZ_ASSERT(false, "Should not get here.");
 }
 
 bool
-WebGLFramebuffer::AttachPoint::HasImage() const
+WebGLFBAttachPoint::HasImage() const
 {
     if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel))
         return true;
 
     if (Renderbuffer())
         return true;
 
     return false;
 }
 
 const WebGLRectangleObject&
-WebGLFramebuffer::AttachPoint::RectangleObject() const
+WebGLFBAttachPoint::RectangleObject() const
 {
     MOZ_ASSERT(HasImage(),
                "Make sure it has an image before requesting the rectangle.");
 
     if (Texture()) {
         MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel));
         return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
     }
@@ -280,17 +280,17 @@ WebGLContext::IsFormatValidForFB(GLenum 
     case LOCAL_GL_RGBA16F:
         return IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float);
     }
 
     return false;
 }
 
 bool
-WebGLFramebuffer::AttachPoint::IsComplete() const
+WebGLFBAttachPoint::IsComplete() const
 {
     if (!HasImage())
         return false;
 
     const WebGLRectangleObject& rect = RectangleObject();
 
     if (!rect.Width() ||
         !rect.Height())
@@ -348,17 +348,17 @@ WebGLFramebuffer::AttachPoint::IsComplet
         MOZ_ASSERT(false, "Invalid WebGL attachment point?");
         return false;
     }
     MOZ_ASSERT(false, "Should not get here.");
     return false;
 }
 
 void
-WebGLFramebuffer::AttachPoint::FinalizeAttachment(gl::GLContext* gl,
+WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl,
                                                  FBAttachment attachmentLoc) const
 {
     if (!HasImage()) {
         switch (attachmentLoc.get()) {
         case LOCAL_GL_DEPTH_ATTACHMENT:
         case LOCAL_GL_STENCIL_ATTACHMENT:
         case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
             break;
@@ -449,17 +449,17 @@ WebGLFramebuffer::FramebufferRenderbuffe
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", rb))
         return;
 
     // `attachPoint` is validated by ValidateFramebufferAttachment().
-    AttachPoint& attachPoint = GetAttachPoint(attachPointEnum);
+    WebGLFBAttachPoint& attachPoint = GetAttachPoint(attachPointEnum);
     attachPoint.SetRenderbuffer(rb);
 
     InvalidateFramebufferStatus();
 }
 
 void
 WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPointEnum,
                                        TexImageTarget texImageTarget,
@@ -476,23 +476,23 @@ WebGLFramebuffer::FramebufferTexture2D(F
         bool isTexTarget2D = texImageTarget == LOCAL_GL_TEXTURE_2D;
         if (isTexture2D != isTexTarget2D) {
             mContext->ErrorInvalidOperation("framebufferTexture2D: Mismatched"
                                             " texture and texture target.");
             return;
         }
     }
 
-    AttachPoint& attachPoint = GetAttachPoint(attachPointEnum);
+    WebGLFBAttachPoint& attachPoint = GetAttachPoint(attachPointEnum);
     attachPoint.SetTexImage(tex, texImageTarget, level);
 
     InvalidateFramebufferStatus();
 }
 
-WebGLFramebuffer::AttachPoint&
+WebGLFBAttachPoint&
 WebGLFramebuffer::GetAttachPoint(FBAttachment attachPoint)
 {
     switch (attachPoint.get()) {
     case LOCAL_GL_COLOR_ATTACHMENT0:
         return mColorAttachment0;
 
     case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
         return mDepthStencilAttachment;
@@ -576,17 +576,17 @@ WebGLFramebuffer::HasDefinedAttachments(
     for (size_t i = 0; i < moreColorAttachmentCount; i++) {
         hasAttachments |= mMoreColorAttachments[i].IsDefined();
     }
 
     return hasAttachments;
 }
 
 static bool
-IsIncomplete(const WebGLFramebuffer::AttachPoint& cur)
+IsIncomplete(const WebGLFBAttachPoint& cur)
 {
     return cur.IsDefined() && !cur.IsComplete();
 }
 
 bool
 WebGLFramebuffer::HasIncompleteAttachments() const
 {
     bool hasIncomplete = false;
@@ -626,17 +626,17 @@ WebGLFramebuffer::GetAnyRectObject() con
         if (mMoreColorAttachments[i].HasImage())
             return mMoreColorAttachments[i].RectangleObject();
     }
 
     MOZ_CRASH("Should not get here.");
 }
 
 static bool
-RectsMatch(const WebGLFramebuffer::AttachPoint& attachment,
+RectsMatch(const WebGLFBAttachPoint& attachment,
            const WebGLRectangleObject& rect)
 {
     return attachment.RectangleObject().HasSameDimensionsAs(rect);
 }
 
 bool
 WebGLFramebuffer::AllImageRectsMatch() const
 {
@@ -834,17 +834,17 @@ void WebGLFramebuffer::EnsureColorAttach
 {
     MOZ_ASSERT(colorAttachmentId < WebGLContext::kMaxColorAttachments);
 
     if (colorAttachmentId < ColorAttachmentCount())
         return;
 
     while (ColorAttachmentCount() < WebGLContext::kMaxColorAttachments) {
         GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + ColorAttachmentCount();
-        mMoreColorAttachments.AppendElement(AttachPoint(this, nextAttachPoint));
+        mMoreColorAttachments.AppendElement(WebGLFBAttachPoint(this, nextAttachPoint));
     }
 
     MOZ_ASSERT(ColorAttachmentCount() == WebGLContext::kMaxColorAttachments);
 }
 
 static void
 FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined)
 {
@@ -936,30 +936,30 @@ WebGLFramebuffer::ValidateForRead(const 
     *out_format = attachPoint.EffectiveInternalFormat();
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
-WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLFramebufferBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLFramebufferBinding::Wrap(cx, this, givenProto);
 }
 
 inline void
-ImplCycleCollectionUnlink(mozilla::WebGLFramebuffer::AttachPoint& field)
+ImplCycleCollectionUnlink(mozilla::WebGLFBAttachPoint& field)
 {
     field.Unlink();
 }
 
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
-                            mozilla::WebGLFramebuffer::AttachPoint& field,
+                            mozilla::WebGLFBAttachPoint& field,
                             const char* name,
                             uint32_t flags = 0)
 {
     CycleCollectionNoteChild(callback, field.Texture(), name, flags);
     CycleCollectionNoteChild(callback, field.Renderbuffer(), name, flags);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebuffer,
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -2,120 +2,125 @@
 /* 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 WEBGL_FRAMEBUFFER_H_
 #define WEBGL_FRAMEBUFFER_H_
 
 #include "mozilla/LinkedList.h"
+#include "mozilla/WeakPtr.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
+#include "WebGLRenderbuffer.h"
+#include "WebGLTexture.h"
 
 namespace mozilla {
 
+class WebGLFramebuffer;
 class WebGLRenderbuffer;
 class WebGLTexture;
 
 namespace gl {
     class GLContext;
 } // namespace gl
 
+class WebGLFBAttachPoint
+{
+public:
+    WebGLFramebuffer* const mFB;
+private:
+    WebGLRefPtr<WebGLTexture> mTexturePtr;
+    WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
+    FBAttachment mAttachmentPoint;
+    TexImageTarget mTexImageTarget;
+    GLint mTexImageLevel;
+
+public:
+    WebGLFBAttachPoint(WebGLFramebuffer* fb, FBAttachment attachmentPoint);
+    ~WebGLFBAttachPoint();
+
+    void Unlink() {
+        mRenderbufferPtr = nullptr;
+        mTexturePtr = nullptr;
+    }
+
+    bool IsDefined() const;
+    bool IsDeleteRequested() const;
+
+    TexInternalFormat EffectiveInternalFormat() const;
+
+    bool HasAlpha() const;
+    bool IsReadableFloat() const;
+
+    void Clear() {
+        SetRenderbuffer(nullptr);
+    }
+
+    void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
+    void SetRenderbuffer(WebGLRenderbuffer* rb);
+
+    const WebGLTexture* Texture() const {
+        return mTexturePtr;
+    }
+    WebGLTexture* Texture() {
+        return mTexturePtr;
+    }
+    const WebGLRenderbuffer* Renderbuffer() const {
+        return mRenderbufferPtr;
+    }
+    WebGLRenderbuffer* Renderbuffer() {
+        return mRenderbufferPtr;
+    }
+    TexImageTarget ImageTarget() const {
+        return mTexImageTarget;
+    }
+    GLint MipLevel() const {
+        return mTexImageLevel;
+    }
+
+    bool HasUninitializedImageData() const;
+    void SetImageDataStatus(WebGLImageDataStatus x);
+
+    const WebGLRectangleObject& RectangleObject() const;
+
+    bool HasImage() const;
+    bool IsComplete() const;
+
+    void FinalizeAttachment(gl::GLContext* gl,
+                            FBAttachment attachmentLoc) const;
+};
+
 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)
 
-    class AttachPoint
-    {
-    public:
-        WebGLFramebuffer* const mFB;
-    private:
-        WebGLRefPtr<WebGLTexture> mTexturePtr;
-        WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
-        FBAttachment mAttachmentPoint;
-        TexImageTarget mTexImageTarget;
-        GLint mTexImageLevel;
-
-    public:
-        AttachPoint(WebGLFramebuffer* fb, FBAttachment attachmentPoint);
-        ~AttachPoint();
-
-        void Unlink() {
-            mRenderbufferPtr = nullptr;
-            mTexturePtr = nullptr;
-        }
-
-        bool IsDefined() const;
-        bool IsDeleteRequested() const;
-
-        TexInternalFormat EffectiveInternalFormat() const;
-
-        bool HasAlpha() const;
-        bool IsReadableFloat() const;
-
-        void Clear() {
-            SetRenderbuffer(nullptr);
-        }
-
-        void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level);
-        void SetRenderbuffer(WebGLRenderbuffer* rb);
-
-        const WebGLTexture* Texture() const {
-            return mTexturePtr;
-        }
-        WebGLTexture* Texture() {
-            return mTexturePtr;
-        }
-        const WebGLRenderbuffer* Renderbuffer() const {
-            return mRenderbufferPtr;
-        }
-        WebGLRenderbuffer* Renderbuffer() {
-            return mRenderbufferPtr;
-        }
-        TexImageTarget ImageTarget() const {
-            return mTexImageTarget;
-        }
-        GLint MipLevel() const {
-            return mTexImageLevel;
-        }
-
-        bool HasUninitializedImageData() const;
-        void SetImageDataStatus(WebGLImageDataStatus x);
-
-        const WebGLRectangleObject& RectangleObject() const;
-
-        bool HasImage() const;
-        bool IsComplete() const;
-
-        void FinalizeAttachment(gl::GLContext* gl,
-                                FBAttachment attachmentLoc) const;
-    };
-
     const GLuint mGLName;
 
 private:
     mutable GLenum mStatus;
 
     GLenum mReadBufferMode;
 
     // No need to chase pointers for the oft-used color0.
-    AttachPoint mColorAttachment0;
-    AttachPoint mDepthAttachment;
-    AttachPoint mStencilAttachment;
-    AttachPoint mDepthStencilAttachment;
-    nsTArray<AttachPoint> mMoreColorAttachments;
+    WebGLFBAttachPoint mColorAttachment0;
+    WebGLFBAttachPoint mDepthAttachment;
+    WebGLFBAttachPoint mStencilAttachment;
+    WebGLFBAttachPoint mDepthStencilAttachment;
+    nsTArray<WebGLFBAttachPoint> mMoreColorAttachments;
 
 #ifdef ANDROID
     // Bug 1140459: Some drivers (including our test slaves!) don't
     // give reasonable answers for IsRenderbuffer, maybe others.
     // This shows up on Android 2.3 emulator.
     //
     // So we track the `is a Framebuffer` state ourselves.
     bool mIsFB;
@@ -143,60 +148,60 @@ public:
 
     bool HasDefinedAttachments() const;
     bool HasIncompleteAttachments() const;
     bool AllImageRectsMatch() const;
     FBStatus PrecheckFramebufferStatus() const;
     FBStatus CheckFramebufferStatus() const;
 
     GLenum
-    GetFormatForAttachment(const WebGLFramebuffer::AttachPoint& attachment) const;
+    GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const;
 
     bool HasDepthStencilConflict() const {
         return int(mDepthAttachment.IsDefined()) +
                int(mStencilAttachment.IsDefined()) +
                int(mDepthStencilAttachment.IsDefined()) >= 2;
     }
 
     size_t ColorAttachmentCount() const {
         return 1 + mMoreColorAttachments.Length();
     }
-    const AttachPoint& ColorAttachment(size_t colorAttachmentId) const {
+    const WebGLFBAttachPoint& ColorAttachment(size_t colorAttachmentId) const {
         MOZ_ASSERT(colorAttachmentId < ColorAttachmentCount());
         return colorAttachmentId ? mMoreColorAttachments[colorAttachmentId - 1]
                                  : mColorAttachment0;
     }
 
-    const AttachPoint& DepthAttachment() const {
+    const WebGLFBAttachPoint& DepthAttachment() const {
         return mDepthAttachment;
     }
 
-    const AttachPoint& StencilAttachment() const {
+    const WebGLFBAttachPoint& StencilAttachment() const {
         return mStencilAttachment;
     }
 
-    const AttachPoint& DepthStencilAttachment() const {
+    const WebGLFBAttachPoint& DepthStencilAttachment() const {
         return mDepthStencilAttachment;
     }
 
-    AttachPoint& GetAttachPoint(FBAttachment attachPointEnum);
+    WebGLFBAttachPoint& GetAttachPoint(FBAttachment attachPointEnum);
 
     void DetachTexture(const WebGLTexture* tex);
 
     void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
     const WebGLRectangleObject& RectangleObject() const;
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     void FinalizeAttachments() const;
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
 
     // mask mirrors glClear.
     bool HasCompletePlanes(GLbitfield mask);
 
     bool CheckAndInitializeAttachments();
--- a/dom/canvas/WebGLFramebufferAttachable.cpp
+++ b/dom/canvas/WebGLFramebufferAttachable.cpp
@@ -1,33 +1,30 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLFramebufferAttachable.h"
 
-#include "WebGLContext.h"
 #include "WebGLFramebuffer.h"
-#include "WebGLRenderbuffer.h"
-#include "WebGLTexture.h"
 
 namespace mozilla {
 
 void
-WebGLFramebufferAttachable::MarkAttachment(const WebGLFramebuffer::AttachPoint& attachment)
+WebGLFramebufferAttachable::MarkAttachment(const WebGLFBAttachPoint& attachment)
 {
     if (mAttachmentPoints.Contains(&attachment))
         return; // Already attached. Ignore.
 
     mAttachmentPoints.AppendElement(&attachment);
 }
 
 void
-WebGLFramebufferAttachable::UnmarkAttachment(const WebGLFramebuffer::AttachPoint& attachment)
+WebGLFramebufferAttachable::UnmarkAttachment(const WebGLFBAttachPoint& attachment)
 {
     const size_t i = mAttachmentPoints.IndexOf(&attachment);
     if (i == mAttachmentPoints.NoIndex) {
         MOZ_ASSERT(false, "Is not attached to FB");
         return;
     }
 
     mAttachmentPoints.RemoveElementAt(i);
--- a/dom/canvas/WebGLFramebufferAttachable.h
+++ b/dom/canvas/WebGLFramebufferAttachable.h
@@ -1,30 +1,27 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_FRAMEBUFFER_ATTACHABLE_H_
 #define WEBGL_FRAMEBUFFER_ATTACHABLE_H_
 
-#include "GLDefs.h"
-#include "mozilla/WeakPtr.h"
 #include "nsTArray.h"
-#include "WebGLFramebuffer.h"
-#include "WebGLStrongTypes.h"
 
 namespace mozilla {
+class WebGLFBAttachPoint;
 
 class WebGLFramebufferAttachable
 {
-    nsTArray<const WebGLFramebuffer::AttachPoint*> mAttachmentPoints;
+    nsTArray<const WebGLFBAttachPoint*> mAttachmentPoints;
 
 public:
     // Track FBO/Attachment combinations
-    void MarkAttachment(const WebGLFramebuffer::AttachPoint& attachment);
-    void UnmarkAttachment(const WebGLFramebuffer::AttachPoint& attachment);
+    void MarkAttachment(const WebGLFBAttachPoint& attachment);
+    void UnmarkAttachment(const WebGLFBAttachPoint& attachment);
     void InvalidateStatusOfAttachedFBs() const;
 };
 
 } // namespace mozilla
 
 #endif // !WEBGLFRAMEBUFFERATTACHABLE_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLMemoryTracker.cpp
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLMemoryTracker.h"
+
+#include "WebGLBuffer.h"
+#include "WebGLContext.h"
+#include "WebGLVertexAttribData.h"
+#include "WebGLProgram.h"
+#include "WebGLRenderbuffer.h"
+#include "WebGLShader.h"
+#include "WebGLTexture.h"
+#include "WebGLUniformLocation.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
+
+NS_IMETHODIMP
+WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
+                                   nsISupports* data, bool)
+{
+#define REPORT(_path, _kind, _units, _amount, _desc)                         \
+    do {                                                                     \
+      nsresult rv;                                                           \
+      rv = handleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
+                                   _kind, _units, _amount,                   \
+                                   NS_LITERAL_CSTRING(_desc), data);         \
+      NS_ENSURE_SUCCESS(rv, rv);                                             \
+    } while (0)
+
+    REPORT("webgl-texture-memory",
+           KIND_OTHER, UNITS_BYTES, GetTextureMemoryUsed(),
+           "Memory used by WebGL textures.The OpenGL"
+           " implementation is free to store these textures in either video"
+           " memory or main memory. This measurement is only a lower bound,"
+           " actual memory usage may be higher for example if the storage"
+           " is strided.");
+
+    REPORT("webgl-texture-count",
+           KIND_OTHER, UNITS_COUNT, GetTextureCount(),
+           "Number of WebGL textures.");
+
+    REPORT("webgl-buffer-memory",
+           KIND_OTHER, UNITS_BYTES, GetBufferMemoryUsed(),
+           "Memory used by WebGL buffers. The OpenGL"
+           " implementation is free to store these buffers in either video"
+           " memory or main memory. This measurement is only a lower bound,"
+           " actual memory usage may be higher for example if the storage"
+           " is strided.");
+
+    REPORT("explicit/webgl/buffer-cache-memory",
+           KIND_HEAP, UNITS_BYTES, GetBufferCacheMemoryUsed(),
+           "Memory used by WebGL buffer caches. The WebGL"
+           " implementation caches the contents of element array buffers"
+           " only.This adds up with the webgl-buffer-memory value, but"
+           " contrary to it, this one represents bytes on the heap,"
+           " not managed by OpenGL.");
+
+    REPORT("webgl-buffer-count",
+           KIND_OTHER, UNITS_COUNT, GetBufferCount(),
+           "Number of WebGL buffers.");
+
+    REPORT("webgl-renderbuffer-memory",
+           KIND_OTHER, UNITS_BYTES, GetRenderbufferMemoryUsed(),
+           "Memory used by WebGL renderbuffers. The OpenGL"
+           " implementation is free to store these renderbuffers in either"
+           " video memory or main memory. This measurement is only a lower"
+           " bound, actual memory usage may be higher for example if the"
+           " storage is strided.");
+
+    REPORT("webgl-renderbuffer-count",
+           KIND_OTHER, UNITS_COUNT, GetRenderbufferCount(),
+           "Number of WebGL renderbuffers.");
+
+    REPORT("explicit/webgl/shader",
+           KIND_HEAP, UNITS_BYTES, GetShaderSize(),
+           "Combined size of WebGL shader ASCII sources and translation"
+           " logs cached on the heap.");
+
+    REPORT("webgl-shader-count",
+           KIND_OTHER, UNITS_COUNT, GetShaderCount(),
+           "Number of WebGL shaders.");
+
+    REPORT("webgl-context-count",
+           KIND_OTHER, UNITS_COUNT, GetContextCount(),
+           "Number of WebGL contexts.");
+
+#undef REPORT
+
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(WebGLMemoryTracker, nsIMemoryReporter)
+
+StaticRefPtr<WebGLMemoryTracker> WebGLMemoryTracker::sUniqueInstance;
+
+WebGLMemoryTracker*
+WebGLMemoryTracker::UniqueInstance()
+{
+    if (!sUniqueInstance) {
+        sUniqueInstance = new WebGLMemoryTracker;
+        sUniqueInstance->InitMemoryReporter();
+    }
+    return sUniqueInstance;
+}
+
+WebGLMemoryTracker::WebGLMemoryTracker()
+{
+}
+
+void
+WebGLMemoryTracker::InitMemoryReporter()
+{
+    RegisterWeakMemoryReporter(this);
+}
+
+WebGLMemoryTracker::~WebGLMemoryTracker()
+{
+    UnregisterWeakMemoryReporter(this);
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(WebGLBufferMallocSizeOf)
+
+int64_t
+WebGLMemoryTracker::GetBufferCacheMemoryUsed()
+{
+    const ContextsArrayType& contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
+             buffer;
+             buffer = buffer->getNext())
+        {
+            if (buffer->Content() == WebGLBuffer::Kind::ElementArray) {
+                result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
+            }
+        }
+    }
+    return result;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(WebGLShaderMallocSizeOf)
+
+int64_t
+WebGLMemoryTracker::GetShaderSize()
+{
+    const ContextsArrayType& contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLShader* shader = contexts[i]->mShaders.getFirst();
+             shader;
+             shader = shader->getNext())
+        {
+            result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetTextureMemoryUsed()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLTexture* texture = contexts[i]->mTextures.getFirst();
+             texture;
+             texture = texture->getNext())
+        {
+            result += texture->MemoryUsage();
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetTextureCount()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLTexture* texture = contexts[i]->mTextures.getFirst();
+             texture;
+             texture = texture->getNext())
+        {
+            result++;
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetBufferMemoryUsed()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
+             buffer;
+             buffer = buffer->getNext())
+        {
+            result += buffer->ByteLength();
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetBufferCount()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
+             buffer;
+             buffer = buffer->getNext())
+        {
+            result++;
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetRenderbufferMemoryUsed()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLRenderbuffer* rb = contexts[i]->mRenderbuffers.getFirst();
+             rb;
+             rb = rb->getNext())
+        {
+            result += rb->MemoryUsage();
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetRenderbufferCount()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLRenderbuffer* rb = contexts[i]->mRenderbuffers.getFirst();
+             rb;
+             rb = rb->getNext())
+        {
+            result++;
+        }
+    }
+    return result;
+}
+
+/*static*/ int64_t
+WebGLMemoryTracker::GetShaderCount()
+{
+    const ContextsArrayType & contexts = Contexts();
+    int64_t result = 0;
+    for(size_t i = 0; i < contexts.Length(); ++i) {
+        for (const WebGLShader* shader = contexts[i]->mShaders.getFirst();
+             shader;
+             shader = shader->getNext())
+        {
+            result++;
+        }
+    }
+    return result;
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGLMemoryTracker.h
+++ b/dom/canvas/WebGLMemoryTracker.h
@@ -3,27 +3,21 @@
  * 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 WEBGL_MEMORY_TRACKER_H_
 #define WEBGL_MEMORY_TRACKER_H_
 
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
-#include "WebGLBuffer.h"
-#include "WebGLContext.h"
-#include "WebGLVertexAttribData.h"
-#include "WebGLProgram.h"
-#include "WebGLRenderbuffer.h"
-#include "WebGLShader.h"
-#include "WebGLTexture.h"
-#include "WebGLUniformLocation.h"
 
 namespace mozilla {
 
+class WebGLContext;
+
 class WebGLMemoryTracker : public nsIMemoryReporter
 {
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIMEMORYREPORTER
 
     WebGLMemoryTracker();
     static StaticRefPtr<WebGLMemoryTracker> sUniqueInstance;
 
@@ -53,117 +47,33 @@ class WebGLMemoryTracker : public nsIMem
         if (contexts.IsEmpty()) {
             sUniqueInstance = nullptr;
         }
     }
 
   private:
     virtual ~WebGLMemoryTracker();
 
-    static int64_t GetTextureMemoryUsed() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLTexture* texture = contexts[i]->mTextures.getFirst();
-                 texture;
-                 texture = texture->getNext())
-            {
-                result += texture->MemoryUsage();
-            }
-        }
-        return result;
-    }
+    static int64_t GetTextureMemoryUsed();
 
-    static int64_t GetTextureCount() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLTexture* texture = contexts[i]->mTextures.getFirst();
-                 texture;
-                 texture = texture->getNext())
-            {
-                result++;
-            }
-        }
-        return result;
-    }
+    static int64_t GetTextureCount();
 
-    static int64_t GetBufferMemoryUsed() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
-                 buffer;
-                 buffer = buffer->getNext())
-            {
-                result += buffer->ByteLength();
-            }
-        }
-        return result;
-    }
+    static int64_t GetBufferMemoryUsed();
 
     static int64_t GetBufferCacheMemoryUsed();
 
-    static int64_t GetBufferCount() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
-                 buffer;
-                 buffer = buffer->getNext())
-            {
-                result++;
-            }
-        }
-        return result;
-    }
+    static int64_t GetBufferCount();
 
-    static int64_t GetRenderbufferMemoryUsed() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLRenderbuffer* rb = contexts[i]->mRenderbuffers.getFirst();
-                 rb;
-                 rb = rb->getNext())
-            {
-                result += rb->MemoryUsage();
-            }
-        }
-        return result;
-    }
+    static int64_t GetRenderbufferMemoryUsed();
 
-    static int64_t GetRenderbufferCount() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLRenderbuffer* rb = contexts[i]->mRenderbuffers.getFirst();
-                 rb;
-                 rb = rb->getNext())
-            {
-                result++;
-            }
-        }
-        return result;
-    }
+    static int64_t GetRenderbufferCount();
 
     static int64_t GetShaderSize();
 
-    static int64_t GetShaderCount() {
-        const ContextsArrayType & contexts = Contexts();
-        int64_t result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            for (const WebGLShader* shader = contexts[i]->mShaders.getFirst();
-                 shader;
-                 shader = shader->getNext())
-            {
-                result++;
-            }
-        }
-        return result;
-    }
+    static int64_t GetShaderCount();
 
     static int64_t GetContextCount() {
         return Contexts().Length();
     }
 };
 
 } // namespace mozilla
 
--- a/dom/canvas/WebGLObjectModel.h
+++ b/dom/canvas/WebGLObjectModel.h
@@ -2,17 +2,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 WEBGLOBJECTMODEL_H_
 #define WEBGLOBJECTMODEL_H_
 
 #include "nsCycleCollectionNoteChild.h"
-#include "nsICanvasRenderingContextInternal.h"
+
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLContext;
 
 /* Each WebGL object class WebGLFoo wants to:
  *  - inherit WebGLRefCountedObject<WebGLFoo>
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -1,17 +1,21 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLProgram.h"
 
 #include "GLContext.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "nsRefPtr.h"
+#include "WebGLActiveInfo.h"
 #include "WebGLContext.h"
 #include "WebGLShader.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLValidateStrings.h"
 
 namespace mozilla {
 
 /* If `name`: "foo[3]"
@@ -62,22 +66,22 @@ ParseName(const nsCString& name, nsCStri
     *out_isArray = true;
     *out_arrayIndex = indexNum;
     return true;
 }
 
 static void
 AddActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
               const nsACString& baseUserName, const nsACString& baseMappedName,
-              std::vector<nsRefPtr<WebGLActiveInfo>>* activeInfoList,
+              std::vector<RefPtr<WebGLActiveInfo>>* activeInfoList,
               std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
 {
-    nsRefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
-                                                         isArray, baseUserName,
-                                                         baseMappedName);
+    RefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
+                                                       isArray, baseUserName,
+                                                       baseMappedName);
     activeInfoList->push_back(info);
 
     infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
 }
 
 static void
 AddActiveBlockInfo(const nsACString& baseUserName,
                    const nsACString& baseMappedName,
@@ -197,20 +201,20 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         nsAutoCString baseUserName;
         if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
             baseUserName = baseMappedName;
 
             if (needsCheckForArrays && !isArray) {
                 // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
                 // not an array. Our current linux Try slaves return the location of `foo`
                 // anyways, though.
-                std::string mappedName = baseMappedName.BeginReading();
-                mappedName += "[0]";
+                std::string mappedNameStr = baseMappedName.BeginReading();
+                mappedNameStr += "[0]";
 
-                GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedName.c_str());
+                GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
                 if (loc != -1)
                     isArray = true;
             }
         }
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                       (int)isArray, baseMappedName.BeginReading(),
@@ -241,24 +245,27 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
             nsAutoCString baseMappedName;
             bool isArray;
             size_t arrayIndex;
             if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
                 MOZ_CRASH("Failed to parse `mappedName` received from driver.");
 
             nsAutoCString baseUserName;
-            if (!prog->FindUniformBlockByMappedName(baseMappedName, &baseUserName, &isArray)) {
+            if (!prog->FindUniformBlockByMappedName(baseMappedName, &baseUserName,
+                                                    &isArray))
+            {
                 baseUserName = baseMappedName;
 
                 if (needsCheckForArrays && !isArray) {
-                    std::string mappedName = baseMappedName.BeginReading();
-                    mappedName += "[0]";
+                    std::string mappedNameStr = baseMappedName.BeginReading();
+                    mappedNameStr += "[0]";
 
-                    GLuint loc = gl->fGetUniformBlockIndex(prog->mGLName, mappedName.c_str());
+                    GLuint loc = gl->fGetUniformBlockIndex(prog->mGLName,
+                                                           mappedNameStr.c_str());
                     if (loc != LOCAL_GL_INVALID_INDEX)
                         isArray = true;
                 }
             }
 
 #ifdef DUMP_SHADERVAR_MAPPINGS
             printf_stderr("[uniform block %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                           (int)isArray, baseMappedName.BeginReading(),
@@ -272,18 +279,18 @@ QueryProgramInfo(WebGLProgram* prog, gl:
     }
 
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 
-webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg)
-    : prog(aProg)
+webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
+    : prog(prog)
     , fragDataMap(nullptr)
 { }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLProgram
 
 static GLuint
 CreateProgram(gl::GLContext* gl)
@@ -295,16 +302,21 @@ CreateProgram(gl::GLContext* gl)
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mGLName(CreateProgram(webgl->GL()))
     , mTransformFeedbackBufferMode(LOCAL_GL_NONE)
 {
     mContext->mPrograms.insertBack(this);
 }
 
+WebGLProgram::~WebGLProgram()
+{
+    DeleteOnce();
+}
+
 void
 WebGLProgram::Delete()
 {
     gl::GLContext* gl = mContext->GL();
 
     gl->MakeCurrent();
     gl->fDeleteProgram(mGLName);
 
@@ -420,17 +432,17 @@ WebGLProgram::GetActiveAttrib(GLuint ind
     const auto& activeList = mMostRecentLinkInfo->activeAttribs;
 
     if (index >= activeList.size()) {
         mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
                                     index, "ACTIVE_ATTRIBS", activeList.size());
         return nullptr;
     }
 
-    nsRefPtr<WebGLActiveInfo> ret =  activeList[index];
+    RefPtr<WebGLActiveInfo> ret = activeList[index];
     return ret.forget();
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLProgram::GetActiveUniform(GLuint index) const
 {
     if (!mMostRecentLinkInfo) {
         // According to the spec, this can return null.
@@ -441,17 +453,17 @@ WebGLProgram::GetActiveUniform(GLuint in
     const auto& activeList = mMostRecentLinkInfo->activeUniforms;
 
     if (index >= activeList.size()) {
         mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
                                     index, "ACTIVE_UNIFORMS", activeList.size());
         return nullptr;
     }
 
-    nsRefPtr<WebGLActiveInfo> ret = activeList[index];
+    RefPtr<WebGLActiveInfo> ret = activeList[index];
     return ret.forget();
 }
 
 void
 WebGLProgram::GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const
 {
     out->TruncateLength(0);
 
@@ -622,17 +634,17 @@ WebGLProgram::GetActiveUniformBlockName(
 
     const webgl::UniformBlockInfo* blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
 
     retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mBaseUserName));
 }
 
 void
 WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname,
-                                         Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const
+                                         dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const
 {
     retval.SetNull();
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
         return;
     }
 
     const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
@@ -658,17 +670,17 @@ WebGLProgram::GetActiveUniformBlockParam
         gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
         retval.SetValue().SetAsUnsignedLong() = param;
         return;
     }
 }
 
 void
 WebGLProgram::GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
-                                                  Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
+                                                  dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
                                                   ErrorResult& rv) const
 {
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
         return;
     }
 
     const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
@@ -1008,17 +1020,17 @@ WebGLProgram::GetTransformFeedbackVaryin
     }
 
     const nsCString& varyingUserName = mTransformFeedbackVaryings[index];
 
     WebGLActiveInfo* info;
     LinkInfo()->FindAttrib(varyingUserName, (const WebGLActiveInfo**) &info);
     MOZ_ASSERT(info);
 
-    nsRefPtr<WebGLActiveInfo> ret(info);
+    RefPtr<WebGLActiveInfo> ret(info);
     return ret.forget();
 }
 
 bool
 WebGLProgram::FindUniformBlockByMappedName(const nsACString& mappedName,
                                            nsCString* const out_userName,
                                            bool* const out_isArray) const
 {
@@ -1029,19 +1041,19 @@ WebGLProgram::FindUniformBlockByMappedNa
         return true;
 
     return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
-WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
+WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLProgramBinding::Wrap(js, this, aGivenProto);
+    return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -2,32 +2,43 @@
 /* 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 WEBGL_PROGRAM_H_
 #define WEBGL_PROGRAM_H_
 
 #include <map>
-#include "mozilla/CheckedInt.h"
-#include "mozilla/LinkedList.h"
-#include "mozilla/dom/WebGL2RenderingContextBinding.h"
-#include "nsString.h"
-#include "nsWrapperCache.h"
 #include <set>
 #include <vector>
+
+#include "mozilla/LinkedList.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+#include "nsString.h"
+#include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
-#include "WebGLShader.h"
+
+
+template<class> class nsRefPtr;
 
 namespace mozilla {
-
+class ErrorResult;
 class WebGLActiveInfo;
 class WebGLProgram;
+class WebGLShader;
 class WebGLUniformLocation;
 
+namespace dom {
+template<typename> struct Nullable;
+class OwningUnsignedLongOrUint32ArrayOrBoolean;
+template<typename> class Sequence;
+} // namespace dom
+
 namespace webgl {
 
 struct UniformBlockInfo final
     : public RefCounted<UniformBlockInfo>
 {
     MOZ_DECLARE_REFCOUNTED_TYPENAME(UniformBlockInfo);
 
     const nsCString mBaseUserName;
@@ -43,31 +54,31 @@ struct UniformBlockInfo final
 struct LinkedProgramInfo final
     : public RefCounted<LinkedProgramInfo>
     , public SupportsWeakPtr<LinkedProgramInfo>
 {
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
 
     WebGLProgram* const prog;
-    std::vector<nsRefPtr<WebGLActiveInfo>> activeAttribs;
-    std::vector<nsRefPtr<WebGLActiveInfo>> activeUniforms;
+    std::vector<RefPtr<WebGLActiveInfo>> activeAttribs;
+    std::vector<RefPtr<WebGLActiveInfo>> activeUniforms;
 
     // Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped
     // user-facing `GLActiveInfo::name`s, without any final "[0]".
     std::map<nsCString, const WebGLActiveInfo*> attribMap;
     std::map<nsCString, const WebGLActiveInfo*> uniformMap;
     std::map<nsCString, const nsCString>* fragDataMap;
 
     std::vector<RefPtr<UniformBlockInfo>> uniformBlocks;
 
     // Needed for draw call validation.
     std::set<GLuint> activeAttribLocs;
 
-    explicit LinkedProgramInfo(WebGLProgram* aProg);
+    explicit LinkedProgramInfo(WebGLProgram* prog);
 
     bool FindAttrib(const nsCString& baseUserName,
                     const WebGLActiveInfo** const out_activeInfo) const
     {
         auto itr = attribMap.find(baseUserName);
         if (itr == attribMap.end())
             return false;
 
@@ -114,20 +125,16 @@ struct LinkedProgramInfo final
     bool HasActiveAttrib(GLuint loc) const {
         auto itr = activeAttribLocs.find(loc);
         return itr != activeAttribLocs.end();
     }
 };
 
 } // namespace webgl
 
-class WebGLShader;
-
-typedef nsDataHashtable<nsCStringHashKey, nsCString> CStringMap;
-
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
     , public WebGLContextBoundObject
 {
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLProgram)
@@ -146,19 +153,19 @@ public:
     void GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const;
     GLint GetAttribLocation(const nsAString& name) const;
     GLint GetFragDataLocation(const nsAString& name) const;
     void GetProgramInfoLog(nsAString* const out) const;
     JS::Value GetProgramParameter(GLenum pname) const;
     GLuint GetUniformBlockIndex(const nsAString& name) const;
     void GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& name) const;
     void GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname,
-                                    Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const;
+                                    dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const;
     void GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
-                                             Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
+                                             dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
                                              ErrorResult& rv) const;
     already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const;
     void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const;
 
     bool LinkProgram();
     bool UseProgram() const;
     void ValidateProgram() const;
 
@@ -182,22 +189,20 @@ public:
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
 private:
-    ~WebGLProgram() {
-        DeleteOnce();
-    }
+    ~WebGLProgram();
 
     bool LinkAndUpdate();
 
 public:
     const GLuint mGLName;
 
 private:
     WebGLRefPtr<WebGLShader> mVertShader;
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -8,19 +8,19 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "nsContentUtils.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 JSObject*
-WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLQueryBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLQueryBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLQuery::WebGLQuery(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mGLName(0)
     , mType(0)
 {
     mContext->mQueries.insertBack(this);
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -3,16 +3,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/. */
 
 #ifndef WEBGL_QUERY_H_
 #define WEBGL_QUERY_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLQuery final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLQuery>
     , public LinkedListElement<WebGLQuery>
@@ -31,17 +32,17 @@ public:
     void Delete();
 
     // nsWrapperCache
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     // NS
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLQuery)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLQuery)
 
 
 private:
     ~WebGLQuery() {
         DeleteOnce();
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLRenderbuffer.h"
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
+#include "WebGLStrongTypes.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 
 static GLenum
 DepthStencilDepthFormat(gl::GLContext* gl)
 {
     // We might not be able to get 24-bit, so let's pretend!
@@ -36,19 +37,19 @@ NeedsDepthStencilEmu(gl::GLContext* gl, 
     MOZ_ASSERT(internalFormat != LOCAL_GL_DEPTH_STENCIL);
     if (internalFormat != LOCAL_GL_DEPTH24_STENCIL8)
         return false;
 
     return !SupportsDepthStencil(gl);
 }
 
 JSObject*
-WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLRenderbufferBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLRenderbufferBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mPrimaryRB(0)
     , mSecondaryRB(0)
     , mInternalFormat(0)
     , mInternalFormatForGL(0)
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -3,18 +3,20 @@
  * 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 WEBGL_RENDERBUFFER_H_
 #define WEBGL_RENDERBUFFER_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
+#include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 class WebGLRenderbuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
@@ -59,17 +61,17 @@ public:
 
     void BindRenderbuffer() const;
     void RenderbufferStorage(GLsizei samples, GLenum internalFormat,
                              GLsizei width, GLsizei height) const;
     void FramebufferRenderbuffer(FBAttachment attachment) const;
     // Only handles a subset of `pname`s.
     GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const;
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLRenderbuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLRenderbuffer)
 
 protected:
     ~WebGLRenderbuffer() {
         DeleteOnce();
     }
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -34,19 +34,19 @@ WebGLSampler::Delete()
 
 WebGLContext*
 WebGLSampler::GetParentObject() const
 {
     return Context();
 }
 
 JSObject*
-WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLSampler::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLSamplerBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLSamplerBinding::Wrap(cx, this, givenProto);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSampler)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSampler, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSampler, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -23,17 +23,17 @@ class WebGLSampler final
 public:
     explicit WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
     const GLuint mGLName;
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 private:
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSampler)
 
 private:
     ~WebGLSampler();
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -398,19 +398,19 @@ WebGLShader::ApplyTransformFeedbackVaryi
 
     out_mappedVaryings->swap(mappedVaryings);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Boilerplate
 
 JSObject*
-WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
+WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLShaderBinding::Wrap(js, this, aGivenProto);
+    return dom::WebGLShaderBinding::Wrap(js, this, givenProto);
 }
 
 size_t
 WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
 {
     size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get())
                                       : 0;
     return mallocSizeOf(this) +
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -1,20 +1,25 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_SHADER_H_
 #define WEBGL_SHADER_H_
 
+#include <string>
+#include <vector>
+
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
+#include "nsString.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 namespace webgl {
 class ShaderValidator;
 } // namespace webgl
 
@@ -60,22 +65,22 @@ public:
     }
 
     void ApplyTransformFeedbackVaryings(GLuint prog,
                                         const std::vector<nsCString>& varyings,
                                         GLenum bufferMode,
                                         std::vector<std::string>* out_mappedVaryings) const;
 
     // Other funcs
-    size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
     void Delete();
 
     WebGLContext* GetParentObject() const { return Context(); }
 
-    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLShader)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLShader)
 
 public:
     const GLuint mGLName;
     const GLenum mType;
 protected:
--- a/dom/canvas/WebGLShaderPrecisionFormat.cpp
+++ b/dom/canvas/WebGLShaderPrecisionFormat.cpp
@@ -6,16 +6,16 @@
 #include "WebGLShaderPrecisionFormat.h"
 
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 bool
-WebGLShaderPrecisionFormat::WrapObject(JSContext* aCx,
-                                       JS::Handle<JSObject*> aGivenProto,
-                                       JS::MutableHandle<JSObject*> aReflector)
+WebGLShaderPrecisionFormat::WrapObject(JSContext* cx,
+                                       JS::Handle<JSObject*> givenProto,
+                                       JS::MutableHandle<JSObject*> reflector)
 {
-    return dom::WebGLShaderPrecisionFormatBinding::Wrap(aCx, this, aGivenProto, aReflector);
+    return dom::WebGLShaderPrecisionFormatBinding::Wrap(cx, this, givenProto, reflector);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderPrecisionFormat.h
+++ b/dom/canvas/WebGLShaderPrecisionFormat.h
@@ -17,17 +17,17 @@ public:
     WebGLShaderPrecisionFormat(WebGLContext* context, GLint rangeMin,
                                GLint rangeMax, GLint precision)
         : WebGLContextBoundObject(context)
         , mRangeMin(rangeMin)
         , mRangeMax(rangeMax)
         , mPrecision(precision)
     { }
 
-    bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
+    bool WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto, JS::MutableHandle<JSObject*> reflector);
 
     // WebIDL WebGLShaderPrecisionFormat API
     GLint RangeMin() const {
         return mRangeMin;
     }
 
     GLint RangeMax() const {
         return mRangeMax;
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_STRONG_TYPES_H_
 #define WEBGL_STRONG_TYPES_H_
 
+#include <algorithm>
+
 #include "GLDefs.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 /* Usage:
  * ===========
  *
--- a/dom/canvas/WebGLSync.cpp
+++ b/dom/canvas/WebGLSync.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLSync.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSync::WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags)
     : WebGLContextBoundObject(webgl)
 {
    mGLName = mContext->gl->fFenceSync(condition, flags);
 }
@@ -33,18 +35,18 @@ WebGLContext*
 WebGLSync::GetParentObject() const
 {
     return Context();
 }
 
 // -------------------------------------------------------------------------
 // IMPLEMENT NS
 JSObject*
-WebGLSync::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLSync::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLSyncBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLSyncBinding::Wrap(cx, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLSync)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLSync, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLSync, Release);
 
 } // namespace mozilla
--- a/dom/canvas/WebGLSync.h
+++ b/dom/canvas/WebGLSync.h
@@ -21,17 +21,17 @@ class WebGLSync final
     friend class WebGL2Context;
 
 public:
     WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSync)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSync)
 
 private:
     ~WebGLSync();
 
     GLsync mGLName;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -14,18 +14,18 @@
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexelConversions.h"
 #include "mozilla/gfx/Logging.h"
 
 namespace mozilla {
 
 JSObject*
-WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
-    return dom::WebGLTextureBinding::Wrap(cx, this, aGivenProto);
+WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
+    return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
     : WebGLContextBoundObject(webgl)
     , mGLName(tex)
     , mTarget(LOCAL_GL_NONE)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -2,21 +2,22 @@
 /* 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 WEBGL_TEXTURE_H_
 #define WEBGL_TEXTURE_H_
 
 #include <algorithm>
+
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
-#include "nsAlgorithm.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool
@@ -42,17 +43,17 @@ public:
 
     bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
     GLenum Target() const { return mTarget; }
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
 
 protected:
     ~WebGLTexture() {
         DeleteOnce();
     }
--- a/dom/canvas/WebGLTimerQuery.cpp
+++ b/dom/canvas/WebGLTimerQuery.cpp
@@ -9,24 +9,24 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "nsContentUtils.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 JSObject*
-WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-  return dom::WebGLTimerQueryEXTBinding::Wrap(cx, this, aGivenProto);
+  return dom::WebGLTimerQueryEXTBinding::Wrap(cx, this, givenProto);
 }
 
-WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint aName)
+WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint name)
   : WebGLContextBoundObject(webgl)
-  , mGLName(aName)
+  , mGLName(name)
   , mTarget(LOCAL_GL_NONE)
 {
 }
 
 WebGLTimerQuery::~WebGLTimerQuery()
 {
   DeleteOnce();
 }
--- a/dom/canvas/WebGLTimerQuery.h
+++ b/dom/canvas/WebGLTimerQuery.h
@@ -24,25 +24,25 @@ public:
   void Delete();
 
   bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
   GLenum Target() const { return mTarget; }
 
   WebGLContext* GetParentObject() const;
 
   // NS
-  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
   const GLenum mGLName;
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
 
 private:
-  explicit WebGLTimerQuery(WebGLContext* webgl, GLuint aName);
+  explicit WebGLTimerQuery(WebGLContext* webgl, GLuint name);
   ~WebGLTimerQuery();
 
   GLenum mTarget;
 
   friend class WebGLExtensionDisjointTimerQuery;
 };
 
 } // namespace mozilla
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -40,19 +40,19 @@ WebGLTransformFeedback::Delete()
 
 WebGLContext*
 WebGLTransformFeedback::GetParentObject() const
 {
     return Context();
 }
 
 JSObject*
-WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLTransformFeedback::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLTransformFeedbackBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLTransformFeedbackBinding::Wrap(cx, this, givenProto);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTransformFeedback)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTransformFeedback, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTransformFeedback, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -21,17 +21,17 @@ class WebGLTransformFeedback final
     friend class WebGLContext;
     friend class WebGL2Context;
 
 public:
     explicit WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 
     void Delete();
     WebGLContext* GetParentObject() const;
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     const GLuint mGLName;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
 
 private:
     ~WebGLTransformFeedback();
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -316,19 +316,19 @@ WebGLUniformLocation::GetUniform(JSConte
     default:
         MOZ_CRASH("Invalid elemType.");
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
-WebGLUniformLocation::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
+WebGLUniformLocation::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLUniformLocationBinding::Wrap(js, this, aGivenProto);
+    return dom::WebGLUniformLocationBinding::Wrap(js, this, givenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLUniformLocation)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLUniformLocation, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLUniformLocation, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLUniformLocation.h
+++ b/dom/canvas/WebGLUniformLocation.h
@@ -6,16 +6,17 @@
 #ifndef WEBGL_UNIFORM_LOCATION_H_
 #define WEBGL_UNIFORM_LOCATION_H_
 
 #include "GLDefs.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS
 #include "nsISupportsImpl.h" // NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING
 #include "nsWrapperCache.h"
+
 #include "WebGLObjectModel.h"
 
 struct JSContext;
 
 namespace mozilla {
 class WebGLActiveInfo;
 class WebGLContext;
 class WebGLProgram;
@@ -27,23 +28,22 @@ struct LinkedProgramInfo;
 class WebGLUniformLocation final
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLUniformLocation)
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLUniformLocation)
 
-    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     WebGLContext* GetParentObject() const {
         return mContext;
     }
 
-
     const WeakPtr<const webgl::LinkedProgramInfo> mLinkInfo;
     const GLuint mLoc;
     const WebGLActiveInfo* const mActiveInfo;
 
     WebGLUniformLocation(WebGLContext* webgl, const webgl::LinkedProgramInfo* linkInfo,
                          GLuint loc, const WebGLActiveInfo* activeInfo);
 
     bool ValidateForProgram(WebGLProgram* prog, WebGLContext* webgl,
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -10,19 +10,19 @@
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLVertexArrayGL.h"
 #include "WebGLVertexArrayFake.h"
 
 namespace mozilla {
 
 JSObject*
-WebGLVertexArray::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+WebGLVertexArray::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
-    return dom::WebGLVertexArrayObjectOESBinding::Wrap(cx, this, aGivenProto);
+    return dom::WebGLVertexArrayObjectOESBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mGLName(0)
 {
     mContext->mVertexArrays.insertBack(this);
 }
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -1,18 +1,20 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 WEBGL_VERTEX_ARRAY_H_
 #define WEBGL_VERTEX_ARRAY_H_
 
+#include "nsTArray.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
+
 #include "WebGLBuffer.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake;
@@ -43,17 +45,17 @@ public:
     // Implement parent classes:
     void Delete();
     bool IsVertexArray();
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+    virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray)
 
     GLuint GLName() const { return mGLName; }
 
 protected:
     explicit WebGLVertexArray(WebGLContext* webgl);
--- a/dom/canvas/WebGLVertexArrayObject.cpp
+++ b/dom/canvas/WebGLVertexArrayObject.cpp
@@ -1,17 +1,19 @@
 /* -*- 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 "WebGLVertexArrayObject.h"
 
+#include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "WebGLContext.h"
 
 namespace mozilla {
 namespace dom {
 
 WebGLVertexArray*
 WebGLVertexArrayObject::Create(WebGLContext* webgl)
 {
   // WebGL 2: This is core in GL ES 3. If support is missing something
--- a/dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
+++ b/dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
@@ -8,18 +8,16 @@
 
 #include "WebGLElementArrayCache.cpp"
 
 #include <cstdlib>
 #include <iostream>
 #include "nscore.h"
 #include "nsTArray.h"
 
-using namespace mozilla;
-
 int gTestsPassed = 0;
 
 void
 VerifyImplFunction(bool condition, const char* file, int line)
 {
   if (condition) {
     gTestsPassed++;
   } else {
@@ -60,32 +58,33 @@ GLType()
   case 1:  return LOCAL_GL_UNSIGNED_BYTE;
   default:
     VERIFY(false);
     return 0;
   }
 }
 
 void
-CheckValidate(bool expectSuccess, WebGLElementArrayCache& c, GLenum type,
+CheckValidate(bool expectSuccess, mozilla::WebGLElementArrayCache& c, GLenum type,
               uint32_t maxAllowed, size_t first, size_t count)
 {
   uint32_t out_upperBound = 0;
   const bool success = c.Validate(type, maxAllowed, first, count, &out_upperBound);
   VERIFY(success == expectSuccess);
   if (success) {
     VERIFY(out_upperBound <= maxAllowed);
   } else {
     VERIFY(out_upperBound > maxAllowed);
   }
 }
 
 template<typename T>
 void
-CheckValidateOneTypeVariousBounds(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+CheckValidateOneTypeVariousBounds(mozilla::WebGLElementArrayCache& c, size_t firstByte,
+                                  size_t countBytes)
 {
   size_t first = firstByte / sizeof(T);
   size_t count = countBytes / sizeof(T);
 
   GLenum type = GLType<T>();
 
   T max = 0;
   for (size_t i = 0; i < count; i++)
@@ -96,17 +95,18 @@ CheckValidateOneTypeVariousBounds(WebGLE
   CheckValidate(true, c, type, T(-1), first, count);
   if (T(max + 1)) CheckValidate(true, c, type, T(max + 1), first, count);
   if (max > 0) {
     CheckValidate(false, c, type, max - 1, first, count);
     CheckValidate(false, c, type, 0, first, count);
   }
 }
 
-void CheckValidateAllTypes(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+void CheckValidateAllTypes(mozilla::WebGLElementArrayCache& c, size_t firstByte,
+                           size_t countBytes)
 {
   CheckValidateOneTypeVariousBounds<uint8_t>(c, firstByte, countBytes);
   CheckValidateOneTypeVariousBounds<uint16_t>(c, firstByte, countBytes);
   CheckValidateOneTypeVariousBounds<uint32_t>(c, firstByte, countBytes);
 }
 
 template<typename T>
 void
@@ -115,17 +115,17 @@ CheckSanity()
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                         // ensure we exercise some nontrivial tree-walking
   T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
   size_t numBytes = numElems * sizeof(T);
   MOZ_RELEASE_ASSERT(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
-  WebGLElementArrayCache c;
+  mozilla::WebGLElementArrayCache c;
   c.BufferData(data, numBytes);
   CheckValidate(true,  c, type, 6, 0, 8);
   CheckValidate(false, c, type, 5, 0, 8);
   CheckValidate(true,  c, type, 3, 0, 3);
   CheckValidate(false, c, type, 2, 0, 3);
   CheckValidate(true,  c, type, 6, 2, 4);
   CheckValidate(false, c, type, 5, 2, 4);
 
@@ -156,17 +156,17 @@ CheckUintOverflow()
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                               // ensure we exercise some nontrivial tree-walking
   T data[numElems];
   size_t numBytes = numElems * sizeof(T);
   MOZ_RELEASE_ASSERT(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
-  WebGLElementArrayCache c;
+  mozilla::WebGLElementArrayCache c;
 
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
 
   // bug 825205
   uint32_t bigValWrappingToZero = uint32_t(T(-1)) + 1;
   CheckValidate(true,  c, type, bigValWrappingToZero,     0, numElems);
@@ -182,17 +182,17 @@ main(int argc, char* argv[])
   CheckSanity<uint8_t>();
   CheckSanity<uint16_t>();
   CheckSanity<uint32_t>();
 
   CheckUintOverflow<uint8_t>();
   CheckUintOverflow<uint16_t>();
 
   nsTArray<uint8_t> v, vsub;
-  WebGLElementArrayCache b;
+  mozilla::WebGLElementArrayCache b;
 
   for (int maxBufferSize = 1; maxBufferSize <= 4096; maxBufferSize *= 2) {
     // See bug 800612. We originally had | repeat = min(maxBufferSize, 20) |
     // and a real bug was only caught on Windows and not on Linux due to rand()
     // producing different values. In that case, the minimum value by which to replace
     // this 20 to reproduce the bug on Linux, was 25. Replacing it with 64 should give
     // us some comfort margin.
     int repeat = std::min(maxBufferSize, 64);
@@ -224,8 +224,9 @@ main(int argc, char* argv[])
         } // bufferSubDataCalls
       } // j
     } // i
   } // maxBufferSize
 
   std::cerr << argv[0] << ": all " << gTestsPassed << " tests passed" << std::endl;
   return 0;
 }
+
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -71,17 +71,16 @@ UNIFIED_SOURCES += [
     'WebGLBuffer.cpp',
     'WebGLContext.cpp',
     'WebGLContextBuffers.cpp',
     'WebGLContextDraw.cpp',
     'WebGLContextExtensions.cpp',
     'WebGLContextFramebufferOperations.cpp',
     'WebGLContextGL.cpp',
     'WebGLContextLossHandler.cpp',
-    'WebGLContextReporter.cpp',
     'WebGLContextState.cpp',
     'WebGLContextUnchecked.cpp',
     'WebGLContextUtils.cpp',
     'WebGLContextValidate.cpp',
     'WebGLContextVertexArray.cpp',
     'WebGLContextVertices.cpp',
     'WebGLElementArrayCache.cpp',
     'WebGLExtensionBase.cpp',
@@ -108,16 +107,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionTextureFloat.cpp',
     'WebGLExtensionTextureFloatLinear.cpp',
     'WebGLExtensionTextureHalfFloat.cpp',
     'WebGLExtensionTextureHalfFloatLinear.cpp',
     'WebGLExtensionVertexArray.cpp',
     'WebGLFormats.cpp',
     'WebGLFramebuffer.cpp',
     'WebGLFramebufferAttachable.cpp',
+    'WebGLMemoryTracker.cpp',
     'WebGLObjectModel.cpp',
     'WebGLProgram.cpp',
     'WebGLQuery.cpp',
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
     'WebGLShaderValidator.cpp',
@@ -128,17 +128,19 @@ UNIFIED_SOURCES += [
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
     'WebGLVertexArrayObject.cpp',
 ]
+
 LOCAL_INCLUDES += [
+    '../..', # Support `#include "mfbt/RefPtr.h"`
     '/js/xpconnect/wrappers',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -37,20 +37,20 @@ class nsICanvasRenderingContextInternal 
   public nsAPostRefreshObserver
 {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
-  void SetCanvasElement(mozilla::dom::HTMLCanvasElement* aParentCanvas)
+  void SetCanvasElement(mozilla::dom::HTMLCanvasElement* parentCanvas)
   {
     RemovePostRefreshObserver();
-    mCanvasElement = aParentCanvas;
+    mCanvasElement = parentCanvas;
     AddPostRefreshObserverIfNecessary();
   }
 
   virtual nsIPresShell *GetPresShell() {
     if (mCanvasElement) {
       return mCanvasElement->OwnerDoc()->GetShell();
     }
     return nullptr;
@@ -88,69 +88,69 @@ public:
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Creates an image buffer. Returns null on failure.
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
+  virtual void GetImageBuffer(uint8_t** imageBuffer, int32_t* format) = 0;
 
   // Gives you a stream containing the image represented by this context.
-  // The format is given in aMimeTime, for example "image/png".
+  // The format is given in mimeTime, for example "image/png".
   //
-  // If the image format does not support transparency or aIncludeTransparency
+  // If the image format does not support transparency or includeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
-  NS_IMETHOD GetInputStream(const char *aMimeType,
-                            const char16_t *aEncoderOptions,
-                            nsIInputStream **aStream) = 0;
-  
+  NS_IMETHOD GetInputStream(const char *mimeType,
+                            const char16_t *encoderOptions,
+                            nsIInputStream **stream) = 0;
+
   // This gets an Azure SourceSurface for the canvas, this will be a snapshot
   // of the canvas at the time it was called.
-  // If aPremultAlpha is provided, then it assumed the callee can handle
-  // un-premultiplied surfaces, and *aPremultAlpha will be set to false
+  // If premultAlpha is provided, then it assumed the callee can handle
+  // un-premultiplied surfaces, and *premultAlpha will be set to false
   // if one is returned.
-  virtual already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) = 0;
+  virtual already_AddRefed<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* premultAlpha = nullptr) = 0;
 
   // If this context is opaque, the backing store of the canvas should
   // be created as opaque; all compositing operators should assume the
   // dst alpha is always 1.0.  If this is never called, the context
   // defaults to false (not opaque).
   NS_IMETHOD SetIsOpaque(bool isOpaque) = 0;
   virtual bool GetIsOpaque() = 0;
 
   // Invalidate this context and release any held resources, in preperation
   // for possibly reinitializing with SetDimensions/InitializeWithSurface.
   NS_IMETHOD Reset() = 0;
 
   // Return the CanvasLayer for this context, creating
   // one for the given layer manager if not available.
-  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                                       CanvasLayer *aOldLayer,
-                                                       LayerManager *aManager) = 0;
+  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* builder,
+                                                       CanvasLayer *oldLayer,
+                                                       LayerManager *manager) = 0;
 
   // Return true if the canvas should be forced to be "inactive" to ensure
   // it can be drawn to the screen even if it's too large to be blitted by
   // an accelerated CanvasLayer.
-  virtual bool ShouldForceInactiveLayer(LayerManager *aManager) { return false; }
+  virtual bool ShouldForceInactiveLayer(LayerManager *manager) { return false; }
 
   virtual void MarkContextClean() = 0;
 
   // Redraw the dirty rectangle of this canvas.
   NS_IMETHOD Redraw(const gfxRect &dirty) = 0;
 
-  NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions) { return NS_OK; }
+  NS_IMETHOD SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options) { return NS_OK; }
 
   // return true and fills in the bounding rect if elementis a child and has a hit region.
-  virtual bool GetHitRegionRect(mozilla::dom::Element* aElement, nsRect& aRect) { return false; }
+  virtual bool GetHitRegionRect(mozilla::dom::Element* element, nsRect& rect) { return false; }
 
   // Given a point, return hit region ID if it exists or an empty string if it doesn't
-  virtual nsString GetHitRegion(const mozilla::gfx::Point& aPoint) { return nsString(); }
+  virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
 
   //
   // shmem support
   //
 
   // If this context can be set to use Mozilla's Shmem segments as its backing
   // store, this will set it to that state. Note that if you have drawn
   // anything into this canvas before changing the shmem state, it will be
--- a/dom/interfaces/apps/mozIApplicationClearPrivateDataParams.idl
+++ b/dom/interfaces/apps/mozIApplicationClearPrivateDataParams.idl
@@ -11,10 +11,10 @@
 interface mozIApplicationClearPrivateDataParams : nsISupports
 {
   readonly attribute unsigned long appId;
   readonly attribute boolean browserOnly;
 };
 
 %{C++
 #define TOPIC_WEB_APP_CLEAR_DATA "webapps-clear-data"
-#define TOPIC_CLEAR_COOKIEJAR_DATA "clear-cookiejar-data"
+#define TOPIC_CLEAR_ORIGIN_DATA "clear-origin-data"
 %}
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4603,17 +4603,17 @@ ContentParent::RecvRecordingDeviceEvents
     return true;
 }
 
 bool
 ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                             int32_t* aStatus,
                                             bool* aSuccess)
 {
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     if (!gfxInfo) {
         *aSuccess = false;
         return true;
     }
 
     *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aStatus));
     return true;
 }
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -1090,17 +1090,17 @@ ParticularProcessPriorityManager::SetPri
   if (oldPriority != mPriority) {
     ProcessPriorityManagerImpl::GetSingleton()->
       NotifyProcessPriorityChanged(this, oldPriority);
 
     unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
   }
 
   if (aPriority < PROCESS_PRIORITY_FOREGROUND) {
-    unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("low-memory"));
+    unused << mContentParent->SendFlushMemory(NS_LITERAL_STRING("lowering-priority"));
   }
 
   FireTestOnlyObserverNotification("process-priority-set",
     ProcessPriorityToString(mPriority));
 }
 
 void
 ParticularProcessPriorityManager::Freeze()
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -37,48 +37,16 @@ public:
 
 private:
   virtual ~GumResolver() {}
   nsRefPtr<Promise> mPromise;
 };
 
 class MediaDevices::EnumDevResolver : public nsIGetUserMediaDevicesSuccessCallback
 {
-  static bool HasAPersistentPermission(uint64_t aWindowId)
-  {
-    nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
-        (nsGlobalWindow::GetInnerWindowWithId(aWindowId));
-    if (NS_WARN_IF(!window)) {
-      return false;
-    }
-    // Check if this site has persistent permissions.
-    nsresult rv;
-    nsCOMPtr<nsIPermissionManager> mgr =
-      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return false; // no permission manager no permissions!
-    }
-
-    uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
-    uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
-    {
-      auto* principal = window->GetExtantDoc()->NodePrincipal();
-      rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-      rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-    }
-    return audio == nsIPermissionManager::ALLOW_ACTION ||
-           video == nsIPermissionManager::ALLOW_ACTION;
-  }
-
 public:
   NS_DECL_ISUPPORTS
 
   EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
   : mPromise(aPromise), mWindowId(aWindowId) {}
 
   NS_IMETHOD
   OnSuccess(nsIVariant* aDevices) override
@@ -119,18 +87,17 @@ public:
       if (isVideo || isAudio) {
         MediaDeviceKind kind = isVideo ?
             MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput;
         nsString id;
         nsString name;
         device->GetId(id);
         // Include name only if page currently has a gUM stream active or
         // persistent permissions (audio or video) have been granted
-        if (MediaManager::Get()->IsWindowActivelyCapturing(mWindowId) ||
-            HasAPersistentPermission(mWindowId) ||
+        if (MediaManager::Get()->IsActivelyCapturingOrHasAPermission(mWindowId) ||
             Preferences::GetBool("media.navigator.permission.disabled", false)) {
           device->GetName(name);
         }
         nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
         infos.AppendElement(info);
       }
     }
     mPromise->MaybeResolve(infos);
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -277,16 +277,17 @@ private:
       mQueuedSamples.Clear();
       mOutputRequested = false;
       mInputExhausted = false;
       mDrainComplete = false;
       mTimeThreshold.reset();
       mOutput.Clear();
       mNumSamplesInput = 0;
       mNumSamplesOutput = 0;
+      mSizeOfQueue = 0;
     }
 
     // Used by the MDSM for logging purposes.
     Atomic<size_t> mSizeOfQueue;
     // Sample format monitoring.
     uint32_t mLastStreamSourceID;
     media::TimeIntervals mTimeRanges;
     nsRefPtr<SharedTrackInfo> mInfo;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -679,23 +679,25 @@ MediaOperationTask::ReturnCallbackError(
 class GetUserMediaStreamRunnable : public nsRunnable
 {
 public:
   GetUserMediaStreamRunnable(
     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
     nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
     uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener* aListener,
+    const nsCString& aOrigin,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
     PeerIdentity* aPeerIdentity)
     : mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mWindowID(aWindowID)
     , mListener(aListener)
+    , mOrigin(aOrigin)
     , mPeerIdentity(aPeerIdentity)
     , mManager(MediaManager::GetInstance())
   {
     mOnSuccess.swap(aOnSuccess);
     mOnFailure.swap(aOnFailure);
   }
 
   ~GetUserMediaStreamRunnable() {}
@@ -850,29 +852,35 @@ public:
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationTask
     // to ensure it's kept alive until the MediaOperationTask runs (at least).
     MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(MEDIA_START, mListener, trackunion,
                              tracksAvailableCallback,
                              mAudioSource, mVideoSource, false, mWindowID,
                              mOnFailure.forget()));
-
     // We won't need mOnFailure now.
     mOnFailure = nullptr;
+
+    if (!MediaManager::IsPrivateBrowsing(window)) {
+      // Call GetOriginKey again, this time w/persist = true, to promote
+      // deviceIds to persistent, in case they're not already. Fire'n'forget.
+      nsRefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, false, true);
+    }
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   nsRefPtr<MediaEngineSource> mAudioSource;
   nsRefPtr<MediaEngineSource> mVideoSource;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
+  nsCString mOrigin;
   nsAutoPtr<PeerIdentity> mPeerIdentity;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 static bool
 IsOn(const OwningBooleanOrMediaTrackConstraints &aUnion) {
   return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
 }
@@ -1035,23 +1043,25 @@ class GetUserMediaTask : public Task
 {
 public:
   GetUserMediaTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aOnSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs,
+    const nsCString& aOrigin,
     MediaManager::SourceSet* aSourceSet)
     : mConstraints(aConstraints)
     , mOnSuccess(aOnSuccess)
     , mOnFailure(aOnFailure)
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
+    , mOrigin(aOrigin)
     , mDeviceChosen(false)
     , mSourceSet(aSourceSet)
     , mManager(MediaManager::GetInstance())
   {}
 
   ~GetUserMediaTask() {
   }
 
@@ -1107,17 +1117,17 @@ public:
       }
     }
     PeerIdentity* peerIdentity = nullptr;
     if (!mConstraints.mPeerIdentity.IsEmpty()) {
       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
     }
 
     NS_DispatchToMainThread(do_AddRef(new GetUserMediaStreamRunnable(
-      mOnSuccess, mOnFailure, mWindowID, mListener,
+      mOnSuccess, mOnFailure, mWindowID, mListener, mOrigin,
       (mAudioDevice? mAudioDevice->GetSource() : nullptr),
       (mVideoDevice? mVideoDevice->GetSource() : nullptr),
       peerIdentity
     )));
 
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
   }
@@ -1186,16 +1196,17 @@ private:
 
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<AudioDevice> mAudioDevice;
   nsRefPtr<VideoDevice> mVideoDevice;
   MediaEnginePrefs mPrefs;
+  nsCString mOrigin;
 
   bool mDeviceChosen;
 public:
   nsAutoPtr<MediaManager::SourceSet> mSourceSet;
 private:
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
@@ -1580,21 +1591,28 @@ MediaManager::GetUserMedia(nsPIDOMWindow
                              NS_LITERAL_STRING("In shutdown"));
     onFailure->OnError(error);
     return NS_OK;
   }
 
   // Determine permissions early (while we still have a stack).
 
   nsIURI* docURI = aWindow->GetDocumentURI();
+  if (!docURI) {
+    return NS_ERROR_UNEXPECTED;
+  }
   bool loop = IsLoop(docURI);
   bool privileged = loop || IsPrivileged();
   bool isHTTPS = false;
-  if (docURI) {
-    docURI->SchemeIs("https", &isHTTPS);
+  docURI->SchemeIs("https", &isHTTPS);
+
+  nsCString origin;
+  nsresult rv = nsPrincipal::GetOriginForURI(docURI, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
     c.mVideo.SetAsBoolean() = false;
   }
 
   MediaSourceEnum videoType = dom::MediaSourceEnum::Camera;
 
@@ -1691,17 +1709,16 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
     new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID);
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
   if (!privileged) {
     // Check if this site has had persistent permissions denied.
-    nsresult rv;
     nsCOMPtr<nsIPermissionManager> permManager =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
     if (IsOn(c.mAudio)) {
       rv = permManager->TestExactPermissionFromPrincipal(
         aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm);
@@ -1731,31 +1748,31 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   }
 #endif
 
   // Get list of all devices, with origin-specific device ids.
 
   MediaEnginePrefs prefs = mPrefs;
 
   nsString callID;
-  nsresult rv = GenerateUUID(callID);
+  rv = GenerateUUID(callID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool fake = c.mFake.WasPassed()? c.mFake.Value() :
       Preferences::GetBool("media.navigator.streams.fake");
 
   bool fakeTracks = c.mFakeTracks.WasPassed()? c.mFakeTracks.Value() : false;
 
   bool askPermission = !privileged &&
       (!fake || Preferences::GetBool("media.navigator.permission.fake"));
 
   nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
                                                      fake, fakeTracks);
-  p->Then([this, onSuccess, onFailure, windowID, c, listener,
-           askPermission, prefs, isHTTPS, callID](SourceSet*& aDevices) mutable {
+  p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
+           prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
     ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
 
     // Ensure this pointer is still valid, and window is still alive.
     nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
     nsRefPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
         (nsGlobalWindow::GetInnerWindowWithId(windowID));
     if (!mgr || !window) {
       return;
@@ -1783,17 +1800,17 @@ MediaManager::GetUserMedia(nsPIDOMWindow
         }
       }
     }
 
     // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
     nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
                                                            onFailure.forget(),
                                                            windowID, listener,
-                                                           prefs,
+                                                           prefs, origin,
                                                            devices.forget()));
     // Store the task w/callbacks.
     mActiveCallbacks.Put(callID, task.forget());
 
     // Add a WindowID cross-reference so OnNavigation can tear things down
     nsTArray<nsString>* array;
     if (!mCallIds.Get(windowID, &array)) {
       array = new nsTArray<nsString>();
@@ -1919,22 +1936,25 @@ MediaManager::EnumerateDevicesImpl(uint6
   // 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);
   nsCString origin;
   nsPrincipal::GetOriginForURI(window->GetDocumentURI(), 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.
 
-  nsRefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing);
+  nsRefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing,
+                                                      persist);
   p->Then([id, aWindowId, aVideoType,
            aFake, aFakeTracks](const nsCString& aOriginKey) mutable {
     MOZ_ASSERT(NS_IsMainThread());
     nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
 
     nsRefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aVideoType,
                                                            aFake, aFakeTracks);
     p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
@@ -2626,31 +2646,63 @@ MediaManager::StopMediaStreams()
     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
     if (win) {
       OnNavigation(win->WindowID());
     }
   }
 }
 
 bool
-MediaManager::IsWindowActivelyCapturing(uint64_t aWindowId)
+MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
 {
+  // Does page currently have a gUM stream active?
+
   nsCOMPtr<nsISupportsArray> array;
   GetActiveMediaCaptureWindows(getter_AddRefs(array));
   uint32_t len;
   array->Count(&len);
   for (uint32_t i = 0; i < len; i++) {
     nsCOMPtr<nsISupports> window;
     array->GetElementAt(i, getter_AddRefs(window));
     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
     if (win && win->WindowID() == aWindowId) {
       return true;
     }
   }
-  return false;
+
+  // Or are persistent permissions (audio or video) granted?
+
+  nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
+      (nsGlobalWindow::GetInnerWindowWithId(aWindowId));
+  if (NS_WARN_IF(!window)) {
+    return false;
+  }
+  // Check if this site has persistent permissions.
+  nsresult rv;
+  nsCOMPtr<nsIPermissionManager> mgr =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false; // no permission manager no permissions!
+  }
+
+  uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
+  uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
+  {
+    auto* principal = window->GetExtantDoc()->NodePrincipal();
+    rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+    rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+  }
+  return audio == nsIPermissionManager::ALLOW_ACTION ||
+         video == nsIPermissionManager::ALLOW_ACTION;
 }
 
 void
 GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
               uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
               int32_t aPlayoutDelay)
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -574,27 +574,27 @@ public:
                                uint64_t aInnerWindowID = 0);
 
   nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
                             nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
                             nsIDOMGetUserMediaErrorCallback* aOnFailure);
 
   nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
   void OnNavigation(uint64_t aWindowID);
-  bool IsWindowActivelyCapturing(uint64_t aWindowId);
+  bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
+  static bool IsPrivateBrowsing(nsPIDOMWindow *window);
 private:
   typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
 
   static bool IsPrivileged();
   static bool IsLoop(nsIURI* aDocURI);
-  static bool IsPrivateBrowsing(nsPIDOMWindow *window);
   static nsresult GenerateUUID(nsAString& aResult);
   static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
 public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
   static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
   static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
 private:
   already_AddRefed<PledgeSourceSet>
   EnumerateRawDevices(uint64_t aWindowId, dom::MediaSourceEnum aSrcType,
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -45,30 +45,24 @@ PRLogModuleInfo* gMediaResourceLog;
 static const uint32_t HTTP_OK_CODE = 200;
 static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
 
 namespace mozilla {
 
 void
 MediaResource::Destroy()
 {
-  // If we're being destroyed on a non-main thread, we AddRef again and
-  // use a proxy to release the MediaResource on the main thread, where
-  // the MediaResource is deleted. This ensures we only delete the
-  // MediaResource on the main thread.
-  if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    NS_ENSURE_TRUE_VOID(mainThread);
-    nsRefPtr<MediaResource> doomed(this);
-    if (NS_FAILED(NS_ProxyRelease(mainThread, doomed, true))) {
-      NS_WARNING("Failed to proxy release to main thread!");
-    }
-  } else {
+  // Ensures we only delete the MediaResource on the main thread.
+  if (NS_IsMainThread()) {
     delete this;
+    return;
   }
+  nsCOMPtr<nsIRunnable> destroyRunnable =
+    NS_NewNonOwningRunnableMethod(this, &MediaResource::Destroy);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(destroyRunnable)));
 }
 
 NS_IMPL_ADDREF(MediaResource)
 NS_IMPL_RELEASE_WITH_DESTROY(MediaResource, Destroy())
 NS_IMPL_QUERY_INTERFACE0(MediaResource)
 
 ChannelMediaResource::ChannelMediaResource(MediaDecoder* aDecoder,
                                            nsIChannel* aChannel,
--- a/dom/media/android/AndroidMediaPluginHost.cpp
+++ b/dom/media/android/AndroidMediaPluginHost.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "mozilla/Preferences.h"
 #include "MediaResource.h"
 #include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/Services.h"
 #include "AndroidMediaPluginHost.h"
 #include "nsXPCOMStrings.h"
 #include "nsISeekableStream.h"
 #include "AndroidMediaReader.h"
 #include "nsIGfxInfo.h"
 #include "gfxCrashReporterUtils.h"
 #include "prmem.h"
 #include "prlink.h"
@@ -106,17 +107,17 @@ static bool IsOmxSupported()
   if (disabled) {
     NS_WARNING("XXX stagefright disabled\n");
     return false;
   }
 
   ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled);
 
   if (!forceEnabled) {
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     if (gfxInfo) {
       int32_t status;
       if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) {
         if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
           NS_WARNING("XXX stagefright blacklisted\n");
           return false;
         }
       }
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -129,17 +129,17 @@ public:
 
 #if defined(DEBUG)
   void Dump(const char* aPath) {
     mInputBuffer.Dump(aPath);
   }
 #endif
 
 private:
-  ~SourceBufferResource();
+  virtual ~SourceBufferResource();
   nsresult SeekInternal(int64_t aOffset);
   nsresult ReadInternal(char* aBuffer, uint32_t aCount, uint32_t* aBytes, bool aMayBlock);
   nsresult ReadAtInternal(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes, bool aMayBlock);
 
   const nsCString mType;
 
   // Provides synchronization between SourceBuffers and InputAdapters.
   // Protects all of the member variables below.  Read() will await a
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -104,16 +104,17 @@ TrackBuffersManager::TrackBuffersManager
     NS_NewRunnableFunction([self] () {
       self->mMediaSourceDuration.Connect(self->mParentDecoder->CanonicalExplicitDuration());
     });
   GetTaskQueue()->Dispatch(task.forget());
 }
 
 TrackBuffersManager::~TrackBuffersManager()
 {
+  ShutdownDemuxers();
 }
 
 bool
 TrackBuffersManager::AppendData(MediaByteBuffer* aData,
                                 TimeUnit aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("Appending %lld bytes", aData->Length());
@@ -728,27 +729,34 @@ void
 TrackBuffersManager::ScheduleSegmentParserLoop()
 {
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethod(this, &TrackBuffersManager::SegmentParserLoop);
   GetTaskQueue()->Dispatch(task.forget());
 }
 
 void
-TrackBuffersManager::CreateDemuxerforMIMEType()
+TrackBuffersManager::ShutdownDemuxers()
 {
   if (mVideoTracks.mDemuxer) {
     mVideoTracks.mDemuxer->BreakCycles();
     mVideoTracks.mDemuxer = nullptr;
   }
   if (mAudioTracks.mDemuxer) {
     mAudioTracks.mDemuxer->BreakCycles();
     mAudioTracks.mDemuxer = nullptr;
   }
   mInputDemuxer = nullptr;
+}
+
+void
+TrackBuffersManager::CreateDemuxerforMIMEType()
+{
+  ShutdownDemuxers();
+
   if (mType.LowerCaseEqualsLiteral("video/webm") || mType.LowerCaseEqualsLiteral("audio/webm")) {
     NS_WARNING("Waiting on WebMDemuxer");
   // mInputDemuxer = new WebMDemuxer(mCurrentInputBuffer);
     return;
   }
 
 #ifdef MOZ_FMP4
   if (mType.LowerCaseEqualsLiteral("video/mp4") || mType.LowerCaseEqualsLiteral("audio/mp4")) {
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -97,16 +97,17 @@ public:
 private:
   virtual ~TrackBuffersManager();
   // All following functions run on the taskqueue.
   nsRefPtr<AppendPromise> InitSegmentParserLoop();
   void ScheduleSegmentParserLoop();
   void SegmentParserLoop();
   void AppendIncomingBuffers();
   void InitializationSegmentReceived();
+  void ShutdownDemuxers();
   void CreateDemuxerforMIMEType();
   void NeedMoreData();
   void RejectAppend(nsresult aRejectValue, const char* aName);
   // Will return a promise that will be resolved once all frames of the current
   // media segment have been processed.
   nsRefPtr<CodedFrameProcessingPromise> CodedFrameProcessing();
   void CompleteCodedFrameProcessing();
   // Called by ResetParserState. Complete parsing the input buffer for the
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMF.h"
 #include "WMFDecoderModule.h"
 #include "WMFVideoMFTManager.h"
 #include "WMFAudioMFTManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Services.h"
 #include "WMFMediaDataDecoder.h"
 #include "nsIWindowsRegKey.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIGfxInfo.h"
 #include "GfxDriverInfo.h"
 #include "gfxWindowsPlatform.h"
 #include "MediaInfo.h"
@@ -91,17 +92,17 @@ WMFDecoderModule::CreateAudioDecoder(con
 }
 
 bool
 WMFDecoderModule::ShouldUseDXVA(const VideoInfo& aConfig) const
 {
   static bool isAMD = false;
   static bool initialized = false;
   if (!initialized) {
-    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     nsAutoString vendor;
     gfxInfo->GetAdapterVendorID(vendor);
     isAMD = vendor.Equals(widget::GfxDriverInfo::GetDeviceVendor(widget::VendorAMD), nsCaseInsensitiveStringComparator()) ||
             vendor.Equals(widget::GfxDriverInfo::GetDeviceVendor(widget::VendorATI), nsCaseInsensitiveStringComparator());
     initialized = true;
   }
   if (!isAMD) {
     return true;
--- a/dom/media/systemservices/MediaChild.cpp
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -15,28 +15,29 @@
 #undef LOG
 PRLogModuleInfo *gMediaChildLog;
 #define LOG(args) MOZ_LOG(gMediaChildLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace media {
 
 already_AddRefed<Pledge<nsCString>>
-GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist)
 {
   nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
   MOZ_ASSERT(mgr);
 
   nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
   uint32_t id = mgr->mGetOriginKeyPledges.Append(*p);
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing);
+    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing,
+                                              aPersist);
   } else {
-    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing);
+    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing, aPersist);
   }
   return p.forget();
 }
 
 void
 SanitizeOriginKeys(const uint64_t& aSinceWhen)
 {
   LOG(("SanitizeOriginKeys since %llu", 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);
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist);
 
 void
 SanitizeOriginKeys(const uint64_t& aSinceWhen);
 
 class Child : public PMediaChild
 {
 public:
   static Child* Get();
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -43,43 +43,47 @@ class OriginKeyStore : public nsISupport
 {
   NS_DECL_THREADSAFE_ISUPPORTS
   class OriginKey
   {
   public:
     static const size_t DecodedLength = 18;
     static const size_t EncodedLength = DecodedLength * 4 / 3;
 
-    OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
+    explicit OriginKey(const nsACString& aKey, int64_t aSecondsStamp = 0) // 0 = temporal
     : mKey(aKey)
     , mSecondsStamp(aSecondsStamp) {}
 
     nsCString mKey; // Base64 encoded.
     int64_t mSecondsStamp;
   };
 
   class OriginKeysTable
   {
   public:
-    OriginKeysTable() {}
+    OriginKeysTable() : mPersistCount(0) {}
 
     nsresult
-    GetOriginKey(const nsACString& aOrigin, nsCString& result)
+    GetOriginKey(const nsACString& aOrigin, nsCString& aResult, bool aPersist = false)
     {
       OriginKey* key;
       if (!mKeys.Get(aOrigin, &key)) {
         nsCString salt; // Make a new one
         nsresult rv = GenerateRandomName(salt, key->EncodedLength);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
-        key = new OriginKey(salt, PR_Now() / PR_USEC_PER_SEC);
+        key = new OriginKey(salt);
         mKeys.Put(aOrigin, key);
       }
-      result = key->mKey;
+      if (aPersist && !key->mSecondsStamp) {
+        key->mSecondsStamp = PR_Now() / PR_USEC_PER_SEC;
+        mPersistCount++;
+      }
+      aResult = key->mKey;
       return NS_OK;
     }
 
     static PLDHashOperator
     HashCleaner(const nsACString& aOrigin, nsAutoPtr<OriginKey>& aOriginKey,
                 void *aUserArg)
     {
       OriginKey* since = static_cast<OriginKey*>(aUserArg);
@@ -93,33 +97,35 @@ class OriginKeyStore : public nsISupport
           PL_DHASH_REMOVE : PL_DHASH_NEXT;
     }
 
     void Clear(int64_t aSinceWhen)
     {
       // Avoid int64_t* <-> void* casting offset
       OriginKey since(nsCString(), aSinceWhen  / PR_USEC_PER_SEC);
       mKeys.Enumerate(HashCleaner, &since);
+      mPersistCount = 0;
     }
 
   protected:
     nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
+    size_t mPersistCount;
   };
 
   class OriginKeysLoader : public OriginKeysTable
   {
   public:
     OriginKeysLoader() {}
 
     nsresult
-    GetOriginKey(const nsACString& aOrigin, nsCString& result)
+    GetOriginKey(const nsACString& aOrigin, nsCString& aResult, bool aPersist)
     {
-      auto before = mKeys.Count();
-      OriginKeysTable::GetOriginKey(aOrigin, result);
-      if (mKeys.Count() != before) {
+      auto before = mPersistCount;
+      OriginKeysTable::GetOriginKey(aOrigin, aResult, aPersist);
+      if (mPersistCount != before) {
         Save();
       }
       return NS_OK;
     }
 
     already_AddRefed<nsIFile>
     GetFile()
     {
@@ -157,16 +163,17 @@ class OriginKeyStore : public nsISupport
 
       nsCOMPtr<nsIInputStream> stream;
       rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
       MOZ_ASSERT(i);
+      MOZ_ASSERT(!mPersistCount);
 
       nsCString line;
       bool hasMoreLines;
       rv = i->ReadLine(line, &hasMoreLines);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
@@ -203,24 +210,29 @@ class OriginKeyStore : public nsISupport
         }
         nsCString dummy;
         rv = Base64Decode(key, dummy);
         if (NS_FAILED(rv)) {
           continue;
         }
         mKeys.Put(origin, new OriginKey(key, secondsstamp));
       }
+      mPersistCount = mKeys.Count();
       return NS_OK;
     }
 
     static PLDHashOperator
     HashWriter(const nsACString& aOrigin, OriginKey* aOriginKey, void *aUserArg)
     {
       auto* stream = static_cast<nsIOutputStream *>(aUserArg);
 
+      if (!aOriginKey->mSecondsStamp) {
+        return PL_DHASH_NEXT; // don't write temporal ones
+      }
+
       nsCString buffer;
       buffer.Append(aOriginKey->mKey);
       buffer.Append(' ');
       buffer.AppendInt(aOriginKey->mSecondsStamp);
       buffer.Append(' ');
       buffer.Append(aOrigin);
       buffer.Append('\n');
 
@@ -370,18 +382,19 @@ Parent<NonE10s>* Parent<NonE10s>::GetSin
 // error: 'this' was not captured for this lambda function
 
 template<class Super> static
 Parent<Super>* GccGetSingleton() { return Parent<Super>::GetSingleton(); };
 
 
 template<class Super> bool
 Parent<Super>::RecvGetOriginKey(const uint32_t& aRequestId,
-                         const nsCString& aOrigin,
-                         const bool& aPrivateBrowsing)
+                                const nsCString& aOrigin,
+                                const bool& aPrivateBrowsing,
+                                const bool& aPersist)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // First, get profile dir.
 
   MOZ_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIFile> profileDir;
   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
@@ -396,25 +409,25 @@ Parent<Super>::RecvGetOriginKey(const ui
   nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
   uint32_t id = mOutstandingPledges.Append(*p);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(sts);
   nsRefPtr<OriginKeyStore> store(mOriginKeyStore);
   bool sameProcess = mSameProcess;
 
-  rv = sts->Dispatch(NewRunnableFrom([id, profileDir, store, sameProcess,
-                                      aOrigin, aPrivateBrowsing]() -> nsresult {
+  rv = sts->Dispatch(NewRunnableFrom([id, profileDir, store, sameProcess, aOrigin,
+                                      aPrivateBrowsing, aPersist]() -> nsresult {
     MOZ_ASSERT(!NS_IsMainThread());
     store->mOriginKeys.SetProfileDir(profileDir);
     nsCString result;
     if (aPrivateBrowsing) {
       store->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
     } else {
-      store->mOriginKeys.GetOriginKey(aOrigin, result);
+      store->mOriginKeys.GetOriginKey(aOrigin, result, aPersist);
     }
 
     // Pass result back to main thread.
     nsresult rv;
     rv = NS_DispatchToMainThread(NewRunnableFrom([id, store, sameProcess,
                                                   result]() -> nsresult {
       Parent* parent = GccGetSingleton<Super>(); // GetSingleton();
       if (!parent) {
--- a/dom/media/systemservices/MediaParent.h
+++ b/dom/media/systemservices/MediaParent.h
@@ -22,17 +22,18 @@ class OriginKeyStore;
 
 class NonE10s
 {
   typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason
       ActorDestroyReason;
 protected:
   virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
                                 const nsCString& aOrigin,
-                                const bool& aPrivateBrowsing) = 0;
+                                const bool& aPrivateBrowsing,
+                                const bool& aPersist) = 0;
   virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) = 0;
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) = 0;
 };
 
 // Super = PMediaParent or NonE10s
 
 template<class Super>
@@ -40,17 +41,18 @@ class Parent : public Super
 {
   typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason
       ActorDestroyReason;
 public:
   static Parent* GetSingleton();
 
   virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
                                 const nsCString& aOrigin,
-                                const bool& aPrivateBrowsing) override;
+                                const bool& aPrivateBrowsing,
+                                const bool& aPersist) override;
   virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   explicit Parent(bool aSameProcess = false);
   virtual ~Parent();
 private:
 
   nsRefPtr<OriginKeyStore> mOriginKeyStore;
--- a/dom/media/systemservices/PMedia.ipdl
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -8,22 +8,33 @@ namespace mozilla {
 namespace media {
 
 protocol PMedia
 {
   manager PContent;
 
 parent:
   /**
-   * Requests a persistent unique secret key for each origin.
+   * 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.
+   * 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.
    */
-  GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing);
+  GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing,
+               bool aPersist);
 
   /**
    * On clear cookies. Fire and forget.
    */
   SanitizeOriginKeys(uint64_t aSinceWhen);
 
 child:
   GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -485,36 +485,38 @@ function IsWindows8OrLater() {
 }
 
 // These are files that are non seekable, due to problems with the media,
 // for example broken or missing indexes.
 var gUnseekableTests = [
   { name:"no-cues.webm", type:"video/webm" },
   { name:"bogus.duh", type:"bogus/duh"}
 ];
-// Android supports fragmented MP4 playback from 4.3.
-// Fragmented MP4.
+
+var androidVersion = -1; // non-Android platforms
 if (manifestNavigator().userAgent.indexOf("Mobile") != -1) {
   // See nsSystemInfo.cpp, the getProperty('version') returns different value
   // on each platforms, so we need to distinguish the android and B2G platform.
-  var androidVersion;
-  if (navigator.userAgent.indexOf("Android") != -1) {
-    androidSDKVer = SpecialPowers.Cc['@mozilla.org/system-info;1']
-                                 .getService(SpecialPowers.Ci.nsIPropertyBag2)
-                                 .getProperty('version');
-  } else if (navigator.userAgent.indexOf("Android") == -1) {
-    androidSDKVer = SpecialPowers.Cc['@mozilla.org/system-info;1']
-                                 .getService(SpecialPowers.Ci.nsIPropertyBag2)
-                                 .getProperty('sdk_version');
-  }
-  if (androidVersion >= 18) {
-    gUnseekableTests = gUnseekableTests.concat([
-      { name:"street.mp4", type:"video/mp4" }
-    ]);
-  }
+  var versionString = manifestNavigator().userAgent.indexOf("Android") != -1 ?
+                      'version' : 'sdk_version';
+  androidVersion = SpecialPowers.Cc['@mozilla.org/system-info;1']
+                                .getService(SpecialPowers.Ci.nsIPropertyBag2)
+                                .getProperty(versionString);
+}
+
+function getAndroidVersion() {
+  return androidVersion;
+}
+
+//Android supports fragmented MP4 playback from 4.3.
+//Fragmented MP4.
+if (getAndroidVersion() >= 18) {
+  gUnseekableTests = gUnseekableTests.concat([
+    { name:"street.mp4", type:"video/mp4" }
+  ]);
 }
 
 // These are files suitable for using with a "new Audio" constructor.
 var gAudioTests = [
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"sound.ogg", type:"audio/ogg" },
   { name:"owl.mp3", type:"audio/mpeg", duration:3.29 },
   { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
@@ -1542,13 +1544,12 @@ function setMediaTestsPrefs(callback, ex
   if (extraPrefs) {
     prefs = prefs.concat(extraPrefs);
   }
   SpecialPowers.pushPrefEnv({"set": prefs}, callback);
 }
 
 // B2G emulator and Android 2.3 are condidered slow platforms
 function isSlowPlatform() {
-  return SpecialPowers.Services.appinfo.name == "B2G" ||
-         navigator.userAgent.indexOf("Mobile") != -1 && androidVersion == 10;
+  return SpecialPowers.Services.appinfo.name == "B2G" || getAndroidVersion() == 10;
 }
 
 SimpleTest.requestFlakyTimeout("untriaged");
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -152,31 +152,37 @@ skip-if = toolkit == 'gonk' || buildapp 
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_answererAddSecondAudioStream.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_removeAudioTrack.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_removeThenAddAudioTrack.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_addSecondVideoStream.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_removeVideoTrack.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_removeThenAddVideoTrack.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_replaceVideoThenRenegotiate.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_addSecondAudioStreamNoBundle.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_removeThenAddAudioTrackNoBundle.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
 [test_peerConnection_addSecondVideoStreamNoBundle.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_removeThenAddVideoTrackNoBundle.html]
-skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
+# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s
+skip-if = toolkit == 'gonk' || (os == 'linux' && debug && e10s)
 [test_peerConnection_addDataChannel.html]
 skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
 [test_peerConnection_addDataChannelNoBundle.html]
 skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly
 [test_peerConnection_webAudio.html]
 tags = webaudio webrtc
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_localRollback.html]
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -86,20 +86,26 @@ PushSubscription.prototype = {
     let json = aMessage.data;
     let resolver = this.takePromiseResolver(json.requestID);
     if (resolver == null) {
       return;
     }
 
     switch (aMessage.name) {
       case "PushService:Unregister:OK":
-        resolver.resolve(true);
+        if (typeof json.result !== "boolean") {
+          debug("Expected boolean result from PushService!");
+          resolve.reject("NetworkError");
+          return;
+        }
+
+        resolver.resolve(json.result);
         break;
       case "PushService:Unregister:KO":
-        resolver.resolve(false);
+        resolver.reject("NetworkError");
         break;
       default:
         debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
     }
   },
 
 };
 
@@ -284,31 +290,38 @@ Push.prototype = {
         function() {
           reject("PermissionDeniedError");
         }
       );
     }.bind(this));
     return p;
   },
 
-  hasPermission: function() {
-    debug("hasPermission()" + this._scope);
+  permissionState: function() {
+    debug("permissionState()" + this._scope);
+
+    let p = this.createPromise((resolve, reject) => {
+      let permission = Ci.nsIPermissionManager.DENY_ACTION;
 
-    let p = this.createPromise(function(resolve, reject) {
-      let permissionManager = Cc["@mozilla.org/permissionmanager;1"]
-                              .getService(Ci.nsIPermissionManager);
-      let permission =
-        permissionManager.testExactPermissionFromPrincipal(this._principal,
-                                                           "push");
+      try {
+        let permissionManager = Cc["@mozilla.org/permissionmanager;1"]
+                                .getService(Ci.nsIPermissionManager);
+        permission =
+          permissionManager.testExactPermissionFromPrincipal(this._principal,
+                                                             "push");
+      } catch(e) {
+        reject();
+        return;
+      }
 
-      let pushPermissionStatus = "default";
+      let pushPermissionStatus = "prompt";
       if (permission == Ci.nsIPermissionManager.ALLOW_ACTION) {
         pushPermissionStatus = "granted";
       } else if (permission == Ci.nsIPermissionManager.DENY_ACTION) {
         pushPermissionStatus = "denied";
       }
       resolve(pushPermissionStatus);
-    }.bind(this));
+    });
     return p;
   },
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push, PushSubscription]);
--- a/dom/push/PushDB.jsm
+++ b/dom/push/PushDB.jsm
@@ -18,53 +18,103 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.importGlobalProperties(["indexedDB"]);
 
 const prefs = new Preferences("dom.push.");
 
 this.EXPORTED_SYMBOLS = ["PushDB"];
 
-this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, schemaFunction) {
+this.PushDB = function PushDB(dbName, dbVersion, dbStoreName, keyPath, model) {
   debug("PushDB()");
   this._dbStoreName = dbStoreName;
-  this._schemaFunction = schemaFunction;
+  this._keyPath = keyPath;
+  this._model = model;
 
   // set the indexeddb database
   this.initDBHelper(dbName, dbVersion,
                     [dbStoreName]);
   gDebuggingEnabled = prefs.get("debug");
   prefs.observe("debug", this);
 };
 
 this.PushDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
+  toPushRecord: function(record) {
+    if (!record) {
+      return;
+    }
+    return new this._model(record);
+  },
+
+  isValidRecord: function(record) {
+    return record && typeof record.scope == "string" &&
+           typeof record.originAttributes == "string" &&
+           record.quota >= 0 &&
+           typeof record[this._keyPath] == "string";
+  },
+
   upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
-    if (this._schemaFunction) {
-      this._schemaFunction(aTransaction, aDb, aOldVersion, aNewVersion, this);
+    if (aOldVersion <= 3) {
+      //XXXnsm We haven't shipped Push during this upgrade, so I'm just going to throw old
+      //registrations away without even informing the app.
+      if (aDb.objectStoreNames.contains(this._dbStoreName)) {
+        aDb.deleteObjectStore(this._dbStoreName);
+      }
+
+      let objectStore = aDb.createObjectStore(this._dbStoreName,
+                                              { keyPath: this._keyPath });
+
+      // index to fetch records based on endpoints. used by unregister
+      objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
+
+      // index to fetch records by identifiers.
+      // In the current security model, the originAttributes distinguish between
+      // different 'apps' on the same origin. Since ServiceWorkers are
+      // same-origin to the scope they are registered for, the attributes and
+      // scope are enough to reconstruct a valid principal.
+      objectStore.createIndex("identifiers", ["scope", "originAttributes"], { unique: true });
+      objectStore.createIndex("originAttributes", "originAttributes", { unique: false });
+    }
+
+    if (aOldVersion < 4) {
+      let objectStore = aTransaction.objectStore(this._dbStoreName);
+
+      // index to fetch active and expired registrations.
+      objectStore.createIndex("quota", "quota", { unique: false });
     }
   },
 
   /*
    * @param aRecord
    *        The record to be added.
    */
 
   put: function(aRecord) {
     debug("put()" + JSON.stringify(aRecord));
+    if (!this.isValidRecord(aRecord)) {
+      return Promise.reject(new TypeError(
+        "Scope, originAttributes, and quota are required! " +
+          JSON.stringify(aRecord)
+        )
+      );
+    }
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readwrite",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
-          aStore.put(aRecord).onsuccess = function setTxnResult(aEvent) {
+        (aTxn, aStore) => {
+          aTxn.result = undefined;
+
+          aStore.put(aRecord).onsuccess = aEvent => {
             debug("Request successful. Updated record ID: " +
                   aEvent.target.result);
+            aTxn.result = this.toPushRecord(aRecord);
           };
         },
         resolve,
         reject
       )
     );
   },
 
@@ -106,43 +156,43 @@ this.PushDB.prototype = {
 
   getByPushEndpoint: function(aPushEndpoint) {
     debug("getByPushEndpoint()");
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
+        (aTxn, aStore) => {
           aTxn.result = undefined;
 
           let index = aStore.index("pushEndpoint");
-          index.get(aPushEndpoint).onsuccess = function setTxnResult(aEvent) {
-            aTxn.result = aEvent.target.result;
+          index.get(aPushEndpoint).onsuccess = aEvent => {
+            aTxn.result = this.toPushRecord(aEvent.target.result);
             debug("Fetch successful " + aEvent.target.result);
           };
         },
         resolve,
         reject
       )
     );
   },
 
   getByKeyID: function(aKeyID) {
     debug("getByKeyID()");
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
+        (aTxn, aStore) => {
           aTxn.result = undefined;
 
-          aStore.get(aKeyID).onsuccess = function setTxnResult(aEvent) {
-            aTxn.result = aEvent.target.result;
+          aStore.get(aKeyID).onsuccess = aEvent => {
+            aTxn.result = this.toPushRecord(aEvent.target.result);
             debug("Fetch successful " + aEvent.target.result);
           };
         },
         resolve,
         reject
       )
     );
   },
@@ -156,47 +206,48 @@ this.PushDB.prototype = {
                new TypeError("Scope and originAttributes are required! " +
                              JSON.stringify(aPageRecord)));
     }
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
+        (aTxn, aStore) => {
           aTxn.result = undefined;
 
           let index = aStore.index("identifiers");
           let request = index.get(IDBKeyRange.only([aPageRecord.scope, aPageRecord.originAttributes]));
-          request.onsuccess = function setTxnResult(aEvent) {
-            aTxn.result = aEvent.target.result;
-          }
+          request.onsuccess = aEvent => {
+            aTxn.result = this.toPushRecord(aEvent.target.result);
+          };
         },
         resolve,
         reject
       )
     );
   },
 
   _getAllByKey: function(aKeyName, aKeyValue) {
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
+        (aTxn, aStore) => {
           aTxn.result = undefined;
 
           let index = aStore.index(aKeyName);
           // It seems ok to use getAll here, since unlike contacts or other
           // high storage APIs, we don't expect more than a handful of
           // registrations per domain, and usually only one.
           let getAllReq = index.mozGetAll(aKeyValue);
-          getAllReq.onsuccess = function setTxnResult(aEvent) {
-            aTxn.result = aEvent.target.result;
-          }
+          getAllReq.onsuccess = aEvent => {
+            aTxn.result = aEvent.target.result.map(
+              record => this.toPushRecord(record));
+          };
         },
         resolve,
         reject
       )
     );
   },
 
   // aOriginAttributes must be a string!
@@ -209,20 +260,98 @@ this.PushDB.prototype = {
 
   getAllKeyIDs: function() {
     debug("getAllKeyIDs()");
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
-        function txnCb(aTxn, aStore) {
+        (aTxn, aStore) => {
           aTxn.result = undefined;
-          aStore.mozGetAll().onsuccess = function(event) {
-            aTxn.result = event.target.result;
+          aStore.mozGetAll().onsuccess = event => {
+            aTxn.result = event.target.result.map(
+              record => this.toPushRecord(record));
+          };
+        },
+        resolve,
+        reject
+      )
+    );
+  },
+
+  _getAllByPushQuota: function(range) {
+    debug("getAllByPushQuota()");
+
+    return new Promise((resolve, reject) =>
+      this.newTxn(
+        "readonly",
+        this._dbStoreName,
+        (aTxn, aStore) => {
+          aTxn.result = [];
+
+          let index = aStore.index("quota");
+          index.openCursor(range).onsuccess = event => {
+            let cursor = event.target.result;
+            if (cursor) {
+              aTxn.result.push(this.toPushRecord(cursor.value));
+              cursor.continue();
+            }
+          };
+        },
+        resolve,
+        reject
+      )
+    );
+  },
+
+  getAllUnexpired: function() {
+    debug("getAllUnexpired()");
+    return this._getAllByPushQuota(IDBKeyRange.lowerBound(1));
+  },
+
+  getAllExpired: function() {
+    debug("getAllExpired()");
+    return this._getAllByPushQuota(IDBKeyRange.only(0));
+  },
+
+  /**
+   * Updates an existing push registration.
+   *
+   * @param {String} aKeyID The registration ID.
+   * @param {Function} aUpdateFunc A function that receives the existing
+   *  registration record as its argument, and returns a new record. If the
+   *  function returns `null` or `undefined`, the record will not be updated.
+   *  If the record does not exist, the function will not be called.
+   * @returns {Promise} A promise resolved with either the updated record, or
+   *  `undefined` if the record was not updated.
+   */
+  update: function(aKeyID, aUpdateFunc) {
+    return new Promise((resolve, reject) =>
+      this.newTxn(
+        "readwrite",
+        this._dbStoreName,
+        (aTxn, aStore) => {
+          aStore.get(aKeyID).onsuccess = aEvent => {
+            aTxn.result = undefined;
+
+            let record = aEvent.target.result;
+            if (!record) {
+              debug("update: Key ID " + aKeyID + " does not exist");
+              return;
+            }
+            let newRecord = aUpdateFunc(this.toPushRecord(record));
+            if (!this.isValidRecord(newRecord)) {
+              debug("update: Ignoring invalid update for key ID " + aKeyID);
+              return;
+            }
+            aStore.put(newRecord).onsuccess = aEvent => {
+              debug("update: Update successful for key ID " + aKeyID);
+              aTxn.result = newRecord;
+            };
           };
         },
         resolve,
         reject
       )
     );
   },
 
--- a/dom/push/PushNotificationService.js
+++ b/dom/push/PushNotificationService.js
@@ -34,17 +34,21 @@ PushNotificationService.prototype = {
   contractID: "@mozilla.org/push/NotificationService;1",
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIPushNotificationService]),
 
   register: function register(scope, originAttributes) {
-    return PushService._register({scope, originAttributes});
+    return PushService._register({
+      scope: scope,
+      originAttributes: originAttributes,
+      maxQuota: Infinity,
+    });
   },
 
   unregister: function unregister(scope, originAttributes) {
     return PushService._unregister({scope, originAttributes});
   },
 
   registration: function registration(scope, originAttributes) {
     return PushService._registration({scope, originAttributes});
new file mode 100644
--- /dev/null
+++ b/dom/push/PushRecord.jsm
@@ -0,0 +1,183 @@
+/* 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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/Preferences.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+this.EXPORTED_SYMBOLS = ["PushRecord"];
+
+const prefs = new Preferences("dom.push.");
+
+// History transition types that can fire an `pushsubscriptionchange` event
+// when the user visits a site with expired push registrations. Visits only
+// count if the user sees the origin in the address bar. This excludes embedded
+// resources, downloads, and framed links.
+const QUOTA_REFRESH_TRANSITIONS_SQL = [
+  Ci.nsINavHistoryService.TRANSITION_LINK,
+  Ci.nsINavHistoryService.TRANSITION_TYPED,
+  Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
+  Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
+  Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY
+].join(",");
+
+function PushRecord(props) {
+  this.pushEndpoint = props.pushEndpoint;
+  this.scope = props.scope;
+  this.origin = Services.io.newURI(this.scope, null, null).prePath;
+  this.originAttributes = props.originAttributes;
+  this.pushCount = props.pushCount || 0;
+  this.lastPush = props.lastPush || 0;
+  this.setQuota(props.quota);
+}
+
+PushRecord.prototype = {
+  setQuota(suggestedQuota) {
+    this.quota = (!isNaN(suggestedQuota) && suggestedQuota >= 0) ?
+                 suggestedQuota : prefs.get("maxQuotaPerSubscription");
+  },
+
+  updateQuota(lastVisit) {
+    if (this.isExpired() || !this.quotaApplies()) {
+      // Ignore updates if the registration is already expired, or isn't
+      // subject to quota.
+      return;
+    }
+    if (lastVisit < 0) {
+      // If the user cleared their history, but retained the push permission,
+      // mark the registration as expired.
+      this.quota = 0;
+      return;
+    }
+    let currentQuota;
+    if (lastVisit > this.lastPush) {
+      // If the user visited the site since the last time we received a
+      // notification, reset the quota.
+      let daysElapsed = (Date.now() - lastVisit) / 24 / 60 / 60 / 1000;
+      currentQuota = Math.min(
+        Math.round(8 * Math.pow(daysElapsed, -0.8)),
+        prefs.get("maxQuotaPerSubscription")
+      );
+    } else {
+      // The user hasn't visited the site since the last notification.
+      currentQuota = this.quota;
+    }
+    this.quota = Math.max(currentQuota - 1, 0);
+  },
+
+  receivedPush(lastVisit) {
+    this.updateQuota(lastVisit);
+    this.pushCount++;
+    this.lastPush = Date.now();
+  },
+
+  /**
+   * Queries the Places database for the last time a user visited the site
+   * associated with a push registration.
+   *
+   * @returns {Promise} A promise resolved with either the last time the user
+   *  visited the site, or `-Infinity` if the site is not in the user's history.
+   *  The time is expressed in milliseconds since Epoch.
+   */
+  getLastVisit() {
+    if (!this.quotaApplies() || this.isTabOpen()) {
+      // If the registration isn't subject to quota, or the user already
+      // has the site open, skip the Places query.
+      return Promise.resolve(Date.now());
+    }
+    return PlacesUtils.withConnectionWrapper("PushRecord.getLastVisit", db => {
+      // We're using a custom query instead of `nsINavHistoryQueryOptions`
+      // because the latter doesn't expose a way to filter by transition type:
+      // `setTransitions` performs a logical "and," but we want an "or." We
+      // also avoid an unneeded left join on `moz_favicons`, and an `ORDER BY`
+      // clause that emits a suboptimal index warning.
+      return db.executeCached(
+        `SELECT MAX(p.last_visit_date)
+         FROM moz_places p
+         INNER JOIN moz_historyvisits h ON p.id = h.place_id
+         WHERE (
+           p.url >= :urlLowerBound AND p.url <= :urlUpperBound AND
+           h.visit_type IN (${QUOTA_REFRESH_TRANSITIONS_SQL})
+         )
+        `,
+        {
+          // Restrict the query to all pages for this origin.
+          urlLowerBound: this.origin,
+          urlUpperBound: this.origin + "\x7f"
+        }
+      );
+    }).then(rows => {
+      if (!rows.length) {
+        return -Infinity;
+      }
+      // Places records times in microseconds.
+      let lastVisit = rows[0].getResultByIndex(0);
+      return lastVisit / 1000;
+    });
+  },
+
+  isTabOpen() {
+    let windows = Services.wm.getEnumerator("navigator:browser");
+    while (windows.hasMoreElements()) {
+      let window = windows.getNext();
+      if (window.closed || PrivateBrowsingUtils.isWindowPrivate(window)) {
+        continue;
+      }
+      // `gBrowser` on Desktop; `BrowserApp` on Fennec.
+      let tabs = window.gBrowser ? window.gBrowser.tabContainer.children :
+                 window.BrowserApp.tabs;
+      for (let tab of tabs) {
+        // `linkedBrowser` on Desktop; `browser` on Fennec.
+        let tabURI = (tab.linkedBrowser || tab.browser).currentURI;
+        if (tabURI.prePath == this.origin) {
+          return true;
+        }
+      }
+    }
+    return false;
+  },
+
+  quotaApplies() {
+    return Number.isFinite(this.quota);
+  },
+
+  isExpired() {
+    return this.quota === 0;
+  },
+
+  toRegistration() {
+    return {
+      pushEndpoint: this.pushEndpoint,
+      lastPush: this.lastPush,
+      pushCount: this.pushCount,
+    };
+  },
+
+  toRegister() {
+    return {
+      pushEndpoint: this.pushEndpoint,
+    };
+  },
+};
+
+// Mark the `origin` property as non-enumerable to avoid storing the
+// registration origin in IndexedDB.
+Object.defineProperty(PushRecord.prototype, "origin", {
+  configurable: true,
+  enumerable: false,
+  writable: true,
+});
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -78,16 +78,17 @@ const UNINIT_EVENT = 3;
  * (PushServiceWebSocket) to communicate with the server and PushDB (IndexedDB)
  * for persistence.
  */
 this.PushService = {
   _service: null,
   _state: PUSH_SERVICE_UNINIT,
   _db: null,
   _options: null,
+  _alarmID: null,
 
   // When serverURI changes (this is used for testing), db is cleaned up and a
   // a new db is started. This events must be sequential.
   _serverURIProcessQueue: null,
   _serverURIProcessEnqueue: function(op) {
     if (!this._serverURIProcessQueue) {
       this._serverURIProcessQueue = Promise.resolve();
     }
@@ -176,21 +177,21 @@ this.PushService = {
       this._setState(PUSH_SERVICE_ACTIVE_OFFLINE);
     } else {
       if (this._state == PUSH_SERVICE_RUNNING) {
         // PushService was not in the offline state, but got notification to
         // go online (a offline notification has not been sent).
         // Disconnect first.
         this._service.disconnect();
       }
-      this._db.getAllKeyIDs()
-        .then(keyIDs => {
-          if (keyIDs.length > 0) {
+      this._db.getAllUnexpired()
+        .then(records => {
+          if (records.length > 0) {
             // if there are request waiting
-            this._service.connect(keyIDs);
+            this._service.connect(records);
           }
         });
       this._setState(PUSH_SERVICE_RUNNING);
     }
   },
 
   _changeStateConnectionEnabledEvent: function(enabled) {
     debug("changeStateConnectionEnabledEvent: " + enabled);
@@ -237,16 +238,20 @@ this.PushService = {
         } else if (aData == "dom.push.connection.enabled") {
           this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"));
 
         } else if (aData == "dom.push.debug") {
           gDebuggingEnabled = prefs.get("debug");
         }
         break;
 
+      case "idle-daily":
+        this._dropExpiredRegistrations();
+        break;
+
       case "webapps-clear-data":
         debug("webapps-clear-data");
 
         let data = aSubject
                      .QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
         if (!data) {
           debug("webapps-clear-data: Failed to get information about " +
                 "application");
@@ -254,36 +259,36 @@ this.PushService = {
         }
 
         var originAttributes =
           ChromeUtils.originAttributesToSuffix({ appId: data.appId,
                                                  inBrowser: data.browserOnly });
         this._db.getAllByOriginAttributes(originAttributes)
           .then(records => {
             records.forEach(record => {
-              this._db.delete(this._service.getKeyFromRecord(record))
+              this._db.delete(record.keyID)
                 .then(_ => {
                   // courtesy, but don't establish a connection
                   // just for it
                   if (this._ws) {
                     debug("Had a connection, so telling the server");
-                    this._sendRequest("unregister", {channelID: records.channelID})
+                    this._sendRequest("unregister", {channelID: record.channelID})
                         .catch(function(e) {
                           debug("Unregister errored " + e);
                         });
                   }
                 }, err => {
-                  debug("webapps-clear-data: " + scope +
-                        " Could not delete entry " + records.channelID);
+                  debug("webapps-clear-data: " + record.scope +
+                        " Could not delete entry " + record.channelID);
 
                   // courtesy, but don't establish a connection
                   // just for it
                   if (this._ws) {
                     debug("Had a connection, so telling the server");
-                    this._sendRequest("unregister", {channelID: records.channelID})
+                    this._sendRequest("unregister", {channelID: record.channelID})
                         .catch(function(e) {
                           debug("Unregister errored " + e);
                         });
                   }
                   throw "Database error";
                 });
             });
           }, _ => {
@@ -432,20 +437,21 @@ this.PushService = {
         } catch(e) {}
       }
       if (!service) {
         this._setState(PUSH_SERVICE_INIT);
         return;
       }
 
       // Start service.
-      this._startService(service, uri, false, options);
-      // Before completing the activation check prefs. This will first check
-      // connection.enabled pref and then check offline state.
-      this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"));
+      this._startService(service, uri, false, options).then(_ => {
+        // Before completing the activation check prefs. This will first check
+        // connection.enabled pref and then check offline state.
+        this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"));
+      });
 
     } else {
       // This is only used for testing. Different tests require connecting to
       // slightly different URLs.
       prefs.observe("serverURL", this);
 
       this._serverURIProcessEnqueue(_ =>
         this._changeServerURL(prefs.get("serverURL"), STARTING_SERVICE_EVENT));
@@ -454,18 +460,16 @@ this.PushService = {
 
   _startObservers: function() {
     debug("startObservers");
 
     if (this._state != PUSH_SERVICE_ACTIVATING) {
       return;
     }
 
-    this._alarmID = null;
-
     Services.obs.addObserver(this, "webapps-clear-data", false);
 
     // On B2G the NetworkManager interface fires a network-active-changed
     // event.
     //
     // The "active network" is based on priority - i.e. Wi-Fi has higher
     // priority than data. The PushService should just use the preferred
     // network, and not care about all interface changes.
@@ -481,16 +485,19 @@ this.PushService = {
     // On B2G both events fire, one after the other, when the network goes
     // online, so we explicitly check for the presence of NetworkManager and
     // don't add an observer for offline-status-changed on B2G.
     this._networkStateChangeEventName = this.getNetworkStateChangeEventName();
     Services.obs.addObserver(this, this._networkStateChangeEventName, false);
 
     // Used to monitor if the user wishes to disable Push.
     prefs.observe("connection.enabled", this);
+
+    // Used to prune expired registrations and notify dormant service workers.
+    Services.obs.addObserver(this, "idle-daily", false);
   },
 
   _startService: function(service, serverURI, event, options = {}) {
     debug("startService");
 
     if (this._state != PUSH_SERVICE_ACTIVATING) {
       return;
     }
@@ -577,30 +584,31 @@ this.PushService = {
     if (this._state < PUSH_SERVICE_ACTIVATING) {
       return;
     }
 
     prefs.ignore("debug", this);
     prefs.ignore("connection.enabled", this);
 
     Services.obs.removeObserver(this, this._networkStateChangeEventName);
-    Services.obs.removeObserver(this, "webapps-clear-data", false);
+    Services.obs.removeObserver(this, "webapps-clear-data");
+    Services.obs.removeObserver(this, "idle-daily");
   },
 
   uninit: function() {
     debug("uninit()");
 
     if (this._state == PUSH_SERVICE_UNINIT) {
       return;
     }
 
     this._setState(PUSH_SERVICE_UNINIT);
 
     prefs.ignore("serverURL", this);
-    Services.obs.removeObserver(this, "xpcom-shutdown", false);
+    Services.obs.removeObserver(this, "xpcom-shutdown");
 
     this._serverURIProcessEnqueue(_ =>
             this._changeServerURL("", UNINIT_EVENT));
     debug("shutdown complete!");
   },
 
   /** |delay| should be in milliseconds. */
   setAlarm: function(delay) {
@@ -648,88 +656,118 @@ this.PushService = {
     }
   },
 
   dropRegistrations: function() {
     return this._notifyAllAppsRegister()
       .then(_ => this._db.drop());
   },
 
+  _notifySubscriptionChangeObservers: function(record) {
+    let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
+                     .getService(Ci.nsIMessageListenerManager);
+    // Notify XPCOM observers.
+    Services.obs.notifyObservers(
+      null,
+      "push-subscription-change",
+      record.scope
+    );
+
+    let data = {
+      originAttributes: record.originAttributes,
+      scope: record.scope
+    };
+
+    globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
+  },
+
   // Fires a push-register system message to all applications that have
   // registration.
   _notifyAllAppsRegister: function() {
     debug("notifyAllAppsRegister()");
     // records are objects describing the registration as stored in IndexedDB.
-    return this._db.getAllKeyIDs()
-      .then(records => {
-        let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
-                         .getService(Ci.nsIMessageListenerManager);
-        for (let record of records) {
-          // Notify XPCOM observers.
-          Services.obs.notifyObservers(
-            null,
-            "push-subscription-change",
-            scope
-          );
-
-          let data = {
-            originAttributes: record.originAttributes,
-            scope: record.scope
-          };
-
-          globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
-        }
-      });
+    return this._db.getAllUnexpired().then(records =>
+      records.forEach(record =>
+        this._notifySubscriptionChangeObservers(record)
+      )
+    );
   },
 
   dropRegistrationAndNotifyApp: function(aKeyId) {
-    return this._db.getByKeyID(aKeyId)
-      .then(record => {
-        let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
-                         .getService(Ci.nsIMessageListenerManager);
-        Services.obs.notifyObservers(
-          null,
-          "push-subscription-change",
-          record.scope
-        );
-
-        let data = {
-          originAttributes: record.originAttributes,
-          scope: record.scope
-        };
-
-        globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
-      })
-      .then(_ => this._db.delete(aKeyId));
+    return this._db.getByKeyID(aKeyId).then(record => {
+      this._notifySubscriptionChangeObservers(record);
+      return this._db.delete(aKeyId);
+    });
   },
 
   updateRegistrationAndNotifyApp: function(aOldKey, aRecord) {
     return this._db.delete(aOldKey)
-      .then(_ => this._db.put(aRecord)
-        .then(record => {
-          let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
-                           .getService(Ci.nsIMessageListenerManager);
-          Services.obs.notifyObservers(
-            null,
-            "push-subscription-change",
-            record.scope
-          );
-
-          let data = {
-            originAttributes: record.originAttributes,
-            scope: record.scope
-          };
-
-          globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
-        }));
+      .then(_ => this._db.put(aRecord))
+      .then(record => this._notifySubscriptionChangeObservers(record));
   },
 
-  receivedPushMessage: function(aPushRecord, message) {
-    this._db.put(aPushRecord)
-      .then(_ => this._notifyApp(aPushRecord, message));
+  /**
+   * Dispatches an incoming message to a service worker, recalculating the
+   * quota for the associated push registration. If the quota is exceeded,
+   * the registration and message will be dropped, and the worker will not
+   * be notified.
+   *
+   * @param {String} keyID The push registration ID.
+   * @param {String} message The message contents.
+   * @param {Function} updateFunc A function that receives the existing
+   *  registration record as its argument, and returns a new record. If the
+   *  function returns `null` or `undefined`, the record will not be updated.
+   *  `PushServiceWebSocket` uses this to drop incoming updates with older
+   *  versions.
+   */
+  receivedPushMessage: function(keyID, message, updateFunc) {
+    debug("receivedPushMessage()");
+
+    let shouldNotify = false;
+    this.getByKeyID(keyID).then(record => {
+      if (!record) {
+        throw new Error("No record for key ID " + keyID);
+      }
+      return record.getLastVisit();
+    }).then(lastVisit => {
+      // As a special case, don't notify the service worker if the user
+      // cleared their history.
+      shouldNotify = isFinite(lastVisit);
+      return this._db.update(keyID, record => {
+        let newRecord = updateFunc(record);
+        if (!newRecord) {
+          return null;
+        }
+        if (newRecord.isExpired()) {
+          // Because `unregister` is advisory only, we can still receive messages
+          // for stale registrations from the server.
+          debug("receivedPushMessage: Ignoring update for expired key ID " + keyID);
+          return null;
+        }
+        newRecord.receivedPush(lastVisit);
+        return newRecord;
+      });
+    }).then(record => {
+      if (!record) {
+        return null;
+      }
+      if (shouldNotify) {
+        this._notifyApp(record, message);
+      }
+      if (record.isExpired()) {
+        // Drop the registration in the background. If the user returns to the
+        // site, the service worker will be notified on the next `idle-daily`
+        // event.
+        this._sendRequest("unregister", record).catch(error => {
+          debug("receivedPushMessage: Unregister error: " + error);
+        });
+      }
+    }).catch(error => {
+      debug("receivedPushMessage: Error notifying app: " + error);
+    });
   },
 
   _notifyApp: function(aPushRecord, message) {
     if (!aPushRecord || !aPushRecord.scope ||
         aPushRecord.originAttributes === undefined) {
       debug("notifyApp() something is undefined.  Dropping notification: " +
         JSON.stringify(aPushRecord) );
       return;
@@ -770,18 +808,18 @@ this.PushService = {
                  .getService(Ci.nsIMessageListenerManager);
     globalMM.broadcastAsyncMessage('push', data);
   },
 
   getByKeyID: function(aKeyID) {
     return this._db.getByKeyID(aKeyID);
   },
 
-  getAllKeyIDs: function() {
-    return this._db.getAllKeyIDs();
+  getAllUnexpired: function() {
+    return this._db.getAllUnexpired();
   },
 
   _sendRequest: function(action, aRecord) {
     if (this._state == PUSH_SERVICE_CONNECTION_DISABLE) {
       return Promise.reject({state: 0, error: "Service not active"});
     } else if (this._state == PUSH_SERVICE_ACTIVE_OFFLINE) {
       return Promise.reject({state: 0, error: "NetworkError"});
     }
@@ -791,55 +829,67 @@ this.PushService = {
   /**
    * Called on message from the child process. aPageRecord is an object sent by
    * navigator.push, identifying the sending page and other fields.
    */
   _registerWithServer: function(aPageRecord) {
     debug("registerWithServer()" + JSON.stringify(aPageRecord));
 
     return this._sendRequest("register", aPageRecord)
-      .then(pushRecord => this._onRegisterSuccess(pushRecord),
+      .then(record => this._onRegisterSuccess(record),
             err => this._onRegisterError(err))
-      .then(pushRecord => {
+      .then(record => {
         this._deletePendingRequest(aPageRecord);
-        return pushRecord;
+        return record;
       }, err => {
         this._deletePendingRequest(aPageRecord);
         throw err;
      });
   },
 
   _register: function(aPageRecord) {
     debug("_register()");
     if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
       return Promise.reject({state: 0, error: "NotFoundError"});
     }
 
     return this._checkActivated()
       .then(_ => this._db.getByIdentifiers(aPageRecord))
-      .then(pushRecord => {
-        if (pushRecord === undefined) {
+      .then(record => {
+        if (!record) {
           return this._lookupOrPutPendingRequest(aPageRecord);
         }
-        return pushRecord;
+        if (record.isExpired()) {
+          return record.getLastVisit().then(lastVisit => {
+            if (lastVisit > record.lastPush) {
+              // If the user revisited the site, drop the expired push
+              // registration and re-register.
+              return this._db.delete(record.keyID).then(_ => {
+                return this._lookupOrPutPendingRequest(aPageRecord);
+              });
+            }
+            throw {state: 0, error: "NotFoundError"};
+          });
+        }
+        return record;
       }, error => {
         debug("getByIdentifiers failed");
         throw error;
       });
   },
 
   /**
    * Exceptions thrown in _onRegisterSuccess are caught by the promise obtained
    * from _service.request, causing the promise to be rejected instead.
    */
   _onRegisterSuccess: function(aRecord) {
     debug("_onRegisterSuccess()");
 
     return this._db.put(aRecord)
-      .then(_ => aRecord, error => {
+      .catch(error => {
         // Unable to save. Destroy the subscription in the background.
         this._sendRequest("unregister", aRecord).catch(err => {
           debug("_onRegisterSuccess: Error unregistering stale subscription" +
             err);
         });
         throw error;
       });
   },
@@ -872,45 +922,45 @@ this.PushService = {
 
     let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender);
     let pageRecord = aMessage.data;
 
     let principal = aMessage.principal;
     if (!principal) {
       debug("No principal passed!");
       let message = {
-        requestID: aPageRecord.requestID,
+        requestID: pageRecord.requestID,
         error: "SecurityError"
       };
       mm.sendAsyncMessage("PushService:Register:KO", message);
       return;
     }
 
     pageRecord.originAttributes =
       ChromeUtils.originAttributesToSuffix(principal.originAttributes);
 
     if (!pageRecord.scope || pageRecord.originAttributes === undefined) {
       debug("Incorrect identifier values set! " + JSON.stringify(pageRecord));
       let message = {
-        requestID: aPageRecord.requestID,
+        requestID: pageRecord.requestID,
         error: "SecurityError"
       };
       mm.sendAsyncMessage("PushService:Register:KO", message);
       return;
     }
 
     this[aMessage.name.slice("Push:".length).toLowerCase()](pageRecord, mm);
   },
 
   register: function(aPageRecord, aMessageManager) {
     debug("register(): " + JSON.stringify(aPageRecord));
 
     this._register(aPageRecord)
-      .then(pushRecord => {
-        let message = this._service.prepareRegister(pushRecord);
+      .then(record => {
+        let message = record.toRegister();
         message.requestID = aPageRecord.requestID;
         aMessageManager.sendAsyncMessage("PushService:Register:OK", message);
       }, error => {
         let message = {
           requestID: aPageRecord.requestID,
           error
         };
         aMessageManager.sendAsyncMessage("PushService:Register:KO", message);
@@ -946,41 +996,42 @@ this.PushService = {
     debug("_unregister()");
     if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
       return Promise.reject({state: 0, error: "NotFoundError"});
     }
 
     return this._checkActivated()
       .then(_ => this._db.getByIdentifiers(aPageRecord))
       .then(record => {
-        // If the endpoint didn't exist, let's just fail.
         if (record === undefined) {
-          throw "NotFoundError";
+          return false;
         }
 
         return Promise.all([
           this._sendRequest("unregister", record),
-          this._db.delete(this._service.getKeyFromRecord(record))
-        ]);
+          this._db.delete(record.keyID),
+        ]).then(() => true);
       });
   },
 
   unregister: function(aPageRecord, aMessageManager) {
     debug("unregister() " + JSON.stringify(aPageRecord));
 
     this._unregister(aPageRecord)
-      .then(_ =>
-        aMessageManager.sendAsyncMessage("PushService:Unregister:OK", {
-          requestID: aPageRecord.requestID,
-          pushEndpoint: aPageRecord.pushEndpoint
-        }), error =>
-        aMessageManager.sendAsyncMessage("PushService:Unregister:KO", {
-          requestID: aPageRecord.requestID,
-          error
-        })
+      .then(result => {
+          aMessageManager.sendAsyncMessage("PushService:Unregister:OK", {
+            requestID: aPageRecord.requestID,
+            result: result,
+          })
+        }, error => {
+          debug("unregister(): Actual error " + error);
+          aMessageManager.sendAsyncMessage("PushService:Unregister:KO", {
+            requestID: aPageRecord.requestID,
+          })
+        }
       );
   },
 
   _clearAll: function _clearAll() {
     return this._checkActivated()
       .then(_ => this._db.clearAll())
       .catch(_ => {
         return Promise.resolve();
@@ -993,21 +1044,29 @@ this.PushService = {
   _registration: function(aPageRecord) {
     debug("_registration()");
     if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
       return Promise.reject({state: 0, error: "NotFoundError"});
     }
 
     return this._checkActivated()
       .then(_ => this._db.getByIdentifiers(aPageRecord))
-      .then(pushRecord => {
-        if (!pushRecord) {
+      .then(record => {
+        if (!record) {
           return null;
         }
-        return this._service.prepareRegistration(pushRecord);
+        if (record.isExpired()) {
+          return record.getLastVisit().then(lastVisit => {
+            if (lastVisit > record.lastPush) {
+              return this._db.delete(record.keyID).then(_ => null);
+            }
+            throw {state: 0, error: "NotFoundError"};
+          });
+        }
+        return record.toRegistration();
       });
   },
 
   registration: function(aPageRecord, aMessageManager) {
     debug("registration()");
 
     return this._registration(aPageRecord)
       .then(registration =>
@@ -1015,10 +1074,34 @@ this.PushService = {
           requestID: aPageRecord.requestID,
           registration
         }), error =>
         aMessageManager.sendAsyncMessage("PushService:Registration:KO", {
           requestID: aPageRecord.requestID,
           error
         })
       );
-  }
+  },
+
+  _dropExpiredRegistrations: function() {
+    debug("dropExpiredRegistrations()");
+
+    this._db.getAllExpired().then(records => {
+      return Promise.all(records.map(record => {
+        return record.getLastVisit().then(lastVisit => {
+          if (lastVisit > record.lastPush) {
+            // If the user revisited the site, drop the expired push
+            // registration and notify the associated service worker.
+            return this._db.delete(record.keyID).then(() => {
+              this._notifySubscriptionChangeObservers(record);
+            });
+          }
+        }).catch(error => {
+          debug("dropExpiredRegistrations: Error dropping registration " +
+            record.keyID + ": " + error);
+        });
+      }));
+    }).catch(error => {
+      debug("dropExpiredRegistrations: Error dropping registrations: " +
+        error);
+    });
+  },
 };
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -6,16 +6,17 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
+const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
@@ -28,17 +29,17 @@ var gDebuggingEnabled = prefs.get("debug
 
 function debug(s) {
   if (gDebuggingEnabled) {
     dump("-*- PushServiceHttp2.jsm: " + s + "\n");
   }
 }
 
 const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
-const kPUSHHTTP2DB_DB_VERSION = 3; // Change this if the IndexedDB format changes
+const kPUSHHTTP2DB_DB_VERSION = 4; // Change this if the IndexedDB format changes
 const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
 
 /**
  * A proxy between the PushService and connections listening for incoming push
  * messages. The PushService can silence messages from the connections by
  * setting PushSubscriptionListener._pushService to null. This is required
  * because it can happen that there is an outstanding push message that will
  * be send on OnStopRequest but the PushService may not be interested in these.
@@ -280,26 +281,25 @@ SubscriptionListener.prototype = {
       let uriTry = Services.io.newURI(subscriptionUri, null, null);
     } catch (e) {
       debug("Invalid URI " + subscriptionUri);
       this._subInfo.reject({error: "Return code 201, but URI is bogus. " +
                                    subscriptionUri});
       return;
     }
 
-    var reply = {
+    let reply = new PushRecordHttp2({
       subscriptionUri: subscriptionUri,
       pushEndpoint: linkParserResult.pushEndpoint,
       pushReceiptEndpoint: linkParserResult.pushReceiptEndpoint,
-      pageURL: this._subInfo.record.pageURL,
       scope: this._subInfo.record.scope,
       originAttributes: this._subInfo.record.originAttributes,
-      pushCount: 0,
-      lastPush: 0
-    };
+      quota: this._subInfo.record.maxQuota,
+    });
+
     this._subInfo.resolve(reply);
   },
 };
 
 function retryAfterParser(aRequest) {
   var retryAfter = 0;
   try {
     var retryField = aRequest.getResponseHeader("retry-after");
@@ -374,61 +374,22 @@ function linkParser(linkHeader, serverUR
 this.PushServiceHttp2 = {
   _mainPushService: null,
   _serverURI: null,
 
   // Keep information about all connections, e.g. the channel, listener...
   _conns: {},
   _started: false,
 
-  upgradeSchema: function(aTransaction,
-                          aDb,
-                          aOldVersion,
-                          aNewVersion,
-                          aDbInstance) {
-    debug("upgradeSchemaHttp2()");
-
-    //XXXnsm We haven't shipped Push during this upgrade, so I'm just going to throw old
-    //registrations away without even informing the app.
-    if (aNewVersion != aOldVersion) {
-      try {
-        aDb.deleteObjectStore(aDbInstance._dbStoreName);
-      } catch (e) {
-        if (e.name === "NotFoundError") {
-          debug("No existing object store found");
-        } else {
-          throw e;
-        }
-      }
-    }
-
-    let objectStore = aDb.createObjectStore(aDbInstance._dbStoreName,
-                                            { keyPath: "subscriptionUri" });
-
-    // index to fetch records based on endpoints. used by unregister
-    objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
-
-    // index to fetch records by identifiers.
-    // In the current security model, the originAttributes distinguish between
-    // different 'apps' on the same origin. Since ServiceWorkers are
-    // same-origin to the scope they are registered for, the attributes and
-    // scope are enough to reconstruct a valid principal.
-    objectStore.createIndex("identifiers", ["scope", "originAttributes"], { unique: true });
-    objectStore.createIndex("originAttributes", "originAttributes", { unique: false });
-  },
-
-  getKeyFromRecord: function(aRecord) {
-    return aRecord.subscriptionUri;
-  },
-
   newPushDB: function() {
     return new PushDB(kPUSHHTTP2DB_DB_NAME,
                       kPUSHHTTP2DB_DB_VERSION,
                       kPUSHHTTP2DB_STORE_NAME,
-                      this.upgradeSchema);
+                      "subscriptionUri",
+                      PushRecordHttp2);
   },
 
   hasmainPushService: function() {
     return this._mainPushService !== null;
   },
 
   checkServerURI: function(serverURL) {
     if (!serverURL) {
@@ -793,47 +754,47 @@ this.PushServiceHttp2 = {
     } else {
       this._retryAfterBackoff(aSubscriptionUri, -1);
     }
   },
 
   _pushChannelOnStop: function(aUri, aAckUri, aMessage) {
     debug("pushChannelOnStop() ");
 
-    let sendNotification = function(aAckUri, aPushRecord, self) {
-      aPushRecord.pushCount = aPushRecord.pushCount + 1;
-      aPushRecord.lastPush = new Date().getTime();
-      self._mainPushService.receivedPushMessage(aPushRecord, aMessage);
-      self._ackMsgRecv(aAckUri);
-    };
-
-    let recoverNoSuchEndpoint = function() {
-      debug("Could not get push endpoint " + aUri + " from DB");
-    };
-
-    this._mainPushService.getByKeyID(aUri)
-      .then(pushRecord => sendNotification(aAckUri, pushRecord, this),
-            recoverNoSuchEndpoint);
+    this._mainPushService.receivedPushMessage(aUri, aMessage, record => {
+      // Always update the stored record.
+      return record;
+    });
+    this._ackMsgRecv(aAckUri);
   },
 
   onAlarmFired: function() {
     // Conditions are arranged in decreasing specificity.
     // i.e. when _waitingForPong is true, other conditions are also true.
     this._startConnectionsWaitingForAlarm();
   },
+};
 
-  prepareRegistration: function(aPushRecord) {
-    return {
-      pushEndpoint: aPushRecord.pushEndpoint,
-      pushReceiptEndpoint: aPushRecord.pushReceiptEndpoint,
-      version: aPushRecord.version,
-      lastPush: aPushRecord.lastPush,
-      pushCount: aPushRecord.pushCount
-    };
+function PushRecordHttp2(record) {
+  PushRecord.call(this, record);
+  this.subscriptionUri = record.subscriptionUri;
+  this.pushReceiptEndpoint = record.pushReceiptEndpoint;
+}
+
+PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
+  keyID: {
+    get() {
+      return this.subscriptionUri;
+    },
   },
+});
 
-  prepareRegister: function(aPushRecord) {
-    return {
-      pushEndpoint: aPushRecord.pushEndpoint,
-      pushReceiptEndpoint: aPushRecord.pushReceiptEndpoint
-    };
-  }
+PushRecordHttp2.prototype.toRegistration = function() {
+  let registration = PushRecord.prototype.toRegistration.call(this);
+  registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
+  return registration;
 };
+
+PushRecordHttp2.prototype.toRegister = function() {
+  let register = PushRecord.prototype.toRegister.call(this);
+  register.pushReceiptEndpoint = this.pushReceiptEndpoint;
+  return register;
+};
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -6,16 +6,17 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const {PushDB} = Cu.import("resource://gre/modules/PushDB.jsm");
+const {PushRecord} = Cu.import("resource://gre/modules/PushRecord.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
@@ -27,17 +28,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/power/powermanagerservice;1",
                                    "nsIPowerManagerService");
 #endif
 
 var threadManager = Cc["@mozilla.org/thread-manager;1"]
                       .getService(Ci.nsIThreadManager);
 
 const kPUSHWSDB_DB_NAME = "pushapi";
-const kPUSHWSDB_DB_VERSION = 3; // Change this if the IndexedDB format changes
+const kPUSHWSDB_DB_VERSION = 4; // Change this if the IndexedDB format changes
 const kPUSHWSDB_STORE_NAME = "pushapi";
 
 const kUDP_WAKEUP_WS_STATUS_CODE = 4774;  // WebSocket Close status code sent
                                           // by server to signal that it can
                                           // wake client up using UDP.
 
 const kWS_MAX_WENTDOWN = 2;
 
@@ -120,61 +121,22 @@ const STATE_WAITING_FOR_WS_START = 1;
 const STATE_WAITING_FOR_HELLO = 2;
 // Websocket operational, handshake completed, begin protocol messaging.
 const STATE_READY = 3;
 
 this.PushServiceWebSocket = {
   _mainPushService: null,
   _serverURI: null,
 
-  upgradeSchema: function(aTransaction,
-                          aDb,
-                          aOldVersion,
-                          aNewVersion,
-                          aDbInstance) {
-    debug("upgradeSchemaWS()");
-
-    //XXXnsm We haven't shipped Push during this upgrade, so I'm just going to throw old
-    //registrations away without even informing the app.
-    if (aNewVersion != aOldVersion) {
-      try {
-        aDb.deleteObjectStore(aDbInstance._dbStoreName);
-      } catch (e) {
-        if (e.name === "NotFoundError") {
-          debug("No existing object store found");
-        } else {
-          throw e;
-        }
-      }
-    }
-
-    let objectStore = aDb.createObjectStore(aDbInstance._dbStoreName,
-                                            { keyPath: "channelID" });
-
-    // index to fetch records based on endpoints. used by unregister
-    objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
-
-    // index to fetch records by identifiers.
-    // In the current security model, the originAttributes distinguish between
-    // different 'apps' on the same origin. Since ServiceWorkers are
-    // same-origin to the scope they are registered for, the attributes and
-    // scope are enough to reconstruct a valid principal.
-    objectStore.createIndex("identifiers", ["scope", "originAttributes"], { unique: true });
-    objectStore.createIndex("originAttributes", "originAttributes", { unique: false });
-  },
-
-  getKeyFromRecord: function(aRecord) {
-    return aRecord.channelID;
-  },
-
   newPushDB: function() {
     return new PushDB(kPUSHWSDB_DB_NAME,
                       kPUSHWSDB_DB_VERSION,
                       kPUSHWSDB_STORE_NAME,
-                      this.upgradeSchema);
+                      "channelID",
+                      PushRecordWebSocket);
   },
 
   disconnect: function() {
     this._shutdownWS();
   },
 
   observe: function(aSubject, aTopic, aData) {
 
@@ -677,20 +639,20 @@ this.PushServiceWebSocket = {
       this._currentState = STATE_WAITING_FOR_WS_START;
     } catch(e) {
       debug("Error opening websocket. asyncOpen failed!");
       this._shutdownWS();
       this._reconnectAfterBackoff();
     }
   },
 
-  connect: function(channelIDs) {
+  connect: function(records) {
     debug("connect");
     // Check to see if we need to do anything.
-    if (channelIDs.length > 0) {
+    if (records.length > 0) {
       this._beginWSSetup();
     }
   },
 
   /**
    * There is only one alarm active at any time. This alarm has 3 intervals
    * corresponding to 3 tasks.
    *
@@ -878,26 +840,24 @@ this.PushServiceWebSocket = {
       }
       catch (e) {
         debug("Invalid pushEndpoint " + reply.pushEndpoint);
         tmp.reject({state: 0, error: "Invalid pushEndpoint " +
                                      reply.pushEndpoint});
         return;
       }
 
-      let record = {
+      let record = new PushRecordWebSocket({
         channelID: reply.channelID,
         pushEndpoint: reply.pushEndpoint,
-        pageURL: tmp.record.pageURL,
         scope: tmp.record.scope,
         originAttributes: tmp.record.originAttributes,
-        pushCount: 0,
-        lastPush: 0,
-        version: null
-      };
+        version: null,
+        quota: tmp.record.maxQuota,
+      });
       dump("PushWebSocket " +  JSON.stringify(record));
       tmp.resolve(record);
     } else {
       tmp.reject(reply);
     }
   },
 
   /**
@@ -1011,21 +971,20 @@ this.PushServiceWebSocket = {
         this._wsSendMessage(data);
       }
     }
   },
 
   _queueRequest(data) {
     if (this._currentState != STATE_READY) {
       if (!this._notifyRequestQueue) {
-        this._enqueue(_ => {
-          return new Promise((resolve, reject) => {
-                               this._notifyRequestQueue = resolve;
-                             });
+        let promise = new Promise((resolve, reject) => {
+          this._notifyRequestQueue = resolve;
         });
+        this._enqueue(_ => promise);
       }
 
     }
 
     this._enqueue(_ => this._send(data));
     if (!this._ws) {
       // This will end up calling notifyRequestQueue().
       this._beginWSSetup();
@@ -1036,46 +995,27 @@ this.PushServiceWebSocket = {
         this._notifyRequestQueue = null;
       }
     }
   },
 
   _receivedUpdate: function(aChannelID, aLatestVersion) {
     debug("Updating: " + aChannelID + " -> " + aLatestVersion);
 
-    let compareRecordVersionAndNotify = function(aPushRecord) {
-      debug("compareRecordVersionAndNotify()");
-      if (!aPushRecord) {
-        debug("No record for channel ID " + aChannelID);
-        return;
+    this._mainPushService.receivedPushMessage(aChannelID, "", record => {
+      if (record.version === null ||
+          record.version < aLatestVersion) {
+        debug("Version changed for " + aChannelID + ": " + aLatestVersion);
+        record.version = aLatestVersion;
+        return record;
       }
-
-      if (aPushRecord.version === null ||
-          aPushRecord.version < aLatestVersion) {
-        debug("Version changed, notifying app and updating DB");
-        aPushRecord.version = aLatestVersion;
-        aPushRecord.pushCount = aPushRecord.pushCount + 1;
-        aPushRecord.lastPush = new Date().getTime();
-        this._mainPushService.receivedPushMessage(aPushRecord,
-                                                  "Short as life is, we make " +
-                                                  "it still shorter by the " +
-                                                  "careless waste of time.");
-      }
-      else {
-        debug("No significant version change: " + aLatestVersion);
-      }
-    };
-
-    let recoverNoSuchChannelID = function(aChannelIDFromServer) {
-      debug("Could not get channelID " + aChannelIDFromServer + " from DB");
-    };
-
-    this._mainPushService.getByKeyID(aChannelID)
-      .then(compareRecordVersionAndNotify.bind(this),
-            err => recoverNoSuchChannelID(err));
+      debug("No significant version change for " + aChannelID + ": " +
+            aLatestVersion);
+      return null;
+    });
   },
 
   // begin Push protocol handshake
   _wsOnStart: function(context) {
     debug("wsOnStart()");
     this._releaseWakeLock();
 
     if (this._currentState != STATE_WAITING_FOR_WS_START) {
@@ -1116,17 +1056,17 @@ this.PushServiceWebSocket = {
 
         data.mobilenetwork = {
           mcc: networkState.mcc,
           mnc: networkState.mnc,
           netid: networkState.netid
         };
       }
 
-      this._mainPushService.getAllKeyIDs()
+      this._mainPushService.getAllUnexpired()
         .then(sendHelloMessage.bind(this),
               sendHelloMessage.bind(this));
     });
   },
 
   /**
    * This statusCode is not the websocket protocol status code, but the TCP
    * connection close status code.
@@ -1294,31 +1234,16 @@ this.PushServiceWebSocket = {
    * If this happens, we reconnect the WebSocket to not miss out on
    * notifications.
    */
   onStopListening: function(aServ, aStatus) {
     debug("UDP Server socket was shutdown. Status: " + aStatus);
     this._udpServer = undefined;
     this._beginWSSetup();
   },
-
-  prepareRegistration: function(aPushRecord) {
-    return {
-      pushEndpoint: aPushRecord.pushEndpoint,
-      version: aPushRecord.version,
-      lastPush: aPushRecord.lastPush,
-      pushCount: aPushRecord.pushCount
-    };
-  },
-
-  prepareRegister: function(aPushRecord) {
-    return {
-      pushEndpoint: aPushRecord.pushEndpoint
-    };
-  }
 };
 
 let PushNetworkInfo = {
   /**
    * Returns information about MCC-MNC and the IP of the current connection.
    */
   getNetworkInformation: function() {
     debug("getNetworkInformation()");
@@ -1434,8 +1359,28 @@ let PushNetworkInfo = {
 
     debug("[_getMobileNetworkId:queryDNSForDomain] Getting mobile network ID");
 
     let netidAddress = "wakeup.mnc" + ("00" + networkInfo.mnc).slice(-3) +
       ".mcc" + ("00" + networkInfo.mcc).slice(-3) + ".3gppnetwork.org";
     queryDNSForDomain(netidAddress, callback);
   }
 };
+
+function PushRecordWebSocket(record) {
+  PushRecord.call(this, record);
+  this.channelID = record.channelID;
+  this.version = record.version;
+}
+
+PushRecordWebSocket.prototype = Object.create(PushRecord.prototype, {
+  keyID: {
+    get() {
+      return this.channelID;
+    },
+  },
+});
+
+PushRecordWebSocket.prototype.toRegistration = function() {
+  let registration = PushRecord.prototype.toRegistration.call(this);
+  registration.version = this.version;
+  return registration;
+};
--- a/dom/push/moz.build
+++ b/dom/push/moz.build
@@ -10,16 +10,17 @@ EXTRA_COMPONENTS += [
 ]
 
 EXTRA_PP_JS_MODULES += [
     'PushServiceWebSocket.jsm',
 ]
 
 EXTRA_JS_MODULES += [
     'PushDB.jsm',
+    'PushRecord.jsm',
     'PushService.jsm',
     'PushServiceHttp2.jsm',
 ]
 
 MOCHITEST_MANIFESTS += [
     'test/mochitest.ini',
 ]
 
--- a/dom/push/test/test_has_permissions.html
+++ b/dom/push/test/test_has_permissions.html
@@ -37,23 +37,23 @@ http://creativecommons.org/licenses/publ
       ok(result, "Unregister should return true.");
     }, function(e) {
       dump("Unregistering the SW failed with " + e + "\n");
     });
   }
 
   function hasPermission(swr) {
     var p = new Promise(function(res, rej) {
-      swr.pushManager.hasPermission().then(
-        function(status) {
-          debug("status: " + status);
-          ok(true, "hasPermission() returned a status");
+      swr.pushManager.permissionState().then(
+        function(state) {
+          debug("state: " + state);
+          ok(["granted", "denied", "prompt"].indexOf(state) >= 0, "permissionState() returned a valid state.");
           res(swr);
         }, function(error) {
-          ok(false, "hasPermission failed.");
+          ok(false, "permissionState() failed.");
           res(swr);
         }
         );
     });
     return p;
   }
 
   function runTest() {
--- a/dom/push/test/test_permissions.html
+++ b/dom/push/test/test_permissions.html
@@ -65,20 +65,31 @@ http://creativecommons.org/licenses/publ
           ok(true, "getSubscription() could not register for push notification");
           res(swr);
         }
         );
     });
     return p;
   }
 
+  function checkPermissionState(swr) {
+    return swr.pushManager.permissionState().then(function(state) {
+      ok(state === "denied", "permissionState() should resolve to denied.");
+      return swr;
+    }).catch(function(e) {
+      ok(false, "permissionState() should resolve to denied.");
+      return swr;
+    });
+  }
+
   function runTest() {
     start()
     .then(setupPushNotification)
     .then(getEndpoint)
+    .then(checkPermissionState)
     .then(unregister)
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.addPermission('push', false, document);
   SpecialPowers.pushPrefEnv({"set": [
--- a/dom/push/test/test_register.html
+++ b/dom/push/test/test_register.html
@@ -38,16 +38,26 @@ http://creativecommons.org/licenses/publ
         res(swr)
       }
       controlledFrame = iframe;
       document.body.appendChild(iframe);
     });
     return p;
   }
 
+  function checkPermissionState(swr) {
+    return swr.pushManager.permissionState().then(function(state) {
+      ok(state === "granted", "permissionState() should resolve to granted.");
+      return swr;
+    }).catch(function(e) {
+      ok(false, "permissionState() should resolve to granted.");
+      return swr;
+    });
+  }
+
   function sendPushToPushServer(pushEndpoint) {
     // Work around CORS for now.
     var xhr = new XMLHttpRequest();
     xhr.open('GET', "http://mochi.test:8888/tests/dom/push/test/push-server.sjs", true);
     xhr.setRequestHeader("X-Push-Server", pushEndpoint);
     xhr.onload = function(e) {
       debug("xhr : " + this.status);
     }
@@ -99,16 +109,17 @@ http://creativecommons.org/licenses/publ
     return p.then(function() {
       return pushSubscription;
     });
   }
 
   function runTest() {
     start()
     .then(createControlledIFrame)
+    .then(checkPermissionState)
     .then(setupPushNotification)
     .then(waitForPushNotification)
     .then(unregisterPushNotification)
     .then(unregister)
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
--- a/dom/push/test/worker.js
+++ b/dom/push/test/worker.js
@@ -3,16 +3,19 @@
 
 this.onpush = handlePush;
 
 function handlePush(event) {
 
   self.clients.matchAll().then(function(result) {
     if (event instanceof PushEvent &&
       event.data instanceof PushMessageData &&
-      event.data.text().length > 0) {
+      event.data.text === undefined &&
+      event.data.json === undefined &&
+      event.data.arrayBuffer === undefined &&
+      event.data.blob === undefined) {
 
       result[0].postMessage({type: "finished", okay: "yes"});
       return;
     }
     result[0].postMessage({type: "finished", okay: "no"});
   });
 }
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -5,16 +5,17 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/Timer.jsm');
 Cu.import('resource://gre/modules/Promise.jsm');
 Cu.import('resource://gre/modules/Preferences.jsm');
+Cu.import('resource://gre/modules/PlacesUtils.jsm');
 
 const serviceExports = Cu.import('resource://gre/modules/PushService.jsm', {});
 const servicePrefs = new Preferences('dom.push.');
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "PushNotificationService",
   "@mozilla.org/push/NotificationService;1",
@@ -57,16 +58,35 @@ function after(times, func) {
   return function afterFunc() {
     if (--times <= 0) {
       return func.apply(this, arguments);
     }
   };
 }
 
 /**
+ * Updates the places database.
+ *
+ * @param {mozIPlaceInfo} place A place record to insert.
+ * @returns {Promise} A promise that fulfills when the database is updated.
+ */
+function addVisit(place) {
+  return new Promise((resolve, reject) => {
+    if (typeof place.uri == 'string') {
+      place.uri = Services.io.newURI(place.uri, null, null);
+    }
+    PlacesUtils.asyncHistory.updatePlaces(place, {
+      handleCompletion: resolve,
+      handleError: reject,
+      handleResult() {},
+    });
+  });
+}
+
+/**
  * Defers one or more callbacks until the next turn of the event loop. Multiple
  * callbacks are executed in order.
  *
  * @param {Function[]} callbacks The callbacks to execute. One callback will be
  *  executed per tick.
  */
 function waterfall(...callbacks) {
   callbacks.reduce((promise, callback) => promise.then(() => {
@@ -194,17 +214,18 @@ function setPrefs(prefs = {}) {
     'adaptive.lastGoodPingInterval.mobile': 3 * 60 * 1000,
     'adaptive.lastGoodPingInterval.wifi': 3 * 60 * 1000,
     'adaptive.gap': 60000,
     'adaptive.upperLimit': 29 * 60 * 1000,
     // Misc. defaults.
     'adaptive.mobile': '',
     'http2.maxRetries': 2,
     'http2.retryInterval': 500,
-    'http2.reset_retry_count_after_ms': 60000
+    'http2.reset_retry_count_after_ms': 60000,
+    maxQuotaPerSubscription: 16,
   }, prefs);
   for (let pref in defaultPrefs) {
     servicePrefs.set(pref, defaultPrefs[pref]);
   }
 }
 
 function compareAscending(a, b) {
   return a > b ? 1 : a < b ? -1 : 0;
--- a/dom/push/test/xpcshell/test_clearAll_successful.js
+++ b/dom/push/test/xpcshell/test_clearAll_successful.js
@@ -15,17 +15,19 @@ function run_test() {
 
 add_task(function* test_unregister_success() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID,
     pushEndpoint: 'https://example.org/update/unregister-success',
     scope: 'https://example.com/page/unregister-success',
-    version: 1
+    version: 1,
+    originAttributes: '',
+    quota: Infinity,
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
--- a/dom/push/test/xpcshell/test_notification_ack.js
+++ b/dom/push/test/xpcshell/test_notification_ack.js
@@ -21,29 +21,32 @@ function run_test() {
 add_task(function* test_notification_ack() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
     pushEndpoint: 'https://example.com/update/1',
     scope: 'https://example.org/1',
     originAttributes: '',
-    version: 1
+    version: 1,
+    quota: Infinity,
   }, {
     channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
     pushEndpoint: 'https://example.com/update/2',
     scope: 'https://example.org/2',
     originAttributes: '',
-    version: 2
+    version: 2,
+    quota: Infinity,
   }, {
     channelID: '5477bfda-22db-45d4-9614-fee369630260',
     pushEndpoint: 'https://example.com/update/3',
     scope: 'https://example.org/3',
     originAttributes: '',
-    version: 3
+    version: 3,
+    quota: Infinity,
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification('push-notification'),
     promiseObserverNotification('push-notification'),
--- a/dom/push/test/xpcshell/test_notification_duplicate.js
+++ b/dom/push/test/xpcshell/test_notification_duplicate.js
@@ -19,23 +19,25 @@ function run_test() {
 add_task(function* test_notification_duplicate() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '8d2d9400-3597-4c5a-8a38-c546b0043bcc',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/1',
     originAttributes: "",
-    version: 2
+    version: 2,
+    quota: Infinity,
   }, {
     channelID: '27d1e393-03ef-4c72-a5e6-9e890dfccad0',
     pushEndpoint: 'https://example.org/update/2',
     scope: 'https://example.com/2',
     originAttributes: "",
-    version: 2
+    version: 2,
+    quota: Infinity,
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let acks = 0;
--- a/dom/push/test/xpcshell/test_notification_error.js
+++ b/dom/push/test/xpcshell/test_notification_error.js
@@ -21,29 +21,32 @@ add_task(function* test_notification_err
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   let originAttributes = '';
   let records = [{
     channelID: 'f04f1e46-9139-4826-b2d1-9411b0821283',
     pushEndpoint: 'https://example.org/update/success-1',
     scope: 'https://example.com/a',
     originAttributes: originAttributes,
-    version: 1
+    version: 1,
+    quota: Infinity,
   }, {
     channelID: '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
     pushEndpoint: 'https://example.org/update/error',
     scope: 'https://example.com/b',
     originAttributes: originAttributes,
-    version: 2
+    version: 2,
+    quota: Infinity,
   }, {
     channelID: 'b63f7bef-0a0d-4236-b41e-086a69dfd316',
     pushEndpoint: 'https://example.org/update/success-2',
     scope: 'https://example.com/c',
     originAttributes: originAttributes,
-    version: 3
+    version: 3,
+    quota: Infinity,
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification(
       'push-notification',
--- a/dom/push/test/xpcshell/test_notification_http2.js
+++ b/dom/push/test/xpcshell/test_notification_http2.js
@@ -53,29 +53,32 @@ add_task(function* test_pushNotification
 
   var serverURL = "https://localhost:" + serverPort;
 
   let records = [{
     subscriptionUri: serverURL + '/pushNotifications/subscription1',
     pushEndpoint: serverURL + '/pushEndpoint1',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint1',
     scope: 'https://example.com/page/1',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription2',
     pushEndpoint: serverURL + '/pushEndpoint2',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint2',
     scope: 'https://example.com/page/2',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription3',
     pushEndpoint: serverURL + '/pushEndpoint3',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint3',
     scope: 'https://example.com/page/3',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification('push-notification'),
--- a/dom/push/test/xpcshell/test_notification_incomplete.js
+++ b/dom/push/test/xpcshell/test_notification_incomplete.js
@@ -19,44 +19,52 @@ function run_test() {
 
 add_task(function* test_notification_incomplete() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '123',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/page/1',
-    version: 1
+    version: 1,
+    originAttributes: '',
+    quota: Infinity,
   }, {
     channelID: '3ad1ed95-d37a-4d88-950f-22cbe2e240d7',
     pushEndpoint: 'https://example.org/update/2',
     scope: 'https://example.com/page/2',
-    version: 1
+    version: 1,
+    originAttributes: '',
+    quota: Infinity,
   }, {
     channelID: 'd239498b-1c85-4486-b99b-205866e82d1f',
     pushEndpoint: 'https://example.org/update/3',
     scope: 'https://example.com/page/3',
-    version: 3
+    version: 3,
+    originAttributes: '',
+    quota: Infinity,
   }, {
     channelID: 'a50de97d-b496-43ce-8b53-05522feb78db',
     pushEndpoint: 'https://example.org/update/4',
     scope: 'https://example.com/page/4',
-    version: 10
+    version: 10,
+    originAttributes: '',
+    quota: Infinity,
   }];
   for (let record of records) {
-    db.put(record);
+    yield db.put(record);
   }
 
   Services.obs.addObserver(function observe(subject, topic, data) {
     ok(false, 'Should not deliver malformed updates');
   }, 'push-notification', false);
 
   let notificationDefer = Promise.defer();
   let notificationDone = after(2, notificationDefer.resolve);
-  let prevHandler = PushService._handleNotificationReply;
+  let prevHandler = PushServiceWebSocket._handleNotificationReply;
   PushServiceWebSocket._handleNotificationReply = function _handleNotificationReply() {
     notificationDone();
     return prevHandler.apply(this, arguments);
   };
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
--- a/dom/push/test/xpcshell/test_notification_version_string.js
+++ b/dom/push/test/xpcshell/test_notification_version_string.js
@@ -17,17 +17,18 @@ function run_test() {
 add_task(function* test_notification_version_string() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID: '6ff97d56-d0c0-43bc-8f5b-61b855e1d93b',
     pushEndpoint: 'https://example.org/updates/1',
     scope: 'https://example.com/page/1',
     originAttributes: '',
-    version: 2
+    version: 2,
+    quota: Infinity,
   });
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
@@ -65,9 +66,10 @@ add_task(function* test_notification_ver
   strictEqual(message.version, 4, 'Wrong version');
 
   yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
     'Timed out waiting for string acknowledgement');
 
   let storeRecord = yield db.getByKeyID(
     '6ff97d56-d0c0-43bc-8f5b-61b855e1d93b');
   strictEqual(storeRecord.version, 4, 'Wrong record version');
+  equal(storeRecord.quota, Infinity, 'Wrong quota');
 });
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_quota_exceeded.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+const userAgentID = '7eb873f9-8d47-4218-804b-fff78dc04e88';
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID,
+  });
+  run_next_test();
+}
+
+add_task(function* test_expiration_origin_threshold() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => db.drop().then(_ => db.close()));
+
+  yield db.put({
+    channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
+    pushEndpoint: 'https://example.org/push/1',
+    scope: 'https://example.com/auctions',
+    pushCount: 0,
+    lastPush: 0,
+    version: null,
+    originAttributes: '',
+    quota: 16,
+  });
+  yield db.put({
+    channelID: '46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
+    pushEndpoint: 'https://example.org/push/2',
+    scope: 'https://example.com/deals',
+    pushCount: 0,
+    lastPush: 0,
+    version: null,
+    originAttributes: '',
+    quota: 16,
+  });
+
+  // The notification threshold is per-origin, even with multiple service
+  // workers for different scopes.
+  yield addVisit({
+    uri: 'https://example.com/login',
+    title: 'Sign in to see your auctions',
+    visits: [{
+      visitDate: (Date.now() - 7 * 24 * 60 * 60 * 1000) * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  });
+
+  // We'll always use your most recent visit to an origin.
+  yield addVisit({
+    uri: 'https://example.com/auctions',
+    title: 'Your auctions',
+    visits: [{
+      visitDate: (Date.now() - 2 * 24 * 60 * 60 * 1000) * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  });
+
+  // ...But we won't count downloads or embeds.
+  yield addVisit({
+    uri: 'https://example.com/invoices/invoice.pdf',
+    title: 'Invoice #123',
+    visits: [{
+      visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_EMBED,
+    }, {
+      visitDate: Date.now() * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
+    }],
+  });
+
+  // We expect to receive 6 notifications: 5 on the `auctions` channel,
+  // and 1 on the `deals` channel. They're from the same origin, but
+  // different scopes, so each can send 5 notifications before we remove
+  // their subscription.
+  let updates = 0;
+  let notifyPromise = promiseObserverNotification('push-notification', (subject, data) => {
+    updates++;
+    return updates == 6;
+  });
+  let unregisterDefer = Promise.defer();
+
+  PushService.init({
+    serverURI: 'wss://push.example.org/',
+    networkInfo: new MockDesktopNetworkInfo(),
+    db,
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(request) {
+          deepEqual(request.channelIDs.sort(), [
+            '46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
+            'eb33fc90-c883-4267-b5cb-613969e8e349',
+          ], 'Wrong active registrations in handshake');
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'hello',
+            status: 200,
+            uaid: userAgentID,
+          }));
+          // We last visited the site 2 days ago, so we can send 5
+          // notifications without throttling. Sending a 6th should
+          // drop the registration.
+          for (let version = 1; version <= 6; version++) {
+            this.serverSendMsg(JSON.stringify({
+              messageType: 'notification',
+              updates: [{
+                channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
+                version,
+              }],
+            }));
+          }
+          // But the limits are per-channel, so we can send 5 more
+          // notifications on a different channel.
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'notification',
+            updates: [{
+              channelID: '46cc6f6a-c106-4ffa-bb7c-55c60bd50c41',
+              version: 1,
+            }],
+          }));
+        },
+        onUnregister(request) {
+          equal(request.channelID, 'eb33fc90-c883-4267-b5cb-613969e8e349', 'Unregistered wrong channel ID');
+          unregisterDefer.resolve();
+        },
+        // We expect to receive acks, but don't care about their
+        // contents.
+        onACK(request) {},
+      });
+    },
+  });
+
+  yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
+    'Timed out waiting for unregister request');
+
+  yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
+    'Timed out waiting for notifications');
+
+  let expiredRecord = yield db.getByKeyID('eb33fc90-c883-4267-b5cb-613969e8e349');
+  strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
+});
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_quota_observer.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+const userAgentID = '28cd09e2-7506-42d8-9e50-b02785adc7ef';
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID,
+  });
+  run_next_test();
+}
+
+add_task(function* test_expiration_history_observer() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => db.drop().then(_ => db.close()));
+
+  // A registration that we'll expire...
+  yield db.put({
+    channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
+    pushEndpoint: 'https://example.org/push/1',
+    scope: 'https://example.com/deals',
+    pushCount: 0,
+    lastPush: 0,
+    version: null,
+    originAttributes: '',
+    quota: 16,
+  });
+
+  // ...And an expired registration that we'll revive later.
+  yield db.put({
+    channelID: 'eb33fc90-c883-4267-b5cb-613969e8e349',
+    pushEndpoint: 'https://example.org/push/2',
+    scope: 'https://example.com/auctions',
+    pushCount: 0,
+    lastPush: 0,
+    version: null,
+    originAttributes: '',
+    quota: 0,
+  });
+
+  yield addVisit({
+    uri: 'https://example.com/infrequent',
+    title: 'Infrequently-visited page',
+    visits: [{
+      visitDate: (Date.now() - 14 * 24 * 60 * 60 * 1000) * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  });
+
+  let unregisterDefer = Promise.defer();
+
+  PushService.init({
+    serverURI: 'wss://push.example.org/',
+    networkInfo: new MockDesktopNetworkInfo(),
+    db,
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(request) {
+          deepEqual(request.channelIDs, [
+            '379c0668-8323-44d2-a315-4ee83f1a9ee9',
+          ], 'Should not include expired channel IDs');
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'hello',
+            status: 200,
+            uaid: userAgentID,
+          }));
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'notification',
+            updates: [{
+              channelID: '379c0668-8323-44d2-a315-4ee83f1a9ee9',
+              version: 2,
+            }],
+          }));
+        },
+        onUnregister(request) {
+          equal(request.channelID, '379c0668-8323-44d2-a315-4ee83f1a9ee9', 'Dropped wrong channel ID');
+          unregisterDefer.resolve();
+        },
+        onACK(request) {},
+      });
+    }
+  });
+
+  yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
+    'Timed out waiting for unregister request');
+
+  let expiredRecord = yield db.getByKeyID('379c0668-8323-44d2-a315-4ee83f1a9ee9');
+  strictEqual(expiredRecord.quota, 0, 'Expired record not updated');
+
+  let notifiedScopes = [];
+  let subChangePromise = promiseObserverNotification('push-subscription-change', (subject, data) => {
+    notifiedScopes.push(data);
+    return notifiedScopes.length == 2;
+  });
+
+  // Now visit the site...
+  yield addVisit({
+    uri: 'https://example.com/another-page',
+    title: 'Infrequently-visited page',
+    visits: [{
+      visitDate: Date.now() * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  });
+  Services.obs.notifyObservers(null, 'idle-daily', '');
+
+  // And we should receive notifications for both scopes.
+  yield waitForPromise(subChangePromise, DEFAULT_TIMEOUT,
+    'Timed out waiting for subscription change events');
+  deepEqual(notifiedScopes.sort(), [
+    'https://example.com/auctions',
+    'https://example.com/deals'
+  ], 'Wrong scopes for subscription changes');
+
+  let aRecord = yield db.getByKeyID('379c0668-8323-44d2-a315-4ee83f1a9ee9');
+  ok(!aRecord, 'Should drop expired record');
+
+  let bRecord = yield db.getByKeyID('eb33fc90-c883-4267-b5cb-613969e8e349');
+  ok(!bRecord, 'Should drop evicted record');
+});
--- a/dom/push/test/xpcshell/test_register_5xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_register_5xxCode_http2.js
@@ -80,17 +80,17 @@ add_task(function* test1() {
   PushService.init({
     serverURI: serverURL + "/subscribe5xxCode",
     service: PushServiceHttp2,
     db
   });
 
   let newRecord = yield PushNotificationService.register(
     'https://example.com/retry5xxCode',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
 
   var subscriptionUri = serverURL + '/subscription';
   var pushEndpoint = serverURL + '/pushEndpoint';
   var pushReceiptEndpoint = serverURL + '/receiptPushEndpoint';
   equal(newRecord.subscriptionUri, subscriptionUri,
     'Wrong subscription ID in registration record');
   equal(newRecord.pushEndpoint, pushEndpoint,
--- a/dom/push/test/xpcshell/test_register_case.js
+++ b/dom/push/test/xpcshell/test_register_case.js
@@ -43,17 +43,17 @@ add_task(function* test_register_case() 
           }));
         }
       });
     }
   });
 
   let newRecord = yield waitForPromise(
     PushNotificationService.register('https://example.net/case',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     DEFAULT_TIMEOUT,
     'Mixed-case register response timed out'
   );
   equal(newRecord.pushEndpoint, 'https://example.com/update/case',
     'Wrong push endpoint in registration record');
   equal(newRecord.scope, 'https://example.net/case',
     'Wrong scope in registration record');
 
--- a/dom/push/test/xpcshell/test_register_flush.js
+++ b/dom/push/test/xpcshell/test_register_flush.js
@@ -25,17 +25,18 @@ function run_test() {
 add_task(function* test_register_flush() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let record = {
     channelID: '9bcc7efb-86c7-4457-93ea-e24e6eb59b74',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/page/1',
     originAttributes: '',
-    version: 2
+    version: 2,
+    quota: Infinity,
   };
   yield db.put(record);
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   let ackDone = after(2, ackDefer.resolve);
   PushService.init({
--- a/dom/push/test/xpcshell/test_register_invalid_channel.js
+++ b/dom/push/test/xpcshell/test_register_invalid_channel.js
@@ -44,17 +44,17 @@ add_task(function* test_register_invalid
           }));
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register('https://example.com/invalid-channel',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'Invalid channel ID';
     },
     'Wrong error for invalid channel ID'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records for error responses');
--- a/dom/push/test/xpcshell/test_register_invalid_endpoint.js
+++ b/dom/push/test/xpcshell/test_register_invalid_endpoint.js
@@ -46,17 +46,17 @@ add_task(function* test_register_invalid
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register(
       'https://example.net/page/invalid-endpoint',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes('Invalid pushEndpoint');
     },
     'Wrong error for invalid endpoint'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records with invalid endpoints');
--- a/dom/push/test/xpcshell/test_register_invalid_json.js
+++ b/dom/push/test/xpcshell/test_register_invalid_json.js
@@ -45,17 +45,17 @@ add_task(function* test_register_invalid
           registers++;
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register('https://example.net/page/invalid-json',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for invalid JSON response'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after invalid JSON response timed out');
--- a/dom/push/test/xpcshell/test_register_no_id.js
+++ b/dom/push/test/xpcshell/test_register_no_id.js
@@ -49,17 +49,17 @@ add_task(function* test_register_no_id()
           }));
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register('https://example.com/incomplete',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for incomplete register response'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after incomplete register response timed out');
--- a/dom/push/test/xpcshell/test_register_request_queue.js
+++ b/dom/push/test/xpcshell/test_register_request_queue.js
@@ -41,21 +41,21 @@ add_task(function* test_register_request
           ok(false, 'Should cancel timed-out requests');
         }
       });
     }
   });
 
   let firstRegister = PushNotificationService.register(
     'https://example.com/page/1',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
   let secondRegister = PushNotificationService.register(
     'https://example.com/page/1',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
 
   yield waitForPromise(Promise.all([
     rejects(firstRegister, function(error) {
       return error == 'TimeoutError';
     }, 'Should time out the first request'),
     rejects(secondRegister, function(error) {
       return error == 'TimeoutError';
--- a/dom/push/test/xpcshell/test_register_rollback.js
+++ b/dom/push/test/xpcshell/test_register_rollback.js
@@ -70,17 +70,17 @@ add_task(function* test_register_rollbac
         }
       });
     }
   });
 
   // Should return a rejected promise if storage fails.
   yield rejects(
     PushNotificationService.register('https://example.com/storage-error',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'universe has imploded';
     },
     'Wrong error for unregister database failure'
   );
 
   // Should send an out-of-band unregister request.
   yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
--- a/dom/push/test/xpcshell/test_register_success.js
+++ b/dom/push/test/xpcshell/test_register_success.js
@@ -53,25 +53,29 @@ add_task(function* test_register_success
           }));
         }
       });
     }
   });
 
   let newRecord = yield PushNotificationService.register(
     'https://example.org/1',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
   equal(newRecord.channelID, channelID,
     'Wrong channel ID in registration record');
   equal(newRecord.pushEndpoint, 'https://example.com/update/1',
     'Wrong push endpoint in registration record');
   equal(newRecord.scope, 'https://example.org/1',
     'Wrong scope in registration record');
+  equal(newRecord.quota, Infinity,
+    'Wrong quota in registration record');
 
   let record = yield db.getByKeyID(channelID);
   equal(record.channelID, channelID,
     'Wrong channel ID in database record');
   equal(record.pushEndpoint, 'https://example.com/update/1',
     'Wrong push endpoint in database record');
   equal(record.scope, 'https://example.org/1',
     'Wrong scope in database record');
+  equal(record.quota, Infinity,
+    'Wrong quota in database record');
 });
--- a/dom/push/test/xpcshell/test_register_timeout.js
+++ b/dom/push/test/xpcshell/test_register_timeout.js
@@ -79,17 +79,17 @@ add_task(function* test_register_timeout
           registers++;
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register('https://example.net/page/timeout',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for request timeout'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records for timed-out responses');
--- a/dom/push/test/xpcshell/test_register_wrong_id.js
+++ b/dom/push/test/xpcshell/test_register_wrong_id.js
@@ -55,17 +55,17 @@ add_task(function* test_register_wrong_i
           }));
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register('https://example.com/mismatched',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for mismatched register reply'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after mismatched register reply timed out');
--- a/dom/push/test/xpcshell/test_register_wrong_type.js
+++ b/dom/push/test/xpcshell/test_register_wrong_type.js
@@ -51,17 +51,17 @@ add_task(function* test_register_wrong_t
       });
     }
   });
 
   let promise =
 
   yield rejects(
     PushNotificationService.register('https://example.com/mistyped',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for non-string channel ID'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after sending non-string channel ID timed out');
--- a/dom/push/test/xpcshell/test_registration_error.js
+++ b/dom/push/test/xpcshell/test_registration_error.js
@@ -27,15 +27,15 @@ add_task(function* test_registrations_er
     }),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   yield rejects(
     PushNotificationService.registration('https://example.net/1',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'Database error';
     },
     'Wrong message'
   );
 });
--- a/dom/push/test/xpcshell/test_registration_none.js
+++ b/dom/push/test/xpcshell/test_registration_none.js
@@ -20,11 +20,11 @@ add_task(function* test_registration_non
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   let registration = yield PushNotificationService.registration(
     'https://example.net/1',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false });
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }));
   ok(!registration, 'Should not open a connection without registration');
 });
--- a/dom/push/test/xpcshell/test_registration_success.js
+++ b/dom/push/test/xpcshell/test_registration_success.js
@@ -16,34 +16,38 @@ function run_test() {
 add_task(function* test_registration_success() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: 'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
     pushEndpoint: 'https://example.com/update/same-manifest/1',
     scope: 'https://example.net/a',
     originAttributes: '',
-    version: 5
+    version: 5,
+    quota: Infinity,
   }, {
     channelID: 'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
     pushEndpoint: 'https://example.com/update/same-manifest/2',
     scope: 'https://example.net/b',
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: 42 }),
-    version: 10
+    version: 10,
+    quota: Infinity,
   }, {
     channelID: 'b1cf38c9-6836-4d29-8a30-a3e98d59b728',
     pushEndpoint: 'https://example.org/update/different-manifest',
     scope: 'https://example.org/c',
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: 42, inBrowser: true }),
-    version: 15
+    version: 15,
+    quota: Infinity,
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
+  let handshakeDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(request) {
           equal(request.uaid, userAgentID, 'Wrong device ID in handshake');
           deepEqual(request.channelIDs.sort(), [
@@ -51,21 +55,28 @@ add_task(function* test_registration_suc
             'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
             'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
           ], 'Wrong channel list in handshake');
           this.serverSendMsg(JSON.stringify({
             messageType: 'hello',
             status: 200,
             uaid: userAgentID
           }));
+          handshakeDefer.resolve();
         }
       });
     }
   });
 
+  yield waitForPromise(
+    handshakeDefer.promise,
+    DEFAULT_TIMEOUT,
+    'Timed out waiting for handshake'
+  );
+
   let registration = yield PushNotificationService.registration(
     'https://example.net/a', '');
   equal(
     registration.pushEndpoint,
     'https://example.com/update/same-manifest/1',
     'Wrong push endpoint for scope'
   );
   equal(registration.version, 5, 'Wrong version for scope');
--- a/dom/push/test/xpcshell/test_registration_success_http2.js
+++ b/dom/push/test/xpcshell/test_registration_success_http2.js
@@ -37,29 +37,32 @@ add_task(function* test_pushNotification
 
   var serverURL = "https://localhost:" + serverPort;
 
   let records = [{
     subscriptionUri: serverURL + '/subscriptionA',
     pushEndpoint: serverURL + '/pushEndpointA',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointA',
     scope: 'https://example.net/a',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/subscriptionB',
     pushEndpoint: serverURL + '/pushEndpointB',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointB',
     scope: 'https://example.net/b',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }, {
     subscriptionUri: serverURL + '/subscriptionC',
     pushEndpoint: serverURL + '/pushEndpointC',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointC',
     scope: 'https://example.net/c',
-    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL,
--- a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
@@ -67,17 +67,19 @@ add_task(function* test1() {
   do_test_pending();
 
   var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
 
   let records = [{
     subscriptionUri: serverURL + '/subscription4xxCode',
     pushEndpoint: serverURL + '/pushEndpoint',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
-    scope: 'https://example.com/page'
+    scope: 'https://example.com/page',
+    originAttributes: '',
+    quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
--- a/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js
@@ -77,17 +77,19 @@ add_task(function* test1() {
   do_test_pending();
 
   var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
 
   let records = [{
     subscriptionUri: serverURL + '/subscription5xxCode',
     pushEndpoint: serverURL + '/pushEndpoint',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
-    scope: 'https://example.com/page'
+    scope: 'https://example.com/page',
+    originAttributes: '',
+    quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
--- a/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js
@@ -62,17 +62,19 @@ add_task(function* test1() {
   do_test_pending();
 
   var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
 
   let records = [{
     subscriptionUri: 'http://localhost/subscriptionNotExist',
     pushEndpoint: serverURL + '/pushEndpoint',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
-    scope: 'https://example.com/page'
+    scope: 'https://example.com/page',
+    originAttributes: '',
+    quota: Infinity,
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL + "/subscribe",
--- a/dom/push/test/xpcshell/test_unregister_empty_scope.js
+++ b/dom/push/test/xpcshell/test_unregister_empty_scope.js
@@ -25,15 +25,15 @@ add_task(function* test_unregister_empty
           }));
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.unregister('',
-      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error.error == 'NotFoundError';
     },
     'Wrong error for empty endpoint'
   );
 });
--- a/dom/push/test/xpcshell/test_unregister_error.js
+++ b/dom/push/test/xpcshell/test_unregister_error.js
@@ -16,17 +16,18 @@ function run_test() {
 add_task(function* test_unregister_error() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID: channelID,
     pushEndpoint: 'https://example.org/update/failure',
     scope: 'https://example.net/page/failure',
     originAttributes: '',
-    version: 1
+    version: 1,
+    quota: Infinity,
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
--- a/dom/push/test/xpcshell/test_unregister_invalid_json.js
+++ b/dom/push/test/xpcshell/test_unregister_invalid_json.js
@@ -20,23 +20,25 @@ function run_test() {
 add_task(function* test_unregister_invalid_json() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '87902e90-c57e-4d18-8354-013f4a556559',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.edu/page/1',
     originAttributes: '',
-    version: 1
+    version: 1,
+    quota: Infinity,
   }, {
     channelID: '057caa8f-9b99-47ff-891c-adad18ce603e',
     pushEndpoint: 'https://example.com/update/2',
     scope: 'https://example.net/page/1',
     originAttributes: '',
-    version: 1
+    version: 1,
+    quota: Infinity,
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let unregisterDefer = Promise.defer();
   let unregisterDone = after(2, unregisterDefer.resolve);
   PushService.init({
--- a/dom/push/test/xpcshell/test_unregister_not_found.js
+++ b/dom/push/test/xpcshell/test_unregister_not_found.js
@@ -23,15 +23,13 @@ add_task(function* test_unregister_not_f
             status: 200,
             uaid: 'f074ed80-d479-44fa-ba65-792104a79ea9'
           }));
         }
       });
     }
   });
 
-  let promise = PushNotificationService.unregister(
+  let result = yield PushNotificationService.unregister(
     'https://example.net/nonexistent',
-    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false });
-  yield rejects(promise, function(error) {
-    return error == 'NotFoundError';
-  }, 'Wrong error for nonexistent scope');
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }));
+  ok(result === false, "unregister should resolve with false for nonexistent scope");
 });
--- a/dom/push/test/xpcshell/test_unregister_success.js
+++ b/dom/push/test/xpcshell/test_unregister_success.js
@@ -16,17 +16,18 @@ function run_test() {
 add_task(function* test_unregister_success() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID,
     pushEndpoint: 'https://example.org/update/unregister-success',
     scope: 'https://example.com/page/unregister-success',
     originAttributes: '',
-    version: 1
+    version: 1,
+    quota: Infinity,
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
--- a/dom/push/test/xpcshell/test_unregister_success_http2.js
+++ b/dom/push/test/xpcshell/test_unregister_success_http2.js
@@ -50,16 +50,17 @@ add_task(function* test_pushUnsubscripti
   var serverURL = "https://localhost:" + serverPort;
 
   yield db.put({
     subscriptionUri: serverURL + '/subscriptionUnsubscriptionSuccess',
     pushEndpoint: serverURL + '/pushEndpointUnsubscriptionSuccess',
     pushReceiptEndpoint: serverURL + '/receiptPushEndpointUnsubscriptionSuccess',
     scope: 'https://example.com/page/unregister-success',
     originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
+    quota: Infinity,
   });
 
   PushService.init({
     serverURI: serverURL,
     db
   });
 
   yield PushNotificationService.unregister(
--- a/dom/push/test/xpcshell/test_webapps_cleardata.js
+++ b/dom/push/test/xpcshell/test_webapps_cleardata.js
@@ -21,17 +21,16 @@ function run_test() {
   );
   run_next_test();
 }
 
 add_task(function* test_webapps_cleardata() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
-  PushService._generateID = () => channelID;
   PushService.init({
     serverURI: "wss://push.example.org",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
     makeWebSocket(uri) {
       return new MockWebSocket(uri, {
         onHello(data) {
           equal(data.messageType, 'hello', 'Handshake: wrong message type');
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -4,16 +4,18 @@ tail =
 # Push notifications and alarms are currently disabled on Android.
 skip-if = toolkit == 'android'
 
 [test_notification_ack.js]
 [test_notification_duplicate.js]
 [test_notification_error.js]
 [test_notification_incomplete.js]
 [test_notification_version_string.js]
+[test_quota_exceeded.js]
+[test_quota_observer.js]
 [test_register_case.js]
 [test_register_flush.js]
 [test_register_invalid_channel.js]
 [test_register_invalid_endpoint.js]
 [test_register_invalid_json.js]
 [test_register_no_id.js]
 [test_register_request_queue.js]
 [test_register_rollback.js]
--- a/dom/requestsync/RequestSyncService.jsm
+++ b/dom/requestsync/RequestSyncService.jsm
@@ -15,16 +15,17 @@ const RSYNCDB_NAME = "requestSync";
 const RSYNC_MIN_INTERVAL = 100;
 
 const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes
 
 const RSYNC_STATE_ENABLED = "enabled";
 const RSYNC_STATE_DISABLED = "disabled";
 const RSYNC_STATE_WIFIONLY = "wifiOnly";
 
+Cu.import("resource://gre/modules/BrowserUtils.jsm");
 Cu.import('resource://gre/modules/IndexedDBHelper.jsm');
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.importGlobalProperties(["indexedDB"]);
 
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
@@ -76,17 +77,17 @@ this.RequestSyncService = {
   init: function() {
     debug("init");
 
     this._messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.addObserver(this, 'xpcom-shutdown', false);
-    Services.obs.addObserver(this, 'clear-cookiejar-data', false);
+    Services.obs.addObserver(this, 'clear-origin-data', false);
     Services.obs.addObserver(this, 'wifi-state-changed', false);
 
     this.initDBHelper("requestSync", RSYNCDB_VERSION, [RSYNCDB_NAME]);
 
     // Loading all the data from the database into the _registrations map.
     // Any incoming message will be stored and processed when the async
     // operation is completed.
 
@@ -112,17 +113,17 @@ this.RequestSyncService = {
   shutdown: function() {
     debug("shutdown");
 
     this._messages.forEach((function(msgName) {
       ppmm.removeMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.removeObserver(this, 'xpcom-shutdown');
-    Services.obs.removeObserver(this, 'clear-cookiejar-data');
+    Services.obs.removeObserver(this, 'clear-origin-data');
     Services.obs.removeObserver(this, 'wifi-state-changed');
 
     this.close();
 
     // Removing all the registrations will delete the pending timers.
     let self = this;
     this.forEachRegistration(function(aObj) {
       let key = self.principalToKey(aObj.principal);
@@ -133,17 +134,17 @@ this.RequestSyncService = {
   observe: function(aSubject, aTopic, aData) {
     debug("observe");
 
     switch (aTopic) {
       case 'xpcom-shutdown':
         this.shutdown();
         break;
 
-      case 'clear-cookiejar-data':
+      case 'clear-origin-data':
         this.clearData(aData);
         break;
 
       case 'wifi-state-changed':
         this.wifiStateChanged(aSubject == 'enabled');
         break;
 
       default:
@@ -155,21 +156,22 @@ this.RequestSyncService = {
   // When an app is uninstalled, we have to clean all its tasks.
   clearData: function(aData) {
     debug('clearData');
 
     if (!aData) {
       return;
     }
 
-    let partialKey = aData;
+    let pattern = JSON.parse(aData);
     let dbKeys = [];
 
-    for (let key  in this._registrations) {
-      if (key.indexOf(partialKey) != 0) {
+    for (let key in this._registrations) {
+      let prin = BrowserUtils.principalFromOrigin(key);
+      if (!ChromeUtils.originAttributesMatchPattern(prin.originAttributes, pattern)) {
         continue;
       }
 
       for (let task in this._registrations[key]) {
         dbKeys = this._registrations[key][task].dbKey;
         this.removeRegistrationInternal(task, key);
       }
     }
@@ -194,17 +196,17 @@ this.RequestSyncService = {
   // Creation of the schema for the database.
   upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
     debug('updateSchema');
     aDb.createObjectStore(RSYNCDB_NAME, { autoIncrement: true });
   },
 
   // This method generates the key for the indexedDB object storage.
   principalToKey: function(aPrincipal) {
-    return aPrincipal.cookieJar + '|' + aPrincipal.origin;
+    return aPrincipal.origin;
   },
 
   // Add a task to the _registrations map and create the timer if it's needed.
   addRegistration: function(aObj) {
     debug('addRegistration');
 
     let key = this.principalToKey(aObj.principal);
     if (!(key in this._registrations)) {
--- a/dom/system/gonk/SystemWorkerManager.cpp
+++ b/dom/system/gonk/SystemWorkerManager.cpp
@@ -114,17 +114,17 @@ SystemWorkerManager::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   mShutdown = true;
 
   ShutdownAutoMounter();
 
 #ifdef MOZ_B2G_RIL
-  RilWorker::Shutdown();
+  RilConsumer::Shutdown();
 #endif
 
   nsCOMPtr<nsIWifi> wifi(do_QueryInterface(mWifiWorker));
   if (wifi) {
     wifi->Shutdown();
     wifi = nullptr;
   }
   mWifiWorker = nullptr;
@@ -196,17 +196,17 @@ SystemWorkerManager::RegisterRilWorker(u
 
   WorkerCrossThreadDispatcher *wctd =
     GetWorkerCrossThreadDispatcher(aCx, aWorker);
   if (!wctd) {
     NS_WARNING("Failed to GetWorkerCrossThreadDispatcher for ril");
     return NS_ERROR_FAILURE;
   }
 
-  return RilWorker::Register(aClientId, wctd);
+  return RilConsumer::Register(aClientId, wctd);
 #endif // MOZ_B2G_RIL
 }
 
 nsresult
 SystemWorkerManager::InitWifi(JSContext *cx)
 {
   nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kWifiWorkerCID);
   NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE);
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -6,39 +6,51 @@
 
 /**
  * A collection of static utility methods that are only exposed to Chrome. This
  * interface is not exposed in workers, while ThreadSafeChromeUtils is.
  */
 [ChromeOnly, Exposed=(Window,System)]
 interface ChromeUtils : ThreadSafeChromeUtils {
   /**
-   * A helper that converts OriginAttributesDictionary to cookie jar opaque
-   * identfier.
-   *
-   * @param originAttrs       The originAttributes from the caller.
-   */
-  static ByteString
-  originAttributesToCookieJar(optional OriginAttributesDictionary originAttrs);
-
-  /**
    * A helper that converts OriginAttributesDictionary to a opaque suffix string.
    *
    * @param originAttrs       The originAttributes from the caller.
    */