merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 12 Feb 2015 15:33:22 +0100
changeset 246310 81f979b17fbdc4ffdc2c0657c8d8b6333383f9a5
parent 246224 b5cfc9fad354b387f102c3a68b869d7c9c48d3ff (current diff)
parent 246309 f310a80db30d0a3d901f1e09c5fa0dc5af68597b (diff)
child 246341 f969dd9c7a050478bd76470f7244ada0d7c279d0
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
merge mozilla-inbound to mozilla-central a=merge
testing/web-platform/meta/media-source/mediasource-duration-boundaryconditions.html.ini
new file mode 100644
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,14 @@
+# This Makefile is used as a shim to aid people with muscle memory
+# so that they can type "make".
+#
+# This file and all of its targets should not be used by anything important.
+
+all: build
+
+build:
+	./mach build
+
+clean:
+	./mach clobber
+
+.PHONY: all build clean
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -147,16 +147,18 @@ interface nsIAccessible : nsISupports
    *                 constants)
    * @param aExtraState - the second bit field
    *                      (see nsIAccessibleStates::EXT_STATE_* constants)
    */
   void getState(out unsigned long aState, out unsigned long aExtraState);
 
   /**
    * Help text associated with node
+   *
+   * @note As of now, this just returns empty string.
    */
   readonly attribute AString help;
 
   /**
    * Focused accessible child of node
    */
   readonly attribute nsIAccessible focusedChild;
 
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -77,16 +77,29 @@ DocAccessibleChild::RecvName(const uint6
   if (!acc)
     return true;
 
   acc->Name(*aName);
   return true;
 }
 
 bool
+DocAccessibleChild::RecvValue(const uint64_t& aID, nsString* aValue)
+{
+  Accessible* acc =
+    mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
+  if (!acc) {
+    return true;
+  }
+
+  acc->Value(*aValue);
+  return true;
+}
+
+bool
 DocAccessibleChild::RecvDescription(const uint64_t& aID, nsString* aDesc)
 {
   Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
   if (!acc)
     return true;
 
   acc->Description(*aDesc);
   return true;
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -38,16 +38,18 @@ public:
    */
   virtual bool RecvState(const uint64_t& aID, uint64_t* aState) MOZ_OVERRIDE;
 
   /*
    * Get the name for the accessible with given id.
    */
   virtual bool RecvName(const uint64_t& aID, nsString* aName) MOZ_OVERRIDE;
 
+  virtual bool RecvValue(const uint64_t& aID, nsString* aValue) MOZ_OVERRIDE;
+  
   /*
    * Get the description for the accessible with given id.
    */
   virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE;
 
   virtual bool RecvAttributes(const uint64_t& aID, nsTArray<Attribute> *aAttributes) MOZ_OVERRIDE;
   virtual bool RecvTextSubstring(const uint64_t& aID,
                                  const int32_t& aStartOffset,
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -43,16 +43,17 @@ parent:
    */
   Event(uint64_t aID, uint32_t type);
   ShowEvent(ShowEventData data);
   HideEvent(uint64_t aRootID);
 
 child:
   prio(high) sync State(uint64_t aID) returns(uint64_t states);
   prio(high) sync Name(uint64_t aID) returns(nsString name);
+  prio(high) sync Value(uint64_t aID) returns(nsString value);
   prio(high) sync Description(uint64_t aID) returns(nsString desc);
   prio(high) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
-prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
-                              aEndOffset) returns(nsString aText);
+  prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
+                                aEndOffset) returns(nsString aText);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -59,16 +59,22 @@ ProxyAccessible::State() const
 
 void
 ProxyAccessible::Name(nsString& aName) const
 {
   unused << mDoc->SendName(mID, &aName);
 }
 
 void
+ProxyAccessible::Value(nsString& aValue) const
+{
+  unused << mDoc->SendValue(mID, &aValue);
+}
+
+void
 ProxyAccessible::Description(nsString& aDesc) const
 {
   unused << mDoc->SendDescription(mID, &aDesc);
 }
 
 void
 ProxyAccessible::Attributes(nsTArray<Attribute> *aAttrs) const
 {
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -64,16 +64,21 @@ public:
    */
   uint64_t State() const;
 
   /*
    * Set aName to the name of the proxied accessible.
    */
   void Name(nsString& aName) const;
 
+  /*
+   * Set aValue to the value of the proxied accessible.
+   */
+  void Value(nsString& aValue) const;
+
   /**
    * Set aDesc to the description of the proxied accessible.
    */
   void Description(nsString& aDesc) const;
 
   /**
    * Get the set of attributes on the proxied accessible.
    */
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -230,16 +230,17 @@ DEFAULT_TEST_PREFS = {
     # Assume the about:newtab page's intro panels have been shown to not depend on
     # which test runs first and happens to open about:newtab
     'browser.newtabpage.introShown': True,
     # Disable useragent updates.
     'general.useragent.updates.enabled': False,
     'dom.mozApps.debug': True,
     'dom.apps.customization.enabled': True,
     'media.eme.enabled': True,
+    'media.eme.apiVisible': True,
     # Don't forceably kill content processes after a timeout
     'dom.ipc.tabs.shutdownTimeoutSecs': 0,
     # Don't show the search first run UI by default
     'browser.search.highlightCount': 0,
     'general.useragent.locale': "en-US",
     'intl.locale.matchOS': "en-US",
     'dom.indexedDB.experimental': True
 }
--- a/addon-sdk/source/test/preferences/test.json
+++ b/addon-sdk/source/test/preferences/test.json
@@ -38,14 +38,15 @@
   "network.http.bypass-cachelock-threshold": 200000,
   "geo.provider.testing": true,
   "browser.pagethumbnails.capturing_disabled": true,
   "browser.download.panel.shown": true,
   "general.useragent.updates.enabled": false,
   "dom.mozApps.debug": true,
   "dom.apps.customization.enabled": true,
   "media.eme.enabled": true,
+  "media.eme.apiVisible": true,
   "dom.ipc.tabs.shutdownTimeoutSecs": 0,
   "browser.search.highlightCount": 0,
   "general.useragent.locale": "en-US",
   "intl.locale.matchOS": "en-US",
   "dom.indexedDB.experimental": true
 }
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -342,17 +342,16 @@ var shell = {
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
-    this.contentBrowser.addEventListener('mozbrowsertouchcarettap', this, true);
 
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
@@ -369,17 +368,16 @@ var shell = {
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
-    this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
   },
 
   // If this key event represents a hardware button which needs to be send as
   // a message, broadcasts it with the message set to 'xxx-button-press' or
   // 'xxx-button-release'.
@@ -464,22 +462,16 @@ var shell = {
         this.notifyContentStart();
        break;
       case 'mozbrowserscrollviewchange':
         this.sendChromeEvent({
           type: 'scrollviewchange',
           detail: evt.detail,
         });
         break;
-      case 'mozbrowsertouchcarettap':
-        this.sendChromeEvent({
-          type: 'touchcarettap',
-          detail: evt.detail,
-        });
-        break;
       case 'mozbrowserselectionstatechanged':
         // The mozbrowserselectionstatechanged event, may have crossed the chrome-content boundary.
         // This event always dispatch to shell.js. But the offset we got from this event is
         // based on tab's coordinate. So get the actual offsets between shell and evt.target.
         let elt = evt.target;
         let win = elt.ownerDocument.defaultView;
         let offsetX = win.mozInnerScreenX - window.mozInnerScreenX;
         let offsetY = win.mozInnerScreenY - window.mozInnerScreenY;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1189,16 +1189,18 @@ pref("security.sandbox.windows.log", fal
 
 // Controls whether and how the Windows NPAPI plugin process is sandboxed.
 // To get a different setting for a particular plugin replace "default", with
 // the plugin's nice file name, see: nsPluginTag::GetNiceFileName.
 // On windows these levels are:
 // 0 - no sandbox
 // 1 - sandbox with USER_NON_ADMIN access token level
 // 2 - a more strict sandbox, which might cause functionality issues
+// 3 - the strongest settings we seem to be able to use without breaking
+//     everything, but will definitely cause some functionality restrictions
 pref("dom.ipc.plugins.sandbox-level.default", 0);
 pref("dom.ipc.plugins.sandbox-level.flash", 1);
 
 #if defined(MOZ_CONTENT_SANDBOX)
 // This controls whether the Windows content process sandbox is using a more
 // strict sandboxing policy.  This will require a restart.
 pref("security.sandbox.windows.content.moreStrict", false);
 
@@ -1780,18 +1782,20 @@ pref("identity.fxaccounts.migrateToDevEd
 // On GTK, we now default to showing the menubar only when alt is pressed:
 #ifdef MOZ_WIDGET_GTK
 pref("ui.key.menuAccessKeyFocuses", true);
 #endif
 
 // Encrypted media extensions.
 #ifdef RELEASE_BUILD
 pref("media.eme.enabled", false);
+pref("media.eme.apiVisible", false);
 #else
 pref("media.eme.enabled", true);
+pref("media.eme.apiVisible", true);
 #endif
 
 // Play with different values of the decay time and get telemetry,
 // 0 means to randomize (and persist) the experiment value in users' profiles,
 // -1 means no experiment is run and we use the preferred value for frecency (6h)
 pref("browser.cache.frecency_experiment", 0);
 
 pref("browser.translation.detectLanguage", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3809,24 +3809,24 @@ function FillHistoryMenu(aParent) {
   var tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
   var tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
   var tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");
 
   for (var j = end - 1; j >= start; j--) {
     let item = document.createElement("menuitem");
     let entry = sessionHistory.getEntryAtIndex(j, false);
     let uri = entry.URI.spec;
-    let uriCopy = BrowserUtils.makeURI(uri);
+    let entryURI = BrowserUtils.makeURIFromCPOW(entry.URI);
 
     item.setAttribute("uri", uri);
     item.setAttribute("label", entry.title || uri);
     item.setAttribute("index", j);
 
     if (j != index) {
-      PlacesUtils.favicons.getFaviconURLForPage(uriCopy, function (aURI) {
+      PlacesUtils.favicons.getFaviconURLForPage(entryURI, function (aURI) {
         if (aURI) {
           let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
           iconURL = PlacesUtils.getImageURLForResolution(window, iconURL);
           item.style.listStyleImage = "url(" + iconURL + ")";
         }
       });
     }
 
@@ -5813,19 +5813,19 @@ function handleLinkClick(event, href, li
       sm.checkSameOriginURI(referrerURI, targetURI, false);
       persistAllowMixedContentInChildTab = true;
     }
     catch (e) { }
   }
 
   urlSecurityCheck(href, doc.nodePrincipal);
   let params = { charset: doc.characterSet,
-                 allowMixedContent: persistAllowMixedContentInChildTab };
-  if (!BrowserUtils.linkHasNoReferrer(linkNode))
-    params.referrerURI = referrerURI;
+                 allowMixedContent: persistAllowMixedContentInChildTab,
+                 referrerURI: referrerURI,
+                 noReferrer: BrowserUtils.linkHasNoReferrer(linkNode) };
   openLinkIn(href, where, params);
   event.preventDefault();
   return true;
 }
 
 function middleMousePaste(event) {
   let clipboard = readFromClipboard();
   if (!clipboard)
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -851,19 +851,19 @@ nsContextMenu.prototype = {
     if (!editable) {
       return false;
     }
     // Otherwise make sure that nothing in the parent chain disables spellchecking
     return aNode.spellcheck;
   },
 
   _openLinkInParameters : function (doc, extra) {
-    let params = { charset: doc.characterSet };
-    if (!BrowserUtils.linkHasNoReferrer(this.link))
-      params.referrerURI = document.documentURIObject;
+    let params = { charset: doc.characterSet,
+                   referrerURI: doc.documentURIObject,
+                   noReferrer: BrowserUtils.linkHasNoReferrer(this.link) };
     for (let p in extra)
       params[p] = extra[p];
     return params;
   },
 
   // Open linked-to URL in a new window.
   openLink : function () {
     var doc = this.target.ownerDocument;
@@ -1193,26 +1193,27 @@ nsContextMenu.prototype = {
         }
 
         let extHelperAppSvc = 
           Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
           getService(Ci.nsIExternalHelperAppService);
         let channel = aRequest.QueryInterface(Ci.nsIChannel);
         this.extListener =
           extHelperAppSvc.doContent(channel.contentType, aRequest, 
-                                    doc.defaultView, true, window);
+                                    null, true, window);
         this.extListener.onStartRequest(aRequest, aContext);
       }, 
 
       onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext, 
                                                        aStatusCode) {
         if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
           // do it the old fashioned way, which will pick the best filename
           // it can without waiting.
-          saveURL(linkURL, linkText, dialogTitle, bypassCache, false, doc.documentURIObject, doc);
+          saveURL(linkURL, linkText, dialogTitle, bypassCache, false,
+                  BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
         }
         if (this.extListener)
           this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
       },
 
       onDataAvailable: function saveLinkAs_onDataAvailable(aRequest, aContext,
                                                            aInputStream,
                                                            aOffset, aCount) {
@@ -1247,39 +1248,39 @@ nsContextMenu.prototype = {
         return;
       }
     }
 
     // set up a channel to do the saving
     var ioService = Cc["@mozilla.org/network/io-service;1"].
                     getService(Ci.nsIIOService);
     var channel = ioService.newChannelFromURI2(makeURI(linkURL),
-                                               doc,
-                                               null, // aLoadingPrincipal
+                                               null, // aLoadingNode
+                                               this.principal, // aLoadingPrincipal
                                                null, // aTriggeringPrincipal
                                                Ci.nsILoadInfo.SEC_NORMAL,
                                                Ci.nsIContentPolicy.TYPE_OTHER);
     if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
-      let docIsPrivate = PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
+      let docIsPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
       channel.setPrivate(docIsPrivate);
     }
     channel.notificationCallbacks = new callbacks();
 
     let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
 
     if (bypassCache)
       flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
 
     if (channel instanceof Ci.nsICachingChannel)
       flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
 
     channel.loadFlags |= flags;
 
     if (channel instanceof Ci.nsIHttpChannel) {
-      channel.referrer = doc.documentURIObject;
+      channel.referrer = BrowserUtils.makeURIFromCPOW(doc.documentURIObject);
       if (channel instanceof Ci.nsIHttpChannelInternal)
         channel.forceAllowThirdPartyCookie = true;
     }
 
     // fallback to the old way if we don't see the headers quickly 
     var timeToWait = 
       gPrefService.getIntPref("browser.download.saveLinkAsFilenameTimeout");
     var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -1311,22 +1312,22 @@ nsContextMenu.prototype = {
   },
 
   // Save URL of the clicked upon image, video, or audio.
   saveMedia: function() {
     var doc =  this.target.ownerDocument;
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
-                   true, false, doc.documentURIObject, doc);
+                   true, false, BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
     }
     else if (this.onImage) {
       urlSecurityCheck(this.mediaURL, this.principal);
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
-                   false, doc.documentURIObject, doc);
+                   false, BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
     }
     else if (this.onVideo || this.onAudio) {
       urlSecurityCheck(this.mediaURL, this.principal);
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
     }
   },
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1362,46 +1362,49 @@
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
             var aForceNotRemote;
+            var aNoReferrer;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
               aForceNotRemote       = params.forceNotRemote;
+              aNoReferrer           = params.noReferrer;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   charset: aCharset,
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
-                                  forceNotRemote: aForceNotRemote });
+                                  forceNotRemote: aForceNotRemote,
+                                  noReferrer: aNoReferrer });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1671,30 +1674,32 @@
         <body>
           <![CDATA[
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
+            var aNoReferrer;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
               aForceNotRemote       = params.forceNotRemote;
+              aNoReferrer           = params.noReferrer;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -1821,17 +1826,19 @@
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
               }
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               if (aAllowMixedContent)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
               try {
-                b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
+                b.loadURIWithFlags(aURI, flags,
+                                   aNoReferrer ? null : aReferrerURI,
+                                   aCharset, aPostData);
               } catch (ex) {
                 Cu.reportError(ex);
               }
             }
 
             // We start our browsers out as inactive, and then maintain
             // activeness in the tab switcher.
             b.docShellIsActive = false;
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -218,16 +218,17 @@ function openLinkIn(url, where, params) 
   var aRelatedToCurrent     = params.relatedToCurrent;
   var aAllowMixedContent    = params.allowMixedContent;
   var aInBackground         = params.inBackground;
   var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
   var aInitiatingDoc        = params.initiatingDoc;
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
+  var aNoReferrer           = params.noReferrer;
 
   if (where == "save") {
     if (!aInitiatingDoc) {
       Components.utils.reportError("openUILink/openLinkIn was called with " +
         "where == 'save' but without initiatingDoc.  See bug 814264.");
       return;
     }
     saveURL(url, null, null, true, null, aReferrerURI, aInitiatingDoc);
@@ -335,17 +336,18 @@ function openLinkIn(url, where, params) 
     w.gBrowser.loadOneTab(url, {
       referrerURI: aReferrerURI,
       charset: aCharset,
       postData: aPostData,
       inBackground: loadInBackground,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipTabAnimation,
-      allowMixedContent: aAllowMixedContent
+      allowMixedContent: aAllowMixedContent,
+      noReferrer: aNoReferrer
     });
     break;
   }
 
   w.gBrowser.selectedBrowser.focus();
 
   if (!loadInBackground && w.isBlankPageURL(url)) {
     w.focusAndSelectUrlBar();
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -63,19 +63,19 @@ let ContentClick = {
 
     // This part is based on handleLinkClick.
     var where = window.whereToOpenLink(json);
     if (where == "current")
       return false;
 
     // Todo(903022): code for where == save
 
-    let params = { charset: browser.characterSet };
-    if (!json.noReferrer)
-      params.referrerURI = browser.documentURI;
+    let params = { charset: browser.characterSet,
+                   referrerURI: browser.documentURI,
+                   noReferrer: json.noReferrer };
     window.openLinkIn(json.href, where, params);
 
     // Mark the page as a user followed link.  This is done so that history can
     // distinguish automatic embed visits from user activated ones.  For example
     // pages loaded in frames are embed visits and lost with the session, while
     // visits across frames should be preserved.
     try {
       if (!PrivateBrowsingUtils.isWindowPrivate(window))
--- a/configure.in
+++ b/configure.in
@@ -7956,18 +7956,18 @@ dnl Graphics checks.
 dnl ========================================================
 
 if test "${OS_TARGET}" = "WINNT" -o \
         "${OS_ARCH}" = "Darwin" -o \
         "${MOZ_WIDGET_TOOLKIT}" = "android" -o \
         "${MOZ_WIDGET_TOOLKIT}" = "gonk" -o \
         "${MOZ_WIDGET_TOOLKIT}" = "gtk2" -o \
         "${MOZ_WIDGET_TOOLKIT}" = "gtk3"; then
-    case "${target_cpu}" in
-    i*86*|x86_64|arm)
+    case "${CPU_ARCH}" in
+    x86|x86_64|arm)
         MOZ_ENABLE_SKIA=1
         ;;
     *)
         MOZ_ENABLE_SKIA=
         ;;
     esac
 else
 MOZ_ENABLE_SKIA=
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2603,16 +2603,21 @@ Navigator::RequestMediaKeySystemAccess(c
                                        ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   nsRefPtr<Promise> p = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  if (!Preferences::GetBool("media.eme.enabled", false)) {
+    p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return p.forget();
+  }
+
   if (aKeySystem.IsEmpty() ||
       (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
     p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return p.forget();
   }
 
   if (!MediaKeySystemAccess::IsKeySystemSupported(aKeySystem)) {
     p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -219,17 +219,17 @@ NS_GetContentList(nsINode* aRootNode,
     PL_DHashTableInit(&gContentListHashTable, &hash_table_ops,
                       sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gContentListHashTable.IsInitialized()) {
     entry = static_cast<ContentListHashEntry *>
-                       (PL_DHashTableAdd(&gContentListHashTable, &hashKey));
+      (PL_DHashTableAdd(&gContentListHashTable, &hashKey, fallible));
     if (entry)
       list = entry->mContentList;
   }
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
@@ -327,18 +327,17 @@ GetFuncStringContentList(nsINode* aRootN
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable.IsInitialized()) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
-                       (PL_DHashTableAdd(&gFuncStringContentListHashTable,
-                                         &hashKey));
+      (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey, fallible));
     if (entry) {
       list = entry->mContentList;
 #ifdef DEBUG
       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
 #endif
     }
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -55,16 +55,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/TextEvents.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsBindingManager.h"
+#include "nsCaret.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsCOMPtr.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentDLF.h"
 #include "nsContentList.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCPrefetchService.h"
@@ -3957,17 +3958,17 @@ nsContentUtils::GetListenerManagerForNod
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-               (PL_DHashTableAdd(&sEventListenerManagersHash, aNode));
+      (PL_DHashTableAdd(&sEventListenerManagersHash, aNode, fallible));
 
   if (!entry) {
     return nullptr;
   }
 
   if (!entry->mListenerManager) {
     entry->mListenerManager = new EventListenerManager(aNode);
 
@@ -6793,16 +6794,49 @@ nsContentUtils::GetSelectionInTextContro
     }
   }
 
   // Make sure aOutStartOffset <= aOutEndOffset.
   aOutStartOffset = std::min(anchorOffset, focusOffset);
   aOutEndOffset = std::max(anchorOffset, focusOffset);
 }
 
+/* static */
+nsRect
+nsContentUtils::GetSelectionBoundingRect(Selection* aSel)
+{
+  nsRect res;
+  // Bounding client rect may be empty after calling GetBoundingClientRect
+  // when range is collapsed. So we get caret's rect when range is
+  // collapsed.
+  if (aSel->IsCollapsed()) {
+    nsIFrame* frame = nsCaret::GetGeometry(aSel, &res);
+    if (frame) {
+      nsIFrame* relativeTo =
+        nsLayoutUtils::GetContainingBlockForClientRect(frame);
+      res = nsLayoutUtils::TransformFrameRectToAncestor(frame, res, relativeTo);
+    }
+  } else {
+    int32_t rangeCount = aSel->GetRangeCount();
+    nsLayoutUtils::RectAccumulator accumulator;
+    for (int32_t idx = 0; idx < rangeCount; ++idx) {
+      nsRange* range = aSel->GetRangeAt(idx);
+      nsRange::CollectClientRects(&accumulator, range,
+                                  range->GetStartParent(), range->StartOffset(),
+                                  range->GetEndParent(), range->EndOffset(),
+                                  true, false);
+    }
+    res = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
+      accumulator.mResultRect;
+  }
+
+  return res;
+}
+
+
 nsIEditor*
 nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext)
 {
   nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
   bool isEditable;
   if (!docShell ||
       NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
     return nullptr;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2172,16 +2172,24 @@ public:
    * @param aOutEndOffset   Output end offset
    */
   static void GetSelectionInTextControl(mozilla::dom::Selection* aSelection,
                                         Element* aRoot,
                                         int32_t& aOutStartOffset,
                                         int32_t& aOutEndOffset);
 
   /**
+   * Takes a selection, and return selection's bounding rect which is relative
+   * to root frame.
+   *
+   * @param aSel      Selection to check
+   */
+  static nsRect GetSelectionBoundingRect(mozilla::dom::Selection* aSel);
+
+  /**
    * Takes a frame for anonymous content within a text control (<input> or
    * <textarea>), and returns an offset in the text content, adjusted for a
    * trailing <br> frame.
    *
    * @param aOffsetFrame      Frame for the text content in which the offset
    *                          lies
    * @param aOffset           Offset as calculated by GetContentOffsetsFromPoint
    * @param aOutOffset        Output adjusted offset
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4002,19 +4002,18 @@ nsDocument::SetSubDocumentFor(Element* a
 
       mSubDocuments = PL_NewDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
       if (!mSubDocuments) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
     }
 
     // Add a mapping to the hash table
-    SubDocMapEntry *entry =
-      static_cast<SubDocMapEntry*>
-                 (PL_DHashTableAdd(mSubDocuments, aElement));
+    SubDocMapEntry *entry = static_cast<SubDocMapEntry*>
+      (PL_DHashTableAdd(mSubDocuments, aElement, fallible));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (entry->mSubDocument) {
       entry->mSubDocument->SetParentDocument(nullptr);
 
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -225,17 +225,17 @@ nsPropertyTable::SetPropertyInternal(nsP
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-                                           (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject));
+    (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject, fallible));
   if (!entry)
     return NS_ERROR_OUT_OF_MEMORY;
   // A nullptr entry->key is the sign that the entry has just been allocated
   // for us.  If it's non-nullptr then we have an existing entry.
   if (entry->key) {
     if (aOldValue)
       *aOldValue = entry->value;
     else if (propertyList->mDtorFunc)
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -135,19 +135,18 @@ nsScriptNameSpaceManager::~nsScriptNameS
   }
   MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
 }
 
 nsGlobalNameStruct *
 nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey,
                                     const char16_t **aClassName)
 {
-  GlobalNameMapEntry *entry =
-    static_cast<GlobalNameMapEntry *>
-               (PL_DHashTableAdd(aTable, aKey));
+  GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>
+    (PL_DHashTableAdd(aTable, aKey, fallible));
 
   if (!entry) {
     return nullptr;
   }
 
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/file_null_baseuri.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Bug 1121857 - document.baseURI should not get blocked if baseURI is null</title>
+  </head>
+  <body>
+  <script type="text/javascript">
+    // check the initial base-uri
+    window.parent.postMessage({baseURI: document.baseURI, test: "initial_base_uri"}, "*");
+
+    // append a child and check the base-uri
+    var baseTag = document.head.appendChild(document.createElement('base'));
+    baseTag.href = 'http://www.base-tag.com';
+    window.parent.postMessage({baseURI: document.baseURI, test: "changed_base_uri"}, "*");
+
+    // remove the child and check that the base-uri is back to the initial one
+    document.head.remove(baseTag);
+    window.parent.postMessage({baseURI: document.baseURI, test: "initial_base_uri"}, "*");
+  </script>
+</body>
+</html>
--- a/dom/base/test/csp/mochitest.ini
+++ b/dom/base/test/csp/mochitest.ini
@@ -95,16 +95,17 @@ support-files =
   file_redirect_report.sjs
   file_subframe_run_js_if_allowed.html
   file_subframe_run_js_if_allowed.html^headers^
   file_leading_wildcard.html
   file_multi_policy_injection_bypass.html
   file_multi_policy_injection_bypass.html^headers^
   file_multi_policy_injection_bypass_2.html
   file_multi_policy_injection_bypass_2.html^headers^
+  file_null_baseuri.html
   file_form-action.html
   file_worker_redirect.html
   file_worker_redirect.sjs
   file_csp_referrerdirective.html
   referrerdirective.sjs
 
 [test_base-uri.html]
 [test_connect-src.html]
@@ -146,11 +147,12 @@ skip-if = buildapp == 'b2g' # intermitte
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_303_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_307_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_subframe_run_js_if_allowed.html]
 [test_leading_wildcard.html]
 [test_multi_policy_injection_bypass.html]
+[test_null_baseuri.html]
 [test_CSP_referrerdirective.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_worker_redirect.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/csp/test_null_baseuri.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1121857 - document.baseURI should not get blocked if baseURI is null</title>
+  <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="visibility: hidden">
+    <iframe style="width:100%;" id="testframe"></iframe>
+  </div>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Creating a 'base' element and appending that element
+ * to document.head. After setting baseTag.href and finally
+ * removing the created element from the head, the baseURI
+ * should be the inital baseURI of the page.
+ */
+
+const TOTAL_TESTS = 3;
+var test_counter = 0;
+
+// a postMessage handler to communicate the results back to the parent.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+  // make sure the base-uri before and after the test is the initial base uri of the page
+  if (event.data.test === "initial_base_uri") {
+    ok(event.data.baseURI.startsWith("http://mochi.test"), "baseURI should be 'http://mochi.test'!");
+  }
+  // check that appending the child and setting the base tag actually affects the base-uri
+  else if (event.data.test === "changed_base_uri") {
+    ok(event.data.baseURI === "http://www.base-tag.com/", "baseURI should be 'http://www.base-tag.com'!");
+  }
+  // we shouldn't get here, but just in case, throw an error.
+  else {
+    ok(false, "unrecognized test!");
+  }
+
+  if (++test_counter === TOTAL_TESTS) {
+    SimpleTest.finish();
+  }
+}
+
+function startTest() {
+  var src = "file_csp_testserver.sjs";
+  // append the file that should be served
+  src += "?file=" + escape("tests/dom/base/test/csp/file_null_baseuri.html");
+  // using 'unsafe-inline' since we load the testcase using an inline script
+  // within file_null_baseuri.html
+  src += "&csp=" + escape("default-src * 'unsafe-inline';");
+
+  document.getElementById("testframe").src = src;
+}
+
+
+SimpleTest.waitForExplicitFinish();
+startTest();
+
+</script>
+</body>
+</html>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -392,17 +392,16 @@ class CGDOMJSClass(CGThing):
                       ${objectMoved} /* objectMovedOp */
                     },
                     {
                       nullptr, /* lookupProperty */
                       nullptr, /* defineProperty */
                       nullptr, /* getProperty */
                       nullptr, /* setProperty */
                       nullptr, /* getOwnPropertyDescriptor */
-                      nullptr, /* setPropertyAttributes */
                       nullptr, /* deleteProperty */
                       nullptr, /* watch */
                       nullptr, /* unwatch */
                       nullptr, /* getElements */
                       nullptr, /* enumerate */
                       JS_ObjectToOuterObject /* thisObject */
                     }
                     """,
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -167,22 +167,16 @@ BrowserElementChild.prototype = {
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('scrollviewchange',
                      this._ScrollViewChangeHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
-    addEventListener('touchcarettap',
-                     this._touchCaretTapHandler.bind(this),
-                     /* useCapture = */ true,
-                     /* wantsUntrusted = */ false);
-
-
     // This listens to unload events from our message manager, but /not/ from
     // the |content| window.  That's because the window's unload event doesn't
     // bubble, and we're not using a capturing listener.  If we'd used
     // useCapture == true, we /would/ hear unload events from the window, which
     // is not what we want!
     addEventListener('unload',
                      this._unloadHandler.bind(this),
                      /* useCapture = */ false,
@@ -572,21 +566,16 @@ BrowserElementChild.prototype = {
 
     if (lang) {
       meta.lang = lang;
     }
 
     sendAsyncMsg('metachange', meta);
   },
 
-  _touchCaretTapHandler: function(e) {
-    e.stopPropagation();
-    sendAsyncMsg('touchcarettap');
-  },
-
   _ScrollViewChangeHandler: function(e) {
     e.stopPropagation();
     let detail = {
       state: e.state,
       scrollX: e.scrollX,
       scrollY: e.scrollY,
     };
     sendAsyncMsg('scrollviewchange', detail);
@@ -620,16 +609,18 @@ BrowserElementChild.prototype = {
       // The collapsed SelectionStateChanged event is unnecessary to dispatch,
       // bypass this event by default, but here comes some exceptional cases
       if (isCollapsed) {
         if (isMouseUp && canPaste) {
           // Always dispatch to support shortcut mode which can paste previous
           // copied content easily
         } else if (e.states.indexOf('blur') == 0) {
           // Always dispatch to notify the blur for the focus content
+        } else if (e.states.indexOf('taponcaret') == 0) {
+          // Always dispatch to notify the caret be touched
         } else {
           return;
         }
       }
     }
 
     // If we select something and selection range is visible, we cache current
     // event's target to selectionStateChangedTarget.
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -194,17 +194,16 @@ BrowserElementParent.prototype = {
       "fullscreen-origin-change": this._remoteFullscreenOriginChange,
       "rollback-fullscreen": this._remoteFrameFullscreenReverted,
       "exit-fullscreen": this._exitFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionstatechanged": this._handleSelectionStateChanged,
       "scrollviewchange": this._handleScrollViewChange,
-      "touchcarettap": this._handleTouchCaretTap
     };
 
     let mmSecuritySensitiveCalls = {
       "showmodalprompt": this._handleShowModalPrompt,
       "contextmenu": this._fireCtxMenuEvent,
       "securitychange": this._fireEventFromMsg,
       "locationchange": this._fireEventFromMsg,
       "iconchange": this._fireEventFromMsg,
@@ -434,22 +433,16 @@ BrowserElementParent.prototype = {
   },
 
   _handleScrollViewChange: function(data) {
     let evt = this._createEvent("scrollviewchange", data.json,
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
 
-  _handleTouchCaretTap: function(data) {
-    let evt = this._createEvent("touchcarettap", data.json,
-                                /* cancelable = */ false);
-    this._frameElement.dispatchEvent(evt);
-  },
-
   _createEvent: function(evtName, detail, cancelable) {
     // This will have to change if we ever want to send a CustomEvent with null
     // detail.  For now, it's OK.
     if (detail !== undefined && detail !== null) {
       detail = Cu.cloneInto(detail, this._window);
       return new this._window.CustomEvent('mozbrowser' + evtName,
                                           { bubbles: true,
                                             cancelable: cancelable,
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -1,45 +1,47 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIDocument;
+interface nsIPrincipal;
 interface nsIURI;
 
 [builtinclass, uuid(d4367ffe-e435-4195-95f8-0a51b1bbfdfb)]
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   [noscript] void UnregisterSucceeded(in bool aState);
   [noscript] void UnregisterFailed();
 };
 
-[builtinclass, uuid(430f02bf-63d0-44ab-9a59-7bd49c608949)]
+[builtinclass, uuid(861b55e9-d6ac-47cf-a528-8590e9b44de6)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
    * Returns a Promise.
    */
   nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
 
   /**
    * Unregister an existing ServiceWorker registration for `aScope`.
    * It keeps aCallback alive until the operation is concluded.
    */
-  void unregister(in nsIServiceWorkerUnregisterCallback aCallback,
+  void unregister(in nsIPrincipal aPrincipal,
+                  in nsIServiceWorkerUnregisterCallback aCallback,
                   in DOMString aScope);
 
   // Returns a Promise
   nsISupports getRegistrations(in nsIDOMWindow aWindow);
 
   // Returns a Promise
   nsISupports getRegistration(in nsIDOMWindow aWindow, in DOMString aScope);
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -14,16 +14,17 @@ include protocol PContentPermissionReque
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
 include PContentPermission;
+include ServiceWorkerRegistrarTypes;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using LayoutDeviceIntRect from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
@@ -60,16 +61,21 @@ namespace dom {
 
 struct NativeKeyBinding
 {
   CommandInt[] singleLineCommands;
   CommandInt[] multiLineCommands;
   CommandInt[] richTextCommands;
 };
 
+struct BrowserConfiguration
+{
+  ServiceWorkerRegistrationData[] serviceWorkerRegistrations;
+};
+
 union MaybeNativeKeyBinding
 {
   NativeKeyBinding;
   void_t;
 };
 
 struct ShowInfo
 {
@@ -466,17 +472,17 @@ child:
     Show(nsIntSize size,
          ShowInfo info,
          ScrollingBehavior scrolling,
          TextureFactoryIdentifier textureFactoryIdentifier,
          uint64_t layersId,
          nullable PRenderFrame renderFrame,
          bool parentIsActive);
 
-    LoadURL(nsCString uri);
+    LoadURL(nsCString uri, BrowserConfiguration config);
 
     CacheFileDescriptor(nsString path, FileDescriptor fd);
 
     UpdateDimensions(nsIntRect rect, nsIntSize size, ScreenOrientation orientation,
                      nsIntPoint chromeDisp) compress;
 
     UpdateFrame(FrameMetrics frame);
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -10,23 +10,24 @@
 
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/IntentionalCrash.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/plugins/PluginWidgetChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
-#include "mozilla/layers/ActiveElementManager.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
@@ -94,78 +95,34 @@
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
+using namespace mozilla::dom::workers;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::docshell;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
 
 NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
 
 static const CSSSize kDefaultViewportSize(980, 480);
 
 static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
-static int32_t sActiveDurationMs = 10;
-static bool sActiveDurationMsSet = false;
-
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
 
-class TabChild::DelayedFireSingleTapEvent MOZ_FINAL : public nsITimerCallback
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  DelayedFireSingleTapEvent(nsIWidget* aWidget,
-                            LayoutDevicePoint& aPoint,
-                            nsITimer* aTimer)
-    : mWidget(do_GetWeakReference(aWidget))
-    , mPoint(aPoint)
-    // Hold the reference count until we are called back.
-    , mTimer(aTimer)
-  {
-  }
-
-  NS_IMETHODIMP Notify(nsITimer*) MOZ_OVERRIDE
-  {
-    nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget);
-    if (widget) {
-      APZCCallbackHelper::FireSingleTapEvent(mPoint, widget);
-    }
-    mTimer = nullptr;
-    return NS_OK;
-  }
-
-  void ClearTimer() {
-    mTimer = nullptr;
-  }
-
-private:
-  ~DelayedFireSingleTapEvent()
-  {
-  }
-
-  nsWeakPtr mWidget;
-  LayoutDevicePoint mPoint;
-  nsCOMPtr<nsITimer> mTimer;
-};
-
-NS_IMPL_ISUPPORTS(TabChild::DelayedFireSingleTapEvent,
-                  nsITimerCallback)
-
 class TabChild::DelayedFireContextMenuEvent MOZ_FINAL : public nsITimerCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit DelayedFireContextMenuEvent(TabChild* tabChild)
     : mTabChild(tabChild)
   {
@@ -532,28 +489,26 @@ bool
 TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics)
 {
   MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID);
 
   if (aFrameMetrics.GetIsRoot()) {
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
     if (APZCCallbackHelper::HasValidPresShellId(utils, aFrameMetrics)) {
       mLastRootMetrics = ProcessUpdateFrame(aFrameMetrics);
-      APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, mLastRootMetrics);
       return true;
     }
   } else {
     // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe.
     // This requires special handling.
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(
                                       aFrameMetrics.GetScrollId());
     if (content) {
       FrameMetrics newSubFrameMetrics(aFrameMetrics);
       APZCCallbackHelper::UpdateSubFrame(content, newSubFrameMetrics);
-      APZCCallbackHelper::UpdateCallbackTransform(aFrameMetrics, newSubFrameMetrics);
       return true;
     }
   }
   return true;
 }
 
 FrameMetrics
 TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
@@ -844,16 +799,46 @@ TabChild::Create(nsIContentChild* aManag
         return child.forget();
     }
 
     nsRefPtr<TabChild> iframe = new TabChild(aManager, aTabId,
                                              aContext, aChromeFlags);
     return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
 }
 
+class TabChildSetTargetAPZCCallback : public SetTargetAPZCCallback {
+public:
+  explicit TabChildSetTargetAPZCCallback(TabChild* aTabChild)
+    : mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
+  {}
+
+  void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const MOZ_OVERRIDE {
+    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
+      static_cast<TabChild*>(tabChild.get())->SendSetTargetAPZC(aInputBlockId, aTargets);
+    }
+  }
+
+private:
+  nsWeakPtr mTabChild;
+};
+
+class TabChildContentReceivedInputBlockCallback : public ContentReceivedInputBlockCallback {
+public:
+  explicit TabChildContentReceivedInputBlockCallback(TabChild* aTabChild)
+    : mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
+  {}
+
+  void Run(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, bool aPreventDefault) const MOZ_OVERRIDE {
+    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
+      static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
+    }
+  }
+private:
+  nsWeakPtr mTabChild;
+};
 
 TabChild::TabChild(nsIContentChild* aManager,
                    const TabId& aTabId,
                    const TabContext& aContext,
                    uint32_t aChromeFlags)
   : TabContext(aContext)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
@@ -863,37 +848,26 @@ TabChild::TabChild(nsIContentChild* aMan
   , mActivePointerId(-1)
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
-  , mPendingTouchPreventedResponse(false)
-  , mPendingTouchPreventedBlockId(0)
-  , mTouchEndCancelled(false)
-  , mEndTouchIsClick(false)
   , mIgnoreKeyPressEvent(false)
-  , mActiveElementManager(new ActiveElementManager())
+  , mSetTargetAPZCCallback(new TabChildSetTargetAPZCCallback(this))
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
   , mIPCOpen(true)
   , mParentIsActive(false)
 {
-  if (!sActiveDurationMsSet) {
-    Preferences::AddIntVarCache(&sActiveDurationMs,
-                                "ui.touch_activation.duration_ms",
-                                sActiveDurationMs);
-    sActiveDurationMsSet = true;
-  }
-
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
 }
 
 NS_IMETHODIMP
@@ -1142,16 +1116,19 @@ TabChild::Init()
   // XXX: ideally, we would set a chrome event handler earlier,
   // and all windows, even the root one, will use the docshell one.
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   nsCOMPtr<EventTarget> chromeHandler =
     do_QueryInterface(window->GetChromeEventHandler());
   docShell->SetChromeEventHandler(chromeHandler);
 
+  mAPZEventState = new APZEventState(mWidget,
+      new TabChildContentReceivedInputBlockCallback(this));
+
   return NS_OK;
 }
 
 void
 TabChild::NotifyTabContextUpdated()
 {
     nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
     MOZ_ASSERT(docShell);
@@ -1561,42 +1538,31 @@ TabChild::ProvideWindowCommon(nsIDOMWind
   for (size_t i = 0; i < frameScripts.Length(); i++) {
     FrameScriptInfo& info = frameScripts[i];
     if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
       MOZ_CRASH();
     }
   }
 
   if (!urlToLoad.IsEmpty()) {
-    newChild->RecvLoadURL(urlToLoad);
+    newChild->RecvLoadURL(urlToLoad, BrowserConfiguration());
   }
 
   nsCOMPtr<nsIDOMWindow> win = do_GetInterface(newChild->WebNavigation());
   win.forget(aReturn);
   return NS_OK;
 }
 
 bool
 TabChild::HasValidInnerSize()
 {
   return mHasValidInnerSize;
 }
 
 void
-TabChild::SendPendingTouchPreventedResponse(bool aPreventDefault,
-                                            const ScrollableLayerGuid& aGuid)
-{
-  if (mPendingTouchPreventedResponse) {
-    MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
-    SendContentReceivedInputBlock(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, aPreventDefault);
-    mPendingTouchPreventedResponse = false;
-  }
-}
-
-void
 TabChild::DestroyWindow()
 {
     nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
     if (baseWindow)
         baseWindow->Destroy();
 
     // NB: the order of mWidget->Destroy() and mRemoteFrame->Destroy()
     // is important: we want to kill off remote layers before their
@@ -1712,32 +1678,37 @@ TabChild::IsRootContentDocument()
     // We do this because we make a remote frame opaque iff
     // IsRootContentDocument(), and making vanilla remote frames transparent
     // breaks our remote reftests.
 
     return !HasAppOwnerApp();
 }
 
 bool
-TabChild::RecvLoadURL(const nsCString& uri)
+TabChild::RecvLoadURL(const nsCString& aURI,
+                      const BrowserConfiguration& aConfiguration)
 {
     SetProcessNameToAppName();
 
-    nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(uri).get(),
+    nsresult rv = WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
                                            nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
                                            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER,
                                            nullptr, nullptr, nullptr);
     if (NS_FAILED(rv)) {
         NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
     }
 
 #ifdef MOZ_CRASHREPORTER
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), uri);
+    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI);
 #endif
 
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    MOZ_ASSERT(swm);
+    swm->LoadRegistrations(aConfiguration.serviceWorkerRegistrations());
+
     return true;
 }
 
 bool
 TabChild::RecvCacheFileDescriptor(const nsString& aPath,
                                   const FileDescriptor& aFileDescriptor)
 {
     MOZ_ASSERT(NS_IsMainThread());
@@ -2107,161 +2078,47 @@ TabChild::RecvHandleDoubleTap(const CSSP
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Gesture:DoubleTap"), data);
 
     return true;
 }
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
-  TABC_LOG("Handling single tap at %s on %s with %p %p %d\n",
-    Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mGlobal.get(),
-    mTabChildGlobal.get(), mTouchEndCancelled);
-
-  if (!mGlobal || !mTabChildGlobal) {
-    return true;
-  }
-
-  if (mTouchEndCancelled) {
-    return true;
-  }
-
-  LayoutDevicePoint currentPoint =
-      APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, GetPresShellResolution())
-    * mWidget->GetDefaultScale();;
-  if (!mActiveElementManager->ActiveElementUsesStyle()) {
-    // If the active element isn't visually affected by the :active style, we
-    // have no need to wait the extra sActiveDurationMs to make the activation
-    // visually obvious to the user.
-    APZCCallbackHelper::FireSingleTapEvent(currentPoint, mWidget);
-    return true;
-  }
-
-  TABC_LOG("Active element uses style, scheduling timer for click event\n");
-  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  nsRefPtr<DelayedFireSingleTapEvent> callback =
-    new DelayedFireSingleTapEvent(mWidget, currentPoint, timer);
-  nsresult rv = timer->InitWithCallback(callback,
-                                        sActiveDurationMs,
-                                        nsITimer::TYPE_ONE_SHOT);
-  if (NS_FAILED(rv)) {
-    // Make |callback| not hold the timer, so they will both be destructed when
-    // we leave the scope of this function.
-    callback->ClearTimer();
+  if (mGlobal && mTabChildGlobal) {
+    mAPZEventState->ProcessSingleTap(aPoint, aGuid, GetPresShellResolution());
   }
   return true;
 }
 
 bool
 TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId)
 {
-  TABC_LOG("Handling long tap at %s with %p %p\n",
-    Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
-
-  if (!mGlobal || !mTabChildGlobal) {
-    return true;
+  if (mGlobal && mTabChildGlobal) {
+    mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aGuid,
+        aInputBlockId, GetPresShellResolution());
   }
-
-  SendPendingTouchPreventedResponse(false, aGuid);
-
-  bool eventHandled =
-      DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
-                         APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, GetPresShellResolution()),
-                         2, 1, 0, true,
-                         nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
-
-  TABC_LOG("Contextmenu event handled: %d\n", eventHandled);
-
-  // If no one handle context menu, fire MOZLONGTAP event
-  if (!eventHandled) {
-    LayoutDevicePoint currentPoint =
-        APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, GetPresShellResolution())
-      * mWidget->GetDefaultScale();
-    int time = 0;
-    nsEventStatus status =
-        APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget);
-    eventHandled = (status == nsEventStatus_eConsumeNoDefault);
-    TABC_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
-  }
-
-  SendContentReceivedInputBlock(aGuid, aInputBlockId, eventHandled);
-
   return true;
 }
 
 bool
 TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
-  RecvHandleSingleTap(aPoint, aGuid);
+  if (mGlobal && mTabChildGlobal) {
+    mAPZEventState->ProcessLongTapUp(aPoint, aGuid, GetPresShellResolution());
+  }
   return true;
 }
 
 bool
 TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
                                    const APZStateChange& aChange,
                                    const int& aArg)
 {
-  switch (aChange)
-  {
-  case APZStateChange::TransformBegin:
-  {
-    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
-    nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
-    if (scrollbarMediator) {
-      scrollbarMediator->ScrollbarActivityStarted();
-    }
-
-    nsCOMPtr<nsIDocument> doc = GetDocument();
-    if (doc) {
-      nsCOMPtr<nsIDocShell> docshell(doc->GetDocShell());
-      if (docshell && sf) {
-        nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
-        nsdocshell->NotifyAsyncPanZoomStarted(sf->GetScrollPositionCSSPixels());
-      }
-    }
-    break;
-  }
-  case APZStateChange::TransformEnd:
-  {
-    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
-    nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
-    if (scrollbarMediator) {
-      scrollbarMediator->ScrollbarActivityStopped();
-    }
-
-    nsCOMPtr<nsIDocument> doc = GetDocument();
-    if (doc) {
-      nsCOMPtr<nsIDocShell> docshell(doc->GetDocShell());
-      if (docshell && sf) {
-        nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
-        nsdocshell->NotifyAsyncPanZoomStopped(sf->GetScrollPositionCSSPixels());
-      }
-    }
-    break;
-  }
-  case APZStateChange::StartTouch:
-  {
-    mActiveElementManager->HandleTouchStart(aArg);
-    break;
-  }
-  case APZStateChange::StartPanning:
-  {
-    mActiveElementManager->HandlePanStart();
-    break;
-  }
-  case APZStateChange::EndTouch:
-  {
-    mEndTouchIsClick = aArg;
-    break;
-  }
-  default:
-    // APZStateChange has a 'sentinel' value, and the compiler complains
-    // if an enumerator is not handled and there is no 'default' case.
-    break;
-  }
+  mAPZEventState->ProcessAPZStateChange(GetDocument(), aViewId, aChange, aArg);
   return true;
 }
 
 bool
 TabChild::RecvActivate()
 {
   nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation());
   browser->Activate();
@@ -2291,18 +2148,18 @@ bool
 TabChild::RecvMouseEvent(const nsString& aType,
                          const float&    aX,
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
-  DispatchMouseEvent(aType, CSSPoint(aX, aY), aButton, aClickCount, aModifiers,
-                     aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN);
+  APZCCallbackHelper::DispatchMouseEvent(GetDOMWindowUtils(), aType, CSSPoint(aX, aY),
+      aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN);
   return true;
 }
 
 bool
 TabChild::RecvRealMouseEvent(const WidgetMouseEvent& event)
 {
   WidgetMouseEvent localEvent(event);
   localEvent.widget = mWidget;
@@ -2312,33 +2169,26 @@ TabChild::RecvRealMouseEvent(const Widge
 
 bool
 TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
                               const ScrollableLayerGuid& aGuid,
                               const uint64_t& aInputBlockId)
 {
   if (IsAsyncPanZoomEnabled()) {
     nsCOMPtr<nsIDocument> document(GetDocument());
-    if (nsIPresShell* shell = document->GetShell()) {
-      if (nsIFrame* rootFrame = shell->GetRootFrame()) {
-        nsTArray<ScrollableLayerGuid> targets;
-        bool waitForRefresh =
-          PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId, rootFrame, aEvent.refPoint, &targets);
-
-        SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
-      }
-    }
+    APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document, aEvent, aGuid,
+        aInputBlockId, mSetTargetAPZCCallback);
   }
 
   WidgetWheelEvent event(aEvent);
   event.widget = mWidget;
   APZCCallbackHelper::DispatchWidgetEvent(event);
 
   if (IsAsyncPanZoomEnabled()) {
-    SendContentReceivedInputBlock(aGuid, aInputBlockId, event.mFlags.mDefaultPrevented);
+    mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId);
   }
   return true;
 }
 
 static Touch*
 GetTouchForIdentifier(const WidgetTouchEvent& aEvent, int32_t aId)
 {
   for (uint32_t i = 0; i < aEvent.touches.Length(); ++i) {
@@ -2447,23 +2297,25 @@ TabChild::FireContextMenuEvent()
 
   double scale;
   GetDefaultScale(&scale);
   if (scale < 0) {
     scale = 1;
   }
 
   MOZ_ASSERT(mTapHoldTimer && mActivePointerId >= 0);
-  bool defaultPrevented = DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
-                                             mGestureDownPoint / CSSToLayoutDeviceScale(scale),
-                                             2 /* Right button */,
-                                             1 /* Click count */,
-                                             0 /* Modifiers */,
-                                             true /* Ignore root scroll frame */,
-                                             nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+  bool defaultPrevented = APZCCallbackHelper::DispatchMouseEvent(
+      GetDOMWindowUtils(),
+      NS_LITERAL_STRING("contextmenu"),
+      mGestureDownPoint / CSSToLayoutDeviceScale(scale),
+      2 /* Right button */,
+      1 /* Click count */,
+      0 /* Modifiers */,
+      true /* Ignore root scroll frame */,
+      nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   // Fire a click event if someone didn't call preventDefault() on the context
   // menu event.
   if (defaultPrevented) {
     CancelTapTracking();
   } else if (mTapHoldTimer) {
     mTapHoldTimer->Cancel();
     mTapHoldTimer = nullptr;
@@ -2475,170 +2327,16 @@ TabChild::CancelTapTracking()
 {
   mActivePointerId = -1;
   if (mTapHoldTimer) {
     mTapHoldTimer->Cancel();
   }
   mTapHoldTimer = nullptr;
 }
 
-static nsIScrollableFrame*
-GetScrollableAncestorFrame(nsIFrame* aTarget)
-{
-  if (!aTarget) {
-    return nullptr;
-  }
-  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
-                 | nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
-  return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
-}
-
-static dom::Element*
-GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
-{
-  if (!aScrollableFrame) {
-    return nullptr;
-  }
-  nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
-  if (!scrolledFrame) {
-    return nullptr;
-  }
-  // |scrolledFrame| should at this point be the root content frame of the
-  // nearest ancestor scrollable frame. The element corresponding to this
-  // frame should be the one with the displayport set on it, so find that
-  // element and return it.
-  nsIContent* content = scrolledFrame->GetContent();
-  MOZ_ASSERT(content->IsElement()); // roc says this must be true
-  return content->AsElement();
-}
-
-class DisplayportSetListener : public nsAPostRefreshObserver {
-public:
-  DisplayportSetListener(TabChild* aTabChild,
-                         nsIPresShell* aPresShell,
-                         const uint64_t& aInputBlockId,
-                         const nsTArray<ScrollableLayerGuid>& aTargets)
-    : mTabChild(aTabChild)
-    , mPresShell(aPresShell)
-    , mInputBlockId(aInputBlockId)
-    , mTargets(aTargets)
-  {
-  }
-
-  virtual ~DisplayportSetListener()
-  {
-  }
-
-  void DidRefresh() MOZ_OVERRIDE {
-    if (!mTabChild) {
-      MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
-      return;
-    }
-
-    TABC_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
-    mTabChild->SendSetTargetAPZC(mInputBlockId, mTargets);
-
-    if (!mPresShell->RemovePostRefreshObserver(this)) {
-      MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
-      // Graceful handling, just in case...
-      mTabChild = nullptr;
-      mPresShell = nullptr;
-      return;
-    }
-
-    delete this;
-  }
-
-private:
-  nsRefPtr<TabChild> mTabChild;
-  nsRefPtr<nsIPresShell> mPresShell;
-  uint64_t mInputBlockId;
-  nsTArray<ScrollableLayerGuid> mTargets;
-};
-
-bool
-TabChild::PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
-                                              const uint64_t& aInputBlockId,
-                                              nsIFrame* aRootFrame,
-                                              const LayoutDeviceIntPoint& aRefPoint,
-                                              nsTArray<ScrollableLayerGuid>* aTargets)
-{
-  ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
-  nsPoint point =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(WebWidget(), aRefPoint, aRootFrame);
-  nsIFrame* target =
-    nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
-  nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
-  nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
-
-  nsAutoString dpElementDesc;
-  if (dpElement) {
-    dpElement->Describe(dpElementDesc);
-  }
-  TABC_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
-      aInputBlockId, dpElement.get(),
-      NS_LossyConvertUTF16toASCII(dpElementDesc).get());
-
-  bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
-    dpElement, &(guid.mPresShellId), &(guid.mScrollId));
-  aTargets->AppendElement(guid);
-
-  if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
-    return false;
-  }
-
-  TABC_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
-  return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
-      scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
-}
-
-void
-TabChild::SendSetTargetAPZCNotification(nsIPresShell* aShell,
-                                        const uint64_t& aInputBlockId,
-                                        const nsTArray<ScrollableLayerGuid>& aTargets,
-                                        bool aWaitForRefresh)
-{
-  bool waitForRefresh = aWaitForRefresh;
-  if (waitForRefresh) {
-    TABC_LOG("At least one target got a new displayport, need to wait for refresh\n");
-    waitForRefresh = aShell->AddPostRefreshObserver(
-      new DisplayportSetListener(this, aShell, aInputBlockId, aTargets));
-  }
-  if (!waitForRefresh) {
-    TABC_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
-    SendSetTargetAPZC(aInputBlockId, aTargets);
-  } else {
-    TABC_LOG("Successfully registered post-refresh observer\n");
-  }
-}
-
-void
-TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
-                                        const ScrollableLayerGuid& aGuid,
-                                        const uint64_t& aInputBlockId)
-{
-  nsCOMPtr<nsIDocument> document(GetDocument());
-  nsIPresShell* shell = document->GetShell();
-  if (!shell) {
-    return;
-  }
-  nsIFrame* rootFrame = shell->GetRootFrame();
-  if (!rootFrame) {
-    return;
-  }
-
-  bool waitForRefresh = false;
-  nsTArray<ScrollableLayerGuid> targets;
-  for (size_t i = 0; i < aEvent.touches.Length(); i++) {
-    waitForRefresh |= PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId,
-        rootFrame, aEvent.touches[i]->mRefPoint, &targets);
-  }
-  SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
-}
-
 float
 TabChild::GetPresShellResolution() const
 {
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsIPresShell* shell = document->GetShell();
   if (!shell) {
     return 1.0f;
   }
@@ -2647,79 +2345,37 @@ TabChild::GetPresShellResolution() const
 
 bool
 TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
                              const ScrollableLayerGuid& aGuid,
                              const uint64_t& aInputBlockId)
 {
   TABC_LOG("Receiving touch event of type %d\n", aEvent.message);
 
-  for (size_t i = 0; i < aEvent.touches.Length(); i++) {
-    aEvent.touches[i]->mRefPoint = APZCCallbackHelper::ApplyCallbackTransform(
-        aEvent.touches[i]->mRefPoint, aGuid, mWidget->GetDefaultScale(),
-        GetPresShellResolution());
-  }
-
-  if (aEvent.message == NS_TOUCH_START && IsAsyncPanZoomEnabled()) {
-    SendSetTargetAPZCNotification(aEvent, aGuid, aInputBlockId);
-  }
-
   WidgetTouchEvent localEvent(aEvent);
   localEvent.widget = mWidget;
+
+  APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
+      mWidget->GetDefaultScale(), GetPresShellResolution());
+
+  if (localEvent.message == NS_TOUCH_START && IsAsyncPanZoomEnabled()) {
+    nsCOMPtr<nsIDocument> document = GetDocument();
+    APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document,
+        localEvent, aGuid, aInputBlockId, mSetTargetAPZCCallback);
+  }
+
   // Dispatch event to content (potentially a long-running operation)
   nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
 
   if (!IsAsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
   }
 
-  if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
-    mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
-  }
-
-  bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
-                          localEvent.mFlags.mMultipleActionsPrevented;
-  switch (aEvent.message) {
-  case NS_TOUCH_START: {
-    mTouchEndCancelled = false;
-    if (mPendingTouchPreventedResponse) {
-      // We can enter here if we get two TOUCH_STARTs in a row and didn't
-      // respond to the first one. Respond to it now.
-      SendContentReceivedInputBlock(mPendingTouchPreventedGuid, mPendingTouchPreventedBlockId, false);
-      mPendingTouchPreventedResponse = false;
-    }
-    if (isTouchPrevented) {
-      SendContentReceivedInputBlock(aGuid, aInputBlockId, isTouchPrevented);
-    } else {
-      mPendingTouchPreventedResponse = true;
-      mPendingTouchPreventedGuid = aGuid;
-      mPendingTouchPreventedBlockId = aInputBlockId;
-    }
-    break;
-  }
-
-  case NS_TOUCH_END:
-    if (isTouchPrevented) {
-      mTouchEndCancelled = true;
-      mEndTouchIsClick = false;
-    }
-    // fall through
-  case NS_TOUCH_CANCEL:
-    mActiveElementManager->HandleTouchEnd(mEndTouchIsClick);
-    // fall through
-  case NS_TOUCH_MOVE: {
-    SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
-    break;
-  }
-
-  default:
-    NS_WARNING("Unknown touch event type");
-  }
-
+  mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId);
   return true;
 }
 
 bool
 TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  const uint64_t& aInputBlockId)
 {
@@ -3255,34 +2911,16 @@ void
 TabChild::NotifyPainted()
 {
     if (!mNotified) {
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
-bool
-TabChild::DispatchMouseEvent(const nsString&       aType,
-                             const CSSPoint&       aPoint,
-                             const int32_t&        aButton,
-                             const int32_t&        aClickCount,
-                             const int32_t&        aModifiers,
-                             const bool&           aIgnoreRootScrollFrame,
-                             const unsigned short& aInputSourceArg)
-{
-  nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
-  NS_ENSURE_TRUE(utils, true);
-  
-  bool defaultPrevented = false;
-  utils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
-                        aIgnoreRootScrollFrame, 0, aInputSourceArg, false, 4, &defaultPrevented);
-  return defaultPrevented;
-}
-
 void
 TabChild::MakeVisible()
 {
     if (mWidget) {
         mWidget->Show(true);
     }
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -40,17 +40,18 @@ class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
 namespace layers {
-class ActiveElementManager;
+class APZEventState;
+struct SetTargetAPZCCallback;
 }
 
 namespace widget {
 struct AutoCacheNativeKeyCommands;
 }
 
 namespace plugins {
 class PluginWidgetChild;
@@ -246,17 +247,18 @@ class TabChild MOZ_FINAL : public TabChi
                            public nsITabChild,
                            public nsIObserver,
                            public TabContext,
                            public nsITooltipListener
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
-    typedef mozilla::layers::ActiveElementManager ActiveElementManager;
+    typedef mozilla::layers::APZEventState APZEventState;
+    typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
 
 public:
     /**
      * Find TabChild of aTabId in the same content process of the
      * caller.
      */
     static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId);
 
@@ -307,17 +309,18 @@ public:
                                     const nsAString& aMessage,
                                     const mozilla::dom::StructuredCloneData& aData,
                                     JS::Handle<JSObject *> aCpows,
                                     nsIPrincipal* aPrincipal) MOZ_OVERRIDE;
     virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                          const ViewID& aViewId,
                                          const bool& aIsRoot,
                                          const ZoomConstraints& aConstraints) MOZ_OVERRIDE;
-    virtual bool RecvLoadURL(const nsCString& uri) MOZ_OVERRIDE;
+    virtual bool RecvLoadURL(const nsCString& aURI,
+                             const BrowserConfiguration& aConfiguration) MOZ_OVERRIDE;
     virtual bool RecvCacheFileDescriptor(const nsString& aPath,
                                          const FileDescriptor& aFileDescriptor)
                                          MOZ_OVERRIDE;
     virtual bool RecvShow(const nsIntSize& aSize,
                           const ShowInfo& aInfo,
                           const ScrollingBehavior& aScrolling,
                           const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                           const uint64_t& aLayersId,
@@ -429,27 +432,16 @@ public:
 
     void SetBackgroundColor(const nscolor& aColor);
 
     void NotifyPainted();
 
     void RequestNativeKeyBindings(mozilla::widget::AutoCacheNativeKeyCommands* aAutoCache,
                                   WidgetKeyboardEvent* aEvent);
 
-    /** Return a boolean indicating if the page has called preventDefault on
-     *  the event.
-     */
-    bool DispatchMouseEvent(const nsString&       aType,
-                            const CSSPoint&       aPoint,
-                            const int32_t&        aButton,
-                            const int32_t&        aClickCount,
-                            const int32_t&        aModifiers,
-                            const bool&           aIgnoreRootScrollFrame,
-                            const unsigned short& aInputSourceArg);
-
     /**
      * Signal to this TabChild that it should be made visible:
      * activated widget, retained layer tree, etc.  (Respectively,
      * made not visible.)
      */
     void MakeVisible();
     void MakeHidden();
 
@@ -534,17 +526,16 @@ private:
      */
     TabChild(nsIContentChild* aManager,
              const TabId& aTabId,
              const TabContext& aContext,
              uint32_t aChromeFlags);
 
     nsresult Init();
 
-    class DelayedFireSingleTapEvent;
     class DelayedFireContextMenuEvent;
 
     // Notify others that our TabContext has been updated.  (At the moment, this
     // sets the appropriate app-id and is-browser flags on our docshell.)
     //
     // You should call this after calling TabContext::SetTabContext().  We also
     // call this during Init().
     void NotifyTabContextUpdated();
@@ -588,39 +579,16 @@ private:
                         nsIURI* aURI,
                         const nsAString& aName,
                         const nsACString& aFeatures,
                         bool* aWindowIsNew,
                         nsIDOMWindow** aReturn);
 
     bool HasValidInnerSize();
 
-    void SendPendingTouchPreventedResponse(bool aPreventDefault,
-                                           const ScrollableLayerGuid& aGuid);
-
-    // Adds the scrollable layer target to the target list, and returns whether
-    // or not the caller should wait for a refresh to send a target
-    // notification.
-    bool PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
-                                             const uint64_t& aInputBlockId,
-                                             nsIFrame* aRootFrame,
-                                             const LayoutDeviceIntPoint& aRefPoint,
-                                             nsTArray<ScrollableLayerGuid>* aTargets);
-
-    // Sends a SetTarget notification for APZC, given one or more previous
-    // calls to PrepareForAPZCSetTargetNotification().
-    void SendSetTargetAPZCNotification(nsIPresShell* aShell,
-                                       const uint64_t& aInputBlockId,
-                                       const nsTArray<ScrollableLayerGuid>& aTargets,
-                                       bool aWaitForRefresh);
-
-    void SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
-                                       const mozilla::layers::ScrollableLayerGuid& aGuid,
-                                       const uint64_t& aInputBlockId);
-
     // Get the pres shell resolution of the document in this tab.
     float GetPresShellResolution() const;
 
     void SetTabId(const TabId& aTabId);
 
     class CachedFileDescriptorInfo;
     class CachedFileDescriptorCallbackRunnable;
     class DelayedDeleteRunnable;
@@ -649,25 +617,20 @@ private:
     nsAutoTArray<nsAutoPtr<CachedFileDescriptorInfo>, 1>
         mCachedFileDescriptorInfos;
     nscolor mLastBackgroundColor;
     bool mDidFakeShow;
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
-    bool mPendingTouchPreventedResponse;
-    ScrollableLayerGuid mPendingTouchPreventedGuid;
-    uint64_t mPendingTouchPreventedBlockId;
-
-    bool mTouchEndCancelled;
-    bool mEndTouchIsClick;
 
     bool mIgnoreKeyPressEvent;
-    nsRefPtr<ActiveElementManager> mActiveElementManager;
+    nsRefPtr<APZEventState> mAPZEventState;
+    nsRefPtr<SetTargetAPZCCallback> mSetTargetAPZCCallback;
     bool mHasValidInnerSize;
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     nsIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
     bool mIPCOpen;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -8,16 +8,17 @@
 
 #include "TabParent.h"
 
 #include "AppProcessChecker.h"
 #include "mozIApplication.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Hal.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/InputAPZContext.h"
@@ -40,16 +41,17 @@
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadInfo.h"
+#include "nsPrincipal.h"
 #include "nsIPromptFactory.h"
 #include "nsIURI.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIWindowCreator2.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIXULWindow.h"
 #include "nsIRemoteBrowser.h"
 #include "nsViewManager.h"
@@ -688,16 +690,29 @@ TabParent::SendLoadRemoteScript(const ns
     mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope));
     return true;
   }
 
   MOZ_ASSERT(mDelayedFrameScripts.IsEmpty());
   return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope);
 }
 
+bool
+TabParent::InitBrowserConfiguration(nsIURI* aURI,
+                                    BrowserConfiguration& aConfiguration)
+{
+  // Get the list of ServiceWorkerRegistation for this origin.
+  nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+  MOZ_ASSERT(swr);
+
+  swr->GetRegistrations(aConfiguration.serviceWorkerRegistrations());
+
+  return true;
+}
+
 void
 TabParent::LoadURL(nsIURI* aURI)
 {
     MOZ_ASSERT(aURI);
 
     if (mIsDestroyed) {
         return;
     }
@@ -722,17 +737,23 @@ TabParent::LoadURL(nsIURI* aURI)
     uint32_t appId = OwnOrContainingAppId();
     if (mSendOfflineStatus && NS_IsAppOffline(appId)) {
       // If the app is offline in the parent process
       // pass that state to the child process as well
       unused << SendAppOfflineStatus(appId, true);
     }
     mSendOfflineStatus = false;
 
-    unused << SendLoadURL(spec);
+    // This object contains the configuration for this new app.
+    BrowserConfiguration configuration;
+    if (NS_WARN_IF(!InitBrowserConfiguration(aURI, configuration))) {
+      return;
+    }
+
+    unused << SendLoadURL(spec, configuration);
 
     // If this app is a packaged app then we can speed startup by sending over
     // the file descriptor for the "application.zip" file that it will
     // invariably request. Only do this once.
     if (!mAppPackageFileDescriptorSent) {
         mAppPackageFileDescriptorSent = true;
 
         nsCOMPtr<mozIApplication> app = GetOwnOrContainingApp();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -398,16 +398,19 @@ protected:
                                         uint64_t* aLayersId) MOZ_OVERRIDE;
 
     virtual bool RecvSetDimensions(const uint32_t& aFlags,
                                    const int32_t& aX, const int32_t& aY,
                                    const int32_t& aCx, const int32_t& aCy) MOZ_OVERRIDE;
 
     bool SendCompositionChangeEvent(mozilla::WidgetCompositionEvent& event);
 
+    bool InitBrowserConfiguration(nsIURI* aURI,
+                                  BrowserConfiguration& aConfiguration);
+
     // IME
     static TabParent *mIMETabParent;
     nsString mIMECacheText;
     uint32_t mIMESelectionAnchor;
     uint32_t mIMESelectionFocus;
     mozilla::WritingMode mWritingMode;
     bool mIMEComposing;
     bool mIMECompositionEnding;
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -107,16 +107,17 @@ IPDL_SOURCES += [
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
+    '/caps',
     '/chrome',
     '/docshell/base',
     '/dom/base',
     '/dom/bluetooth',
     '/dom/bluetooth/ipc',
     '/dom/devicestorage',
     '/dom/filesystem',
     '/dom/fmradio/ipc',
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1248,16 +1248,17 @@ void MediaDecoderStateMachine::UpdatePla
   AssertCurrentThreadInMonitor();
 
   NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime");
   mCurrentFrameTime = aTime - mStartTime;
   NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!");
   if (aTime > mEndTime) {
     NS_ASSERTION(mCurrentFrameTime > GetDuration(),
                  "CurrentTime must be after duration if aTime > endTime!");
+    DECODER_LOG("Setting new end time to %lld", aTime);
     mEndTime = aTime;
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
     NS_DispatchToMainThread(event);
   }
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime)
@@ -1392,16 +1393,31 @@ int64_t MediaDecoderStateMachine::GetDur
 int64_t MediaDecoderStateMachine::GetEndTime()
 {
   if (mEndTime == -1 && mDurationSet) {
     return INT64_MAX;
   }
   return mEndTime;
 }
 
+// Runnable which dispatches an event to the main thread to seek to the new
+// aSeekTarget.
+class SeekRunnable : public nsRunnable {
+public:
+  SeekRunnable(MediaDecoder* aDecoder, double aSeekTarget)
+    : mDecoder(aDecoder), mSeekTarget(aSeekTarget) {}
+  NS_IMETHOD Run() {
+    mDecoder->Seek(mSeekTarget, SeekTarget::Accurate);
+    return NS_OK;
+  }
+private:
+  nsRefPtr<MediaDecoder> mDecoder;
+  double mSeekTarget;
+};
+
 void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
 {
   NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
                "Should be on main or decode thread.");
   AssertCurrentThreadInMonitor();
 
   if (aDuration == -1) {
     mDurationSet = false;
@@ -1415,16 +1431,31 @@ void MediaDecoderStateMachine::SetDurati
   }
 
   if (aDuration == INT64_MAX) {
     mEndTime = -1;
     return;
   }
 
   mEndTime = mStartTime + aDuration;
+
+  if (mDecoder && mEndTime >= 0 && mEndTime < mCurrentFrameTime) {
+    // The current playback position is now past the end of the element duration
+    // the user agent must also seek to the time of the end of the media
+    // resource.
+    if (NS_IsMainThread()) {
+      // Seek synchronously.
+      mDecoder->Seek(double(mEndTime) / USECS_PER_S, SeekTarget::Accurate);
+    } else {
+      // Queue seek to new end position.
+      nsCOMPtr<nsIRunnable> task =
+        new SeekRunnable(mDecoder, double(mEndTime) / USECS_PER_S);
+      NS_DispatchToMainThread(task);
+    }
+  }
 }
 
 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
   AssertCurrentThreadInMonitor();
   int64_t duration = GetDuration();
   if (aDuration != duration &&
       mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -631,16 +631,17 @@ private:
 template<typename PromiseType, typename TargetType>
 static nsRefPtr<PromiseType>
 ProxyInternal(TargetType* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName)
 {
   nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
   nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r);
   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+  (void) rv; // Avoid compilation failures in builds with MOZ_DIAGNOSTIC_ASSERT disabled.
   return Move(p);
 }
 
 } // namespace detail
 
 template<typename PromiseType, typename TargetType, typename ThisType>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(TargetType* aTarget, ThisType* aThisVal, const char* aCallerName,
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -13,51 +13,54 @@
 #include "MediaData.h"
 #ifdef MOZ_FMP4
 #include "MP4Stream.h"
 #endif
 #include "SourceBufferResource.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+/* Polyfill __func__ on MSVC to pass to the log. */
+#ifdef _MSC_VER
+#define __func__ __FUNCTION__
+#endif
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+#define MSE_DEBUG(name, arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
-#define MSE_DEBUGV(...)
-#define MSE_API(...)
 #endif
 
 namespace mozilla {
 
-ContainerParser::ContainerParser()
+ContainerParser::ContainerParser(const nsACString& aType)
   : mHasInitData(false)
+  , mType(aType)
 {
 }
 
 bool
 ContainerParser::IsInitSegmentPresent(LargeDataBuffer* aData)
 {
-MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
-            this, aData->Length(),
+MSE_DEBUG(ContainerParser, "aLength=%u [%x%x%x%x]",
+            aData->Length(),
             aData->Length() > 0 ? (*aData)[0] : 0,
             aData->Length() > 1 ? (*aData)[1] : 0,
             aData->Length() > 2 ? (*aData)[2] : 0,
             aData->Length() > 3 ? (*aData)[3] : 0);
 return false;
 }
 
 bool
 ContainerParser::IsMediaSegmentPresent(LargeDataBuffer* aData)
 {
-  MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
-            this, aData->Length(),
+  MSE_DEBUG(ContainerParser, "aLength=%u [%x%x%x%x]",
+            aData->Length(),
             aData->Length() > 0 ? (*aData)[0] : 0,
             aData->Length() > 1 ? (*aData)[1] : 0,
             aData->Length() > 2 ? (*aData)[2] : 0,
             aData->Length() > 3 ? (*aData)[3] : 0);
   return false;
 }
 
 bool
@@ -89,18 +92,20 @@ ContainerParser::HasCompleteInitData()
 LargeDataBuffer*
 ContainerParser::InitData()
 {
   return mInitData;
 }
 
 class WebMContainerParser : public ContainerParser {
 public:
-  WebMContainerParser()
-    : mParser(0), mOffset(0)
+  explicit WebMContainerParser(const nsACString& aType)
+    : ContainerParser(aType)
+    , mParser(0)
+    , mOffset(0)
   {}
 
   static const unsigned NS_PER_USEC = 1000;
   static const unsigned USEC_PER_SEC = 1000000;
 
   bool IsInitSegmentPresent(LargeDataBuffer* aData)
   {
     ContainerParser::IsInitSegmentPresent(aData);
@@ -175,21 +180,21 @@ public:
       if (mParser.mInitEndOffset > 0) {
         MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength());
         if (!mInitData->SetLength(mParser.mInitEndOffset)) {
           // Super unlikely OOM
           return false;
         }
         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
         mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset);
-        MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                  this, mParser.mInitEndOffset);
+        MSE_DEBUG(WebMContainerParser, "Stashed init of %u bytes.",
+                  mParser.mInitEndOffset);
         mResource = nullptr;
       } else {
-        MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
+        MSE_DEBUG(WebMContainerParser, "Incomplete init found.");
       }
       mHasInitData = true;
     }
     mOffset += aData->Length();
 
     if (mapping.IsEmpty()) {
       return false;
     }
@@ -203,18 +208,18 @@ public:
     if (endIdx == 0) {
       return false;
     }
 
     uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
     aStart = mapping[0].mTimecode / NS_PER_USEC;
     aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
 
-    MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
-              this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
+    MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
+              aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
 
     mapping.RemoveElementsAt(0, endIdx + 1);
     mOverlappedMapping.AppendElements(mapping);
 
     return true;
   }
 
   int64_t GetRoundingError()
@@ -227,17 +232,20 @@ private:
   WebMBufferedParser mParser;
   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
   int64_t mOffset;
 };
 
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
-  MP4ContainerParser() :mMonitor("MP4ContainerParser Index Monitor") {}
+  explicit MP4ContainerParser(const nsACString& aType)
+    : ContainerParser(aType)
+    , mMonitor("MP4ContainerParser Index Monitor")
+  {}
 
   bool IsInitSegmentPresent(LargeDataBuffer* aData)
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
 
@@ -303,35 +311,35 @@ public:
       uint32_t length = range.mEnd - range.mStart;
       if (length) {
         if (!mInitData->SetLength(length)) {
           // Super unlikely OOM
           return false;
         }
         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
         mResource->ReadFromCache(buffer, range.mStart, length);
-        MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
-                  this, length);
+        MSE_DEBUG(MP4ContainerParser ,"Stashed init of %u bytes.",
+                  length);
       } else {
-        MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Incomplete init found.");
+        MSE_DEBUG(MP4ContainerParser, "Incomplete init found.");
       }
       mHasInitData = true;
     }
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
     mResource->EvictData(mParser->mOffset, mParser->mOffset);
 
     if (compositionRange.IsNull()) {
       return false;
     }
     aStart = compositionRange.start;
     aEnd = compositionRange.end;
-    MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
-              this, aStart, aEnd);
+    MSE_DEBUG(MP4ContainerParser, "[%lld, %lld]",
+              aStart, aEnd);
     return true;
   }
 
   // Gaps of up to 20ms (marginally longer than a single frame at 60fps) are considered
   // to be sequential frames.
   int64_t GetRoundingError()
   {
     return 20000;
@@ -343,20 +351,22 @@ private:
   Monitor mMonitor;
 };
 #endif
 
 /*static*/ ContainerParser*
 ContainerParser::CreateForMIMEType(const nsACString& aType)
 {
   if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
-    return new WebMContainerParser();
+    return new WebMContainerParser(aType);
   }
 
 #ifdef MOZ_FMP4
   if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
-    return new MP4ContainerParser();
+    return new MP4ContainerParser(aType);
   }
 #endif
-  return new ContainerParser();
+  return new ContainerParser(aType);
 }
 
+#undef MSE_DEBUG
+
 } // namespace mozilla
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -3,25 +3,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_CONTAINERPARSER_H_
 #define MOZILLA_CONTAINERPARSER_H_
 
 #include "nsRefPtr.h"
+#include "nsString.h"
 
 namespace mozilla {
 
 class LargeDataBuffer;
 class SourceBufferResource;
 
 class ContainerParser {
 public:
-  ContainerParser();
+  explicit ContainerParser(const nsACString& aType);
   virtual ~ContainerParser() {}
 
   // Return true if aData starts with an initialization segment.
   // The base implementation exists only for debug logging and is expected
   // to be called first from the overriding implementation.
   virtual bool IsInitSegmentPresent(LargeDataBuffer* aData);
 
   // Return true if aData starts with a media segment.
@@ -52,12 +53,13 @@ public:
   bool HasCompleteInitData();
 
   static ContainerParser* CreateForMIMEType(const nsACString& aType);
 
 protected:
   nsRefPtr<LargeDataBuffer> mInitData;
   nsRefPtr<SourceBufferResource> mResource;
   bool mHasInitData;
+  const nsCString mType;
 };
 
 } // namespace mozilla
 #endif /* MOZILLA_CONTAINERPARSER_H_ */
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -50,18 +50,18 @@ PRLogModuleInfo* GetMediaSourceAPILog()
 {
   static PRLogModuleInfo* sLogModule;
   if (!sLogModule) {
     sLogModule = PR_NewLogModule("MediaSource");
   }
   return sLogModule;
 }
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSource(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define MSE_API(arg, ...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, ("MediaSource(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_API(...)
 #endif
 
 // Arbitrary limit.
 static const unsigned int MAX_SOURCE_BUFFERS = 16;
 
@@ -130,17 +130,17 @@ MediaSource::Constructor(const GlobalObj
 
   nsRefPtr<MediaSource> mediaSource = new MediaSource(window);
   return mediaSource.forget();
 }
 
 MediaSource::~MediaSource()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("MediaSource(%p)::~MediaSource()", this);
+  MSE_API("");
   if (mDecoder) {
     mDecoder->DetachMediaSource();
   }
 }
 
 SourceBufferList*
 MediaSource::SourceBuffers()
 {
@@ -174,44 +174,44 @@ MediaSource::Duration()
   MOZ_ASSERT(mDecoder);
   return mDecoder->GetMediaSourceDuration();
 }
 
 void
 MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("MediaSource(%p)::SetDuration(aDuration=%f, ErrorResult)", this, aDuration);
+  MSE_API("SetDuration(aDuration=%f, ErrorResult)", aDuration);
   if (aDuration < 0 || IsNaN(aDuration)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   SetDuration(aDuration, MSRangeRemovalAction::RUN);
 }
 
 void
 MediaSource::SetDuration(double aDuration, MSRangeRemovalAction aAction)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("MediaSource(%p)::SetDuration(aDuration=%f)", this, aDuration);
+  MSE_API("SetDuration(aDuration=%f)", aDuration);
   mDecoder->SetMediaSourceDuration(aDuration, aAction);
 }
 
 already_AddRefed<SourceBuffer>
 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsresult rv = mozilla::IsTypeSupported(aType);
-  MSE_API("MediaSource(%p)::AddSourceBuffer(aType=%s)%s",
-          this, NS_ConvertUTF16toUTF8(aType).get(),
+  MSE_API("AddSourceBuffer(aType=%s)%s",
+          NS_ConvertUTF16toUTF8(aType).get(),
           rv == NS_OK ? "" : " [not supported]");
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
   if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
@@ -229,26 +229,26 @@ MediaSource::AddSourceBuffer(const nsASt
   }
   nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
   if (!sourceBuffer) {
     aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
     return nullptr;
   }
   mSourceBuffers->Append(sourceBuffer);
   mActiveSourceBuffers->Append(sourceBuffer);
-  MSE_DEBUG("MediaSource(%p)::AddSourceBuffer() sourceBuffer=%p", this, sourceBuffer.get());
+  MSE_DEBUG("sourceBuffer=%p", sourceBuffer.get());
   return sourceBuffer.forget();
 }
 
 void
 MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   SourceBuffer* sourceBuffer = &aSourceBuffer;
-  MSE_API("MediaSource(%p)::RemoveSourceBuffer(aSourceBuffer=%p)", this, sourceBuffer);
+  MSE_API("RemoveSourceBuffer(aSourceBuffer=%p)", sourceBuffer);
   if (!mSourceBuffers->Contains(sourceBuffer)) {
     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
     return;
   }
 
   sourceBuffer->AbortBufferAppend();
   // TODO:
   // abort stream append loop (if running)
@@ -266,18 +266,18 @@ MediaSource::RemoveSourceBuffer(SourceBu
   mSourceBuffers->Remove(sourceBuffer);
   // TODO: Free all resources associated with sourceBuffer
 }
 
 void
 MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("MediaSource(%p)::EndOfStream(aError=%d)",
-          this, aError.WasPassed() ? uint32_t(aError.Value()) : 0);
+  MSE_API("EndOfStream(aError=%d)",
+          aError.WasPassed() ? uint32_t(aError.Value()) : 0);
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   SetReadyState(MediaSourceReadyState::Ended);
   mSourceBuffers->Ended();
@@ -308,18 +308,20 @@ MediaSource::EndOfStream(const Optional<
   }
 }
 
 /* static */ bool
 MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsresult rv = mozilla::IsTypeSupported(aType);
-  MSE_API("MediaSource::IsTypeSupported(aType=%s)%s",
-          NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "" : " [not supported]");
+#define this nullptr
+  MSE_API("IsTypeSupported(aType=%s)%s ",
+          NS_ConvertUTF16toUTF8(aType).get(), rv == NS_OK ? "OK" : "[not supported]");
+#undef this // don't ever remove this line !
   return NS_SUCCEEDED(rv);
 }
 
 /* static */ bool
 MediaSource::Enabled(JSContext* cx, JSObject* aGlobal)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -359,17 +361,17 @@ MediaSource::Enabled(JSContext* cx, JSOb
    return eTLDplusOne.EqualsLiteral("youtube.com") ||
           eTLDplusOne.EqualsLiteral("youtube-nocookie.com");
 }
 
 bool
 MediaSource::Attach(MediaSourceDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::Attach(aDecoder=%p) owner=%p", this, aDecoder, aDecoder->GetOwner());
+  MSE_DEBUG("Attach(aDecoder=%p) owner=%p", aDecoder, aDecoder->GetOwner());
   MOZ_ASSERT(aDecoder);
   MOZ_ASSERT(aDecoder->GetOwner());
   if (mReadyState != MediaSourceReadyState::Closed) {
     return false;
   }
   MOZ_ASSERT(!mMediaElement);
   mMediaElement = aDecoder->GetOwner()->GetMediaElement();
   MOZ_ASSERT(!mDecoder);
@@ -378,18 +380,18 @@ MediaSource::Attach(MediaSourceDecoder* 
   SetReadyState(MediaSourceReadyState::Open);
   return true;
 }
 
 void
 MediaSource::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::Detach() mDecoder=%p owner=%p",
-            this, mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr);
+  MSE_DEBUG("mDecoder=%p owner=%p",
+            mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr);
   if (!mDecoder) {
     MOZ_ASSERT(mReadyState == MediaSourceReadyState::Closed);
     MOZ_ASSERT(mActiveSourceBuffers->IsEmpty() && mSourceBuffers->IsEmpty());
     return;
   }
   mDecoder->DetachMediaSource();
   mDecoder = nullptr;
   mMediaElement = nullptr;
@@ -414,26 +416,26 @@ MediaSource::MediaSource(nsPIDOMWindow* 
   mSourceBuffers = new SourceBufferList(this);
   mActiveSourceBuffers = new SourceBufferList(this);
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
   if (sop) {
     mPrincipal = sop->GetPrincipal();
   }
 
-  MSE_API("MediaSource(%p)::MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
-          this, aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
+  MSE_API("MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
+          aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
 }
 
 void
 MediaSource::SetReadyState(MediaSourceReadyState aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState != mReadyState);
-  MSE_DEBUG("MediaSource(%p)::SetReadyState(aState=%d) mReadyState=%d", this, aState, mReadyState);
+  MSE_DEBUG("SetReadyState(aState=%d) mReadyState=%d", aState, mReadyState);
 
   MediaSourceReadyState oldState = mReadyState;
   mReadyState = aState;
 
   if (mReadyState == MediaSourceReadyState::Open &&
       (oldState == MediaSourceReadyState::Closed ||
        oldState == MediaSourceReadyState::Ended)) {
     QueueAsyncSimpleEvent("sourceopen");
@@ -455,70 +457,70 @@ MediaSource::SetReadyState(MediaSourceRe
 
   NS_WARNING("Invalid MediaSource readyState transition");
 }
 
 void
 MediaSource::DispatchSimpleEvent(const char* aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("MediaSource(%p) Dispatch event '%s'", this, aName);
+  MSE_API("Dispatch event '%s'", aName);
   DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
 }
 
 void
 MediaSource::QueueAsyncSimpleEvent(const char* aName)
 {
-  MSE_DEBUG("MediaSource(%p) Queuing event '%s'", this, aName);
+  MSE_DEBUG("Queuing event '%s'", aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaSource::DurationChange(double aOldDuration, double aNewDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
+  MSE_DEBUG("DurationChange(aOldDuration=%f, aNewDuration=%f)", aOldDuration, aNewDuration);
 
   if (aNewDuration < aOldDuration) {
     // Remove all buffered data from aNewDuration.
     mSourceBuffers->RangeRemoval(aNewDuration, PositiveInfinity<double>());
   }
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
 }
 
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::NotifyEvicted(aStart=%f, aEnd=%f)", this, aStart, aEnd);
+  MSE_DEBUG("NotifyEvicted(aStart=%f, aEnd=%f)", aStart, aEnd);
   // Cycle through all SourceBuffers and tell them to evict data in
   // the given range.
   mSourceBuffers->Evict(aStart, aEnd);
 }
 
 void
 MediaSource::QueueInitializationEvent()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mFirstSourceBufferInitialized) {
     return;
   }
   mFirstSourceBufferInitialized = true;
-  MSE_DEBUG("MediaSource(%p)::QueueInitializationEvent()", this);
+  MSE_DEBUG("");
   nsRefPtr<nsIRunnable> task =
     NS_NewRunnableMethod(this, &MediaSource::InitializationEvent);
   NS_DispatchToMainThread(task);
 }
 
 void
 MediaSource::InitializationEvent()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::InitializationEvent()", this);
+  MSE_DEBUG("");
   if (mDecoder) {
     mDecoder->PrepareReaderInitialization();
   }
 }
 
 #if defined(DEBUG)
 void
 MediaSource::Dump(const char* aPath)
@@ -557,11 +559,14 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Media
 
 NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaSource, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
+#undef MSE_DEBUG
+#undef MSE_API
+
 } // namespace dom
 
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -8,28 +8,26 @@
 #include "prlog.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaSource.h"
 #include "MediaSourceReader.h"
 #include "MediaSourceResource.h"
 #include "MediaSourceUtils.h"
+#include "VideoUtils.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define MSE_DEBUGV(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG + 1, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
-#define MSE_API(...)
 #endif
 
 namespace mozilla {
 
 class SourceBufferDecoder;
 
 MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
   : mMediaSource(nullptr)
@@ -82,24 +80,24 @@ MediaSourceDecoder::GetSeekable(dom::Tim
     // Return empty range.
   } else if (duration > 0 && mozilla::IsInfinite(duration)) {
     nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges();
     mReader->GetBuffered(bufferedRanges);
     aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime());
   } else {
     aSeekable->Add(0, duration);
   }
-  MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable ranges=%s", this, DumpTimeRanges(aSeekable).get());
+  MSE_DEBUG("ranges=%s", DumpTimeRanges(aSeekable).get());
   return NS_OK;
 }
 
 void
 MediaSourceDecoder::Shutdown()
 {
-  MSE_DEBUG("MediaSourceDecoder(%p)::Shutdown", this);
+  MSE_DEBUG("Shutdown");
   // Detach first so that TrackBuffers are unused on the main thread when
   // shut down on the decode task queue.
   if (mMediaSource) {
     mMediaSource->Detach();
   }
 
   MediaDecoder::Shutdown();
   // Kick WaitForData out of its slumber.
@@ -222,17 +220,23 @@ MediaSourceDecoder::SetInitialDuration(i
 }
 
 void
 MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   double oldDuration = mMediaSourceDuration;
   if (aDuration >= 0) {
-    mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
+    int64_t checkedDuration;
+    if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
+      // INT64_MAX is used as infinity by the state machine.
+      // We want a very bigger number, but not infinity.
+      checkedDuration = INT64_MAX - 1;
+    }
+    mDecoderStateMachine->SetDuration(checkedDuration);
     mMediaSourceDuration = aDuration;
   } else {
     mDecoderStateMachine->SetDuration(INT64_MAX);
     mMediaSourceDuration = PositiveInfinity<double>();
   }
   if (mReader) {
     mReader->SetMediaSourceDuration(mMediaSourceDuration);
   }
@@ -304,9 +308,19 @@ MediaSourceDecoder::SetCDMProxy(CDMProxy
 #endif
 
 bool
 MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
 {
   return mReader->IsActiveReader(aReader);
 }
 
+double
+MediaSourceDecoder::GetDuration()
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  return mMediaSourceDuration;
+}
+
+#undef MSE_DEBUG
+#undef MSE_DEBUGV
+
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -51,16 +51,19 @@ public:
                                                          int64_t aTimestampOffset /* microseconds */);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
   void Ended();
   bool IsExpectingMoreData() MOZ_OVERRIDE;
 
+  // Return the duration of the video in seconds.
+  virtual double GetDuration() MOZ_OVERRIDE;
+
   void SetInitialDuration(int64_t aDuration);
   void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
   double GetMediaSourceDuration();
   void DurationChanged(double aOldDuration, double aNewDuration);
 
   // Called whenever a TrackBuffer has new data appended or a new decoder
   // initializes.  Safe to call from any thread.
   void NotifyTimeRangesChanged();
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -17,26 +17,21 @@
 
 #ifdef MOZ_FMP4
 #include "SharedDecoderManager.h"
 #include "MP4Decoder.h"
 #include "MP4Reader.h"
 #endif
 
 #ifdef PR_LOGGING
-extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
-
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSourceReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define MSE_DEBUGV(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG + 1, ("MediaSourceReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
-#define MSE_API(...)
 #endif
 
 // When a stream hits EOS it needs to decide what other stream to switch to. Due
 // to inaccuracies is determining buffer end frames (Bug 1065207) and rounding
 // issues we use a fuzz factor to determine the end time of this stream for
 // switching to the new stream. This value is based on the end of frame
 // default value used in Blink, kDefaultBufferDurationInMs.
 #define EOS_FUZZ_US 125000
@@ -64,18 +59,17 @@ MediaSourceReader::MediaSourceReader(Med
 #endif
 {
 }
 
 void
 MediaSourceReader::PrepareInitialization()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MSE_DEBUG("MediaSourceReader(%p)::PrepareInitialization trackBuffers=%u",
-            this, mTrackBuffers.Length());
+  MSE_DEBUG("trackBuffers=%u", mTrackBuffers.Length());
   mEssentialTrackBuffers.AppendElements(mTrackBuffers);
   mHasEssentialTrackBuffers = true;
   mDecoder->NotifyWaitingForResourcesStatusChanged();
 }
 
 bool
 MediaSourceReader::IsWaitingMediaResources()
 {
@@ -89,44 +83,44 @@ MediaSourceReader::IsWaitingMediaResourc
 
   return !mHasEssentialTrackBuffers;
 }
 
 size_t
 MediaSourceReader::SizeOfVideoQueueInFrames()
 {
   if (!GetVideoReader()) {
-    MSE_DEBUG("MediaSourceReader(%p)::SizeOfVideoQueue called with no video reader", this);
+    MSE_DEBUG("called with no video reader");
     return 0;
   }
   return GetVideoReader()->SizeOfVideoQueueInFrames();
 }
 
 size_t
 MediaSourceReader::SizeOfAudioQueueInFrames()
 {
   if (!GetAudioReader()) {
-    MSE_DEBUG("MediaSourceReader(%p)::SizeOfAudioQueue called with no audio reader", this);
+    MSE_DEBUG("called with no audio reader");
     return 0;
   }
   return GetAudioReader()->SizeOfAudioQueueInFrames();
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MediaSourceReader::RequestAudioData()
 {
   nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
-  MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
+  MSE_DEBUGV("");
   if (!GetAudioReader()) {
-    MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
+    MSE_DEBUG("called with no audio reader");
     mAudioPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (IsSeeking()) {
-    MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called mid-seek. Rejecting.", this);
+    MSE_DEBUG("called mid-seek. Rejecting.");
     mAudioPromise.Reject(CANCELED, __func__);
     return p;
   }
   MOZ_DIAGNOSTIC_ASSERT(!mAudioSeekRequest.Exists());
 
   SwitchSourceResult ret = SwitchAudioSource(&mLastAudioTime);
   switch (ret) {
     case SOURCE_NEW:
@@ -162,22 +156,22 @@ MediaSourceReader::OnAudioDecoded(AudioD
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mAudioRequest.Complete();
 
   int64_t ourTime = aSample->mTime + mAudioSourceDecoder->GetTimestampOffset();
   if (aSample->mDiscontinuity) {
     mAudioDiscontinuity = true;
   }
 
-  MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
-             this, ourTime, aSample->mDuration, aSample->mDiscontinuity);
+  MSE_DEBUGV("[mTime=%lld mDuration=%lld mDiscontinuity=%d]",
+             ourTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropAudioBeforeThreshold) {
     if (ourTime < mTimeThreshold) {
-      MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
-                this, ourTime, mTimeThreshold);
+      MSE_DEBUG("mTime=%lld < mTimeThreshold=%lld",
+                ourTime, mTimeThreshold);
       mAudioRequest.Begin(GetAudioReader()->RequestAudioData()
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnAudioDecoded,
                                         &MediaSourceReader::OnAudioNotDecoded));
       return;
     }
     mDropAudioBeforeThreshold = false;
   }
@@ -223,17 +217,17 @@ AdjustEndTime(int64_t* aEndTime, SourceB
 }
 
 void
 MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mAudioRequest.Complete();
 
-  MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
+  MSE_DEBUG("aReason=%u IsEnded: %d", aReason, IsEnded());
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mAudioPromise.Reject(aReason, __func__);
     return;
   }
 
   // End of stream. Force switching past this stream to another reader by
   // switching to the end of the buffered range.
   MOZ_ASSERT(aReason == END_OF_STREAM);
@@ -252,30 +246,30 @@ MediaSourceReader::OnAudioNotDecoded(Not
 
   CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
 }
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
 MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__);
-  MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)",
-             this, aSkipToNextKeyframe, aTimeThreshold);
+  MSE_DEBUGV("RequestVideoData(%d, %lld)",
+             aSkipToNextKeyframe, aTimeThreshold);
   if (!GetVideoReader()) {
-    MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
+    MSE_DEBUG("called with no video reader");
     mVideoPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (aSkipToNextKeyframe) {
     mTimeThreshold = aTimeThreshold;
     mDropAudioBeforeThreshold = true;
     mDropVideoBeforeThreshold = true;
   }
   if (IsSeeking()) {
-    MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called mid-seek. Rejecting.", this);
+    MSE_DEBUG("called mid-seek. Rejecting.");
     mVideoPromise.Reject(CANCELED, __func__);
     return p;
   }
   MOZ_DIAGNOSTIC_ASSERT(!mVideoSeekRequest.Exists());
 
   SwitchSourceResult ret = SwitchVideoSource(&mLastVideoTime);
   switch (ret) {
     case SOURCE_NEW:
@@ -314,22 +308,22 @@ MediaSourceReader::OnVideoDecoded(VideoD
   mVideoRequest.Complete();
 
   // Adjust the sample time into our reference.
   int64_t ourTime = aSample->mTime + mVideoSourceDecoder->GetTimestampOffset();
   if (aSample->mDiscontinuity) {
     mVideoDiscontinuity = true;
   }
 
-  MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
-             this, ourTime, aSample->mDuration, aSample->mDiscontinuity);
+  MSE_DEBUGV("[mTime=%lld mDuration=%lld mDiscontinuity=%d]",
+             ourTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropVideoBeforeThreshold) {
     if (ourTime < mTimeThreshold) {
-      MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
-                this, ourTime, mTimeThreshold);
+      MSE_DEBUG("mTime=%lld < mTimeThreshold=%lld",
+                ourTime, mTimeThreshold);
       DoVideoRequest();
       return;
     }
     mDropVideoBeforeThreshold = false;
     mTimeThreshold = 0;
   }
 
   // Adjust the sample time into our reference.
@@ -348,17 +342,17 @@ MediaSourceReader::OnVideoDecoded(VideoD
 }
 
 void
 MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking());
   mVideoRequest.Complete();
 
-  MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
+  MSE_DEBUG("aReason=%u IsEnded: %d", aReason, IsEnded());
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mVideoPromise.Reject(aReason, __func__);
     return;
   }
 
   // End of stream. Force switching past this stream to another reader by
   // switching to the end of the buffered range.
   MOZ_ASSERT(aReason == END_OF_STREAM);
@@ -475,18 +469,18 @@ MediaSourceReader::SelectDecoder(int64_t
   // providing a given buffered range is expected to replace an older one.
   for (int32_t i = aTrackDecoders.Length() - 1; i >= 0; --i) {
     nsRefPtr<SourceBufferDecoder> newDecoder = aTrackDecoders[i];
 
     nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
     newDecoder->GetBuffered(ranges);
     if (ranges->Find(double(aTarget) / USECS_PER_S,
                      double(aTolerance) / USECS_PER_S) == dom::TimeRanges::NoIndex) {
-      MSE_DEBUGV("MediaSourceReader(%p)::SelectDecoder(%lld) newDecoder=%p target not in ranges=%s",
-                 this, aTarget, newDecoder.get(), DumpTimeRanges(ranges).get());
+      MSE_DEBUGV("SelectDecoder(%lld) newDecoder=%p target not in ranges=%s",
+                 aTarget, newDecoder.get(), DumpTimeRanges(ranges).get());
       continue;
     }
 
     return newDecoder.forget();
   }
 
   return nullptr;
 }
@@ -528,18 +522,18 @@ MediaSourceReader::SwitchAudioSource(int
       // start time.
       nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
       newDecoder->GetBuffered(ranges);
       int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
       if (*aTarget < startTime) {
         *aTarget = startTime;
       }
     }
-    MSE_DEBUGV("MediaSourceReader(%p)::SwitchAudioSource switched decoder to %p (fuzz:%d)",
-               this, mAudioSourceDecoder.get(), usedFuzz);
+    MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
+               mAudioSourceDecoder.get(), usedFuzz);
     return SOURCE_NEW;
   }
   return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
 }
 
 MediaSourceReader::SwitchSourceResult
 MediaSourceReader::SwitchVideoSource(int64_t* aTarget)
 {
@@ -568,18 +562,18 @@ MediaSourceReader::SwitchVideoSource(int
       // start time.
       nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
       newDecoder->GetBuffered(ranges);
       int64_t startTime = ranges->GetStartTime() * USECS_PER_S;
       if (*aTarget < startTime) {
         *aTarget = startTime;
       }
     }
-    MSE_DEBUGV("MediaSourceReader(%p)::SwitchVideoSource switched decoder to %p (fuzz:%d)",
-               this, mVideoSourceDecoder.get(), usedFuzz);
+    MSE_DEBUGV("switched decoder to %p (fuzz:%d)",
+               mVideoSourceDecoder.get(), usedFuzz);
     return SOURCE_NEW;
   }
   return newDecoder ? SOURCE_EXISTING : SOURCE_ERROR;
 }
 
 bool
 MediaSourceReader::IsDormantNeeded()
 {
@@ -648,59 +642,59 @@ MediaSourceReader::CreateSubDecoder(cons
   // borrowing.
   reader->SetBorrowedTaskQueue(GetTaskQueue());
 
 #ifdef MOZ_FMP4
   reader->SetSharedDecoderManager(mSharedDecoderManager);
 #endif
   reader->Init(nullptr);
 
-  MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p",
-            this, decoder.get(), reader.get());
+  MSE_DEBUG("subdecoder %p subreader %p",
+            decoder.get(), reader.get());
   decoder->SetReader(reader);
 #ifdef MOZ_EME
   decoder->SetCDMProxy(mCDMProxy);
 #endif
   return decoder.forget();
 }
 
 void
 MediaSourceReader::AddTrackBuffer(TrackBuffer* aTrackBuffer)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MSE_DEBUG("MediaSourceReader(%p)::AddTrackBuffer %p", this, aTrackBuffer);
+  MSE_DEBUG("AddTrackBuffer(%p)", aTrackBuffer);
   mTrackBuffers.AppendElement(aTrackBuffer);
 }
 
 void
 MediaSourceReader::RemoveTrackBuffer(TrackBuffer* aTrackBuffer)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MSE_DEBUG("MediaSourceReader(%p)::RemoveTrackBuffer %p", this, aTrackBuffer);
+  MSE_DEBUG("RemoveTrackBuffer(%p)", aTrackBuffer);
   mTrackBuffers.RemoveElement(aTrackBuffer);
   if (mAudioTrack == aTrackBuffer) {
     mAudioTrack = nullptr;
   }
   if (mVideoTrack == aTrackBuffer) {
     mVideoTrack = nullptr;
   }
 }
 
 void
 MediaSourceReader::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(aTrackBuffer->IsReady());
   MOZ_ASSERT(mTrackBuffers.Contains(aTrackBuffer));
   if (aInfo.HasAudio() && !mAudioTrack) {
-    MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p audio", this, aTrackBuffer);
+    MSE_DEBUG("%p audio", aTrackBuffer);
     mAudioTrack = aTrackBuffer;
   }
   if (aInfo.HasVideo() && !mVideoTrack) {
-    MSE_DEBUG("MediaSourceReader(%p)::OnTrackBufferConfigured %p video", this, aTrackBuffer);
+    MSE_DEBUG("%p video", aTrackBuffer);
     mVideoTrack = aTrackBuffer;
   }
   mDecoder->NotifyWaitingForResourcesStatusChanged();
 }
 
 bool
 MediaSourceReader::TrackBuffersContainTime(int64_t aTime)
 {
@@ -724,18 +718,18 @@ MediaSourceReader::NotifyTimeRangesChang
         this, &MediaSourceReader::AttemptSeek));
     GetTaskQueue()->Dispatch(task.forget());
   }
 }
 
 nsRefPtr<MediaDecoderReader::SeekPromise>
 MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg which is non-MSE */)
 {
-  MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
-            this, aTime);
+  MSE_DEBUG("Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
+            aTime);
 
   MOZ_ASSERT(mSeekPromise.IsEmpty());
   nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
 
   if (IsShutdown()) {
     mSeekPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
@@ -820,17 +814,17 @@ MediaSourceReader::OnVideoSeekFailed(nsr
 void
 MediaSourceReader::DoAudioSeek()
 {
   SwitchAudioSource(&mPendingSeekTime);
   mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mPendingSeekTime), 0)
                          ->RefableThen(GetTaskQueue(), __func__, this,
                                        &MediaSourceReader::OnAudioSeekCompleted,
                                        &MediaSourceReader::OnAudioSeekFailed));
-  MSE_DEBUG("MediaSourceReader(%p)::DoAudioSeek reader=%p", this, GetAudioReader());
+  MSE_DEBUG("reader=%p", GetAudioReader());
 }
 
 void
 MediaSourceReader::OnAudioSeekCompleted(int64_t aTime)
 {
   mAudioSeekRequest.Complete();
   mPendingSeekTime = -1;
   // The aTime we receive is in the sub-reader's reference.
@@ -880,17 +874,17 @@ MediaSourceReader::AttemptSeek()
 void
 MediaSourceReader::DoVideoSeek()
 {
   SwitchVideoSource(&mPendingSeekTime);
   mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mPendingSeekTime), 0)
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnVideoSeekCompleted,
                                         &MediaSourceReader::OnVideoSeekFailed));
-  MSE_DEBUG("MediaSourceReader(%p)::DoVideoSeek reader=%p", this, GetVideoReader());
+  MSE_DEBUG("reader=%p", GetVideoReader());
 }
 
 nsresult
 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(aBuffered->Length() == 0);
   if (mTrackBuffers.IsEmpty()) {
@@ -919,17 +913,17 @@ MediaSourceReader::GetBuffered(dom::Time
       // Normalize() will then merge with the old last range.
       sourceRanges->Add(sourceRanges->GetEndTime(), highestEndTime);
       sourceRanges->Normalize();
     }
 
     intersectionRanges->Intersection(sourceRanges);
   }
 
-  MSE_DEBUG("MediaSourceReader(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get());
+  MSE_DEBUG("ranges=%s", DumpTimeRanges(intersectionRanges).get());
   return NS_OK;
 }
 
 nsRefPtr<MediaDecoderReader::WaitForDataPromise>
 MediaSourceReader::WaitForData(MediaData::Type aType)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
@@ -945,59 +939,59 @@ MediaSourceReader::MaybeNotifyHaveData()
   if (!IsSeeking() && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
     haveAudio = true;
     WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
   }
   if (!IsSeeking() && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) {
     haveVideo = true;
     WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
   }
-  MSE_DEBUG("MediaSourceReader(%p)::MaybeNotifyHaveData haveAudio=%d, haveVideo=%d", this,
-            haveAudio, haveVideo);
+  MSE_DEBUG("isSeeking=%d haveAudio=%d, haveVideo=%d",
+            IsSeeking(), haveAudio, haveVideo);
 }
 
 nsresult
 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-  MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata tracks=%u/%u audio=%p video=%p",
-            this, mEssentialTrackBuffers.Length(), mTrackBuffers.Length(),
+  MSE_DEBUG("tracks=%u/%u audio=%p video=%p",
+            mEssentialTrackBuffers.Length(), mTrackBuffers.Length(),
             mAudioTrack.get(), mVideoTrack.get());
 
   mEssentialTrackBuffers.Clear();
   if (!mAudioTrack && !mVideoTrack) {
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
-              this, mAudioTrack.get(), mVideoTrack.get());
+    MSE_DEBUG("missing track: mAudioTrack=%p mVideoTrack=%p",
+              mAudioTrack.get(), mVideoTrack.get());
     return NS_ERROR_FAILURE;
   }
 
   if (mAudioTrack) {
     MOZ_ASSERT(mAudioTrack->IsReady());
     mAudioSourceDecoder = mAudioTrack->Decoders()[0];
 
     const MediaInfo& info = GetAudioReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasAudio());
     mInfo.mAudio = info.mAudio;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
-              this, mAudioSourceDecoder.get(),
+    MSE_DEBUG("audio reader=%p duration=%lld",
+              mAudioSourceDecoder.get(),
               mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
   }
 
   if (mVideoTrack) {
     MOZ_ASSERT(mVideoTrack->IsReady());
     mVideoSourceDecoder = mVideoTrack->Decoders()[0];
 
     const MediaInfo& info = GetVideoReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
     mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
-    MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
-              this, GetVideoReader(),
+    MSE_DEBUG("video reader=%p duration=%lld",
+              GetVideoReader(),
               GetVideoReader()->GetDecoder()->GetMediaDuration());
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
@@ -1131,9 +1125,11 @@ MediaSourceReader::GetReaderAudioTime(in
 }
 
 int64_t
 MediaSourceReader::GetReaderVideoTime(int64_t aTime) const
 {
   return aTime - mVideoSourceDecoder->GetTimestampOffset();
 }
 
+#undef MSE_DEBUG
+#undef MSE_DEBUGV
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -7,24 +7,23 @@
 #ifndef MOZILLA_MEDIASOURCERESOURCE_H_
 #define MOZILLA_MEDIASOURCERESOURCE_H_
 
 #include "MediaResource.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSourceResource(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #endif
 
-#define UNIMPLEMENTED() MSE_DEBUG("MediaSourceResource(%p): UNIMPLEMENTED FUNCTION at %s:%d", this, __FILE__, __LINE__)
+#define UNIMPLEMENTED() MSE_DEBUG("UNIMPLEMENTED FUNCTION at %s:%d", __FILE__, __LINE__)
 
 namespace mozilla {
 
 class MediaSourceResource MOZ_FINAL : public MediaResource
 {
 public:
   explicit MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
     : mPrincipal(aPrincipal) {}
@@ -82,11 +81,12 @@ private:
   }
 
   nsRefPtr<nsIPrincipal> mPrincipal;
   const nsCString mType;
 };
 
 } // namespace mozilla
 
+#undef MSE_DEBUG
 #undef UNIMPLEMENTED
 
 #endif /* MOZILLA_MEDIASOURCERESOURCE_H_ */
--- a/dom/media/mediasource/ResourceQueue.h
+++ b/dom/media/mediasource/ResourceQueue.h
@@ -10,18 +10,23 @@
 #include <algorithm>
 #include "nsDeque.h"
 #include "MediaData.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetSourceBufferResourceLog();
 
-#define SBR_DEBUG(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define SBR_DEBUGV(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
+/* Polyfill __func__ on MSVC to pass to the log. */
+#ifdef _MSC_VER
+#define __func__ __FUNCTION__
+#endif
+
+#define SBR_DEBUG(arg, ...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, ("ResourceQueue(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define SBR_DEBUGV(arg, ...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, ("ResourceQueue(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #else
 #define SBR_DEBUG(...)
 #define SBR_DEBUGV(...)
 #endif
 
 namespace mozilla {
 
 // A SourceBufferResource has a queue containing the data that is appended
@@ -101,43 +106,43 @@ public:
   void AppendItem(LargeDataBuffer* aData) {
     mLogicalLength += aData->Length();
     Push(new ResourceItem(aData));
   }
 
   // Tries to evict at least aSizeToEvict from the queue up until
   // aOffset. Returns amount evicted.
   uint32_t Evict(uint64_t aOffset, uint32_t aSizeToEvict) {
-    SBR_DEBUG("ResourceQueue(%p)::Evict(aOffset=%llu, aSizeToEvict=%u)",
-              this, aOffset, aSizeToEvict);
+    SBR_DEBUG("Evict(aOffset=%llu, aSizeToEvict=%u)",
+              aOffset, aSizeToEvict);
     return EvictBefore(std::min(aOffset, mOffset + (uint64_t)aSizeToEvict));
   }
 
   uint32_t EvictBefore(uint64_t aOffset) {
-    SBR_DEBUG("ResourceQueue(%p)::EvictBefore(%llu)", this, aOffset);
+    SBR_DEBUG("EvictBefore(%llu)", aOffset);
     uint32_t evicted = 0;
     while (ResourceItem* item = ResourceAt(0)) {
-      SBR_DEBUG("ResourceQueue(%p)::EvictBefore item=%p length=%d offset=%llu",
-                this, item, item->mData->Length(), mOffset);
+      SBR_DEBUG("item=%p length=%d offset=%llu",
+                item, item->mData->Length(), mOffset);
       if (item->mData->Length() + mOffset >= aOffset) {
         break;
       }
       mOffset += item->mData->Length();
       evicted += item->mData->Length();
       delete PopFront();
     }
     return evicted;
   }
 
   uint32_t EvictAll() {
-    SBR_DEBUG("ResourceQueue(%p)::EvictAll()", this);
+    SBR_DEBUG("EvictAll()");
     uint32_t evicted = 0;
     while (ResourceItem* item = ResourceAt(0)) {
-      SBR_DEBUG("ResourceQueue(%p)::EvictAll item=%p length=%d offset=%llu",
-                this, item, item->mData->Length(), mOffset);
+      SBR_DEBUG("item=%p length=%d offset=%llu",
+                item, item->mData->Length(), mOffset);
       mOffset += item->mData->Length();
       evicted += item->mData->Length();
       delete PopFront();
     }
     return evicted;
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
@@ -204,10 +209,13 @@ private:
 
   // Logical length of the resource.
   uint64_t mLogicalLength;
 
   // Logical offset into the resource of the first element in the queue.
   uint64_t mOffset;
 };
 
+#undef SBR_DEBUG
+#undef SBR_DEBUGV
+
 } // namespace mozilla
 #endif /* MOZILLA_RESOURCEQUEUE_H_ */
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -24,19 +24,19 @@
 
 struct JSContext;
 class JSObject;
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
+#define MSE_DEBUGV(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG + 1, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
+#define MSE_API(arg, ...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
 #define MSE_DEBUGV(...)
 #define MSE_API(...)
 #endif
 
 namespace mozilla {
 
@@ -67,41 +67,46 @@ private:
   nsRefPtr<LargeDataBuffer> mData;
   double mTimestampOffset;
   uint32_t mUpdateID;
 };
 
 class RangeRemovalRunnable : public nsRunnable {
 public:
   RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
-                     double aStart,
-                     double aEnd)
+                       double aStart,
+                       double aEnd)
   : mSourceBuffer(aSourceBuffer)
   , mStart(aStart)
   , mEnd(aEnd)
   { }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
 
+    if (!mSourceBuffer->mUpdating) {
+      // abort was called in between.
+      return NS_OK;
+    }
     mSourceBuffer->DoRangeRemoval(mStart, mEnd);
+    mSourceBuffer->StopUpdating();
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<SourceBuffer> mSourceBuffer;
   double mStart;
   double mEnd;
 };
 
 void
 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetMode(aMode=%d)", this, aMode);
+  MSE_API("SetMode(aMode=%d)", aMode);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aMode == SourceBufferAppendMode::Sequence) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
@@ -113,17 +118,17 @@ SourceBuffer::SetMode(SourceBufferAppend
   // TODO: If aMode is "sequence", set sequence start time.
   mAppendMode = aMode;
 }
 
 void
 SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetTimestampOffset(aTimestampOffset=%f)", this, aTimestampOffset);
+  MSE_API("SetTimestampOffset(aTimestampOffset=%f)", aTimestampOffset);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
@@ -144,90 +149,90 @@ SourceBuffer::GetBuffered(ErrorResult& a
   double highestEndTime = mTrackBuffer->Buffered(ranges);
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     // Set the end time on the last range to highestEndTime by adding a
     // new range spanning the current end time to highestEndTime, which
     // Normalize() will then merge with the old last range.
     ranges->Add(ranges->GetEndTime(), highestEndTime);
     ranges->Normalize();
   }
-  MSE_DEBUGV("SourceBuffer(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(ranges).get());
+  MSE_DEBUGV("ranges=%s", DumpTimeRanges(ranges).get());
   return ranges.forget();
 }
 
 void
 SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowStart(aAppendWindowStart=%f)", this, aAppendWindowStart);
+  MSE_API("SetAppendWindowStart(aAppendWindowStart=%f)", aAppendWindowStart);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   mAppendWindowStart = aAppendWindowStart;
 }
 
 void
 SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::SetAppendWindowEnd(aAppendWindowEnd=%f)", this, aAppendWindowEnd);
+  MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd);
   if (!IsAttached() || mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (IsNaN(aAppendWindowEnd) ||
       aAppendWindowEnd <= mAppendWindowStart) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
   mAppendWindowEnd = aAppendWindowEnd;
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBuffer)", this);
+  MSE_API("AppendBuffer(ArrayBuffer)");
   aData.ComputeLengthAndData();
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::AppendBuffer(ArrayBufferView)", this);
+  MSE_API("AppendBuffer(ArrayBufferView)");
   aData.ComputeLengthAndData();
   AppendData(aData.Data(), aData.Length(), aRv);
 }
 
 void
 SourceBuffer::Abort(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::Abort()", this);
+  MSE_API("Abort()");
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   AbortBufferAppend();
   mTrackBuffer->ResetParserState();
   mAppendWindowStart = 0;
   mAppendWindowEnd = PositiveInfinity<double>();
   // Discard the current decoder so no new data will be added to it.
-  MSE_DEBUG("SourceBuffer(%p)::Abort() Discarding decoder", this);
+  MSE_DEBUG("Discarding decoder");
   mTrackBuffer->DiscardCurrentDecoder();
 }
 
 void
 SourceBuffer::AbortBufferAppend()
 {
   if (mUpdating) {
     mPendingAppend.DisconnectIfExists();
@@ -237,17 +242,17 @@ SourceBuffer::AbortBufferAppend()
     AbortUpdating();
   }
 }
 
 void
 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd);
+  MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd);
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (IsNaN(mMediaSource->Duration()) ||
       aStart < 0 || aStart > mMediaSource->Duration() ||
       aEnd <= aStart || IsNaN(aEnd)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
@@ -255,92 +260,90 @@ SourceBuffer::Remove(double aStart, doub
   }
   if (mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
     mMediaSource->SetReadyState(MediaSourceReadyState::Open);
   }
-  RangeRemoval(aStart, aEnd);
+
+  StartUpdating();
+  nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
+  NS_DispatchToMainThread(task);
 }
 
 void
 SourceBuffer::RangeRemoval(double aStart, double aEnd)
 {
   StartUpdating();
-
-  nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
+  DoRangeRemoval(aStart, aEnd);
+  nsRefPtr<nsIRunnable> task =
+    NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(task);
 }
 
 void
 SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
 {
-  MSE_DEBUG("SourceBuffer(%p)::DoRangeRemoval (updating:%d)",
-            this, mUpdating);
-  if (!mUpdating) {
-    // abort was called in between.
-    return;
-  }
+  MSE_DEBUG("DoRangeRemoval(%f, %f)", aStart, aEnd);
   if (mTrackBuffer && !IsInfinite(aStart)) {
     int64_t start = aStart * USECS_PER_S;
     int64_t end = IsInfinite(aEnd) ? INT64_MAX : (int64_t)(aEnd * USECS_PER_S);
     mTrackBuffer->RangeRemoval(start, end);
   }
-
-  StopUpdating();
 }
 
 void
 SourceBuffer::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Detach", this);
+  MSE_DEBUG("Detach");
   AbortBufferAppend();
   if (mTrackBuffer) {
     mTrackBuffer->Detach();
   }
   mTrackBuffer = nullptr;
   mMediaSource = nullptr;
 }
 
 void
 SourceBuffer::Ended()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(IsAttached());
-  MSE_DEBUG("SourceBuffer(%p)::Ended", this);
+  MSE_DEBUG("Ended");
   mTrackBuffer->EndCurrentDecoder();
 }
 
 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
   : DOMEventTargetHelper(aMediaSource->GetParentObject())
   , mMediaSource(aMediaSource)
   , mAppendWindowStart(0)
   , mAppendWindowEnd(PositiveInfinity<double>())
   , mTimestampOffset(0)
   , mAppendMode(SourceBufferAppendMode::Segments)
   , mUpdating(false)
   , mUpdateID(0)
+  , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
   mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
                                             75 * (1 << 20));
   mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
-  MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
-            this, mTrackBuffer.get());
+  MSE_DEBUG("Create mTrackBuffer=%p",
+            mTrackBuffer.get());
 }
 
 SourceBuffer::~SourceBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mMediaSource);
-  MSE_DEBUG("SourceBuffer(%p)::~SourceBuffer", this);
+  MSE_DEBUG("");
 }
 
 MediaSource*
 SourceBuffer::GetParentObject() const
 {
   return mMediaSource;
 }
 
@@ -349,24 +352,24 @@ SourceBuffer::WrapObject(JSContext* aCx)
 {
   return SourceBufferBinding::Wrap(aCx, this);
 }
 
 void
 SourceBuffer::DispatchSimpleEvent(const char* aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBuffer(%p) Dispatch event '%s'", this, aName);
+  MSE_API("Dispatch event '%s'", aName);
   DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
 }
 
 void
 SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
 {
-  MSE_DEBUG("SourceBuffer(%p) Queuing event '%s'", this, aName);
+  MSE_DEBUG("Queuing event '%s'", aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 void
 SourceBuffer::StartUpdating()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -414,17 +417,17 @@ SourceBuffer::CheckEndTime()
   if (endTime > duration) {
     mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
-  MSE_DEBUG("SourceBuffer(%p)::AppendData(aLength=%u)", this, aLength);
+  MSE_DEBUG("AppendData(aLength=%u)", aLength);
 
   nsRefPtr<LargeDataBuffer> data = PrepareAppend(aData, aLength, aRv);
   if (!data) {
     return;
   }
   StartUpdating();
 
   MOZ_ASSERT(mAppendMode == SourceBufferAppendMode::Segments,
@@ -543,18 +546,18 @@ SourceBuffer::PrepareAppend(const uint8_
   // Attempt to evict the amount of data we are about to add by lowering the
   // threshold.
   uint32_t toEvict =
     (mEvictionThreshold > aLength) ? mEvictionThreshold - aLength : aLength;
   bool evicted =
     mTrackBuffer->EvictData(mMediaSource->GetDecoder()->GetCurrentTime(),
                             toEvict, &newBufferStartTime);
   if (evicted) {
-    MSE_DEBUG("SourceBuffer(%p)::AppendData Evict; current buffered start=%f",
-              this, GetBufferedStart());
+    MSE_DEBUG("AppendData Evict; current buffered start=%f",
+              GetBufferedStart());
 
     // We notify that we've evicted from the time range 0 through to
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, newBufferStartTime);
   }
 
   nsRefPtr<LargeDataBuffer> data = new LargeDataBuffer();
   if (!data->AppendElements(aData, aLength)) {
@@ -582,17 +585,17 @@ SourceBuffer::GetBufferedEnd()
   nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
   return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
 }
 
 void
 SourceBuffer::Evict(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBuffer(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
+  MSE_DEBUG("Evict(aStart=%f, aEnd=%f)", aStart, aEnd);
   double currentTime = mMediaSource->GetDecoder()->GetCurrentTime();
   double evictTime = aEnd;
   const double safety_threshold = 5;
   if (currentTime + safety_threshold >= evictTime) {
     evictTime -= safety_threshold;
   }
   mTrackBuffer->EvictBefore(evictTime);
 }
@@ -624,11 +627,15 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
+#undef MSE_DEBUG
+#undef MSE_DEBUGV
+#undef MSE_API
+
 } // namespace dom
 
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -108,29 +108,30 @@ public:
 
   // Evict data in the source buffer in the given time range.
   void Evict(double aStart, double aEnd);
 
   double GetBufferedStart();
   double GetBufferedEnd();
 
   // Runs the range removal algorithm as defined by the MSE spec.
-  // RangeRemoval will queue a call to DoRangeRemoval.
   void RangeRemoval(double aStart, double aEnd);
+  // Actually remove data between aStart and aEnd
   void DoRangeRemoval(double aStart, double aEnd);
 
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~SourceBuffer();
 
   friend class AsyncEventRunner<SourceBuffer>;
   friend class AppendDataRunnable;
+  friend class RangeRemovalRunnable;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   // Update mUpdating and fire the appropriate events.
   void StartUpdating();
   void StopUpdating();
   void AbortUpdating();
 
@@ -174,14 +175,15 @@ private:
   bool mUpdating;
 
   // Each time mUpdating is set to true, mUpdateID will be incremented.
   // This allows for a queued AppendData task to identify if it was earlier
   // aborted and another AppendData queued.
   uint32_t mUpdateID;
 
   MediaPromiseConsumerHolder<TrackBufferAppendPromise> mPendingAppend;
+  const nsCString mType;
 };
 
 } // namespace dom
 
 } // namespace mozilla
 #endif /* mozilla_dom_SourceBuffer_h_ */
--- a/dom/media/mediasource/SourceBufferDecoder.cpp
+++ b/dom/media/mediasource/SourceBufferDecoder.cpp
@@ -1,29 +1,27 @@
 /* -*- 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 "MediaSourceUtils.h"
 #include "SourceBufferDecoder.h"
 #include "prlog.h"
 #include "AbstractMediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "mozilla/dom/TimeRanges.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (TOSTRING(name) "SourceBufferDecoder(%p:%s)::%s: " arg, this, mResource->GetContentType().get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
-#define MSE_API(...)
 #endif
 
 namespace mozilla {
 
 class ReentrantMonitor;
 
 namespace layers {
 
@@ -52,110 +50,110 @@ SourceBufferDecoder::~SourceBufferDecode
 {
   MOZ_COUNT_DTOR(SourceBufferDecoder);
 }
 
 bool
 SourceBufferDecoder::IsShutdown() const
 {
   // SourceBufferDecoder cannot be shut down.
-  MSE_DEBUG("SourceBufferDecoder(%p)::IsShutdown UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
   return false;
 }
 
 void
 SourceBufferDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::NotifyBytesConsumed UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 int64_t
 SourceBufferDecoder::GetMediaDuration()
 {
   return mMediaDuration;
 }
 
 VideoFrameContainer*
 SourceBufferDecoder::GetVideoFrameContainer()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::GetVideoFrameContainer UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
   return nullptr;
 }
 
 bool
 SourceBufferDecoder::IsTransportSeekable()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::IsTransportSeekable UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
   return false;
 }
 
 bool
 SourceBufferDecoder::IsMediaSeekable()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::IsMediaSeekable UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
   return false;
 }
 
 void
 SourceBufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
                                     nsAutoPtr<MetadataTags> aTags,
                                     bool aRestoredFromDormant)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::MetadataLoaded UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
                                       bool aRestoredFromDormant)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::FirstFrameLoaded UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::QueueMetadata(int64_t aTime,
                                    nsAutoPtr<MediaInfo> aInfo,
                                    nsAutoPtr<MetadataTags> aTags)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::QueueMetadata UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::RemoveMediaTracks()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::RemoveMediaTracks UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::SetMediaEndTime(int64_t aTime)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::SetMediaEndTime UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::UpdatePlaybackPosition(int64_t aTime)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::UpdatePlaybackPosition UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 bool
 SourceBufferDecoder::HasInitializationData()
 {
   return true;
 }
 
 void
 SourceBufferDecoder::OnReadMetadataCompleted()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::OnReadMetadataCompleted UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::NotifyWaitingForResourcesStatusChanged()
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::NotifyWaitingForResourcesStatusChanged UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 ReentrantMonitor&
 SourceBufferDecoder::GetReentrantMonitor()
 {
   return mParentDecoder->GetReentrantMonitor();
 }
 
@@ -203,23 +201,23 @@ void
 SourceBufferDecoder::Trim(int64_t aDuration)
 {
   mTrimmedOffset = (double)aDuration / USECS_PER_S;
 }
 
 void
 SourceBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::UpdateEstimatedMediaDuration UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 void
 SourceBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::SetMediaSeekable UNIMPLEMENTED", this);
+  MSE_DEBUG("UNIMPLEMENTED");
 }
 
 layers::ImageContainer*
 SourceBufferDecoder::GetImageContainer()
 {
   return mParentDecoder->GetImageContainer();
 }
 
@@ -280,9 +278,10 @@ SourceBufferDecoder::ConvertToByteOffset
   int64_t length = GetResource()->GetLength();
   MOZ_ASSERT(length > 0);
   int64_t offset =
     ((aTime - double(mTimestampOffset) / USECS_PER_S) /
       (double(mRealMediaDuration) / USECS_PER_S)) * length;
   return offset;
 }
 
+#undef MSE_DEBUG
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferList.cpp
+++ b/dom/media/mediasource/SourceBufferList.cpp
@@ -11,30 +11,28 @@
 #include "mozilla/dom/SourceBufferListBinding.h"
 #include "mozilla/mozalloc.h"
 #include "nsCOMPtr.h"
 #include "nsIRunnable.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* GetMediaSourceAPILog();
+#define MSE_API(arg, ...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, ("SourceBufferList(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("SourceBufferList(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#else
+#define MSE_API(...)
+#define MSE_DEBUG(...)
+#endif
+
 struct JSContext;
 class JSObject;
 
-#ifdef PR_LOGGING
-extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
-
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define MSE_DEBUG(...)
-#define MSE_API(...)
-#endif
-
 namespace mozilla {
 
 namespace dom {
 
 SourceBufferList::~SourceBufferList()
 {
 }
 
@@ -106,27 +104,27 @@ SourceBufferList::AnyUpdating()
   }
   return false;
 }
 
 void
 SourceBufferList::RangeRemoval(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBufferList(%p)::RangeRemoval(aStart=%f, aEnd=%f", this, aStart, aEnd);
+  MSE_DEBUG("RangeRemoval(aStart=%f, aEnd=%f)", aStart, aEnd);
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
     mSourceBuffers[i]->RangeRemoval(aStart, aEnd);
   }
 }
 
 void
 SourceBufferList::Evict(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBufferList(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
+  MSE_DEBUG("Evict(aStart=%f, aEnd=%f)", aStart, aEnd);
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
     mSourceBuffers[i]->Evict(aStart, aEnd);
   }
 }
 
 void
 SourceBufferList::Ended()
 {
@@ -146,24 +144,24 @@ SourceBufferList::GetHighestBufferedEndT
   }
   return highestEndTime;
 }
 
 void
 SourceBufferList::DispatchSimpleEvent(const char* aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_API("SourceBufferList(%p) Dispatch event '%s'", this, aName);
+  MSE_API("Dispatch event '%s'", aName);
   DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
 }
 
 void
 SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
 {
-  MSE_DEBUG("SourceBufferList(%p) Queuing event '%s'", this, aName);
+  MSE_DEBUG("Queue event '%s'", aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBufferList>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 #if defined(DEBUG)
 void
 SourceBufferList::Dump(const char* aPath)
 {
@@ -196,11 +194,13 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Sourc
                                    mMediaSource, mSourceBuffers)
 
 NS_IMPL_ADDREF_INHERITED(SourceBufferList, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(SourceBufferList, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBufferList)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
+#undef MSE_API
+#undef MSE_DEBUG
 } // namespace dom
 
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferResource.cpp
+++ b/dom/media/mediasource/SourceBufferResource.cpp
@@ -18,41 +18,41 @@ PRLogModuleInfo* GetSourceBufferResource
 {
   static PRLogModuleInfo* sLogModule;
   if (!sLogModule) {
     sLogModule = PR_NewLogModule("SourceBufferResource");
   }
   return sLogModule;
 }
 
-#define SBR_DEBUG(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define SBR_DEBUGV(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
+#define SBR_DEBUG(arg, ...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, ("SourceBufferResource(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
+#define SBR_DEBUGV(arg, ...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, ("SourceBufferResource(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define SBR_DEBUG(...)
 #define SBR_DEBUGV(...)
 #endif
 
 namespace mozilla {
 
 nsresult
 SourceBufferResource::Close()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
-  SBR_DEBUG("SourceBufferResource(%p)::Close", this);
+  SBR_DEBUG("Close");
   //MOZ_ASSERT(!mClosed);
   mClosed = true;
   mon.NotifyAll();
   return NS_OK;
 }
 
 nsresult
 SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
-  SBR_DEBUGV("SourceBufferResource(%p)::Read(aBuffer=%p, aCount=%u, aBytes=%p)",
-             this, aBytes, aCount, aBytes);
+  SBR_DEBUGV("Read(aBuffer=%p, aCount=%u, aBytes=%p)",
+             aBytes, aCount, aBytes);
   ReentrantMonitorAutoEnter mon(mMonitor);
 
   return ReadInternal(aBuffer, aCount, aBytes, /* aMayBlock = */ true);
 }
 
 nsresult
 SourceBufferResource::ReadInternal(char* aBuffer, uint32_t aCount, uint32_t* aBytes, bool aMayBlock)
 {
@@ -62,26 +62,26 @@ SourceBufferResource::ReadInternal(char*
   // Cache the offset for the read in case mOffset changes while waiting on the
   // monitor below. It's basically impossible to implement these API semantics
   // sanely. :-(
   uint64_t readOffset = mOffset;
 
   while (aMayBlock &&
          !mEnded &&
          readOffset + aCount > static_cast<uint64_t>(GetLength())) {
-    SBR_DEBUGV("SourceBufferResource(%p)::ReadInternal waiting for data", this);
+    SBR_DEBUGV("waiting for data");
     mMonitor.Wait();
   }
 
   uint32_t available = GetLength() - readOffset;
   uint32_t count = std::min(aCount, available);
-  SBR_DEBUGV("SourceBufferResource(%p)::ReadInternal() readOffset=%llu GetLength()=%u available=%u count=%u mEnded=%d",
+  SBR_DEBUGV("readOffset=%llu GetLength()=%u available=%u count=%u mEnded=%d",
              this, readOffset, GetLength(), available, count, mEnded);
   if (available == 0) {
-    SBR_DEBUGV("SourceBufferResource(%p)::ReadInternal() reached EOF", this);
+    SBR_DEBUGV("reached EOF");
     *aBytes = 0;
     return NS_OK;
   }
 
   mInputBuffer.CopyData(readOffset, count, aBuffer);
   *aBytes = count;
 
   // From IRC:
@@ -90,18 +90,18 @@ SourceBufferResource::ReadInternal(char*
   mOffset = readOffset + count;
 
   return NS_OK;
 }
 
 nsresult
 SourceBufferResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::ReadAt(aOffset=%lld, aBuffer=%p, aCount=%u, aBytes=%p)",
-            this, aOffset, aBytes, aCount, aBytes);
+  SBR_DEBUG("ReadAt(aOffset=%lld, aBuffer=%p, aCount=%u, aBytes=%p)",
+            aOffset, aBytes, aCount, aBytes);
   ReentrantMonitorAutoEnter mon(mMonitor);
   return ReadAtInternal(aOffset, aBuffer, aCount, aBytes, /* aMayBlock = */ true);
 }
 
 nsresult
 SourceBufferResource::ReadAtInternal(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes,
                                      bool aMayBlock)
 {
@@ -112,34 +112,35 @@ SourceBufferResource::ReadAtInternal(int
   }
 
   return ReadInternal(aBuffer, aCount, aBytes, aMayBlock);
 }
 
 nsresult
 SourceBufferResource::Seek(int32_t aWhence, int64_t aOffset)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::Seek(aWhence=%d, aOffset=%lld)", this, aWhence, aOffset);
+  SBR_DEBUG("Seek(aWhence=%d, aOffset=%lld)",
+            aWhence, aOffset);
   ReentrantMonitorAutoEnter mon(mMonitor);
 
   int64_t newOffset = mOffset;
   switch (aWhence) {
   case nsISeekableStream::NS_SEEK_END:
     newOffset = GetLength() - aOffset;
     break;
   case nsISeekableStream::NS_SEEK_CUR:
     newOffset += aOffset;
     break;
   case nsISeekableStream::NS_SEEK_SET:
     newOffset = aOffset;
     break;
   }
 
-  SBR_DEBUGV("SourceBufferResource(%p)::Seek() newOffset=%lld GetOffset()=%llu GetLength()=%llu)",
-             this, newOffset, mInputBuffer.GetOffset(), GetLength());
+  SBR_DEBUGV("newOffset=%lld GetOffset()=%llu GetLength()=%llu)",
+             newOffset, mInputBuffer.GetOffset(), GetLength());
   nsresult rv = SeekInternal(newOffset);
   mon.NotifyAll();
   return rv;
 }
 
 nsresult
 SourceBufferResource::SeekInternal(int64_t aOffset)
 {
@@ -154,88 +155,89 @@ SourceBufferResource::SeekInternal(int64
 
   mOffset = aOffset;
   return NS_OK;
 }
 
 nsresult
 SourceBufferResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::ReadFromCache(aBuffer=%p, aOffset=%lld, aCount=%u)",
-            this, aBuffer, aOffset, aCount);
+  SBR_DEBUG("ReadFromCache(aBuffer=%p, aOffset=%lld, aCount=%u)",
+            aBuffer, aOffset, aCount);
   ReentrantMonitorAutoEnter mon(mMonitor);
   uint32_t bytesRead;
   int64_t oldOffset = mOffset;
   nsresult rv = ReadAtInternal(aOffset, aBuffer, aCount, &bytesRead, /* aMayBlock = */ false);
   mOffset = oldOffset; // ReadFromCache isn't supposed to affect the seek position.
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ReadFromCache return failure if not all the data is cached.
   return bytesRead == aCount ? NS_OK : NS_ERROR_FAILURE;
 }
 
 uint32_t
 SourceBufferResource::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::EvictData(aPlaybackOffset=%llu,"
-            "aThreshold=%u)", this, aPlaybackOffset, aThreshold);
+  SBR_DEBUG("EvictData(aPlaybackOffset=%llu,"
+            "aThreshold=%u)", aPlaybackOffset, aThreshold);
   ReentrantMonitorAutoEnter mon(mMonitor);
   return mInputBuffer.Evict(aPlaybackOffset, aThreshold);
 }
 
 void
 SourceBufferResource::EvictBefore(uint64_t aOffset)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::EvictBefore(aOffset=%llu)", this, aOffset);
+  SBR_DEBUG("EvictBefore(aOffset=%llu)", aOffset);
   ReentrantMonitorAutoEnter mon(mMonitor);
   // If aOffset is past the current playback offset we don't evict.
   if (aOffset < mOffset) {
     mInputBuffer.EvictBefore(aOffset);
   }
 }
 
 uint32_t
 SourceBufferResource::EvictAll()
 {
-  SBR_DEBUG("SourceBufferResource(%p)::EvictAll()", this);
+  SBR_DEBUG("EvictAll()");
   ReentrantMonitorAutoEnter mon(mMonitor);
   return mInputBuffer.EvictAll();
 }
 
 void
 SourceBufferResource::AppendData(LargeDataBuffer* aData)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this,
+  SBR_DEBUG("AppendData(aData=%p, aLength=%u)",
             aData->Elements(), aData->Length());
   ReentrantMonitorAutoEnter mon(mMonitor);
   mInputBuffer.AppendItem(aData);
   mEnded = false;
   mon.NotifyAll();
 }
 
 void
 SourceBufferResource::Ended()
 {
-  SBR_DEBUG("SourceBufferResource(%p)::Ended()", this);
+  SBR_DEBUG("");
   ReentrantMonitorAutoEnter mon(mMonitor);
   mEnded = true;
   mon.NotifyAll();
 }
 
 SourceBufferResource::~SourceBufferResource()
 {
-  SBR_DEBUG("SourceBufferResource(%p)::~SourceBufferResource()", this);
+  SBR_DEBUG("");
   MOZ_COUNT_DTOR(SourceBufferResource);
 }
 
 SourceBufferResource::SourceBufferResource(const nsACString& aType)
   : mType(aType)
   , mMonitor("mozilla::SourceBufferResource::mMonitor")
   , mOffset(0)
   , mClosed(false)
   , mEnded(false)
 {
-  SBR_DEBUG("SourceBufferResource(%p)::SourceBufferResource(aType=%s)",
-            this, nsCString(aType).get());
+  SBR_DEBUG("");
   MOZ_COUNT_CTOR(SourceBufferResource);
 }
 
+#undef SBR_DEBUG
+#undef SBR_DEBUGV
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -15,25 +15,16 @@
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nscore.h"
 #include "prlog.h"
 
-#ifdef PR_LOGGING
-extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
-
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#else
-#define MSE_DEBUG(...)
-#endif
-
 #define UNIMPLEMENTED() { /* Logging this is too spammy to do by default */ }
 
 class nsIStreamListener;
 
 namespace mozilla {
 
 class MediaDecoder;
 class LargeDataBuffer;
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -18,25 +18,20 @@
 #include "mozilla/Preferences.h"
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
-extern PRLogModuleInfo* GetMediaSourceAPILog();
 
-#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
-#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
-#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
+#define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("TrackBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
-#define MSE_DEBUGV(...)
-#define MSE_API(...)
 #endif
 
 // Time in seconds to substract from the current time when deciding the
 // time point to evict data before in a decoder. This is used to help
 // prevent evicting the current playback point.
 #define MSE_EVICT_THRESHOLD_TIME 2.0
 
 // Time in microsecond under which a timestamp will be considered to be 0.
@@ -52,17 +47,17 @@ TrackBuffer::TrackBuffer(MediaSourceDeco
   , mAdjustedTimestamp(0)
   , mShutdown(false)
 {
   MOZ_COUNT_CTOR(TrackBuffer);
   mParser = ContainerParser::CreateForMIMEType(aType);
   mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
   aParentDecoder->AddTrackBuffer(this);
   mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
-  MSE_DEBUG("TrackBuffer(%p) created for parent decoder %p", this, aParentDecoder);
+  MSE_DEBUG("TrackBuffer created for parent decoder %p", aParentDecoder);
 }
 
 TrackBuffer::~TrackBuffer()
 {
   MOZ_COUNT_DTOR(TrackBuffer);
 }
 
 class MOZ_STACK_CLASS DecodersToInitialize MOZ_FINAL {
@@ -153,19 +148,19 @@ TrackBuffer::AppendData(LargeDataBuffer*
   nsRefPtr<TrackBufferAppendPromise> p = mInitializationPromise.Ensure(__func__);
   bool hadInitData = mParser->HasInitData();
   bool hadCompleteInitData = mParser->HasCompleteInitData();
   nsRefPtr<LargeDataBuffer> oldInit = mParser->InitData();
   bool newInitData = mParser->IsInitSegmentPresent(aData);
 
   // TODO: Run more of the buffer append algorithm asynchronously.
   if (newInitData) {
-    MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
+    MSE_DEBUG("New initialization segment.");
   } else if (!hadInitData) {
-    MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
+    MSE_DEBUG("Non-init segment appended during initialization.");
     mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
 
   int64_t start = 0, end = 0;
   bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
   bool gotInit = mParser->HasCompleteInitData();
 
@@ -194,35 +189,36 @@ TrackBuffer::AppendData(LargeDataBuffer*
   }
 
   if (gotMedia) {
     if (mParser->IsMediaSegmentPresent(aData) && mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment ||
          (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
-      MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
-                this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
+      MSE_DEBUG("Data last=[%lld, %lld] overlaps [%lld, %lld]",
+                mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       if (!newInitData) {
         // This data is earlier in the timeline than data we have already
         // processed or not continuous, so we must create a new decoder
         // to handle the decoding.
         if (!hadCompleteInitData ||
             !decoders.NewDecoder(aTimestampOffset)) {
           mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
           return p;
         }
-        MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
+        MSE_DEBUG("Decoder marked as initialized.");
         AppendDataToCurrentResource(oldInit, 0);
       }
       mLastStartTimestamp = start;
     } else {
-      MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
-                this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
+      MSE_DEBUG("Segment last=[%lld, %lld] [%lld, %lld]",
+                mLastStartTimestamp,
+                mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
   if (gotMedia && start != mAdjustedTimestamp &&
       ((start < 0 && -start < FUZZ_TIMESTAMP_OFFSET && start < mAdjustedTimestamp) ||
        (start > 0 && (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)))) {
@@ -329,67 +325,67 @@ TrackBuffer::EvictData(double aPlaybackT
   for (; i < decoders.Length() && toEvict > 0; ++i) {
     nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
     decoders[i]->GetBuffered(buffered);
     bool onCurrent = decoders[i] == mCurrentDecoder;
     if (onCurrent) {
       pastCurrentDecoder = false;
     }
 
-    MSE_DEBUG("TrackBuffer(%p)::EvictData decoder=%u/%u threshold=%u "
+    MSE_DEBUG("decoder=%u/%u threshold=%u "
               "toEvict=%lld current=%s pastCurrent=%s",
-              this, i, decoders.Length(), aThreshold, toEvict,
+              i, decoders.Length(), aThreshold, toEvict,
               onCurrent ? "true" : "false",
               pastCurrentDecoder ? "true" : "false");
 
     if (pastCurrentDecoder &&
         !mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
       // Remove data from older decoders than the current one.
       // Don't remove data if it is currently active.
-      MSE_DEBUG("TrackBuffer(%p)::EvictData evicting all before start "
+      MSE_DEBUG("evicting all before start "
                 "bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
-                this, buffered->GetStartTime(), buffered->GetEndTime(),
+                buffered->GetStartTime(), buffered->GetEndTime(),
                 aPlaybackTime, decoders[i]->GetResource()->GetSize());
       toEvict -= decoders[i]->GetResource()->EvictAll();
     } else {
       // To ensure we don't evict data past the current playback position
       // we apply a threshold of a few seconds back and evict data up to
       // that point.
       if (aPlaybackTime > MSE_EVICT_THRESHOLD_TIME) {
         double time = aPlaybackTime - MSE_EVICT_THRESHOLD_TIME;
         int64_t playbackOffset = decoders[i]->ConvertToByteOffset(time);
-        MSE_DEBUG("TrackBuffer(%p)::EvictData evicting some bufferedEnd=%f"
+        MSE_DEBUG("evicting some bufferedEnd=%f"
                   "aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
-                  this, buffered->GetEndTime(), aPlaybackTime, time,
+                  buffered->GetEndTime(), aPlaybackTime, time,
                   playbackOffset, decoders[i]->GetResource()->GetSize());
         if (playbackOffset > 0) {
           toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
                                                            toEvict);
         }
       }
     }
   }
 
   // Remove decoders that have no data in them
   for (i = 0; i < decoders.Length(); ++i) {
     nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
     decoders[i]->GetBuffered(buffered);
-    MSE_DEBUG("TrackBuffer(%p):EvictData maybe remove empty decoders=%d "
+    MSE_DEBUG("maybe remove empty decoders=%d "
               "size=%lld start=%f end=%f",
-              this, i, decoders[i]->GetResource()->GetSize(),
+              i, decoders[i]->GetResource()->GetSize(),
               buffered->GetStartTime(), buffered->GetEndTime());
     if (decoders[i] == mCurrentDecoder
         || mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
       continue;
     }
 
     if (decoders[i]->GetResource()->GetSize() == 0 ||
         buffered->GetStartTime() < 0.0 ||
         buffered->GetEndTime() < 0.0) {
-      MSE_DEBUG("TrackBuffer(%p):EvictData remove empty decoders=%d", this, i);
+      MSE_DEBUG("remove empty decoders=%d", i);
       RemoveDecoder(decoders[i]);
     }
   }
 
   bool evicted = toEvict < (totalSize - aThreshold);
   if (evicted) {
     nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
     mCurrentDecoder->GetBuffered(ranges);
@@ -402,17 +398,18 @@ TrackBuffer::EvictData(double aPlaybackT
 void
 TrackBuffer::EvictBefore(double aTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     int64_t endOffset = mInitializedDecoders[i]->ConvertToByteOffset(aTime);
     if (endOffset > 0) {
-      MSE_DEBUG("TrackBuffer(%p)::EvictBefore decoder=%u offset=%lld", this, i, endOffset);
+      MSE_DEBUG("decoder=%u offset=%lld",
+                i, endOffset);
       mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset);
     }
   }
 }
 
 double
 TrackBuffer::Buffered(dom::TimeRanges* aRanges)
 {
@@ -465,61 +462,59 @@ TrackBuffer::QueueInitializeDecoder(Sour
     return false;
   }
 
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::InitializeDecoder,
                                                       aDecoder);
   if (NS_FAILED(mTaskQueue->Dispatch(task))) {
-    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
+    MSE_DEBUG("failed to enqueue decoder initialization task");
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
   return true;
 }
 
 void
 TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (!mParentDecoder) {
-    MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
-              this);
+    MSE_DEBUG("decoder was shutdown. Aborting initialization.");
     return;
   }
   // ReadMetadata may block the thread waiting on data, so we must be able
   // to leave the monitor while we call it. For the rest of this function
   // we want to hold the monitor though, since we run on a different task queue
   // from the reader and interact heavily with it.
   mParentDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
   if (mCurrentDecoder != aDecoder) {
-    MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
-              this);
+    MSE_DEBUG("append was cancelled. Aborting initialization.");
     // If we reached this point, the SourceBuffer would have disconnected
     // the promise. So no need to reject it.
     return;
   }
 
   // We may be shut down at any time by the reader on another thread. So we need
   // to check for this each time we acquire the monitor. If that happens, we
   // need to abort immediately, because the reader has forgotten about us, and
   // important pieces of our state (like mTaskQueue) have also been torn down.
   if (mShutdown) {
-    MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
+    MSE_DEBUG("was shut down. Aborting initialization.");
     RemoveDecoder(aDecoder);
     return;
   }
 
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   MediaDecoderReader* reader = aDecoder->GetReader();
-  MSE_DEBUG("TrackBuffer(%p): Initializing subdecoder %p reader %p",
-            this, aDecoder, reader);
+  MSE_DEBUG("Initializing subdecoder %p reader %p",
+            aDecoder, reader);
 
   MediaInfo mi;
   nsAutoPtr<MetadataTags> tags; // TODO: Handle metadata.
   nsresult rv;
 
   // HACK WARNING:
   // We only reach this point once we know that we have a complete init segment.
   // We don't want the demuxer to do a blocking read as no more data can be
@@ -539,118 +534,116 @@ TrackBuffer::InitializeDecoder(SourceBuf
     // Adding an empty buffer will reopen the SourceBufferResource
     nsRefPtr<LargeDataBuffer> emptyBuffer = new LargeDataBuffer;
     aDecoder->GetResource()->AppendData(emptyBuffer);
   }
   // HACK END.
 
   reader->SetIdle();
   if (mShutdown) {
-    MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
+    MSE_DEBUG("was shut down while reading metadata. Aborting initialization.");
     return;
   }
 
   if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
     mWaitingDecoders.AppendElement(aDecoder);
     return;
   }
 
   aDecoder->SetTaskQueue(nullptr);
 
   if (NS_FAILED(rv) || (!mi.HasVideo() && !mi.HasAudio())) {
     // XXX: Need to signal error back to owning SourceBuffer.
-    MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
-              this, reader, rv, mi.HasAudio(), mi.HasVideo());
+    MSE_DEBUG("Reader %p failed to initialize rv=%x audio=%d video=%d",
+              reader, rv, mi.HasAudio(), mi.HasVideo());
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 
   if (mi.HasVideo()) {
-    MSE_DEBUG("TrackBuffer(%p): Reader %p video resolution=%dx%d",
-              this, reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
+    MSE_DEBUG("Reader %p video resolution=%dx%d",
+              reader, mi.mVideo.mDisplay.width, mi.mVideo.mDisplay.height);
   }
   if (mi.HasAudio()) {
-    MSE_DEBUG("TrackBuffer(%p): Reader %p audio sampleRate=%d channels=%d",
-              this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
+    MSE_DEBUG("Reader %p audio sampleRate=%d channels=%d",
+              reader, mi.mAudio.mRate, mi.mAudio.mChannels);
   }
 
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::CompleteInitializeDecoder,
                                                       aDecoder);
   if (NS_FAILED(NS_DispatchToMainThread(task))) {
-    MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
+    MSE_DEBUG("Failed to enqueue decoder initialization task");
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 }
 
 void
 TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (!mParentDecoder) {
-    MSE_DEBUG("TrackBuffer(%p) was shutdown. Aborting initialization.",
-              this);
+    MSE_DEBUG("was shutdown. Aborting initialization.");
     return;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   if (mCurrentDecoder != aDecoder) {
-    MSE_DEBUG("TrackBuffer(%p) append was cancelled. Aborting initialization.",
-              this);
+    MSE_DEBUG("append was cancelled. Aborting initialization.");
     // If we reached this point, the SourceBuffer would have disconnected
     // the promise. So no need to reject it.
     return;
   }
 
   if (mShutdown) {
-    MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
+    MSE_DEBUG("was shut down. Aborting initialization.");
     RemoveDecoder(aDecoder);
     return;
   }
 
   if (!RegisterDecoder(aDecoder)) {
-    MSE_DEBUG("TrackBuffer(%p): Reader %p not activated",
-              this, aDecoder->GetReader());
+    MSE_DEBUG("Reader %p not activated",
+              aDecoder->GetReader());
     RemoveDecoder(aDecoder);
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return;
   }
 
   int64_t duration = aDecoder->GetMediaDuration();
   if (!duration) {
     // Treat a duration of 0 as infinity
     duration = -1;
   }
   mParentDecoder->SetInitialDuration(duration);
 
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   mParentDecoder->GetReader()->MaybeNotifyHaveData();
 
-  MSE_DEBUG("TrackBuffer(%p): Reader %p activated",
-            this, aDecoder->GetReader());
+  MSE_DEBUG("Reader %p activated",
+            aDecoder->GetReader());
   mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
 }
 
 bool
 TrackBuffer::ValidateTrackFormats(const MediaInfo& aInfo)
 {
   if (mInfo.HasAudio() != aInfo.HasAudio() ||
       mInfo.HasVideo() != aInfo.HasVideo()) {
-    MSE_DEBUG("TrackBuffer(%p)::ValidateTrackFormats audio/video track mismatch", this);
+    MSE_DEBUG("audio/video track mismatch");
     return false;
   }
 
   // TODO: Support dynamic audio format changes.
   if (mInfo.HasAudio() &&
       (mInfo.mAudio.mRate != aInfo.mAudio.mRate ||
        mInfo.mAudio.mChannels != aInfo.mAudio.mChannels)) {
-    MSE_DEBUG("TrackBuffer(%p)::ValidateTrackFormats audio format mismatch", this);
+    MSE_DEBUG("audio format mismatch");
     return false;
   }
 
   return true;
 }
 
 bool
 TrackBuffer::RegisterDecoder(SourceBufferDecoder* aDecoder)
@@ -658,17 +651,17 @@ TrackBuffer::RegisterDecoder(SourceBuffe
   mParentDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   const MediaInfo& info = aDecoder->GetReader()->GetMediaInfo();
   // Initialize the track info since this is the first decoder.
   if (mInitializedDecoders.IsEmpty()) {
     mInfo = info;
     mParentDecoder->OnTrackBufferConfigured(this, mInfo);
   }
   if (!ValidateTrackFormats(info)) {
-    MSE_DEBUG("TrackBuffer(%p)::RegisterDecoder with mismatched audio/video tracks", this);
+    MSE_DEBUG("mismatched audio/video tracks");
     return false;
   }
   mInitializedDecoders.AppendElement(aDecoder);
   mParentDecoder->NotifyTimeRangesChanged();
   return true;
 }
 
 void
@@ -902,17 +895,17 @@ TrackBuffer::RangeRemoval(int64_t aStart
   int64_t bufferedStart = buffered->GetStartTime() * USECS_PER_S;
 
   if (bufferedStart < 0 || aStart > bufferedEnd || aEnd < bufferedStart) {
     // Nothing to remove.
     return false;
   }
   if (aEnd < bufferedEnd) {
     // TODO. We only handle trimming.
-    NS_WARNING("TrackBuffer::RangeRemoval unsupported arguments. "
+    NS_WARNING("RangeRemoval unsupported arguments. "
                "Can only handle trimming");
     return false;
   }
 
   nsTArray<SourceBufferDecoder*> decoders;
   decoders.AppendElements(mInitializedDecoders);
 
   // Only trimming existing buffers.
@@ -921,25 +914,27 @@ TrackBuffer::RangeRemoval(int64_t aStart
     if (aStart <= buffered->GetStartTime()) {
       // We've completely emptied it, can clear the data.
       int64_t size = decoders[i]->GetResource()->GetSize();
       decoders[i]->GetResource()->EvictData(size, size);
       if (decoders[i] == mCurrentDecoder ||
           mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
         continue;
       }
-      MSE_DEBUG("TrackBuffer(%p):RangeRemoval remove empty decoders=%d", this, i);
+      MSE_DEBUG("remove empty decoders=%d", i);
       RemoveDecoder(decoders[i]);
     }
   }
   return true;
 }
 
 void
 TrackBuffer::AdjustDecodersTimestampOffset(int32_t aOffset)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   for (uint32_t i = 0; i < mDecoders.Length(); i++) {
     mDecoders[i]->SetTimestampOffset(mDecoders[i]->GetTimestampOffset() + aOffset);
   }
 }
 
+#undef MSE_DEBUG
+
 } // namespace mozilla
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -36,10 +36,11 @@ skip-if = (toolkit == 'android' || build
 [test_SeekableBeforeEndOfStreamSplit.html]
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SetModeThrows.html]
 [test_SplitAppendDelay.html]
 [test_SplitAppend.html]
 [test_TimestampOffset_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_TruncatedDuration.html]
 [test_WaitingOnMissingData.html]
  skip-if = android_version == '10' # bug 1115148 - frequent failures on Android 2.3
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_TruncatedDuration.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: truncating the media seeks to end of media and update buffered range</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// This test append data to a mediasource and then seek to half the duration
+// of the video.
+// We then shorten the video to 1/3rd of its original size by modifying the
+// mediasource.duration attribute.
+// We ensure that the buffered range immediately reflect the truncation
+// and that we've seeked to the new end of the media as per W3C spec and
+// video.currentTime got updated.
+
+SimpleTest.waitForExplicitFinish();
+
+function round(n) {
+  return Math.round(n * 1000) / 1000;
+}
+
+function do_seeking(e) {
+  var v = e.target;
+  v.removeEventListener("seeking", do_seeking, false);
+  SimpleTest.finish();
+}
+
+function do_seeked(e) {
+  var v = e.target;
+  v.removeEventListener("seeked", do_seeked, false);
+  var duration = round(v.duration / 3);
+  v._ms.duration = duration
+  v._sb.abort(); // this shouldn't abort updating the duration (bug 1130826).
+  ok(v.seeking, "seeking is true");
+  // test playback position was updated (bug 1130839).
+  is(v.currentTime, duration, "current time was updated");
+  is(v.duration, duration, "element duration was updated");
+  is(v._sb.buffered.length, 1, "One buffered range");
+  is(v._sb.buffered.end(0), duration, "sourcebuffer truncated");
+  // Truncated mediasource duration will cause the video element to seek.
+  v.addEventListener("seeking", do_seeking, false);
+}
+
+function do_loaded(e) {
+  var v = e.target;
+  v.removeEventListener("loadeddata", do_loaded, false);
+  v.currentTime = v.duration / 2;
+  is(v.currentTime, v.duration / 2, "current time was updated");
+  ok(v.seeking, "seeking is true");
+  v.addEventListener("seeked", do_seeked, false);
+}
+
+runWithMSE(function (ms, v) {
+  ms.addEventListener("sourceopen", function () {
+    var sb = ms.addSourceBuffer("video/webm");
+    v._sb = sb;
+    v._ms = ms;
+
+    fetchWithXHR("seek.webm", function (arrayBuffer) {
+      sb.appendBuffer(new Uint8Array(arrayBuffer));
+      v.addEventListener("loadeddata", do_loaded, false);
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/network/NetworkStatsDB.jsm
+++ b/dom/network/NetworkStatsDB.jsm
@@ -542,26 +542,48 @@ NetworkStatsDB.prototype = {
       let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let request = aStore.openCursor(range, 'prev');
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
         if (!cursor) {
           // Empty, so save first element.
 
+          if (!isAccumulative) {
+            this._saveStats(aTxn, aStore, stats);
+            return;
+          }
+
           // There could be a time delay between the point when the network
           // interface comes up and the point when the database is initialized.
           // In this short interval some traffic data are generated but are not
           // registered by the first sample.
-          if (isAccumulative) {
-            stats.rxBytes = stats.rxTotalBytes;
-            stats.txBytes = stats.txTotalBytes;
-          }
+          stats.rxBytes = stats.rxTotalBytes;
+          stats.txBytes = stats.txTotalBytes;
 
-          this._saveStats(aTxn, aStore, stats);
+          // However, if the interface is not switched on after the database is
+          // initialized (dual sim use case) stats should be set to 0.
+          let req = aStore.index("network").openKeyCursor(null, "nextunique");
+          req.onsuccess = function onsuccess(event) {
+            let cursor = event.target.result;
+            if (cursor) {
+              if (cursor.key[1] == stats.network[1]) {
+                stats.rxBytes = 0;
+                stats.txBytes = 0;
+                this._saveStats(aTxn, aStore, stats);
+                return;
+              }
+
+              cursor.continue();
+              return;
+            }
+
+            this._saveStats(aTxn, aStore, stats);
+          }.bind(this);
+
           return;
         }
 
         // There are old samples
         if (DEBUG) {
           debug("Last value " + JSON.stringify(cursor.value));
         }
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -222,17 +222,16 @@ const static js::Class sNPObjectJSWrappe
       NPObjWrapper_ObjectMoved
     },
     {
         nullptr, // lookupProperty
         nullptr, // defineProperty
         nullptr, // getProperty
         nullptr, // setProperty
         nullptr, // getOwnPropertyDescriptor
-        nullptr, // setPropertyAttributes
         nullptr, // deleteProperty
         nullptr, nullptr, // watch/unwatch
         nullptr, // getElements
         NPObjWrapper_Enumerate,
         nullptr,
     }
   };
 
@@ -1867,17 +1866,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!sNPObjWrappers.IsInitialized()) {
     // No hash yet (or any more), initialize it.
     if (!CreateNPObjWrapperTable()) {
       return nullptr;
     }
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
 
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
@@ -2030,17 +2029,17 @@ static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
     return o->mNpp;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
 
   if (!entry) {
     return nullptr;
   }
 
   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
 
   return entry->mNpp;
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -9,16 +9,20 @@
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 #include "mozilla/Telemetry.h"
 #include "nsThreadUtils.h"
 
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#include "nsDirectoryServiceDefs.h"
+#endif
+
 using std::vector;
 using std::string;
 
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::plugins::LaunchCompleteTask;
 using mozilla::plugins::PluginProcessParent;
 using base::ProcessArchitecture;
@@ -37,22 +41,77 @@ PluginProcessParent::PluginProcessParent
     mRunCompleteTaskImmediately(false)
 {
 }
 
 PluginProcessParent::~PluginProcessParent()
 {
 }
 
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void
+AddSandboxAllowedFile(vector<std::wstring>& aAllowedFiles, nsIProperties* aDirSvc,
+                      const char* aDir, const nsAString& aSuffix = EmptyString())
+{
+    nsCOMPtr<nsIFile> userDir;
+    nsresult rv = aDirSvc->Get(aDir, NS_GET_IID(nsIFile), getter_AddRefs(userDir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+    }
+
+    nsAutoString userDirPath;
+    rv = userDir->GetPath(userDirPath);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+    }
+
+    if (!aSuffix.IsEmpty()) {
+        userDirPath.Append(aSuffix);
+    }
+    aAllowedFiles.push_back(userDirPath.get());
+    return;
+}
+
+static void
+AddSandboxAllowedFiles(int32_t aSandboxLevel,
+                       vector<std::wstring>& aAllowedFilesRead,
+                       vector<std::wstring>& aAllowedFilesReadWrite)
+{
+    if (aSandboxLevel < 3) {
+        return;
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIProperties> dirSvc =
+        do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+    }
+
+    AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
+    AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
+                          NS_LITERAL_STRING("\\*"));
+
+    AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
+    AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
+    AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR,
+                          NS_LITERAL_STRING("\\*"));
+}
+#endif
+
 bool
 PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
                             int32_t aSandboxLevel)
 {
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     mSandboxLevel = aSandboxLevel;
+    AddSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
+                           mAllowedFilesReadWrite);
 #else
     if (aSandboxLevel != 0) {
         MOZ_ASSERT(false,
                    "Can't enable an NPAPI process sandbox for platform/build.");
     }
 #endif
 
     ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -647,29 +647,29 @@ var interfaceNamesInGlobalScope =
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaDevices",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaElementAudioSourceNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaError",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeyError", pref: "media.eme.enabled"},
+    {name: "MediaKeyError", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaEncryptedEvent", pref: "media.eme.enabled"},
+    {name: "MediaEncryptedEvent", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeys", pref: "media.eme.enabled"},
+    {name: "MediaKeys", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeySession", pref: "media.eme.enabled"},
+    {name: "MediaKeySession", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeySystemAccess", pref: "media.eme.enabled"},
+    {name: "MediaKeySystemAccess", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeyMessageEvent", pref: "media.eme.enabled"},
+    {name: "MediaKeyMessageEvent", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaKeyStatusMap", pref: "media.eme.enabled"},
+    {name: "MediaKeyStatusMap", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaRecorder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaSource", pref: "media.mediasource.enabled"},
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -140,19 +140,19 @@ partial interface HTMLMediaElement {
   // * onmozinterruptbegin - called when the media element is interrupted
   //   because of the audiochannel manager.
   // * onmozinterruptend - called when the interruption is concluded
 };
 
 #ifdef MOZ_EME
 // Encrypted Media Extensions
 partial interface HTMLMediaElement {
-  [Pref="media.eme.enabled"]
+  [Pref="media.eme.apiVisible"]
   readonly attribute MediaKeys? mediaKeys;
 
   // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
-  [Pref="media.eme.enabled", NewObject]
+  [Pref="media.eme.apiVisible", NewObject]
   Promise<void> setMediaKeys(MediaKeys? mediaKeys);
 
-  [Pref="media.eme.enabled"]
+  [Pref="media.eme.apiVisible"]
   attribute EventHandler onencrypted;
 };
 #endif
--- a/dom/webidl/MediaEncryptedEvent.webidl
+++ b/dom/webidl/MediaEncryptedEvent.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
-[Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyNeededEventInit eventInitDict)]
+[Pref="media.eme.apiVisible", Constructor(DOMString type, optional MediaKeyNeededEventInit eventInitDict)]
 interface MediaEncryptedEvent : Event {
   readonly attribute DOMString initDataType;
   [Throws]
   readonly attribute ArrayBuffer? initData;
 };
 
 dictionary MediaKeyNeededEventInit : EventInit {
   DOMString initDataType = "";
--- a/dom/webidl/MediaKeyError.webidl
+++ b/dom/webidl/MediaKeyError.webidl
@@ -8,12 +8,12 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
 // According to the spec, "The future of error events and MediaKeyError
 // is uncertain."
 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21798
-[Pref="media.eme.enabled"]
+[Pref="media.eme.apiVisible"]
 interface MediaKeyError : Event {
   readonly attribute unsigned long systemCode;
 };
--- a/dom/webidl/MediaKeyMessageEvent.webidl
+++ b/dom/webidl/MediaKeyMessageEvent.webidl
@@ -12,17 +12,17 @@
 
 enum MediaKeyMessageType {
   "license-request",
   "license-renewal",
   "license-release",
   "individualization-request"
 };
 
-[Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
+[Pref="media.eme.apiVisible", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
 interface MediaKeyMessageEvent : Event {
   readonly attribute MediaKeyMessageType messageType;
   [Throws]
   readonly attribute ArrayBuffer message;
 };
 
 dictionary MediaKeyMessageEventInit : EventInit {
   MediaKeyMessageType messageType = "license-request";
--- a/dom/webidl/MediaKeySession.webidl
+++ b/dom/webidl/MediaKeySession.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
-[Pref="media.eme.enabled"]
+[Pref="media.eme.apiVisible"]
 interface MediaKeySession : EventTarget {
   // error state
   readonly attribute MediaKeyError? error;
 
   // session properties
   readonly attribute DOMString keySystem;
   readonly attribute DOMString sessionId;
 
--- a/dom/webidl/MediaKeyStatusMap.webidl
+++ b/dom/webidl/MediaKeyStatusMap.webidl
@@ -13,17 +13,17 @@
 enum MediaKeyStatus {
   "usable",
   "expired",
   "output-downscaled",
   "output-not-allowed",
   "internal-error"
 };
 
-[Pref="media.eme.enabled"]
+[Pref="media.eme.apiVisible"]
 interface MediaKeyStatusMap {
   [Throws]
   readonly attribute unsigned long size;
 
   [Throws]
   object keys();
 
   [Throws]
--- a/dom/webidl/MediaKeySystemAccess.webidl
+++ b/dom/webidl/MediaKeySystemAccess.webidl
@@ -21,14 +21,14 @@ dictionary MediaKeySystemOptions {
   DOMString            audioType = "";
   DOMString            audioCapability = "";
   DOMString            videoType = "";
   DOMString            videoCapability = "";
   MediaKeysRequirement uniqueidentifier = "optional";
   MediaKeysRequirement stateful = "optional";
 };
 
-[Pref="media.eme.enabled"]
+[Pref="media.eme.apiVisible"]
 interface MediaKeySystemAccess {
   readonly    attribute DOMString keySystem;
   [NewObject]
   Promise<MediaKeys> createMediaKeys();
 };
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -8,17 +8,17 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
 enum IsTypeSupportedResult { "" /* empty string */, "maybe", "probably" };
 enum SessionType { "temporary", "persistent" };
 
-[Pref="media.eme.enabled"]
+[Pref="media.eme.apiVisible"]
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional SessionType sessionType = "temporary");
 
   [NewObject]
   Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -409,14 +409,14 @@ partial interface Navigator {
 
 partial interface Navigator {
   [Pref="dom.tv.enabled", CheckPermissions="tv", Func="Navigator::HasTVSupport"]
   readonly attribute TVManager? tv;
 };
 
 #ifdef MOZ_EME
 partial interface Navigator {
-  [Pref="media.eme.enabled", NewObject]
+  [Pref="media.eme.apiVisible", NewObject]
   Promise<MediaKeySystemAccess>
   requestMediaKeySystemAccess(DOMString keySystem,
                               optional sequence<MediaKeySystemOptions> supportedConfigurations);
 };
 #endif
--- a/dom/webidl/SelectionStateChangedEvent.webidl
+++ b/dom/webidl/SelectionStateChangedEvent.webidl
@@ -8,17 +8,18 @@ enum SelectionState {
   "drag",
   "mousedown",
   "mouseup",
   "keypress",
   "selectall",
   "collapsetostart",
   "collapsetoend",
   "blur",
-  "updateposition"
+  "updateposition",
+  "taponcaret"
 };
 
 dictionary SelectionStateChangedEventInit : EventInit {
   boolean visible = true;
   DOMString selectedText = "";
   DOMRectReadOnly? boundingClientRect = null;
   sequence<SelectionState> states = [];
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1,14 +1,15 @@
 /* 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 "ServiceWorkerManager.h"
 
+#include "nsIAppsService.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsPIDOMWindow.h"
 
@@ -16,16 +17,19 @@
 
 #include "mozilla/LoadContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/InstallEventBinding.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
@@ -40,19 +44,68 @@
 #include "WorkerScope.h"
 
 #ifdef PostMessage
 #undef PostMessage
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 
 BEGIN_WORKERS_NAMESPACE
 
+struct ServiceWorkerManager::PendingOperation
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+
+  ServiceWorkerJobQueue* mQueue;
+  nsRefPtr<ServiceWorkerJob> mJob;
+
+  ServiceWorkerRegistrationData mRegistration;
+};
+
+namespace {
+
+nsresult
+PopulateRegistrationData(nsIPrincipal* aPrincipal,
+                         const ServiceWorkerRegistrationInfo* aRegistration,
+                         ServiceWorkerRegistrationData& aData)
+{
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aRegistration);
+
+  bool isNullPrincipal = true;
+  nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // No null principals.
+  if (NS_WARN_IF(isNullPrincipal)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aData.scope() = aRegistration->mScope;
+  aData.scriptSpec() = aRegistration->mScriptSpec;
+
+  if (aRegistration->mActiveWorker) {
+    aData.currentWorkerURL() = aRegistration->mActiveWorker->ScriptSpec();
+  }
+
+  return NS_OK;
+}
+
+} // Anonymous namespace
+
 NS_IMPL_ISUPPORTS0(ServiceWorkerJob)
 NS_IMPL_ISUPPORTS0(ServiceWorkerRegistrationInfo)
 
 void
 ServiceWorkerJob::Done(nsresult aStatus)
 {
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
     // Windows builds complain if the return value of NS_WARN_IF isn't used.
@@ -88,20 +141,22 @@ ServiceWorkerRegistrationInfo::Clear()
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
   swm->InvalidateServiceWorkerRegistrationWorker(this,
                                                  WhichServiceWorker::INSTALLING_WORKER |
                                                  WhichServiceWorker::WAITING_WORKER |
                                                  WhichServiceWorker::ACTIVE_WORKER);
 }
 
-ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope)
-  : mControlledDocumentsCounter(0),
-    mScope(aScope),
-    mPendingUninstall(false)
+ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
+                                                             nsIPrincipal* aPrincipal)
+  : mControlledDocumentsCounter(0)
+  , mScope(aScope)
+  , mPrincipal(aPrincipal)
+  , mPendingUninstall(false)
 { }
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
 }
@@ -110,24 +165,37 @@ ServiceWorkerRegistrationInfo::~ServiceW
 // ServiceWorkerManager //
 //////////////////////////
 
 NS_IMPL_ADDREF(ServiceWorkerManager)
 NS_IMPL_RELEASE(ServiceWorkerManager)
 
 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
   NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
   if (aIID.Equals(NS_GET_IID(ServiceWorkerManager)))
     foundInterface = static_cast<nsIServiceWorkerManager*>(this);
   else
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
+  : mActor(nullptr)
 {
+  // Register this component to PBackground.
+  MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
+
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(swr);
+
+    nsTArray<ServiceWorkerRegistrationData> data;
+    swr->GetRegistrations(data);
+    LoadRegistrations(data);
+  }
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mServiceWorkerRegistrationInfos.Clear();
 }
 
@@ -375,16 +443,17 @@ class ServiceWorkerRegisterJob MOZ_FINAL
                                            public nsIStreamLoaderObserver
 {
   friend class ContinueInstallTask;
 
   nsCString mScope;
   nsCString mScriptSpec;
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
 
   ~ServiceWorkerRegisterJob()
   { }
 
   enum
   {
     REGISTER_JOB = 0,
     UPDATE_JOB = 1,
@@ -392,55 +461,67 @@ class ServiceWorkerRegisterJob MOZ_FINAL
 
 public:
   NS_DECL_ISUPPORTS
 
   // [[Register]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            const nsCString& aScope,
                            const nsCString& aScriptSpec,
-                           ServiceWorkerUpdateFinishCallback* aCallback)
+                           ServiceWorkerUpdateFinishCallback* aCallback,
+                           nsIPrincipal* aPrincipal)
     : ServiceWorkerJob(aQueue)
     , mScope(aScope)
     , mScriptSpec(aScriptSpec)
     , mCallback(aCallback)
+    , mPrincipal(aPrincipal)
     , mJobType(REGISTER_JOB)
   { }
 
   // [[Update]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            ServiceWorkerRegistrationInfo* aRegistration,
                            ServiceWorkerUpdateFinishCallback* aCallback)
     : ServiceWorkerJob(aQueue)
     , mRegistration(aRegistration)
     , mCallback(aCallback)
     , mJobType(UPDATE_JOB)
   { }
 
   void
   Start() MOZ_OVERRIDE
   {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm->HasBackgroundActor()) {
+      nsCOMPtr<nsIRunnable> runnable =
+        NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::Start);
+      swm->AppendPendingOperation(runnable);
+      return;
+    }
+
     if (mJobType == REGISTER_JOB) {
-      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
       mRegistration = swm->GetRegistration(mScope);
 
       if (mRegistration) {
         nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
         if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
             mScriptSpec.Equals(mRegistration->mScriptSpec)) {
           mRegistration->mPendingUninstall = false;
           Succeed();
           Done(NS_OK);
           return;
         }
       } else {
-        mRegistration = swm->CreateNewRegistration(mScope);
+        mRegistration = swm->CreateNewRegistration(mScope, mPrincipal);
       }
 
       mRegistration->mScriptSpec = mScriptSpec;
+      swm->StoreRegistration(mPrincipal, mRegistration);
     } else {
       MOZ_ASSERT(mJobType == UPDATE_JOB);
     }
 
     Update();
   }
 
   NS_IMETHOD
@@ -861,23 +942,46 @@ ServiceWorkerManager::Register(nsIDOMWin
 
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(cleanedScope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerResolveWindowPromiseOnUpdateCallback> cb =
     new ServiceWorkerResolveWindowPromiseOnUpdateCallback(window, promise);
 
   nsRefPtr<ServiceWorkerRegisterJob> job =
-    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb);
+    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal);
   queue->Append(job);
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
+void
+ServiceWorkerManager::AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
+                                             ServiceWorkerJob* aJob)
+{
+  MOZ_ASSERT(!mActor);
+  MOZ_ASSERT(aQueue);
+  MOZ_ASSERT(aJob);
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mQueue = aQueue;
+  opt->mJob = aJob;
+}
+
+void
+ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
+{
+  MOZ_ASSERT(!mActor);
+  MOZ_ASSERT(aRunnable);
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mRunnable = aRunnable;
+}
+
 /*
  * Used to handle ExtendableEvent::waitUntil() and proceed with
  * installation/activation.
  */
 class LifecycleEventPromiseHandler MOZ_FINAL : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mActivateImmediately;
@@ -1022,16 +1126,17 @@ ServiceWorkerRegistrationInfo::Activate(
     exitingWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   mActiveWorker = activatingWorker.forget();
   mWaitingWorker = nullptr;
   mActiveWorker->UpdateState(ServiceWorkerState::Activating);
 
   swm->CheckPendingReadyPromises();
+  swm->StoreRegistration(mPrincipal, this);
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
     NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, &ServiceWorkerManager::FireControllerChange, this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
   // XXXnsm I have my doubts about this. Leaving the main thread means that
   // subsequent calls to Activate() not from a Register() call, i.e. due to all
@@ -1377,27 +1482,30 @@ ServiceWorkerManager::CheckReadyPromise(
   return false;
 }
 
 class ServiceWorkerUnregisterJob MOZ_FINAL : public ServiceWorkerJob
 {
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   const nsCString mScope;
   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
+  PrincipalInfo mPrincipalInfo;
 
   ~ServiceWorkerUnregisterJob()
   { }
 
 public:
   ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
                              const nsACString& aScope,
-                             nsIServiceWorkerUnregisterCallback* aCallback)
+                             nsIServiceWorkerUnregisterCallback* aCallback,
+                             PrincipalInfo& aPrincipalInfo)
     : ServiceWorkerJob(aQueue)
     , mScope(aScope)
     , mCallback(aCallback)
+    , mPrincipalInfo(aPrincipalInfo)
   {
     AssertIsOnMainThread();
   }
 
   void
   Start() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
@@ -1440,51 +1548,68 @@ private:
         return NS_OK;
       }
 
       // "Invoke [[Clear Registration]]..."
       registration->Clear();
       swm->RemoveRegistration(registration);
     }
 
+    MOZ_ASSERT(swm->mActor);
+    swm->mActor->SendUnregisterServiceWorker(mPrincipalInfo,
+                                             NS_ConvertUTF8toUTF16(mScope));
     return NS_OK;
   }
 
   // The unregister job is done irrespective of success or failure of any sort.
   void
   UnregisterAndDone()
   {
     Done(Unregister());
   }
 };
 
 NS_IMETHODIMP
-ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
+ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
+                                 nsIServiceWorkerUnregisterCallback* aCallback,
                                  const nsAString& aScope)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
 // This is not accessible by content, and callers should always ensure scope is
 // a correct URI, so this is wrapped in DEBUG
 #ifdef DEBUG
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 #endif
 
   NS_ConvertUTF16toUTF8 scope(aScope);
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scope);
   MOZ_ASSERT(queue);
 
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                    &principalInfo)))) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
   nsRefPtr<ServiceWorkerUnregisterJob> job =
-    new ServiceWorkerUnregisterJob(queue, scope, aCallback);
-  queue->Append(job);
+    new ServiceWorkerUnregisterJob(queue, scope, aCallback, principalInfo);
+
+  if (mActor) {
+    queue->Append(job);
+    return NS_OK;
+  }
+
+  AppendPendingOperation(queue, job);
   return NS_OK;
 }
 
 /* static */
 already_AddRefed<ServiceWorkerManager>
 ServiceWorkerManager::GetInstance()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
@@ -1608,16 +1733,95 @@ ServiceWorkerManager::CreateServiceWorke
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
+void
+ServiceWorkerManager::LoadRegistrations(
+                  const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
+{
+  AssertIsOnMainThread();
+
+  for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
+    nsCOMPtr<nsIPrincipal> principal =
+      PrincipalInfoToPrincipal(aRegistrations[i].principal());
+    if (!principal) {
+      continue;
+    }
+
+    ServiceWorkerRegistrationInfo* registration =
+      CreateNewRegistration(aRegistrations[i].scope(), principal);
+
+    registration->mScriptSpec = aRegistrations[i].scriptSpec();
+
+    registration->mActiveWorker =
+      new ServiceWorkerInfo(registration, aRegistrations[i].currentWorkerURL());
+  }
+}
+
+void
+ServiceWorkerManager::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+ServiceWorkerManager::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!mActor);
+  mActor = aActor;
+
+  // Flush the pending requests.
+  for (uint32_t i = 0, len = mPendingOperations.Length(); i < len; ++i) {
+    MOZ_ASSERT(mPendingOperations[i].mRunnable ||
+               (mPendingOperations[i].mJob && mPendingOperations[i].mQueue));
+
+    if (mPendingOperations[i].mRunnable) {
+      nsresult rv = NS_DispatchToCurrentThread(mPendingOperations[i].mRunnable);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch a runnable.");
+        return;
+      }
+    } else {
+      mPendingOperations[i].mQueue->Append(mPendingOperations[i].mJob);
+    }
+  }
+
+  mPendingOperations.Clear();
+}
+
+void
+ServiceWorkerManager::StoreRegistration(
+                                   nsIPrincipal* aPrincipal,
+                                   ServiceWorkerRegistrationInfo* aRegistration)
+{
+  MOZ_ASSERT(mActor);
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aRegistration);
+
+  ServiceWorkerRegistrationData data;
+  nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  PrincipalInfo principalInfo;
+  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
+                                                    &principalInfo)))) {
+    return;
+  }
+
+  mActor->SendRegisterServiceWorker(data);
+}
+
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<nsIDocument> document = aWindow->GetExtantDoc();
   return GetServiceWorkerRegistrationInfo(document);
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
@@ -2161,25 +2365,26 @@ ServiceWorkerManager::GetServicedClients
 
 void
 ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
 {
   mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument, aRegistration);
 }
 
 ServiceWorkerRegistrationInfo*
-ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope)
+ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope,
+                                            nsIPrincipal* aPrincipal)
 {
 #ifdef DEBUG
   AssertIsOnMainThread();
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 #endif
-  ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope);
+  ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope, aPrincipal);
   // From now on ownership of registration is with
   // mServiceWorkerRegistrationInfos.
   mServiceWorkerRegistrationInfos.Put(aScope, registration);
   AddScope(mOrderedScopes, aScope);
   return registration;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -3,34 +3,44 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_serviceworkermanager_h
 #define mozilla_dom_workers_serviceworkermanager_h
 
 #include "nsIServiceWorkerManager.h"
 #include "nsCOMPtr.h"
 
+#include "ipc/IPCMessageUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
 #include "mozilla/dom/ServiceWorkerCommon.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
 
 class nsIScriptError;
 
 namespace mozilla {
+
+namespace ipc {
+class BackgroundChild;
+}
+
 namespace dom {
 
 class ServiceWorkerRegistration;
 
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerInfo;
@@ -126,27 +136,30 @@ class ServiceWorkerRegistrationInfo MOZ_
 public:
   NS_DECL_ISUPPORTS
 
   nsCString mScope;
   // The scriptURL for the registration. This may be completely different from
   // the URLs of the following three workers.
   nsCString mScriptSpec;
 
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
   nsRefPtr<ServiceWorkerInfo> mActiveWorker;
   nsRefPtr<ServiceWorkerInfo> mWaitingWorker;
   nsRefPtr<ServiceWorkerInfo> mInstallingWorker;
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
   bool mWaitingToActivate;
 
-  explicit ServiceWorkerRegistrationInfo(const nsACString& aScope);
+  explicit ServiceWorkerRegistrationInfo(const nsACString& aScope,
+                                         nsIPrincipal* aPrincipal);
 
   already_AddRefed<ServiceWorkerInfo>
   Newest()
   {
     nsRefPtr<ServiceWorkerInfo> newest;
     if (mInstallingWorker) {
       newest = mInstallingWorker;
     } else if (mWaitingWorker) {
@@ -213,16 +226,22 @@ public:
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
 
   const nsCString&
   ScriptSpec() const
   {
     return mScriptSpec;
   }
 
+  void SetScriptSpec(const nsCString& aSpec)
+  {
+    MOZ_ASSERT(!aSpec.IsEmpty());
+    mScriptSpec = aSpec;
+  }
+
   explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
                              const nsACString& aScriptSpec)
     : mRegistration(aReg)
     , mScriptSpec(aScriptSpec)
     , mState(ServiceWorkerState::EndGuard_)
   {
     MOZ_ASSERT(mRegistration);
   }
@@ -261,38 +280,38 @@ public:
   { 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 }     \
 }
 
 /*
  * The ServiceWorkerManager is a per-process global that deals with the
  * installation, querying and event dispatch of ServiceWorkers for all the
  * origins in the process.
  */
-class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
+class ServiceWorkerManager MOZ_FINAL
+  : public nsIServiceWorkerManager
+  , public nsIIPCBackgroundChildCreateCallback
 {
   friend class ActivationRunnable;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerRegisterJob;
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class QueueFireUpdateFoundRunnable;
   friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
   {
     AssertIsOnMainThread();
-    if (!Preferences::GetBool("dom.serviceWorkers.enabled")) {
-      return nullptr;
-    }
 
     ServiceWorkerManager* res = new ServiceWorkerManager;
     NS_ADDREF(res);
     return res;
   }
 
   // Ordered list of scopes for glob matching.
   // Each entry is an absolute URL representing the scope.
@@ -320,32 +339,35 @@ public:
   GetRegistration(const nsCString& aScope) const
   {
     nsRefPtr<ServiceWorkerRegistrationInfo> reg;
     mServiceWorkerRegistrationInfos.Get(aScope, getter_AddRefs(reg));
     return reg.forget();
   }
 
   ServiceWorkerRegistrationInfo*
-  CreateNewRegistration(const nsCString& aScope);
+  CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal);
 
   void
   RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
   {
     MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
     ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
     mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
   }
 
   ServiceWorkerJobQueue*
   GetOrCreateJobQueue(const nsCString& aScope)
   {
     return mJobQueues.LookupOrAdd(aScope);
   }
 
+  void StoreRegistration(nsIPrincipal* aPrincipal,
+                         ServiceWorkerRegistrationInfo* aRegistration);
+
   void
   FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
 
   // Returns true if the error was handled, false if normal worker error
   // handling should continue.
   bool
   HandleError(JSContext* aCx,
               const nsCString& aScope,
@@ -359,16 +381,19 @@ public:
 
   void
   GetServicedClients(const nsCString& aScope,
                      nsTArray<uint64_t>* aControlledDocuments);
 
   static already_AddRefed<ServiceWorkerManager>
   GetInstance();
 
+ void LoadRegistrations(
+                 const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
+
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
@@ -445,22 +470,36 @@ private:
     PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
       : mURI(aURI), mPromise(aPromise)
     { }
 
     nsCOMPtr<nsIURI> mURI;
     nsRefPtr<Promise> mPromise;
   };
 
+  void AppendPendingOperation(nsIRunnable* aRunnable);
+  void AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
+                              ServiceWorkerJob* aJob);
+
+  bool HasBackgroundActor() const
+  {
+    return !!mActor;
+  }
+
   static PLDHashOperator
   CheckPendingReadyPromisesEnumerator(nsISupports* aSupports,
                                       nsAutoPtr<PendingReadyPromise>& aData,
                                       void* aUnused);
 
   nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
+
+  mozilla::ipc::PBackgroundChild* mActor;
+
+  struct PendingOperation;
+  nsTArray<PendingOperation> mPendingOperations;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
                               NS_SERVICEWORKERMANAGER_IMPL_IID);
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -0,0 +1,671 @@
+/* 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 "ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+
+#include "nsIEventTarget.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsIObserverService.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAutoPtr.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
+
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
+                  nsIObserver)
+
+void
+ServiceWorkerRegistrar::Initialize()
+{
+  MOZ_ASSERT(!gServiceWorkerRegistrar);
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return;
+  }
+
+  gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
+  ClearOnShutdown(&gServiceWorkerRegistrar);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
+                                              "profile-after-change", false);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
+                          false);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+}
+
+/* static */ already_AddRefed<ServiceWorkerRegistrar>
+ServiceWorkerRegistrar::Get()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  MOZ_ASSERT(gServiceWorkerRegistrar);
+  nsRefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
+  return service.forget();
+}
+
+ServiceWorkerRegistrar::ServiceWorkerRegistrar()
+  : mMonitor("ServiceWorkerRegistrar.mMonitor")
+  , mDataLoaded(false)
+  , mShuttingDown(false)
+  , mShutdownCompleteFlag(nullptr)
+  , mRunnableCounter(0)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
+{
+  MOZ_ASSERT(!mRunnableCounter);
+}
+
+void
+ServiceWorkerRegistrar::GetRegistrations(
+                               nsTArray<ServiceWorkerRegistrationData>& aValues)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aValues.IsEmpty());
+
+  // If we don't have the profile directory, profile is not started yet (and
+  // probably we are in a utest).
+  if (!mProfileDir) {
+    return;
+  }
+
+  // We care just about the first execution because this can be blocked by
+  // loading data from disk.
+  static bool firstTime = true;
+  TimeStamp startTime;
+
+  if (firstTime) {
+    startTime = TimeStamp::NowLoRes();
+  }
+
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    // Waiting for data loaded.
+    mMonitor.AssertCurrentThreadOwns();
+    while (!mDataLoaded) {
+      mMonitor.Wait();
+    }
+
+    aValues.AppendElements(mData);
+  }
+
+  if (firstTime) {
+    firstTime = false;
+    Telemetry::AccumulateTimeDelta(
+      Telemetry::SERVICE_WORKER_REGISTRATION_LOADING,
+      startTime);
+  }
+}
+
+void
+ServiceWorkerRegistrar::RegisterServiceWorker(
+                                     const ServiceWorkerRegistrationData& aData)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mShuttingDown) {
+    NS_WARNING("Failed to register a serviceWorker during shutting down.");
+    return;
+  }
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    MOZ_ASSERT(mDataLoaded);
+
+    bool found = false;
+    for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
+      if (mData[i].scope() == aData.scope()) {
+        mData[i] = aData;
+        found = true;
+        break;
+      }
+    }
+
+    if (!found) {
+      mData.AppendElement(aData);
+    }
+  }
+
+  ScheduleSaveData();
+}
+
+void
+ServiceWorkerRegistrar::UnregisterServiceWorker(const nsACString& aScope)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mShuttingDown) {
+    NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
+    return;
+  }
+
+  bool deleted = false;
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    MOZ_ASSERT(mDataLoaded);
+
+    for (uint32_t i = 0; i < mData.Length(); ++i) {
+      if (mData[i].scope() == aScope) {
+        mData.RemoveElementAt(i);
+        deleted = true;
+        break;
+      }
+    }
+  }
+
+  if (deleted) {
+    ScheduleSaveData();
+  }
+}
+
+void
+ServiceWorkerRegistrar::LoadData()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mDataLoaded);
+
+  nsresult rv = ReadData();
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    DeleteData();
+    // Also if the reading failed we have to notify what is waiting for data.
+  }
+
+  MonitorAutoLock lock(mMonitor);
+  MOZ_ASSERT(!mDataLoaded);
+  mDataLoaded = true;
+  mMonitor.Notify();
+}
+
+nsresult
+ServiceWorkerRegistrar::ReadData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool exists;
+  rv = file->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
+  MOZ_ASSERT(lineInputStream);
+
+  nsAutoCString line;
+  bool hasMoreLines;
+  rv = lineInputStream->ReadLine(line, &hasMoreLines);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // The file is corrupted ?
+  // FIXME: in case we implement a version 2, we should inform the user using
+  // the console about this issue.
+  if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  while (hasMoreLines) {
+    ServiceWorkerRegistrationData* entry = mData.AppendElement();
+
+#define GET_LINE(x)                                   \
+    rv = lineInputStream->ReadLine(x, &hasMoreLines); \
+    if (NS_WARN_IF(NS_FAILED(rv))) {                  \
+      return rv;                                      \
+    }                                                 \
+    if (NS_WARN_IF(!hasMoreLines)) {                  \
+      return NS_ERROR_FAILURE;                        \
+    }
+
+    GET_LINE(line);
+
+    if (line.EqualsLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL)) {
+      entry->principal() = mozilla::ipc::SystemPrincipalInfo();
+    } else {
+      if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      GET_LINE(line);
+
+      uint32_t appId = line.ToInteger(&rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      GET_LINE(line);
+
+      if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
+          !line.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      bool isInBrowserElement = line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
+
+      GET_LINE(line);
+      entry->principal() =
+        mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement,
+                                           line);
+    }
+
+    GET_LINE(entry->scope());
+    GET_LINE(entry->scriptSpec());
+    GET_LINE(entry->currentWorkerURL());
+
+#undef GET_LINE
+
+    rv = lineInputStream->ReadLine(line, &hasMoreLines);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+ServiceWorkerRegistrar::DeleteData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  {
+    MonitorAutoLock lock(mMonitor);
+    mData.Clear();
+  }
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = file->Remove(false);
+  if (rv == NS_ERROR_FILE_NOT_FOUND) {
+    return;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+class ServiceWorkerRegistrarSaveDataRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  ServiceWorkerRegistrarSaveDataRunnable()
+    : mThread(do_GetCurrentThread())
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    nsRefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->SaveData();
+
+    nsRefPtr<nsRunnable> runnable =
+      NS_NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved);
+    nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIThread> mThread;
+};
+
+void
+ServiceWorkerRegistrar::ScheduleSaveData()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShuttingDown);
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target, "Must have stream transport service");
+
+  nsRefPtr<nsRunnable> runnable =
+    new ServiceWorkerRegistrarSaveDataRunnable();
+  nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  ++mRunnableCounter;
+}
+
+void
+ServiceWorkerRegistrar::ShutdownCompleted()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
+  *mShutdownCompleteFlag = true;
+}
+
+void
+ServiceWorkerRegistrar::SaveData()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  nsresult rv = WriteData();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
+    DeleteData();
+  }
+}
+
+void
+ServiceWorkerRegistrar::DataSaved()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mRunnableCounter);
+
+  --mRunnableCounter;
+  MaybeScheduleShutdownCompleted();
+}
+
+void
+ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mRunnableCounter || !mShuttingDown) {
+    return;
+  }
+
+  nsRefPtr<nsRunnable> runnable =
+     NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+nsresult
+ServiceWorkerRegistrar::WriteData()
+{
+  // We cannot assert about the correct thread because normally this method
+  // runs on a IO thread, but in gTests we call it from the main-thread.
+
+  MOZ_ASSERT(mProfileDir);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // We need a lock to take a snapshot of the data.
+  nsTArray<ServiceWorkerRegistrationData> data;
+  {
+    MonitorAutoLock lock(mMonitor);
+    data = mData;
+  }
+
+  nsCOMPtr<nsIOutputStream> stream;
+  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString buffer;
+  buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
+  buffer.Append('\n');
+
+  uint32_t count;
+  rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (count != buffer.Length()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
+    const mozilla::ipc::PrincipalInfo& info = data[i].principal();
+
+    if (info.type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
+      buffer.AssignLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL);
+    } else {
+      MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+
+      const mozilla::ipc::ContentPrincipalInfo& cInfo =
+        info.get_ContentPrincipalInfo();
+
+      buffer.AssignLiteral(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL);
+      buffer.Append('\n');
+
+      buffer.AppendInt(cInfo.appId());
+      buffer.Append('\n');
+
+      if (cInfo.isInBrowserElement()) {
+        buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TRUE);
+      } else {
+        buffer.AppendLiteral(SERVICEWORKERREGISTRAR_FALSE);
+      }
+
+      buffer.Append('\n');
+      buffer.Append(cInfo.spec());
+    }
+
+    buffer.Append('\n');
+
+    buffer.Append(data[i].scope());
+    buffer.Append('\n');
+
+    buffer.Append(data[i].scriptSpec());
+    buffer.Append('\n');
+
+    buffer.Append(data[i].currentWorkerURL());
+    buffer.Append('\n');
+
+    buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
+    buffer.Append('\n');
+
+    rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (count != buffer.Length()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
+  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
+  MOZ_ASSERT(safeStream);
+
+  rv = safeStream->Finish();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+ServiceWorkerRegistrar::ProfileStarted()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mProfileDir);
+
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(mProfileDir));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target, "Must have stream transport service");
+
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData);
+  rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the LoadDataRunnable.");
+  }
+}
+
+void
+ServiceWorkerRegistrar::ProfileStopped()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mProfileDir) {
+    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                         getter_AddRefs(mProfileDir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+  PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
+  if (!child) {
+    return;
+  }
+
+  bool completed = false;
+  mShutdownCompleteFlag = &completed;
+
+  child->SendShutdownServiceWorkerRegistrar();
+
+  nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+  while (true) {
+    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread));
+    if (completed) {
+      break;
+    }
+  }
+}
+
+void
+ServiceWorkerRegistrar::Shutdown()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShuttingDown);
+
+  mShuttingDown = true;
+  MaybeScheduleShutdownCompleted();
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
+                                const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!strcmp(aTopic, "profile-after-change")) {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    observerService->RemoveObserver(this, "profile-after-change");
+
+    // The profile is fully loaded, now we can proceed with the loading of data
+    // from disk.
+    ProfileStarted();
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, "profile-before-change")) {
+    nsCOMPtr<nsIObserverService> observerService =
+      services::GetObserverService();
+    observerService->RemoveObserver(this, "profile-before-change");
+
+    // Shutting down, let's sync the data.
+    ProfileStopped();
+
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrar.h
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_ServiceWorkerRegistrar_h
+#define mozilla_dom_workers_ServiceWorkerRegistrar_h
+
+#include "mozilla/Monitor.h"
+#include "mozilla/Telemetry.h"
+#include "nsClassHashtable.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+#define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
+#define SERVICEWORKERREGISTRAR_VERSION "1"
+#define SERVICEWORKERREGISTRAR_TERMINATOR "#"
+#define SERVICEWORKERREGISTRAR_TRUE "true"
+#define SERVICEWORKERREGISTRAR_FALSE "false"
+#define SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL "system"
+#define SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "content"
+
+
+class nsIFile;
+class nsRunnable;
+
+namespace mozilla {
+namespace dom {
+
+class ServiceWorkerRegistrationData;
+
+class ServiceWorkerRegistrar : public nsIObserver
+{
+  friend class ServiceWorkerRegistrarSaveDataRunnable;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static void Initialize();
+
+  void Shutdown();
+
+  void DataSaved();
+
+  static already_AddRefed<ServiceWorkerRegistrar> Get();
+
+  void GetRegistrations(nsTArray<ServiceWorkerRegistrationData>& aValues);
+
+  void RegisterServiceWorker(const ServiceWorkerRegistrationData& aData);
+  void UnregisterServiceWorker(const nsACString& aScope);
+
+protected:
+  // These methods are protected because we test this class using gTest
+  // subclassing it.
+  void LoadData();
+  void SaveData();
+
+  nsresult ReadData();
+  nsresult WriteData();
+  void DeleteData();
+
+  ServiceWorkerRegistrar();
+  virtual ~ServiceWorkerRegistrar();
+
+private:
+  void ProfileStarted();
+  void ProfileStopped();
+
+  void ScheduleSaveData();
+  void ShutdownCompleted();
+  void MaybeScheduleShutdownCompleted();
+
+  mozilla::Monitor mMonitor;
+
+protected:
+  // mData and mDataLoaded are protected by mMonitor.
+  nsTArray<ServiceWorkerRegistrationData> mData;
+  bool mDataLoaded;
+
+  bool mShuttingDown;
+  bool* mShutdownCompleteFlag;
+  uint32_t mRunnableCounter;
+
+  nsCOMPtr<nsIFile> mProfileDir;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_workers_ServiceWorkerRegistrar_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerRegistrarTypes.ipdlh
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* 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 PBackgroundSharedTypes;
+
+namespace mozilla {
+namespace dom {
+
+struct ServiceWorkerRegistrationData
+{
+  nsCString scope;
+  nsCString scriptSpec;
+  nsCString currentWorkerURL;
+  PrincipalInfo principal;
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -195,17 +195,17 @@ ServiceWorkerRegistration::Unregister(Er
   nsRefPtr<Promise> promise = Promise::Create(go, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsRefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
 
   NS_ConvertUTF8toUTF16 scope(uriSpec);
-  aRv = swm->Unregister(cb, scope);
+  aRv = swm->Unregister(documentPrincipal, cb, scope);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -585,17 +585,17 @@ public:
     if (NS_WARN_IF(NS_FAILED(rv))) {
       UnregisterFailed();
       return NS_OK;
     }
 
     // We don't need to check if the principal can load this mScope because a
     // ServiceWorkerGlobalScope can always unregister itself.
 
-    rv = swm->Unregister(this, mScope);
+    rv = swm->Unregister(mWorkerPrivate->GetPrincipal(), this, mScope);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       UnregisterFailed();
       return NS_OK;
     }
 
     return NS_OK;
   }
 
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Public stuff.
 EXPORTS.mozilla.dom += [
     'ServiceWorkerCommon.h',
     'ServiceWorkerContainer.h',
     'ServiceWorkerEvents.h',
+    'ServiceWorkerRegistrar.h',
     'ServiceWorkerRegistration.h',
     'WorkerPrivate.h',
     'WorkerRunnable.h',
     'WorkerScope.h',
 ]
 
 EXPORTS.mozilla.dom.workers += [
     'ServiceWorkerManager.h',
@@ -61,28 +62,33 @@ UNIFIED_SOURCES += [
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'ServiceWorker.cpp',
     'ServiceWorkerClient.cpp',
     'ServiceWorkerClients.cpp',
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
+    'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
     'WorkerPrivate.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
     'XMLHttpRequest.cpp',
     'XMLHttpRequestUpload.cpp',
 ]
 
+IPDL_SOURCES += [
+    'ServiceWorkerRegistrarTypes.ipdlh',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/dom/base',
@@ -103,8 +109,10 @@ MOCHITEST_MANIFESTS += [
     'test/fetch/mochitest.ini',
     'test/mochitest.ini',
     'test/serviceworkers/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
+
+TEST_DIRS += ['test/gtest']
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -0,0 +1,282 @@
+/* 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 "gtest/gtest.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIOutputStream.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+class ServiceWorkerRegistrarTest : public ServiceWorkerRegistrar
+{
+public:
+  ServiceWorkerRegistrarTest()
+  {
+    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(mProfileDir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+  nsresult TestReadData() { return ReadData(); }
+  nsresult TestWriteData() { return WriteData(); }
+  void TestDeleteData() { DeleteData(); }
+
+  nsTArray<ServiceWorkerRegistrationData>& TestGetData() { return mData; }
+};
+
+already_AddRefed<nsIFile>
+GetFile()
+{
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
+  return file.forget();
+}
+
+bool
+CreateFile(const nsACString& aData)
+{
+  nsCOMPtr<nsIFile> file = GetFile();
+
+  nsCOMPtr<nsIOutputStream> stream;
+  nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  uint32_t count;
+  rv = stream->Write(aData.Data(), aData.Length(), &count);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  if (count != aData.Length()) {
+    return false;
+  }
+
+  return true;
+}
+
+TEST(ServiceWorkerRegistrar, TestNoFile)
+{
+  nsCOMPtr<nsIFile> file = GetFile();
+  ASSERT_TRUE(file) << "GetFile must return a nsIFIle";
+
+  bool exists;
+  nsresult rv = file->Exists(&exists);
+  ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail";
+
+  if (exists) {
+    rv = file->Remove(false);
+    ASSERT_EQ(NS_OK, rv) << "nsIFile::Remove cannot fail";
+  }
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestEmptyFile)
+{
+  ASSERT_TRUE(CreateFile(EmptyCString())) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_NE(NS_OK, rv) << "ReadData() should fail if the file is empty";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestRightVersionFile)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString(SERVICEWORKERREGISTRAR_VERSION "\n"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail when the version is correct";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestWrongVersionFile)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString(SERVICEWORKERREGISTRAR_VERSION "bla\n"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_NE(NS_OK, rv) << "ReadData() should fail when the version is not correct";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
+}
+
+TEST(ServiceWorkerRegistrar, TestReadData)
+{
+  nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL "\n");
+  buffer.Append("scope 0\nscriptSpec 0\ncurrentWorkerURL 0\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "\n");
+  buffer.Append("123\n" SERVICEWORKERREGISTRAR_TRUE "\n");
+  buffer.Append("spec 1\nscope 1\nscriptSpec 1\ncurrentWorkerURL 1\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL "\n");
+  buffer.Append("0\n" SERVICEWORKERREGISTRAR_FALSE "\n");
+  buffer.Append("spec 2\nscope 2\nscriptSpec 2\ncurrentWorkerURL 2\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)3, data.Length()) << "4 entries should be found";
+
+  const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
+  ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) << "First principal must be system";
+
+  ASSERT_STREQ("scope 0", data[0].scope().get());
+  ASSERT_STREQ("scriptSpec 0", data[0].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
+
+  const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
+  ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
+
+  ASSERT_EQ((uint32_t)123, cInfo1.appId());
+  ASSERT_EQ((uint32_t)true, cInfo1.isInBrowserElement());
+  ASSERT_STREQ("spec 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", data[1].scope().get());
+  ASSERT_STREQ("scriptSpec 1", data[1].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
+
+  const mozilla::ipc::PrincipalInfo& info2 = data[2].principal();
+  ASSERT_EQ(info2.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo2 = data[2].principal();
+
+  ASSERT_EQ((uint32_t)0, cInfo2.appId());
+  ASSERT_EQ((uint32_t)false, cInfo2.isInBrowserElement());
+  ASSERT_STREQ("spec 2", cInfo2.spec().get());
+  ASSERT_STREQ("scope 2", data[2].scope().get());
+  ASSERT_STREQ("scriptSpec 2", data[2].scriptSpec().get());
+  ASSERT_STREQ("currentWorkerURL 2", data[2].currentWorkerURL().get());
+}
+
+TEST(ServiceWorkerRegistrar, TestDeleteData)
+{
+  ASSERT_TRUE(CreateFile(nsAutoCString("Foobar"))) << "CreateFile should not fail";
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  swr->TestDeleteData();
+
+  nsCOMPtr<nsIFile> file = GetFile();
+
+  bool exists;
+  nsresult rv = file->Exists(&exists);
+  ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail";
+
+  ASSERT_FALSE(exists) << "The file should not exist after a DeleteData().";
+}
+
+TEST(ServiceWorkerRegistrar, TestWriteData)
+{
+  {
+    nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+    nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+
+    for (int i = 0; i < 10; ++i) {
+      ServiceWorkerRegistrationData* d = data.AppendElement();
+
+      if ((i % 2) == 0) {
+        d->principal() = mozilla::ipc::SystemPrincipalInfo();
+      } else if ((i % 2) == 1) {
+        nsAutoCString spec;
+        spec.AppendPrintf("spec write %d", i);
+        d->principal() = mozilla::ipc::ContentPrincipalInfo(i, i % 2, spec);
+      }
+
+      d->scope().AppendPrintf("scope write %d", i);
+      d->scriptSpec().AppendPrintf("scriptSpec write %d", i);
+      d->currentWorkerURL().AppendPrintf("currentWorkerURL write %d", i);
+    }
+
+    nsresult rv = swr->TestWriteData();
+    ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail";
+  }
+
+  nsRefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)10, data.Length()) << "10 entries should be found";
+
+  for (int i = 0; i < 10; ++i) {
+    nsAutoCString test;
+
+    if ((i % 2) == 0) {
+      ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo);
+    } else if ((i % 2) == 1) {
+      ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+      const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal();
+
+      ASSERT_EQ((uint32_t)i, cInfo.appId());
+      ASSERT_EQ((uint32_t)(i %2), cInfo.isInBrowserElement());
+
+      test.AppendPrintf("spec write %d", i);
+      ASSERT_STREQ(test.get(), cInfo.spec().get());
+    }
+
+    test.Truncate();
+    test.AppendPrintf("scope write %d", i);
+    ASSERT_STREQ(test.get(), data[i].scope().get());
+
+    test.Truncate();
+    test.AppendPrintf("scriptSpec write %d", i);
+    ASSERT_STREQ(test.get(), data[i].scriptSpec().get());
+
+    test.Truncate();
+    test.AppendPrintf("currentWorkerURL write %d", i);
+    ASSERT_STREQ(test.get(), data[i].currentWorkerURL().get());
+  }
+}
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  int rv = RUN_ALL_TESTS();
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES = [
+    'TestReadWrite.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/dom/workers/test/navigator_worker.js
+++ b/dom/workers/test/navigator_worker.js
@@ -13,66 +13,73 @@ var supportedProps = [
   "product",
   "taintEnabled",
   "userAgent",
   "onLine",
   "language",
   "languages",
 ];
 
-var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
-var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
-
-// Prepare the interface map showing if a propery should exist in this build.
-// For example, if interfaceMap[foo] = true means navigator.foo should exist.
-var interfaceMap = {};
-
-for (var prop of supportedProps) {
-  if (typeof(prop) === "string") {
-    interfaceMap[prop] = true;
-    continue;
+self.onmessage = function(event) {
+  if (!event || !event.data) {
+    return;
   }
 
-  if (prop.b2g === !isB2G) {
-    interfaceMap[prop.name] = false;
-    continue;
+  startTest(event.data.isB2G);
+};
+
+function startTest(isB2G) {
+  // Prepare the interface map showing if a propery should exist in this build.
+  // For example, if interfaceMap[foo] = true means navigator.foo should exist.
+  var interfaceMap = {};
+
+  for (var prop of supportedProps) {
+    if (typeof(prop) === "string") {
+      interfaceMap[prop] = true;
+      continue;
+    }
+
+    if (prop.b2g === !isB2G) {
+      interfaceMap[prop.name] = false;
+      continue;
+    }
+
+    interfaceMap[prop.name] = true;
   }
 
-  interfaceMap[prop.name] = true;
-}
-
-for (var prop in navigator) {
-  // Make sure the list is current!
-  if (!interfaceMap[prop]) {
-    throw "Navigator has the '" + prop + "' property that isn't in the list!";
-  }
-}
-
-var obj;
-
-for (var prop in interfaceMap) {
-  // Skip the property that is not supposed to exist in this build.
-  if (!interfaceMap[prop]) {
-    continue;
+  for (var prop in navigator) {
+    // Make sure the list is current!
+    if (!interfaceMap[prop]) {
+      throw "Navigator has the '" + prop + "' property that isn't in the list!";
+    }
   }
 
-  if (typeof navigator[prop] == "undefined") {
-    throw "Navigator has no '" + prop + "' property!";
+  var obj;
+
+  for (var prop in interfaceMap) {
+    // Skip the property that is not supposed to exist in this build.
+    if (!interfaceMap[prop]) {
+      continue;
+    }
+
+    if (typeof navigator[prop] == "undefined") {
+      throw "Navigator has no '" + prop + "' property!";
+    }
+
+    obj = { name:  prop };
+
+    if (prop === "taintEnabled") {
+      obj.value = navigator[prop]();
+    } else if (prop === "getDataStores") {
+      obj.value = typeof navigator[prop];
+    } else {
+      obj.value = navigator[prop];
+    }
+
+    postMessage(JSON.stringify(obj));
   }
 
-  obj = { name:  prop };
-
-  if (prop === "taintEnabled") {
-    obj.value = navigator[prop]();
-  } else if (prop === "getDataStores") {
-    obj.value = typeof navigator[prop];
-  } else {
-    obj.value = navigator[prop];
-  }
+  obj = {
+    name: "testFinished"
+  };
 
   postMessage(JSON.stringify(obj));
 }
-
-obj = {
-  name: "testFinished"
-};
-
-postMessage(JSON.stringify(obj));
--- a/dom/workers/test/test_navigator.html
+++ b/dom/workers/test/test_navigator.html
@@ -55,14 +55,16 @@ Tests of DOM Worker Navigator
        "Mismatched navigator string for " + args.name + "!");
   };
 
   worker.onerror = function(event) {
     ok(false, "Worker had an error: " + event.message);
     SimpleTest.finish();
   }
 
+  worker.postMessage({ 'isB2G': SpecialPowers.isB2G });
+
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -776,19 +776,18 @@ XULDocument::AddBroadcastListenerFor(Ele
         }
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
                    (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
 
     if (!entry) {
-        entry =
-            static_cast<BroadcasterMapEntry*>
-                       (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster));
+        entry = static_cast<BroadcasterMapEntry*>
+            (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster, fallible));
 
         if (! entry) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         entry->mBroadcaster = &aBroadcaster;
 
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -23,17 +23,18 @@ class nsContentSupportMap {
 public:
     nsContentSupportMap() { Init(); }
     ~nsContentSupportMap() { Finish(); }
 
     nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) {
         if (!mMap.IsInitialized())
             return NS_ERROR_NOT_INITIALIZED;
 
-        PLDHashEntryHdr* hdr = PL_DHashTableAdd(&mMap, aElement);
+        PLDHashEntryHdr* hdr =
+            PL_DHashTableAdd(&mMap, aElement, mozilla::fallible);
         if (!hdr)
             return NS_ERROR_OUT_OF_MEMORY;
 
         Entry* entry = static_cast<Entry*>(hdr);
         NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry");
         entry->mContent = aElement;
         entry->mMatch   = aMatch;
         return NS_OK;
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -32,17 +32,18 @@ public:
 
     ~nsTemplateMap() { Finish(); }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
         NS_ASSERTION(!PL_DHashTableSearch(&mTable, aContent),
                      "aContent already in map");
 
-        Entry* entry = static_cast<Entry*>(PL_DHashTableAdd(&mTable, aContent));
+        Entry* entry = static_cast<Entry*>
+            (PL_DHashTableAdd(&mTable, aContent, fallible));
 
         if (entry) {
             entry->mContent = aContent;
             entry->mTemplate = aTemplate;
         }
     }
 
     void
--- a/embedding/components/commandhandler/nsCommandParams.cpp
+++ b/embedding/components/commandhandler/nsCommandParams.cpp
@@ -225,17 +225,18 @@ nsCommandParams::GetOrMakeEntry(const ch
 {
   HashEntry *foundEntry =
     (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName);
   if (foundEntry) { // reuse existing entry
     foundEntry->Reset(entryType);
     return foundEntry;
   }
 
-  foundEntry = (HashEntry *)PL_DHashTableAdd(&mValuesHash, (void *)aName);
+  foundEntry = static_cast<HashEntry*>
+    (PL_DHashTableAdd(&mValuesHash, (void *)aName, fallible));
   if (!foundEntry) {
     return nullptr;
   }
 
   // Use placement new. Our ctor does not clobber keyHash, which is important.
   new (foundEntry) HashEntry(entryType, aName);
   return foundEntry;
 }
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -101,131 +101,137 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
     aSuccessOut = true;
   }
   // Return the final scroll position after setting it so that anything that relies
   // on it can have an accurate value. Note that even if we set it above re-querying it
   // is a good idea because it may have gotten clamped or rounded.
   return geckoScrollPosition;
 }
 
+/**
+ * Scroll the scroll frame associated with |aContent| to the scroll position
+ * requested in |aMetrics|.
+ * The scroll offset in |aMetrics| is updated to reflect the actual scroll
+ * position.
+ * The displayport stored in |aMetrics| and the callback-transform stored on
+ * the content are updated to reflect any difference between the requested
+ * and actual scroll positions.
+ */
+static void
+ScrollFrame(nsIContent* aContent,
+            FrameMetrics& aMetrics)
+{
+  // Scroll the window to the desired spot
+  nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+  bool scrollUpdated = false;
+  CSSPoint apzScrollOffset = aMetrics.GetScrollOffset();
+  CSSPoint actualScrollOffset = ScrollFrameTo(sf, apzScrollOffset, scrollUpdated);
+
+  if (scrollUpdated) {
+    // Correct the display port due to the difference between mScrollOffset and the
+    // actual scroll offset.
+    AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
+  } else {
+    // For whatever reason we couldn't update the scroll offset on the scroll frame,
+    // which means the data APZ used for its displayport calculation is stale. Fall
+    // back to a sane default behaviour. Note that we don't tile-align the recentered
+    // displayport because tile-alignment depends on the scroll position, and the
+    // scroll position here is out of our control. See bug 966507 comment 21 for a
+    // more detailed explanation.
+    RecenterDisplayPort(aMetrics);
+  }
+
+  aMetrics.SetScrollOffset(actualScrollOffset);
+
+  // APZ transforms inputs assuming we applied the exact scroll offset it
+  // requested (|apzScrollOffset|). Since we may not have, record the difference
+  // between what APZ asked for and what we actually applied, and apply it to
+  // input events to compensate.
+  if (aContent) {
+    CSSPoint scrollDelta = apzScrollOffset - actualScrollOffset;
+    aContent->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta),
+                          nsINode::DeleteProperty<CSSPoint>);
+  }
+}
+
+static void
+SetDisplayPortMargins(nsIDOMWindowUtils* aUtils,
+                      nsIContent* aContent,
+                      FrameMetrics& aMetrics)
+{
+  if (!aContent) {
+    return;
+  }
+  nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
+  if (!element) {
+    return;
+  }
+
+  ScreenMargin margins = aMetrics.GetDisplayPortMargins();
+  aUtils->SetDisplayPortMarginsForElement(margins.left,
+                                          margins.top,
+                                          margins.right,
+                                          margins.bottom,
+                                          element, 0);
+  CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels();
+  nsRect base(0, 0,
+              baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
+              baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
+  nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
+}
+
 void
 APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
                                     FrameMetrics& aMetrics)
 {
-    // Precondition checks
-    MOZ_ASSERT(aUtils);
-    MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
-    if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
-        return;
-    }
-
-    // Set the scroll port size, which determines the scroll range. For example if
-    // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
-    // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
-    // overscroll). Note that if the content here was zoomed to 2x, the document would
-    // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
-    // scroll range would be 900. Therefore this calculation depends on the zoom applied
-    // to the content relative to the container.
-    CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
-    aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
-
-    // Scroll the window to the desired spot
-    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
-    bool scrollUpdated = false;
-    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated);
+  // Precondition checks
+  MOZ_ASSERT(aUtils);
+  MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
+  if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
+    return;
+  }
 
-    if (scrollUpdated) {
-        // Correct the display port due to the difference between mScrollOffset and the
-        // actual scroll offset.
-        AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
-    } else {
-        // For whatever reason we couldn't update the scroll offset on the scroll frame,
-        // which means the data APZ used for its displayport calculation is stale. Fall
-        // back to a sane default behaviour. Note that we don't tile-align the recentered
-        // displayport because tile-alignment depends on the scroll position, and the
-        // scroll position here is out of our control. See bug 966507 comment 21 for a
-        // more detailed explanation.
-        RecenterDisplayPort(aMetrics);
-    }
-
-    aMetrics.SetScrollOffset(actualScrollOffset);
-
-    // The pres shell resolution is updated by the the async zoom since the
-    // last paint.
-    float presShellResolution = aMetrics.GetPresShellResolution()
-                              * aMetrics.GetAsyncZoom().scale;
-    aUtils->SetResolutionAndScaleTo(presShellResolution, presShellResolution);
+  // Set the scroll port size, which determines the scroll range. For example if
+  // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
+  // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
+  // overscroll). Note that if the content here was zoomed to 2x, the document would
+  // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
+  // scroll range would be 900. Therefore this calculation depends on the zoom applied
+  // to the content relative to the container.
+  // Note that this needs to happen before scrolling the frame (in UpdateFrameCommon),
+  // otherwise the scroll position may get clamped incorrectly.
+  CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
+  aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
 
-    // Finally, we set the displayport.
-    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
-    if (!content) {
-        return;
-    }
-    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(content);
-    if (!element) {
-        return;
-    }
+  nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
+  ScrollFrame(content, aMetrics);
 
-    ScreenMargin margins = aMetrics.GetDisplayPortMargins();
-    aUtils->SetDisplayPortMarginsForElement(margins.left,
-                                            margins.top,
-                                            margins.right,
-                                            margins.bottom,
-                                            element, 0);
-    CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels();
-    nsRect base(0, 0,
-                baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
-                baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
-    nsLayoutUtils::SetDisplayPortBaseIfNotSet(content, base);
+  // The pres shell resolution is updated by the the async zoom since the
+  // last paint.
+  float presShellResolution = aMetrics.GetPresShellResolution()
+                            * aMetrics.GetAsyncZoom().scale;
+  aUtils->SetResolutionAndScaleTo(presShellResolution, presShellResolution);
+
+  SetDisplayPortMargins(aUtils, content, aMetrics);
 }
 
 void
 APZCCallbackHelper::UpdateSubFrame(nsIContent* aContent,
                                    FrameMetrics& aMetrics)
 {
-    // Precondition checks
-    MOZ_ASSERT(aContent);
-    MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
-    if (aMetrics.GetScrollId() == FrameMetrics::NULL_SCROLL_ID) {
-        return;
-    }
-
-    nsCOMPtr<nsIDOMWindowUtils> utils = GetDOMWindowUtils(aContent);
-    if (!utils) {
-        return;
-    }
-
-    // We currently do not support zooming arbitrary subframes. They can only
-    // be scrolled, so here we only have to set the scroll position and displayport.
-
-    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
-    bool scrollUpdated = false;
-    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated);
+  // Precondition checks
+  MOZ_ASSERT(aContent);
+  MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
 
-    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
-    if (element) {
-        if (scrollUpdated) {
-            AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
-        } else {
-            RecenterDisplayPort(aMetrics);
-        }
-        ScreenMargin margins = aMetrics.GetDisplayPortMargins();
-        utils->SetDisplayPortMarginsForElement(margins.left,
-                                               margins.top,
-                                               margins.right,
-                                               margins.bottom,
-                                               element, 0);
-        CSSRect baseCSS = aMetrics.CalculateCompositedRectInCssPixels();
-        nsRect base(0, 0,
-                    baseCSS.width * nsPresContext::AppUnitsPerCSSPixel(),
-                    baseCSS.height * nsPresContext::AppUnitsPerCSSPixel());
-        nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent, base);
-    }
-
-    aMetrics.SetScrollOffset(actualScrollOffset);
+  // We don't currently support zooming for subframes, so nothing extra
+  // needs to be done beyond the tasks common to this and UpdateRootFrame.
+  ScrollFrame(aContent, aMetrics);
+  if (nsCOMPtr<nsIDOMWindowUtils> utils = GetDOMWindowUtils(aContent)) {
+    SetDisplayPortMargins(utils, aContent, aMetrics);
+  }
 }
 
 already_AddRefed<nsIDOMWindowUtils>
 APZCCallbackHelper::GetDOMWindowUtils(const nsIDocument* aDoc)
 {
     nsCOMPtr<nsIDOMWindowUtils> utils;
     nsCOMPtr<nsIDOMWindow> window = aDoc->GetDefaultView();
     if (window) {
@@ -301,28 +307,16 @@ APZCCallbackHelper::AcknowledgeScrollUpd
     nsCOMPtr<nsIRunnable> r1 = new AcknowledgeScrollUpdateEvent(aScrollId, aScrollGeneration);
     if (!NS_IsMainThread()) {
         NS_DispatchToMainThread(r1);
     } else {
         r1->Run();
     }
 }
 
-void
-APZCCallbackHelper::UpdateCallbackTransform(const FrameMetrics& aApzcMetrics, const FrameMetrics& aActualMetrics)
-{
-    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aApzcMetrics.GetScrollId());
-    if (!content) {
-        return;
-    }
-    CSSPoint scrollDelta = aApzcMetrics.GetScrollOffset() - aActualMetrics.GetScrollOffset();
-    content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta),
-                         nsINode::DeleteProperty<CSSPoint>);
-}
-
 CSSPoint
 APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
                                            const ScrollableLayerGuid& aGuid,
                                            float aPresShellResolution)
 {
     // First, scale inversely by the pres shell resolution to cancel the
     // scale-to-resolution transform that the compositor adds to the layer with
     // the pres shell resolution. The points sent to Gecko by APZ don't have
@@ -361,16 +355,28 @@ APZCCallbackHelper::ApplyCallbackTransfo
                                            const CSSToLayoutDeviceScale& aScale,
                                            float aPresShellResolution)
 {
     LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
     point = ApplyCallbackTransform(point / aScale, aGuid, aPresShellResolution) * aScale;
     return gfx::RoundedToInt(point);
 }
 
+void
+APZCCallbackHelper::ApplyCallbackTransform(WidgetTouchEvent& aEvent,
+                                           const ScrollableLayerGuid& aGuid,
+                                           const CSSToLayoutDeviceScale& aScale,
+                                           float aPresShellResolution)
+{
+  for (size_t i = 0; i < aEvent.touches.Length(); i++) {
+    aEvent.touches[i]->mRefPoint = ApplyCallbackTransform(
+        aEvent.touches[i]->mRefPoint, aGuid, aScale, aPresShellResolution);
+  }
+}
+
 nsEventStatus
 APZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent& aEvent)
 {
   if (!aEvent.widget)
     return nsEventStatus_eConsumeNoDefault;
 
   // A nested process may be capturing events.
   if (TabParent* capturer = TabParent::GetEventCapturer()) {
@@ -408,25 +414,213 @@ APZCCallbackHelper::DispatchSynthesizedM
   if (aMsg != NS_MOUSE_MOVE) {
     event.clickCount = 1;
   }
   event.widget = aWidget;
 
   return DispatchWidgetEvent(event);
 }
 
+bool
+APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
+                                       const nsString& aType,
+                                       const CSSPoint& aPoint,
+                                       int32_t aButton,
+                                       int32_t aClickCount,
+                                       int32_t aModifiers,
+                                       bool aIgnoreRootScrollFrame,
+                                       unsigned short aInputSourceArg)
+{
+  NS_ENSURE_TRUE(aUtils, true);
+
+  bool defaultPrevented = false;
+  aUtils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
+                         aIgnoreRootScrollFrame, 0, aInputSourceArg, false, 4, &defaultPrevented);
+  return defaultPrevented;
+}
+
+
 void
 APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
                                        nsIWidget* aWidget)
 {
   if (aWidget->Destroyed()) {
     return;
   }
   APZCCH_LOG("Dispatching single-tap component events to %s\n",
     Stringify(aPoint).c_str());
   int time = 0;
   DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, aWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, aWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aWidget);
 }
 
+static nsIScrollableFrame*
+GetScrollableAncestorFrame(nsIFrame* aTarget)
+{
+  if (!aTarget) {
+    return nullptr;
+  }
+  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
+                 | nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
+  return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
+}
+
+static dom::Element*
+GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
+{
+  if (!aScrollableFrame) {
+    return nullptr;
+  }
+  nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
+  if (!scrolledFrame) {
+    return nullptr;
+  }
+  // |scrolledFrame| should at this point be the root content frame of the
+  // nearest ancestor scrollable frame. The element corresponding to this
+  // frame should be the one with the displayport set on it, so find that
+  // element and return it.
+  nsIContent* content = scrolledFrame->GetContent();
+  MOZ_ASSERT(content->IsElement()); // roc says this must be true
+  return content->AsElement();
+}
+
+// Determine the scrollable target frame for the given point and add it to
+// the target list. If the frame doesn't have a displayport, set one.
+// Return whether or not a displayport was set.
+static bool
+PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
+                                    const ScrollableLayerGuid& aGuid,
+                                    nsIFrame* aRootFrame,
+                                    const LayoutDeviceIntPoint& aRefPoint,
+                                    nsTArray<ScrollableLayerGuid>* aTargets)
+{
+  ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
+  nsPoint point =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
+  nsIFrame* target =
+    nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+  nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
+  nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
+
+  nsAutoString dpElementDesc;
+  if (dpElement) {
+    dpElement->Describe(dpElementDesc);
+  }
+  APZCCH_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
+      aInputBlockId, dpElement.get(),
+      NS_LossyConvertUTF16toASCII(dpElementDesc).get());
+
+  bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+    dpElement, &(guid.mPresShellId), &(guid.mScrollId));
+  aTargets->AppendElement(guid);
+
+  if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
+    return false;
+  }
+
+  APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
+  return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
+      scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
+}
+
+class DisplayportSetListener : public nsAPostRefreshObserver {
+public:
+  DisplayportSetListener(const nsRefPtr<SetTargetAPZCCallback>& aCallback,
+                         nsIPresShell* aPresShell,
+                         const uint64_t& aInputBlockId,
+                         const nsTArray<ScrollableLayerGuid>& aTargets)
+    : mCallback(aCallback)
+    , mPresShell(aPresShell)
+    , mInputBlockId(aInputBlockId)
+    , mTargets(aTargets)
+  {
+  }
+
+  virtual ~DisplayportSetListener()
+  {
+  }
+
+  void DidRefresh() MOZ_OVERRIDE {
+    if (!mCallback) {
+      MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
+      return;
+    }
+
+    APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
+    mCallback->Run(mInputBlockId, mTargets);
+
+    if (!mPresShell->RemovePostRefreshObserver(this)) {
+      MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
+      // Graceful handling, just in case...
+      mCallback = nullptr;
+      mPresShell = nullptr;
+      return;
+    }
+
+    delete this;
+  }
+
+private:
+  nsRefPtr<SetTargetAPZCCallback> mCallback;
+  nsRefPtr<nsIPresShell> mPresShell;
+  uint64_t mInputBlockId;
+  nsTArray<ScrollableLayerGuid> mTargets;
+};
+
+// Sends a SetTarget notification for APZC, given one or more previous
+// calls to PrepareForAPZCSetTargetNotification().
+static void
+SendSetTargetAPZCNotificationHelper(nsIPresShell* aShell,
+                                    const uint64_t& aInputBlockId,
+                                    const nsTArray<ScrollableLayerGuid>& aTargets,
+                                    bool aWaitForRefresh,
+                                    const nsRefPtr<SetTargetAPZCCallback>& aCallback)
+{
+  bool waitForRefresh = aWaitForRefresh;
+  if (waitForRefresh) {
+    APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n");
+    waitForRefresh = aShell->AddPostRefreshObserver(
+      new DisplayportSetListener(aCallback, aShell, aInputBlockId, aTargets));
+  }
+  if (!waitForRefresh) {
+    APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
+    aCallback->Run(aInputBlockId, aTargets);
+  } else {
+    APZCCH_LOG("Successfully registered post-refresh observer\n");
+  }
+}
+
+void
+APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
+                                                  nsIDocument* aDocument,
+                                                  const WidgetGUIEvent& aEvent,
+                                                  const ScrollableLayerGuid& aGuid,
+                                                  uint64_t aInputBlockId,
+                                                  const nsRefPtr<SetTargetAPZCCallback>& aCallback)
+{
+  if (nsIPresShell* shell = aDocument->GetShell()) {
+    if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+      bool waitForRefresh = false;
+      nsTArray<ScrollableLayerGuid> targets;
+
+      if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
+        for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
+          waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+              rootFrame, touchEvent->touches[i]->mRefPoint, &targets);
+        }
+      } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
+        waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+            rootFrame, wheelEvent->refPoint, &targets);
+      }
+      // TODO: Do other types of events need to be handled?
+
+      if (!targets.IsEmpty()) {
+        SendSetTargetAPZCNotificationHelper(shell, aInputBlockId, targets,
+            waitForRefresh, aCallback);
+      }
+    }
+  }
+}
+
 }
 }
+
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -13,16 +13,27 @@
 class nsIContent;
 class nsIDocument;
 class nsIWidget;
 template<class T> struct already_AddRefed;
 
 namespace mozilla {
 namespace layers {
 
+/* A base class for callbacks to be passed to APZCCallbackHelper::SendSetTargetAPZCNotification.
+ * If we had something like std::function, we could just use
+ * std::function<void(uint64_t, const nsTArray<ScrollableLayerGuid>&)>. */
+struct SetTargetAPZCCallback {
+public:
+  NS_INLINE_DECL_REFCOUNTING(SetTargetAPZCCallback)
+  virtual void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const = 0;
+protected:
+  virtual ~SetTargetAPZCCallback() {}
+};
+
 /* This class contains some helper methods that facilitate implementing the
    GeckoContentController callback interface required by the AsyncPanZoomController.
    Since different platforms need to implement this interface in similar-but-
    not-quite-the-same ways, this utility class provides some helpful methods
    to hold code that can be shared across the different platform implementations.
  */
 class APZCCallbackHelper
 {
@@ -70,29 +81,16 @@ public:
                                              uint32_t* aPresShellIdOut,
                                              FrameMetrics::ViewID* aViewIdOut);
 
     /* Tell layout that we received the scroll offset update for the given view ID, so
        that it accepts future scroll offset updates from APZ. */
     static void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
                                         const uint32_t& aScrollGeneration);
 
-    /* Save an "input transform" property on the content element corresponding to
-       the scrollable content. This is needed because in some cases when the APZ code
-       sends a paint request via the GeckoContentController interface, we don't always
-       apply the scroll offset that was requested. Since the APZ code doesn't know
-       that we didn't apply it, it will transform inputs assuming that we had applied
-       it, and so we need to apply a fixup to the input to account for the fact that
-       we didn't.
-       The |aApzcMetrics| argument are the metrics that the APZ sent us, and the
-       |aActualMetrics| argument are the metrics representing the gecko state after we
-       applied some or all of the APZ metrics. */
-    static void UpdateCallbackTransform(const FrameMetrics& aApzcMetrics,
-                                        const FrameMetrics& aActualMetrics);
-
     /* Apply an "input transform" to the given |aInput| and return the transformed value.
        The input transform applied is the one for the content element corresponding to
        |aGuid|; this is populated in a previous call to UpdateCallbackTransform. See that
        method's documentations for details.
        This method additionally adjusts |aInput| by inversely scaling by the provided
        pres shell resolution, to cancel out a compositor-side transform (added in
        bug 1076241) that APZ doesn't unapply. */
     static CSSPoint ApplyCallbackTransform(const CSSPoint& aInput,
@@ -103,30 +101,61 @@ public:
        Requires an additonal |aScale| parameter to convert between CSS and
        LayoutDevice space. */
     static mozilla::LayoutDeviceIntPoint
     ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
                            const ScrollableLayerGuid& aGuid,
                            const CSSToLayoutDeviceScale& aScale,
                            float aPresShellResolution);
 
+    /* Convenience function for applying a callback transform to all touch
+     * points of a touch event. */
+    static void ApplyCallbackTransform(WidgetTouchEvent& aEvent,
+                                       const ScrollableLayerGuid& aGuid,
+                                       const CSSToLayoutDeviceScale& aScale,
+                                       float aPresShellResolution);
+
     /* Dispatch a widget event via the widget stored in the event, if any.
      * In a child process, allows the TabParent event-capture mechanism to
      * intercept the event. */
     static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
 
     /* Synthesize a mouse event with the given parameters, and dispatch it
      * via the given widget. */
     static nsEventStatus DispatchSynthesizedMouseEvent(uint32_t aMsg,
                                                        uint64_t aTime,
                                                        const LayoutDevicePoint& aRefPoint,
                                                        nsIWidget* aWidget);
 
+    /* Dispatch a mouse event with the given parameters.
+     * Return whether or not any listeners have called preventDefault on the event. */
+    static bool DispatchMouseEvent(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
+                                   const nsString& aType,
+                                   const CSSPoint& aPoint,
+                                   int32_t aButton,
+                                   int32_t aClickCount,
+                                   int32_t aModifiers,
+                                   bool aIgnoreRootScrollFrame,
+                                   unsigned short aInputSourceArg);
+
     /* Fire a single-tap event at the given point. The event is dispatched
      * via the given widget. */
     static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
                                    nsIWidget* aWidget);
+
+    /* Perform hit-testing on the touch points of |aEvent| to determine
+     * which scrollable frames they target. If any of these frames don't have
+     * a displayport, set one. Finally, invoke the provided callback with
+     * the guids of the target frames. If any displayports needed to be set,
+     * the callback is invoked after the next refresh, otherwise it's invoked
+     * right away. */
+    static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
+                                              nsIDocument* aDocument,
+                                              const WidgetGUIEvent& aEvent,
+                                              const ScrollableLayerGuid& aGuid,
+                                              uint64_t aInputBlockId,
+                                              const nsRefPtr<SetTargetAPZCCallback>& aCallback);
 };
 
 }
 }
 
 #endif /* mozilla_layers_APZCCallbackHelper_h */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "APZEventState.h"
+
+#include "ActiveElementManager.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/Preferences.h"
+#include "nsCOMPtr.h"
+#include "nsDocShell.h"
+#include "nsITimer.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIWidget.h"
+
+#define APZES_LOG(...)
+// #define APZES_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)
+
+namespace mozilla {
+namespace layers {
+
+static int32_t sActiveDurationMs = 10;
+static bool sActiveDurationMsSet = false;
+
+APZEventState::APZEventState(nsIWidget* aWidget,
+                             const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback)
+  : mWidget(nullptr)  // initialized in constructor body
+  , mActiveElementManager(new ActiveElementManager())
+  , mContentReceivedInputBlockCallback(aCallback)
+  , mPendingTouchPreventedResponse(false)
+  , mPendingTouchPreventedBlockId(0)
+  , mEndTouchIsClick(false)
+  , mTouchEndCancelled(false)
+{
+  nsresult rv;
+  mWidget = do_GetWeakReference(aWidget, &rv);
+  MOZ_ASSERT(NS_SUCCEEDED(rv), "APZEventState constructed with a widget that"
+      " does not support weak references. APZ will NOT work!");
+
+  if (!sActiveDurationMsSet) {
+    Preferences::AddIntVarCache(&sActiveDurationMs,
+                                "ui.touch_activation.duration_ms",
+                                sActiveDurationMs);
+    sActiveDurationMsSet = true;
+  }
+}
+
+APZEventState::~APZEventState()
+{}
+
+class DelayedFireSingleTapEvent MOZ_FINAL : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  DelayedFireSingleTapEvent(nsWeakPtr aWidget,
+                            LayoutDevicePoint& aPoint,
+                            nsITimer* aTimer)
+    : mWidget(aWidget)
+    , mPoint(aPoint)
+    // Hold the reference count until we are called back.
+    , mTimer(aTimer)
+  {
+  }
+
+  NS_IMETHODIMP Notify(nsITimer*) MOZ_OVERRIDE
+  {
+    if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
+      APZCCallbackHelper::FireSingleTapEvent(mPoint, widget);
+    }
+    mTimer = nullptr;
+    return NS_OK;
+  }
+
+  void ClearTimer() {
+    mTimer = nullptr;
+  }
+
+private:
+  ~DelayedFireSingleTapEvent()
+  {
+  }
+
+  nsWeakPtr mWidget;
+  LayoutDevicePoint mPoint;
+  nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
+
+void
+APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
+                                const ScrollableLayerGuid& aGuid,
+                                float aPresShellResolution)
+{
+  APZES_LOG("Handling single tap at %s on %s with %d\n",
+    Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return;
+  }
+
+  if (mTouchEndCancelled) {
+    return;
+  }
+
+  LayoutDevicePoint currentPoint =
+      APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
+    * widget->GetDefaultScale();;
+  if (!mActiveElementManager->ActiveElementUsesStyle()) {
+    // If the active element isn't visually affected by the :active style, we
+    // have no need to wait the extra sActiveDurationMs to make the activation
+    // visually obvious to the user.
+    APZCCallbackHelper::FireSingleTapEvent(currentPoint, widget);
+    return;
+  }
+
+  APZES_LOG("Active element uses style, scheduling timer for click event\n");
+  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  nsRefPtr<DelayedFireSingleTapEvent> callback =
+    new DelayedFireSingleTapEvent(mWidget, currentPoint, timer);
+  nsresult rv = timer->InitWithCallback(callback,
+                                        sActiveDurationMs,
+                                        nsITimer::TYPE_ONE_SHOT);
+  if (NS_FAILED(rv)) {
+    // Make |callback| not hold the timer, so they will both be destructed when
+    // we leave the scope of this function.
+    callback->ClearTimer();
+  }
+}
+
+void
+APZEventState::ProcessLongTap(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
+                              const CSSPoint& aPoint,
+                              const ScrollableLayerGuid& aGuid,
+                              uint64_t aInputBlockId,
+                              float aPresShellResolution)
+{
+  APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return;
+  }
+
+  SendPendingTouchPreventedResponse(false, aGuid);
+
+  bool eventHandled =
+      APZCCallbackHelper::DispatchMouseEvent(aUtils, NS_LITERAL_STRING("contextmenu"),
+                         APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution),
+                         2, 1, 0, true,
+                         nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+
+  APZES_LOG("Contextmenu event handled: %d\n", eventHandled);
+
+  // If no one handle context menu, fire MOZLONGTAP event
+  if (!eventHandled) {
+    LayoutDevicePoint currentPoint =
+        APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, aPresShellResolution)
+      * widget->GetDefaultScale();
+    int time = 0;
+    nsEventStatus status =
+        APZCCallbackHelper::DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, widget);
+    eventHandled = (status == nsEventStatus_eConsumeNoDefault);
+    APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
+  }
+
+  mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled);
+}
+
+void
+APZEventState::ProcessLongTapUp(const CSSPoint& aPoint,
+                                const ScrollableLayerGuid& aGuid,
+                                float aPresShellResolution)
+{
+  APZES_LOG("Handling long tap up at %s\n", Stringify(aPoint).c_str());
+
+  ProcessSingleTap(aPoint, aGuid, aPresShellResolution);
+}
+
+void
+APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+                                 const ScrollableLayerGuid& aGuid,
+                                 uint64_t aInputBlockId)
+{
+  if (aEvent.message == NS_TOUCH_START && aEvent.touches.Length() > 0) {
+    mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
+  }
+
+  bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
+      aEvent.mFlags.mMultipleActionsPrevented;
+  switch (aEvent.message) {
+  case NS_TOUCH_START: {
+    mTouchEndCancelled = false;
+    if (mPendingTouchPreventedResponse) {
+      // We can enter here if we get two TOUCH_STARTs in a row and didn't
+      // respond to the first one. Respond to it now.
+      mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
+          mPendingTouchPreventedBlockId, false);
+      mPendingTouchPreventedResponse = false;
+    }
+    if (isTouchPrevented) {
+      mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, isTouchPrevented);
+    } else {
+      mPendingTouchPreventedResponse = true;
+      mPendingTouchPreventedGuid = aGuid;
+      mPendingTouchPreventedBlockId = aInputBlockId;
+    }
+    break;
+  }
+
+  case NS_TOUCH_END:
+    if (isTouchPrevented) {
+      mTouchEndCancelled = true;
+      mEndTouchIsClick = false;
+    }
+    // fall through
+  case NS_TOUCH_CANCEL:
+    mActiveElementManager->HandleTouchEnd(mEndTouchIsClick);
+    // fall through
+  case NS_TOUCH_MOVE: {
+    SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
+    break;
+  }
+
+  default:
+    NS_WARNING("Unknown touch event type");
+  }
+}
+
+void
+APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+                                 const ScrollableLayerGuid& aGuid,
+                                 uint64_t aInputBlockId)
+{
+  mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, aEvent.mFlags.mDefaultPrevented);
+}
+
+void
+APZEventState::ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
+                                     ViewID aViewId,
+                                     APZStateChange aChange,
+                                     int aArg)
+{
+  switch (aChange)
+  {
+  case APZStateChange::TransformBegin:
+  {
+    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+    nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+    if (scrollbarMediator) {
+      scrollbarMediator->ScrollbarActivityStarted();
+    }
+
+    if (aDocument) {
+      nsCOMPtr<nsIDocShell> docshell(aDocument->GetDocShell());
+      if (docshell && sf) {
+        nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+        nsdocshell->NotifyAsyncPanZoomStarted(sf->GetScrollPositionCSSPixels());
+      }
+    }
+    break;
+  }
+  case APZStateChange::TransformEnd:
+  {
+    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+    nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(sf);
+    if (scrollbarMediator) {
+      scrollbarMediator->ScrollbarActivityStopped();
+    }
+
+    if (aDocument) {
+      nsCOMPtr<nsIDocShell> docshell(aDocument->GetDocShell());
+      if (docshell && sf) {
+        nsDocShell* nsdocshell = static_cast<nsDocShell*>(docshell.get());
+        nsdocshell->NotifyAsyncPanZoomStopped(sf->GetScrollPositionCSSPixels());
+      }
+    }
+    break;
+  }
+  case APZStateChange::StartTouch:
+  {
+    mActiveElementManager->HandleTouchStart(aArg);
+    break;
+  }
+  case APZStateChange::StartPanning:
+  {
+    mActiveElementManager->HandlePanStart();
+    break;
+  }
+  case APZStateChange::EndTouch:
+  {
+    mEndTouchIsClick = aArg;
+    break;
+  }
+  default:
+    // APZStateChange has a 'sentinel' value, and the compiler complains
+    // if an enumerator is not handled and there is no 'default' case.
+    break;
+  }
+}
+
+void
+APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault,
+                                                 const ScrollableLayerGuid& aGuid)
+{
+  if (mPendingTouchPreventedResponse) {
+    MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
+    mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
+        mPendingTouchPreventedBlockId, aPreventDefault);
+    mPendingTouchPreventedResponse = false;
+  }
+}
+
+already_AddRefed<nsIWidget>
+APZEventState::GetWidget() const
+{
+  nsCOMPtr<nsIWidget> result = do_QueryReferent(mWidget);
+  return result.forget();
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZEventState_h
+#define mozilla_layers_APZEventState_h
+
+#include <stdint.h>
+
+#include "FrameMetrics.h"     // for ScrollableLayerGuid
+#include "Units.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/layers/GeckoContentController.h"  // for APZStateChange
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"  // for NS_INLINE_DECL_REFCOUNTING
+#include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
+#include "nsRefPtr.h"
+
+template <class> class nsCOMPtr;
+class nsIDOMWindowUtils;
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class ActiveElementManager;
+
+struct ContentReceivedInputBlockCallback {
+public:
+  NS_INLINE_DECL_REFCOUNTING(ContentReceivedInputBlockCallback);
+  virtual void Run(const ScrollableLayerGuid& aGuid,
+                   uint64_t aInputBlockId,
+                   bool aPreventDefault) const = 0;
+protected:
+  virtual ~ContentReceivedInputBlockCallback() {}
+};
+
+/**
+ * A content-side component that keeps track of state for handling APZ
+ * gestures and sending APZ notifications.
+ */
+class APZEventState {
+  typedef GeckoContentController::APZStateChange APZStateChange;
+  typedef FrameMetrics::ViewID ViewID;
+public:
+  APZEventState(nsIWidget* aWidget,
+                const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback);
+
+  NS_INLINE_DECL_REFCOUNTING(APZEventState);
+
+  void ProcessSingleTap(const CSSPoint& aPoint,
+                        const ScrollableLayerGuid& aGuid,
+                        float aPresShellResolution);
+  void ProcessLongTap(const nsCOMPtr<nsIDOMWindowUtils>& aUtils,
+                      const CSSPoint& aPoint,
+                      const ScrollableLayerGuid& aGuid,
+                      uint64_t aInputBlockId,
+                      float aPresShellResolution);
+  void ProcessLongTapUp(const CSSPoint& aPoint,
+                        const ScrollableLayerGuid& aGuid,
+                        float aPresShellResolution);
+  void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId);
+  void ProcessWheelEvent(const WidgetWheelEvent& aEvent,
+                         const ScrollableLayerGuid& aGuid,
+                         uint64_t aInputBlockId);
+  void ProcessAPZStateChange(const nsCOMPtr<nsIDocument>& aDocument,
+                             ViewID aViewId,
+                             APZStateChange aChange,
+                             int aArg);
+private:
+  ~APZEventState();
+  void SendPendingTouchPreventedResponse(bool aPreventDefault,
+                                         const ScrollableLayerGuid& aGuid);
+  already_AddRefed<nsIWidget> GetWidget() const;
+private:
+  nsWeakPtr mWidget;
+  nsRefPtr<ActiveElementManager> mActiveElementManager;
+  nsRefPtr<ContentReceivedInputBlockCallback> mContentReceivedInputBlockCallback;
+  bool mPendingTouchPreventedResponse;
+  ScrollableLayerGuid mPendingTouchPreventedGuid;
+  uint64_t mPendingTouchPreventedBlockId;
+  bool mEndTouchIsClick;
+  bool mTouchEndCancelled;
+};
+
+}
+}
+
+#endif /* mozilla_layers_APZEventState_h */
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -2,29 +2,34 @@
 /* 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 "ChromeProcessController.h"
 
 #include "MainThreadUtils.h"    // for NS_IsMainThread()
 #include "base/message_loop.h"  // for MessageLoop
+#include "mozilla/dom/Element.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZEventState.h"
 #include "nsIDocument.h"
+#include "nsIInterfaceRequestorUtils.h"
 #include "nsIPresShell.h"
 #include "nsLayoutUtils.h"
 #include "nsView.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
-ChromeProcessController::ChromeProcessController(nsIWidget* aWidget)
+ChromeProcessController::ChromeProcessController(nsIWidget* aWidget,
+                                                 APZEventState* aAPZEventState)
   : mWidget(aWidget)
+  , mAPZEventState(aAPZEventState)
   , mUILoop(MessageLoop::current())
 {
   // Otherwise we're initializing mUILoop incorrectly.
   MOZ_ASSERT(NS_IsMainThread());
 
   mUILoop->PostTask(
       FROM_HERE,
       NewRunnableMethod(this, &ChromeProcessController::InitializeRoot));
@@ -35,19 +40,17 @@ ChromeProcessController::InitializeRoot(
 {
   // Create a view-id and set a zero-margin displayport for the root element
   // of the root document in the chrome process. This ensures that the scroll
   // frame for this element gets an APZC, which in turn ensures that all content
   // in the chrome processes is covered by an APZC.
   // The displayport is zero-margin because this element is generally not
   // actually scrollable (if it is, APZC will set proper margins when it's
   // scrolled).
-  nsView* view = nsView::GetViewFor(mWidget);
-  MOZ_ASSERT(view);
-  nsIPresShell* presShell = view->GetPresShell();
+  nsIPresShell* presShell = GetPresShell();
   MOZ_ASSERT(presShell);
   MOZ_ASSERT(presShell->GetDocument());
   nsIContent* content = presShell->GetDocument()->GetDocumentElement();
   MOZ_ASSERT(content);
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
     nsLayoutUtils::SetDisplayPortMargins(content, presShell, ScreenMargin(), 0,
@@ -101,27 +104,91 @@ ChromeProcessController::Destroy()
 float
 ChromeProcessController::GetPresShellResolution() const
 {
   // The document in the chrome process cannot be zoomed, so its pres shell
   // resolution is 1.
   return 1.0f;
 }
 
+nsIPresShell*
+ChromeProcessController::GetPresShell() const
+{
+  nsView* view = nsView::GetViewFor(mWidget);
+  MOZ_ASSERT(view);
+  return view->GetPresShell();
+}
+
+already_AddRefed<nsIDOMWindowUtils>
+ChromeProcessController::GetDOMWindowUtils() const
+{
+  if (nsIDocument* doc = GetPresShell()->GetDocument()) {
+    nsCOMPtr<nsIDOMWindowUtils> result = do_GetInterface(doc->GetWindow());
+    return result.forget();
+  }
+  return nullptr;
+}
+
 void
 ChromeProcessController::HandleSingleTap(const CSSPoint& aPoint,
                                          int32_t aModifiers,
                                          const ScrollableLayerGuid& aGuid)
 {
   if (MessageLoop::current() != mUILoop) {
     mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this, &ChromeProcessController::HandleSingleTap,
                           aPoint, aModifiers, aGuid));
     return;
   }
 
-  LayoutDevicePoint point =
-      APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid, GetPresShellResolution())
-    * mWidget->GetDefaultScale();
+  mAPZEventState->ProcessSingleTap(aPoint, aGuid, GetPresShellResolution());
+}
+
+void
+ChromeProcessController::HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
+                                       const ScrollableLayerGuid& aGuid,
+                                       uint64_t aInputBlockId)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &ChromeProcessController::HandleLongTap,
+                          aPoint, aModifiers, aGuid, aInputBlockId));
+    return;
+  }
+
+  mAPZEventState->ProcessLongTap(GetDOMWindowUtils(), aPoint, aGuid,
+      aInputBlockId, GetPresShellResolution());
+}
 
-  APZCCallbackHelper::FireSingleTapEvent(point, mWidget);
+void
+ChromeProcessController::HandleLongTapUp(const CSSPoint& aPoint, int32_t aModifiers,
+                                         const ScrollableLayerGuid& aGuid)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &ChromeProcessController::HandleLongTapUp,
+                          aPoint, aModifiers, aGuid));
+    return;
+  }
+
+  mAPZEventState->ProcessLongTapUp(aPoint, aGuid, GetPresShellResolution());
 }
+
+void
+ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                              APZStateChange aChange,
+                                              int aArg)
+{
+  if (MessageLoop::current() != mUILoop) {
+    mUILoop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &ChromeProcessController::NotifyAPZStateChange,
+                          aGuid, aChange, aArg));
+    return;
+  }
+
+  mAPZEventState->ProcessAPZStateChange(GetPresShell()->GetDocument(), aGuid.mScrollId, aChange, aArg);
+}
+
+
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -3,59 +3,69 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_ChromeProcessController_h
 #define mozilla_layers_ChromeProcessController_h
 
 #include "mozilla/layers/GeckoContentController.h"
 #include "nsCOMPtr.h"
+#include "nsRefPtr.h"
 
+class nsIDOMWindowUtils;
+class nsIPresShell;
 class nsIWidget;
 
 class MessageLoop;
 
 namespace mozilla {
 
 namespace layers {
+
+class APZEventState;
 class CompositorParent;
 
 // A ChromeProcessController is attached to the root of a compositor's layer
 // tree.
 class ChromeProcessController : public mozilla::layers::GeckoContentController
 {
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
 
 public:
-  explicit ChromeProcessController(nsIWidget* aWidget);
+  explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState);
   virtual void Destroy() MOZ_OVERRIDE;
 
   // GeckoContentController interface
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
   virtual void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
   virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId,
                                        const uint32_t& aScrollGeneration) MOZ_OVERRIDE;
 
   virtual void HandleDoubleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
   virtual void HandleSingleTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
   virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, int32_t aModifiers,
                                const ScrollableLayerGuid& aGuid,
-                               uint64_t aInputBlockId) MOZ_OVERRIDE {}
+                               uint64_t aInputBlockId) MOZ_OVERRIDE;
   virtual void HandleLongTapUp(const CSSPoint& aPoint, int32_t aModifiers,
-                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE {}
+                               const ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
   virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect,
                                        const mozilla::CSSSize &aScrollableSize) MOZ_OVERRIDE {}
-
+  virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                    APZStateChange aChange,
+                                    int aArg) MOZ_OVERRIDE;
 private:
   nsCOMPtr<nsIWidget> mWidget;
+  nsRefPtr<APZEventState> mAPZEventState;
   MessageLoop* mUILoop;
 
   void InitializeRoot();
   float GetPresShellResolution() const;
+  nsIPresShell* GetPresShell() const;
+  already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils() const;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* mozilla_layers_ChromeProcessController_h */
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -856,17 +856,17 @@ bool
 BufferTextureClient::AllocateForYCbCr(gfx::IntSize aYSize,
                                       gfx::IntSize aCbCrSize,
                                       StereoMode aStereoMode)
 {
   MOZ_ASSERT(IsValid());
 
   size_t bufSize = YCbCrImageDataSerializer::ComputeMinBufferSize(aYSize,
                                                                   aCbCrSize);
-  if (!Allocate(bufSize)) {
+  if (!bufSize || !Allocate(bufSize)) {
     return false;
   }
   YCbCrImageDataSerializer serializer(GetBuffer(), GetBufferSize());
   serializer.InitializeBufferInfo(aYSize,
                                   aCbCrSize,
                                   aStereoMode);
   mSize = aYSize;
   return true;
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -87,16 +87,17 @@ EXPORTS.mozilla.layers += [
     'apz/public/GeckoContentController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZCTreeManager.h',
     'apz/src/APZUtils.h',
     'apz/testutil/APZTestData.h',
     'apz/util/ActiveElementManager.h',
     'apz/util/APZCCallbackHelper.h',
+    'apz/util/APZEventState.h',
     'apz/util/APZThreadUtils.h',
     'apz/util/ChromeProcessController.h',
     'apz/util/InputAPZContext.h',
     'AtomicRefCountedWithFinalize.h',
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
@@ -222,16 +223,17 @@ UNIFIED_SOURCES += [
     'apz/src/HitTestingTreeNode.cpp',
     'apz/src/InputBlockState.cpp',
     'apz/src/InputQueue.cpp',
     'apz/src/OverscrollHandoffState.cpp',
     'apz/src/TaskThrottler.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
+    'apz/util/APZEventState.cpp',
     'apz/util/APZThreadUtils.cpp',
     'apz/util/ChromeProcessController.cpp',
     'apz/util/InputAPZContext.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
@@ -350,16 +352,20 @@ IPDL_SOURCES = [
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+LOCAL_INCLUDES += [
+    '/docshell/base',  # for nsDocShell.h
+]
+
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_DEBUG']:
     DEFINES['D3D_DEBUG_INFO'] = True
 
 if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
     DEFINES['MOZ_ENABLE_D3D10_LAYER'] = True
 
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -683,19 +683,18 @@ public:
             }
             uint32_t timestamp = strtoul(beginning, nullptr, 10);
             beginning = end + 1;
             if (!(end = strchr(beginning, ';'))) {
                 break;
             }
             uint32_t filesize = strtoul(beginning, nullptr, 10);
 
-            FNCMapEntry* mapEntry =
-                static_cast<FNCMapEntry*>
-                (PL_DHashTableAdd(&mMap, filename.get()));
+            FNCMapEntry* mapEntry = static_cast<FNCMapEntry*>
+                (PL_DHashTableAdd(&mMap, filename.get(), fallible));
             if (mapEntry) {
                 mapEntry->mFilename.Assign(filename);
                 mapEntry->mTimestamp = timestamp;
                 mapEntry->mFilesize = filesize;
                 mapEntry->mFaces.Assign(faceList);
                 // entries from the startupcache are marked "non-existing"
                 // until we have confirmed that the file still exists
                 mapEntry->mFileExists = false;
@@ -732,19 +731,18 @@ public:
 
     virtual void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
         if (!mMap.IsInitialized()) {
             return;
         }
-        FNCMapEntry* entry =
-            static_cast<FNCMapEntry*>
-            (PL_DHashTableAdd(&mMap, aFileName.get()));
+        FNCMapEntry* entry = static_cast<FNCMapEntry*>
+            (PL_DHashTableAdd(&mMap, aFileName.get(), fallible));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
         }
         mWriteNeeded = true;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -5,20 +5,22 @@
 #include "BackgroundParentImpl.h"
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "nsNetUtil.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 
@@ -66,16 +68,17 @@ public:
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::BroadcastChannelParent;
+using mozilla::dom::ServiceWorkerRegistrationData;
 
 BackgroundParentImpl::BackgroundParentImpl()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
 }
@@ -348,16 +351,207 @@ BackgroundParentImpl::DeallocPBroadcastC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
+namespace {
+
+class RegisterServiceWorkerCallback MOZ_FINAL : public nsRunnable
+{
+public:
+  explicit RegisterServiceWorkerCallback(
+                                     const ServiceWorkerRegistrationData& aData)
+    : mData(aData)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    nsRefPtr<dom::ServiceWorkerRegistrar> service =
+      dom::ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->RegisterServiceWorker(mData);
+    return NS_OK;
+  }
+
+private:
+  ServiceWorkerRegistrationData mData;
+};
+
+class UnregisterServiceWorkerCallback MOZ_FINAL : public nsRunnable
+{
+public:
+  explicit UnregisterServiceWorkerCallback(const nsString& aScope)
+    : mScope(aScope)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    nsRefPtr<dom::ServiceWorkerRegistrar> service =
+      dom::ServiceWorkerRegistrar::Get();
+    MOZ_ASSERT(service);
+
+    service->UnregisterServiceWorker(NS_ConvertUTF16toUTF8(mScope));
+    return NS_OK;
+  }
+
+private:
+  nsString mScope;
+};
+
+class CheckPrincipalWithCallbackRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  CheckPrincipalWithCallbackRunnable(already_AddRefed<ContentParent> aParent,
+                                     const PrincipalInfo& aPrincipalInfo,
+                                     nsRunnable* aCallback)
+    : mContentParent(aParent)
+    , mPrincipalInfo(aPrincipalInfo)
+    , mCallback(aCallback)
+    , mBackgroundThread(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mCallback);
+    MOZ_ASSERT(mBackgroundThread);
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    if (NS_IsMainThread()) {
+      nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
+      AssertAppPrincipal(mContentParent, principal);
+      mContentParent = nullptr;
+
+      mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      return NS_OK;
+    }
+
+    AssertIsOnBackgroundThread();
+    mCallback->Run();
+    mCallback = nullptr;
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<ContentParent> mContentParent;
+  PrincipalInfo mPrincipalInfo;
+  nsRefPtr<nsRunnable> mCallback;
+  nsCOMPtr<nsIThread> mBackgroundThread;
+};
+
+} // anonymous namespace
+
+bool
+BackgroundParentImpl::RecvRegisterServiceWorker(
+                                     const ServiceWorkerRegistrationData& aData)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // Basic validation.
+  if (aData.scope().IsEmpty() ||
+      aData.scriptSpec().IsEmpty() ||
+      aData.principal().type() == PrincipalInfo::TNullPrincipalInfo) {
+    return false;
+  }
+
+  nsRefPtr<RegisterServiceWorkerCallback> callback =
+    new RegisterServiceWorkerCallback(aData);
+
+  nsRefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    callback->Run();
+    return true;
+  }
+
+  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
+    new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(),
+                                           callback);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+BackgroundParentImpl::RecvUnregisterServiceWorker(
+                                            const PrincipalInfo& aPrincipalInfo,
+                                            const nsString& aScope)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  // Basic validation.
+  if (aScope.IsEmpty() ||
+      aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
+    return false;
+  }
+
+  nsRefPtr<UnregisterServiceWorkerCallback> callback =
+    new UnregisterServiceWorkerCallback(aScope);
+
+  nsRefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    callback->Run();
+    return true;
+  }
+
+  nsRefPtr<CheckPrincipalWithCallbackRunnable> runnable =
+    new CheckPrincipalWithCallbackRunnable(parent.forget(), aPrincipalInfo,
+                                           callback);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  if (BackgroundParent::IsOtherProcessActor(this)) {
+    return false;
+  }
+
+  nsRefPtr<dom::ServiceWorkerRegistrar> service =
+    dom::ServiceWorkerRegistrar::Get();
+  MOZ_ASSERT(service);
+
+  service->Shutdown();
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -78,14 +78,25 @@ protected:
   virtual bool
   RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsString& origin,
                                    const nsString& channel) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
+                            MOZ_OVERRIDE;
+
+  virtual bool
+  RecvUnregisterServiceWorker(const PrincipalInfo& aPrincipalInfo,
+                              const nsString& aScope) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvShutdownServiceWorkerRegistrar() MOZ_OVERRIDE;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -843,16 +843,22 @@ GeckoChildProcessHost::PerformAsyncLaunc
   };
 
   if (shouldSandboxCurrentProcess) {
     for (auto it = mAllowedFilesRead.begin();
          it != mAllowedFilesRead.end();
          ++it) {
       mSandboxBroker.AllowReadFile(it->c_str());
     }
+
+    for (auto it = mAllowedFilesReadWrite.begin();
+         it != mAllowedFilesReadWrite.end();
+         ++it) {
+      mSandboxBroker.AllowReadWriteFile(it->c_str());
+    }
   }
 #endif // XP_WIN && MOZ_SANDBOX
 
   // Add the application directory path (-appdir path)
   AddAppDirToCommandLine(cmdLine);
 
   // XXX Command line params past this point are expected to be at
   // the end of the command line string, and in a specific order.
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -166,16 +166,17 @@ protected:
 
 #ifdef XP_WIN
   void InitWindowsGroupID();
   nsString mGroupId;
 
 #ifdef MOZ_SANDBOX
   SandboxBroker mSandboxBroker;
   std::vector<std::wstring> mAllowedFilesRead;
+  std::vector<std::wstring> mAllowedFilesReadWrite;
   bool mEnableSandboxLogging;
   int32_t mSandboxLevel;
   bool mMoreStrictSandbox;
 #endif
 #endif // XP_WIN
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -7,16 +7,17 @@ include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PFileDescriptorSet;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
+include ServiceWorkerRegistrarTypes;
 
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
@@ -30,16 +31,21 @@ parent:
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
 
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
 
+  RegisterServiceWorker(ServiceWorkerRegistrationData data);
+  UnregisterServiceWorker(PrincipalInfo principalInfo,
+                          nsString scope);
+  ShutdownServiceWorkerRegistrar();
+
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -174,36 +174,19 @@ WrapperAnswer::RecvDefineProperty(const 
     RootedId id(cx);
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!toDescriptor(cx, descriptor, &desc))
         return fail(cx, rs);
 
-    if (!js::CheckDefineProperty(cx, obj, id, desc.value(), desc.attributes(),
-                                 desc.getter(), desc.setter()))
-    {
+    bool ignored;
+    if (!js_DefineOwnProperty(cx, obj, id, desc, &ignored))
         return fail(cx, rs);
-    }
-
-    if (!JS_DefinePropertyById(cx, obj, id, desc.value(),
-                               // Descrriptors never store JSNatives for
-                               // accessors: they have either JSFunctions or
-                               // JSPropertyOps.
-                               desc.attributes() | JSPROP_PROPOP_ACCESSORS,
-                               JS_PROPERTYOP_GETTER(desc.getter()
-                                                    ? desc.getter()
-                                                    : JS_PropertyStub),
-                               JS_PROPERTYOP_SETTER(desc.setter()
-                                                    ? desc.setter()
-                                                    : JS_StrictPropertyStub)))
-    {
-        return fail(cx, rs);
-    }
 
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvDelete(const ObjectId &objId, const JSIDVariant &idVar, ReturnStatus *rs,
                           bool *success)
 {
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -172,18 +172,16 @@ typedef bool
                   JS::MutableHandleValue vp);
 typedef bool
 (* SetPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                   JS::MutableHandleValue vp, bool strict);
 typedef bool
 (* GetOwnPropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                      JS::MutableHandle<JSPropertyDescriptor> desc);
 typedef bool
-(* SetAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
-typedef bool
 (* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
 
 typedef bool
 (* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
 
 typedef bool
 (* UnwatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
@@ -341,39 +339,38 @@ struct ClassExtension
 
 struct ObjectOps
 {
     LookupPropertyOp    lookupProperty;
     DefinePropertyOp    defineProperty;
     GetPropertyOp       getProperty;
     SetPropertyOp       setProperty;
     GetOwnPropertyOp    getOwnPropertyDescriptor;
-    SetAttributesOp     setAttributes;
     DeletePropertyOp    deleteProperty;
     WatchOp             watch;
     UnwatchOp           unwatch;
     GetElementsOp       getElements;
     JSNewEnumerateOp    enumerate;
     ObjectOp            thisObject;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
     {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
-     nullptr, nullptr, nullptr, nullptr}
+     nullptr, nullptr, nullptr}
 
 } // namespace js
 
 // Classes, objects, and properties.
 
 typedef void (*JSClassInternal)();
 
 struct JSClass {
     JS_CLASS_MEMBERS(JSFinalizeOp);
 
-    void                *reserved[24];
+    void                *reserved[23];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports *)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
 #define JSCLASS_IMPLEMENTS_BARRIERS     (1<<5)  // Correctly implements GC read
                                                 // and write barriers
 #define JSCLASS_EMULATES_UNDEFINED      (1<<6)  // objects of this class act
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -958,17 +958,17 @@ const Class AsmJSModuleObject::class_ = 
     nullptr, /* hasInstance */
     nullptr, /* construct */
     AsmJSModuleObject_trace
 };
 
 AsmJSModuleObject *
 AsmJSModuleObject::create(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *module)
 {
-    JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr, nullptr);
+    JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr, JS::NullPtr());
     if (!obj)
         return nullptr;
     AsmJSModuleObject *nobj = &obj->as<AsmJSModuleObject>();
 
     nobj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget()));
     return nobj;
 }
 
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -434,17 +434,17 @@ typedef int32_t
 
 typedef const char *
 (* GetAvailable)(int32_t localeIndex);
 
 static bool
 intl_availableLocales(JSContext *cx, CountAvailable countAvailable,
                       GetAvailable getAvailable, MutableHandleValue result)
 {
-    RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, nullptr));
+    RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, js::NullPtr()));
     if (!locales)
         return false;
 
 #if ENABLE_INTL_API
     uint32_t count = countAvailable();
     RootedValue t(cx, BooleanValue(true));
     for (uint32_t i = 0; i < count; i++) {
         const char *locale = getAvailable(i);
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -285,17 +285,18 @@ CreateSimdClass(JSContext *cx, Handle<Gl
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
     if (!funcProto)
         return nullptr;
 
     // Create type constructor itself and initialize its reserved slots.
 
     Rooted<SimdTypeDescr*> typeDescr(cx);
-    typeDescr = NewObjectWithProto<SimdTypeDescr>(cx, funcProto, global, SingletonObject);
+    typeDescr = NewObjectWithProto<SimdTypeDescr>(cx, funcProto, GlobalObject::upcast(global),
+                                                  SingletonObject);
     if (!typeDescr)
         return nullptr;
 
     typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
@@ -306,17 +307,17 @@ CreateSimdClass(JSContext *cx, Handle<Gl
         return nullptr;
 
     // Create prototype property, which inherits from Object.prototype.
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
         return nullptr;
     Rooted<TypedProto*> proto(cx);
-    proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, SingletonObject);
+    proto = NewObjectWithProto<TypedProto>(cx, objProto, NullPtr(), SingletonObject);
     if (!proto)
         return nullptr;
     typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
 
     // Link constructor to prototype and install properties.
 
     if (!JS_DefineFunctions(cx, typeDescr, T::TypeDescriptorMethods))
         return nullptr;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -484,17 +484,17 @@ static TypedProto *
 CreatePrototypeObjectForComplexTypeInstance(JSContext *cx, HandleObject ctorPrototype)
 {
     RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
     if (!ctorPrototypePrototype)
         return nullptr;
 
     return NewObjectWithProto<TypedProto>(cx,
                                           &*ctorPrototypePrototype,
-                                          nullptr,
+                                          NullPtr(),
                                           TenuredObject);
 }
 
 const Class ArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -581,17 +581,17 @@ ArrayTypeDescr *
 ArrayMetaTypeDescr::create(JSContext *cx,
                            HandleObject arrayTypePrototype,
                            HandleTypeDescr elementType,
                            HandleAtom stringRepr,
                            int32_t size,
                            int32_t length)
 {
     Rooted<ArrayTypeDescr*> obj(cx);
-    obj = NewObjectWithProto<ArrayTypeDescr>(cx, arrayTypePrototype, nullptr, SingletonObject);
+    obj = NewObjectWithProto<ArrayTypeDescr>(cx, arrayTypePrototype, NullPtr(), SingletonObject);
     if (!obj)
         return nullptr;
 
     obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(ArrayTypeDescr::Kind));
     obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
     obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
     obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
@@ -773,21 +773,21 @@ StructMetaTypeDescr::create(JSContext *c
     AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
     AutoValueVector fieldOffsets(cx);  // Offset of each field field.
     RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
     RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
     CheckedInt32 sizeSoFar(0);         // Size of struct thus far.
     int32_t alignment = 1;             // Alignment of struct.
     bool opaque = false;               // Opacity of struct.
 
-    userFieldOffsets = NewObjectWithProto<PlainObject>(cx, nullptr, nullptr, TenuredObject);
+    userFieldOffsets = NewObjectWithProto<PlainObject>(cx, nullptr, NullPtr(), TenuredObject);
     if (!userFieldOffsets)
         return nullptr;
 
-    userFieldTypes = NewObjectWithProto<PlainObject>(cx, nullptr, nullptr, TenuredObject);
+    userFieldTypes = NewObjectWithProto<PlainObject>(cx, nullptr, NullPtr(), TenuredObject);
     if (!userFieldTypes)
         return nullptr;
 
     if (!stringBuffer.append("new StructType({")) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
@@ -907,17 +907,17 @@ StructMetaTypeDescr::create(JSContext *c
     }
 
     // Now create the resulting type descriptor.
     RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
     if (!structTypePrototype)
         return nullptr;
 
     Rooted<StructTypeDescr*> descr(cx);
-    descr = NewObjectWithProto<StructTypeDescr>(cx, structTypePrototype, nullptr,
+    descr = NewObjectWithProto<StructTypeDescr>(cx, structTypePrototype, NullPtr(),
                                                 SingletonObject);
     if (!descr)
         return nullptr;
 
     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Struct));
     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(alignment));
     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
@@ -1149,17 +1149,17 @@ DefineSimpleTypeDescr(JSContext *cx,
     if (!objProto)
         return false;
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
     if (!funcProto)
         return false;
 
     Rooted<T*> descr(cx);
-    descr = NewObjectWithProto<T>(cx, funcProto, global, SingletonObject);
+    descr = NewObjectWithProto<T>(cx, funcProto, GlobalObject::upcast(global), SingletonObject);
     if (!descr)
         return false;
 
     descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
     descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
     descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type)));
     descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(T::size(type)));
     descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
@@ -1169,17 +1169,17 @@ DefineSimpleTypeDescr(JSContext *cx,
         return false;
 
     if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
         return false;
 
     // Create the typed prototype for the scalar type. This winds up
     // not being user accessible, but we still create one for consistency.
     Rooted<TypedProto*> proto(cx);
-    proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, TenuredObject);
+    proto = NewObjectWithProto<TypedProto>(cx, objProto, NullPtr(), TenuredObject);
     if (!proto)
         return false;
     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
 
     RootedValue descrValue(cx, ObjectValue(*descr));
     if (!DefineProperty(cx, module, className, descrValue, nullptr, nullptr, 0))
         return false;
 
@@ -1204,29 +1204,30 @@ DefineMetaTypeDescr(JSContext *cx,
         return nullptr;
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
     if (!funcProto)
         return nullptr;
 
     // Create ctor.prototype, which inherits from Function.__proto__
 
-    RootedObject proto(cx, NewObjectWithProto<PlainObject>(cx, funcProto, global,
+    RootedObject proto(cx, NewObjectWithProto<PlainObject>(cx, funcProto,
+                                                           GlobalObject::upcast(global),
                                                            SingletonObject));
     if (!proto)
         return nullptr;
 
     // Create ctor.prototype.prototype, which inherits from Object.__proto__
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
         return nullptr;
     RootedObject protoProto(cx);
     protoProto = NewObjectWithProto<PlainObject>(cx, objProto,
-                                                 global, SingletonObject);
+                                                 GlobalObject::upcast(global), SingletonObject);
     if (!protoProto)
         return nullptr;
 
     RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
     if (!DefineProperty(cx, proto, cx->names().prototype, protoProtoValue,
                         nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
@@ -1263,17 +1264,18 @@ DefineMetaTypeDescr(JSContext *cx,
 bool
 GlobalObject::initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global)
 {
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
         return false;
 
     Rooted<TypedObjectModuleObject*> module(cx);
-    module = NewObjectWithProto<TypedObjectModuleObject>(cx, objProto, global);
+    module = NewObjectWithProto<TypedObjectModuleObject>(cx, objProto,
+                                                         GlobalObject::upcast(global));
     if (!module)
         return false;
 
     if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
         return false;
 
     // uint8, uint16, any, etc
 
@@ -2036,32 +2038,16 @@ IsOwnId(JSContext *cx, HandleObject obj,
         if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index))
             return true;
     }
 
     return false;
 }
 
 bool
-TypedObject::obj_setPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id,
-                                       unsigned *attrsp)
-{
-    if (IsOwnId(cx, obj, id))
-        return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
-
-    RootedObject proto(cx, obj->getProto());
-    if (!proto) {
-        *attrsp = 0;
-        return true;
-    }
-
-    return SetPropertyAttributes(cx, proto, id, attrsp);
-}
-
-bool
 TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
@@ -2326,17 +2312,16 @@ LazyArrayBufferTable::sizeOfIncludingThi
         JS_NULL_CLASS_SPEC,                              \
         JS_NULL_CLASS_EXT,                               \
         {                                                \
             TypedObject::obj_lookupProperty,             \
             TypedObject::obj_defineProperty,             \
             TypedObject::obj_getProperty,                \
             TypedObject::obj_setProperty,                \
             TypedObject::obj_getOwnPropertyDescriptor,   \
-            TypedObject::obj_setPropertyAttributes,      \
             TypedObject::obj_deleteProperty,             \
             nullptr, nullptr, /* watch/unwatch */        \
             nullptr,   /* getElements */                 \
             TypedObject::obj_enumerate,                  \
             nullptr, /* thisObject */                    \
         }                                                \
     }
 
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -545,19 +545,16 @@ class TypedObject : public JSObject
                                 MutableHandleValue vp, bool strict);
 
     static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                                MutableHandleValue vp, bool strict);
 
     static bool obj_getOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                                              MutableHandle<JSPropertyDescriptor> desc);
 
-    static bool obj_setPropertyAttributes(JSContext *cx, HandleObject obj,
-                                          HandleId id, unsigned *attrsp);
-
     static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
 
     static bool obj_enumerate(JSContext *cx, HandleObject obj, AutoIdVector &properties);
 
   public:
     TypedProto &typedProto() const {
         return getProto()->as<TypedProto>();
     }
--- a/js/src/ctypes/libffi/configure
+++ b/js/src/ctypes/libffi/configure
@@ -17221,17 +17221,17 @@ case "$host" in
 	TARGET=POWERPC; TARGETDIR=powerpc
 	;;
   powerpc-*-darwin* | powerpc64-*-darwin*)
 	TARGET=POWERPC_DARWIN; TARGETDIR=powerpc
 	;;
   powerpc-*-aix* | rs6000-*-aix*)
 	TARGET=POWERPC_AIX; TARGETDIR=powerpc
 	;;
-  powerpc-*-freebsd* | powerpc-*-openbsd*)
+  powerpc-*-freebsd* | powerpc-*-openbsd* | powerpc-*-netbsd*)
 	TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
 	HAVE_LONG_DOUBLE_VARIANT=1
 	;;
   powerpc64-*-freebsd*)
 	TARGET=POWERPC; TARGETDIR=powerpc
 	;;
   powerpc*-*-rtems*)
 	TARGET=POWERPC; TARGETDIR=powerpc
--- a/js/src/ctypes/libffi/configure.ac
+++ b/js/src/ctypes/libffi/configure.ac
@@ -241,17 +241,17 @@ case "$host" in
 	TARGET=POWERPC; TARGETDIR=powerpc
 	;;
   powerpc-*-darwin* | powerpc64-*-darwin*)
 	TARGET=POWERPC_DARWIN; TARGETDIR=powerpc
 	;;
   powerpc-*-aix* | rs6000-*-aix*)
 	TARGET=POWERPC_AIX; TARGETDIR=powerpc
 	;;
-  powerpc-*-freebsd* | powerpc-*-openbsd*)
+  powerpc-*-freebsd* | powerpc-*-openbsd* | powerpc-*-netbsd*)
 	TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
 	HAVE_LONG_DOUBLE_VARIANT=1
 	;;
   powerpc64-*-freebsd*)
 	TARGET=POWERPC; TARGETDIR=powerpc
 	;;
   powerpc*-*-rtems*)
 	TARGET=POWERPC; TARGETDIR=powerpc
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -505,22 +505,16 @@ Parser<ParseHandler>::Parser(ExclusiveCo
     ss(nullptr),
     keepAtoms(cx->perThreadData),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
-    sawDeprecatedForEach(false),
-    sawDeprecatedDestructuringForIn(false),
-    sawDeprecatedLegacyGenerator(false),
-    sawDeprecatedExpressionClosure(false),
-    sawDeprecatedLetBlock(false),
-    sawDeprecatedLetExpression(false),
     handler(cx, *alloc, tokenStream, syntaxParser, lazyOuterFunction)
 {
     {
         AutoLockForExclusiveAccess lock(cx);
         cx->perThreadData->addActiveCompilation();
     }
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
@@ -546,18 +540,16 @@ Parser<ParseHandler>::checkOptions()
     return true;
 }
 
 template <typename ParseHandler>
 Parser<ParseHandler>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 
-    accumulateTelemetry();
-
     alloc.release(tempPoolMark);
 
     /*
      * The parser can allocate enormous amounts of memory for large functions.
      * Eagerly free the memory now (which otherwise won't be freed until the
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
@@ -2539,17 +2531,17 @@ Parser<ParseHandler>::functionArgsAndBod
     if (tt != TOK_LC) {
         if (funbox->isStarGenerator()) {
             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow) {
 #if JS_HAS_EXPR_CLOSURES
-            sawDeprecatedExpressionClosure = true;
+            addTelemetry(JSCompartment::DeprecatedExpressionClosure);
 #else
             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
         tokenStream.ungetToken();
         bodyType = ExpressionBody;
@@ -3685,26 +3677,26 @@ Parser<ParseHandler>::deprecatedLetBlock
 
     Node expr;
     if (letContext == LetStatement) {
         expr = statements();
         if (!expr)
             return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
 
-        sawDeprecatedLetBlock = true;
+        addTelemetry(JSCompartment::DeprecatedLetBlock);
         if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_BLOCK))
             return null();
     } else {
         MOZ_ASSERT(letContext == LetExpression);
         expr = assignExpr();
         if (!expr)
             return null();
 
-        sawDeprecatedLetExpression = true;
+        addTelemetry(JSCompartment::DeprecatedLetExpression);
         if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION))
             return null();
     }
     handler.setLexicalScopeBody(block, expr);
     PopStatementPC(tokenStream, pc);
 
     TokenPos letPos(begin, pos().end);
 
@@ -4616,17 +4608,17 @@ Parser<FullParseHandler>::forStatement()
 
     if (allowsForEachIn()) {
         bool matched;
         if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
             return null();
         if (matched) {
             iflags = JSITER_FOREACH;
             isForEach = true;
-            sawDeprecatedForEach = true;
+            addTelemetry(JSCompartment::DeprecatedForEach);
             if (versionNumber() < JSVERSION_LATEST) {
                 if (!report(ParseWarning, pc->sc->strict, null(), JSMSG_DEPRECATED_FOR_EACH))
                     return null();
             }
         }
     }
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
@@ -4851,17 +4843,17 @@ Parser<FullParseHandler>::forStatement()
           case PNK_OBJECT:
             if (versionNumber() == JSVERSION_1_7) {
                 /*
                  * Destructuring for-in requires [key, value] enumeration
                  * in JS1.7.
                  */
                 if (!isForEach && headKind == PNK_FORIN) {
                     iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
-                    sawDeprecatedDestructuringForIn = true;
+                    addTelemetry(JSCompartment::DeprecatedDestructuringForIn);
                 }
             }
             break;
 
           default:;
         }
     } else {
         if (isForEach) {
@@ -5417,17 +5409,17 @@ Parser<ParseHandler>::yieldExpression()
             return null();
 
         if (!pc->sc->isFunctionBox()) {
             report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
             return null();
         }
 
         pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
-        sawDeprecatedLegacyGenerator = true;
+        addTelemetry(JSCompartment::DeprecatedLegacyGenerator);
 
         if (pc->funHasReturnExpr) {
             /* As in Python (see PEP-255), disallow return v; in generators. */
             reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
                             JSMSG_BAD_ANON_GENERATOR_RETURN);
             return null();
         }
         // Fall through.
@@ -6814,17 +6806,17 @@ Parser<FullParseHandler>::legacyComprehe
 
         pn2->pn_iflags = JSITER_ENUMERATE;
         if (allowsForEachIn()) {
             bool matched;
             if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
                 return null();
             if (matched) {
                 pn2->pn_iflags |= JSITER_FOREACH;
-                sawDeprecatedForEach = true;
+                addTelemetry(JSCompartment::DeprecatedForEach);
                 if (versionNumber() < JSVERSION_LATEST) {
                     if (!report(ParseWarning, pc->sc->strict, pn2, JSMSG_DEPRECATED_FOR_EACH))
                         return null();
                 }
             }
         }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
@@ -8379,57 +8371,21 @@ Parser<ParseHandler>::exprInParens()
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
     return pn;
 }
 
 template <typename ParseHandler>
 void
-Parser<ParseHandler>::accumulateTelemetry()
+Parser<ParseHandler>::addTelemetry(JSCompartment::DeprecatedLanguageExtension e)
 {
     JSContext* cx = context->maybeJSContext();
     if (!cx)
         return;
-    const char* filename = getFilename();
-    if (!filename)
-        return;
-
-    bool isAddon = !!cx->compartment()->addonId;
-    bool isHTTP = strncmp(filename, "http://", 7) == 0 || strncmp(filename, "https://", 8) == 0;
-
-    // Only report telemetry for web content, not add-ons or chrome JS.
-    if (isAddon || !isHTTP)
-        return;
-
-    enum DeprecatedLanguageExtensions {
-        DeprecatedForEach = 0,            // JS 1.6+
-        DeprecatedDestructuringForIn = 1, // JS 1.7 only
-        DeprecatedLegacyGenerator = 2,    // JS 1.7+
-        DeprecatedExpressionClosure = 3,  // Added in JS 1.8, but not version-gated
-        DeprecatedLetBlock = 4,           // Added in JS 1.7, but not version-gated
-        DeprecatedLetExpression = 5,      // Added in JS 1.7, but not version-gated
-    };
-
-    // Hazard analysis can't tell that the telemetry callbacks don't GC.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    // Call back into Firefox's Telemetry reporter.
-    int id = JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
-    if (sawDeprecatedForEach)
-         cx->runtime()->addTelemetry(id, DeprecatedForEach);
-    if (sawDeprecatedDestructuringForIn)
-         cx->runtime()->addTelemetry(id, DeprecatedDestructuringForIn);
-    if (sawDeprecatedLegacyGenerator)
-        cx->runtime()->addTelemetry(id, DeprecatedLegacyGenerator);
-    if (sawDeprecatedExpressionClosure)
-         cx->runtime()->addTelemetry(id, DeprecatedExpressionClosure);
-    if (sawDeprecatedLetBlock)
-         cx->runtime()->addTelemetry(id, DeprecatedLetBlock);
-    if (sawDeprecatedLetExpression)
-         cx->runtime()->addTelemetry(id, DeprecatedLetExpression);
+    cx->compartment()->addTelemetry(e);
 }
 
 template class Parser<FullParseHandler>;
 template class Parser<SyntaxParseHandler>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -367,24 +367,16 @@ class Parser : private JS::AutoGCRooter,
      * is not known whether the parse succeeds or fails, this bit is set and
      * the parse will return false.
      */
     bool abortedSyntaxParse:1;
 
     /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
     bool isUnexpectedEOF_:1;
 
-    /* Used for collecting telemetry on SpiderMonkey's deprecated language extensions. */
-    bool sawDeprecatedForEach:1;
-    bool sawDeprecatedDestructuringForIn:1;
-    bool sawDeprecatedLegacyGenerator:1;
-    bool sawDeprecatedExpressionClosure:1;
-    bool sawDeprecatedLetBlock:1;
-    bool sawDeprecatedLetExpression:1;
-
     typedef typename ParseHandler::Node Node;
     typedef typename ParseHandler::DefinitionNode DefinitionNode;
 
   public:
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
 
   private:
@@ -699,17 +691,17 @@ class Parser : private JS::AutoGCRooter,
 
     bool leaveFunction(Node fn, ParseContext<ParseHandler> *outerpc,
                        FunctionSyntaxKind kind = Expression);
 
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
     bool asmJS(Node list);
 
-    void accumulateTelemetry();
+    void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     friend class LegacyCompExprTransplanter;
     friend struct BindData<ParseHandler>;
 };
 
 /* Declare some required template specializations. */
 
 template <>
--- a/js/src/jit-test/lib/census.js
+++ b/js/src/jit-test/lib/census.js
@@ -101,22 +101,22 @@ const Census = {};
           enter: expectedLeaf,
           done: expectedLeaf,
           check: elt => compare(elt, basis)
         };
       }
     };
   }
 
-  function missingProp() {
-    throw "Census mismatch: subject lacks property present in basis";
+  function missingProp(prop) {
+    throw "Census mismatch: subject lacks property present in basis: " + uneval(prop);
   }
 
-  function extraProp() {
-    throw "Census mismatch: subject has property not present in basis";
+  function extraProp(prop) {
+    throw "Census mismatch: subject has property not present in basis: " + uneval(prop);
   }
 
   // Return a walker that checks that the subject census has counts all equal to
   // |basis|.
   Census.assertAllEqual = makeBasisChecker({
     compare: assertEq,
     missing: missingProp,
     extra: extraProp
@@ -125,9 +125,27 @@ const Census = {};
   // Return a walker that checks that the subject census has at least as many
   // items of each category as |basis|.
   Census.assertAllNotLessThan = makeBasisChecker({
     compare: (subject, basis) => assertEq(subject >= basis, true),
     missing: missingProp,
     extra: () => Census.walkAnything
   });
 
+  // Return a walker that checks that the subject census has at most as many
+  // items of each category as |basis|.
+  Census.assertAllNotMoreThan = makeBasisChecker({
+    compare: (subject, basis) => assertEq(subject <= basis, true),
+    missing: missingProp,
+    extra: () => Census.walkAnything
+  });
+
+  // Return a walker that checks that the subject census has within |fudge|
+  // items of each category of the count in |basis|.
+  Census.assertAllWithin = function (fudge, basis) {
+    return makeBasisChecker({
+      compare: (subject, basis) => assertEq(Math.abs(subject - basis) <= fudge, true),
+      missing: missingProp,
+      extra: () => Census.walkAnything
+    })(basis);
+  }
+
 })();
--- a/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js
+++ b/js/src/jit-test/tests/basic/typed-array-sealed-frozen.js
@@ -21,17 +21,17 @@ for (constructor of constructors) {
   assertEq(Object.isExtensible(a), true);
   assertEq(Object.isSealed(a), false);
   assertEq(Object.isFrozen(a), false);
 
   // Should not throw.
   Object.seal(a);
 
   // Should complain that it can't change attributes of indexed typed array properties.
-  assertThrowsInstanceOf(() => Object.freeze(a), InternalError);
+  assertThrowsInstanceOf(() => Object.freeze(a), TypeError);
 }
 
 print();
 
 for (constructor of constructors) {
   print("testing zero-length " + constructor.name);
   let a = new constructor(0);
   assertEq(Object.isExtensible(a), true);
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-02.js
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-02.js
@@ -1,70 +1,49 @@
-// Debugger.Memory.prototype.takeCensus behaves plausibly as we allocate and drop objects.
+// Debugger.Memory.prototype.takeCensus behaves plausibly as we allocate objects.
+
+// Exact object counts vary in ways we can't predict. For example,
+// BaselineScripts can hold onto "template objects", which exist only to hold
+// the shape and type for newly created objects. When BaselineScripts are
+// discarded, these template objects go with them.
+//
+// So instead of expecting precise counts, we expect counts that are at least as
+// many as we would expect given the object graph we've built.
 
 load(libdir + 'census.js');
 
+// A Debugger with no debuggees had better not find anything.
 var dbg = new Debugger;
 var census0 = dbg.memory.takeCensus();
 Census.walkCensus(census0, "census0", Census.assertAllZeros);
 
-var g1 = newGlobal();
-g1.eval('var a = [];');
-g1.eval('function add(f) { a.push({}); a.push(f ? (() => undefined) : null); }');
-g1.eval('function remove() { a.pop(); a.pop(); }');
-g1.add();
-g1.remove();
-
-// Adding a global shouldn't cause any counts to *decrease*.
-dbg.addDebuggee(g1);
-var census1 = dbg.memory.takeCensus();
-Census.walkCensus(census1, "census1", Census.assertAllNotLessThan(census0));
-
-function pointCheck(label, lhs, rhs, objComp, funComp) {
-  print(label);
-  try {
-    assertEq(objComp(lhs.objects.Object.count, rhs.objects.Object.count), true);
-    assertEq(funComp(lhs.objects.Function.count, rhs.objects.Function.count), true);
-  } catch (ex) {
-    print("pointCheck failed: " + ex);
-    print("lhs: " + JSON.stringify(lhs, undefined, 2));
-    print("rhs: " + JSON.stringify(rhs, undefined, 2));
-
-    // Do it again, and put the result where Mozilla's continuous integration
-    // code will find it.
-    var upload_dir = os.getenv("MOZ_UPLOAD_DIR") || ".";
-    redirect(upload_dir + "/Memory-takeCensus-02.txt");
-    print("pointCheck failed: " + ex);
-    print("lhs: " + JSON.stringify(lhs, undefined, 2));
-    print("rhs: " + JSON.stringify(rhs, undefined, 2));
-
-    throw ex;
-  }
+function newGlobalWithDefs() {
+  var g = newGlobal();
+  g.eval(`
+         function times(n, fn) {
+           var a=[];
+           for (var i = 0; i<n; i++)
+             a.push(fn());
+           return a;
+         }`);
+  return g;
 }
 
-function eq(lhs, rhs) { return lhs === rhs; }
-function lt(lhs, rhs) { return lhs < rhs; }
-function gt(lhs, rhs) { return lhs > rhs; }
+// Allocate a large number of various types of objects, and check that census
+// finds them.
+var g = newGlobalWithDefs();
+dbg.addDebuggee(g);
 
-// As we increase the number of reachable objects, the census should
-// reflect that.
-g1.add(false);
-var census2 = dbg.memory.takeCensus();