Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 02 Nov 2015 15:12:16 -0800
changeset 304934 c5c5874b08f3bc150536123d86050db2913200eb
parent 304882 6275cd9c71b76891f6b6585dabc687bc443ab877 (current diff)
parent 304933 469fc8555c437105e4bdeea57e1645525bd83cdc (diff)
child 304935 9f69202d82752e093a653a8f15b0274e347db33a
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
dom/media/gmp/rlz/base/string16.h
dom/workers/ServiceWorkerPeriodicUpdater.cpp
dom/workers/ServiceWorkerPeriodicUpdater.h
dom/workers/test/serviceworkers/periodic.sjs
dom/workers/test/serviceworkers/periodic/frame.html
dom/workers/test/serviceworkers/periodic/register.html
dom/workers/test/serviceworkers/periodic/unregister.html
dom/workers/test/serviceworkers/periodic/wait_for_update.html
dom/workers/test/serviceworkers/periodic_update_test.js
dom/workers/test/serviceworkers/test_periodic_https_update.html
dom/workers/test/serviceworkers/test_periodic_update.html
--- a/b2g/components/AboutServiceWorkers.jsm
+++ b/b2g/components/AboutServiceWorkers.jsm
@@ -104,19 +104,19 @@ this.AboutServiceWorkers = {
         if (!data) {
           self.sendError(message.id, "NoServiceWorkersRegistrations");
           return;
         }
 
         let registrations = [];
 
         for (let i = 0; i < data.length; i++) {
-          let info = data.queryElementAt(i, Ci.nsIServiceWorkerInfo);
+          let info = data.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
           if (!info) {
-            dump("AboutServiceWorkers: Invalid nsIServiceWorkerInfo " +
+            dump("AboutServiceWorkers: Invalid nsIServiceWorkerRegistrationInfo " +
                  "interface.\n");
             continue;
           }
           registrations.push(serializeServiceWorkerInfo(info));
         }
 
         self.sendResult(message.id, {
           enabled: self.enabled,
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -314,36 +314,16 @@
            noautofocus="true"/>
 
     <panel id="loop-panel"
            class="loop-panel social-panel"
            type="arrow"
            orient="horizontal"
            hidden="true"/>
 
-    <menupopup id="processHangOptions"
-               onpopupshowing="ProcessHangMonitor.refreshMenu(window);">
-      <menuitem id="processHangTerminateScript"
-                oncommand="ProcessHangMonitor.terminateScript(window)"
-                accesskey="&processHang.terminateScript.accessKey;"
-                label="&processHang.terminateScript.label;"/>
-      <menuitem id="processHangDebugScript"
-                oncommand="ProcessHangMonitor.debugScript(window)"
-                accesskey="&processHang.debugScript.accessKey;"
-                label="&processHang.debugScript.label;"/>
-      <menuitem id="processHangTerminatePlugin"
-                oncommand="ProcessHangMonitor.terminatePlugin(window)"
-                accesskey="&processHang.terminatePlugin.accessKey;"
-                label="&processHang.terminatePlugin.label;"/>
-      <menuitem id="processHangTerminateProcess"
-                oncommand="ProcessHangMonitor.terminateProcess(window)"
-                accesskey="&processHang.terminateProcess.accessKey;"
-                label="&processHang.terminateProcess.label;"/>
-    </menupopup>
-
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
       <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
                 accesskey="&customizeMenu.moveToPanel.accesskey;"
                 label="&customizeMenu.moveToPanel.label;"
                 contexttype="toolbaritem"
                 class="customize-context-moveToPanel"/>
       <menuitem oncommand="gCustomizeMode.removeFromArea(document.popupNode)"
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -918,25 +918,16 @@ you can use these alternative items. Oth
 
 <!ENTITY panicButton.view.undoWarning             "This action cannot be undone.">
 <!ENTITY panicButton.view.forgetButton            "Forget!">
 
 <!ENTITY panicButton.thankyou.msg1                "Your recent history is cleared.">
 <!ENTITY panicButton.thankyou.msg2                "Safe browsing!">
 <!ENTITY panicButton.thankyou.buttonlabel         "Thanks!">
 
-<!ENTITY processHang.terminateScript.label        "Stop Script">
-<!ENTITY processHang.terminateScript.accessKey    "S">
-<!ENTITY processHang.debugScript.label            "Debug Script">
-<!ENTITY processHang.debugScript.accessKey        "D">
-<!ENTITY processHang.terminatePlugin.label        "Kill Plugin">
-<!ENTITY processHang.terminatePlugin.accessKey    "P">
-<!ENTITY processHang.terminateProcess.label       "Kill Web Process">
-<!ENTITY processHang.terminateProcess.accessKey   "K">
-
 <!ENTITY emeLearnMoreContextMenu.label            "Learn more about DRM…">
 <!ENTITY emeLearnMoreContextMenu.accesskey        "D">
 <!ENTITY emeNotificationsNotNow.label             "Not now">
 <!ENTITY emeNotificationsNotNow.accesskey         "N">
 <!ENTITY emeNotificationsDontAskAgain.label       "Don't ask me again">
 <!ENTITY emeNotificationsDontAskAgain.accesskey   "D">
 
 <!-- LOCALIZATION NOTE (saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label): Pocket is a brand name -->
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -473,19 +473,23 @@ syncPromoNotification.addons.description
 syncPromoNotification.addons-sync-disabled.description=You can use your %S account to synchronize add-ons across multiple devices.\u0020
 
 # Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
 dataReportingNotification.message       = %1$S automatically sends some data to %2$S so that we can improve your experience.
 dataReportingNotification.button.label  = Choose What I Share
 dataReportingNotification.button.accessKey  = C
 
 # Process hang reporter
-processHang.message = A web page is causing %1$S to run slowly. What would you like to do?
-processHang.button.label = Options
-processHang.button.accessKey = O
+processHang.label = A web page is slowing down your browser. What would you like to do?
+processHang.button_stop.label = Stop It
+processHang.button_stop.accessKey = S
+processHang.button_wait.label = Wait
+processHang.button_wait.accessKey = W
+processHang.button_debug.label = Debug Script
+processHang.button_debug.accessKey = D
 
 # Webapps notification popup
 webapps.install = Install
 webapps.install.accesskey = I
 #LOCALIZATION NOTE (webapps.requestInstall2) %S is the web app name
 webapps.requestInstall2 = Do you want to install “%S” from this site?
 webapps.install.success = Application Installed
 webapps.install.inprogress = Installation in progress
--- a/browser/modules/ProcessHangMonitor.jsm
+++ b/browser/modules/ProcessHangMonitor.jsm
@@ -14,32 +14,56 @@ this.EXPORTED_SYMBOLS = ["ProcessHangMon
 Cu.import("resource://gre/modules/Services.jsm");
 
 /**
  * This JSM is responsible for observing content process hang reports
  * and asking the user what to do about them. See nsIHangReport for
  * the platform interface.
  */
 
-/**
- * If a hang hasn't been reported for more than 10 seconds, assume the
- * content process has gotten unstuck (and hide the hang notification).
- */
-const HANG_EXPIRATION_TIME = 10000;
+var ProcessHangMonitor = {
+  /**
+   * If a hang hasn't been reported for more than 10 seconds, assume the
+   * content process has gotten unstuck (and hide the hang notification).
+   */
+  get HANG_EXPIRATION_TIME() {
+    try {
+      return Services.prefs.getIntPref("browser.hangNotification.expiration");
+    } catch (ex) {
+      return 10000;
+    }
+  },
 
-var ProcessHangMonitor = {
+  /**
+   * This timeout is the wait period applied after a user selects "Wait" in
+   * an existing notification.
+   */
+  get WAIT_EXPIRATION_TIME() {
+    try {
+      return Services.prefs.getIntPref("browser.hangNotification.waitPeriod");
+    } catch (ex) {
+      return 10000;
+    }
+  },
+
   /**
    * Collection of hang reports that haven't expired or been dismissed
    * by the user. The keys are nsIHangReports and values keys are
    * timers. Each time the hang is reported, the timer is refreshed so
    * it expires after HANG_EXPIRATION_TIME.
    */
   _activeReports: new Map(),
 
   /**
+   * Collection of hang reports that have been suppressed for a
+   * short period of time.
+   */
+  _pausedReports: new Map(),
+
+  /**
    * Initialize hang reporting. Called once in the parent process.
    */
   init: function() {
     Services.obs.addObserver(this, "process-hang-report", false);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.ww.registerNotification(this);
   },
 
@@ -65,69 +89,101 @@ var ProcessHangMonitor = {
 
       let svc = Cc["@mozilla.org/dom/slow-script-debug;1"].getService(Ci.nsISlowScriptDebug);
       let handler = svc.remoteActivationHandler;
       handler.handleSlowScriptDebug(report.scriptBrowser, callback);
     });
   },
 
   /**
-   * Kill the plugin process causing the hang being reported for the
-   * selected browser in |win|.
+   * Terminate the plugin process associated with a hang being reported
+   * for the selected browser in |win|. Will attempt to generate a combined
+   * crash report for all processes.
    */
   terminatePlugin: function(win) {
     this.handleUserInput(win, report => report.terminatePlugin());
   },
 
   /**
-   * Kill the content process causing the hang being reported for the selected
-   * browser in |win|.
+   * Dismiss the browser notification and invoke an appropriate action based on
+   * the hang type.
    */
-  terminateProcess: function(win) {
-    this.handleUserInput(win, report => report.terminateProcess());
-  },
-
-  /**
-   * Update the "Options" pop-up menu for the hang notification
-   * associated with the selected browser in |win|. The menu should
-   * display only options that are relevant to the given report.
-   */
-  refreshMenu: function(win) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+  stopIt: function (win) {
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
     if (!report) {
       return;
     }
 
-    function setVisible(id, visible) {
-      let item = win.document.getElementById(id);
-      item.hidden = !visible;
+    switch (report.hangType) {
+      case report.SLOW_SCRIPT:
+        this.terminateScript(win);
+        break;
+      case report.PLUGIN_HANG:
+        this.terminatePlugin(win);
+        break;
     }
+  },
+
+  /**
+   * Dismiss the notification, clear the report from the active list and set up
+   * a new timer to track a wait period during which we won't notify.
+   */
+  waitLonger: function(win) {
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
+    if (!report) {
+      return;
+    }
+    // Remove the report from the active list and cancel its timer.
+    this.removeActiveReport(report);
+
+    // NOTE, we didn't call userCanceled on nsIHangReport here. This insures
+    // we don't repeatedly generate and cache crash report data for this hang
+    // in the process hang reporter. It already has one report for the browser
+    // process we want it hold onto.
 
-    if (report.hangType == report.SLOW_SCRIPT) {
-      setVisible("processHangTerminateScript", true);
-      setVisible("processHangDebugScript", true);
-      setVisible("processHangTerminatePlugin", false);
-    } else if (report.hangType == report.PLUGIN_HANG) {
-      setVisible("processHangTerminateScript", false);
-      setVisible("processHangDebugScript", false);
-      setVisible("processHangTerminatePlugin", true);
-    }
+    // Create a new wait timer with notify callback
+    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    timer.initWithCallback(() => {
+      for (let [stashedReport, otherTimer] of this._pausedReports) {
+        if (otherTimer === timer) {
+          this.removePausedReport(stashedReport);
+
+          // Create a new notification display timeout timer
+          let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+          timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+
+          // Store the timer in the active reports map. If we receive a new
+          // observer notification for this hang, we'll redisplay the browser
+          // notification in reportHang below. If we do not receive a new
+          // observer, timer will take care fo cleaning up resources associated
+          // with this hang. The observer for active hangs fires about once
+          // a second.
+          this._activeReports.set(report, timer);
+          break;
+        }
+      }
+    }, this.WAIT_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+
+    this._pausedReports.set(report, timer);
+
+    // remove the browser notification associated with this hang
+    this.updateWindows();
   },
 
   /**
    * If there is a hang report associated with the selected browser in
    * |win|, invoke |func| on that report and stop notifying the user
    * about it.
    */
   handleUserInput: function(win, func) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
     if (!report) {
       return;
     }
-    this.removeReport(report);
+    this.removeActiveReport(report);
 
     return func(report);
   },
 
   observe: function(subject, topic, data) {
     switch (topic) {
       case "xpcom-shutdown":
         Services.obs.removeObserver(this, "xpcom-shutdown");
@@ -148,29 +204,67 @@ var ProcessHangMonitor = {
           this.updateWindows();
         };
         win.addEventListener("load", listener, true);
         break;
     }
   },
 
   /**
-   * Find any active hang reports for the given <browser> element.
+   * Find a active hang report for the given <browser> element.
    */
-  findReport: function(browser) {
+  findActiveReport: function(browser) {
     let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
     for (let [report, timer] of this._activeReports) {
       if (report.isReportForBrowser(frameLoader)) {
         return report;
       }
     }
     return null;
   },
 
   /**
+   * Find a paused hang report for the given <browser> element.
+   */
+  findPausedReport: function(browser) {
+    let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+    for (let [report, timer] of this._pausedReports) {
+      if (report.isReportForBrowser(frameLoader)) {
+        return report;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Remove an active hang report from the active list and cancel the timer
+   * associated with it.
+   */
+  removeActiveReport: function(report) {
+    let timer = this._activeReports.get(report);
+    if (timer) {
+      timer.cancel();
+    }
+    this._activeReports.delete(report);
+    this.updateWindows();
+  },
+
+  /**
+   * Remove a paused hang report from the paused list and cancel the timer
+   * associated with it.
+   */
+  removePausedReport: function(report) {
+    let timer = this._pausedReports.get(report);
+    if (timer) {
+      timer.cancel();
+    }
+    this._pausedReports.delete(report);
+  },
+
+  /**
    * Iterate over all XUL windows and ensure that the proper hang
    * reports are shown for each one. Also install event handlers in
    * each window to watch for events that would cause a different hang
    * report to be displayed.
    */
   updateWindows: function() {
     let e = Services.wm.getEnumerator("navigator:browser");
     while (e.hasMoreElements()) {
@@ -186,17 +280,17 @@ var ProcessHangMonitor = {
       }
     }
   },
 
   /**
    * If there is a hang report for the current tab in |win|, display it.
    */
   updateWindow: function(win) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
 
     if (report) {
       this.showNotification(win, report);
     } else {
       this.hideNotification(win);
     }
   },
 
@@ -207,29 +301,46 @@ var ProcessHangMonitor = {
     let nb = win.document.getElementById("high-priority-global-notificationbox");
     let notification = nb.getNotificationWithValue("process-hang");
     if (notification) {
       return;
     }
 
     let bundle = win.gNavigatorBundle;
     let brandBundle = win.document.getElementById("bundle_brand");
-    let appName = brandBundle.getString("brandShortName");
-    let message = bundle.getFormattedString(
-      "processHang.message",
-      [appName]);
 
     let buttons = [{
-      label: bundle.getString("processHang.button.label"),
-      accessKey: bundle.getString("processHang.button.accessKey"),
-      popup: "processHangOptions",
-      callback: null,
-    }];
+        label: bundle.getString("processHang.button_stop.label"),
+        accessKey: bundle.getString("processHang.button_stop.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.stopIt(win);
+        }
+      },
+      {
+        label: bundle.getString("processHang.button_wait.label"),
+        accessKey: bundle.getString("processHang.button_wait.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.waitLonger(win);
+        }
+      }];
 
-    nb.appendNotification(message, "process-hang",
+#ifdef MOZ_DEV_EDITION
+    if (report.hangType == report.SLOW_SCRIPT) {
+      buttons.push({
+        label: bundle.getString("processHang.button_debug.label"),
+        accessKey: bundle.getString("processHang.button_debug.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.debugScript(win);
+        }
+      });
+    }
+#endif
+
+    nb.appendNotification(bundle.getString("processHang.label"),
+                          "process-hang",
                           "chrome://browser/content/aboutRobots-icon.png",
                           nb.PRIORITY_WARNING_HIGH, buttons);
   },
 
   /**
    * Ensure that no hang notifications are visible in |win|.
    */
   hideNotification: function(win) {
@@ -265,57 +376,56 @@ var ProcessHangMonitor = {
     }
   },
 
   /**
    * Handle a potentially new hang report. If it hasn't been seen
    * before, show a notification for it in all open XUL windows.
    */
   reportHang: function(report) {
-    // If this hang was already reported, then reset the timer for it.
+    // If this hang was already reported reset the timer for it.
     if (this._activeReports.has(report)) {
       let timer = this._activeReports.get(report);
       timer.cancel();
-      timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+      timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+      // if this report is in active but doesn't have a notification associated
+      // with it, display a notification.
+      this.updateWindows();
+      return;
+    }
+
+    // If this hang was already reported and paused by the user ignore it.
+    if (this._pausedReports.has(report)) {
       return;
     }
 
     // On e10s this counts slow-script/hanged-plugin notice only once.
     // This code is not reached on non-e10s.
     if (report.hangType == report.SLOW_SCRIPT) {
       // On non-e10s, SLOW_SCRIPT_NOTICE_COUNT is probed at nsGlobalWindow.cpp
       Services.telemetry.getHistogramById("SLOW_SCRIPT_NOTICE_COUNT").add();
     } else if (report.hangType == report.PLUGIN_HANG) {
       // On non-e10s we have sufficient plugin telemetry probes,
       // so PLUGIN_HANG_NOTICE_COUNT is only probed on e10s.
       Services.telemetry.getHistogramById("PLUGIN_HANG_NOTICE_COUNT").add();
     }
 
     // Otherwise create a new timer and display the report.
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+    timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
 
     this._activeReports.set(report, timer);
     this.updateWindows();
   },
 
   /**
-   * Dismiss a hang report because the user closed the notification
-   * for it or the report expired.
-   */
-  removeReport: function(report) {
-    this._activeReports.delete(report);
-    this.updateWindows();
-  },
-
-  /**
    * Callback for when HANG_EXPIRATION_TIME has elapsed.
    */
   notify: function(timer) {
     for (let [otherReport, otherTimer] of this._activeReports) {
       if (otherTimer === timer) {
-        this.removeReport(otherReport);
+        this.removeActiveReport(otherReport);
         otherReport.userCanceled();
         break;
       }
     }
   },
 };
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -28,29 +28,32 @@ EXTRA_JS_MODULES += [
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'HiddenFrame.jsm',
     'NetworkPrioritizer.jsm',
     'offlineAppCache.jsm',
     'PanelFrame.jsm',
     'PluginContent.jsm',
-    'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'TransientPrefs.jsm',
     'WebappManager.jsm',
     'webrtcUI.jsm',
 ]
 
+EXTRA_PP_JS_MODULES += [
+    'ProcessHangMonitor.jsm'
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 if CONFIG['NIGHTLY_BUILD']:
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_BrowserUITelemetry_buckets.js]
+[browser_ProcessHangNotifications.js]
+skip-if = !e10s
 [browser_ContentSearch.js]
 skip-if = e10s
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
 [browser_NetworkPrioritizer.js]
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser_ProcessHangNotifications.js
@@ -0,0 +1,185 @@
+
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+
+function getNotificationBox(aWindow) {
+  return aWindow.document.getElementById("high-priority-global-notificationbox");
+}
+
+function promiseNotificationShown(aWindow, aName) {
+  return new Promise((resolve) => {
+    let notification = getNotificationBox(aWindow);
+    notification.addEventListener("AlertActive", function active() {
+      notification.removeEventListener("AlertActive", active, true);
+      is(notification.allNotifications.length, 1, "Notification Displayed.");
+      resolve(notification);
+    });
+  });
+}
+
+function promiseReportCallMade(aValue) {
+  return new Promise((resolve) => {
+    let old = gTestHangReport.testCallback;
+    gTestHangReport.testCallback = function (val) {
+      gTestHangReport.testCallback = old;
+      is(aValue, val, "was the correct method call made on the hang report object?");
+      resolve();
+    };
+  });
+}
+
+function pushPrefs(...aPrefs) {
+  return new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+    resolve();
+  });
+}
+
+function popPrefs() {
+  return new Promise((resolve) => {
+    SpecialPowers.popPrefEnv(resolve);
+    resolve();
+  });
+}
+
+let gTestHangReport = {
+  SLOW_SCRIPT: 1,
+  PLUGIN_HANG: 2,
+
+  TEST_CALLBACK_CANCELED: 1,
+  TEST_CALLBACK_TERMSCRIPT: 2,
+  TEST_CALLBACK_TERMPLUGIN: 3,
+
+  _hangType: 1,
+  _tcb: function (aCallbackType) {},
+
+  get hangType() {
+    return this._hangType;
+  },
+
+  set hangType(aValue) {
+    this._hangType = aValue;
+  },
+
+  set testCallback(aValue) {
+    this._tcb = aValue;
+  },
+
+  QueryInterface: function (aIID) {
+    if (aIID.equals(Components.interfaces.nsIHangReport) ||
+        aIID.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_NOINTERFACE;
+  },
+
+  userCanceled: function () {
+    this._tcb(this.TEST_CALLBACK_CANCELED);
+  },
+
+  terminateScript: function () {
+    this._tcb(this.TEST_CALLBACK_TERMSCRIPT);
+  },
+
+  terminatePlugin: function () {
+    this._tcb(this.TEST_CALLBACK_TERMPLUGIN);
+  },
+
+  isReportForBrowser: function(aFrameLoader) {
+    return true;
+  }
+};
+
+// on dev edition we add a button for js debugging of hung scripts.
+let buttonCount = (UpdateUtils.UpdateChannel == "aurora" ? 3 : 2);
+
+/**
+ * Test if hang reports receive a terminate script callback when the user selects
+ * stop in response to a script hang.
+ */
+
+add_task(function* terminateScriptTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  // Click the "Stop It" button, we should get a terminate script callback
+  gTestHangReport.hangType = gTestHangReport.SLOW_SCRIPT;
+  promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMSCRIPT);
+  buttons[0].click();
+  yield promise;
+});
+
+/**
+ * Test if hang reports receive user canceled callbacks after a user selects wait
+ * and the browser frees up from a script hang on its own.
+ */
+
+add_task(function* waitForScriptTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  yield pushPrefs(["browser.hangNotification.waitPeriod", 1000],
+                  ["browser.hangNotification.expiration", 2000]);
+
+  function nocbcheck() {
+    ok(false, "received a callback?");
+  }
+  let oldcb = gTestHangReport.testCallback;
+  gTestHangReport.testCallback = nocbcheck;
+  // Click the "Wait" button this time, we shouldn't get a callback at all.
+  buttons[1].click();
+  gTestHangReport.testCallback = oldcb;
+
+  // send another hang pulse, we should not get a notification here
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  is(notification.currentNotification, null, "no notification should be visible");
+
+  // After selecting Wait, we should get a userCanceled callback after
+  // HANG_EXPIRATION_TIME.
+  yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
+
+  yield popPrefs();
+});
+
+/**
+ * Test if hang reports receive user canceled callbacks after the content
+ * process stops sending hang notifications.
+ */
+
+add_task(function* hangGoesAwayTest() {
+  yield pushPrefs(["browser.hangNotification.expiration", 1000]);
+
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  yield promise;
+
+  yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
+
+  yield popPrefs();
+});
+
+/**
+ * Tests if hang reports receive a terminate plugin callback when the user selects
+ * stop in response to a plugin hang.
+ */
+
+add_task(function* terminatePluginTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  // Click the "Stop It" button, we should get a terminate script callback
+  gTestHangReport.hangType = gTestHangReport.PLUGIN_HANG;
+  promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMPLUGIN);
+  buttons[0].click();
+  yield promise;
+});
--- a/dom/activities/ActivityRequestHandler.js
+++ b/dom/activities/ActivityRequestHandler.js
@@ -31,34 +31,37 @@ function ActivityRequestHandler() {
   // ActivityRequestHandler object.
 }
 
 ActivityRequestHandler.prototype = {
   init: function arh_init(aWindow) {
     this._window = aWindow;
   },
 
-  __init: function arh___init(aId, aOptions) {
+  __init: function arh___init(aId, aOptions, aReturnValue) {
     this._id = aId;
     this._options = aOptions;
+    this._returnValue = aReturnValue;
   },
 
   get source() {
     // We need to clone this object because the this._options.data has
     // the type any in WebIDL which will cause the binding layer to pass
     // the value which is a COW unmodified to content.
     return Cu.cloneInto(this._options, this._window);
   },
 
   postResult: function arh_postResult(aResult) {
-    cpmm.sendAsyncMessage("Activity:PostResult", {
-      "id": this._id,
-      "result": aResult
-    });
-    Services.obs.notifyObservers(null, "activity-success", this._id);
+    if (this._returnValue) {
+      cpmm.sendAsyncMessage("Activity:PostResult", {
+        "id": this._id,
+        "result": aResult
+      });
+      Services.obs.notifyObservers(null, "activity-success", this._id);
+    }
   },
 
   postError: function arh_postError(aError) {
     cpmm.sendAsyncMessage("Activity:PostError", {
       "id": this._id,
       "error": aError
     });
     Services.obs.notifyObservers(null, "activity-error", this._id);
--- a/dom/activities/ActivityWrapper.js
+++ b/dom/activities/ActivityWrapper.js
@@ -32,17 +32,20 @@ ActivityWrapper.prototype = {
     debug("Wrapping " + JSON.stringify(aMessage));
 
     // This message is useful to communicate that the activity message has been
     // properly received by the app. If the app will be killed, the
     // ActivitiesService will be able to fire an error and complete the
     // Activity workflow.
     cpmm.sendAsyncMessage("Activity:Ready", { id: aMessage.id });
 
-    let handler = new aWindow.ActivityRequestHandler(aMessage.id, aMessage.payload);
+    // Gecko should ignore |postResult| calls for WebActivities with no returnValue
+    // We need to pass returnValue to ActivityRequestHandler constructor to then properly
+    // decide if should call postResult or not
+    let handler = new aWindow.ActivityRequestHandler(aMessage.id, aMessage.payload, aMessage.target.returnValue);
 
     // When the activity window is closed, fire an error to notify the activity
     // caller of the situation.
     // We don't need to check whether the activity itself already sent
     // back something since ActivitiesService.jsm takes care of that.
     let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindowUtils);
     let innerWindowID = util.currentInnerWindowID;
--- a/dom/animation/AnimationEffectReadOnly.h
+++ b/dom/animation/AnimationEffectReadOnly.h
@@ -1,43 +1,47 @@
 /* -*- 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/. */
 
-#ifndef mozilla_dom_AnimationEffect_h
-#define mozilla_dom_AnimationEffect_h
+#ifndef mozilla_dom_AnimationEffectReadOnly_h
+#define mozilla_dom_AnimationEffectReadOnly_h
 
-#include "nsISupports.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 
-class AnimationEffectReadOnly
-  : public nsISupports
-  , public nsWrapperCache
+struct ComputedTimingProperties;
+
+class AnimationEffectReadOnly : public nsISupports,
+                                public nsWrapperCache
 {
-protected:
-  virtual ~AnimationEffectReadOnly() { }
-
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadOnly)
 
   explicit AnimationEffectReadOnly(nsISupports* aParent)
     : mParent(aParent)
   {
   }
 
   nsISupports* GetParentObject() const { return mParent; }
 
+  virtual void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
+  {
+  }
+
+protected:
+  virtual ~AnimationEffectReadOnly() = default;
+
 protected:
   nsCOMPtr<nsISupports> mParent;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_AnimationEffect_h
+#endif // mozilla_dom_AnimationEffectReadOnly_h
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1,28 +1,96 @@
 /* -*- 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 "mozilla/dom/KeyframeEffect.h"
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/dom/PropertyIndexedKeyframesBinding.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "AnimationCommon.h"
 #include "nsCSSParser.h"
 #include "nsCSSPropertySet.h"
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 #include "nsCSSValue.h"
 #include "nsStyleUtil.h"
+#include <algorithm>    // std::max
 
 namespace mozilla {
 
+// Helper functions for generating a ComputedTimingProperties dictionary
+static dom::FillMode
+ConvertFillMode(uint8_t aFill)
+{
+  switch (aFill) {
+    case NS_STYLE_ANIMATION_FILL_MODE_NONE:
+      return dom::FillMode::None;
+    case NS_STYLE_ANIMATION_FILL_MODE_FORWARDS:
+      return dom::FillMode::Forwards;
+    case NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS:
+      return dom::FillMode::Backwards;
+    case NS_STYLE_ANIMATION_FILL_MODE_BOTH:
+      return dom::FillMode::Both;
+    default:
+      MOZ_ASSERT(false, "The mapping of FillMode is not correct");
+      return dom::FillMode::None;
+  }
+}
+
+static dom::PlaybackDirection
+ConvertPlaybackDirection(uint8_t aDirection)
+{
+  switch (aDirection) {
+    case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
+      return dom::PlaybackDirection::Normal;
+    case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
+      return dom::PlaybackDirection::Reverse;
+    case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
+      return dom::PlaybackDirection::Alternate;
+    case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
+      return dom::PlaybackDirection::Alternate_reverse;
+    default:
+      MOZ_ASSERT(false, "The mapping of PlaybackDirection is not correct");
+      return dom::PlaybackDirection::Normal;
+  }
+}
+
+static void
+GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
+                            const Nullable<TimeDuration>& aLocalTime,
+                            const AnimationTiming& aTiming,
+                            dom::ComputedTimingProperties& aRetVal)
+{
+  // AnimationEffectTimingProperties
+  aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
+  aRetVal.mFill = ConvertFillMode(aTiming.mFillMode);
+  aRetVal.mIterations = aTiming.mIterationCount;
+  aRetVal.mDuration.SetAsUnrestrictedDouble() = aTiming.mIterationDuration.ToMilliseconds();
+  aRetVal.mDirection = ConvertPlaybackDirection(aTiming.mDirection);
+
+  // ComputedTimingProperties
+  aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
+  aRetVal.mEndTime
+    = std::max(aRetVal.mDelay + aRetVal.mActiveDuration + aRetVal.mEndDelay, 0.0);
+  aRetVal.mLocalTime = dom::AnimationUtils::TimeDurationToDouble(aLocalTime);
+  aRetVal.mProgress = aComputedTiming.mProgress;
+  if (!aRetVal.mProgress.IsNull()) {
+    // Convert the returned currentIteration into Infinity if we set
+    // (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
+    double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
+                     ? PositiveInfinity<double>()
+                     : static_cast<double>(aComputedTiming.mCurrentIteration);
+    aRetVal.mCurrentIteration.SetValue(iteration);
+  }
+}
+
 void
 ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
 {
   mType = aFunction.mType;
   if (nsTimingFunction::IsSplineType(mType)) {
     mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
                          aFunction.mFunc.mX2, aFunction.mFunc.mY2);
   } else {
@@ -100,19 +168,16 @@ ComputedTimingFunction::AppendToString(n
                                              aResult);
       break;
     default:
       nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult);
       break;
   }
 }
 
-// In the Web Animations model, the iteration progress can be outside the range
-// [0.0, 1.0] but it shouldn't be Infinity.
-const double ComputedTiming::kNullProgress = PositiveInfinity<double>();
 
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
                                    AnimationEffectReadOnly,
                                    mTarget,
                                    mAnimation)
 
@@ -166,16 +231,26 @@ KeyframeEffectReadOnly::GetLocalTime() c
   // time is equal to the parent time.
   Nullable<TimeDuration> result;
   if (mAnimation) {
     result = mAnimation->GetCurrentTime();
   }
   return result;
 }
 
+void
+KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
+{
+  const Nullable<TimeDuration> currentTime = GetLocalTime();
+  GetComputedTimingDictionary(GetComputedTimingAt(currentTime, mTiming),
+                              currentTime,
+                              mTiming,
+                              aRetVal);
+}
+
 ComputedTiming
 KeyframeEffectReadOnly::GetComputedTimingAt(
                           const Nullable<TimeDuration>& aLocalTime,
                           const AnimationTiming& aTiming)
 {
   const TimeDuration zeroDuration;
 
   // Currently we expect negative durations to be picked up during CSS
@@ -201,86 +276,88 @@ KeyframeEffectReadOnly::GetComputedTimin
   // When we finish exactly at the end of an iteration we need to report
   // the end of the final iteration and not the start of the next iteration
   // so we set up a flag for that case.
   bool isEndOfFinalIteration = false;
 
   // Get the normalized time within the active interval.
   StickyTimeDuration activeTime;
   if (localTime >= aTiming.mDelay + result.mActiveDuration) {
-    result.mPhase = ComputedTiming::AnimationPhase_After;
+    result.mPhase = ComputedTiming::AnimationPhase::After;
     if (!aTiming.FillsForwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress = ComputedTiming::kNullProgress;
+      result.mProgress.SetNull();
       return result;
     }
     activeTime = result.mActiveDuration;
     // Note that infinity == floor(infinity) so this will also be true when we
     // have finished an infinitely repeating animation of zero duration.
     isEndOfFinalIteration =
       aTiming.mIterationCount != 0.0 &&
       aTiming.mIterationCount == floor(aTiming.mIterationCount);
   } else if (localTime < aTiming.mDelay) {
-    result.mPhase = ComputedTiming::AnimationPhase_Before;
+    result.mPhase = ComputedTiming::AnimationPhase::Before;
     if (!aTiming.FillsBackwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress = ComputedTiming::kNullProgress;
+      result.mProgress.SetNull();
       return result;
     }
     // activeTime is zero
   } else {
     MOZ_ASSERT(result.mActiveDuration != zeroDuration,
                "How can we be in the middle of a zero-duration interval?");
-    result.mPhase = ComputedTiming::AnimationPhase_Active;
+    result.mPhase = ComputedTiming::AnimationPhase::Active;
     activeTime = localTime - aTiming.mDelay;
   }
 
   // Get the position within the current iteration.
   StickyTimeDuration iterationTime;
   if (aTiming.mIterationDuration != zeroDuration) {
     iterationTime = isEndOfFinalIteration
                     ? StickyTimeDuration(aTiming.mIterationDuration)
                     : activeTime % aTiming.mIterationDuration;
   } /* else, iterationTime is zero */
 
   // Determine the 0-based index of the current iteration.
   if (isEndOfFinalIteration) {
     result.mCurrentIteration =
       aTiming.mIterationCount == NS_IEEEPositiveInfinity()
-      ? UINT64_MAX // FIXME: When we return this via the API we'll need
-                   // to make sure it ends up being infinity.
+      ? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this
+                   // into Infinity.
       : static_cast<uint64_t>(aTiming.mIterationCount) - 1;
   } else if (activeTime == zeroDuration) {
     // If the active time is zero we're either in the first iteration
     // (including filling backwards) or we have finished an animation with an
     // iteration duration of zero that is filling forwards (but we're not at
     // the exact end of an iteration since we deal with that above).
     result.mCurrentIteration =
-      result.mPhase == ComputedTiming::AnimationPhase_After
+      result.mPhase == ComputedTiming::AnimationPhase::After
       ? static_cast<uint64_t>(aTiming.mIterationCount) // floor
       : 0;
   } else {
     result.mCurrentIteration =
       static_cast<uint64_t>(activeTime / aTiming.mIterationDuration); // floor
   }
 
   // Normalize the iteration time into a fraction of the iteration duration.
-  if (result.mPhase == ComputedTiming::AnimationPhase_Before) {
-    result.mProgress = 0.0;
-  } else if (result.mPhase == ComputedTiming::AnimationPhase_After) {
-    result.mProgress = isEndOfFinalIteration
-                       ? 1.0
-                       : fmod(aTiming.mIterationCount, 1.0f);
+  if (result.mPhase == ComputedTiming::AnimationPhase::Before) {
+    result.mProgress.SetValue(0.0);
+  } else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
+    double progress = isEndOfFinalIteration
+                      ? 1.0
+                      : fmod(aTiming.mIterationCount, 1.0f);
+    result.mProgress.SetValue(progress);
   } else {
     // We are in the active phase so the iteration duration can't be zero.
     MOZ_ASSERT(aTiming.mIterationDuration != zeroDuration,
                "In the active phase of a zero-duration animation?");
-    result.mProgress = aTiming.mIterationDuration == TimeDuration::Forever()
-                       ? 0.0
-                       : iterationTime / aTiming.mIterationDuration;
+    double progress = aTiming.mIterationDuration == TimeDuration::Forever()
+                      ? 0.0
+                      : iterationTime / aTiming.mIterationDuration;
+    result.mProgress.SetValue(progress);
   }
 
   bool thisIterationReverse = false;
   switch (aTiming.mDirection) {
     case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
       thisIterationReverse = false;
       break;
     case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
@@ -289,17 +366,17 @@ KeyframeEffectReadOnly::GetComputedTimin
     case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
       thisIterationReverse = (result.mCurrentIteration & 1) == 1;
       break;
     case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
       thisIterationReverse = (result.mCurrentIteration & 1) == 0;
       break;
   }
   if (thisIterationReverse) {
-    result.mProgress = 1.0 - result.mProgress;
+    result.mProgress.SetValue(1.0 - result.mProgress.Value());
   }
 
   return result;
 }
 
 StickyTimeDuration
 KeyframeEffectReadOnly::ActiveDuration(const AnimationTiming& aTiming)
 {
@@ -319,38 +396,38 @@ KeyframeEffectReadOnly::ActiveDuration(c
 // https://w3c.github.io/web-animations/#in-play
 bool
 KeyframeEffectReadOnly::IsInPlay() const
 {
   if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
     return false;
   }
 
-  return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase_Active;
+  return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active;
 }
 
 // https://w3c.github.io/web-animations/#current
 bool
 KeyframeEffectReadOnly::IsCurrent() const
 {
   if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
     return false;
   }
 
   ComputedTiming computedTiming = GetComputedTiming();
-  return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
-         computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
+  return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
+         computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
 }
 
 // https://w3c.github.io/web-animations/#in-effect
 bool
 KeyframeEffectReadOnly::IsInEffect() const
 {
   ComputedTiming computedTiming = GetComputedTiming();
-  return computedTiming.mProgress != ComputedTiming::kNullProgress;
+  return !computedTiming.mProgress.IsNull();
 }
 
 void
 KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
 {
   mAnimation = aAnimation;
 }
 
@@ -386,22 +463,23 @@ KeyframeEffectReadOnly::HasAnimationOfPr
 void
 KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
                                      nsCSSPropertySet& aSetProperties)
 {
   ComputedTiming computedTiming = GetComputedTiming();
 
   // If the progress is null, we don't have fill data for the current
   // time so we shouldn't animate.
-  if (computedTiming.mProgress == ComputedTiming::kNullProgress) {
+  if (computedTiming.mProgress.IsNull()) {
     return;
   }
 
-  MOZ_ASSERT(0.0 <= computedTiming.mProgress &&
-             computedTiming.mProgress <= 1.0,
+  MOZ_ASSERT(!computedTiming.mProgress.IsNull() &&
+             0.0 <= computedTiming.mProgress.Value() &&
+             computedTiming.mProgress.Value() <= 1.0,
              "iteration progress should be in [0-1]");
 
   for (size_t propIdx = 0, propEnd = mProperties.Length();
        propIdx != propEnd; ++propIdx)
   {
     const AnimationProperty& prop = mProperties[propIdx];
 
     MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key");
@@ -429,17 +507,17 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
     aSetProperties.AddProperty(prop.mProperty);
 
     MOZ_ASSERT(prop.mSegments.Length() > 0,
                "property should not be in animations if it has no segments");
 
     // FIXME: Maybe cache the current segment?
     const AnimationPropertySegment *segment = prop.mSegments.Elements(),
                                 *segmentEnd = segment + prop.mSegments.Length();
-    while (segment->mToKey < computedTiming.mProgress) {
+    while (segment->mToKey < computedTiming.mProgress.Value()) {
       MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
       ++segment;
       if (segment == segmentEnd) {
         MOZ_ASSERT_UNREACHABLE("incorrect iteration progress");
         break; // in order to continue in outer loop (just below)
       }
       MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
     }
@@ -453,17 +531,17 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
                "out of array bounds");
 
     if (!aStyleRule) {
       // Allocate the style rule now that we know we have animation data.
       aStyleRule = new AnimValuesStyleRule();
     }
 
     double positionInSegment =
-      (computedTiming.mProgress - segment->mFromKey) /
+      (computedTiming.mProgress.Value() - segment->mFromKey) /
       (segment->mToKey - segment->mFromKey);
     double valuePosition =
       segment->mTimingFunction.GetValue(positionInSegment);
 
     StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty);
 
 #ifdef DEBUG
     bool result =
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -26,16 +26,20 @@
 
 struct JSContext;
 class nsCSSPropertySet;
 
 namespace mozilla {
 
 class AnimValuesStyleRule;
 
+namespace dom {
+struct ComputedTimingProperties;
+}
+
 /**
  * Input timing parameters.
  *
  * Eventually this will represent all the input timing parameters specified
  * by content but for now it encapsulates just the subset of those
  * parameters passed to GetPositionInIteration.
  */
 struct AnimationTiming
@@ -67,48 +71,35 @@ struct AnimationTiming
 };
 
 /**
  * Stores the results of calculating the timing properties of an animation
  * at a given sample time.
  */
 struct ComputedTiming
 {
-  ComputedTiming()
-    : mProgress(kNullProgress)
-    , mCurrentIteration(0)
-    , mPhase(AnimationPhase_Null)
-  { }
-
-  static const double kNullProgress;
-
   // The total duration of the animation including all iterations.
   // Will equal StickyTimeDuration::Forever() if the animation repeats
   // indefinitely.
-  StickyTimeDuration mActiveDuration;
-
+  StickyTimeDuration  mActiveDuration;
   // Progress towards the end of the current iteration. If the effect is
   // being sampled backwards, this will go from 1.0 to 0.0.
-  // Will be kNullProgress if the animation is neither animating nor
+  // Will be null if the animation is neither animating nor
   // filling at the sampled time.
-  double mProgress;
-
-  // Zero-based iteration index (meaningless if mProgress is kNullProgress).
-  uint64_t mCurrentIteration;
+  Nullable<double>    mProgress;
+  // Zero-based iteration index (meaningless if mProgress is null).
+  uint64_t            mCurrentIteration = 0;
 
-  enum {
-    // Not sampled (null sample time)
-    AnimationPhase_Null,
-    // Sampled prior to the start of the active interval
-    AnimationPhase_Before,
-    // Sampled within the active interval
-    AnimationPhase_Active,
-    // Sampled after (or at) the end of the active interval
-    AnimationPhase_After
-  } mPhase;
+  enum class AnimationPhase {
+    Null,   // Not sampled (null sample time)
+    Before, // Sampled prior to the start of the active interval
+    Active, // Sampled within the active interval
+    After   // Sampled after (or at) the end of the active interval
+  };
+  AnimationPhase      mPhase = AnimationPhase::Null;
 };
 
 class ComputedTimingFunction
 {
 public:
   typedef nsTimingFunction::Type Type;
   typedef nsTimingFunction::StepSyntax StepSyntax;
   void Init(const nsTimingFunction &aFunction);
@@ -265,30 +256,34 @@ public:
 
   // This function takes as input the timing parameters of an animation and
   // returns the computed timing at the specified local time.
   //
   // The local time may be null in which case only static parameters such as the
   // active duration are calculated. All other members of the returned object
   // are given a null/initial value.
   //
-  // This function returns ComputedTiming::kNullProgress for the mProgress
-  // member of the return value if the animation should not be run
+  // This function returns a null mProgress member of the return value
+  // if the animation should not be run
   // (because it is not currently active and is not filling at this time).
   static ComputedTiming
   GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
                       const AnimationTiming& aTiming);
 
   // Shortcut for that gets the computed timing using the current local time as
   // calculated from the timeline time.
-  ComputedTiming GetComputedTiming(const AnimationTiming* aTiming
-                                     = nullptr) const {
+  ComputedTiming
+  GetComputedTiming(const AnimationTiming* aTiming = nullptr) const
+  {
     return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming);
   }
 
+  void
+  GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override;
+
   // Return the duration of the active interval for the given timing parameters.
   static StickyTimeDuration
   ActiveDuration(const AnimationTiming& aTiming);
 
   bool IsInPlay() const;
   bool IsCurrent() const;
   bool IsInEffect() const;
 
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_animation-computed-timing.html
@@ -0,0 +1,566 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+@keyframes moveAnimation {
+  from { margin-left: 100px }
+  to { margin-left: 200px }
+}
+</style>
+<body>
+<script>
+
+'use strict';
+
+// --------------------
+// delay
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 0,
+                'Initial value of delay');
+}, 'delay of a new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s -10s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, -10000,
+                'Initial value of delay');
+}, 'Negative delay of a new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 10s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 10000,
+                'Initial value of delay');
+}, 'Positive delay of a new animation');
+
+
+// --------------------
+// endDelay
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endDelay, 0,
+                'Initial value of endDelay');
+}, 'endDelay of a new animation');
+
+
+// --------------------
+// fill
+// --------------------
+test(function(t) {
+  var getEffectWithFill = function(fill) {
+    var div = addDiv(t, {style: 'animation: moveAnimation 100s ' + fill});
+    return div.getAnimations()[0].effect;
+  };
+
+  var effect = getEffectWithFill('');
+  assert_equals(effect.getComputedTiming().fill, 'none',
+                'Initial value of fill');
+  effect = getEffectWithFill('forwards');
+  assert_equals(effect.getComputedTiming().fill, 'forwards',
+                'Fill forwards');
+  effect = getEffectWithFill('backwards');
+  assert_equals(effect.getComputedTiming().fill, 'backwards',
+                'Fill backwards');
+  effect = getEffectWithFill('both');
+  assert_equals(effect.getComputedTiming().fill, 'both',
+                'Fill forwards and backwards');
+}, 'fill of a new animation');
+
+
+// --------------------
+// iterationStart
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterationStart, 0,
+                'Initial value of iterationStart');
+}, 'iterationStart of a new animation');
+
+
+// --------------------
+// iterations
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterations, 1,
+                'Initial value of iterations');
+}, 'iterations of a new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2016.5'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterations, 2016.5,
+                'Initial value of iterations');
+}, 'iterations of a finitely repeating animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterations, Infinity,
+                'Initial value of iterations');
+}, 'iterations of an infinitely repeating animation');
+
+
+// --------------------
+// duration
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s -10s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().duration, 100000,
+                'Initial value of duration');
+}, 'duration of a new animation');
+
+
+// --------------------
+// direction
+// --------------------
+test(function(t) {
+  var getEffectWithDir = function(dir) {
+    var div = addDiv(t, {style: 'animation: moveAnimation 100s ' + dir});
+    return div.getAnimations()[0].effect;
+  };
+
+  var effect = getEffectWithDir('');
+  assert_equals(effect.getComputedTiming().direction, 'normal',
+                'Initial value of normal direction');
+  effect = getEffectWithDir('reverse');
+  assert_equals(effect.getComputedTiming().direction, 'reverse',
+                'Initial value of reverse direction');
+  effect = getEffectWithDir('alternate');
+  assert_equals(effect.getComputedTiming().direction, 'alternate',
+                'Initial value of alternate direction');
+  effect = getEffectWithDir('alternate-reverse');
+  assert_equals(effect.getComputedTiming().direction, 'alternate-reverse',
+                'Initial value of alternate-reverse direction');
+}, 'direction of a new animation');
+
+
+// --------------------
+// easing
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().easing, 'linear',
+                'Initial value of easing');
+}, 'easing of a new animation');
+
+
+// ------------------------------
+// endTime
+// = max(start delay + active duration + end delay, 0)
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endTime, 100000,
+                'Initial value of endTime');
+}, 'endTime of an new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s -5s'});
+  var effect = div.getAnimations()[0].effect;
+  var answer = 100000 - 5000; // ms
+  assert_equals(effect.getComputedTiming().endTime, answer,
+                'Initial value of endTime');
+}, 'endTime of an animation with a negative delay');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 10s -100s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endTime, Infinity,
+                'Initial value of endTime');
+}, 'endTime of an infinitely repeating animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 100s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endTime, 100000,
+                'Initial value of endTime');
+}, 'endTime of an infinitely repeating zero-duration animation');
+
+test(function(t) {
+  // Fill forwards so div.getAnimations()[0] wouldn't return
+  // undefined value.
+  var div = addDiv(t, {style: 'animation: moveAnimation 10s -100s forwards'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endTime, 0,
+                'Initial value of endTime');
+}, 'endTime of an animation that finishes before its startTime');
+
+
+// --------------------
+// activeDuration
+// = iteration duration * iteration count
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 5'});
+  var effect = div.getAnimations()[0].effect;
+  var answer = 100000 * 5; // ms
+  assert_equals(effect.getComputedTiming().activeDuration, answer,
+                'Initial value of activeDuration');
+}, 'activeDuration of a new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().activeDuration, Infinity,
+                'Initial value of activeDuration');
+}, 'activeDuration of an infinitely repeating animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 1s infinite'});
+  var effect = div.getAnimations()[0].effect;
+  // If either the iteration duration or iteration count are zero,
+  // the active duration is zero.
+  assert_equals(effect.getComputedTiming().activeDuration, 0,
+                'Initial value of activeDuration');
+}, 'activeDuration of an infinitely repeating zero-duration animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 1s 0'});
+  var effect = div.getAnimations()[0].effect;
+  // If either the iteration duration or iteration count are zero,
+  // the active duration is zero.
+  assert_equals(effect.getComputedTiming().activeDuration, 0,
+                'Initial value of activeDuration');
+}, 'activeDuration of an animation with zero iterations');
+
+
+// --------------------
+// localTime
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().localTime, 0,
+                'Initial value of localTime');
+}, 'localTime of a new animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var anim = div.getAnimations()[0];
+  anim.currentTime = 5000;
+  assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                'current localTime after setting currentTime');
+}, 'localTime of an animation is always equal to currentTime');
+
+async_test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+
+  var anim = div.getAnimations()[0];
+  anim.playbackRate = 2; // 2 times faster
+
+  anim.ready.then(t.step_func(function() {
+    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                  'localTime is equal to currentTime');
+    return waitForFrame();
+  })).then(t.step_func_done(function() {
+    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                  'localTime is equal to currentTime');
+  }));
+}, 'localTime reflects playbackRate immediately');
+
+test(function(t) {
+  var div = addDiv(t);
+  var effect = new KeyframeEffectReadOnly(div, {left: ["0px", "100px"]});
+
+  assert_equals(effect.getComputedTiming().localTime, null,
+                'localTime for orphaned effect');
+}, 'localTime of an AnimationEffect without an Animation');
+
+
+// --------------------
+// progress
+// Note: Default timing function is linear.
+// --------------------
+test(function(t) {
+  [{fill: '',          progress: [ null, null ]},
+   {fill: 'none',      progress: [ null, null ]},
+   {fill: 'forwards',  progress: [ null, 1.0 ]},
+   {fill: 'backwards', progress: [ 0.0, null ]},
+   {fill: 'both',      progress: [ 0.0, 1.0 ]}]
+  .forEach(function(test) {
+    var div =
+      addDiv(t, {style: 'animation: moveAnimation 100s 10s ' + test.fill});
+    var anim = div.getAnimations()[0];
+    assert_true(anim.effect.getComputedTiming().progress === test.progress[0],
+                'initial progress with "' + test.fill + '" fill');
+    anim.finish();
+    assert_true(anim.effect.getComputedTiming().progress === test.progress[1],
+                'finished progress with "' + test.fill + '" fill');
+  });
+}, 'progress of an animation with different fill modes');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10 both'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Initial value of progress');
+  anim.currentTime += 2500;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Value of progress');
+}, 'progress of an integral repeating animation with normal direction');
+
+test(function(t) {
+  var div = addDiv(t);
+  // Note: FillMode here is "both" because
+  // 1. Since this a zero-duration animation, it will already have finished
+  //    so it won't be returned by getAnimations() unless it fills forwards.
+  // 2. Fill backwards, so the progress before phase wouldn't be
+  //    unresolved (null value).
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s infinite both'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Initial value of progress in after phase');
+
+  // Seek backwards
+  anim.currentTime -= 1000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Value of progress before phase');
+}, 'progress of an infinitely repeating zero-duration animation');
+
+test(function(t) {
+  // Default iterations = 1
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s both'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Initial value of progress in after phase');
+
+  // Seek backwards
+  anim.currentTime -= 1000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Value of progress before phase');
+}, 'progress of a finitely repeating zero-duration animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 5s 10.25 both'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Initial value of progress (before phase)');
+
+  // Using iteration duration of 1 now.
+  // currentIteration now is floor(10.25) = 10, so progress should be 25%.
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress in after phase');
+}, 'progress of a non-integral repeating zero-duration animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 5s 10.25 both reverse'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Initial value of progress (before phase)');
+
+  // Seek forwards
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress in after phase');
+}, 'Progress of a non-integral repeating zero-duration animation ' +
+   'with reversing direction');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10.25 both alternate'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Initial value of progress');
+  anim.currentTime += 2500;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+}, 'progress of a non-integral repeating animation ' +
+   'with alternate direction');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 10s 10.25 both alternate-reverse'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Initial value of progress');
+  anim.currentTime += 2500;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.currentTime += 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+}, 'progress of a non-integral repeating animation ' +
+   'with alternate-reversing direction');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.25 both alternate'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Initial value of progress');
+  anim.currentTime += 2500;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+  anim.currentTime -= 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.0,
+                'Value of progress');
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, 0.25,
+                'Value of progress');
+}, 'progress of a non-integral repeating zero-duration animation ' +
+   'with alternate direction');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.25 both alternate-reverse'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Initial value of progress');
+  anim.currentTime += 2500;
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+  anim.currentTime -= 5000;
+  assert_equals(anim.effect.getComputedTiming().progress, 1.0,
+                'Value of progress');
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, 0.75,
+                'Value of progress');
+}, 'progress of a non-integral repeating zero-duration animation ' +
+   'with alternate-reverse direction');
+
+
+// --------------------
+// currentIteration
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2s'});
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().currentIteration, null,
+                'Initial value of currentIteration before phase');
+}, 'currentIteration of a new animation with no backwards fill is unresolved ' +
+   'in before phase');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s'});
+  var anim = div.getAnimations()[0];
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+}, 'currentIteration of a new animation is zero');
+
+test(function(t) {
+  // Note: FillMode here is "both" because
+  // 1. Since this a zero-duration animation, it will already have finished
+  //    so it won't be returned by getAnimations() unless it fills forwards.
+  // 2. Fill backwards, so the currentIteration (before phase) wouldn't be
+  //    unresolved (null value).
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s infinite both'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity,
+                'Initial value of currentIteration in after phase');
+
+  // Seek backwards
+  anim.currentTime -= 2000;
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Value of currentIteration count during before phase');
+}, 'currentIteration of an infinitely repeating zero-duration animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 0s 10.5 both'});
+  var anim = div.getAnimations()[0];
+
+  // Note: currentIteration = ceil(iteration start + iteration count) - 1
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 10,
+                'Initial value of currentIteration');
+
+  // Seek backwards
+  anim.currentTime -= 2000;
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Value of currentIteration count during before phase');
+}, 'currentIteration of a finitely repeating zero-duration animation');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 5.5 forwards'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+  // The 3rd iteration
+  // Note: currentIteration = floor(scaled active time / iteration duration)
+  anim.currentTime = 250000; // ms
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 2,
+                'Value of currentIteration during the 3rd iteration');
+  // Finish
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 5,
+                'Value of currentIteration in after phase');
+}, 'currentIteration of an animation with a non-integral iteration count');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s 2 forwards'});
+  var anim = div.getAnimations()[0];
+
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+  // Finish
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 1,
+                'Value of currentIteration in after phase');
+}, 'currentIteration of an animation with an integral iteration count');
+
+test(function(t) {
+  var div = addDiv(t, {style: 'animation: moveAnimation 100s forwards'});
+  var anim = div.getAnimations()[0];
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+  // Finish
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
+                'Value of currentIteration in after phase');
+}, 'currentIteration of an animation with a default iteration count');
+
+test(function(t) {
+  var div = addDiv(t);
+  var effect = new KeyframeEffectReadOnly(div, {left: ["0px", "100px"]});
+
+  assert_equals(effect.getComputedTiming().currentIteration, null,
+                'currentIteration for orphaned effect');
+}, 'currentIteration of an AnimationEffect without an Animation');
+
+// TODO: If iteration duration is Infinity, currentIteration is 0.
+// However, we cannot set iteration duration to Infinity in CSS Animation now.
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/test_animation-computed-timing.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+  { "set": [["dom.animations-api.core.enabled", true]]},
+  function() {
+    window.open("file_animation-computed-timing.html");
+  });
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-computed-timing.html
@@ -0,0 +1,315 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+
+.animated-div {
+  margin-left: 100px;
+}
+
+</style>
+<body>
+<script>
+
+'use strict';
+
+// --------------------
+// delay
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 0,
+                'Initial value of delay');
+}, 'delay of a new tranisition');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, 10000,
+                'Initial value of delay');
+}, 'Positive delay of a new transition');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s -5s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().delay, -5000,
+                'Initial value of delay');
+}, 'Negative delay of a new transition');
+
+
+// --------------------
+// endDelay
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().endDelay, 0,
+                'Initial value of endDelay');
+}, 'endDelay of a new transition');
+
+
+// --------------------
+// fill
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().fill, 'backwards',
+                'Fill backwards');
+}, 'fill of a new transition');
+
+
+// --------------------
+// iterationStart
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterationStart, 0,
+                'Initial value of iterationStart');
+}, 'iterationStart of a new transition');
+
+
+// --------------------
+// iterations
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().iterations, 1,
+                'Initial value of iterations');
+}, 'iterations of a new transition');
+
+
+// --------------------
+// duration
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().duration, 10000,
+                'Initial value of duration');
+}, 'duration of a new transition');
+
+
+// --------------------
+// direction
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().direction, 'normal',
+                'Initial value of direction');
+}, 'direction of a new transition');
+
+
+// --------------------
+// easing
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().easing, 'linear',
+                'Initial value of easing');
+}, 'easing of a new transition');
+
+
+// ------------------------------
+// endTime
+// = max(start delay + active duration + end delay, 0)
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 100s -5s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  var answer = 100000 - 5000; // ms
+  assert_equals(effect.getComputedTiming().endTime, answer,
+                'Initial value of endTime');
+}, 'endTime of a new transition');
+
+
+// --------------------
+// activeDuration
+// = iteration duration * iteration count(==1)
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 100s -5s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().activeDuration, 100000,
+                'Initial value of activeDuration');
+}, 'activeDuration of a new transition');
+
+
+// --------------------
+// localTime
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 100s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().localTime, 0,
+                'Initial value of localTime');
+}, 'localTime of a new transition');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 100s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var anim = div.getAnimations()[0];
+  anim.currentTime = 5000;
+  assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                'current localTime after setting currentTime');
+}, 'localTime is always equal to currentTime');
+
+async_test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 100s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var anim = div.getAnimations()[0];
+  anim.playbackRate = 2; // 2 times faster
+
+  anim.ready.then(t.step_func(function() {
+    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                  'localTime is equal to currentTime');
+    return waitForFrame();
+  })).then(t.step_func_done(function() {
+    assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+                  'localTime is equal to currentTime');
+  }));
+}, 'localTime reflects playbackRate immediately');
+
+
+// --------------------
+// progress
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10.5s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().progress, 0.0,
+                'Initial value of progress');
+}, 'progress of a new transition');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10.5s 2s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().progress, 0.0,
+                'Initial value of progress');
+}, 'progress of a new transition with positive delay in before phase');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10.5s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var anim = div.getAnimations()[0];
+  anim.finish()
+  assert_equals(anim.effect.getComputedTiming().progress, null,
+                'finished progress');
+}, 'progress of a finished transition');
+
+
+// --------------------
+// currentIteration
+// --------------------
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+}, 'currentIteration of a new transition');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s 2s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var effect = div.getAnimations()[0].effect;
+  assert_equals(effect.getComputedTiming().currentIteration, 0,
+                'Initial value of currentIteration');
+}, 'currentIteration of a new transition with positive delay in before phase');
+
+test(function(t) {
+  var div = addDiv(t, {'class': 'animated-div'});
+  div.style.transition = 'margin-left 10s';
+  flushComputedStyle(div);
+  div.style.marginLeft = '10px';
+
+  var anim = div.getAnimations()[0];
+  anim.finish();
+  assert_equals(anim.effect.getComputedTiming().currentIteration, null,
+                'finished currentIteration');
+}, 'currentIteration of a finished transition');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-computed-timing.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+  { "set": [["dom.animations-api.core.enabled", true]]},
+  function() {
+    window.open("file_animation-computed-timing.html");
+  });
+</script>
+</html>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 support-files =
   testcommon.js
 
 [css-animations/test_animations-dynamic-changes.html]
 support-files = css-animations/file_animations-dynamic-changes.html
 [css-animations/test_animation-cancel.html]
 support-files = css-animations/file_animation-cancel.html
+[css-animations/test_animation-computed-timing.html]
+support-files = css-animations/file_animation-computed-timing.html
 [css-animations/test_animation-currenttime.html]
 support-files = css-animations/file_animation-currenttime.html
 [css-animations/test_animation-finish.html]
 support-files = css-animations/file_animation-finish.html
 [css-animations/test_animation-finished.html]
 support-files = css-animations/file_animation-finished.html
 [css-animations/test_animation-oncancel.html]
 support-files = css-animations/file_animation-oncancel.html
@@ -38,16 +40,18 @@ support-files = css-animations/file_keyf
 support-files = css-animations/file_effect-target.html
 [css-animations/test_element-get-animations.html]
 skip-if = buildapp == 'mulet'
 support-files = css-animations/file_element-get-animations.html
 [css-animations/test_timeline-get-animations.html]
 support-files = css-animations/file_timeline-get-animations.html
 [css-transitions/test_animation-cancel.html]
 support-files = css-transitions/file_animation-cancel.html
+[css-transitions/test_animation-computed-timing.html]
+support-files = css-transitions/file_animation-computed-timing.html
 [css-transitions/test_animation-currenttime.html]
 support-files = css-transitions/file_animation-currenttime.html
 [css-transitions/test_animation-finished.html]
 support-files = css-transitions/file_animation-finished.html
 [css-transitions/test_animation-pausing.html]
 support-files = css-transitions/file_animation-pausing.html
 [css-transitions/test_animation-ready.html]
 support-files = css-transitions/file_animation-ready.html
--- a/dom/base/PerformanceResourceTiming.cpp
+++ b/dom/base/PerformanceResourceTiming.cpp
@@ -22,17 +22,20 @@ NS_INTERFACE_MAP_END_INHERITING(Performa
 
 NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 
 PerformanceResourceTiming::PerformanceResourceTiming(nsPerformanceTiming* aPerformanceTiming,
                                                      nsPerformance* aPerformance,
                                                      const nsAString& aName)
 : PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")),
-  mTiming(aPerformanceTiming)
+  mTiming(aPerformanceTiming),
+  mEncodedBodySize(0),
+  mTransferSize(0),
+  mDecodedBodySize(0)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 }
 
 PerformanceResourceTiming::~PerformanceResourceTiming()
 {
 }
 
--- a/dom/base/PerformanceResourceTiming.h
+++ b/dom/base/PerformanceResourceTiming.h
@@ -47,16 +47,26 @@ public:
     aInitiatorType = mInitiatorType;
   }
 
   void SetInitiatorType(const nsAString& aInitiatorType)
   {
     mInitiatorType = aInitiatorType;
   }
 
+  void GetNextHopProtocol(nsAString& aNextHopProtocol) const
+  {
+    aNextHopProtocol = mNextHopProtocol;
+  }
+
+  void SetNextHopProtocol(const nsAString& aNextHopProtocol)
+  {
+    mNextHopProtocol = aNextHopProtocol;
+  }
+
   DOMHighResTimeStamp FetchStart() const {
     return mTiming
         ? mTiming->FetchStartHighRes()
         : 0;
   }
 
   DOMHighResTimeStamp RedirectStart() const {
     // We have to check if all the redirect URIs had the same origin (since
@@ -123,19 +133,53 @@ public:
     return 0;
   }
 
   virtual const PerformanceResourceTiming* ToResourceTiming() const override
   {
     return this;
   }
 
+  uint64_t TransferSize() const
+  {
+    return mTiming && mTiming->TimingAllowed() ? mTransferSize : 0;
+  }
+
+  uint64_t EncodedBodySize() const
+  {
+    return mTiming && mTiming->TimingAllowed() ? mEncodedBodySize : 0;
+  }
+
+  uint64_t DecodedBodySize() const
+  {
+    return mTiming && mTiming->TimingAllowed() ? mDecodedBodySize : 0;
+  }
+
+  void SetEncodedBodySize(uint64_t aEncodedBodySize)
+  {
+    mEncodedBodySize = aEncodedBodySize;
+  }
+
+  void SetTransferSize(uint64_t aTransferSize)
+  {
+    mTransferSize = aTransferSize;
+  }
+
+  void SetDecodedBodySize(uint64_t aDecodedBodySize)
+  {
+    mDecodedBodySize = aDecodedBodySize;
+  }
+
 protected:
   virtual ~PerformanceResourceTiming();
 
   nsString mInitiatorType;
+  nsString mNextHopProtocol;
   RefPtr<nsPerformanceTiming> mTiming;
+  uint64_t mEncodedBodySize;
+  uint64_t mTransferSize;
+  uint64_t mDecodedBodySize;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_PerformanceResourceTiming_h___ */
--- a/dom/base/nsDOMCID.h
+++ b/dom/base/nsDOMCID.h
@@ -16,21 +16,18 @@
   0x9eb760f0, 0x4380, 0x11d2,                       \
  {0xb3, 0x28, 0x00, 0x80, 0x5f, 0x8a, 0x38, 0x59} }
 
 #define NS_SCRIPT_NAMESET_REGISTRY_CID               \
  { /* 45f27d10-987b-11d2-bd40-00105aa45e89 */        \
   0x45f27d10, 0x987b, 0x11d2,                        \
  {0xbd, 0x40, 0x00, 0x10, 0x5a, 0xa4, 0x5e, 0x89} }
 
-#define SERVICEWORKERPERIODICUPDATER_CONTRACTID \
-  "@mozilla.org/service-worker-periodic-updater;1"
-
 //The dom cannot provide the crypto or pkcs11 classes that
 //were used in older days, so if someone wants to provide
-//the service they must implement an object and give it 
+//the service they must implement an object and give it
 //this class ID
 #define NS_CRYPTO_CONTRACTID "@mozilla.org/security/crypto;1"
 #define NS_PKCS11_CONTRACTID "@mozilla.org/security/pkcs11;1"
 
 #define NS_XPATH_EVALUATOR_CONTRACTID "@mozilla.org/dom/xpath-evaluator;1"
 
 #endif /* nsDOMCID_h__ */
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1474,38 +1474,46 @@ nsObjectLoadingContent::CheckJavaCodebas
 
 bool
 nsObjectLoadingContent::IsYoutubeEmbed()
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "Must be an instance of content");
 
-  // We're only interested in switching out embed tags
-  if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) {
+  // We're only interested in switching out embed and object tags
+  if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed) &&
+      !thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
     return false;
   }
   nsCOMPtr<nsIEffectiveTLDService> tldService =
     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   // If we can't analyze the URL, just pass on through.
   if(!tldService) {
     NS_WARNING("Could not get TLD service!");
     return false;
   }
   nsAutoCString currentBaseDomain;
   bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(mURI, 0, currentBaseDomain));
   if (!ok) {
     // Data URIs won't parse correctly, so just fail silently here.
     return false;
   }
+  // See if URL is referencing youtube
   nsAutoCString domain("youtube.com");
-  if (StringEndsWith(domain, currentBaseDomain)) {
-    return true;
+  if (!StringEndsWith(domain, currentBaseDomain)) {
+    return false;
   }
-  return false;
+  // See if requester is planning on using the JS API.
+  nsAutoCString uri;
+  mURI->GetSpec(uri);
+  if (uri.Find("enablejsapi=1", true, 0, -1) == kNotFound) {
+    return false;
+  }
+  return true;
 }
 
 bool
 nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy)
 {
   if (!aContentPolicy || !mURI) {
     NS_NOTREACHED("Doing it wrong");
     return false;
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -561,16 +561,35 @@ nsPerformance::AddEntry(nsIHttpChannel* 
         new nsPerformanceTiming(this, timedChannel, channel,
             0);
 
     // The PerformanceResourceTiming object will use the nsPerformanceTiming
     // object to get all the required timings.
     RefPtr<PerformanceResourceTiming> performanceEntry =
       new PerformanceResourceTiming(performanceTiming, this, entryName);
 
+    nsAutoCString protocol;
+    channel->GetProtocolVersion(protocol);
+    performanceEntry->SetNextHopProtocol(NS_ConvertUTF8toUTF16(protocol));
+
+    uint64_t encodedBodySize = 0;
+    channel->GetEncodedBodySize(&encodedBodySize);
+    performanceEntry->SetEncodedBodySize(encodedBodySize);
+
+    uint64_t transferSize = 0;
+    channel->GetTransferSize(&transferSize);
+    performanceEntry->SetTransferSize(transferSize);
+
+    uint64_t decodedBodySize = 0;
+    channel->GetDecodedBodySize(&decodedBodySize);
+    if (decodedBodySize == 0) {
+      decodedBodySize = encodedBodySize;
+    }
+    performanceEntry->SetDecodedBodySize(decodedBodySize);
+
     // If the initiator type had no valid value, then set it to the default
     // ("other") value.
     if (initiatorType.IsEmpty()) {
       initiatorType = NS_LITERAL_STRING("other");
     }
     performanceEntry->SetInitiatorType(initiatorType);
     InsertResourceEntry(performanceEntry);
   }
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -16,30 +16,30 @@ interface nsIURI;
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
 };
 
-[scriptable, builtinclass, uuid(e633b73b-a734-4d04-a09c-b7779a439f3f)]
-interface nsIServiceWorkerInfo : nsISupports
+[scriptable, builtinclass, uuid(f222dcad-3081-4699-bbc6-5615e20749C8)]
+interface nsIServiceWorkerRegistrationInfo : nsISupports
 {
   readonly attribute nsIPrincipal principal;
 
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(8fb9db4f-1d04-402b-9c37-542da06e03b9)]
+[scriptable, builtinclass, uuid(10f80c8c-7bf5-479e-a8d8-12ef50c802e8)]
 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.
    *
@@ -108,17 +108,17 @@ interface nsIServiceWorkerManager : nsIS
    *   This eventually results in the registration being deleted from disk too.
    */
   void removeAndPropagate(in AUTF8String aHost);
 
   // Testing
   DOMString getScopeForUrl(in nsIPrincipal aPrincipal, in DOMString aPath);
 
   // Note: This is meant to be used only by about:serviceworkers.
-  //It returns an array of nsIServiceWorkerInfo.
+  // It returns an array of nsIServiceWorkerRegistrationInfos.
   nsIArray getAllRegistrations();
 
   // Note: This is meant to be used only by about:serviceworkers.
   // It calls softUpdate() for each child process.
   [implicit_jscontext] void propagateSoftUpdate(in jsval aOriginAttributes,
                                                 in DOMString aScope);
 
   // Note: This is meant to be used only by about:serviceworkers.
@@ -140,15 +140,13 @@ interface nsIServiceWorkerManager : nsIS
                                   in AString aData,
                                   in AString aBehavior);
   [optional_argc] void sendPushEvent(in ACString aOriginAttributes,
                                      in ACString aScope,
                                      [optional] in uint32_t aDataLength,
                                      [optional, array, size_is(aDataLength)] in uint8_t aDataBytes);
   void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
                                        in ACString scope);
-
-  void updateAllRegistrations();
 };
 
 %{ C++
 #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
 %}
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -76,17 +76,16 @@
 #include "nsIDragService.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMutable.h"
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsIServiceWorkerManager.h"
 #include "nsScreenManagerProxy.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsVariant.h"
 #include "nsXULAppAPI.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
@@ -1429,26 +1428,16 @@ ContentChild::RecvBidiKeyboardNotify(con
     // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
     PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
     if (bidi) {
         bidi->SetIsLangRTL(aIsLangRTL);
     }
     return true;
 }
 
-bool
-ContentChild::RecvUpdateServiceWorkerRegistrations()
-{
-    nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
-    if (swm) {
-        swm->UpdateAllRegistrations();
-    }
-    return true;
-}
-
 static CancelableTask* sFirstIdleTask;
 
 static void FirstIdle(void)
 {
     MOZ_ASSERT(sFirstIdleTask);
     sFirstIdleTask = nullptr;
     ContentChild::GetSingleton()->SendFirstIdle();
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -324,18 +324,16 @@ public:
 
     virtual bool RecvSetOffline(const bool& offline) override;
     virtual bool RecvSetConnectivity(const bool& connectivity) override;
 
     virtual bool RecvSpeakerManagerNotify() override;
 
     virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL) override;
 
-    virtual bool RecvUpdateServiceWorkerRegistrations() override;
-
     virtual bool RecvNotifyVisited(const URIParams& aURI) override;
     // auto remove when alertfinished is received.
     nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
 
     virtual bool RecvSystemMemoryAvailable(const uint64_t& aGetterId,
                                            const uint32_t& aMemoryAvailable) override;
 
     virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -516,18 +516,16 @@ child:
     async SpeakerManagerNotify();
 
     /**
      * Communication between the PuppetBidiKeyboard and the actual
      * BidiKeyboard hosted by the parent
      */
     async BidiKeyboardNotify(bool isLangRTL);
 
-    async UpdateServiceWorkerRegistrations();
-
     async DataStoreNotify(uint32_t aAppId, nsString aName,
                           nsString aManifestURL);
 
     /**
      * Dump this process's GC and CC logs to the provided files.
      *
      * For documentation on the other args, see dumpGCAndCCLogsToFile in
      * nsIMemoryInfoDumper.idl
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -146,17 +146,16 @@ public:
   NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
 
   NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
 
   NS_IMETHOD TerminateScript() override;
   NS_IMETHOD BeginStartingDebugger() override;
   NS_IMETHOD EndStartingDebugger() override;
   NS_IMETHOD TerminatePlugin() override;
-  NS_IMETHOD TerminateProcess() override;
   NS_IMETHOD UserCanceled() override;
 
   NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
 
   // Called when a content process shuts down.
   void Clear() {
     mContentParent = nullptr;
     mActor = nullptr;
@@ -815,45 +814,29 @@ HangMonitoredProcess::EndStartingDebugge
 NS_IMETHODIMP
 HangMonitoredProcess::TerminatePlugin()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (mHangData.type() != HangData::TPluginHangData) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  // generates a crash report that includes a browser report taken here
+  // earlier, the content process, and any plugin process(es).
   uint32_t id = mHangData.get_PluginHangData().pluginId();
   plugins::TerminatePlugin(id, NS_LITERAL_CSTRING("HangMonitor"),
                            mBrowserDumpId);
 
   if (mActor) {
     mActor->CleanupPluginHang(id, false);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HangMonitoredProcess::TerminateProcess()
-{
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
-
-  if (!mContentParent) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (mActor && mHangData.type() == HangData::TPluginHangData) {
-    uint32_t id = mHangData.get_PluginHangData().pluginId();
-    mActor->CleanupPluginHang(id, true);
-  }
-
-  mContentParent->KillHard("HangMonitor");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!mActor) {
     *aResult = false;
     return NS_OK;
   }
--- a/dom/ipc/nsIHangReport.idl
+++ b/dom/ipc/nsIHangReport.idl
@@ -13,17 +13,17 @@ interface nsIFrameLoader;
  * When a content process hangs, Gecko notifies "process-hang-report" observers
  * and passes an nsIHangReport for the subject parameter. There is at most one
  * nsIHangReport associated with a given content process. As long as the content
  * process stays stuck, the "process-hang-report" observer will continue to be
  * notified at regular intervals (approximately once per second). The content
  * process will continue to run uninhibitedly during this time.
  */
 
-[scriptable, uuid(90cea731-dd3e-459e-b017-f9a14697b56e)]
+[scriptable, uuid(5fcffbb9-be62-49b1-b8a1-36e820787a74)]
 interface nsIHangReport : nsISupports
 {
   const unsigned long SLOW_SCRIPT = 1;
   const unsigned long PLUGIN_HANG = 2;
 
   // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG.
   readonly attribute unsigned long hangType;
 
@@ -45,20 +45,16 @@ interface nsIHangReport : nsISupports
   // Terminate the slow script if it is still running.
   // Only valid for SLOW_SCRIPT reports.
   void terminateScript();
 
   // Terminate the plugin if it is still hung.
   // Only valid for PLUGIN_HANG reports.
   void terminatePlugin();
 
-  // Terminate the hung content process unconditionally.
-  // Valid for any type of hang.
-  void terminateProcess();
-
   // Ask the content process to start up the slow script debugger.
   // Only valid for SLOW_SCRIPT reports.
   void beginStartingDebugger();
 
   // Inform the content process that the slow script debugger has finished
   // spinning up. The content process will run a nested event loop until this
   // method is called.
   // Only valid for SLOW_SCRIPT reports.
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -125,17 +125,16 @@ AudioStream::AudioStream()
   , mChannels(0)
   , mOutChannels(0)
   , mWritten(0)
   , mAudioClock(this)
   , mTimeStretcher(nullptr)
   , mDumpFile(nullptr)
   , mBytesPerFrame(0)
   , mState(INITIALIZED)
-  , mLastGoodPosition(0)
   , mIsMonoAudioEnabled(gfxPrefs::MonoAudio())
 {
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
   MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
@@ -617,22 +616,17 @@ AudioStream::GetPositionInFramesUnlocked
   uint64_t position = 0;
   {
     MonitorAutoUnlock mon(mMonitor);
     if (cubeb_stream_get_position(mCubebStream.get(), &position) != CUBEB_OK) {
       return -1;
     }
   }
 
-  MOZ_ASSERT(position >= mLastGoodPosition, "cubeb position shouldn't go backward");
-  // This error handling/recovery keeps us in good shape in release build.
-  if (position >= mLastGoodPosition) {
-    mLastGoodPosition = position;
-  }
-  return std::min<uint64_t>(mLastGoodPosition, INT64_MAX);
+  return std::min<uint64_t>(position, INT64_MAX);
 }
 
 bool
 AudioStream::IsPaused()
 {
   MonitorAutoLock mon(mMonitor);
   return mState == STOPPED;
 }
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -317,18 +317,15 @@ private:
                  // completion.
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED,     // Stream disabled due to an internal error.
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mIsFirst;
-  // The last good position returned by cubeb_stream_get_position(). Used to
-  // check if the cubeb position is going backward.
-  uint64_t mLastGoodPosition;
   // Get this value from the preferece, if true, we would downmix the stereo.
   bool mIsMonoAudioEnabled;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -17,16 +17,20 @@
 #ifdef XP_WIN
 #include "windows.h"
 #ifdef MOZ_SANDBOX
 #include <intrin.h>
 #include <assert.h>
 #endif
 #endif
 
+#ifdef XP_MACOSX
+#include <assert.h>
+#endif
+
 #if defined(HASH_NODE_ID_WITH_DEVICE_ID)
 // In order to provide EME plugins with a "device binding" capability,
 // in the parent we generate and store some random bytes as salt for every
 // (origin, urlBarOrigin) pair that uses EME. We store these bytes so
 // that every time we revisit the same origin we get the same salt.
 // We send this salt to the child on startup. The child collects some
 // device specific data and munges that with the salt to create the
 // "node id" that we expose to EME plugins. It then overwrites the device
@@ -104,16 +108,28 @@ GetStackAfterCurrentFrame(uint8_t** aOut
     bottom = (uint8_t*)memInfo.BaseAddress - 1;
   }
   *aOutTop = top;
   *aOutBottom = bottom;
   return true;
 }
 #endif
 
+#if defined(XP_MACOSX) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
+MOZ_NEVER_INLINE
+static bool
+GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
+{
+  // TODO
+  *aOutTop = nullptr;
+  *aOutBottom = nullptr;
+  return true;
+}
+#endif
+
 #ifdef HASH_NODE_ID_WITH_DEVICE_ID
 static void SecureMemset(void* start, uint8_t value, size_t size)
 {
   // Inline instructions equivalent to RtlSecureZeroMemory().
   for (size_t i = 0; i < size; ++i) {
     volatile uint8_t* p = static_cast<volatile uint8_t*>(start) + i;
     *p = value;
   }
@@ -125,39 +141,39 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
                     uint32_t aUTF8LibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
                     const GMPPlatformAPI* aPlatformAPI)
 {
   std::string nodeId;
 #ifdef HASH_NODE_ID_WITH_DEVICE_ID
   if (aOriginSaltLen > 0) {
-    string16 deviceId;
+    std::vector<uint8_t> deviceId;
     int volumeId;
     if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
       return false;
     }
 
     SHA256Context ctx;
     SHA256_Begin(&ctx);
     SHA256_Update(&ctx, (const uint8_t*)aOriginSalt, aOriginSaltLen);
-    SHA256_Update(&ctx, (const uint8_t*)deviceId.c_str(), deviceId.size() * sizeof(string16::value_type));
+    SHA256_Update(&ctx, deviceId.data(), deviceId.size());
     SHA256_Update(&ctx, (const uint8_t*)&volumeId, sizeof(int));
     uint8_t digest[SHA256_LENGTH] = {0};
     unsigned int digestLen = 0;
     SHA256_End(&ctx, digest, &digestLen, SHA256_LENGTH);
 
     // Overwrite all data involved in calculation as it could potentially
     // identify the user, so there's no chance a GMP can read it and use
     // it for identity tracking.
     SecureMemset(&ctx, 0, sizeof(ctx));
     SecureMemset(aOriginSalt, 0, aOriginSaltLen);
     SecureMemset(&volumeId, 0, sizeof(volumeId));
-    SecureMemset(&deviceId[0], '*', sizeof(string16::value_type) * deviceId.size());
-    deviceId = L"";
+    SecureMemset(deviceId.data(), '*', deviceId.size());
+    deviceId.clear();
 
     if (!rlz_lib::BytesToString(digest, SHA256_LENGTH, &nodeId)) {
       return false;
     }
 
     if (!PR_GetEnv("MOZ_GMP_DISABLE_NODE_ID_CLEANUP")) {
       // We've successfully bound the origin salt to node id.
       // rlz_lib::GetRawMachineId and/or the system functions it
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -104,17 +104,17 @@ UNIFIED_SOURCES += [
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
     'GMPVideoPlaneImpl.cpp',
 ]
 
-if CONFIG['OS_TARGET'] == 'WINNT':
+if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin'):
   DIRS += [
       'rlz',
   ]
 
 IPDL_SOURCES += [
   'GMPTypes.ipdlh',
   'PGMP.ipdl',
   'PGMPAudioDecoder.ipdl',
deleted file mode 100644
--- a/dom/media/gmp/rlz/base/string16.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* -*- 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 FAKE_STRING16_H
-#define FAKE_STRING16_H
-
-#include <string>
-typedef std::wstring string16;
-
-#endif
--- a/dom/media/gmp/rlz/lib/machine_id.h
+++ b/dom/media/gmp/rlz/lib/machine_id.h
@@ -1,21 +1,19 @@
 // Copyright 2012 Google Inc. All Rights Reserved.
 // Use of this source code is governed by an Apache-style license that can be
 // found in the COPYING file.
 
 #ifndef RLZ_LIB_MACHINE_ID_H_
 #define RLZ_LIB_MACHINE_ID_H_
 
-#include "base/string16.h"
-
-#include <string>
+#include <vector>
 
 namespace rlz_lib {
 
 // Retrieves a raw machine identifier string and a machine-specific
 // 4 byte value. GetMachineId() will SHA1 |data|, append |more_data|, compute
 // the Crc8 of that, and return a hex-encoded string of that data.
-bool GetRawMachineId(string16* data, int* more_data);
+bool GetRawMachineId(std::vector<uint8_t>* data, int* more_data);
 
 }  // namespace rlz_lib
 
 #endif  // RLZ_LIB_MACHINE_ID_H_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/network/IOEthernetController.h>
+#include <IOKit/network/IOEthernetInterface.h>
+#include <IOKit/network/IONetworkInterface.h>
+#include <vector>
+
+// Note: The original machine_id_mac.cc code is in namespace rlz_lib below.
+// It depends on some external files, which would bring in a log of Chromium
+// code if imported as well.
+// Instead only the necessary code has been extracted from the relevant files,
+// and further combined and reduced to limit the maintenance burden.
+
+// [Extracted from base/logging.h]
+#define DCHECK assert
+
+namespace base {
+
+// [Extracted from base/mac/scoped_typeref.h and base/mac/scoped_cftyperef.h]
+template<typename T>
+class ScopedCFTypeRef {
+ public:
+  typedef T element_type;
+
+  explicit ScopedCFTypeRef(T object)
+      : object_(object) {
+  }
+
+  ScopedCFTypeRef(const ScopedCFTypeRef<T>& that) = delete;
+  ScopedCFTypeRef(ScopedCFTypeRef<T>&& that) = delete;
+
+  ~ScopedCFTypeRef() {
+    if (object_)
+      CFRelease(object_);
+  }
+
+  ScopedCFTypeRef& operator=(const ScopedCFTypeRef<T>& that) = delete;
+  ScopedCFTypeRef& operator=(ScopedCFTypeRef<T>&& that) = delete;
+
+  operator T() const {
+    return object_;
+  }
+
+  // ScopedCFTypeRef<>::release() is like scoped_ptr<>::release.  It is NOT
+  // a wrapper for CFRelease().
+  T release() {
+    T temp = object_;
+    object_ = NULL;
+    return temp;
+  }
+
+ private:
+  T object_;
+};
+
+namespace mac {
+
+// [Extracted from base/mac/scoped_ioobject.h]
+// Just like ScopedCFTypeRef but for io_object_t and subclasses.
+template<typename IOT>
+class ScopedIOObject {
+ public:
+  typedef IOT element_type;
+
+  explicit ScopedIOObject(IOT object = IO_OBJECT_NULL)
+      : object_(object) {
+  }
+
+  ~ScopedIOObject() {
+    if (object_)
+      IOObjectRelease(object_);
+  }
+
+  ScopedIOObject(const ScopedIOObject&) = delete;
+  void operator=(const ScopedIOObject&) = delete;
+
+  void reset(IOT object = IO_OBJECT_NULL) {
+    if (object_)
+      IOObjectRelease(object_);
+    object_ = object;
+  }
+
+  operator IOT() const {
+    return object_;
+  }
+
+ private:
+  IOT object_;
+};
+
+// [Extracted from base/mac/foundation_util.h]
+template<typename T>
+T CFCast(const CFTypeRef& cf_val);
+
+template<>
+CFDataRef
+CFCast<CFDataRef>(const CFTypeRef& cf_val) {
+  if (cf_val == NULL) {
+    return NULL;
+  }
+  if (CFGetTypeID(cf_val) == CFDataGetTypeID()) {
+    return (CFDataRef)(cf_val);
+  }
+  return NULL;
+}
+
+template<>
+CFStringRef
+CFCast<CFStringRef>(const CFTypeRef& cf_val) {
+  if (cf_val == NULL) {
+    return NULL;
+  }
+  if (CFGetTypeID(cf_val) == CFStringGetTypeID()) {
+    return (CFStringRef)(cf_val);
+  }
+  return NULL;
+}
+
+}  // namespace mac
+
+// [Extracted from base/strings/sys_string_conversions_mac.mm]
+static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
+
+template<typename StringType>
+static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring,
+                                                   CFStringEncoding encoding) {
+  CFIndex length = CFStringGetLength(cfstring);
+  if (length == 0)
+    return StringType();
+
+  CFRange whole_string = CFRangeMake(0, length);
+  CFIndex out_size;
+  CFIndex converted = CFStringGetBytes(cfstring,
+                                       whole_string,
+                                       encoding,
+                                       0,      // lossByte
+                                       false,  // isExternalRepresentation
+                                       NULL,   // buffer
+                                       0,      // maxBufLen
+                                       &out_size);
+  if (converted == 0 || out_size == 0)
+    return StringType();
+
+  // out_size is the number of UInt8-sized units needed in the destination.
+  // A buffer allocated as UInt8 units might not be properly aligned to
+  // contain elements of StringType::value_type.  Use a container for the
+  // proper value_type, and convert out_size by figuring the number of
+  // value_type elements per UInt8.  Leave room for a NUL terminator.
+  typename StringType::size_type elements =
+      out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1;
+
+  std::vector<typename StringType::value_type> out_buffer(elements);
+  converted = CFStringGetBytes(cfstring,
+                               whole_string,
+                               encoding,
+                               0,      // lossByte
+                               false,  // isExternalRepresentation
+                               reinterpret_cast<UInt8*>(&out_buffer[0]),
+                               out_size,
+                               NULL);  // usedBufLen
+  if (converted == 0)
+    return StringType();
+
+  out_buffer[elements - 1] = '\0';
+  return StringType(&out_buffer[0], elements - 1);
+}
+
+std::string SysCFStringRefToUTF8(CFStringRef ref)
+{
+  return CFStringToSTLStringWithEncodingT<std::string>(ref,
+                                                       kNarrowStringEncoding);
+}
+
+} // namespace base
+
+
+namespace rlz_lib {
+
+namespace {
+
+// See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html
+
+// The caller is responsible for freeing |matching_services|.
+bool FindEthernetInterfaces(io_iterator_t* matching_services) {
+  base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+      IOServiceMatching(kIOEthernetInterfaceClass));
+  if (!matching_dict)
+    return false;
+
+  base::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface(
+      CFDictionaryCreateMutable(kCFAllocatorDefault,
+                                0,
+                                &kCFTypeDictionaryKeyCallBacks,
+                                &kCFTypeDictionaryValueCallBacks));
+  if (!primary_interface)
+    return false;
+
+  CFDictionarySetValue(
+      primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
+  CFDictionarySetValue(
+      matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface);
+
+  kern_return_t kern_result = IOServiceGetMatchingServices(
+      kIOMasterPortDefault, matching_dict.release(), matching_services);
+
+  return kern_result == KERN_SUCCESS;
+}
+
+bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator,
+                               uint8_t* buffer, size_t buffer_size) {
+  if (buffer_size < kIOEthernetAddressSize)
+    return false;
+
+  bool success = false;
+
+  bzero(buffer, buffer_size);
+  base::mac::ScopedIOObject<io_object_t> primary_interface;
+  while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)),
+         primary_interface) {
+    io_object_t primary_interface_parent;
+    kern_return_t kern_result = IORegistryEntryGetParentEntry(
+        primary_interface, kIOServicePlane, &primary_interface_parent);
+    base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter(
+        primary_interface_parent);
+    success = kern_result == KERN_SUCCESS;
+
+    if (!success)
+      continue;
+
+    base::ScopedCFTypeRef<CFTypeRef> mac_data(
+        IORegistryEntryCreateCFProperty(primary_interface_parent,
+                                        CFSTR(kIOMACAddress),
+                                        kCFAllocatorDefault,
+                                        0));
+    CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data);
+    if (mac_data_data) {
+      CFDataGetBytes(
+          mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer);
+    }
+  }
+
+  return success;
+}
+
+bool GetMacAddress(unsigned char* buffer, size_t size) {
+  io_iterator_t primary_interface_iterator;
+  if (!FindEthernetInterfaces(&primary_interface_iterator))
+    return false;
+  bool result = GetMACAddressFromIterator(
+      primary_interface_iterator, buffer, size);
+  IOObjectRelease(primary_interface_iterator);
+  return result;
+}
+
+CFStringRef CopySerialNumber() {
+  base::mac::ScopedIOObject<io_service_t> expert_device(
+      IOServiceGetMatchingService(kIOMasterPortDefault,
+          IOServiceMatching("IOPlatformExpertDevice")));
+  if (!expert_device)
+    return NULL;
+
+  base::ScopedCFTypeRef<CFTypeRef> serial_number(
+      IORegistryEntryCreateCFProperty(expert_device,
+                                      CFSTR(kIOPlatformSerialNumberKey),
+                                      kCFAllocatorDefault,
+                                      0));
+  CFStringRef serial_number_cfstring =
+      base::mac::CFCast<CFStringRef>(serial_number.release());
+  if (!serial_number_cfstring)
+    return NULL;
+
+  return serial_number_cfstring;
+}
+
+}  // namespace
+
+bool GetRawMachineId(std::vector<uint8_t>* data, int* more_data) {
+  uint8_t mac_address[kIOEthernetAddressSize];
+
+  std::string id;
+  if (GetMacAddress(mac_address, sizeof(mac_address))) {
+    id += "mac:";
+    static const char hex[] =
+      { '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+    for (int i = 0; i < kIOEthernetAddressSize; ++i) {
+      uint8_t byte = mac_address[i];
+      id += hex[byte >> 4];
+      id += hex[byte & 0xF];
+    }
+  }
+
+  // A MAC address is enough to uniquely identify a machine, but it's only 6
+  // bytes, 3 of which are manufacturer-determined. To make brute-forcing the
+  // SHA1 of this harder, also append the system's serial number.
+  CFStringRef serial = CopySerialNumber();
+  if (serial) {
+    if (!id.empty()) {
+      id += ' ';
+    }
+    id += "serial:";
+    id += base::SysCFStringRefToUTF8(serial);
+    CFRelease(serial);
+  }
+
+  // Get the contents of the string 'id' as a bunch of bytes.
+  data->assign(&id[0], &id[id.size()]);
+
+  // On windows, this is set to the volume id. Since it's not scrambled before
+  // being sent, just set it to 1.
+  *more_data = 1;
+  return true;
+}
+
+}  // namespace rlz_lib
--- a/dom/media/gmp/rlz/moz.build
+++ b/dom/media/gmp/rlz/moz.build
@@ -8,14 +8,26 @@
 # Chromium IPC's headers used in the moz.build of the parent file.
 
 Library('rlz')
 FORCE_STATIC_LIB = True
 USE_STATIC_LIBS = True
 
 UNIFIED_SOURCES += [
     'lib/string_utils.cc',
-    'win/lib/machine_id_win.cc',
 ]
 
+if CONFIG['OS_TARGET'] == 'WINNT':
+    UNIFIED_SOURCES += [
+        'win/lib/machine_id_win.cc',
+    ]
+
+if CONFIG['OS_TARGET'] == 'Darwin':
+    UNIFIED_SOURCES += [
+        'mac/lib/machine_id_mac.cc',
+    ]
+    OS_LIBS += [
+        '-framework IOKit',
+    ]
+
 LOCAL_INCLUDES += [
     '..',
 ]
--- a/dom/media/gmp/rlz/win/lib/machine_id_win.cc
+++ b/dom/media/gmp/rlz/win/lib/machine_id_win.cc
@@ -1,18 +1,18 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 // Use of this source code is governed by an Apache-style license that can be
 // found in the COPYING file.
 
 #include <windows.h>
 #include <sddl.h>  // For ConvertSidToStringSidW.
 #include <string>
+#include <vector>
 
 #include "base/memory/scoped_ptr.h"
-#include "base/string16.h"
 #include "rlz/lib/assert.h"
 
 namespace rlz_lib {
 
 namespace {
 
 bool GetSystemVolumeSerialNumber(int* number) {
   if (!number)
@@ -62,17 +62,17 @@ bool GetComputerSid(const wchar_t* accou
     success = ::LookupAccountNameW(NULL, account_name, sid, &sid_dword_size,
                                    domain_buffer.get(), &domain_size,
                                    &sid_name_use);
   }
 
   return success != FALSE;
 }
 
-std::wstring ConvertSidToString(SID* sid) {
+std::vector<uint8_t> ConvertSidToBytes(SID* sid) {
   std::wstring sid_string;
 #if _WIN32_WINNT >= 0x500
   wchar_t* sid_buffer = NULL;
   if (ConvertSidToStringSidW(sid, &sid_buffer)) {
     sid_string = sid_buffer;
     LocalFree(sid_buffer);
   }
 #else
@@ -93,34 +93,37 @@ std::wstring ConvertSidToString(SID* sid
     base::SStringPrintf(&sid_string, L"S-%d-%lu", SID_REVISION, authority);
   }
 
   int sub_auth_count = *::GetSidSubAuthorityCount(sid);
   for(int i = 0; i < sub_auth_count; ++i)
     base::StringAppendF(&sid_string, L"-%lu", *::GetSidSubAuthority(sid, i));
 #endif
 
-  return sid_string;
+  // Get the contents of the string as a bunch of bytes.
+  return std::vector<uint8_t>(
+           reinterpret_cast<uint8_t*>(&sid_string[0]),
+           reinterpret_cast<uint8_t*>(&sid_string[sid_string.size()]));
 }
 
 }  // namespace
 
-bool GetRawMachineId(string16* sid_string, int* volume_id) {
+bool GetRawMachineId(std::vector<uint8_t>* sid_bytes, int* volume_id) {
   // Calculate the Windows SID.
 
   wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {0};
   DWORD size = arraysize(computer_name);
 
   if (!GetComputerNameW(computer_name, &size)) {
     return false;
   }
   char sid_buffer[SECURITY_MAX_SID_SIZE];
   SID* sid = reinterpret_cast<SID*>(sid_buffer);
   if (GetComputerSid(computer_name, sid, SECURITY_MAX_SID_SIZE)) {
-    *sid_string = ConvertSidToString(sid);
+    *sid_bytes = ConvertSidToBytes(sid);
   }
 
   // Get the system drive volume serial number.
   *volume_id = 0;
   if (!GetSystemVolumeSerialNumber(volume_id)) {
     ASSERT_STRING("GetMachineId: Failed to retrieve volume serial number");
     *volume_id = 0;
   }
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -138,18 +138,22 @@ DecodedAudioDataSink::Init()
 int64_t
 DecodedAudioDataSink::GetPosition()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   int64_t pos;
   if (mAudioStream &&
       (pos = mAudioStream->GetPosition()) >= 0) {
+    NS_ASSERTION(pos >= mLastGoodPosition,
+                 "AudioStream position shouldn't go backward");
     // Update the last good position when we got a good one.
-    mLastGoodPosition = pos;
+    if (pos >= mLastGoodPosition) {
+      mLastGoodPosition = pos;
+    }
   }
 
   return mStartTime + mLastGoodPosition;
 }
 
 bool
 DecodedAudioDataSink::HasUnplayedFrames()
 {
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
@@ -150,49 +150,30 @@ GonkAudioDecoderManager::CreateAudioData
                        frames,
                        mAudioChannels,
                        OmxCopy(data+dataOffset,
                                size,
                                mAudioChannels));
   return NS_OK;
 }
 
-class AutoReleaseAudioBuffer
-{
-public:
-  AutoReleaseAudioBuffer(MediaBuffer* aBuffer, MediaCodecProxy* aCodecProxy)
-    : mAudioBuffer(aBuffer)
-    , mCodecProxy(aCodecProxy)
-  {}
-
-  ~AutoReleaseAudioBuffer()
-  {
-    if (mAudioBuffer) {
-      mCodecProxy->ReleaseMediaBuffer(mAudioBuffer);
-    }
-  }
-private:
-  MediaBuffer* mAudioBuffer;
-  sp<MediaCodecProxy> mCodecProxy;
-};
-
 nsresult
 GonkAudioDecoderManager::Output(int64_t aStreamOffset,
                                 RefPtr<MediaData>& aOutData)
 {
   aOutData = nullptr;
   if (mAudioQueue.GetSize() > 0) {
     aOutData = mAudioQueue.PopFront();
     return mAudioQueue.AtEndOfStream() ? NS_ERROR_ABORT : NS_OK;
   }
 
   status_t err;
   MediaBuffer* audioBuffer = nullptr;
   err = mDecoder->Output(&audioBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
-  AutoReleaseAudioBuffer a(audioBuffer, mDecoder.get());
+  AutoReleaseMediaBuffer a(audioBuffer, mDecoder.get());
 
   switch (err) {
     case OK:
     {
       nsresult rv = CreateAudioData(audioBuffer, aStreamOffset);
       NS_ENSURE_SUCCESS(rv, rv);
       break;
     }
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
@@ -108,18 +108,16 @@ GonkDecoderManager::Flush()
     return NS_OK;
   }
 
   {
     MutexAutoLock lock(mMutex);
     mQueuedSamples.Clear();
   }
 
-  mLastTime = 0;
-
   MonitorAutoLock lock(mFlushMonitor);
   mIsFlushing = true;
   sp<AMessage> flush = new AMessage(kNotifyProcessFlush, id());
   flush->post();
   while (mIsFlushing) {
     lock.Wait();
   }
   return NS_OK;
@@ -134,18 +132,18 @@ GonkDecoderManager::Shutdown()
     mDecoder = nullptr;
   }
 
   mInitPromise.RejectIfExists(DecoderFailureReason::CANCELED, __func__);
 
   return NS_OK;
 }
 
-bool
-GonkDecoderManager::HasQueuedSample()
+size_t
+GonkDecoderManager::NumQueuedSamples()
 {
   MutexAutoLock lock(mMutex);
   return mQueuedSamples.Length();
 }
 
 void
 GonkDecoderManager::ProcessInput(bool aEndOfStream)
 {
@@ -156,51 +154,48 @@ GonkDecoderManager::ProcessInput(bool aE
     }
 
     if (mToDo.get() == nullptr) {
       mToDo = new AMessage(kNotifyDecoderActivity, id());
       if (aEndOfStream) {
         mToDo->setInt32("input-eos", 1);
       }
       mDecoder->requestActivityNotification(mToDo);
+    } else if (aEndOfStream) {
+      mToDo->setInt32("input-eos", 1);
     }
   } else {
     GMDD_LOG("input processed: error#%d", rv);
     mDecodeCallback->Error();
   }
 }
 
 void
 GonkDecoderManager::ProcessFlush()
 {
+  mLastTime = 0;
   MonitorAutoLock lock(mFlushMonitor);
   mWaitOutput.Clear();
   if (mDecoder->flush() != OK) {
     GMDD_LOG("flush error");
     mDecodeCallback->Error();
   }
   mIsFlushing = false;
   lock.NotifyAll();
 }
 
 void
 GonkDecoderManager::ProcessToDo(bool aEndOfStream)
 {
   MOZ_ASSERT(mToDo.get() != nullptr);
   mToDo.clear();
 
-  if (HasQueuedSample()) {
-    status_t pendingInput = ProcessQueuedSamples();
-    if (pendingInput < 0) {
-      mDecodeCallback->Error();
-      return;
-    }
-    if (!aEndOfStream && pendingInput <= MIN_QUEUED_SAMPLES) {
-      mDecodeCallback->InputExhausted();
-    }
+  if (NumQueuedSamples() > 0 && ProcessQueuedSamples() < 0) {
+    mDecodeCallback->Error();
+    return;
   }
 
   nsresult rv = NS_OK;
   while (mWaitOutput.Length() > 0) {
     RefPtr<MediaData> output;
     int64_t offset = mWaitOutput.ElementAt(0);
     rv = Output(offset, output);
     if (rv == NS_OK) {
@@ -222,18 +217,28 @@ GonkDecoderManager::ProcessToDo(bool aEn
     } else if (rv == NS_ERROR_NOT_AVAILABLE) {
       break;
     } else {
       mDecodeCallback->Error();
       return;
     }
   }
 
-  if (HasQueuedSample() || mWaitOutput.Length() > 0) {
+  if (!aEndOfStream && NumQueuedSamples() <= MIN_QUEUED_SAMPLES) {
+    mDecodeCallback->InputExhausted();
+    // No need to shedule todo task this time because InputExhausted() will
+    // cause Input() to be invoked and do it for us.
+    return;
+  }
+
+  if (NumQueuedSamples() || mWaitOutput.Length() > 0) {
     mToDo = new AMessage(kNotifyDecoderActivity, id());
+    if (aEndOfStream) {
+      mToDo->setInt32("input-eos", 1);
+    }
     mDecoder->requestActivityNotification(mToDo);
   }
 }
 
 void
 GonkDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
 {
   switch (aMessage->what()) {
@@ -260,18 +265,17 @@ GonkDecoderManager::onMessageReceived(co
         break;
       }
   }
 }
 
 GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
                                            FlushableTaskQueue* aTaskQueue,
                                            MediaDataDecoderCallback* aCallback)
-  : mTaskQueue(aTaskQueue)
-  , mManager(aManager)
+  : mManager(aManager)
 {
   MOZ_COUNT_CTOR(GonkMediaDataDecoder);
   mManager->SetDecodeCallback(aCallback);
 }
 
 GonkMediaDataDecoder::~GonkMediaDataDecoder()
 {
   MOZ_COUNT_DTOR(GonkMediaDataDecoder);
@@ -300,21 +304,16 @@ GonkMediaDataDecoder::Input(MediaRawData
 {
   mManager->Input(aSample);
   return NS_OK;
 }
 
 nsresult
 GonkMediaDataDecoder::Flush()
 {
-  // Flush the input task queue. This cancels all pending Decode() calls.
-  // Note this blocks until the task queue finishes its current job, if
-  // it's executing at all. Note the MP4Reader ignores all output while
-  // flushing.
-  mTaskQueue->Flush();
   return mManager->Flush();
 }
 
 nsresult
 GonkMediaDataDecoder::Drain()
 {
   mManager->Input(nullptr);
   return NS_OK;
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.h
@@ -6,16 +6,17 @@
 
 #if !defined(GonkMediaDataDecoder_h_)
 #define GonkMediaDataDecoder_h_
 #include "PlatformDecoderModule.h"
 #include <stagefright/foundation/AHandler.h>
 
 namespace android {
 struct ALooper;
+class MediaBuffer;
 class MediaCodecProxy;
 } // namespace android
 
 namespace mozilla {
 class MediaRawData;
 
 // Manage the data flow from inputting encoded data and outputting decode data.
 class GonkDecoderManager : public android::AHandler {
@@ -33,18 +34,18 @@ public:
   nsresult Input(MediaRawData* aSample);
 
   // Flush the queued sample.
   virtual nsresult Flush();
 
   // Shutdown decoder and rejects the init promise.
   virtual nsresult Shutdown();
 
-  // True if sample is queued.
-  bool HasQueuedSample();
+  // How many samples are waiting for processing.
+  size_t NumQueuedSamples();
 
   // Set callback for decoder events, such as requesting more input,
   // returning output, or reporting error.
   void SetDecodeCallback(MediaDataDecoderCallback* aCallback)
   {
     mDecodeCallback = aCallback;
   }
 
@@ -116,16 +117,44 @@ protected:
   android::sp<android::AMessage> mToDo;
 
   // Stores the offset of every output that needs to be read from mDecoder.
   nsTArray<int64_t> mWaitOutput;
 
   MediaDataDecoderCallback* mDecodeCallback; // Reports decoder output or error.
 };
 
+class AutoReleaseMediaBuffer
+{
+public:
+  AutoReleaseMediaBuffer(android::MediaBuffer* aBuffer, android::MediaCodecProxy* aCodec)
+    : mBuffer(aBuffer)
+    , mCodec(aCodec)
+  {}
+
+  ~AutoReleaseMediaBuffer()
+  {
+    MOZ_ASSERT(mCodec.get());
+    if (mBuffer) {
+      mCodec->ReleaseMediaBuffer(mBuffer);
+    }
+  }
+
+  android::MediaBuffer* forget()
+  {
+    android::MediaBuffer* tmp = mBuffer;
+    mBuffer = nullptr;
+    return tmp;
+  }
+
+private:
+  android::MediaBuffer* mBuffer;
+  android::sp<android::MediaCodecProxy> mCodec;
+};
+
 // Samples are decoded using the GonkDecoder (MediaCodec)
 // created by the GonkDecoderManager. This class implements
 // the higher-level logic that drives mapping the Gonk to the async
 // MediaDataDecoder interface. The specifics of decoding the exact stream
 // type are handled by GonkDecoderManager and the GonkDecoder it creates.
 class GonkMediaDataDecoder : public MediaDataDecoder {
 public:
   GonkMediaDataDecoder(GonkDecoderManager* aDecoderManager,
@@ -140,16 +169,15 @@ public:
 
   nsresult Flush() override;
 
   nsresult Drain() override;
 
   nsresult Shutdown() override;
 
 private:
-  RefPtr<FlushableTaskQueue> mTaskQueue;
 
   android::sp<GonkDecoderManager> mManager;
 };
 
 } // namespace mozilla
 
 #endif // GonkMediaDataDecoder_h_
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -9,24 +9,26 @@
 #include <ICrypto.h>
 #include "GonkVideoDecoderManager.h"
 #include "MediaDecoderReader.h"
 #include "ImageContainer.h"
 #include "VideoUtils.h"
 #include "nsThreadUtils.h"
 #include "Layers.h"
 #include "mozilla/Logging.h"
-#include "stagefright/MediaBuffer.h"
-#include "stagefright/MetaData.h"
-#include "stagefright/MediaErrors.h"
+#include <stagefright/MediaBuffer.h>
+#include <stagefright/MetaData.h>
+#include <stagefright/MediaErrors.h>
 #include <stagefright/foundation/AString.h>
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include <cutils/properties.h>
 
 #define CODECCONFIG_TIMEOUT_US 10000LL
 #define READ_OUTPUT_BUFFER_TIMEOUT_US  0LL
 
 #include <android/log.h>
 #define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
 
@@ -40,16 +42,17 @@ namespace mozilla {
 
 GonkVideoDecoderManager::GonkVideoDecoderManager(
   mozilla::layers::ImageContainer* aImageContainer,
   const VideoInfo& aConfig)
   : mImageContainer(aImageContainer)
   , mColorConverterBufferSize(0)
   , mNativeWindow(nullptr)
   , mPendingReleaseItemsLock("GonkVideoDecoderManager::mPendingReleaseItemsLock")
+  , mNeedsCopyBuffer(false)
 {
   MOZ_COUNT_CTOR(GonkVideoDecoderManager);
   mMimeType = aConfig.mMimeType;
   mVideoWidth  = aConfig.mDisplay.width;
   mVideoHeight = aConfig.mDisplay.height;
   mDisplayWidth = aConfig.mDisplay.width;
   mDisplayHeight = aConfig.mDisplay.height;
   mInfo.mVideo = aConfig;
@@ -73,16 +76,18 @@ GonkVideoDecoderManager::Shutdown()
 {
   mVideoCodecRequest.DisconnectIfExists();
   return GonkDecoderManager::Shutdown();
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 GonkVideoDecoderManager::Init()
 {
+  mNeedsCopyBuffer = false;
+
   nsIntSize displaySize(mDisplayWidth, mDisplayHeight);
   nsIntRect pictureRect(0, 0, mVideoWidth, mVideoHeight);
 
   uint32_t maxWidth, maxHeight;
   char propValue[PROPERTY_VALUE_MAX];
   property_get("ro.moz.omx.hw.max_width", propValue, "-1");
   maxWidth = -1 == atoi(propValue) ? MAX_VIDEO_WIDTH : atoi(propValue);
   property_get("ro.moz.omx.hw.max_height", propValue, "-1");
@@ -119,173 +124,350 @@ GonkVideoDecoderManager::Init()
       [self] (bool) -> void {
         self->mVideoCodecRequest.Complete();
         self->codecReserved();
       }, [self] (bool) -> void {
         self->mVideoCodecRequest.Complete();
         self->codecCanceled();
       }));
   mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false, mVideoListener);
-  mDecoder->AsyncAllocateVideoMediaCodec();
 
   uint32_t capability = MediaCodecProxy::kEmptyCapability;
   if (mDecoder->getCapability(&capability) == OK && (capability &
       MediaCodecProxy::kCanExposeGraphicBuffer)) {
     mNativeWindow = new GonkNativeWindow();
   }
 
+  mDecoder->AsyncAllocateVideoMediaCodec();
+
   return p;
 }
 
 nsresult
-GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
+GonkVideoDecoderManager::CreateVideoData(MediaBuffer* aBuffer,
+                                         int64_t aStreamOffset,
+                                         VideoData **v)
 {
   *v = nullptr;
   RefPtr<VideoData> data;
   int64_t timeUs;
   int32_t keyFrame;
 
-  if (mVideoBuffer == nullptr) {
+  if (aBuffer == nullptr) {
     GVDM_LOG("Video Buffer is not valid!");
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
-    ReleaseVideoBuffer();
+  AutoReleaseMediaBuffer autoRelease(aBuffer, mDecoder.get());
+
+  if (!aBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
     GVDM_LOG("Decoder did not return frame time");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mLastTime > timeUs) {
-    ReleaseVideoBuffer();
     GVDM_LOG("Output decoded sample time is revert. time=%lld", timeUs);
     return NS_ERROR_NOT_AVAILABLE;
   }
   mLastTime = timeUs;
 
-  if (mVideoBuffer->range_length() == 0) {
+  if (aBuffer->range_length() == 0) {
     // Some decoders may return spurious empty buffers that we just want to ignore
     // quoted from Android's AwesomePlayer.cpp
-    ReleaseVideoBuffer();
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
+  if (!aBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
     keyFrame = 0;
   }
 
   gfx::IntRect picture = mPicture;
   if (mFrameInfo.mWidth != mInitialFrame.width ||
       mFrameInfo.mHeight != mInitialFrame.height) {
 
     // Frame size is different from what the container reports. This is legal,
     // and we will preserve the ratio of the crop rectangle as it
     // was reported relative to the picture size reported by the container.
     picture.x = (mPicture.x * mFrameInfo.mWidth) / mInitialFrame.width;
     picture.y = (mPicture.y * mFrameInfo.mHeight) / mInitialFrame.height;
     picture.width = (mFrameInfo.mWidth * mPicture.width) / mInitialFrame.width;
     picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
   }
 
-  RefPtr<mozilla::layers::TextureClient> textureClient;
-
-  if ((mVideoBuffer->graphicBuffer().get())) {
-    textureClient = mNativeWindow->getTextureClientFromBuffer(mVideoBuffer->graphicBuffer().get());
+  if (aBuffer->graphicBuffer().get()) {
+    data = CreateVideoDataFromGraphicBuffer(aBuffer, picture);
+    if (data && !mNeedsCopyBuffer) {
+      // RecycleCallback() will be responsible for release the buffer.
+      autoRelease.forget();
+    }
+    mNeedsCopyBuffer = false;
+  } else {
+    data = CreateVideoDataFromDataBuffer(aBuffer, picture);
   }
 
-  if (textureClient) {
-    GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get());
-    grallocClient->SetMediaBuffer(mVideoBuffer);
-    textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
-
-    data = VideoData::Create(mInfo.mVideo,
-                             mImageContainer,
-                             aStreamOffset,
-                             timeUs,
-                             1, // No way to pass sample duration from muxer to
-                                // OMX codec, so we hardcode the duration here.
-                             textureClient,
-                             keyFrame,
-                             -1,
-                             picture);
-  } else {
-    if (!mVideoBuffer->data()) {
-      GVDM_LOG("No data in Video Buffer!");
-      return NS_ERROR_UNEXPECTED;
-    }
-    uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
-    int32_t stride = mFrameInfo.mStride;
-    int32_t slice_height = mFrameInfo.mSliceHeight;
-
-    // Converts to OMX_COLOR_FormatYUV420Planar
-    if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
-      ARect crop;
-      crop.top = 0;
-      crop.bottom = mFrameInfo.mHeight;
-      crop.left = 0;
-      crop.right = mFrameInfo.mWidth;
-      yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
-      if (mColorConverter.convertDecoderOutputToI420(mVideoBuffer->data(),
-          mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
-          ReleaseVideoBuffer();
-          GVDM_LOG("Color conversion failed!");
-          return NS_ERROR_UNEXPECTED;
-      }
-        stride = mFrameInfo.mWidth;
-        slice_height = mFrameInfo.mHeight;
-    }
-
-    size_t yuv420p_y_size = stride * slice_height;
-    size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
-    uint8_t *yuv420p_y = yuv420p_buffer;
-    uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
-    uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
-
-    // This is the approximate byte position in the stream.
-    int64_t pos = aStreamOffset;
-
-    VideoData::YCbCrBuffer b;
-    b.mPlanes[0].mData = yuv420p_y;
-    b.mPlanes[0].mWidth = mFrameInfo.mWidth;
-    b.mPlanes[0].mHeight = mFrameInfo.mHeight;
-    b.mPlanes[0].mStride = stride;
-    b.mPlanes[0].mOffset = 0;
-    b.mPlanes[0].mSkip = 0;
-
-    b.mPlanes[1].mData = yuv420p_u;
-    b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
-    b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
-    b.mPlanes[1].mStride = (stride + 1) / 2;
-    b.mPlanes[1].mOffset = 0;
-    b.mPlanes[1].mSkip = 0;
-
-    b.mPlanes[2].mData = yuv420p_v;
-    b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
-    b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
-    b.mPlanes[2].mStride = (stride + 1) / 2;
-    b.mPlanes[2].mOffset = 0;
-    b.mPlanes[2].mSkip = 0;
-
-    data = VideoData::Create(
-        mInfo.mVideo,
-        mImageContainer,
-        pos,
-        timeUs,
-        1, // We don't know the duration.
-        b,
-        keyFrame,
-        -1,
-        picture);
-    ReleaseVideoBuffer();
+  if (!data) {
+    return NS_ERROR_UNEXPECTED;
   }
+  // Fill necessary info.
+  data->mOffset = aStreamOffset;
+  data->mTime = timeUs;
+  data->mKeyframe = keyFrame;
 
   data.forget(v);
   return NS_OK;
 }
 
+// Copy pixels from one planar YUV to another.
+static void
+CopyYUV(PlanarYCbCrData& aSource, PlanarYCbCrData& aDestination)
+{
+  // Fill Y plane.
+  uint8_t* srcY = aSource.mYChannel;
+  gfx::IntSize ySize = aSource.mYSize;
+  uint8_t* destY = aDestination.mYChannel;
+  // Y plane.
+  for (int i = 0; i < ySize.height; i++) {
+    memcpy(destY, srcY, ySize.width);
+    srcY += aSource.mYStride;
+    destY += aDestination.mYStride;
+  }
+
+  // Fill UV plane.
+  // Line start
+  uint8_t* srcU = aSource.mCbChannel;
+  uint8_t* srcV = aSource.mCrChannel;
+  uint8_t* destU = aDestination.mCbChannel;
+  uint8_t* destV = aDestination.mCrChannel;
+
+  gfx::IntSize uvSize = aSource.mCbCrSize;
+  for (int i = 0; i < uvSize.height; i++) {
+    uint8_t* su = srcU;
+    uint8_t* sv = srcV;
+    uint8_t* du = destU;
+    uint8_t* dv =destV;
+    for (int j = 0; j < uvSize.width; j++) {
+      *du++ = *su++;
+      *dv++ = *sv++;
+      // Move to next pixel.
+      su += aSource.mCbSkip;
+      sv += aSource.mCrSkip;
+      du += aDestination.mCbSkip;
+      dv += aDestination.mCrSkip;
+    }
+    // Move to next line.
+    srcU += aSource.mCbCrStride;
+    srcV += aSource.mCbCrStride;
+    destU += aDestination.mCbCrStride;
+    destV += aDestination.mCbCrStride;
+  }
+}
+
+inline static int
+Align(int aX, int aAlign)
+{
+  return (aX + aAlign - 1) & ~(aAlign - 1);
+}
+
+static void
+CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination, gfx::IntRect& aPicture)
+{
+  void* srcPtr = nullptr;
+  aSource->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &srcPtr);
+  void* destPtr = nullptr;
+  aDestination->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destPtr);
+  MOZ_ASSERT(srcPtr && destPtr);
+
+  // Build PlanarYCbCrData for source buffer.
+  PlanarYCbCrData srcData;
+  switch (aSource->getPixelFormat()) {
+    case HAL_PIXEL_FORMAT_YV12:
+      // Android YV12 format is defined in system/core/include/system/graphics.h
+      srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
+      srcData.mYSkip = 0;
+      srcData.mYSize.width = aSource->getWidth();
+      srcData.mYSize.height = aSource->getHeight();
+      srcData.mYStride = aSource->getStride();
+      // 4:2:0.
+      srcData.mCbCrSize.width = srcData.mYSize.width / 2;
+      srcData.mCbCrSize.height = srcData.mYSize.height / 2;
+      srcData.mCrChannel = srcData.mYChannel + (srcData.mYStride * srcData.mYSize.height);
+      // Aligned to 16 bytes boundary.
+      srcData.mCbCrStride = Align(srcData.mYStride / 2, 16);
+      srcData.mCrSkip = 0;
+      srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height);
+      srcData.mCbSkip = 0;
+      break;
+    case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+      // Venus formats are doucmented in kernel/include/media/msm_media_info.h:
+      srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
+      srcData.mYSkip = 0;
+      srcData.mYSize.width = aSource->getWidth();
+      srcData.mYSize.height = aSource->getHeight();
+      // - Y & UV Width aligned to 128
+      srcData.mYStride = aSource->getStride();
+      srcData.mCbCrSize.width = (srcData.mYSize.width + 1) / 2;
+      srcData.mCbCrSize.height = (srcData.mYSize.height + 1) / 2;
+      // - Y height aligned to 32
+      srcData.mCbChannel = srcData.mYChannel + (srcData.mYStride * Align(srcData.mYSize.height, 32));
+      // Interleaved VU plane.
+      srcData.mCbSkip = 1;
+      srcData.mCrChannel = srcData.mCbChannel + 1;
+      srcData.mCrSkip = 1;
+      srcData.mCbCrStride = srcData.mYStride;
+      break;
+    default:
+      NS_ERROR("Unsupported input gralloc image type. Should never be here.");
+  }
+  // Build PlanarYCbCrData for destination buffer.
+  PlanarYCbCrData destData;
+  destData.mYChannel = static_cast<uint8_t*>(destPtr);
+  destData.mYSkip = 0;
+  destData.mYSize.width = aDestination->getWidth();
+  destData.mYSize.height = aDestination->getHeight();
+  destData.mYStride = aDestination->getStride();
+  // 4:2:0.
+  destData.mCbCrSize.width = destData.mYSize.width / 2;
+  destData.mCbCrSize.height = destData.mYSize.height / 2;
+  destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
+  // Aligned to 16 bytes boundary.
+  destData.mCbCrStride = Align(destData.mYStride / 2, 16);
+  destData.mCrSkip = 0;
+  destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
+  destData.mCbSkip = 0;
+
+  CopyYUV(srcData, destData);
+
+  aSource->unlock();
+  aDestination->unlock();
+}
+
+already_AddRefed<VideoData>
+GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource,
+                                                          gfx::IntRect& aPicture)
+{
+  sp<GraphicBuffer> srcBuffer(aSource->graphicBuffer());
+  RefPtr<TextureClient> textureClient;
+
+  if (mNeedsCopyBuffer) {
+    // Copy buffer contents for bug 1199809.
+    if (!mCopyAllocator) {
+      mCopyAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
+    }
+    if (!mCopyAllocator) {
+      GVDM_LOG("Create buffer allocator failed!");
+      return nullptr;
+    }
+
+    gfx::IntSize size(Align(srcBuffer->getWidth(), 2) , Align(srcBuffer->getHeight(), 2));
+    textureClient =
+      mCopyAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV, size,
+                                      BackendSelector::Content,
+                                      TextureFlags::DEFAULT,
+                                      ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
+    if (!textureClient) {
+      GVDM_LOG("Copy buffer allocation failed!");
+      return nullptr;
+    }
+    // Update size to match buffer's.
+    aPicture.width = size.width;
+    aPicture.height = size.height;
+
+    sp<GraphicBuffer> destBuffer =
+      static_cast<GrallocTextureClientOGL*>(textureClient.get())->GetGraphicBuffer();
+
+    CopyGraphicBuffer(srcBuffer, destBuffer, aPicture);
+  } else {
+    textureClient = mNativeWindow->getTextureClientFromBuffer(srcBuffer.get());
+    textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
+    GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get());
+    grallocClient->SetMediaBuffer(aSource);
+  }
+
+  RefPtr<VideoData> data = VideoData::Create(mInfo.mVideo,
+                                             mImageContainer,
+                                             0, // Filled later by caller.
+                                             0, // Filled later by caller.
+                                             1, // No way to pass sample duration from muxer to
+                                                // OMX codec, so we hardcode the duration here.
+                                             textureClient,
+                                             false, // Filled later by caller.
+                                             -1,
+                                             aPicture);
+  return data.forget();
+}
+
+already_AddRefed<VideoData>
+GonkVideoDecoderManager::CreateVideoDataFromDataBuffer(MediaBuffer* aSource, gfx::IntRect& aPicture)
+{
+  if (!aSource->data()) {
+    GVDM_LOG("No data in Video Buffer!");
+    return nullptr;
+  }
+  uint8_t *yuv420p_buffer = (uint8_t *)aSource->data();
+  int32_t stride = mFrameInfo.mStride;
+  int32_t slice_height = mFrameInfo.mSliceHeight;
+
+  // Converts to OMX_COLOR_FormatYUV420Planar
+  if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
+    ARect crop;
+    crop.top = 0;
+    crop.bottom = mFrameInfo.mHeight;
+    crop.left = 0;
+    crop.right = mFrameInfo.mWidth;
+    yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
+    if (mColorConverter.convertDecoderOutputToI420(aSource->data(),
+        mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
+        GVDM_LOG("Color conversion failed!");
+        return nullptr;
+    }
+      stride = mFrameInfo.mWidth;
+      slice_height = mFrameInfo.mHeight;
+  }
+
+  size_t yuv420p_y_size = stride * slice_height;
+  size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
+  uint8_t *yuv420p_y = yuv420p_buffer;
+  uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
+  uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
+
+  VideoData::YCbCrBuffer b;
+  b.mPlanes[0].mData = yuv420p_y;
+  b.mPlanes[0].mWidth = mFrameInfo.mWidth;
+  b.mPlanes[0].mHeight = mFrameInfo.mHeight;
+  b.mPlanes[0].mStride = stride;
+  b.mPlanes[0].mOffset = 0;
+  b.mPlanes[0].mSkip = 0;
+
+  b.mPlanes[1].mData = yuv420p_u;
+  b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
+  b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
+  b.mPlanes[1].mStride = (stride + 1) / 2;
+  b.mPlanes[1].mOffset = 0;
+  b.mPlanes[1].mSkip = 0;
+
+  b.mPlanes[2].mData = yuv420p_v;
+  b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
+  b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
+  b.mPlanes[2].mStride = (stride + 1) / 2;
+  b.mPlanes[2].mOffset = 0;
+  b.mPlanes[2].mSkip = 0;
+
+  RefPtr<VideoData> data = VideoData::Create(mInfo.mVideo,
+                                             mImageContainer,
+                                             0, // Filled later by caller.
+                                             0, // Filled later by caller.
+                                             1, // We don't know the duration.
+                                             b,
+                                             0, // Filled later by caller.
+                                             -1,
+                                             aPicture);
+
+  return data.forget();
+}
+
 bool
 GonkVideoDecoderManager::SetVideoFormat()
 {
   // read video metadata from MediaCodec
   sp<AMessage> codecFormat;
   if (mDecoder->getOutputFormat(&codecFormat) == OK) {
     AString mime;
     int32_t width = 0;
@@ -330,23 +512,24 @@ GonkVideoDecoderManager::Output(int64_t 
                                 RefPtr<MediaData>& aOutData)
 {
   aOutData = nullptr;
   status_t err;
   if (mDecoder == nullptr) {
     GVDM_LOG("Decoder is not inited");
     return NS_ERROR_UNEXPECTED;
   }
-  err = mDecoder->Output(&mVideoBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
+  MediaBuffer* outputBuffer = nullptr;
+  err = mDecoder->Output(&outputBuffer, READ_OUTPUT_BUFFER_TIMEOUT_US);
 
   switch (err) {
     case OK:
     {
       RefPtr<VideoData> data;
-      nsresult rv = CreateVideoData(aStreamOffset, getter_AddRefs(data));
+      nsresult rv = CreateVideoData(outputBuffer, aStreamOffset, getter_AddRefs(data));
       if (rv == NS_ERROR_NOT_AVAILABLE) {
         // Decoder outputs a empty video buffer, try again
         return NS_ERROR_NOT_AVAILABLE;
       } else if (rv != NS_OK || data == nullptr) {
         GVDM_LOG("Failed to create VideoData");
         return NS_ERROR_UNEXPECTED;
       }
       aOutData = data;
@@ -373,17 +556,17 @@ GonkVideoDecoderManager::Output(int64_t 
     {
 //      GVDM_LOG("Need to try again!");
       return NS_ERROR_NOT_AVAILABLE;
     }
     case android::ERROR_END_OF_STREAM:
     {
       GVDM_LOG("Got the EOS frame!");
       RefPtr<VideoData> data;
-      nsresult rv = CreateVideoData(aStreamOffset, getter_AddRefs(data));
+      nsresult rv = CreateVideoData(outputBuffer, aStreamOffset, getter_AddRefs(data));
       if (rv == NS_ERROR_NOT_AVAILABLE) {
         // For EOS, no need to do any thing.
         return NS_ERROR_ABORT;
       }
       if (rv != NS_OK || data == nullptr) {
         GVDM_LOG("Failed to create video data");
         return NS_ERROR_UNEXPECTED;
       }
@@ -400,23 +583,16 @@ GonkVideoDecoderManager::Output(int64_t 
       GVDM_LOG("Decoder failed, err=%d", err);
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   return NS_OK;
 }
 
-void GonkVideoDecoderManager::ReleaseVideoBuffer() {
-  if (mVideoBuffer) {
-    mDecoder->ReleaseMediaBuffer(mVideoBuffer);
-    mVideoBuffer = nullptr;
-  }
-}
-
 void
 GonkVideoDecoderManager::codecReserved()
 {
   if (mInitPromise.IsEmpty()) {
     return;
   }
   GVDM_LOG("codecReserved");
   sp<AMessage> format = new AMessage;
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.h
@@ -24,16 +24,17 @@ class MediaBuffer;
 struct MOZ_EXPORT AString;
 class GonkNativeWindow;
 } // namespace android
 
 namespace mozilla {
 
 namespace layers {
 class TextureClient;
+class TextureClientRecycleAllocator;
 } // namespace mozilla::layers
 
 class GonkVideoDecoderManager : public GonkDecoderManager {
 typedef android::MediaCodecProxy MediaCodecProxy;
 typedef mozilla::layers::TextureClient TextureClient;
 
 public:
   typedef MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> MediaResourcePromise;
@@ -45,16 +46,26 @@ public:
 
   RefPtr<InitPromise> Init() override;
 
   nsresult Output(int64_t aStreamOffset,
                           RefPtr<MediaData>& aOutput) override;
 
   nsresult Shutdown() override;
 
+  // Bug 1199809: workaround to avoid sending the graphic buffer by making a
+  // copy of output buffer after calling flush(). Bug 1203859 was created to
+  // reimplementing Gonk PDM on top of OpenMax IL directly. Its buffer
+  // management will work better with Gecko and solve problems like this.
+  nsresult Flush() override
+  {
+    mNeedsCopyBuffer = true;
+    return GonkDecoderManager::Flush();
+  }
+
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
     int32_t mStride = 0;
@@ -88,18 +99,22 @@ private:
     VideoResourceListener(const VideoResourceListener &rhs) = delete;
     const VideoResourceListener &operator=(const VideoResourceListener &rhs) = delete;
 
     MozPromiseHolder<MediaResourcePromise> mVideoCodecPromise;
   };
 
   bool SetVideoFormat();
 
-  nsresult CreateVideoData(int64_t aStreamOffset, VideoData** aOutData);
-  void ReleaseVideoBuffer();
+  nsresult CreateVideoData(MediaBuffer* aBuffer, int64_t aStreamOffset, VideoData** aOutData);
+  already_AddRefed<VideoData> CreateVideoDataFromGraphicBuffer(android::MediaBuffer* aSource,
+                                                               gfx::IntRect& aPicture);
+  already_AddRefed<VideoData> CreateVideoDataFromDataBuffer(android::MediaBuffer* aSource,
+                                                            gfx::IntRect& aPicture);
+
   uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
 
   // For codec resource management
   void codecReserved();
   void codecCanceled();
 
   void ReleaseAllPendingVideoBuffers();
   void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer,
@@ -108,18 +123,17 @@ private:
   uint32_t mVideoWidth;
   uint32_t mVideoHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
 
   RefPtr<layers::ImageContainer> mImageContainer;
-
-  android::MediaBuffer* mVideoBuffer;
+  RefPtr<layers::TextureClientRecycleAllocator> mCopyAllocator;
 
   MediaInfo mInfo;
   android::sp<VideoResourceListener> mVideoListener;
   MozPromiseRequestHolder<MediaResourcePromise> mVideoCodecRequest;
   FrameInfo mFrameInfo;
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
@@ -141,13 +155,17 @@ private:
   nsTArray<ReleaseItem> mPendingReleaseItems;
 
   // The lock protects mPendingReleaseItems.
   Mutex mPendingReleaseItemsLock;
 
   // This TaskQueue should be the same one in mDecodeCallback->OnReaderTaskQueue().
   // It is for codec resource mangement, decoding task should not dispatch to it.
   RefPtr<TaskQueue> mReaderTaskQueue;
+
+  // Bug 1199809: do we need to make a copy of output buffer? Used only when
+  // the decoder outputs graphic buffers.
+  bool mNeedsCopyBuffer;
 };
 
 } // namespace mozilla
 
 #endif // GonkVideoDecoderManager_h_
--- a/dom/webidl/ActivityRequestHandler.webidl
+++ b/dom/webidl/ActivityRequestHandler.webidl
@@ -1,15 +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/. */
 
 [Pref="dom.sysmsg.enabled",
  JSImplementation="@mozilla.org/dom/activities/request-handler;1",
- ChromeConstructor(DOMString id, optional ActivityOptions options),
+ ChromeConstructor(DOMString id, optional ActivityOptions options, optional boolean returnvalue),
  ChromeOnly]
 interface ActivityRequestHandler
 {
     [UnsafeInPrerendering]
     void postResult(any result);
     [UnsafeInPrerendering]
     void postError(DOMString error);
     [Pure, Cached, Frozen]
--- a/dom/webidl/AnimationEffectReadOnly.webidl
+++ b/dom/webidl/AnimationEffectReadOnly.webidl
@@ -5,14 +5,50 @@
  *
  * The origin of this IDL file is
  * http://w3c.github.io/web-animations/#animationeffectreadonly
  *
  * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+enum FillMode {
+  "none",
+  "forwards",
+  "backwards",
+  "both",
+  "auto"
+};
+
+enum PlaybackDirection {
+  "normal",
+  "reverse",
+  "alternate",
+  "alternate-reverse"
+};
+
+dictionary AnimationEffectTimingProperties {
+  double                              delay = 0.0;
+  double                              endDelay = 0.0;
+  FillMode                            fill = "auto";
+  double                              iterationStart = 0.0;
+  unrestricted double                 iterations = 1.0;
+  (unrestricted double or DOMString)  duration = "auto";
+  PlaybackDirection                   direction = "normal";
+  DOMString                           easing = "linear";
+};
+
+dictionary ComputedTimingProperties : AnimationEffectTimingProperties {
+  unrestricted double   endTime = 0.0;
+  unrestricted double   activeDuration = 0.0;
+  double?               localTime = null;
+  unrestricted double?  progress = null;
+  unrestricted double?  currentIteration = null;
+};
+
 [Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationEffectReadOnly {
   // Not yet implemented:
   // readonly attribute AnimationEffectTimingReadOnly timing;
-  // readonly attribute ComputedTimingProperties      computedTiming;
+
+  [BinaryName="getComputedTimingAsDict"]
+  ComputedTimingProperties getComputedTiming();
 };
--- a/dom/webidl/PerformanceResourceTiming.webidl
+++ b/dom/webidl/PerformanceResourceTiming.webidl
@@ -13,23 +13,28 @@
 interface PerformanceResourceTiming : PerformanceEntry
 {
   // A string with the name of that element that initiated the load.
   // If the initiator is a CSS resource, the initiatorType attribute must return
   // the string "css".
   // If the initiator is an XMLHttpRequest object, the initiatorType attribute
   // must return the string "xmlhttprequest".
   readonly attribute DOMString initiatorType;
+  readonly attribute DOMString nextHopProtocol;
 
   readonly attribute DOMHighResTimeStamp redirectStart;
   readonly attribute DOMHighResTimeStamp redirectEnd;
   readonly attribute DOMHighResTimeStamp fetchStart;
   readonly attribute DOMHighResTimeStamp domainLookupStart;
   readonly attribute DOMHighResTimeStamp domainLookupEnd;
   readonly attribute DOMHighResTimeStamp connectStart;
   readonly attribute DOMHighResTimeStamp connectEnd;
   readonly attribute DOMHighResTimeStamp secureConnectionStart;
   readonly attribute DOMHighResTimeStamp requestStart;
   readonly attribute DOMHighResTimeStamp responseStart;
   readonly attribute DOMHighResTimeStamp responseEnd;
 
+  readonly attribute unsigned long long transferSize;
+  readonly attribute unsigned long long encodedBodySize;
+  readonly attribute unsigned long long decodedBodySize;
+
   jsonifier;
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -299,17 +299,16 @@ private:
   ~TeardownRunnable() {}
 
   RefPtr<ServiceWorkerManagerChild> mActor;
 };
 
 } // namespace
 
 NS_IMPL_ISUPPORTS0(ServiceWorkerJob)
-NS_IMPL_ISUPPORTS0(ServiceWorkerRegistrationInfo)
 
 void
 ServiceWorkerJob::Done(nsresult aStatus)
 {
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
 #ifdef DEBUG
     nsAutoCString errorName;
     GetErrorName(aStatus, errorName);
@@ -378,16 +377,72 @@ ServiceWorkerRegistrationInfo::ServiceWo
 
 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.");
   }
 }
 
+NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  AssertIsOnMainThread();
+  NS_ADDREF(*aPrincipal = mPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope)
+{
+  AssertIsOnMainThread();
+  CopyUTF8toUTF16(mScope, aScope);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec)
+{
+  AssertIsOnMainThread();
+  CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetCurrentWorkerURL(nsAString& aCurrentWorkerURL)
+{
+  AssertIsOnMainThread();
+  if (mActiveWorker) {
+    CopyUTF8toUTF16(mActiveWorker->ScriptSpec(), aCurrentWorkerURL);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetActiveCacheName(nsAString& aActiveCacheName)
+{
+  AssertIsOnMainThread();
+  if (mActiveWorker) {
+    aActiveCacheName = mActiveWorker->CacheName();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerRegistrationInfo::GetWaitingCacheName(nsAString& aWaitingCacheName)
+{
+  AssertIsOnMainThread();
+  if (mWaitingWorker) {
+    aWaitingCacheName = mWaitingWorker->CacheName();
+  }
+  return NS_OK;
+}
+
 already_AddRefed<ServiceWorkerInfo>
 ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId)
 {
   RefPtr<ServiceWorkerInfo> serviceWorker;
   if (mInstallingWorker && mInstallingWorker->ID() == aId) {
     serviceWorker = mInstallingWorker;
   } else if (mWaitingWorker && mWaitingWorker->ID() == aId) {
     serviceWorker = mWaitingWorker;
@@ -3573,40 +3628,16 @@ ServiceWorkerManager::RemoveRegistration
     //convert.
     NS_WARNING("Unable to unregister serviceworker due to possible OOM");
     return;
   }
 
   mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aRegistration->mScope));
 }
 
-class ServiceWorkerDataInfo final : public nsIServiceWorkerInfo
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISERVICEWORKERINFO
-
-  static already_AddRefed<ServiceWorkerDataInfo>
-  Create(const ServiceWorkerRegistrationInfo* aData);
-
-private:
-  ServiceWorkerDataInfo()
-  {}
-
-  ~ServiceWorkerDataInfo()
-  {}
-
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsString mScope;
-  nsString mScriptSpec;
-  nsString mCurrentWorkerURL;
-  nsString mActiveCacheName;
-  nsString mWaitingCacheName;
-};
-
 void
 ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
   RemoveRegistrationInternal(aRegistration);
   MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope));
   RemoveScopeAndRegistration(aRegistration);
 }
 
@@ -3649,91 +3680,16 @@ HasRootDomain(nsIURI* aURI, const nsACSt
   }
 
   char prevChar = *(--start);
   return prevChar == '.';
 }
 
 } // namespace
 
-NS_IMPL_ISUPPORTS(ServiceWorkerDataInfo, nsIServiceWorkerInfo)
-
-/* static */ already_AddRefed<ServiceWorkerDataInfo>
-ServiceWorkerDataInfo::Create(const ServiceWorkerRegistrationInfo* aData)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aData);
-
-  RefPtr<ServiceWorkerDataInfo> info = new ServiceWorkerDataInfo();
-
-  info->mPrincipal = aData->mPrincipal;
-  CopyUTF8toUTF16(aData->mScope, info->mScope);
-  CopyUTF8toUTF16(aData->mScriptSpec, info->mScriptSpec);
-
-  if (aData->mActiveWorker) {
-    CopyUTF8toUTF16(aData->mActiveWorker->ScriptSpec(),
-                    info->mCurrentWorkerURL);
-    info->mActiveCacheName = aData->mActiveWorker->CacheName();
-  }
-
-  if (aData->mWaitingWorker) {
-    info->mWaitingCacheName = aData->mWaitingWorker->CacheName();
-  }
-
-  return info.forget();
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetPrincipal(nsIPrincipal** aPrincipal)
-{
-  AssertIsOnMainThread();
-  NS_ADDREF(*aPrincipal = mPrincipal);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetScope(nsAString& aScope)
-{
-  AssertIsOnMainThread();
-  aScope = mScope;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetScriptSpec(nsAString& aScriptSpec)
-{
-  AssertIsOnMainThread();
-  aScriptSpec = mScriptSpec;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetCurrentWorkerURL(nsAString& aCurrentWorkerURL)
-{
-  AssertIsOnMainThread();
-  aCurrentWorkerURL = mCurrentWorkerURL;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetActiveCacheName(nsAString& aActiveCacheName)
-{
-  AssertIsOnMainThread();
-  aActiveCacheName = mActiveCacheName;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ServiceWorkerDataInfo::GetWaitingCacheName(nsAString& aWaitingCacheName)
-{
-  AssertIsOnMainThread();
-  aWaitingCacheName = mWaitingCacheName;
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
   if (!array) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -3743,22 +3699,17 @@ ServiceWorkerManager::GetAllRegistration
     for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
       ServiceWorkerRegistrationInfo* reg = it2.UserData();
       MOZ_ASSERT(reg);
 
       if (reg->mPendingUninstall) {
         continue;
       }
 
-      nsCOMPtr<nsIServiceWorkerInfo> info = ServiceWorkerDataInfo::Create(reg);
-      if (NS_WARN_IF(!info)) {
-        continue;
-      }
-
-      array->AppendElement(info, false);
+      array->AppendElement(reg, false);
     }
   }
 
   array.forget(aResult);
   return NS_OK;
 }
 
 // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
@@ -3922,33 +3873,16 @@ ServiceWorkerManager::RemoveAllRegistrat
         RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
         swm->ForceUnregister(data, reg);
       }
     }
   }
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::UpdateAllRegistrations()
-{
-  AssertIsOnMainThread();
-
-  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
-    for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
-      ServiceWorkerRegistrationInfo* info = it2.UserData();
-      MOZ_ASSERT(!info->mScope.IsEmpty());
-
-      SoftUpdate(info->mPrincipal, info->mScope);
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 ServiceWorkerManager::Observe(nsISupports* aSubject,
                               const char* aTopic,
                               const char16_t* aData)
 {
   if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
     MOZ_ASSERT(XRE_IsParentProcess());
     RemoveAll();
     PropagateRemoveAll();
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -46,26 +46,25 @@ namespace workers {
 class ServiceWorker;
 class ServiceWorkerClientInfo;
 class ServiceWorkerInfo;
 class ServiceWorkerJob;
 class ServiceWorkerJobQueue;
 class ServiceWorkerManagerChild;
 class ServiceWorkerPrivate;
 
-// Needs to inherit from nsISupports because NS_ProxyRelease() does not support
-// non-ISupports classes.
-class ServiceWorkerRegistrationInfo final : public nsISupports
+class ServiceWorkerRegistrationInfo final : public nsIServiceWorkerRegistrationInfo
 {
   uint32_t mControlledDocumentsCounter;
 
   virtual ~ServiceWorkerRegistrationInfo();
 
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
 
   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;
 
deleted file mode 100644
--- a/dom/workers/ServiceWorkerPeriodicUpdater.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- 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 "ServiceWorkerPeriodicUpdater.h"
-#include "mozilla/ClearOnShutdown.h"
-#include "mozilla/unused.h"
-#include "mozilla/Services.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/dom/ContentParent.h"
-#include "nsIServiceWorkerManager.h"
-
-#define OBSERVER_TOPIC_IDLE_DAILY "idle-daily"
-
-namespace mozilla {
-namespace dom {
-namespace workers {
-
-NS_IMPL_ISUPPORTS(ServiceWorkerPeriodicUpdater, nsIObserver)
-
-StaticRefPtr<ServiceWorkerPeriodicUpdater>
-ServiceWorkerPeriodicUpdater::sInstance;
-bool
-ServiceWorkerPeriodicUpdater::sPeriodicUpdatesEnabled = true;
-
-already_AddRefed<ServiceWorkerPeriodicUpdater>
-ServiceWorkerPeriodicUpdater::GetSingleton()
-{
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  if (!sInstance) {
-    sInstance = new ServiceWorkerPeriodicUpdater();
-    ClearOnShutdown(&sInstance);
-  }
-  RefPtr<ServiceWorkerPeriodicUpdater> copy(sInstance.get());
-  return copy.forget();
-}
-
-ServiceWorkerPeriodicUpdater::ServiceWorkerPeriodicUpdater()
-{
-  Preferences::AddBoolVarCache(&sPeriodicUpdatesEnabled,
-                               "dom.serviceWorkers.periodic-updates.enabled",
-                               true);
-}
-
-ServiceWorkerPeriodicUpdater::~ServiceWorkerPeriodicUpdater()
-{
-}
-
-NS_IMETHODIMP
-ServiceWorkerPeriodicUpdater::Observe(nsISupports* aSubject,
-                                      const char* aTopic,
-                                      const char16_t* aData)
-{
-  // In tests, the pref is set to false so that the idle-daily service does not
-  // trigger updates leading to intermittent failures.
-  // We're called from SpecialPowers inside tests, in which case we need to
-  // update during the test run, for which we use a non-empty aData.
-  NS_NAMED_LITERAL_STRING(CallerSpecialPowers, "Caller:SpecialPowers");
-  if (strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY) == 0 &&
-      (sPeriodicUpdatesEnabled || (aData && CallerSpecialPowers.Equals(aData)))) {
-    // First, update all registrations in the parent process.
-    nsCOMPtr<nsIServiceWorkerManager> swm =
-      mozilla::services::GetServiceWorkerManager();
-    if (swm) {
-        swm->UpdateAllRegistrations();
-    }
-
-    // Now, tell all child processes to update their registrations as well.
-    nsTArray<ContentParent*> children;
-    ContentParent::GetAll(children);
-    for (uint32_t i = 0; i < children.Length(); i++) {
-      Unused << children[i]->SendUpdateServiceWorkerRegistrations();
-    }
-  }
-
-  return NS_OK;
-}
-
-} // namespace workers
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/workers/ServiceWorkerPeriodicUpdater.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_ServiceWorkerPeriodicUpdater_h
-#define mozilla_ServiceWorkerPeriodicUpdater_h
-
-#include "nsCOMPtr.h"
-#include "nsIObserver.h"
-#include "mozilla/StaticPtr.h"
-
-namespace mozilla {
-namespace dom {
-namespace workers {
-
-/**
- * This XPCOM component is main-process only, which means that it will never
- * get instantiated in child processes.  When we receive the idle-daily
- * notification in this component, we iterate over all PContent children, and
- * send each one a message that will trigger a call to
- * nsIServiceWorkerManager::UpdateAllRegistrations() in all child processes.
- */
-
-class ServiceWorkerPeriodicUpdater final : public nsIObserver
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  static already_AddRefed<ServiceWorkerPeriodicUpdater> GetSingleton();
-
-private:
-  ServiceWorkerPeriodicUpdater();
-  ~ServiceWorkerPeriodicUpdater();
-
-  static StaticRefPtr<ServiceWorkerPeriodicUpdater> sInstance;
-  static bool sPeriodicUpdatesEnabled;
-};
-
-} // namespace workers
-} // namespace dom
-} // namespace mozilla
-
-#endif
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -15,17 +15,16 @@ EXPORTS.mozilla.dom += [
     'WorkerPrefs.h',
     'WorkerPrivate.h',
     'WorkerRunnable.h',
     'WorkerScope.h',
 ]
 
 EXPORTS.mozilla.dom.workers += [
     'ServiceWorkerManager.h',
-    'ServiceWorkerPeriodicUpdater.h',
     'WorkerDebuggerManager.h',
     'Workers.h',
 ]
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DataStore.h',
     'DataStoreCursor.h',
@@ -68,17 +67,16 @@ UNIFIED_SOURCES += [
     'ServiceWorkerClients.cpp',
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
     'ServiceWorkerManagerChild.cpp',
     'ServiceWorkerManagerParent.cpp',
     'ServiceWorkerManagerService.cpp',
     'ServiceWorkerMessageEvent.cpp',
-    'ServiceWorkerPeriodicUpdater.cpp',
     'ServiceWorkerPrivate.cpp',
     'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
     'WorkerDebuggerManager.cpp',
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -126,22 +126,16 @@ support-files =
   notificationclick.html
   notificationclick.js
   notificationclick_focus.html
   notificationclick_focus.js
   worker_updatefoundevent.js
   worker_updatefoundevent2.js
   updatefoundevent.html
   empty.js
-  periodic_update_test.js
-  periodic.sjs
-  periodic/frame.html
-  periodic/register.html
-  periodic/wait_for_update.html
-  periodic/unregister.html
   notification_constructor_error.js
   notification_get_sw.js
   notification/register.html
   notification/listener.html
   notification_alt/register.html
   sanitize/frame.html
   sanitize/register.html
   sanitize/example_check_and_unregister.html
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic.sjs
+++ /dev/null
@@ -1,24 +0,0 @@
-function handleRequest(request, response) {
-  var stateName = request.scheme + 'periodiccounter';
-  if (request.queryString == 'clearcounter') {
-    setState(stateName, '');
-    return;
-  }
-  
-  if (!getState(stateName)) {
-    setState(stateName, '1');
-  } else {
-    // Make sure that we pass a string value to setState!
-    setState(stateName, "" + (parseInt(getState(stateName)) + 1));
-  }
-  response.setHeader("Content-Type", "application/javascript", false);
-  response.write(getScript(stateName));
-}
-
-function getScript(stateName) {
-  return "onfetch = function(e) {" +
-           "if (e.request.url.indexOf('get-sw-version') > -1) {" +
-             "e.respondWith(new Response('" + getState(stateName) + "'));" +
-           "}" +
-         "};";
-}
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic/frame.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<script>
-  fetch("get-sw-version").then(function(r) {
-    return r.text();
-  }).then(function(body) {
-    parent.postMessage({status: "callback", data: body}, "*");
-  });
-</script>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic/register.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<script>
-  function done() {
-    parent.postMessage({status: "callback", data: "done"}, "*");
-  }
-
-  navigator.serviceWorker.ready.then(done);
-  navigator.serviceWorker.register("../periodic.sjs", {scope: "."});
-</script>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic/unregister.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<script>
-  fetch("../periodic.sjs?clearcounter").then(function() {
-    return navigator.serviceWorker.getRegistration(".");
-  }).then(function(registration) {
-    registration.unregister().then(function(success) {
-      if (success) {
-        parent.postMessage({status: "callback", data: "done"}, "*");
-      } else {
-        dump("Unregister failed\n");
-      }
-    }, function(e) {
-      dump("Unregistering the SW failed with " + e + "\n");
-    });
-  });
-</script>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic/wait_for_update.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<script>
-  navigator.serviceWorker.getRegistration(".").then(function(reg) {
-    reg.onupdatefound = function() {
-      reg.onupdatefound = null;
-      var sw = reg.installing;
-      sw.onstatechange = function() {
-        sw.onstatechange = null;
-        var success = !reg.waiting && reg.active;
-        parent.postMessage({status: "callback", data: "done"}, "*");
-      };
-    };
-    SpecialPowers.startPeriodicServiceWorkerUpdates();
-  });
-</script>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/periodic_update_test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var oldSWVersion, newSWVersion;
-// This will be set by the test to the base directory for the test files.
-var gPrefix;
-
-function start() {
-  const Cc = SpecialPowers.Cc;
-  const Ci = SpecialPowers.Ci;
-
-  function testVersion() {
-    // Verify that the service worker has been correctly updated.
-    testFrame(gPrefix + "periodic/frame.html").then(function(body) {
-      newSWVersion = parseInt(body);
-      is(newSWVersion, 2, "Expected correct new version");
-      ok(newSWVersion > oldSWVersion,
-         "The SW should be successfully updated, old: " + oldSWVersion +
-         ", new: " + newSWVersion);
-      unregisterSW().then(function() {
-        SimpleTest.finish();
-      });
-    });
-  }
-
-  registerSW().then(function() {
-    return testFrame(gPrefix + "periodic/frame.html").then(function(body) {
-      oldSWVersion = parseInt(body);
-      is(oldSWVersion, 1, "Expected correct old version");
-    });
-  }).then(function() {
-    return testFrame(gPrefix + "periodic/wait_for_update.html");
-  }).then(function() {
-    return testVersion();
-  });
-}
-
-function testFrame(src) {
-  return new Promise(function(resolve, reject) {
-    var iframe = document.createElement("iframe");
-    iframe.src = src;
-    window.onmessage = function(e) {
-      if (e.data.status == "callback") {
-        window.onmessage = null;
-        var result = e.data.data;
-        iframe.src = "about:blank";
-        document.body.removeChild(iframe);
-        iframe = null;
-        SpecialPowers.exactGC(window, function() {
-          resolve(result);
-        });
-      }
-    };
-    document.body.appendChild(iframe);
-  });
-}
-
-function registerSW() {
-  return testFrame(gPrefix + "periodic/register.html");
-}
-
-function unregisterSW() {
-  return testFrame(gPrefix + "periodic/unregister.html");
-}
-
-function runTheTest() {
-  SimpleTest.waitForExplicitFinish();
-
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
-  ]}, function() {
-    start();
-  });
-}
--- a/dom/workers/test/serviceworkers/test_aboutserviceworkers.html
+++ b/dom/workers/test/serviceworkers/test_aboutserviceworkers.html
@@ -41,17 +41,17 @@ function uninstallApp() {
 function update() {
   return new Promise((resolve, reject) => {
     let registrations = [];
     registrations = gServiceWorkerManager.getAllRegistrations();
 
     ok(registrations.length === 1, "The registration shows up in about:serviceworker page");
 
     for (let i = 0; i < registrations.length; i++) {
-      let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerInfo);
+      let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
       if (!registration) {
         reject();
         return;
       }
       info('Update service worker registration');
       gServiceWorkerManager.propagateSoftUpdate(
         registration.principal.originAttributes,
         registration.scope
--- a/dom/workers/test/serviceworkers/test_app_installation.html
+++ b/dom/workers/test/serviceworkers/test_app_installation.html
@@ -86,17 +86,17 @@ function installApp() {
 function checkSwRegistration(aExpectedRegistrations) {
   return new Promise((resolve, reject) => {
     let registrations = [];
     registrations = gServiceWorkerManager.getAllRegistrations();
     is(registrations.length, aExpectedRegistrations.length,
        "There should be " + aExpectedRegistrations.length + " registrations");
 
     for (let i = 0; i < registrations.length; i++) {
-      let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerInfo);
+      let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
       if (!registration) {
         reject();
         return;
       }
       is(registration.principal.origin, aExpectedRegistrations[i].origin,
          "Registration principal should be as expected");
     }
     resolve();
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_periodic_https_update.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1159378 - Test the periodic update of service workers</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="periodic_update_test.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-gPrefix = "https://example.com/tests/dom/workers/test/serviceworkers/";
-runTheTest();
-
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_periodic_update.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1112469 - Test the periodic update of service workers</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="periodic_update_test.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-gPrefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/";
-runTheTest();
-
-</script>
-</pre>
-</body>
-</html>
-
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1935,16 +1935,17 @@ DrawTargetCG::GetNativeSurface(NativeSur
 #endif
 }
 
 void
 DrawTargetCG::Mask(const Pattern &aSource,
                    const Pattern &aMask,
                    const DrawOptions &aDrawOptions)
 {
+  MOZ_CRASH("not completely implemented");
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   if (isGradient(aMask)) {
     assert(0);
   } else {
     if (aMask.GetType() == PatternType::COLOR) {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -584,29 +584,30 @@ SampleAnimations(Layer* aLayer, TimeStam
     // example, while they are waiting to be removed) we currently just
     // assume that we should fill.
     timing.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH;
 
     ComputedTiming computedTiming =
       dom::KeyframeEffectReadOnly::GetComputedTimingAt(
         Nullable<TimeDuration>(elapsedDuration), timing);
 
-    MOZ_ASSERT(0.0 <= computedTiming.mProgress &&
-               computedTiming.mProgress <= 1.0,
+    MOZ_ASSERT(!computedTiming.mProgress.IsNull() &&
+               0.0 <= computedTiming.mProgress.Value() &&
+               computedTiming.mProgress.Value() <= 1.0,
                "iteration progress should be in [0-1]");
 
     int segmentIndex = 0;
     AnimationSegment* segment = animation.segments().Elements();
-    while (segment->endPortion() < computedTiming.mProgress) {
+    while (segment->endPortion() < computedTiming.mProgress.Value()) {
       ++segment;
       ++segmentIndex;
     }
 
     double positionInSegment =
-      (computedTiming.mProgress - segment->startPortion()) /
+      (computedTiming.mProgress.Value() - segment->startPortion()) /
       (segment->endPortion() - segment->startPortion());
 
     double portion =
       animData.mFunctions[segmentIndex]->GetValue(positionInSegment);
 
     // interpolate the property
     Animatable interpolatedValue;
     SampleValue(portion, animation, animData.mStartValues[segmentIndex],
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -86,18 +86,29 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_
 
     # gcc lto likes to put the top level asm in syscall.cc in a different partition
     # from the function using it which breaks the build.  Work around that by
     # forcing there to be only one partition.
     if '-flto' in CONFIG['OS_CXXFLAGS'] and not CONFIG['CLANG_CXX']:
         LDFLAGS += ['--param lto-partitions=1']
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
+    # For sandbox includes and the include dependencies those have
+    LOCAL_INCLUDES += [
+        '/security/sandbox/chromium',
+        '/security/sandbox/chromium-shim',
+    ]
     USE_LIBS += [
         'mozsandbox',
+        'rlz',
+    ]
+
+    DEFINES['HASH_NODE_ID_WITH_DEVICE_ID'] = 1;
+    SOURCES += [
+        'sha256.c',
     ]
 
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']]
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -182,20 +182,20 @@ class GCSchedulingTunables
     bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; }
     uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; }
     uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; }
     uint64_t highFrequencyHighLimitBytes() const { return highFrequencyHighLimitBytes_; }
     double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; }
     double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
     double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
-    unsigned minEmptyChunkCount() const { return minEmptyChunkCount_; }
+    unsigned minEmptyChunkCount(const AutoLockGC&) const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
 
-    void setParameter(JSGCParamKey key, uint32_t value);
+    void setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock);
 };
 
 /*
  * GC Scheduling Overview
  * ======================
  *
  * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because
  * of the large number of subtle, cross-cutting, and widely dispersed factors
@@ -586,19 +586,19 @@ class GCRuntime
     void finish();
 
     inline int zeal();
     inline bool upcomingZealousGC();
     inline bool needZealousGC();
 
     bool addRoot(Value* vp, const char* name);
     void removeRoot(Value* vp);
-    void setMarkStackLimit(size_t limit);
+    void setMarkStackLimit(size_t limit, AutoLockGC& lock);
 
-    void setParameter(JSGCParamKey key, uint32_t value);
+    void setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
     uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
 
     bool triggerGC(JS::gcreason::Reason reason);
     void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock);
     bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason);
     bool maybeGC(Zone* zone);
     void maybePeriodicFullGC();
     void minorGC(JS::gcreason::Reason reason) {
@@ -750,16 +750,17 @@ class GCRuntime
     void setMaxMallocBytes(size_t value);
     int32_t getMallocBytes() const { return mallocBytesUntilGC; }
     void resetMallocBytes();
     bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
     void updateMallocCounter(JS::Zone* zone, size_t nbytes);
     void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void* data);
+    void callGCCallback(JSGCStatus status) const;
     bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
     void removeFinalizeCallback(JSFinalizeCallback func);
     bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data);
     void removeWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback);
     bool addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data);
     void removeWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback);
     JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -43,17 +43,18 @@ JS::Zone::Zone(JSRuntime* rt)
     gcPreserveCode_(false),
     jitUsingBarriers_(false),
     listNext_(NotOnList)
 {
     /* Ensure that there are no vtables to mess us up here. */
     MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
                static_cast<JS::shadow::Zone*>(this));
 
-    threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState);
+    AutoLockGC lock(rt);
+    threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock);
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
 }
 
 Zone::~Zone()
 {
     JSRuntime* rt = runtimeFromMainThread();
     if (this == rt->gc.systemZone)
         rt->gc.systemZone = nullptr;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -43,26 +43,28 @@ class ZoneHeapThreshold
         gcTriggerBytes_(0)
     {}
 
     double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
     size_t gcTriggerBytes() const { return gcTriggerBytes_; }
     double allocTrigger(bool highFrequencyGC) const;
 
     void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
-                       const GCSchedulingTunables& tunables, const GCSchedulingState& state);
+                       const GCSchedulingTunables& tunables, const GCSchedulingState& state,
+                       const AutoLockGC& lock);
     void updateForRemovedArena(const GCSchedulingTunables& tunables);
 
   private:
     static double computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
                                                          const GCSchedulingTunables& tunables,
                                                          const GCSchedulingState& state);
     static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
                                           JSGCInvocationKind gckind,
-                                          const GCSchedulingTunables& tunables);
+                                          const GCSchedulingTunables& tunables,
+                                          const AutoLockGC& lock);
 };
 
 // Maps a Cell* to a unique, 64bit id.
 using UniqueIdMap = HashMap<Cell*, uint64_t, PointerHasher<Cell*, 3>, SystemAllocPolicy>;
 
 extern uint64_t NextCellUniqueId(JSRuntime* rt);
 
 } // namespace gc
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1434,17 +1434,18 @@ JS_UpdateWeakPointerAfterGCUnbarriered(J
 {
     if (IsAboutToBeFinalizedUnbarriered(objp))
         *objp = nullptr;
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime* rt, JSGCParamKey key, uint32_t value)
 {
-    rt->gc.setParameter(key, value);
+    AutoLockGC lock(rt);
+    rt->gc.setParameter(key, value, lock);
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSRuntime* rt, JSGCParamKey key)
 {
     AutoLockGC lock(rt);
     return rt->gc.getParameter(key, lock);
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -734,32 +734,32 @@ GCRuntime::expireEmptyChunkPool(bool shr
     for (ChunkPool::Iter iter(emptyChunks(lock)); !iter.done();) {
         Chunk* chunk = iter.get();
         iter.next();
 
         MOZ_ASSERT(chunk->unused());
         MOZ_ASSERT(!fullChunks(lock).contains(chunk));
         MOZ_ASSERT(!availableChunks(lock).contains(chunk));
         if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
-            (freeChunkCount >= tunables.minEmptyChunkCount() &&
+            (freeChunkCount >= tunables.minEmptyChunkCount(lock) &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             emptyChunks(lock).remove(chunk);
             prepareToFreeChunk(chunk->info);
             expired.push(chunk);
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
         }
     }
     MOZ_ASSERT(expired.verify());
     MOZ_ASSERT(emptyChunks(lock).verify());
     MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
-    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount());
+    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount(lock));
     return expired;
 }
 
 static void
 FreeChunkPool(JSRuntime* rt, ChunkPool& pool)
 {
     for (ChunkPool::Iter iter(pool); !iter.done();) {
         Chunk* chunk = iter.get();
@@ -1016,17 +1016,17 @@ Chunk::updateChunkListAfterFree(JSRuntim
 
 inline bool
 GCRuntime::wantBackgroundAllocation(const AutoLockGC& lock) const
 {
     // To minimize memory waste, we do not want to run the background chunk
     // allocation if we already have some empty chunks or when the runtime has
     // a small heap size (and therefore likely has a small growth rate).
     return allocTask.enabled() &&
-           emptyChunks(lock).count() < tunables.minEmptyChunkCount() &&
+           emptyChunks(lock).count() < tunables.minEmptyChunkCount(lock) &&
            (fullChunks(lock).count() + availableChunks(lock).count()) >= 4;
 }
 
 void
 GCRuntime::startBackgroundAllocTaskIfIdle()
 {
     AutoLockHelperThreadState helperLock;
     if (allocTask.isRunning())
@@ -1283,19 +1283,24 @@ GCRuntime::init(uint32_t maxbytes, uint3
 
     if (!helperState.init())
         return false;
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
-    tunables.setParameter(JSGC_MAX_BYTES, maxbytes);
+    AutoLockGC lock(rt);
+    tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock);
     setMaxMallocBytes(maxbytes);
 
+    const char* size = getenv("JSGC_MARK_STACK_LIMIT");
+    if (size)
+        setMarkStackLimit(atoi(size), lock);
+
     jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD;
 
     if (!nursery.init(maxNurseryBytes))
         return false;
 
     if (!nursery.isEnabled()) {
         MOZ_ASSERT(nursery.nurserySize() == 0);
         ++rt->gc.generationalDisabled;
@@ -1388,53 +1393,53 @@ GCRuntime::finishRoots()
 {
     if (rootsHash.initialized())
         rootsHash.clear();
 
     FinishPersistentRootedChains(rt->mainThread.roots);
 }
 
 void
-GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
+GCRuntime::setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock)
 {
     switch (key) {
       case JSGC_MAX_MALLOC_BYTES:
         setMaxMallocBytes(value);
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
             zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9);
         break;
       case JSGC_SLICE_TIME_BUDGET:
         defaultTimeBudget_ = value ? value : SliceBudget::UnlimitedTimeBudget;
         break;
       case JSGC_MARK_STACK_LIMIT:
-        setMarkStackLimit(value);
+        setMarkStackLimit(value, lock);
         break;
       case JSGC_DECOMMIT_THRESHOLD:
         decommitThreshold = value * 1024 * 1024;
         break;
       case JSGC_MODE:
         mode = JSGCMode(value);
         MOZ_ASSERT(mode == JSGC_MODE_GLOBAL ||
                    mode == JSGC_MODE_COMPARTMENT ||
                    mode == JSGC_MODE_INCREMENTAL);
         break;
       case JSGC_COMPACTING_ENABLED:
         compactingEnabled = value != 0;
         break;
       default:
-        tunables.setParameter(key, value);
+        tunables.setParameter(key, value, lock);
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             zone->threshold.updateAfterGC(zone->usage.gcBytes(), GC_NORMAL, tunables,
-                                         schedulingState);
+                                          schedulingState, lock);
         }
     }
 }
 
 void
-GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value)
+GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock)
 {
     switch(key) {
       case JSGC_MAX_BYTES:
         gcMaxBytes_ = value;
         break;
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         highFrequencyThresholdUsec_ = value * PRMJ_USEC_PER_MSEC;
         break;
@@ -1531,31 +1536,32 @@ GCRuntime::getParameter(JSGCParamKey key
         return uint32_t(tunables.lowFrequencyHeapGrowth() * 100);
       case JSGC_DYNAMIC_HEAP_GROWTH:
         return tunables.isDynamicHeapGrowthEnabled();
       case JSGC_DYNAMIC_MARK_SLICE:
         return tunables.isDynamicMarkSliceEnabled();
       case JSGC_ALLOCATION_THRESHOLD:
         return tunables.gcZoneAllocThresholdBase() / 1024 / 1024;
       case JSGC_MIN_EMPTY_CHUNK_COUNT:
-        return tunables.minEmptyChunkCount();
+        return tunables.minEmptyChunkCount(lock);
       case JSGC_MAX_EMPTY_CHUNK_COUNT:
         return tunables.maxEmptyChunkCount();
       case JSGC_COMPACTING_ENABLED:
         return compactingEnabled;
       default:
         MOZ_ASSERT(key == JSGC_NUMBER);
         return uint32_t(number);
     }
 }
 
 void
-GCRuntime::setMarkStackLimit(size_t limit)
+GCRuntime::setMarkStackLimit(size_t limit, AutoLockGC& lock)
 {
     MOZ_ASSERT(!rt->isHeapBusy());
+    AutoUnlockGC unlock(lock);
     AutoStopVerifyingBarriers pauseVerification(rt, false);
     marker.setMaxCapacity(limit);
 }
 
 bool
 GCRuntime::addBlackRootsTracer(JSTraceDataOp traceOp, void* data)
 {
     AssertHeapIsIdle(rt);
@@ -1584,16 +1590,46 @@ GCRuntime::setGrayRootsTracer(JSTraceDat
 
 void
 GCRuntime::setGCCallback(JSGCCallback callback, void* data)
 {
     gcCallback.op = callback;
     gcCallback.data = data;
 }
 
+void
+GCRuntime::callGCCallback(JSGCStatus status) const
+{
+    if (gcCallback.op)
+        gcCallback.op(rt, status, gcCallback.data);
+}
+
+namespace {
+
+class AutoNotifyGCActivity {
+  public:
+    explicit AutoNotifyGCActivity(GCRuntime& gc) : gc_(gc) {
+        if (!gc_.isIncrementalGCInProgress()) {
+            gcstats::AutoPhase ap(gc_.stats, gcstats::PHASE_GC_BEGIN);
+            gc_.callGCCallback(JSGC_BEGIN);
+        }
+    }
+    ~AutoNotifyGCActivity() {
+        if (!gc_.isIncrementalGCInProgress()) {
+            gcstats::AutoPhase ap(gc_.stats, gcstats::PHASE_GC_END);
+            gc_.callGCCallback(JSGC_END);
+        }
+    }
+
+  private:
+    GCRuntime& gc_;
+};
+
+} // (anon)
+
 bool
 GCRuntime::addFinalizeCallback(JSFinalizeCallback callback, void* data)
 {
     return finalizeCallbacks.append(Callback<JSFinalizeCallback>(callback, data));
 }
 
 void
 GCRuntime::removeFinalizeCallback(JSFinalizeCallback callback)
@@ -1606,20 +1642,18 @@ GCRuntime::removeFinalizeCallback(JSFina
             break;
         }
     }
 }
 
 void
 GCRuntime::callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const
 {
-    for (const Callback<JSFinalizeCallback>* p = finalizeCallbacks.begin();
-         p < finalizeCallbacks.end(); p++)
-    {
-        p->op(fop, status, !isFull, p->data);
+    for (auto& p : finalizeCallbacks) {
+        p.op(fop, status, !isFull, p.data);
     }
 }
 
 bool
 GCRuntime::addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data)
 {
     return updateWeakPointerZoneGroupCallbacks.append(
             Callback<JSWeakPointerZoneGroupCallback>(callback, data));
@@ -1800,32 +1834,34 @@ ZoneHeapThreshold::computeZoneHeapGrowth
     MOZ_ASSERT(factor >= minRatio);
     MOZ_ASSERT(factor <= maxRatio);
     return factor;
 }
 
 /* static */ size_t
 ZoneHeapThreshold::computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
                                            JSGCInvocationKind gckind,
-                                           const GCSchedulingTunables& tunables)
+                                           const GCSchedulingTunables& tunables,
+                                           const AutoLockGC& lock)
 {
     size_t base = gckind == GC_SHRINK
-                ? Max(lastBytes, tunables.minEmptyChunkCount() * ChunkSize)
+                ? Max(lastBytes, tunables.minEmptyChunkCount(lock) * ChunkSize)
                 : Max(lastBytes, tunables.gcZoneAllocThresholdBase());
     double trigger = double(base) * growthFactor;
     return size_t(Min(double(tunables.gcMaxBytes()), trigger));
 }
 
 void
 ZoneHeapThreshold::updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
                                  const GCSchedulingTunables& tunables,
-                                 const GCSchedulingState& state)
+                                 const GCSchedulingState& state, const AutoLockGC& lock)
 {
     gcHeapGrowthFactor_ = computeZoneHeapGrowthFactorForHeapSize(lastBytes, tunables, state);
-    gcTriggerBytes_ = computeZoneTriggerBytes(gcHeapGrowthFactor_, lastBytes, gckind, tunables);
+    gcTriggerBytes_ = computeZoneTriggerBytes(gcHeapGrowthFactor_, lastBytes, gckind, tunables,
+                                              lock);
 }
 
 void
 ZoneHeapThreshold::updateForRemovedArena(const GCSchedulingTunables& tunables)
 {
     size_t amount = ArenaSize * gcHeapGrowthFactor_;
 
     MOZ_ASSERT(amount > 0);
@@ -5203,19 +5239,20 @@ GCRuntime::beginSweepingZoneGroup()
 }
 
 void
 GCRuntime::endSweepingZoneGroup()
 {
     /* Update the GC state for zones we have swept. */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCSweeping());
+        AutoLockGC lock(rt);
         zone->setGCState(Zone::Finished);
         zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind, tunables,
-                                      schedulingState);
+                                      schedulingState, lock);
     }
 
     /* Start background thread to sweep zones if required. */
     ZoneList zones;
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
         zones.append(zone);
     if (sweepOnBackgroundThread)
         queueZonesForBackgroundSweep(zones);
@@ -6187,16 +6224,18 @@ class AutoScheduleZonesForGC
  * use during the marking implementation.
  *
  * Returns true if we "reset" an existing incremental GC, which would force us
  * to run another cycle.
  */
 MOZ_NEVER_INLINE bool
 GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::Reason reason)
 {
+    AutoNotifyGCActivity notify(*this);
+
     evictNursery(reason);
 
     /*
      * Marking can trigger many incidental post barriers, some of them for
      * objects which are not going to be live after the GC.
      */
     AutoDisableStoreBuffer adsb(this);
 
@@ -6353,35 +6392,19 @@ GCRuntime::collect(bool nonincrementalBy
     AutoTraceLog logGC(TraceLoggerForMainThread(rt), TraceLogger_GC);
     AutoStopVerifyingBarriers av(rt, IsShutdownGC(reason));
     AutoEnqueuePendingParseTasksAfterGC aept(*this);
     AutoScheduleZonesForGC asz(rt);
     gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind, budget, reason);
 
     bool repeat = false;
     do {
-        /*
-         * Let the API user decide to defer a GC if it wants to (unless this
-         * is the last context). Invoke the callback regardless.
-         */
-        if (!isIncrementalGCInProgress()) {
-            gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
-            if (gcCallback.op)
-                gcCallback.op(rt, JSGC_BEGIN, gcCallback.data);
-        }
-
         poked = false;
         bool wasReset = gcCycle(nonincrementalByAPI, budget, reason);
 
-        if (!isIncrementalGCInProgress()) {
-            gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
-            if (gcCallback.op)
-                gcCallback.op(rt, JSGC_END, gcCallback.data);
-        }
-
         /* Need to re-schedule all zones for GC. */
         if (poked && cleanUpEverything)
             JS::PrepareForFullGC(rt);
 
         /*
          * This code makes an extra effort to collect compartments that we
          * thought were dead at the start of the GC. See the large comment in
          * beginMarkPhase.
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -290,20 +290,16 @@ JSRuntime::init(uint32_t maxbytes, uint3
     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
         return false;
 
     js::TlsPerThreadData.set(&mainThread);
 
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
-    const char* size = getenv("JSGC_MARK_STACK_LIMIT");
-    if (size)
-        gc.setMarkStackLimit(atoi(size));
-
     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
     JS::CompartmentOptions options;
     ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options));
     if (!atomsCompartment || !atomsCompartment->init(nullptr))
         return false;
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -77,13 +77,9 @@
 // {5a75c25a-5e7e-4d90-8f7c-07eb15cc0aa8}
 #define QUOTA_MANAGER_CID \
 { 0x5a75c25a, 0x5e7e, 0x4d90, { 0x8f, 0x7c, 0x07, 0xeb, 0x15, 0xcc, 0x0a, 0xa8 } }
 
 // {c74bde32-bcc7-4840-8430-c733351b212a}
 #define SERVICEWORKERMANAGER_CID \
 { 0xc74bde32, 0xbcc7, 0x4840, { 0x84, 0x30, 0xc7, 0x33, 0x35, 0x1b, 0x21, 0x2a } }
 
-// {91f43ef6-8159-457a-ba68-249c3ff7a06a}
-#define SERVICEWORKERPERIODICUPDATER_CID \
-{ 0x91f43ef6, 0x8159, 0x457a, { 0xba, 0x68, 0x24, 0x9c, 0x3f, 0xf7, 0xa0, 0x6a } }
-
 #endif /* nsLayoutCID_h__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -84,17 +84,16 @@
 #include "nsZipArchive.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
-#include "mozilla/dom/workers/ServiceWorkerPeriodicUpdater.h"
 #include "mozilla/dom/workers/WorkerDebuggerManager.h"
 #include "mozilla/OSFileConstants.h"
 #include "mozilla/Services.h"
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 #include "mozilla/dom/FakeSpeechRecognitionService.h"
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
@@ -267,17 +266,16 @@ static void Shutdown();
 #include "mozilla/TextInputProcessor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::workers::ServiceWorkerManager;
-using mozilla::dom::workers::ServiceWorkerPeriodicUpdater;
 using mozilla::dom::workers::WorkerDebuggerManager;
 using mozilla::dom::UDPSocketChild;
 using mozilla::dom::time::TimeService;
 using mozilla::net::StreamingProtocolControllerService;
 using mozilla::gmp::GeckoMediaPluginService;
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
@@ -315,18 +313,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Exception
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManager,
                                          QuotaManager::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager,
                                          ServiceWorkerManager::GetInstance)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerPeriodicUpdater,
-                                         ServiceWorkerPeriodicUpdater::GetSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(WorkerDebuggerManager)
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
                                          SystemWorkerManager::FactoryCreate)
 #endif
 #ifdef MOZ_B2G_BT
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(BluetoothService,
@@ -766,17 +762,16 @@ NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CI
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
 NS_DEFINE_NAMED_CID(QUOTA_MANAGER_CID);
 NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID);
-NS_DEFINE_NAMED_CID(SERVICEWORKERPERIODICUPDATER_CID);
 NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID);
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_CID);
 #endif
 #ifdef MOZ_B2G_BT
 NS_DEFINE_NAMED_CID(BLUETOOTHSERVICE_CID);
 #endif
 #ifdef MOZ_WIDGET_GONK
@@ -1076,17 +1071,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_XPCEXCEPTION_CID, false, nullptr, ExceptionConstructor },
   { &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, DOMSessionStorageManagerConstructor },
   { &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, DOMLocalStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, nullptr, NS_NewJSON },
   { &kNS_TEXTEDITOR_CID, false, nullptr, nsPlaintextEditorConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
   { &kQUOTA_MANAGER_CID, false, nullptr, QuotaManagerConstructor },
   { &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor },
-  { &kSERVICEWORKERPERIODICUPDATER_CID, false, nullptr, ServiceWorkerPeriodicUpdaterConstructor },
   { &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor },
 #ifdef MOZ_WIDGET_GONK
   { &kSYSTEMWORKERMANAGER_CID, true, nullptr, SystemWorkerManagerConstructor },
 #endif
 #ifdef MOZ_B2G_BT
   { &kBLUETOOTHSERVICE_CID, true, nullptr, BluetoothServiceConstructor },
 #endif
 #ifdef MOZ_WIDGET_GONK
@@ -1246,17 +1240,16 @@ static const mozilla::Module::ContractID
   // Keeping the old ContractID for backward compatibility
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
   { QUOTA_MANAGER_CONTRACTID, &kQUOTA_MANAGER_CID },
   { SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID },
-  { SERVICEWORKERPERIODICUPDATER_CONTRACTID, &kSERVICEWORKERPERIODICUPDATER_CID, Module::MAIN_PROCESS_ONLY },
   { WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID },
 #ifdef MOZ_WIDGET_GONK
   { SYSTEMWORKERMANAGER_CONTRACTID, &kSYSTEMWORKERMANAGER_CID },
 #endif
 #ifdef MOZ_B2G_BT
   { BLUETOOTHSERVICE_CONTRACTID, &kBLUETOOTHSERVICE_CID },
 #endif
 #ifdef MOZ_WIDGET_GONK
@@ -1362,17 +1355,16 @@ static const mozilla::Module::CategoryEn
 #ifdef MOZ_WIDGET_GONK
   { "profile-after-change", "Gonk System Worker Manager", SYSTEMWORKERMANAGER_CONTRACTID },
 #endif
 #ifdef MOZ_B2G_BT
   { "profile-after-change", "Bluetooth Service", BLUETOOTHSERVICE_CONTRACTID },
 #endif
   { "profile-after-change", "PresentationDeviceManager", PRESENTATION_DEVICE_MANAGER_CONTRACTID },
   { "profile-after-change", "PresentationService", PRESENTATION_SERVICE_CONTRACTID },
-  { "idle-daily", "ServiceWorker Periodic Updater", SERVICEWORKERPERIODICUPDATER_CONTRACTID },
   { nullptr }
 };
 
 static void
 LayoutModuleDtor()
 {
   Shutdown();
   nsContentUtils::XPCOMShutdown();
--- a/layout/reftests/table-overflow/reftest.list
+++ b/layout/reftests/table-overflow/reftest.list
@@ -1,5 +1,6 @@
 == bug785684-x.html bug785684-ref.html
 == bug785684-y.html bug785684-ref.html
 skip-if(B2G||Mulet) == table-row-pagination.html table-row-pagination-ref.html # Frequently failing on b2g (bug 1155426)
 == 963441.html 963441-ref.html
 == table-caption-scroll.html table-caption-scroll-ref.html
+== table-cell-block-overflow.html table-cell-block-overflow-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-overflow/table-cell-block-overflow-ref.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <style>
+  .content {
+    padding: 20px;
+    height: 100%;
+    width:  20px;
+    height:  54px;
+    background: purple;
+  }
+  .fake-td {
+    float: left;
+    border: 20px solid teal;
+    height: 54px;
+    width: 60px;
+    padding: 1px;
+    margin: 1px;
+  }
+  .fake-table-tr {
+    padding: 1px;
+  }
+  </style>
+</head>
+  <body>
+    <div class="fake-table-tr">
+      <div class="fake-td">
+        <div class="content"></div>
+      </div>
+      <div class="fake-td">
+        <div class="content"></div>
+      </div>
+      <div class="fake-td">
+        <div class="content"></div>
+      </div>
+      <div class="fake-td">
+        <div class="content"></div>
+      </div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-overflow/table-cell-block-overflow.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test table-cell content overflowing in the block direction for each
+    vertical-align value</title>
+  <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
+  <style>
+  td {
+    border: 20px solid teal;
+    height: 100%;
+  }
+  table {
+    height: 100px;
+  }
+  .content {
+    padding: 20px;
+    height: 100%;
+    width:  20px;
+    background: purple;
+  }
+  </style>
+</head>
+  <body>
+    <table>
+      <tr>
+        <td style="vertical-align: middle">
+          <div class="content"></div>
+        </td>
+        <td style="vertical-align: baseline">
+          <div class="content"></div>
+        </td>
+        <td style="vertical-align: bottom">
+          <div class="content"></div>
+        </td>
+        <td style="vertical-align: top">
+          <div class="content"></div>
+        </td>
+      </tr>
+    </table>
+  </body>
+</html>
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -193,47 +193,47 @@ CSSAnimation::QueueEvents()
   nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
   if (!presContext) {
     return;
   }
   nsAnimationManager* manager = presContext->AnimationManager();
 
   ComputedTiming computedTiming = mEffect->GetComputedTiming();
 
-  if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Null) {
+  if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Null) {
     return; // do nothing
   }
 
   // Note that script can change the start time, so we have to handle moving
   // backwards through the animation as well as forwards. An 'animationstart'
   // is dispatched if we enter the active phase (regardless if that is from
   // before or after the animation's active phase). An 'animationend' is
   // dispatched if we leave the active phase (regardless if that is to before
   // or after the animation's active phase).
 
   bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE &&
                    mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER;
   bool isActive =
-         computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
+         computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
   bool isSameIteration =
          computedTiming.mCurrentIteration == mPreviousPhaseOrIteration;
   bool skippedActivePhase =
     (mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE &&
-     computedTiming.mPhase == ComputedTiming::AnimationPhase_After) ||
+     computedTiming.mPhase == ComputedTiming::AnimationPhase::After) ||
     (mPreviousPhaseOrIteration == PREVIOUS_PHASE_AFTER &&
-     computedTiming.mPhase == ComputedTiming::AnimationPhase_Before);
+     computedTiming.mPhase == ComputedTiming::AnimationPhase::Before);
 
   MOZ_ASSERT(!skippedActivePhase || (!isActive && !wasActive),
              "skippedActivePhase only makes sense if we were & are inactive");
 
-  if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Before) {
+  if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Before) {
     mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE;
-  } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) {
+  } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Active) {
     mPreviousPhaseOrIteration = computedTiming.mCurrentIteration;
-  } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) {
+  } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::After) {
     mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER;
   }
 
   EventMessage message;
 
   if (!wasActive && isActive) {
     message = eAnimationStart;
   } else if (wasActive && !isActive) {
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -52,24 +52,24 @@ ElementPropertyTransition::CurrentValueP
   // interval. However, it might be possible that we're behind on flushing
   // causing us to get called *after* the animation interval. So, just in
   // case, we override the fill mode to 'both' to ensure the progress
   // is never null.
   AnimationTiming timingToUse = mTiming;
   timingToUse.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH;
   ComputedTiming computedTiming = GetComputedTiming(&timingToUse);
 
-  MOZ_ASSERT(computedTiming.mProgress != ComputedTiming::kNullProgress,
+  MOZ_ASSERT(!computedTiming.mProgress.IsNull(),
              "Got a null progress for a fill mode of 'both'");
   MOZ_ASSERT(mProperties.Length() == 1,
              "Should have one animation property for a transition");
   MOZ_ASSERT(mProperties[0].mSegments.Length() == 1,
              "Animation property should have one segment for a transition");
   return mProperties[0].mSegments[0].mTimingFunction
-         .GetValue(computedTiming.mProgress);
+         .GetValue(computedTiming.mProgress.Value());
 }
 
 ////////////////////////// CSSTransition ////////////////////////////
 
 JSObject*
 CSSTransition::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::CSSTransitionBinding::Wrap(aCx, this, aGivenProto);
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -622,18 +622,19 @@ void nsTableCellFrame::BlockDirAlignChil
       kidBStart = bSize - childBSize - bEndInset;
     break;
 
     default:
     case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
       // Align the middle of the child frame with the middle of the content area,
       kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2;
   }
-  // if the content is larger than the cell bsize, align from bstart
-  kidBStart = std::max(0, kidBStart);
+  // If the content is larger than the cell bsize, align from bStartInset
+  // (cell's content-box bstart edge).
+  kidBStart = std::max(bStartInset, kidBStart);
 
   if (kidBStart != kidRect.BStart(aWM)) {
     // Invalidate at the old position first
     firstKid->InvalidateFrameSubtree();
   }
 
   firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM),
                                           kidBStart), containerSize);
--- a/layout/tools/reftest/b2g_desktop.py
+++ b/layout/tools/reftest/b2g_desktop.py
@@ -132,18 +132,16 @@ class B2GDesktopReftest(RefTest):
         prefs["font.size.inflation.emPerLine"] = 0
         prefs["font.size.inflation.minTwips"] = 0
         prefs["network.dns.localDomains"] = "app://test-container.gaiamobile.org"
         prefs["reftest.browser.iframe.enabled"] = False
         prefs["reftest.remote"] = False
         # Set a future policy version to avoid the telemetry prompt.
         prefs["toolkit.telemetry.prompted"] = 999
         prefs["toolkit.telemetry.notifiedOptOut"] = 999
-        # Disable periodic updates of service workers
-        prefs["dom.serviceWorkers.periodic-updates.enabled"] = False
 
         # Set the extra prefs.
         profile.set_preferences(prefs)
         return profile
 
     def build_command_line(self, app, ignore_window_size=False,
                            browser_arg=None):
         cmd = os.path.abspath(app)
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -60,13 +60,10 @@
     branch.setBoolPref("browser.search.isUS", true);
     branch.setCharPref("browser.search.countryCode", "US");
     // Prevent the geoSpecificDefaults XHR by emptying the URL.
     branch.setCharPref("browser.search.geoSpecificDefaults.url", "");
 
     // Make sure SelfSupport doesn't hit the network.
     branch.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/");
 
-    // Disable periodic updates of service workers.
-    branch.setBoolPref("dom.serviceWorkers.periodic-updates.enabled", false);
-
     // Allow XUL and XBL files to be opened from file:// URIs
     branch.setBoolPref("dom.allow_XUL_XBL_for_file", true);
--- a/layout/tools/reftest/runreftestb2g.py
+++ b/layout/tools/reftest/runreftestb2g.py
@@ -256,18 +256,16 @@ class B2GRemoteReftest(RefTest):
         prefs["app.update.enabled"] = False
         prefs["app.update.url"] = ""
         prefs["app.update.url.override"] = ""
         # Disable webapp updates
         prefs["webapps.update.enabled"] = False
         # Disable tiles also
         prefs["browser.newtabpage.directory.source"] = ""
         prefs["browser.newtabpage.directory.ping"] = ""
-        # Disable periodic updates of service workers
-        prefs["dom.serviceWorkers.periodic-updates.enabled"] = False
 
         if options.oop:
             prefs['browser.tabs.remote.autostart'] = True
             prefs['reftest.browser.iframe.enabled'] = True
 
         # Set the extra prefs.
         profile.set_preferences(prefs)
 
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -1852,17 +1852,18 @@ Predictor::Resetter::OnCacheStorageInfo(
   MOZ_ASSERT(NS_IsMainThread());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Predictor::Resetter::OnCacheEntryInfo(nsIURI *uri, const nsACString &idEnhance,
                                       int64_t dataSize, int32_t fetchCount,
-                                      uint32_t lastModifiedTime, uint32_t expirationTime)
+                                      uint32_t lastModifiedTime, uint32_t expirationTime,
+                                      bool aPinned)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The predictor will only ever touch entries with no idEnhance ("") or an
   // idEnhance of PREDICTOR_ORIGIN_EXTENSION, so we filter out any entries that
   // don't match that to avoid doing extra work.
   if (idEnhance.EqualsLiteral(PREDICTOR_ORIGIN_EXTENSION)) {
     // This is an entry we own, so we can just doom it entirely
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -1300,16 +1300,17 @@ GetGroupForCache(const nsCSubstring &cli
   return NS_OK;
 }
 
 void
 AppendJARIdentifier(nsACString &_result, OriginAttributes const *aOriginAttributes)
 {
   nsAutoCString suffix;
   aOriginAttributes->CreateSuffix(suffix);
+  _result.Append('#');
   _result.Append(suffix);
 }
 
 } // namespace
 
 // static
 nsresult
 nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL,
--- a/netwerk/cache2/CacheEntry.h
+++ b/netwerk/cache2/CacheEntry.h
@@ -66,16 +66,17 @@ public:
   nsCString const &GetStorageID() const { return mStorageID; }
   nsCString const &GetEnhanceID() const { return mEnhanceID; }
   nsIURI* GetURI() const { return mURI; }
   // Accessible at any time
   bool IsUsingDisk() const { return mUseDisk; }
   bool IsReferenced() const;
   bool IsFileDoomed();
   bool IsDoomed() const { return mIsDoomed; }
+  bool IsPinned() const { return mPinned; }
 
   // Methods for entry management (eviction from memory),
   // called only on the management thread.
 
   // TODO make these inline
   double GetFrecency() const;
   uint32_t GetExpirationTime() const;
   uint32_t UseCount() const { return mUseCount; }
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -2377,17 +2377,17 @@ CacheFileIOManager::GetEntryInfo(const S
   }
   uint32_t lastModified;
   if (NS_FAILED(metadata->GetLastModified(&lastModified))) {
     lastModified = 0;
   }
 
   // Call directly on the callback.
   aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize, fetchCount,
-                         lastModified, expirationTime);
+                         lastModified, expirationTime, metadata->Pinned());
 
   return NS_OK;
 }
 
 nsresult
 CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
                                                int64_t aTruncatePos,
                                                int64_t aEOFPos)
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -306,25 +306,26 @@ private:
       walker->mSize += size;
 
     walker->mEntryArray.AppendElement(aEntry);
     return PL_DHASH_NEXT;
   }
 
   virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
                            int64_t aDataSize, int32_t aFetchCount,
-                           uint32_t aLastModifiedTime, uint32_t aExpirationTime)
+                           uint32_t aLastModifiedTime, uint32_t aExpirationTime,
+                           bool aPinned)
   {
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_NewURI(getter_AddRefs(uri), aURISpec);
     if (NS_FAILED(rv))
       return;
 
     mCallback->OnCacheEntryInfo(uri, aIdEnhance, aDataSize, aFetchCount,
-                                aLastModifiedTime, aExpirationTime);
+                                aLastModifiedTime, aExpirationTime, aPinned);
   }
 
 private:
   nsCString mContextKey;
   nsTArray<RefPtr<CacheEntry> > mEntryArray;
 };
 
 // WalkDiskCacheRunnable
@@ -374,28 +375,29 @@ private:
 
       nsCOMPtr<nsIURI> uri;
       nsresult rv = NS_NewURI(getter_AddRefs(uri), mURISpec);
       if (NS_FAILED(rv))
         return NS_OK;
 
       mWalker->mCallback->OnCacheEntryInfo(
         uri, mIdEnhance, mDataSize, mFetchCount,
-        mLastModifiedTime, mExpirationTime);
+        mLastModifiedTime, mExpirationTime, mPinned);
       return NS_OK;
     }
 
     RefPtr<WalkDiskCacheRunnable> mWalker;
 
     nsCString mURISpec;
     nsCString mIdEnhance;
     int64_t mDataSize;
     int32_t mFetchCount;
     uint32_t mLastModifiedTime;
     uint32_t mExpirationTime;
+    bool mPinned;
   };
 
   NS_IMETHODIMP Run()
   {
     // The main loop
     nsresult rv;
 
     if (CacheStorageService::IsOnManagementThread()) {
@@ -465,28 +467,30 @@ private:
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
   virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
                            int64_t aDataSize, int32_t aFetchCount,
-                           uint32_t aLastModifiedTime, uint32_t aExpirationTime)
+                           uint32_t aLastModifiedTime, uint32_t aExpirationTime,
+                           bool aPinned)
   {
     // Called directly from CacheFileIOManager::GetEntryInfo.
 
     // Invoke onCacheEntryInfo on the main thread for this entry.
     RefPtr<OnCacheEntryInfoRunnable> info = new OnCacheEntryInfoRunnable(this);
     info->mURISpec = aURISpec;
     info->mIdEnhance = aIdEnhance;
     info->mDataSize = aDataSize;
     info->mFetchCount = aFetchCount;
     info->mLastModifiedTime = aLastModifiedTime;
     info->mExpirationTime = aExpirationTime;
+    info->mPinned = aPinned;
 
     NS_DispatchToMainThread(info);
   }
 
   RefPtr<nsILoadContextInfo> mLoadInfo;
   enum {
     // First, we collect stats for the load context.
     COLLECT_STATS,
@@ -1919,17 +1923,18 @@ CacheStorageService::GetCacheEntryInfo(C
     lastModified = 0;
   }
   uint32_t expirationTime;
   if (NS_FAILED(aEntry->GetExpirationTime(&expirationTime))) {
     expirationTime = 0;
   }
 
   aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize,
-                         fetchCount, lastModified, expirationTime);
+                         fetchCount, lastModified, expirationTime,
+                         aEntry->IsPinned());
 }
 
 // Telementry collection
 
 namespace {
 
 bool TelemetryEntryKey(CacheEntry const* entry, nsAutoCString& key)
 {
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -98,17 +98,18 @@ public:
 
   // Helper thread-safe interface to pass entry info, only difference from
   // nsICacheStorageVisitor is that instead of nsIURI only the uri spec is
   // passed.
   class EntryInfoCallback {
   public:
     virtual void OnEntryInfo(const nsACString & aURISpec, const nsACString & aIdEnhance,
                              int64_t aDataSize, int32_t aFetchCount,
-                             uint32_t aLastModifiedTime, uint32_t aExpirationTime) = 0;
+                             uint32_t aLastModifiedTime, uint32_t aExpirationTime,
+                             bool aPinned) = 0;
   };
 
   // Invokes OnEntryInfo for the given aEntry, synchronously.
   static void GetCacheEntryInfo(CacheEntry* aEntry, EntryInfoCallback *aVisitor);
 
   // Memory reporting
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
--- a/netwerk/cache2/OldWrappers.cpp
+++ b/netwerk/cache2/OldWrappers.cpp
@@ -263,17 +263,17 @@ NS_IMETHODIMP _OldVisitCallbackWrapper::
   if (NS_FAILED(entryInfo->GetExpirationTime(&expirationTime)))
     expirationTime = 0;
   uint32_t lastModified;
   if (NS_FAILED(entryInfo->GetLastModified(&lastModified)))
     lastModified = 0;
 
   // Send them to the consumer.
   rv = mCB->OnCacheEntryInfo(
-    uri, enhanceId, (int64_t)dataSize, fetchCount, lastModified, expirationTime);
+    uri, enhanceId, (int64_t)dataSize, fetchCount, lastModified, expirationTime, false);
 
   *_retval = NS_SUCCEEDED(rv);
   return NS_OK;
 }
 
 // _OldGetDiskConsumption
 
 //static
--- a/netwerk/cache2/nsICacheStorageVisitor.idl
+++ b/netwerk/cache2/nsICacheStorageVisitor.idl
@@ -2,31 +2,32 @@
  * 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 "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
 
-[scriptable, uuid(946bd799-9410-4945-9085-79c7fe019e83)]
+[scriptable, uuid(6cc7c253-93b6-482b-8e9d-1e04d8e9d655)]
 interface nsICacheStorageVisitor : nsISupports
 {
   /**
    */
   void onCacheStorageInfo(in uint32_t aEntryCount,
                           in uint64_t aConsumption,
                           in uint64_t aCapacity,
                           in nsIFile aDiskDirectory);
 
   /**
    */
   void onCacheEntryInfo(in nsIURI aURI,
                         in ACString aIdEnhance,
                         in int64_t aDataSize,
                         in long aFetchCount,
                         in uint32_t aLastModifiedTime,
-                        in uint32_t aExpirationTime);
+                        in uint32_t aExpirationTime,
+                        in boolean aPinned);
 
   /**
    */
   void onCacheEntryVisitCompleted();
 };
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -144,28 +144,33 @@ struct ParamTraits<mozilla::net::Resourc
     WriteParam(aMsg, aParam.connectEnd);
     WriteParam(aMsg, aParam.requestStart);
     WriteParam(aMsg, aParam.responseStart);
     WriteParam(aMsg, aParam.responseEnd);
 
     WriteParam(aMsg, aParam.fetchStart);
     WriteParam(aMsg, aParam.redirectStart);
     WriteParam(aMsg, aParam.redirectEnd);
+
+    WriteParam(aMsg, aParam.transferSize);
+    WriteParam(aMsg, aParam.encodedBodySize);
   }
 
   static bool Read(const Message* aMsg, void** aIter, mozilla::net::ResourceTimingStruct* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->domainLookupStart) &&
            ReadParam(aMsg, aIter, &aResult->domainLookupEnd) &&
            ReadParam(aMsg, aIter, &aResult->connectStart) &&
            ReadParam(aMsg, aIter, &aResult->connectEnd) &&
            ReadParam(aMsg, aIter, &aResult->requestStart) &&
            ReadParam(aMsg, aIter, &aResult->responseStart) &&
            ReadParam(aMsg, aIter, &aResult->responseEnd) &&
            ReadParam(aMsg, aIter, &aResult->fetchStart) &&
            ReadParam(aMsg, aIter, &aResult->redirectStart) &&
-           ReadParam(aMsg, aIter, &aResult->redirectEnd);
+           ReadParam(aMsg, aIter, &aResult->redirectEnd) &&
+           ReadParam(aMsg, aIter, &aResult->transferSize) &&
+           ReadParam(aMsg, aIter, &aResult->encodedBodySize);
   }
 };
 
 } // namespace IPC
 
 #endif // mozilla_net_NeckoMessageUtils_h
--- a/netwerk/protocol/about/nsAboutCache.cpp
+++ b/netwerk/protocol/about/nsAboutCache.cpp
@@ -341,40 +341,43 @@ nsAboutCache::OnCacheStorageInfo(uint32_
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAboutCache::OnCacheEntryInfo(nsIURI *aURI, const nsACString & aIdEnhance,
                                int64_t aDataSize, int32_t aFetchCount,
-                               uint32_t aLastModified, uint32_t aExpirationTime)
+                               uint32_t aLastModified, uint32_t aExpirationTime,
+                               bool aPinned)
 {
     // We need mStream for this
     if (!mStream) {
         return NS_ERROR_FAILURE;
     }
 
     if (!mEntriesHeaderAdded) {
         mBuffer.AppendLiteral("<hr/>\n"
                               "<table id=\"entries\">\n"
                               "  <colgroup>\n"
                               "   <col id=\"col-key\">\n"
                               "   <col id=\"col-dataSize\">\n"
                               "   <col id=\"col-fetchCount\">\n"
                               "   <col id=\"col-lastModified\">\n"
                               "   <col id=\"col-expires\">\n"
+                              "   <col id=\"col-pinned\">\n"
                               "  </colgroup>\n"
                               "  <thead>\n"
                               "    <tr>\n"
                               "      <th>Key</th>\n"
                               "      <th>Data size</th>\n"
                               "      <th>Fetch count</th>\n"
                               "      <th>Last Modifed</th>\n"
                               "      <th>Expires</th>\n"
+                              "      <th>Pinning</th>\n"
                               "    </tr>\n"
                               "  </thead>\n");
         mEntriesHeaderAdded = true;
     }
 
     // Generate a about:cache-entry URL for this entry...
 
     nsAutoCString url;
@@ -441,16 +444,25 @@ nsAboutCache::OnCacheEntryInfo(nsIURI *a
     if (aExpirationTime < 0xFFFFFFFF) {
         PrintTimeString(buf, sizeof(buf), aExpirationTime);
         mBuffer.Append(buf);
     } else {
         mBuffer.AppendLiteral("No expiration time");
     }
     mBuffer.AppendLiteral("</td>\n");
 
+    // Pinning
+    mBuffer.AppendLiteral("    <td>");
+    if (aPinned) {
+      mBuffer.Append(NS_LITERAL_CSTRING("Pinned"));
+    } else {
+      mBuffer.Append(NS_LITERAL_CSTRING("&nbsp;"));
+    }
+    mBuffer.AppendLiteral("</td>\n");
+
     // Entry is done...
     mBuffer.AppendLiteral("  </tr>\n");
 
     FlushBuffer();
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -90,16 +90,19 @@ HttpBaseChannel::HttpBaseChannel()
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
   , mCorsIncludeCredentials(false)
   , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
   , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW)
   , mOnStartRequestCalled(false)
+  , mTransferSize(0)
+  , mDecodedBodySize(0)
+  , mEncodedBodySize(0)
   , mRequireCORSPreflight(false)
   , mWithCredentials(false)
   , mReportCollector(new ConsoleReportCollector())
   , mForceMainDocumentChannel(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list.
@@ -1088,16 +1091,37 @@ HttpBaseChannel::nsContentEncodings::Pre
 }
 
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
+HttpBaseChannel::GetTransferSize(uint64_t *aTransferSize)
+{
+  *aTransferSize = mTransferSize;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetDecodedBodySize(uint64_t *aDecodedBodySize)
+{
+  *aDecodedBodySize = mDecodedBodySize;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
+{
+  *aEncodedBodySize = mEncodedBodySize;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetRequestMethod(nsACString& aMethod)
 {
   aMethod = mRequestHead.Method();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod)
@@ -1746,16 +1770,40 @@ HttpBaseChannel::GetIsMainDocumentChanne
 
 NS_IMETHODIMP
 HttpBaseChannel::SetIsMainDocumentChannel(bool aValue)
 {
   mForceMainDocumentChannel = aValue;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::GetProtocolVersion(nsACString& aProtocolVersion)
+{
+  nsresult rv;
+  nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(mSecurityInfo, &rv);
+  nsAutoCString protocol;
+  if (NS_SUCCEEDED(rv) && ssl &&
+      NS_SUCCEEDED(ssl->GetNegotiatedNPN(protocol)) &&
+      !protocol.IsEmpty()) {
+    // The negotiated protocol was not empty so we can use it.
+    aProtocolVersion = protocol;
+    return NS_OK;
+  }
+
+  if (mResponseHead) {
+    uint32_t version = mResponseHead->Version();
+    aProtocolVersion.Assign(nsHttp::GetProtocolVersion(version));
+    return NS_OK;
+  }
+
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetTopWindowURI(nsIURI **aTopWindowURI)
 {
   nsresult rv = NS_OK;
@@ -2391,16 +2439,17 @@ void
 HttpBaseChannel::ReleaseListeners()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
   
   mListener = nullptr;
   mListenerContext = nullptr;
   mCallbacks = nullptr;
   mProgressSink = nullptr;
+  mCompressListener = nullptr;
 }
 
 void
 HttpBaseChannel::DoNotifyListener()
 {
   if (mListener) {
     MOZ_ASSERT(!mOnStartRequestCalled,
                "We should not call OnStartRequest twice");
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -159,19 +159,23 @@ public:
   NS_IMETHOD IsNoStoreResponse(bool *value) override;
   NS_IMETHOD IsNoCacheResponse(bool *value) override;
   NS_IMETHOD IsPrivateResponse(bool *value) override;
   NS_IMETHOD GetResponseStatus(uint32_t *aValue) override;
   NS_IMETHOD GetResponseStatusText(nsACString& aValue) override;
   NS_IMETHOD GetRequestSucceeded(bool *aValue) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
   NS_IMETHOD GetSchedulingContextID(nsID *aSCID) override;
+  NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
+  NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
+  NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SetSchedulingContextID(const nsID aSCID) override;
   NS_IMETHOD GetIsMainDocumentChannel(bool* aValue) override;
   NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
+  NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) override;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) override;
   NS_IMETHOD GetRequestVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD GetResponseVersion(uint32_t *major, uint32_t *minor) override;
   NS_IMETHOD SetCookie(const char *aCookieHeader) override;
   NS_IMETHOD GetThirdPartyFlags(uint32_t *aForce) override;
@@ -350,16 +354,19 @@ protected:
   nsCOMPtr<nsILoadGroup>            mLoadGroup;
   nsCOMPtr<nsISupports>             mOwner;
   nsCOMPtr<nsILoadInfo>             mLoadInfo;
   nsCOMPtr<nsIInterfaceRequestor>   mCallbacks;
   nsCOMPtr<nsIProgressEventSink>    mProgressSink;
   nsCOMPtr<nsIURI>                  mReferrer;
   nsCOMPtr<nsIApplicationCache>     mApplicationCache;
 
+  // An instance of nsHTTPCompressConv
+  nsCOMPtr<nsIStreamListener>       mCompressListener;
+
   nsHttpRequestHead                 mRequestHead;
   nsCOMPtr<nsIInputStream>          mUploadStream;
   nsCOMPtr<nsIRunnable>             mUploadCloneableCallback;
   nsAutoPtr<nsHttpResponseHead>     mResponseHead;
   RefPtr<nsHttpConnectionInfo>    mConnectionInfo;
   nsCOMPtr<nsIProxyInfo>            mProxyInfo;
   nsCOMPtr<nsISupports>             mSecurityInfo;
 
@@ -466,16 +473,20 @@ protected:
   bool mCorsIncludeCredentials;
   uint32_t mCorsMode;
   uint32_t mRedirectMode;
 
   // This parameter is used to ensure that we do not call OnStartRequest more
   // than once.
   bool mOnStartRequestCalled;
 
+  uint64_t mTransferSize;
+  uint64_t mDecodedBodySize;
+  uint64_t mEncodedBodySize;
+
   // The network interface id that's associated with this channel.
   nsCString mNetworkInterfaceId;
 
   nsID mSchedulingContextID;
   bool EnsureSchedulingContextID();
 
   bool                              mRequireCORSPreflight;
   bool                              mWithCredentials;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/net/DNS.h"
 #include "SerializedLoadContext.h"
 #include "nsInputStreamPump.h"
 #include "InterceptedChannel.h"
 #include "nsPerformance.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentSecurityManager.h"
 #include "nsIDeprecationWarner.h"
+#include "nsICompressConvStats.h"
 
 #ifdef OS_POSIX
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
@@ -498,29 +499,31 @@ HttpChannelChild::DoOnStartRequest(nsIRe
   if (NS_FAILED(rv)) {
     Cancel(rv);
     return;
   }
 
   if (mDivertingToParent) {
     mListener = nullptr;
     mListenerContext = nullptr;
+    mCompressListener = nullptr;
     if (mLoadGroup) {
       mLoadGroup->RemoveRequest(this, nullptr, mStatus);
     }
     return;
   }
 
   nsCOMPtr<nsIStreamListener> listener;
   rv = DoApplyContentConversions(mListener, getter_AddRefs(listener),
                                  mListenerContext);
   if (NS_FAILED(rv)) {
     Cancel(rv);
   } else if (listener) {
     mListener = listener;
+    mCompressListener = listener;
   }
 }
 
 class TransportAndDataEvent : public ChannelEvent
 {
  public:
   TransportAndDataEvent(HttpChannelChild* child,
                         const nsresult& channelStatus,
@@ -835,26 +838,33 @@ HttpChannelChild::OnStopRequest(const ns
 
   if (mUnknownDecoderInvolved) {
    LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
         this));
     mUnknownDecoderEventQ.AppendElement(
       new MaybeDivertOnStopHttpEvent(this, channelStatus));
   }
 
+  nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
+  if (conv) {
+      conv->GetDecodedDataLength(&mDecodedBodySize);
+  }
+
   mTransactionTimings.domainLookupStart = timing.domainLookupStart;
   mTransactionTimings.domainLookupEnd = timing.domainLookupEnd;
   mTransactionTimings.connectStart = timing.connectStart;
   mTransactionTimings.connectEnd = timing.connectEnd;
   mTransactionTimings.requestStart = timing.requestStart;
   mTransactionTimings.responseStart = timing.responseStart;
   mTransactionTimings.responseEnd = timing.responseEnd;
   mAsyncOpenTime = timing.fetchStart;
   mRedirectStartTimeStamp = timing.redirectStart;
   mRedirectEndTimeStamp = timing.redirectEnd;
+  mTransferSize = timing.transferSize;
+  mEncodedBodySize = timing.encodedBodySize;
 
   nsPerformance* documentPerformance = GetPerformance();
   if (documentPerformance) {
       documentPerformance->AddEntry(this, this);
   }
 
   DoPreOnStopRequest(channelStatus);
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1169,16 +1169,20 @@ HttpChannelParent::OnStopRequest(nsIRequ
   mChannel->GetConnectStart(&timing.connectStart);
   mChannel->GetConnectEnd(&timing.connectEnd);
   mChannel->GetRequestStart(&timing.requestStart);
   mChannel->GetResponseStart(&timing.responseStart);
   mChannel->GetResponseEnd(&timing.responseEnd);
   mChannel->GetAsyncOpen(&timing.fetchStart);
   mChannel->GetRedirectStart(&timing.redirectStart);
   mChannel->GetRedirectEnd(&timing.redirectEnd);
+  mChannel->GetTransferSize(&timing.transferSize);
+  mChannel->GetEncodedBodySize(&timing.encodedBodySize);
+  // decodedBodySize can be computed in the child process so it doesn't need
+  // to be passed down.
 
   if (mIPCClosed || !SendOnStopRequest(aStatusCode, timing))
     return NS_ERROR_UNEXPECTED;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIStreamListener
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -52,16 +52,28 @@ NullHttpChannel::Init(nsIURI *aURI,
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // NullHttpChannel::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
+NullHttpChannel::GetTransferSize(uint64_t *aTransferSize)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetDecodedBodySize(uint64_t *aDecodedBodySize)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetRequestMethod(nsACString & aRequestMethod)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetRequestMethod(const nsACString & aRequestMethod)
 {
@@ -227,16 +239,28 @@ NullHttpChannel::GetSchedulingContextID(
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetSchedulingContextID(const nsID scID)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+NullHttpChannel::GetProtocolVersion(nsACString& aProtocolVersion)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 //-----------------------------------------------------------------------------
 // NullHttpChannel::nsIChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 NullHttpChannel::GetOriginalURI(nsIURI * *aOriginalURI)
 {
   NS_IF_ADDREF(*aOriginalURI = mOriginalURI);
--- a/netwerk/protocol/http/TimingStruct.h
+++ b/netwerk/protocol/http/TimingStruct.h
@@ -19,14 +19,16 @@ struct TimingStruct {
   TimeStamp responseStart;
   TimeStamp responseEnd;
 };
 
 struct ResourceTimingStruct : TimingStruct {
   TimeStamp fetchStart;
   TimeStamp redirectStart;
   TimeStamp redirectEnd;
+  uint64_t transferSize;
+  uint64_t encodedBodySize;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -224,16 +224,36 @@ nsHttp::IsValidToken(const char *start, 
         const unsigned char idx = *start;
         if (idx > 127 || !kValidTokenMap[idx])
             return false;
     }
 
     return true;
 }
 
+const char*
+nsHttp::GetProtocolVersion(uint32_t pv)
+{
+    switch (pv) {
+    case SPDY_VERSION_31:
+        return "spdy/3.1";
+    case HTTP_VERSION_2:
+    case NS_HTTP_VERSION_2_0:
+        return "h2";
+    case NS_HTTP_VERSION_1_0:
+        return "http/1.0";
+    case NS_HTTP_VERSION_1_1:
+        return "http/1.1";
+    default:
+        NS_WARNING(nsPrintfCString("Unkown protocol version: 0x%X. "
+                                   "Please file a bug", pv).get());
+        return "http/1.1";
+    }
+}
+
 // static
 bool
 nsHttp::IsReasonableHeaderValue(const nsACString &s)
 {
   // Header values MUST NOT contain line-breaks.  RFC 2616 technically
   // permits CTL characters, including CR and LF, in header values provided
   // they are quoted.  However, this can lead to problems if servers do not
   // interpret quoted strings properly.  Disallowing CR and LF here seems
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -162,16 +162,19 @@ struct nsHttp
     static inline bool ParseInt64(const char *input, int64_t *result) {
         const char *next;
         return ParseInt64(input, &next, result) && *next == '\0';
     }
 
     // Return whether the HTTP status code represents a permanent redirect
     static bool IsPermanentRedirect(uint32_t httpStatus);
 
+    // Returns the APLN token which represents the used protocol version.
+    static const char* GetProtocolVersion(uint32_t pv);
+
     // Declare all atoms
     //
     // The atom names and values are stored in nsHttpAtomList.h and are brought
     // to you by the magic of C preprocessing.  Add new atoms to nsHttpAtomList
     // and all support logic will be auto-generated.
     //
 #define HTTP_ATOM(_name, _value) static nsHttpAtom _name;
 #include "nsHttpAtomList.h"
--- a/netwerk/protocol/http/nsHttpAuthCache.cpp
+++ b/netwerk/protocol/http/nsHttpAuthCache.cpp
@@ -11,28 +11,27 @@
 #include <stdlib.h>
 
 #include "mozilla/Attributes.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
+#include "mozilla/DebugOnly.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 namespace net {
 
 static inline void
-GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key)
+GetAuthKey(const char *scheme, const char *host, int32_t port, nsACString const &originSuffix, nsCString &key)
 {
     key.Truncate();
-    key.AppendInt(appId);
-    key.Append(':');
-    key.AppendInt(inBrowserElement);
+    key.Append(originSuffix);
     key.Append(':');
     key.Append(scheme);
     key.AppendLiteral("://");
     key.Append(host);
     key.Append(':');
     key.AppendInt(port);
 }
 
@@ -52,31 +51,31 @@ StrEquivalent(const char16_t *a, const c
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <public>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthCache::nsHttpAuthCache()
     : mDB(nullptr)
-    , mObserver(new AppDataClearObserver(this))
+    , mObserver(new OriginClearObserver(this))
 {
     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     if (obsSvc) {
-        obsSvc->AddObserver(mObserver, "webapps-clear-data", false);
+        obsSvc->AddObserver(mObserver, "clear-origin-data", false);
     }
 }
 
 nsHttpAuthCache::~nsHttpAuthCache()
 {
     if (mDB)
         ClearAll();
     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     if (obsSvc) {
-        obsSvc->RemoveObserver(mObserver, "webapps-clear-data");
+        obsSvc->RemoveObserver(mObserver, "clear-origin-data");
         mObserver->mOwner = nullptr;
     }
 }
 
 nsresult
 nsHttpAuthCache::Init()
 {
     NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
@@ -92,79 +91,76 @@ nsHttpAuthCache::Init()
     return NS_OK;
 }
 
 nsresult
 nsHttpAuthCache::GetAuthEntryForPath(const char *scheme,
                                      const char *host,
                                      int32_t     port,
                                      const char *path,
-                                     uint32_t    appId,
-                                     bool        inBrowserElement,
+                                     nsACString const &originSuffix,
                                      nsHttpAuthEntry **entry)
 {
     LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n",
         scheme, host, port, path));
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
     if (!node)
         return NS_ERROR_NOT_AVAILABLE;
 
     *entry = node->LookupEntryByPath(path);
     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme,
                                        const char *host,
                                        int32_t     port,
                                        const char *realm,
-                                       uint32_t    appId,
-                                       bool        inBrowserElement,
+                                       nsACString const &originSuffix,
                                        nsHttpAuthEntry **entry)
 
 {
     LOG(("nsHttpAuthCache::GetAuthEntryForDomain [key=%s://%s:%d realm=%s]\n",
         scheme, host, port, realm));
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
     if (!node)
         return NS_ERROR_NOT_AVAILABLE;
 
     *entry = node->LookupEntryByRealm(realm);
     return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
 nsresult
 nsHttpAuthCache::SetAuthEntry(const char *scheme,
                               const char *host,
                               int32_t     port,
                               const char *path,
                               const char *realm,
                               const char *creds,
                               const char *challenge,
-                              uint32_t    appId,
-                              bool        inBrowserElement,
+                              nsACString const &originSuffix,
                               const nsHttpAuthIdentity *ident,
                               nsISupports *metadata)
 {
     nsresult rv;
 
     LOG(("nsHttpAuthCache::SetAuthEntry [key=%s://%s:%d realm=%s path=%s metadata=%x]\n",
         scheme, host, port, realm, path, metadata));
 
     if (!mDB) {
         rv = Init();
         if (NS_FAILED(rv)) return rv;
     }
 
     nsAutoCString key;
-    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key);
+    nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key);
 
     if (!node) {
         // create a new entry node and set the given entry
         node = new nsHttpAuthNode();
         if (!node)
             return NS_ERROR_OUT_OF_MEMORY;
         rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
         if (NS_FAILED(rv))
@@ -177,24 +173,23 @@ nsHttpAuthCache::SetAuthEntry(const char
     return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
 }
 
 void
 nsHttpAuthCache::ClearAuthEntry(const char *scheme,
                                 const char *host,
                                 int32_t     port,
                                 const char *realm,
-                                uint32_t    appId,
-                                bool        inBrowserElement)
+                                nsACString const &originSuffix)
 {
     if (!mDB)
         return;
 
     nsAutoCString key;
-    GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
+    GetAuthKey(scheme, host, port, originSuffix, key);
     PL_HashTableRemove(mDB, key.get());
 }
 
 nsresult
 nsHttpAuthCache::ClearAll()
 {
     LOG(("nsHttpAuthCache::ClearAll\n"));
 
@@ -208,24 +203,23 @@ nsHttpAuthCache::ClearAll()
 //-----------------------------------------------------------------------------
 // nsHttpAuthCache <private>
 //-----------------------------------------------------------------------------
 
 nsHttpAuthNode *
 nsHttpAuthCache::LookupAuthNode(const char *scheme,
                                 const char *host,
                                 int32_t     port,
-                                uint32_t    appId,
-                                bool        inBrowserElement,
+                                nsACString const &originSuffix,
                                 nsCString  &key)
 {
     if (!mDB)
         return nullptr;
 
-    GetAuthKey(scheme, host, port, appId, inBrowserElement, key);
+    GetAuthKey(scheme, host, port, originSuffix, key);
 
     return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
 }
 
 void *
 nsHttpAuthCache::AllocTable(void *self, size_t size)
 {
     return malloc(size);
@@ -263,70 +257,66 @@ nsHttpAuthCache::FreeEntry(void *self, P
 PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
 {
     nsHttpAuthCache::AllocTable,
     nsHttpAuthCache::FreeTable,
     nsHttpAuthCache::AllocEntry,
     nsHttpAuthCache::FreeEntry
 };
 
-NS_IMPL_ISUPPORTS(nsHttpAuthCache::AppDataClearObserver, nsIObserver)
+NS_IMPL_ISUPPORTS(nsHttpAuthCache::OriginClearObserver, nsIObserver)
 
 NS_IMETHODIMP
-nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject,
-                                               const char *      topic,
-                                               const char16_t * data_unicode)
+nsHttpAuthCache::OriginClearObserver::Observe(nsISupports *subject,
+                                              const char *      topic,
+                                              const char16_t * data_unicode)
 {
     NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE);
 
-    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
-            do_QueryInterface(subject);
-    if (!params) {
-        NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
-        return NS_ERROR_UNEXPECTED;
+    OriginAttributesPattern pattern;
+    if (!pattern.Init(nsDependentString(data_unicode))) {
+        NS_ERROR("Cannot parse origin attributes pattern");
+        return NS_ERROR_FAILURE;
     }
 
-    uint32_t appId;
-    bool browserOnly;
-
-    nsresult rv = params->GetAppId(&appId);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = params->GetBrowserOnly(&browserOnly);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID);
-    mOwner->ClearAppData(appId, browserOnly);
+    mOwner->ClearOriginData(pattern);
     return NS_OK;
 }
 
 static int
-RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg)
+RemoveEntriesForPattern(PLHashEntry *entry, int32_t number, void *arg)
 {
     nsDependentCString key(static_cast<const char*>(entry->key));
-    nsAutoCString* prefix = static_cast<nsAutoCString*>(arg);
-    if (StringBeginsWith(key, *prefix)) {
+
+    // Extract the origin attributes suffix from the key.
+    int32_t colon = key.Find(NS_LITERAL_CSTRING(":"));
+    MOZ_ASSERT(colon != kNotFound);
+    nsDependentCSubstring oaSuffix;
+    oaSuffix.Rebind(key.BeginReading(), colon);
+
+    // Build the OriginAttributes object of it...
+    OriginAttributes oa;
+    DebugOnly<bool> rv = oa.PopulateFromSuffix(oaSuffix);
+    MOZ_ASSERT(rv);
+
+    // ...and match it against the given pattern.
+    OriginAttributesPattern const *pattern = static_cast<OriginAttributesPattern const*>(arg);
+    if (pattern->Matches(oa)) {
         return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE;
     }
     return HT_ENUMERATE_NEXT;
 }
 
 void
-nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly)
+nsHttpAuthCache::ClearOriginData(OriginAttributesPattern const &pattern)
 {
     if (!mDB) {
         return;
     }
-    nsAutoCString keyPrefix;
-    keyPrefix.AppendInt(appId);
-    keyPrefix.Append(':');
-    if (browserOnly) {
-        keyPrefix.AppendInt(browserOnly);
-        keyPrefix.Append(':');
-    }
-    PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix);
+    PL_HashTableEnumerateEntries(mDB, RemoveEntriesForPattern, (void*)&pattern);
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpAuthIdentity
 //-----------------------------------------------------------------------------
 
 nsresult
 nsHttpAuthIdentity::Set(const char16_t *domain,
--- a/netwerk/protocol/http/nsHttpAuthCache.h
+++ b/netwerk/protocol/http/nsHttpAuthCache.h
@@ -11,16 +11,19 @@
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "plhash.h"
 #include "nsIObserver.h"
 
 class nsCString;
 
 namespace mozilla {
+
+class OriginAttributesPattern;
+
 namespace net {
 
 struct nsHttpAuthPath {
     struct nsHttpAuthPath *mNext;
     char                   mPath[1];
 };
 
 //-----------------------------------------------------------------------------
@@ -176,86 +179,81 @@ public:
 
     // |scheme|, |host|, and |port| are required
     // |path| can be null
     // |entry| is either null or a weak reference
     nsresult GetAuthEntryForPath(const char *scheme,
                                  const char *host,
                                  int32_t     port,
                                  const char *path,
-                                 uint32_t    appId,
-                                 bool        inBrowserElement,
+                                 nsACString const &originSuffix,
                                  nsHttpAuthEntry **entry);
 
     // |scheme|, |host|, and |port| are required
     // |realm| must not be null
     // |entry| is either null or a weak reference
     nsresult GetAuthEntryForDomain(const char *scheme,
                                    const char *host,
                                    int32_t     port,
                                    const char *realm,
-                                   uint32_t    appId,
-                                   bool        inBrowserElement,
+                                   nsACString const &originSuffix,
                                    nsHttpAuthEntry **entry);
 
     // |scheme|, |host|, and |port| are required
     // |path| can be null
     // |realm| must not be null
     // if |credentials|, |user|, |pass|, and |challenge| are each
     // null, then the entry is deleted.
     nsresult SetAuthEntry(const char *scheme,
                           const char *host,
                           int32_t     port,
                           const char *directory,
                           const char *realm,
                           const char *credentials,
                           const char *challenge,
-                          uint32_t    appId,
-                          bool        inBrowserElement,
+                          nsACString const &originSuffix,
                           const nsHttpAuthIdentity *ident,
                           nsISupports *metadata);
 
     void ClearAuthEntry(const char *scheme,
                         const char *host,
                         int32_t     port,
                         const char *realm,
-                        uint32_t    appId,
-                        bool        inBrowserElement);
+                        nsACString const &originSuffix);
 
     // expire all existing auth list entries including proxy auths.
     nsresult ClearAll();
 
 private:
     nsHttpAuthNode *LookupAuthNode(const char *scheme,
                                    const char *host,
                                    int32_t     port,
-                                   uint32_t    appId,
-                                   bool        inBrowserElement,
+                                   nsACString const &originSuffix,
                                    nsCString  &key);
 
     // hash table allocation functions
     static void*        AllocTable(void *, size_t size);
     static void         FreeTable(void *, void *item);
     static PLHashEntry* AllocEntry(void *, const void *key);
     static void         FreeEntry(void *, PLHashEntry *he, unsigned flag);
 
     static PLHashAllocOps gHashAllocOps;
 
-    class AppDataClearObserver : public nsIObserver {
-      virtual ~AppDataClearObserver() {}
+    class OriginClearObserver : public nsIObserver {
+      virtual ~OriginClearObserver() {}
     public:
       NS_DECL_ISUPPORTS
       NS_DECL_NSIOBSERVER
-      explicit AppDataClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {}
+      explicit OriginClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {}
       nsHttpAuthCache* mOwner;
     };
 
-    void ClearAppData(uint32_t appId, bool browserOnly);
+    void ClearOriginData(OriginAttributesPattern const &pattern);
 
 private:
     PLHashTable *mDB; // "host:port" --> nsHttpAuthNode
-    RefPtr<AppDataClearObserver> mObserver;
+    RefPtr<OriginClearObserver> mObserver;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpAuthCache_h__
--- a/netwerk/protocol/http/nsHttpAuthManager.cpp
+++ b/netwerk/protocol/http/nsHttpAuthManager.cpp
@@ -62,36 +62,35 @@ nsHttpAuthManager::GetAuthIdentity(const
                                    nsAString & aUserName,
                                    nsAString & aUserPassword,
                                    bool aIsPrivate,
                                    nsIPrincipal* aPrincipal)
 {
   nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
   nsHttpAuthEntry * entry = nullptr;
   nsresult rv;
-  uint32_t appId = NECKO_NO_APP_ID;
-  bool inBrowserElement = false;
+
+  nsAutoCString originSuffix;
   if (aPrincipal) {
-    appId = aPrincipal->GetAppId();
-    inBrowserElement = aPrincipal->GetIsInBrowserElement();
+    BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originSuffix);
   }
 
   if (!aPath.IsEmpty())
     rv = auth_cache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(),
                                          PromiseFlatCString(aHost).get(),
                                          aPort,
                                          PromiseFlatCString(aPath).get(),
-                                         appId, inBrowserElement,
+                                         originSuffix,
                                          &entry);
   else
     rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(),
                                            PromiseFlatCString(aHost).get(),
                                            aPort,
                                            PromiseFlatCString(aRealm).get(),
-                                           appId, inBrowserElement,
+                                           originSuffix,
                                            &entry);
 
   if (NS_FAILED(rv))
     return rv;
   if (!entry)
     return NS_ERROR_UNEXPECTED;
 
   aUserDomain.Assign(entry->Domain());
@@ -112,32 +111,31 @@ nsHttpAuthManager::SetAuthIdentity(const
                                    const nsAString & aUserPassword,
                                    bool aIsPrivate,
                                    nsIPrincipal* aPrincipal)
 {
   nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(),
                            PromiseFlatString(aUserName).get(),
                            PromiseFlatString(aUserPassword).get());
 
-  uint32_t appId = NECKO_NO_APP_ID;
-  bool inBrowserElement = false;
+  nsAutoCString originSuffix;
   if (aPrincipal) {
-    appId = aPrincipal->GetAppId();
-    inBrowserElement = aPrincipal->GetIsInBrowserElement();
+    BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originSuffix);
   }
 
+
   nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache;
   return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(),
                                   PromiseFlatCString(aHost).get(),
                                   aPort,
                                   PromiseFlatCString(aPath).get(),
                                   PromiseFlatCString(aRealm).get(),
                                   nullptr,  // credentials
                                   nullptr,  // challenge
-                                  appId, inBrowserElement,
+                                  originSuffix,
                                   &ident,
                                   nullptr); // metadata
 }
 
 NS_IMETHODIMP
 nsHttpAuthManager::ClearAll()
 {
   nsresult rv = mAuthCache->ClearAll();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -84,16 +84,17 @@
 #include "InterceptedChannel.h"
 #include "nsIHttpPushListener.h"
 #include "nsIX509Cert.h"
 #include "ScopedNSSTypes.h"
 #include "nsNullPrincipal.h"
 #include "nsIPackagedAppService.h"
 #include "nsIDeprecationWarner.h"
 #include "nsIDocument.h"
+#include "nsICompressConvStats.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
@@ -1054,16 +1055,17 @@ nsHttpChannel::CallOnStartRequest()
       nsCOMPtr<nsIStreamListener> listener;
       nsISupports *ctxt = mListenerContext;
       rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), ctxt);
       if (NS_FAILED(rv)) {
         return rv;
       }
       if (listener) {
         mListener = listener;
+        mCompressListener = listener;
       }
     }
 
     rv = EnsureAssocReq();
     if (NS_FAILED(rv))
         return rv;
 
     // if this channel is for a download, close off access to the cache.
@@ -5346,16 +5348,29 @@ nsHttpChannel::BeginConnect()
          channelClassifier.get(), this));
     channelClassifier->Start(this);
     if (callContinueBeginConnect) {
         ContinueBeginConnect();
     }
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
+{
+    if (mCacheEntry && !mCacheEntryIsWriteOnly) {
+        int64_t dataSize = 0;
+        mCacheEntry->GetDataSize(&dataSize);
+        *aEncodedBodySize = dataSize;
+    } else {
+        *aEncodedBodySize = mLogicalOffset;
+    }
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpChannelInternal
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
 {
     ENSURE_CALLED_BEFORE_CONNECT();
@@ -5839,16 +5854,21 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             else
                 NS_NOTREACHED("unexpected request");
         }
         // Do not to leave the transaction in a suspended state in error cases.
         if (NS_FAILED(status) && mTransaction)
             gHttpHandler->CancelTransaction(mTransaction, status);
     }
 
+    nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
+    if (conv) {
+        conv->GetDecodedDataLength(&mDecodedBodySize);
+    }
+
     if (mTransaction) {
         // determine if we should call DoAuthRetry
         bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
 
         //
         // grab references to connection in case we need to retry an
         // authentication request over it or use it for an upgrade
         // to another protocol.
@@ -5866,16 +5886,18 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             if (conn && !conn->IsPersistent())
                 conn = nullptr;
         }
 
         RefPtr<nsAHttpConnection> stickyConn;
         if (mCaps & NS_HTTP_STICKY_CONNECTION)
             stickyConn = mTransaction->GetConnectionReference();
 
+        mTransferSize = mTransaction->GetTransferSize();
+
         // at this point, we're done with the transaction
         mTransactionTimings = mTransaction->Timings();
         mTransaction = nullptr;
         mTransactionPump = nullptr;
 
         // We no longer need the dns prefetch object
         if (mDNSPrefetch && mDNSPrefetch->TimingsValid()
             && !mTransactionTimings.requestStart.IsNull()
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -127,16 +127,18 @@ public:
     // nsIRequest
     NS_IMETHOD Cancel(nsresult status) override;
     NS_IMETHOD Suspend() override;
     NS_IMETHOD Resume() override;
     // nsIChannel
     NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
     NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
     NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
+    // nsIHttpChannel
+    NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
     // nsIHttpChannelInternal
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
     NS_IMETHOD ForceIntercepted(uint64_t aInterceptionID) override;
     // nsISupportsPriority
     NS_IMETHOD SetPriority(int32_t value) override;
     // nsIClassOfService
     NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
     NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
@@ -525,17 +527,16 @@ private:
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 
     nsCString mUsername;
 
     // If non-null, warnings should be reported to this object.
     HttpChannelSecurityWarningReporter* mWarningReporter;
-
 protected:
     virtual void DoNotifyListenerCleanup() override;
 
 private: // cache telemetry
     bool mDidReval;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpChannel, NS_HTTPCHANNEL_IID)
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -36,29 +36,26 @@ namespace net {
 #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN 1
 #define SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL 2
 
 #define HTTP_AUTH_DIALOG_TOP_LEVEL_DOC 0
 #define HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE 1
 #define HTTP_AUTH_DIALOG_CROSS_ORIGIN_SUBRESOURCE 2
 
 static void
-GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem)
+GetOriginAttributesSuffix(nsIChannel* aChan, nsACString &aSuffix)
 {
-    nsCOMPtr<nsILoadContext> loadContext;
+    OriginAttributes oa;
+
+    // Deliberately ignoring the result and going with defaults
     if (aChan) {
-        NS_QueryNotificationCallbacks(aChan, loadContext);
+        NS_GetOriginAttributes(aChan, oa);
     }
-    if (!loadContext) {
-        *aAppId = NECKO_NO_APP_ID;
-        *aInBrowserElem = false;
-    } else {
-        loadContext->GetAppId(aAppId);
-        loadContext->GetIsInBrowserElement(aInBrowserElem);
-    }
+
+    oa.CreateSuffix(aSuffix);
 }
 
 nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
     : mAuthChannel(nullptr)
     , mIsPrivate(false)
     , mProxyAuthContinuationState(nullptr)
     , mAuthContinuationState(nullptr)
     , mProxyAuth(false)
@@ -397,30 +394,29 @@ nsHttpChannelAuthProvider::GenCredsAndSe
 
     bool saveIdentity =
         0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
 
     // this getter never fails
     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
 
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
-    uint32_t appId;
-    bool isInBrowserElement;
-    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+    nsAutoCString suffix;
+    GetOriginAttributesSuffix(chan, suffix);
 
     // create a cache entry.  we do this even though we don't yet know that
     // these credentials are valid b/c we need to avoid prompting the user
     // more than once in case the credentials are valid.
     //
     // if the credentials are not reusable, then we don't bother sticking
     // them in the auth cache.
     rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
                                  saveCreds ? *result : nullptr,
                                  saveChallenge ? challenge : nullptr,
-                                 appId, isInBrowserElement,
+                                 suffix,
                                  saveIdentity ? &ident : nullptr,
                                  sessionState);
     return rv;
 }
 
 nsresult
 nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth)
 {
@@ -673,30 +669,28 @@ nsHttpChannelAuthProvider::GetCredential
         // regardless of the LOAD_ANONYMOUS flag
     }
     else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
         LOG(("Skipping authentication for anonymous non-proxy request\n"));
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
-    uint32_t appId;
-    bool isInBrowserElement;
-    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+    nsAutoCString suffix;
+    GetOriginAttributesSuffix(chan, suffix);
 
     //
     // if we already tried some credentials for this transaction, then
     // we need to possibly clear them from the cache, unless the credentials
     // in the cache have changed, in which case we'd want to give them a
     // try instead.
     //
     nsHttpAuthEntry *entry = nullptr;
     authCache->GetAuthEntryForDomain(scheme.get(), host, port,
-                                     realm.get(), appId,
-                                     isInBrowserElement, &entry);
+                                     realm.get(), suffix, &entry);
 
     // hold reference to the auth session state (in case we clear our
     // reference to the entry).
     nsCOMPtr<nsISupports> sessionStateGrip;
     if (entry)
         sessionStateGrip = entry->mMetaData;
 
     // for digest auth, maybe our cached nonce value simply timed out...
@@ -717,17 +711,17 @@ nsHttpChannelAuthProvider::GetCredential
         if (entry) {
             if (ident->Equals(entry->Identity())) {
                 if (!identFromURI) {
                     LOG(("  clearing bad auth cache entry\n"));
                     // ok, we've already tried this user identity, so clear the
                     // corresponding entry from the auth cache.
                     authCache->ClearAuthEntry(scheme.get(), host,
                                               port, realm.get(),
-                                              appId, isInBrowserElement);
+                                              suffix);
                     entry = nullptr;
                     ident->Clear();
                 }
             }
             else if (!identFromURI ||
                      (nsCRT::strcmp(ident->User(),
                                     entry->Identity().User()) == 0 &&
                      !(loadFlags &
@@ -1120,25 +1114,23 @@ NS_IMETHODIMP nsHttpChannelAuthProvider:
                                  path, ident, continuationState);
     if (NS_FAILED(rv))
         OnAuthCancelled(aContext, false);
 
     nsAutoCString realm;
     ParseRealm(mCurrentChallenge.get(), realm);
 
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
-    uint32_t appId;
-    bool isInBrowserElement;
-    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+    nsAutoCString suffix;
+    GetOriginAttributesSuffix(chan, suffix);
 
     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     nsHttpAuthEntry *entry = nullptr;
     authCache->GetAuthEntryForDomain(scheme.get(), host, port,
-                                     realm.get(), appId,
-                                     isInBrowserElement,
+                                     realm.get(), suffix,
                                      &entry);
 
     nsCOMPtr<nsISupports> sessionStateGrip;
     if (entry)
         sessionStateGrip = entry->mMetaData;
 
     nsAuthInformationHolder* holder =
             static_cast<nsAuthInformationHolder*>(aAuthInfo);
@@ -1351,22 +1343,21 @@ nsHttpChannelAuthProvider::SetAuthorizat
 
     if (header == nsHttp::Proxy_Authorization) {
         continuationState = &mProxyAuthContinuationState;
     } else {
         continuationState = &mAuthContinuationState;
     }
 
     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
-    uint32_t appId;
-    bool isInBrowserElement;
-    GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
+    nsAutoCString suffix;
+    GetOriginAttributesSuffix(chan, suffix);
 
     rv = authCache->GetAuthEntryForPath(scheme, host, port, path,
-                                        appId, isInBrowserElement, &entry);
+                                        suffix, &entry);
     if (NS_SUCCEEDED(rv)) {
         // if we are trying to add a header for origin server auth and if the
         // URL contains an explicit username, then try the given username first.
         // we only want to do this, however, if we know the URL requires auth
         // based on the presence of an auth cache entry for this URL (which is
         // true since we are here).  but, if the username from the URL matches
         // the username from the cache, then we should prefer the password
         // stored in the cache since that is most likely to be valid.
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -91,16 +91,17 @@ LogHeaders(const char *lineStart)
 nsHttpTransaction::nsHttpTransaction()
     : mLock("transaction lock")
     , mRequestSize(0)
     , mConnection(nullptr)
     , mRequestHead(nullptr)
     , mResponseHead(nullptr)
     , mContentLength(-1)
     , mContentRead(0)
+    , mTransferSize(0)
     , mInvalidResponseBytesRead(0)
     , mPushedStream(nullptr)
     , mInitialRwin(0)
     , mChunkedDecoder(nullptr)
     , mStatus(NS_OK)
     , mPriority(0)
     , mRestartCount(0)
     , mCaps(0)
@@ -768,16 +769,17 @@ nsHttpTransaction::WritePipeSegment(nsIO
     // OK, now let the caller fill this segment with data.
     //
     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
 
     MOZ_ASSERT(*countWritten > 0, "bad writer");
     trans->CountRecvBytes(*countWritten);
     trans->mReceivedData = true;
+    trans->mTransferSize += *countWritten;
 
     // Let the transaction "play" with the buffer.  It is free to modify
     // the contents of the buffer and/or modify countWritten.
     // - Bytes in HTTP headers don't count towards countWritten, so the input
     // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
     // OnInputStreamReady until all headers have been parsed.
     //
     rv = trans->ProcessData(buf, *countWritten, countWritten);
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -157,16 +157,18 @@ public:
     mozilla::TimeStamp GetDomainLookupStart();
     mozilla::TimeStamp GetDomainLookupEnd();
     mozilla::TimeStamp GetConnectStart();
     mozilla::TimeStamp GetConnectEnd();
     mozilla::TimeStamp GetRequestStart();
     mozilla::TimeStamp GetResponseStart();
     mozilla::TimeStamp GetResponseEnd();
 
+    int64_t GetTransferSize() { return mTransferSize; }
+
 private:
     friend class DeleteHttpTransaction;
     virtual ~nsHttpTransaction();
 
     nsresult Restart();
     nsresult RestartInProgress();
     char    *LocateHttpStart(char *buf, uint32_t len,
                              bool aAllowPartialMatch);
@@ -237,16 +239,17 @@ private:
 
     nsAHttpSegmentReader           *mReader;
     nsAHttpSegmentWriter           *mWriter;
 
     nsCString                       mLineBuf;         // may contain a partial line
 
     int64_t                         mContentLength;   // equals -1 if unknown
     int64_t                         mContentRead;     // count of consumed content bytes
+    int64_t                         mTransferSize; // count of received bytes
 
     // After a 304/204 or other "no-content" style response we will skip over
     // up to MAX_INVALID_RESPONSE_BODY_SZ bytes when looking for the next
     // response header to deal with servers that actually sent a response
     // body where they should not have. This member tracks how many bytes have
     // so far been skipped.
     uint32_t                        mInvalidResponseBytesRead;
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor;
 
 /**
  * nsIHttpChannel
  *
  * This interface allows for the modification of HTTP request parameters and
  * the inspection of the resulting HTTP response status and headers when they
  * become available.
  */
-[builtinclass, scriptable, uuid(e90acf2d-eaf2-41d8-97b2-c8d99f6437a1)]
+[builtinclass, scriptable, uuid(b2596105-3d0d-4e6a-824f-0539713bb879)]
 interface nsIHttpChannel : nsIChannel
 {
     /**************************************************************************
      * REQUEST CONFIGURATION
      *
      * Modifying request parameters after asyncOpen has been called is an error.
      */
 
@@ -80,16 +80,41 @@ interface nsIHttpChannel : nsIChannel
     readonly attribute unsigned long referrerPolicy;
 
     /**
      * Set the HTTP referrer URI with a referrer policy.
      */
     void setReferrerWithPolicy(in nsIURI referrer, in unsigned long referrerPolicy);
 
     /**
+     * Returns the network protocol used to fetch the resource as identified
+     * by the ALPN Protocol ID.
+     *
+     * @throws NS_ERROR_NOT_AVAILABLE if called before the response
+     *         has been received (before onStartRequest).
+     */
+    readonly attribute ACString protocolVersion;
+
+    /**
+     * size consumed by the response header fields and the response payload body
+     */
+    readonly attribute uint64_t transferSize;
+
+    /**
+     * The size of the message body received by the client,
+     * after removing any applied content-codings
+     */
+    readonly attribute uint64_t decodedBodySize;
+
+    /**
+     * The size in octets of the payload body, prior to removing content-codings
+     */
+    readonly attribute uint64_t encodedBodySize;
+
+    /**
      * Get the value of a particular request header.
      *
      * @param aHeader
      *        The case-insensitive name of the request header to query (e.g.,
      *        "Cache-Control").
      *
      * @return the value of the request header.
      * @throws NS_ERROR_NOT_AVAILABLE if the header is not set.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -146,16 +146,34 @@ nsViewSourceChannel::InitSrcdoc(nsIURI* 
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetName(nsACString &result)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::GetTransferSize(uint64_t *aTransferSize)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::GetDecodedBodySize(uint64_t *aDecodedBodySize)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsViewSourceChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::IsPending(bool *result)
 {
     NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
 
     return mChannel->IsPending(result);
 }
 
 NS_IMETHODIMP
@@ -632,16 +650,22 @@ nsViewSourceChannel::GetBaseURI(nsIURI**
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetBaseURI(nsIURI* aBaseURI)
 {
   mBaseURI = aBaseURI;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsViewSourceChannel::GetProtocolVersion(nsACString& aProtocolVersion)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 // nsIRequestObserver methods
 NS_IMETHODIMP
 nsViewSourceChannel::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
     NS_ENSURE_TRUE(mListener, NS_ERROR_FAILURE);
     // The channel may have gotten redirected... Time to update our info
     mChannel = do_QueryInterface(aRequest);
     mHttpChannel = do_QueryInterface(aRequest);
--- a/netwerk/streamconv/converters/moz.build
+++ b/netwerk/streamconv/converters/moz.build
@@ -1,14 +1,20 @@
 # -*- 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/.
 
+XPIDL_SOURCES += [
+    'nsICompressConvStats.idl'
+]
+
+XPIDL_MODULE = 'necko_http'
+
 UNIFIED_SOURCES += [
     'mozTXTToHTMLConv.cpp',
     'nsDirIndex.cpp',
     'nsDirIndexParser.cpp',
     'nsHTTPCompressConv.cpp',
     'nsIndexedToHTML.cpp',
     'nsMultiMixedConv.cpp',
     'nsTXTToHTMLConv.cpp',
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
@@ -25,32 +25,34 @@ extern PRLogModuleInfo *gHttpLog;
 
 namespace mozilla {
 namespace net {
 
 // nsISupports implementation
 NS_IMPL_ISUPPORTS(nsHTTPCompressConv,
                   nsIStreamConverter,
                   nsIStreamListener,
-                  nsIRequestObserver)
+                  nsIRequestObserver,
+                  nsICompressConvStats)
 
 // nsFTPDirListingConv methods
 nsHTTPCompressConv::nsHTTPCompressConv()
   : mMode(HTTP_COMPRESS_IDENTITY)
   , mOutBuffer(nullptr)
   , mInpBuffer(nullptr)
   , mOutBufferLen(0)
   , mInpBufferLen(0)
   , mCheckHeaderDone(false)
   , mStreamEnded(false)
   , mStreamInitialized(false)
   , mLen(0)
   , hMode(0)
   , mSkipCount(0)
   , mFlags(0)
+  , mDecodedDataLength(0)
 {
   LOG(("nsHttpCompresssConv %p ctor\n", this));
   if (NS_IsMainThread()) {
     mFailUncleanStops =
       Preferences::GetBool("network.http.enforce-framing.http", false);
   } else {
     mFailUncleanStops = false;
   }
@@ -70,16 +72,23 @@ nsHTTPCompressConv::~nsHTTPCompressConv(
   // For some reason we are not getting Z_STREAM_END.  But this was also seen
   //    for mozilla bug 198133.  Need to handle this case.
   if (mStreamInitialized && !mStreamEnded) {
     inflateEnd (&d_stream);
   }
 }
 
 NS_IMETHODIMP
+nsHTTPCompressConv::GetDecodedDataLength(uint64_t *aDecodedDataLength)
+{
+    *aDecodedDataLength = mDecodedDataLength;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsHTTPCompressConv::AsyncConvertData(const char *aFromType,
                                      const char *aToType,
                                      nsIStreamListener *aListener,
                                      nsISupports *aCtxt)
 {
   if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) ||
       !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) {
     mMode = HTTP_COMPRESS_COMPRESS;
@@ -471,16 +480,17 @@ nsHTTPCompressConv::do_OnDataAvailable(n
   mStream->ShareData(buffer, count);
 
   nsresult rv = mListener->OnDataAvailable(request, context, mStream,
                                            offset, count);
 
   // Make sure the stream no longer references |buffer| in case our listener
   // is crazy enough to try to read from |mStream| after ODA.
   mStream->ShareData("", 0);
+  mDecodedDataLength += count;
 
   return rv;
 }
 
 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
 #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.h
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined (__nsHTTPCompressConv__h__)
 #define	__nsHTTPCompressConv__h__	1
 
 #include "nsIStreamConverter.h"
+#include "nsICompressConvStats.h"
 #include "nsCOMPtr.h"
 
 #include "zlib.h"
 
 // brotli includes
 #undef assert
 #include "assert.h"
 #include "state.h"
@@ -67,22 +68,26 @@ public:
   size_t       mTotalOut;
   nsresult     mStatus;
 
   nsIRequest  *mRequest;
   nsISupports *mContext;
   uint64_t     mSourceOffset;
 };
 
-class nsHTTPCompressConv : public nsIStreamConverter	{
+class nsHTTPCompressConv
+  : public nsIStreamConverter
+  , public nsICompressConvStats
+{
   public:
   // nsISupports methods
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSICOMPRESSCONVSTATS
 
   // nsIStreamConverter methods
     NS_DECL_NSISTREAMCONVERTER
 
     nsHTTPCompressConv ();
 
 private:
     virtual ~nsHTTPCompressConv ();
@@ -114,14 +119,16 @@ private:
     bool        mStreamInitialized;
     bool        mDummyStreamInitialised;
     bool        mFailUncleanStops;
 
     z_stream d_stream;
     unsigned mLen, hMode, mSkipCount, mFlags;
 
     uint32_t check_header (nsIInputStream *iStr, uint32_t streamLen, nsresult *rv);
+
+    uint32_t mDecodedDataLength;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/netwerk/streamconv/converters/nsICompressConvStats.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsICompressConvStats
+ *
+ * This interface allows for the observation of decoded resource sizes
+ */
+[builtinclass, scriptable, uuid(58172ad0-46a9-4893-8fde-cd909c10792a)]
+interface nsICompressConvStats : nsISupports
+{
+    readonly attribute uint64_t decodedDataLength;
+};
--- a/netwerk/test/unit/test_auth_jar.js
+++ b/netwerk/test/unit/test_auth_jar.js
@@ -18,22 +18,18 @@ function run_test() {
   var app1browser = secMan.createCodebasePrincipal(createURI(kURI1), {appId: 1, inBrowser: true});
 
   var am = Cc["@mozilla.org/network/http-auth-manager;1"].
            getService(Ci.nsIHttpAuthManager);
   am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user", "pass", false, app1);
   am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user3", "pass3", false, app1browser);
   am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user2", "pass2", false, app10);
 
-  let subject = {
-    appId: 1,
-    browserOnly: true,
-    QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
-  };
-  Services.obs.notifyObservers(subject, "webapps-clear-data", null);
+  let attrs_inBrowser = JSON.stringify({ appId:1, inBrowser:true });
+  Services.obs.notifyObservers(null, "clear-origin-data", attrs_inBrowser);
   
   var domain = {value: ""}, user = {value: ""}, pass = {value: ""};
   try {
     am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1browser);
     do_check_false(true); // no identity should be present
   } catch (x) {
     do_check_eq(domain.value, "");
     do_check_eq(user.value, "");
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -215,42 +215,63 @@ function test_clearedSpec()
   Assert.throws(() => { url.spec = ""; }, "set empty spec");
   do_check_eq(url.spec, "http://example.com/path");
   url.host = "allizom.org";
 
   var ref = stringToURL("http://allizom.org/path");
   symmetricEquality(true, url, ref);
 }
 
-function test_escapeQueryBrackets()
+function test_escapeBrackets()
 {
+  // Query
   var url = stringToURL("http://example.com/?a[x]=1");
   do_check_eq(url.spec, "http://example.com/?a[x]=1");
 
   url = stringToURL("http://example.com/?a%5Bx%5D=1");
   do_check_eq(url.spec, "http://example.com/?a%5Bx%5D=1");
 
   url = stringToURL("http://[2001::1]/?a[x]=1");
   do_check_eq(url.spec, "http://[2001::1]/?a[x]=1");
 
   url = stringToURL("http://[2001::1]/?a%5Bx%5D=1");
   do_check_eq(url.spec, "http://[2001::1]/?a%5Bx%5D=1");
+
+  // Path
+  url = stringToURL("http://example.com/brackets[x]/test");
+  do_check_eq(url.spec, "http://example.com/brackets[x]/test");
+
+  url = stringToURL("http://example.com/a%5Bx%5D/test");
+  do_check_eq(url.spec, "http://example.com/a%5Bx%5D/test");
+
 }
 
 function test_apostropheEncoding()
 {
   // For now, single quote is escaped everywhere _except_ the path.
   // This policy is controlled by the bitmask in nsEscape.cpp::EscapeChars[]
   var url = stringToURL("http://example.com/dir'/file'.ext'");
   do_check_eq(url.spec, "http://example.com/dir'/file'.ext'");
 }
 
+function test_accentEncoding()
+{
+  var url = stringToURL("http://example.com/?hello=`");
+  do_check_eq(url.spec, "http://example.com/?hello=`");
+  do_check_eq(url.query, "hello=`");
+
+  url = stringToURL("http://example.com/?hello=%2C");
+  do_check_eq(url.spec, "http://example.com/?hello=%2C");
+  do_check_eq(url.query, "hello=%2C");
+}
+
 function run_test()
 {
   test_setEmptyPath();
   test_setQuery();
   test_setRef();
   test_ipv6();
   test_ipv6_fail();
   test_clearedSpec();
-  test_escapeQueryBrackets();
+  test_escapeBrackets();
   test_apostropheEncoding();
+  test_accentEncoding();
 }
--- a/security/sandbox/linux/SandboxFilterUtil.cpp
+++ b/security/sandbox/linux/SandboxFilterUtil.cpp
@@ -9,20 +9,25 @@
 #ifndef ANDROID
 #include <linux/ipc.h>
 #endif
 #include <linux/net.h>
 
 #include "mozilla/UniquePtr.h"
 #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
 
+// Older kernel headers (mostly Android, but also some older desktop
+// distributions) are missing some or all of these:
 #ifndef SYS_ACCEPT4
-// Android's kernel headers don't define these.
 #define SYS_ACCEPT4  18
+#endif
+#ifndef SYS_RECVMMSG
 #define SYS_RECVMMSG 19
+#endif
+#ifndef SYS_SENDMMSG
 #define SYS_SENDMMSG 20
 #endif
 
 using namespace sandbox::bpf_dsl;
 #define CASES SANDBOX_BPF_DSL_CASES
 
 namespace mozilla {
 
--- a/testing/mozbase/mozleak/mozleak/leaklog.py
+++ b/testing/mozbase/mozleak/mozleak/leaklog.py
@@ -135,24 +135,25 @@ def process_single_leak_file(leakLogFile
                 else:
                     recordLeakedObjects = False
             if size < 0 or bytesLeaked < 0 or numLeaked < 0:
                 leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s negative leaks caught!"
                                     % processString)
                 logAsWarning = True
                 continue
             if name != "TOTAL" and numLeaked != 0 and recordLeakedObjects:
-                leakedObjectNames.append(name)
-
                 currExpectedLeak = expectedLeaks.get(name, 0)
                 if not expectedLeaks or numLeaked <= currExpectedLeak:
+                    if not expectedLeaks:
+                        leakedObjectNames.append(name)
                     leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s"
                                                 % (processString, numLeaked, name))
                 else:
-                    leakedObjectAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s leaked too many %s (expected %d, got %d)"
+                    leakedObjectNames.append(name)
+                    leakedObjectAnalysis.append("WARNING | leakcheck | %s leaked too many %s (expected %d, got %d)"
                                                 % (processString, name, currExpectedLeak, numLeaked))
 
 
     leakAnalysis.extend(leakedObjectAnalysis)
     if logAsWarning:
         log.warning('\n'.join(leakAnalysis))
     else:
         log.info('\n'.join(leakAnalysis))
@@ -174,31 +175,32 @@ def process_single_leak_file(leakLogFile
                      % leakLogFileName)
         return
 
     if totalBytesLeaked == 0:
         log.info("TEST-PASS | leakcheck | %s no leaks detected!" %
                  processString)
         return
 
-    # totalBytesLeaked was seen and is non-zero.
-    if totalBytesLeaked > leakThreshold:
+    if totalBytesLeaked > leakThreshold or (expectedLeaks and leakedObjectNames):
         logAsWarning = True
         # Fail the run if we're over the threshold (which defaults to 0)
         prefix = "TEST-UNEXPECTED-FAIL"
     else:
         prefix = "WARNING"
     # Create a comma delimited string of the first N leaked objects found,
     # to aid with bug summary matching in TBPL. Note: The order of the objects
     # had no significance (they're sorted alphabetically).
     maxSummaryObjects = 5
     leakedObjectSummary = ', '.join(leakedObjectNames[:maxSummaryObjects])
     if len(leakedObjectNames) > maxSummaryObjects:
         leakedObjectSummary += ', ...'
 
+    # totalBytesLeaked will include any expected leaks, so it can be off
+    # by a few thousand bytes.
     if logAsWarning:
         log.warning("%s | leakcheck | %s %d bytes leaked (%s)"
                     % (prefix, processString, totalBytesLeaked, leakedObjectSummary))
     else:
         log.info("%s | leakcheck | %s %d bytes leaked (%s)"
                  % (prefix, processString, totalBytesLeaked, leakedObjectSummary))
 
 
--- a/testing/mozbase/mozprofile/mozprofile/profile.py
+++ b/testing/mozbase/mozprofile/mozprofile/profile.py
@@ -383,18 +383,16 @@ class FirefoxProfile(Profile):
                    # Don't report telemetry information
                    'toolkit.telemetry.enabled' : False,
                    # Don't send Telemetry reports to the production server. This is
                    # needed as Telemetry sends pings also if FHR upload is enabled.
                    'toolkit.telemetry.server' : 'http://%(server)s/telemetry-dummy/',
                    # Our current tests expect the unified Telemetry feature to be opt-out,
                    # which is not true while we hold back shipping it.
                    'toolkit.telemetry.unifiedIsOptIn': True,
-                   # Disable periodic updates of service workers
-                   'dom.serviceWorkers.periodic-updates.enabled': False,
                    }
 
 class MetroFirefoxProfile(Profile):
     """Specialized Profile subclass for Firefox Metro"""
 
     preferences = {# Don't automatically update the application for desktop and metro build
                    'app.update.enabled' : False,
                    'app.update.metro.enabled' : False,
@@ -431,18 +429,16 @@ class MetroFirefoxProfile(Profile):
                    'security.notification_enable_delay' : 0,
                    # Suppress automatic safe mode after crashes
                    'toolkit.startup.max_resumed_crashes' : -1,
                    # Don't report telemetry information
                    'toolkit.telemetry.enabled' : False,
                    # Don't send Telemetry reports to the production server. This is
                    # needed as Telemetry sends pings also if FHR upload is enabled.
                    'toolkit.telemetry.server' : 'http://%(server)s/telemetry-dummy/',
-                   # Disable periodic updates of service workers
-                   'dom.serviceWorkers.periodic-updates.enabled': False,
                    }
 
 class ThunderbirdProfile(Profile):
     """Specialized Profile subclass for Thunderbird"""
 
     preferences = {'extensions.update.enabled'    : False,
                    'extensions.update.notifyUser' : False,
                    'browser.shell.checkDefaultBrowser' : False,
--- a/testing/mozharness/scripts/release/antivirus.py
+++ b/testing/mozharness/scripts/release/antivirus.py
@@ -103,17 +103,17 @@ class AntivirusScan(BaseScript, Virtuale
                 "scan-files",
                 "cleanup-cache",
             ],
         )
         self.excludes = self.config.get('excludes', self.DEFAULT_EXCLUDES)
         self.dest_dir = self.CACHE_DIR
 
     def _get_candidates_prefix(self):
-        return "pub/{}/candidates/{}-candidates/build{}".format(
+        return "pub/{}/candidates/{}-candidates/build{}/".format(
             self.config['product'],
             self.config["version"],
             self.config["build_number"]
         )
 
     def _matches_exclude(self, keyname):
         for exclude in self.excludes:
             if re.search(exclude, keyname):
--- a/testing/mozharness/scripts/release/push-candidate-to-releases.py
+++ b/testing/mozharness/scripts/release/push-candidate-to-releases.py
@@ -92,24 +92,24 @@ class ReleasePusher(BaseScript, Virtuale
                 r"^.*/host.*$",
                 r"^.*/mar-tools/.*$",
                 r"^.*gecko-unsigned-unaligned.apk$",
                 r"^.*robocop.apk$",
                 r"^.*contrib.*"
             ]
 
     def _get_candidates_prefix(self):
-        return "pub/{}/candidates/{}-candidates/build{}".format(
+        return "pub/{}/candidates/{}-candidates/build{}/".format(
             self.config['product'],
             self.config["version"],
             self.config["build_number"]
         )
 
     def _get_releases_prefix(self):
-        return "pub/{}/releases/{}".format(
+        return "pub/{}/releases/{}/".format(
             self.config["product"],
             self.config["version"]
         )
 
     def _matches_exclude(self, keyname):
         for exclude in self.config["excludes"]:
             if re.search(exclude, keyname):
                 return True
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -332,19 +332,16 @@ user_pref("browser.reader.detectedFirstA
 // Don't let PAC generator to set PAC, as mochitest framework has its own PAC
 // rules during testing.
 user_pref("network.proxy.pac_generator", false);
 
 // Make tests run consistently on DevEdition (which has a lightweight theme
 // selected by default).
 user_pref("lightweightThemes.selectedThemeID", "");
 
-// Disable periodic updates of service workers.
-user_pref("dom.serviceWorkers.periodic-updates.enabled", false);
-
 // Enable speech synth test service, and disable built in platform services.
 user_pref("media.webspeech.synth.test", true);
 
 // Turn off search suggestions in the location bar so as not to trigger network
 // connections.
 user_pref("browser.urlbar.suggest.searches", false);
 
 // Turn off the location bar search suggestions opt-in.  It interferes with
--- a/testing/specialpowers/components/SpecialPowersObserver.js
+++ b/testing/specialpowers/components/SpecialPowersObserver.js
@@ -83,17 +83,16 @@ SpecialPowersObserver.prototype = new Sp
       this._messageManager.addMessageListener("SpecialPowers.Focus", this);
       this._messageManager.addMessageListener("SPPermissionManager", this);
       this._messageManager.addMessageListener("SPWebAppService", this);
       this._messageManager.addMessageListener("SPObserverService", this);
       this._messageManager.addMessageListener("SPLoadChromeScript", this);
       this._messageManager.addMessageListener("SPChromeScriptMessage", this);
       this._messageManager.addMessageListener("SPQuotaManager", this);
       this._messageManager.addMessageListener("SPSetTestPluginEnabledState", this);
-      this._messageManager.addMessageListener("SPPeriodicServiceWorkerUpdates", this);
       this._messageManager.addMessageListener("SPLoadExtension", this);
       this._messageManager.addMessageListener("SPStartupExtension", this);
       this._messageManager.addMessageListener("SPUnloadExtension", this);
       this._messageManager.addMessageListener("SPExtensionMessage", this);
       this._messageManager.addMessageListener("SPCleanUpSTSData", this);
 
       this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
       this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true);
@@ -164,17 +163,16 @@ SpecialPowersObserver.prototype = new Sp
       this._messageManager.removeMessageListener("SpecialPowers.Focus", this);
       this._messageManager.removeMessageListener("SPPermissionManager", this);
       this._messageManager.removeMessageListener("SPWebAppService", this);
       this._messageManager.removeMessageListener("SPObserverService", this);
       this._messageManager.removeMessageListener("SPLoadChromeScript", this);
       this._messageManager.removeMessageListener("SPChromeScriptMessage", this);
       this._messageManager.removeMessageListener("SPQuotaManager", this);
       this._messageManager.removeMessageListener("SPSetTestPluginEnabledState", this);
-      this._messageManager.removeMessageListener("SPPeriodicServiceWorkerUpdates", this);
       this._messageManager.removeMessageListener("SPLoadExtension", this);
       this._messageManager.removeMessageListener("SPStartupExtension", this);
       this._messageManager.removeMessageListener("SPUnloadExtension", this);
       this._messageManager.removeMessageListener("SPExtensionMessage", this);
       this._messageManager.removeMessageListener("SPCleanUpSTSData", this);
 
       this._messageManager.removeDelayedFrameScript(CHILD_LOGGER_SCRIPT);
       this._messageManager.removeDelayedFrameScript(CHILD_SCRIPT_API);
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -524,27 +524,16 @@ SpecialPowersObserverAPI.prototype = {
           mm.sendAsyncMessage(aMessage.name, reply);
         };
 
         qm.getUsageForPrincipal(principal, callback);
 
         return undefined;	// See comment at the beginning of this function.
       }
 
-      case "SPPeriodicServiceWorkerUpdates": {
-        // We could just dispatch a generic idle-daily notification here, but
-        // this is better since it avoids invoking other idle daily observers
-        // at the cost of hard-coding the usage of PeriodicServiceWorkerUpdater.
-        Cc["@mozilla.org/service-worker-periodic-updater;1"].
-          getService(Ci.nsIObserver).
-          observe(null, "idle-daily", "Caller:SpecialPowers");
-
-        return undefined;	// See comment at the beginning of this function.
-      }
-
       case "SPCleanUpSTSData": {
         let origin = aMessage.data.origin;
         let flags = aMessage.data.flags;
         let uri = Services.io.newURI(origin, null, null);
         let sss = Cc["@mozilla.org/ssservice;1"].
                   getService(Ci.nsISiteSecurityService);
         sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, flags);
       }
--- a/testing/specialpowers/content/specialpowers.js
+++ b/testing/specialpowers/content/specialpowers.js
@@ -27,17 +27,16 @@ function SpecialPowers(window) {
   this.SP_SYNC_MESSAGES = ["SPChromeScriptMessage",
                            "SPLoadChromeScript",
                            "SPObserverService",
                            "SPPermissionManager",
                            "SPPrefService",
                            "SPProcessCrashService",
                            "SPSetTestPluginEnabledState",
                            "SPWebAppService",
-                           "SPPeriodicServiceWorkerUpdates",
                            "SPCleanUpSTSData"];
 
   this.SP_ASYNC_MESSAGES = ["SpecialPowers.Focus",
                             "SpecialPowers.Quit",
                             "SPPingService",
                             "SPQuotaManager",
                             "SPLoadExtension",
                             "SPStartupExtension",
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1994,20 +1994,16 @@ SpecialPowersAPI.prototype = {
     let msg = { op: op, principal: principal, id: id };
     this._sendAsyncMessage(messageTopic, msg);
   },
 
   createDOMFile: function(path, options) {
     return new File(path, options);
   },
 
-  startPeriodicServiceWorkerUpdates: function() {
-    return this._sendSyncMessage('SPPeriodicServiceWorkerUpdates', {});
-  },
-
   removeAllServiceWorkerData: function() {
     this.notifyObserversInParentProcess(null, "browser:purge-session-history", "");
   },
 
   removeServiceWorkerDataForExampleDomain: function() {
     this.notifyObserversInParentProcess(null, "browser:purge-domain-data", "example.com");
   },
 
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -67,18 +67,16 @@ class TPSTestRunner(object):
         'extensions.getAddons.get.url': 'http://127.0.0.1:4567/addons/api/%IDS%.xml',
         'extensions.update.enabled': False,
         # Don't open a dialog to show available add-on updates
         'extensions.update.notifyUser': False,
         'services.sync.addons.ignoreRepositoryChecking': True,
         'services.sync.firstSync': 'notReady',
         'services.sync.lastversion': '1.0',
         'toolkit.startup.max_resumed_crashes': -1,
-        # Disable periodic updates of service workers
-        'dom.serviceWorkers.periodic-updates.enabled': False,
     }
 
     debug_preferences = {
         'services.sync.log.appender.console': 'Trace',
         'services.sync.log.appender.dump': 'Trace',
         'services.sync.log.appender.file.level': 'Trace',
         'services.sync.log.appender.file.logOnSuccess': True,
         'services.sync.log.rootLogger': 'Trace',
--- a/testing/web-platform/meta/url/a-element.html.ini
+++ b/testing/web-platform/meta/url/a-element.html.ini
@@ -58,22 +58,16 @@
     expected: FAIL
 
   [Parsing: <foo:////://///> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <c:/foo> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <[61:24:74\]:98> against <http://example.org/foo/bar>]
-    expected: FAIL
-
-  [Parsing: <http:[61:27\]/:foo> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <http://[1::2\]:3:4> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http://2001::1> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http://2001::1\]> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -400,19 +394,16 @@
     expected: FAIL
 
   [Parsing: <#x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <#> against <test:test?test>]
     expected: FAIL
 
-  [Parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>]
-    expected: FAIL
-
   [Parsing: <i> against <sc:sd>]
     expected: FAIL
 
   [Parsing: <i> against <sc:sd/sd>]
     expected: FAIL
 
   [Parsing: <i> against <sc:/pa/pa>]
     expected: FAIL
--- a/testing/web-platform/meta/url/a-element.xhtml.ini
+++ b/testing/web-platform/meta/url/a-element.xhtml.ini
@@ -58,22 +58,16 @@
     expected: FAIL
 
   [Parsing: <foo:////://///> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <c:/foo> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <[61:24:74\]:98> against <http://example.org/foo/bar>]
-    expected: FAIL
-
-  [Parsing: <http:[61:27\]/:foo> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <http://[1::2\]:3:4> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http://2001::1> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <http://2001::1\]> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -422,19 +416,16 @@
     expected: FAIL
 
   [Parsing: <#x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <#> against <test:test?test>]
     expected: FAIL
 
-  [Parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>]
-    expected: FAIL
-
   [Parsing: <i> against <sc:sd>]
     expected: FAIL
 
   [Parsing: <i> against <sc:sd/sd>]
     expected: FAIL
 
   [Parsing: <i> against <sc:/pa/pa>]
     expected: FAIL
--- a/testing/web-platform/meta/url/url-constructor.html.ini
+++ b/testing/web-platform/meta/url/url-constructor.html.ini
@@ -40,22 +40,16 @@
     expected: FAIL
 
   [Parsing: <foo://///////bar.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <foo:////://///> against <http://example.org/foo/bar>]
     expected: FAIL
 
-  [Parsing: <[61:24:74\]:98> against <http://example.org/foo/bar>]
-    expected: FAIL
-
-  [Parsing: <http:[61:27\]/:foo> against <http://example.org/foo/bar>]
-    expected: FAIL
-
   [Parsing: <gopher:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <data:/example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
 
   [Parsing: <gopher:example.com/> against <http://example.org/foo/bar>]
     expected: FAIL
@@ -268,19 +262,16 @@
     expected: FAIL
 
   [Parsing: <#x> against <about:blank>]
     expected: FAIL
 
   [Parsing: <#> against <test:test?test>]
     expected: FAIL
 
-  [Parsing: <http://`{}:`{}@h/`{}?`{}> against <http://doesnotmatter/>]
-    expected: FAIL
-
   [Parsing: <i> against <sc:/pa/pa>]
     expected: FAIL
 
   [Parsing: <i> against <sc://ho/pa>]
     expected: FAIL
 
   [Parsing: <i> against <sc:///pa/pa>]
     expected: FAIL
--- a/toolkit/components/osfile/modules/ospath_unix.jsm
+++ b/toolkit/components/osfile/modules/ospath_unix.jsm
@@ -160,17 +160,20 @@ var split = function(path) {
 exports.split = split;
 
 /**
  * Returns the file:// URI file path of the given local file path.
  */
 // The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
 var toFileURIExtraEncodings = {';': '%3b', '?': '%3F', '#': '%23'};
 var toFileURI = function toFileURI(path) {
-  let uri = encodeURI(this.normalize(path));
+  // Per https://url.spec.whatwg.org we should not encode [] in the path
+  let dontNeedEscaping = {'%5B': '[', '%5D': ']'};
+  let uri = encodeURI(this.normalize(path)).replace(/%(5B|5D)/gi,
+    match => dontNeedEscaping[match]);
 
   // add a prefix, and encodeURI doesn't escape a few characters that we do
   // want to escape, so fix that up
   let prefix = "file://";
   uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]);
 
   return uri;
 };
--- a/toolkit/components/osfile/modules/ospath_win.jsm
+++ b/toolkit/components/osfile/modules/ospath_win.jsm
@@ -305,17 +305,20 @@ exports.split = split;
 /**
  * Return the file:// URI file path of the given local file path.
  */
 // The case of %3b is designed to match Services.io, but fundamentally doesn't matter.
 var toFileURIExtraEncodings = {';': '%3b', '?': '%3F', '#': '%23'};
 var toFileURI = function toFileURI(path) {
   // URI-escape forward slashes and convert backward slashes to forward
   path = this.normalize(path).replace(/[\\\/]/g, m => (m=='\\')? '/' : '%2F');
-  let uri = encodeURI(path);
+  // Per https://url.spec.whatwg.org we should not encode [] in the path
+  let dontNeedEscaping = {'%5B': '[', '%5D': ']'};
+  let uri = encodeURI(path).replace(/%(5B|5D)/gi,
+    match => dontNeedEscaping[match]);
 
   // add a prefix, and encodeURI doesn't escape a few characters that we do
   // want to escape, so fix that up
   let prefix = "file:///";
   uri = prefix + uri.replace(/[;?#]/g, match => toFileURIExtraEncodings[match]);
 
   // turn e.g., file:///C: into file:///C:/
   if (uri.charAt(uri.length - 1) === ':') {
--- a/toolkit/components/osfile/tests/xpcshell/test_file_URL_conversion.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_file_URL_conversion.js
@@ -49,17 +49,17 @@ function run_test() {
     '/',
     '/test',
     '/test/',
     '/test%2f',
     '/test/test/test',
     '/test;+%',
     '/test?action=index/',
     '/test\ test',
-    '/punctuation/;,/?:@&=+$-_.!~*\'()"#',
+    '/punctuation/;,/?:@&=+$-_.!~*\'()[]"#',
     '/CasePreserving'
   ];
 
   // some additional URIs to test, beyond those generated from paths
   let uris = isWindows ? [
     'file:///C:/test/',
     'file://localhost/C:/test',
     'file:///c:/test/test.txt',
--- a/toolkit/content/aboutServiceWorkers.js
+++ b/toolkit/content/aboutServiceWorkers.js
@@ -50,19 +50,19 @@ function init() {
   try {
     pns = Cc["@mozilla.org/push/NotificationService;1"]
             .getService(Ci.nsIPushNotificationService);
   } catch(e) {
     dump("Could not acquire PushNotificationService\n");
   }
 
   for (let i = 0; i < length; ++i) {
-    let info = data.queryElementAt(i, Ci.nsIServiceWorkerInfo);
+    let info = data.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
     if (!info) {
-      dump("AboutServiceWorkers: Invalid nsIServiceWorkerInfo interface.\n");
+      dump("AboutServiceWorkers: Invalid nsIServiceWorkerRegistrationInfo interface.\n");
       continue;
     }
 
     display(info, pns);
   }
 }
 
 function display(info, pushNotificationService) {
--- a/toolkit/themes/shared/aboutCache.css
+++ b/toolkit/themes/shared/aboutCache.css
@@ -37,16 +37,20 @@ td {
 
 #col-dataSize,
 #col-fetchCount,
 #col-lastModified,
 #col-expires {
   width: 13%;
 }
 
+#col-pinned {
+  width: 5em;
+}
+
 #entries > tbody > tr:nth-child(odd) {
   background-color: rgba(0, 0, 0, .03);
 }
 
 #entries > tbody > tr:nth-child(even) {
   background-color: rgba(0, 0, 0, .06);
 }
 
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -988,35 +988,16 @@ nsLookAndFeel::Init()
     // Scrollbar colors
     style = create_context(path);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozScrollbar = GDK_RGBA_TO_NS_RGBA(color);
     g_object_unref(style);
 
-    // Text colors
-    style = create_context(path);
-    gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW);
-    gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
-    sMozFieldBackground = GDK_RGBA_TO_NS_RGBA(color);
-    gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
-    sMozFieldText = GDK_RGBA_TO_NS_RGBA(color);
-
-    // Selected text and background
-    gtk_style_context_get_background_color(style,
-        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
-        &color);
-    sTextSelectedBackground = GDK_RGBA_TO_NS_RGBA(color);
-    gtk_style_context_get_color(style,
-        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
-        &color);
-    sTextSelectedText = GDK_RGBA_TO_NS_RGBA(color);
-    g_object_unref(style);
-
     // Window colors
     style = create_context(path);
     gtk_style_context_save(style);
     gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozWindowBackground = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sMozWindowText = GDK_RGBA_TO_NS_RGBA(color);
@@ -1072,25 +1053,27 @@ nsLookAndFeel::Init()
     GtkWidget *combobox = gtk_combo_box_new_with_entry();
     GtkWidget *comboboxLabel = gtk_bin_get_child(GTK_BIN(combobox));
 #endif
     GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP);
     GtkWidget *treeView = gtk_tree_view_new();
     GtkWidget *linkButton = gtk_link_button_new("http://example.com/");
     GtkWidget *menuBar = gtk_menu_bar_new();
     GtkWidget *entry = gtk_entry_new();
+    GtkWidget *textView = gtk_text_view_new();
 
     gtk_container_add(GTK_CONTAINER(button), label);
     gtk_container_add(GTK_CONTAINER(parent), button);
     gtk_container_add(GTK_CONTAINER(parent), treeView);
     gtk_container_add(GTK_CONTAINER(parent), linkButton);
     gtk_container_add(GTK_CONTAINER(parent), combobox);
     gtk_container_add(GTK_CONTAINER(parent), menuBar);
     gtk_container_add(GTK_CONTAINER(window), parent);
     gtk_container_add(GTK_CONTAINER(parent), entry);
+    gtk_container_add(GTK_CONTAINER(parent), textView);
     
 #if (MOZ_WIDGET_GTK == 2)
     gtk_widget_set_style(button, nullptr);
     gtk_widget_set_style(label, nullptr);
     gtk_widget_set_style(treeView, nullptr);
     gtk_widget_set_style(linkButton, nullptr);
     gtk_widget_set_style(combobox, nullptr);
     gtk_widget_set_style(comboboxLabel, nullptr);
@@ -1157,16 +1140,36 @@ nsLookAndFeel::Init()
     if (style) {
         sButtonBackground = GDK_COLOR_TO_NS_RGB(style->bg[GTK_STATE_NORMAL]);
         sFrameOuterLightBorder =
             GDK_COLOR_TO_NS_RGB(style->light[GTK_STATE_NORMAL]);
         sFrameInnerDarkBorder =
             GDK_COLOR_TO_NS_RGB(style->dark[GTK_STATE_NORMAL]);
     }
 #else
+    // Text colors
+    style = gtk_widget_get_style_context(textView);
+    gtk_style_context_save(style);
+    gtk_style_context_add_class(style, GTK_STYLE_CLASS_VIEW);
+    gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
+    sMozFieldBackground = GDK_RGBA_TO_NS_RGBA(color);
+    gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
+    sMozFieldText = GDK_RGBA_TO_NS_RGBA(color);
+
+    // Selected text and background
+    gtk_style_context_get_background_color(style,
+        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
+        &color);
+    sTextSelectedBackground = GDK_RGBA_TO_NS_RGBA(color);
+    gtk_style_context_get_color(style,
+        static_cast<GtkStateFlags>(GTK_STATE_FLAG_FOCUSED|GTK_STATE_FLAG_SELECTED),
+        &color);
+    sTextSelectedText = GDK_RGBA_TO_NS_RGBA(color);
+    gtk_style_context_restore(style);
+
     // Button text, background, border
     style = gtk_widget_get_style_context(label);
     gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sButtonText = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color);
     sButtonHoverText = GDK_RGBA_TO_NS_RGBA(color);
 
     // Combobox text color
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2317,30 +2317,30 @@ nsWindow::OnExposeEvent(cairo_t *cr)
 void
 nsWindow::UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect)
 {
     // We need to create our own buffer to force the stride to match the
     // expected stride.
     int32_t stride = GetAlignedStride<4>(BytesPerPixel(SurfaceFormat::A8) *
                                          aBoundsRect.width);
     int32_t bufferSize = stride * aBoundsRect.height;
-    nsAutoArrayPtr<uint8_t> imageBuffer(new (std::nothrow) uint8_t[bufferSize]);
+    UniquePtr<uint8_t[]> imageBuffer(new (std::nothrow) uint8_t[bufferSize]);
     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
-        CreateDrawTargetForData(imageBuffer, aBoundsRect.Size(),
+        CreateDrawTargetForData(imageBuffer.get(), aBoundsRect.Size(),
                                 stride, SurfaceFormat::A8);
 
     if (drawTarget) {
         Matrix transform = Matrix::Translation(-aBoundsRect.x, -aBoundsRect.y);
         drawTarget->SetTransform(transform);
 
         drawTarget->FillRect(Rect(aBoundsRect.x, aBoundsRect.y, aBoundsRect.width, aBoundsRect.height),
                              *aPattern->GetPattern(drawTarget),
                              DrawOptions(1.0, CompositionOp::OP_SOURCE));
     }
-    UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer, stride);
+    UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride);
 }
 
 gboolean
 nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
 {
     // These events are only received on toplevel windows.
     //
     // GDK ensures that the coordinates are the client window top-left wrt the
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -687,26 +687,26 @@ nsTransparencyMode nsBaseWidget::GetTran
   return eTransparencyOpaque;
 }
 
 bool
 nsBaseWidget::IsWindowClipRegionEqual(const nsTArray<nsIntRect>& aRects)
 {
   return mClipRects &&
          mClipRectCount == aRects.Length() &&
-         memcmp(mClipRects, aRects.Elements(), sizeof(nsIntRect)*mClipRectCount) == 0;
+         memcmp(mClipRects.get(), aRects.Elements(), sizeof(nsIntRect)*mClipRectCount) == 0;
 }
 
 void
 nsBaseWidget::StoreWindowClipRegion(const nsTArray<nsIntRect>& aRects)
 {
   mClipRectCount = aRects.Length();
-  mClipRects = new nsIntRect[mClipRectCount];
+  mClipRects = MakeUnique<nsIntRect[]>(mClipRectCount);
   if (mClipRects) {
-    memcpy(mClipRects, aRects.Elements(), sizeof(nsIntRect)*mClipRectCount);
+    memcpy(mClipRects.get(), aRects.Elements(), sizeof(nsIntRect)*mClipRectCount);
   }
 }
 
 void
 nsBaseWidget::GetWindowClipRegion(nsTArray<nsIntRect>* aRects)
 {
   if (mClipRects) {
     aRects->AppendElements(mClipRects.get(), mClipRectCount);
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -504,17 +504,17 @@ protected:
   SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
   RefPtr<WidgetShutdownObserver> mShutdownObserver;
   RefPtr<TextEventDispatcher> mTextEventDispatcher;
   nsCursor          mCursor;
   nsBorderStyle     mBorderStyle;
   nsIntRect         mBounds;
   nsIntRect*        mOriginalBounds;
   // When this pointer is null, the widget is not clipped
-  nsAutoArrayPtr<nsIntRect> mClipRects;
+  mozilla::UniquePtr<nsIntRect[]> mClipRects;
   uint32_t          mClipRectCount;
   nsSizeMode        mSizeMode;
   nsPopupLevel      mPopupLevel;
   nsPopupType       mPopupType;
   SizeConstraints   mSizeConstraints;
 
   bool              mUpdateCursor;
   bool              mUseAttachedEvents;
--- a/widget/nsPrimitiveHelpers.cpp
+++ b/widget/nsPrimitiveHelpers.cpp
@@ -54,21 +54,21 @@ nsPrimitiveHelpers :: CreatePrimitiveFor
       NS_ADDREF(*aPrimitive = primitive);
     }
   }
   else {
     nsCOMPtr<nsISupportsString> primitive =
         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
     if (primitive ) {
       if (aDataLen % 2) {
-        nsAutoArrayPtr<char> buffer(new char[aDataLen + 1]);
+        auto buffer = MakeUnique<char[]>(aDataLen + 1);
         if (!MOZ_LIKELY(buffer))
           return;
 
-        memcpy(buffer, aDataBuff, aDataLen);
+        memcpy(buffer.get(), aDataBuff, aDataLen);
         buffer[aDataLen] = 0;
         const char16_t* start = reinterpret_cast<const char16_t*>(buffer.get());
         // recall that length takes length as characters, not bytes
         primitive->SetData(Substring(start, start + (aDataLen + 1) / 2));
       } else {
         const char16_t* start = reinterpret_cast<const char16_t*>(aDataBuff);
         // recall that length takes length as characters, not bytes
         primitive->SetData(Substring(start, start + (aDataLen / 2)));
--- a/widget/nsTransferable.cpp
+++ b/widget/nsTransferable.cpp
@@ -178,32 +178,33 @@ DataStruct::ReadCache(nsISupports** aDat
     int64_t fileSize;
     int64_t max32 = 0xFFFFFFFF;
     cacheFile->GetFileSize(&fileSize);
     if (fileSize > max32)
       return NS_ERROR_OUT_OF_MEMORY;
 
     uint32_t size = uint32_t(fileSize);
     // create new memory for the large clipboard data
-    nsAutoArrayPtr<char> data(new char[size]);
+    auto data = MakeUnique<char[]>(size);
     if ( !data )
       return NS_ERROR_OUT_OF_MEMORY;
       
     // now read it all in
     nsCOMPtr<nsIInputStream> inStr;
     NS_NewLocalFileInputStream( getter_AddRefs(inStr),
                                 cacheFile);
     
     if (!cacheFile) return NS_ERROR_FAILURE;
 
-    nsresult rv = inStr->Read(data, fileSize, aDataLen);
+    nsresult rv = inStr->Read(data.get(), fileSize, aDataLen);
 
     // make sure we got all the data ok
     if (NS_SUCCEEDED(rv) && *aDataLen == size) {
-      nsPrimitiveHelpers::CreatePrimitiveForData ( mFlavor.get(), data, fileSize, aData );
+      nsPrimitiveHelpers::CreatePrimitiveForData(mFlavor.get(), data.get(),
+                                                 fileSize, aData);
       return *aData ? NS_OK : NS_ERROR_FAILURE;
     }
 
     // zero the return params
     *aData    = nullptr;
     *aDataLen = 0;
   }
 
--- a/widget/windows/LSPAnnotator.cpp
+++ b/widget/windows/LSPAnnotator.cpp
@@ -61,17 +61,17 @@ LSPAnnotationGatherer::Run()
   // Get the size of the buffer we need
   if (SOCKET_ERROR != WSCEnumProtocols(nullptr, nullptr, &size, &err) ||
       err != WSAENOBUFS) {
     // Er, what?
     NS_NOTREACHED("WSCEnumProtocols suceeded when it should have failed ...");
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoArrayPtr<char> byteArray(new char[size]);
+  auto byteArray = MakeUnique<char[]>(size);
   WSAPROTOCOL_INFOW* providers =
     reinterpret_cast<WSAPROTOCOL_INFOW*>(byteArray.get());
 
   int n = WSCEnumProtocols(nullptr, providers, &size, &err);
   if (n == SOCKET_ERROR) {
     // Lame. We provided the right size buffer; we'll just give up now.
     NS_WARNING("Could not get LSP list");
     return NS_ERROR_FAILURE;
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -5,32 +5,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsFilePicker.h"
 
 #include <shlobj.h>
 #include <shlwapi.h>
 #include <cderr.h>
 
+#include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsReadableUtils.h"
 #include "nsNetUtil.h"
 #include "nsWindow.h"
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsIURL.h"
 #include "nsIStringBundle.h"
 #include "nsEnumeratorUtils.h"
 #include "nsCRT.h"
 #include "nsString.h"
 #include "nsToolkit.h"
 #include "WinUtils.h"
 #include "nsPIDOMWindow.h"
 
 using mozilla::IsVistaOrLater;
+using mozilla::MakeUnique;
+using mozilla::UniquePtr;
 using namespace mozilla::widget;
 
 char16_t *nsFilePicker::mLastUsedUnicodeDirectory;
 char nsFilePicker::mLastUsedDirectory[MAX_PATH+1] = { 0 };
 
 static const wchar_t kDialogPtrProp[] = L"DialogPtrProperty";
 static const DWORD kDialogTimerID = 9999;
 static const unsigned long kDialogTimerTimeout = 300;
@@ -65,33 +68,33 @@ private:
 };
 
 // Manages the current working path.
 class AutoRestoreWorkingPath
 {
 public:
   AutoRestoreWorkingPath() {
     DWORD bufferLength = GetCurrentDirectoryW(0, nullptr);
-    mWorkingPath = new wchar_t[bufferLength];
-    if (GetCurrentDirectoryW(bufferLength, mWorkingPath) == 0) {
+    mWorkingPath = MakeUnique<wchar_t[]>(bufferLength);
+    if (GetCurrentDirectoryW(bufferLength, mWorkingPath.get()) == 0) {
       mWorkingPath = nullptr;
     }
   }
 
   ~AutoRestoreWorkingPath() {
     if (HasWorkingPath()) {
-      ::SetCurrentDirectoryW(mWorkingPath);
+      ::SetCurrentDirectoryW(mWorkingPath.get());
     }
   }
 
   inline bool HasWorkingPath() const {
     return mWorkingPath != nullptr;
   }
 private:
-  nsAutoArrayPtr<wchar_t> mWorkingPath;
+  UniquePtr<wchar_t[]> mWorkingPath;
 };
 
 // Manages NS_NATIVE_TMP_WINDOW child windows. NS_NATIVE_TMP_WINDOWs are
 // temporary child windows of mParentWidget created to address RTL issues
 // in picker dialogs. We are responsible for destroying these.
 class AutoDestroyTmpWindow
 {
 public:
@@ -518,26 +521,26 @@ nsFilePicker::SetDialogHandle(HWND aWnd)
 
 // Open the older XP style folder picker dialog. We end up in this call
 // on XP systems or when platform is built without the longhorn SDK.
 bool
 nsFilePicker::ShowXPFolderPicker(const nsString& aInitialDir)
 {
   bool result = false;
 
-  nsAutoArrayPtr<wchar_t> dirBuffer(new wchar_t[FILE_BUFFER_SIZE]);
-  wcsncpy(dirBuffer, aInitialDir.get(), FILE_BUFFER_SIZE);
+  auto dirBuffer = MakeUnique<wchar_t[]>(FILE_BUFFER_SIZE);
+  wcsncpy(dirBuffer.get(), aInitialDir.get(), FILE_BUFFER_SIZE);
   dirBuffer[FILE_BUFFER_SIZE-1] = '\0';
 
   AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
 
   BROWSEINFOW browserInfo = {0};
   browserInfo.pidlRoot       = nullptr;
-  browserInfo.pszDisplayName = dirBuffer;
+  browserInfo.pszDisplayName = dirBuffer.get();
   browserInfo.lpszTitle      = mTitle.get();
   browserInfo.ulFlags        = BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
   browserInfo.hwndOwner      = adtw.get(); 
   browserInfo.iImage         = 0;
   browserInfo.lParam         = reinterpret_cast<LPARAM>(this);
 
   if (!aInitialDir.IsEmpty()) {
     // the dialog is modal so that |initialDir.get()| will be valid in 
@@ -546,19 +549,19 @@ nsFilePicker::ShowXPFolderPicker(const n
     browserInfo.lpfn   = &BrowseCallbackProc;
   } else {
     browserInfo.lParam = 0;
     browserInfo.lpfn   = nullptr;
   }
 
   LPITEMIDLIST list = ::SHBrowseForFolderW(&browserInfo);
   if (list) {
-    result = ::SHGetPathFromIDListW(list, dirBuffer);
+    result = ::SHGetPathFromIDListW(list, dirBuffer.get());
     if (result)
-      mUnicodeFile.Assign(static_cast<const wchar_t*>(dirBuffer));
+      mUnicodeFile.Assign(static_cast<const wchar_t*>(dirBuffer.get()));
     // free PIDL
     CoTaskMemFree(list);
   }
 
   return result;
 }
 
 /*
@@ -662,31 +665,31 @@ nsFilePicker::FilePickerWrapper(OPENFILE
 
 bool
 nsFilePicker::ShowXPFilePicker(const nsString& aInitialDir)
 {
   OPENFILENAMEW ofn = {0};
   ofn.lStructSize = sizeof(ofn);
   nsString filterBuffer = mFilterList;
                                 
-  nsAutoArrayPtr<wchar_t> fileBuffer(new wchar_t[FILE_BUFFER_SIZE]);
-  wcsncpy(fileBuffer,  mDefaultFilePath.get(), FILE_BUFFER_SIZE);
+  auto fileBuffer = MakeUnique<wchar_t[]>(FILE_BUFFER_SIZE);
+  wcsncpy(fileBuffer.get(),  mDefaultFilePath.get(), FILE_BUFFER_SIZE);
   fileBuffer[FILE_BUFFER_SIZE-1] = '\0'; // null terminate in case copy truncated
 
   if (!aInitialDir.IsEmpty()) {
     ofn.lpstrInitialDir = aInitialDir.get();
   }
 
   AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ?
     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
 
   ofn.lpstrTitle   = (LPCWSTR)mTitle.get();
   ofn.lpstrFilter  = (LPCWSTR)filterBuffer.get();
   ofn.nFilterIndex = mSelectedType;
-  ofn.lpstrFile    = fileBuffer;
+  ofn.lpstrFile    = fileBuffer.get();
   ofn.nMaxFile     = FILE_BUFFER_SIZE;
   ofn.hwndOwner    = adtw.get();
   ofn.lCustData    = reinterpret_cast<LPARAM>(this);
   ofn.Flags = OFN_SHAREAWARE | OFN_LONGNAMES | OFN_OVERWRITEPROMPT |
               OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_ENABLESIZING | 
               OFN_EXPLORER;
 
   // Windows Vista and up won't allow you to use the new looking dialogs with
@@ -749,19 +752,19 @@ nsFilePicker::ShowXPFilePicker(const nsS
       // allocated and that the returned value should be freed by the caller.
       // If the hook changes the buffer, it will deallocate the old buffer.
       // This fix would be nice to have in Vista and up, but it would force
       // the file picker to use the old style dialogs because hooks are not
       // allowed in the new file picker UI.  We need to eventually move to
       // the new Common File Dialogs for Vista and up.
       if (!IsVistaOrLater()) {
         ofn.lpfnHook = MultiFilePickerHook;
-        fileBuffer.forget();
+        fileBuffer.release();
         result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
-        fileBuffer = ofn.lpstrFile;
+        fileBuffer.reset(ofn.lpstrFile);
       } else {
         result = FilePickerWrapper(&ofn, PICKER_TYPE_OPEN);
       }
       break;
 
     case modeSave:
       {
         ofn.Flags |= OFN_NOREADONLYRETURN;
@@ -793,27 +796,27 @@ nsFilePicker::ShowXPFilePicker(const nsS
   if (!result)
     return false;
 
   // Remember what filter type the user selected
   mSelectedType = (int16_t)ofn.nFilterIndex;
 
   // Single file selection, we're done
   if (mMode != modeOpenMultiple) {
-    GetQualifiedPath(fileBuffer, mUnicodeFile);
+    GetQualifiedPath(fileBuffer.get(), mUnicodeFile);
     return true;
   }
 
   // Set user-selected location of file or directory.  From msdn's "Open and
   // Save As Dialog Boxes" section:
   // If you specify OFN_EXPLORER, the directory and file name strings are '\0'
   // separated, with an extra '\0' character after the last file name. This
   // format enables the Explorer-style dialog boxes to return long file names
   // that include spaces. 
-  wchar_t *current = fileBuffer;
+  wchar_t *current = fileBuffer.get();
   
   nsAutoString dirName(current);
   // Sometimes dirName contains a trailing slash and sometimes it doesn't:
   if (current[dirName.Length() - 1] != '\\')
     dirName.Append((char16_t)'\\');
   
   while (current && *current && *(current + wcslen(current) + 1)) {
     current = current + wcslen(current) + 1;
@@ -834,17 +837,17 @@ nsFilePicker::ShowXPFilePicker(const nsS
     if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
         !mFiles.AppendObject(file))
       return false;
   }
   
   // Handle the case where the user selected just one file. From msdn: If you
   // specify OFN_ALLOWMULTISELECT and the user selects only one file the
   // lpstrFile string does not have a separator between the path and file name.
-  if (current && *current && (current == fileBuffer)) {
+  if (current && *current && (current == fileBuffer.get())) {
     nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
     NS_ENSURE_TRUE(file, false);
     
     nsAutoString canonicalizedPath;
     GetQualifiedPath(current, canonicalizedPath);
     if (NS_FAILED(file->InitWithPath(canonicalizedPath)) ||
         !mFiles.AppendObject(file))
       return false;
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -66,16 +66,17 @@
 
 #include "mozilla/GenericFactory.h"
 #include "nsSupportsPrimitives.h"
 #include "nsArray.h"
 #include "nsIMutableArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsStringEnumerator.h"
 #include "mozilla/FileUtils.h"
+#include "mozilla/UniquePtr.h"
 #include "nsDataHashtable.h"
 
 #include <new>     // for placement new
 
 #include "mozilla/Omnijar.h"
 
 #include "mozilla/Logging.h"
 
@@ -615,28 +616,28 @@ static void
 DoRegisterManifest(NSLocationType aType,
                    FileLocation& aFile,
                    bool aChromeOnly,
                    bool aXPTOnly)
 {
   MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager);
   uint32_t len;
   FileLocation::Data data;
-  nsAutoArrayPtr<char> buf;
+  UniquePtr<char[]> buf;
   nsresult rv = aFile.GetData(data);
   if (NS_SUCCEEDED(rv)) {
     rv = data.GetSize(&len);
   }
   if (NS_SUCCEEDED(rv)) {
-    buf = new char[len + 1];
-    rv = data.Copy(buf, len);
+    buf = MakeUnique<char[]>(len + 1);
+    rv = data.Copy(buf.get(), len);
   }
   if (NS_SUCCEEDED(rv)) {
     buf[len] = '\0';
-    ParseManifest(aType, aFile, buf, aChromeOnly, aXPTOnly);
+    ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly);
   } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
     nsCString uri;
     aFile.GetURIString(uri);
     LogMessage("Could not read chrome manifest '%s'.", uri.get());
   }
 }
 
 void
@@ -694,27 +695,27 @@ DoRegisterXPT(FileLocation& aFile)
 #ifdef MOZ_B2G_LOADER
   if (IsRegisteredXPTIInfo(aFile)) {
     return;
   }
 #endif
 
   uint32_t len;
   FileLocation::Data data;
-  nsAutoArrayPtr<char> buf;
+  UniquePtr<char[]> buf;
   nsresult rv = aFile.GetData(data);
   if (NS_SUCCEEDED(rv)) {
     rv = data.GetSize(&len);
   }
   if (NS_SUCCEEDED(rv)) {
-    buf = new char[len];
-    rv = data.Copy(buf, len);
+    buf = MakeUnique<char[]>(len);
+    rv = data.Copy(buf.get(), len);
   }
   if (NS_SUCCEEDED(rv)) {
-    XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf, len);
+    XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len);
 #ifdef MOZ_B2G_LOADER
     MarkRegisteredXPTIInfo(aFile);
 #endif
   } else {
     nsCString uri;
     aFile.GetURIString(uri);
     LogMessage("Could not read '%s'.", uri.get());
   }
--- a/xpcom/glue/nsINIParser.cpp
+++ b/xpcom/glue/nsINIParser.cpp
@@ -19,16 +19,18 @@
 #endif
 
 #if defined(XP_WIN)
 #define READ_BINARYMODE L"rb"
 #else
 #define READ_BINARYMODE "r"
 #endif
 
+using namespace mozilla;
+
 #ifdef XP_WIN
 inline FILE*
 TS_tfopen(const char* aPath, const wchar_t* aMode)
 {
   wchar_t wPath[MAX_PATH];
   MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH);
   return _wfopen(wPath, aMode);
 }
@@ -118,27 +120,27 @@ nsINIParser::InitFromFILE(FILE* aFd)
 
   long flen = ftell(aFd);
   /* zero-sized file, or an error */
   if (flen <= 0) {
     return NS_ERROR_FAILURE;
   }
 
   /* malloc an internal buf the size of the file */
-  mFileContents = new char[flen + 2];
+  mFileContents = MakeUnique<char[]>(flen + 2);
   if (!mFileContents) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   /* read the file in one swoop */
   if (fseek(aFd, 0, SEEK_SET) != 0) {
     return NS_BASE_STREAM_OSERROR;
   }
 
-  int rd = fread(mFileContents, sizeof(char), flen, aFd);
+  int rd = fread(mFileContents.get(), sizeof(char), flen, aFd);
   if (rd != flen) {
     return NS_BASE_STREAM_OSERROR;
   }
 
   // We write a UTF16 null so that the file is easier to convert to UTF8
   mFileContents[flen] = mFileContents[flen + 1] = '\0';
 
   char* buffer = &mFileContents[0];
@@ -167,23 +169,23 @@ nsINIParser::InitFromFILE(FILE* aFd)
                                nullptr,
                                0,
                                nullptr,
                                nullptr);
     if (flen == 0) {
       return NS_ERROR_FAILURE;
     }
 
-    nsAutoArrayPtr<char> utf8Buffer(new char[flen]);
+    UniquePtr<char[]> utf8Buffer(new char[flen]);
     if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1,
-                            utf8Buffer, flen, nullptr, nullptr) == 0) {
+                            utf8Buffer.get(), flen, nullptr, nullptr) == 0) {
       return NS_ERROR_FAILURE;
     }
-    mFileContents = utf8Buffer.forget();
-    buffer = mFileContents;
+    mFileContents = Move(utf8Buffer);
+    buffer = mFileContents.get();
   }
 #endif
 
   char* currSection = nullptr;
 
   // outer loop tokenizes into lines
   while (char* token = NS_strtok(kNL, &buffer)) {
     if (token[0] == '#' || token[0] == ';') { // it's a comment
@@ -237,23 +239,23 @@ nsINIParser::InitFromFILE(FILE* aFd)
     // Check whether this key has already been specified; overwrite
     // if so, or append if not.
     while (v) {
       if (!strcmp(key, v->key)) {
         v->value = token;
         break;
       }
       if (!v->next) {
-        v->next = new INIValue(key, token);
+        v->next = MakeUnique<INIValue>(key, token);
         if (!v->next) {
           return NS_ERROR_OUT_OF_MEMORY;
         }
         break;
       }
-      v = v->next;
+      v = v->next.get();
     }
     NS_ASSERTION(v, "v should never be null coming out of this loop");
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -264,17 +266,17 @@ nsINIParser::GetString(const char* aSect
   mSections.Get(aSection, &val);
 
   while (val) {
     if (strcmp(val->key, aKey) == 0) {
       aResult.Assign(val->value);
       return NS_OK;
     }
 
-    val = val->next;
+    val = val->next.get();
   }
 
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsINIParser::GetString(const char* aSection, const char* aKey,
                        char* aResult, uint32_t aResultLen)
@@ -288,17 +290,17 @@ nsINIParser::GetString(const char* aSect
       aResult[aResultLen - 1] = '\0';
       if (strlen(val->value) >= aResultLen) {
         return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
       }
 
       return NS_OK;
     }
 
-    val = val->next;
+    val = val->next.get();
   }
 
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsINIParser::GetSections(INISectionCallback aCB, void* aClosure)
 {
@@ -313,17 +315,17 @@ nsINIParser::GetSections(INISectionCallb
 nsresult
 nsINIParser::GetStrings(const char* aSection,
                         INIStringCallback aCB, void* aClosure)
 {
   INIValue* val;
 
   for (mSections.Get(aSection, &val);
        val;
-       val = val->next) {
+       val = val->next.get()) {
 
     if (!aCB(val->key, val->value, aClosure)) {
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
--- a/xpcom/glue/nsINIParser.h
+++ b/xpcom/glue/nsINIParser.h
@@ -10,17 +10,17 @@
 #define nsINIParser_h__
 
 #ifdef MOZILLA_INTERNAL_API
 #define nsINIParser nsINIParser_internal
 #endif
 
 #include "nscore.h"
 #include "nsClassHashtable.h"
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
 
 #include <stdio.h>
 
 class nsIFile;
 
 class nsINIParser
 {
 public:
@@ -101,18 +101,18 @@ private:
     INIValue(const char* aKey, const char* aValue)
       : key(aKey)
       , value(aValue)
     {
     }
 
     const char* key;
     const char* value;
-    nsAutoPtr<INIValue> next;
+    mozilla::UniquePtr<INIValue> next;
   };
 
   nsClassHashtable<nsDepCharHashKey, INIValue> mSections;
-  nsAutoArrayPtr<char> mFileContents;
+  mozilla::UniquePtr<char[]> mFileContents;
 
   nsresult InitFromFILE(FILE* aFd);
 };
 
 #endif /* nsINIParser_h__ */
--- a/xpcom/io/nsEscape.cpp
+++ b/xpcom/io/nsEscape.cpp
@@ -355,18 +355,18 @@ nsEscapeHTML2(const char16_t* aSourceBuf
 static const uint32_t EscapeChars[256] =
 //   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
 {
      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  // 0x
      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  // 1x
      0,1023,   0, 512,1023,   0,1023, 112,1023,1023,1023,1023,1023,1023, 953, 784,  // 2x   !"#$%&'()*+,-./
   1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008,1008,   0,1008,   0, 768,  // 3x  0123456789:;<=>?
   1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,  // 4x  @ABCDEFGHIJKLMNO
-  1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896, 896, 896, 896,1023,  // 5x  PQRSTUVWXYZ[\]^_
-     0,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,  // 6x  `abcdefghijklmno
+  1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008, 896,1008, 896,1023,  // 5x  PQRSTUVWXYZ[\]^_
+   384,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,  // 6x  `abcdefghijklmno
   1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023,   0,  // 7x  pqrstuvwxyz{|}~ DEL
      0                                                                              // 80 to FF are zero
 };
 
 static uint16_t dontNeedEscape(unsigned char aChar, uint32_t aFlags)
 {
   return EscapeChars[(uint32_t)aChar] & aFlags;
 }
--- a/xpcom/tests/gtest/TestExpirationTracker.cpp
+++ b/xpcom/tests/gtest/TestExpirationTracker.cpp
@@ -13,16 +13,17 @@
 #include "nsString.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPCOM.h"
 #include "nsIFile.h"
 #include "prinrval.h"
 #include "nsThreadUtils.h"
+#include "mozilla/UniquePtr.h"
 #include "gtest/gtest.h"
 
 namespace TestExpirationTracker {
 
 struct Object {
   Object() : mExpired(false) { Touch(); }
   void Touch() { mLastUsed = PR_IntervalNow(); mExpired = false; }
 
@@ -44,17 +45,17 @@ static uint32_t slackMS = 30; // allow t
 template <uint32_t K> class Tracker : public nsExpirationTracker<Object,K> {
 public:
   Tracker() : nsExpirationTracker<Object,K>(periodMS, "Tracker") {
     Object* obj = new Object();
     mUniverse.AppendElement(obj);
     LogAction(obj, "Created");
   }
 
-  nsTArray<nsAutoArrayPtr<Object> > mUniverse;
+  nsTArray<mozilla::UniquePtr<Object>> mUniverse;
 
   void LogAction(Object* aObj, const char* aAction) {
     if (logging) {
       printf("%d %p(%d): %s\n", PR_IntervalNow(),
              static_cast<void*>(aObj), aObj->mLastUsed, aAction);
     }
   }
 
@@ -74,38 +75,38 @@ public:
       if (mUniverse.Length() < 50) {
         obj = new Object();
         mUniverse.AppendElement(obj);
         LogAction(obj, "Created");
       }
       break;
     }
     case 1: {
-      obj = mUniverse[uint32_t(rand())%mUniverse.Length()];
-      if (obj->mExpiration.IsTracked()) {
-        nsExpirationTracker<Object,K>::RemoveObject(obj);
-        LogAction(obj, "Removed");
+      UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+      if (objref->mExpiration.IsTracked()) {
+        nsExpirationTracker<Object,K>::RemoveObject(objref.get());
+        LogAction(objref.get(), "Removed");
       }
       break;
     }
     case 2: {
-      obj = mUniverse[uint32_t(rand())%mUniverse.Length()];
-      if (!obj->mExpiration.IsTracked()) {
-        obj->Touch();
-        nsExpirationTracker<Object,K>::AddObject(obj);
-        LogAction(obj, "Added");
+      UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+      if (!objref->mExpiration.IsTracked()) {
+        objref->Touch();
+        nsExpirationTracker<Object,K>::AddObject(objref.get());
+        LogAction(objref.get(), "Added");
       }
       break;
     }
     case 3: {
-      obj = mUniverse[uint32_t(rand())%mUniverse.Length()];
-      if (obj->mExpiration.IsTracked()) {
-        obj->Touch();
-        nsExpirationTracker<Object,K>::MarkUsed(obj);
-        LogAction(obj, "Marked used");
+      UniquePtr<Object>& objref = mUniverse[uint32_t(rand())%mUniverse.Length()];
+      if (objref->mExpiration.IsTracked()) {
+        objref->Touch();
+        nsExpirationTracker<Object,K>::MarkUsed(objref.get());
+        LogAction(objref.get(), "Marked used");
       }
       break;
     }
     }
   }
   
 protected:
   void NotifyExpired(Object* aObj) {