merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 08 Jul 2015 11:44:32 +0200
changeset 283509 9b902b7669aec048987e332161215b42514a0dfb
parent 283469 bf6d0e98e8c5eb0f81fe9ee052c5fe526923816f (current diff)
parent 283508 2a70a0ed2866fd33307f51c476d844f534c7742b (diff)
child 283510 e25de9972a7779490fcab6a522a6a808211f1c3d
child 283514 4bdba44f56e598fa3afbf6528181980e2bae7f61
child 283534 169640b8e1854e7fb4c3053c8d54cce413852308
child 283565 89d62eb8b35dcb8deaab82bdfd73f8ca4f8be43a
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
9b902b7669ae / 42.0a1 / 20150708030204 / files
nightly linux64
9b902b7669ae / 42.0a1 / 20150708030204 / files
nightly mac
9b902b7669ae / 42.0a1 / 20150708030204 / files
nightly win32
9b902b7669ae / 42.0a1 / 20150708030204 / files
nightly win64
9b902b7669ae / 42.0a1 / 20150708030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/manifest/ImageObjectProcessor.js
dom/manifest/ManifestObtainer.js
dom/manifest/ManifestProcessor.js
dom/manifest/ValueExtractor.js
dom/manifest/WebManifest.jsm
testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/016.html.ini
testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/094.html.ini
testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/101.html.ini
xpcom/tests/TestPLDHash.cpp
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -123,16 +123,17 @@
 @BINPATH@/@MOZ_APP_NAME@-bin
 @BINPATH@/@MOZ_APP_NAME@
 #endif
 @RESPATH@/application.ini
 @RESPATH@/platform.ini
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #endif
+@BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
 @RESPATH@/blocklist.xml
 @RESPATH@/ua-update.json
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 @RESPATH@/run-mozilla.sh
 #endif
 #endif
 #ifdef MOZ_WEBSPEECH_MODELS
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -89,16 +89,44 @@ Sanitizer.prototype = {
       // When cancelled, reject immediately
       if (!ok) {
         deferred.reject("Sanitizer canceled closing windows");
       }
 
       return deferred.promise;
     }
 
+    let cookiesIndex = itemsToClear.indexOf("cookies");
+    if (cookiesIndex != -1) {
+      itemsToClear.splice(cookiesIndex, 1);
+      let item = this.items.cookies;
+      item.range = this.range;
+      let ok = item.clear(() => {
+        try {
+          if (!itemsToClear.length) {
+            // we're done
+            deferred.resolve();
+            return;
+          }
+          let clearedPromise = this.sanitize(itemsToClear);
+          clearedPromise.then(deferred.resolve, deferred.reject);
+        } catch(e) {
+          let error = "Sanitizer threw after clearing cookies: " + e;
+          Cu.reportError(error);
+          deferred.reject(error);
+        }
+      });
+      // When cancelled, reject immediately
+      if (!ok) {
+        deferred.reject("Sanitizer canceled clearing cookies");
+      }
+
+      return deferred.promise;
+    }
+
     TelemetryStopwatch.start("FX_SANITIZE_TOTAL");
 
     // Cache the range of times to clear
     if (this.ignoreTimespan)
       var range = null;  // If we ignore timespan, clear everything
     else
       range = this.range || Sanitizer.getClearRange();
 
@@ -172,17 +200,17 @@ Sanitizer.prototype = {
 
       get canClear()
       {
         return true;
       }
     },
 
     cookies: {
-      clear: function ()
+      clear: function (aCallback)
       {
         TelemetryStopwatch.start("FX_SANITIZE_COOKIES");
         TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2");
 
         var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"]
                                   .getService(Ci.nsICookieManager);
         if (this.range) {
           // Iterate through the cookies and delete any created after our cutoff.
@@ -204,47 +232,63 @@ Sanitizer.prototype = {
 
         // Clear deviceIds. Done asynchronously (returns before complete).
         let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
                                  .getService(Ci.nsIMediaManagerService);
         mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
 
         // Clear plugin data.
         TelemetryStopwatch.start("FX_SANITIZE_PLUGINS");
+        this.clearPluginCookies().then(
+          function() {
+            TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS");
+            TelemetryStopwatch.finish("FX_SANITIZE_COOKIES");
+            aCallback();
+          });
+        return true;
+      },
+
+      clearPluginCookies: function() {
         const phInterface = Ci.nsIPluginHost;
         const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
         let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
 
         // Determine age range in seconds. (-1 means clear all.) We don't know
         // that this.range[1] is actually now, so we compute age range based
         // on the lower bound. If this.range results in a negative age, do
         // nothing.
-        let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000)
-                             : -1;
+        let age = this.range ? (Date.now() / 1000 - this.range[0] / 1000000) : -1;
         if (!this.range || age >= 0) {
           let tags = ph.getPluginTags();
-          for (let i = 0; i < tags.length; i++) {
-            try {
-              ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, age);
-            } catch (e) {
-              // If the plugin doesn't support clearing by age, clear everything.
-              if (e.result == Components.results.
-                    NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
-                try {
-                  ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, -1);
-                } catch (e) {
-                  // Ignore errors from the plugin
-                }
+          function iterate(tag) {
+            let promise = new Promise(resolve => {
+              try {
+                let onClear = function(rv) {
+                  // If the plugin doesn't support clearing by age, clear everything.
+                  if (rv == Components.results. NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
+                    ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, function() {
+                      resolve();
+                    });
+                  } else {
+                    resolve();
+                  }
+                };
+                ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, onClear);
+              } catch (ex) {
+                resolve();
               }
-            }
+            });
+            return promise;
           }
+          let promises = [];
+          for (let tag of tags) {
+            promises.push(iterate(tag));
+          }
+          return Promise.all(promises);
         }
-
-        TelemetryStopwatch.finish("FX_SANITIZE_PLUGINS");
-        TelemetryStopwatch.finish("FX_SANITIZE_COOKIES");
       },
 
       get canClear()
       {
         return true;
       }
     },
 
--- a/browser/base/content/test/plugins/browser_clearplugindata.js
+++ b/browser/base/content/test/plugins/browser_clearplugindata.js
@@ -79,25 +79,25 @@ add_task(function* () {
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
     "Data stored for sites");
 
   // Clear 20 seconds ago
   let now_uSec = Date.now() * 1000;
   sanitizer.range = [now_uSec - 20*1000000, now_uSec];
-  sanitizer.sanitize();
+  yield sanitizer.sanitize();
 
   ok(stored(["bar.com","qux.com"]), "Data stored for sites");
   ok(!stored(["foo.com"]), "Data cleared for foo.com");
   ok(!stored(["baz.com"]), "Data cleared for baz.com");
 
   // Clear everything
   sanitizer.range = null;
-  sanitizer.sanitize();
+  yield sanitizer.sanitize();
 
   ok(!stored(null), "All data cleared");
 
   gBrowser.removeCurrentTab();
   gTestBrowser = null;
 });
 
 add_task(function* () {
@@ -112,16 +112,16 @@ add_task(function* () {
   ok(stored(["foo.com","bar.com","baz.com","qux.com"]),
     "Data stored for sites");
 
   // Attempt to clear 20 seconds ago. The plugin will throw
   // NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, which should result in us
   // clearing all data regardless of age.
   let now_uSec = Date.now() * 1000;
   sanitizer.range = [now_uSec - 20*1000000, now_uSec];
-  sanitizer.sanitize();
+  yield sanitizer.sanitize();
 
   ok(!stored(null), "All data cleared");
 
   gBrowser.removeCurrentTab();
   gTestBrowser = null;
 });
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -146,16 +146,17 @@
 @RESPATH@/update-settings.ini
 #endif
 @RESPATH@/platform.ini
 #ifndef MOZ_NATIVE_SQLITE
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #endif
 #endif
+@BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
 @RESPATH@/browser/blocklist.xml
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 @RESPATH@/run-mozilla.sh
 #endif
 #endif
 
 ; [Components]
--- a/config/config.mk
+++ b/config/config.mk
@@ -624,35 +624,62 @@ EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselis
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
 EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
 EXPAND_MKSHLIB_ARGS = --uselist
 ifdef SYMBOL_ORDER
 EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
 endif
 EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
+# $(call CHECK_SYMBOLS,lib,PREFIX,dep_name,test)
+# Checks that the given `lib` doesn't contain dependency on symbols with a
+# version starting with `PREFIX`_ and matching the `test`. `dep_name` is only
+# used for the error message.
+# `test` is an awk expression using the information in the variable `v` which
+# contains a list of version items ([major, minor, ...]).
+define CHECK_SYMBOLS
+@$(TOOLCHAIN_PREFIX)readelf -sW $(1) | \
+awk '$$8 ~ /@$(2)_/ { \
+	split($$8,a,"@"); \
+	split(a[2],b,"_"); \
+	split(b[2],v,"."); \
+	if ($(4)) { \
+		if (!found) { \
+			print "TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these $(3) symbols to be used:" \
+		} \
+		print " ",$$8; \
+		found=1 \
+	} \
+} \
+END { \
+	if (found) { \
+		exit(1) \
+	} \
+}'
+endef
+
 ifneq (,$(MOZ_LIBSTDCXX_TARGET_VERSION)$(MOZ_LIBSTDCXX_HOST_VERSION))
-ifneq ($(OS_ARCH),Darwin)
-CHECK_STDCXX = @$(TOOLCHAIN_PREFIX)objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(1[1-9]\|[2-9][0-9]\)' > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these libstdc++ symbols to be used:' && $(TOOLCHAIN_PREFIX)objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(1[1-9]\|[2-9][0-9]\)' && exit 1 || true
-endif
+CHECK_STDCXX = $(call CHECK_SYMBOLS,$(1),GLIBCXX,libstdc++,v[1] > 3 || (v[1] == 3 && v[2] == 4 && v[3] > 10))
+CHECK_GLIBC = $(call CHECK_SYMBOLS,$(1),GLIBC,libc,v[1] > 2 || (v[1] == 2 && v[2] > 7))
 endif
 
 ifeq (,$(filter $(OS_TARGET),WINNT Darwin))
 CHECK_TEXTREL = @$(TOOLCHAIN_PREFIX)readelf -d $(1) | grep TEXTREL > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_textrel | We do not want text relocations in libraries and programs' || true
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),android)
 # While this is very unlikely (libc being added by the compiler at the end
 # of the linker command line), if libmozglue.so ends up after libc.so, all
 # hell breaks loose, so better safe than sorry, and check it's actually the
 # case.
 CHECK_MOZGLUE_ORDER = @$(TOOLCHAIN_PREFIX)readelf -d $(1) | grep NEEDED | awk '{ libs[$$NF] = ++n } END { if (libs["[libmozglue.so]"] && libs["[libc.so]"] < libs["[libmozglue.so]"]) { print "libmozglue.so must be linked before libc.so"; exit 1 } }'
 endif
 
 define CHECK_BINARY
+$(call CHECK_GLIBC,$(1))
 $(call CHECK_STDCXX,$(1))
 $(call CHECK_TEXTREL,$(1))
 $(call LOCAL_CHECKS,$(1))
 $(call CHECK_MOZGLUE_ORDER,$(1))
 endef
 
 # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
 # this file
new file mode 100644
--- /dev/null
+++ b/config/external/lgpllibs/moz.build
@@ -0,0 +1,14 @@
+# -*- 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/.
+
+# The lgpllibs library stores symbols from third-party LGPL licensed libraries,
+# such as libav and libsoundtouch. It fulfills the requirement of dynamically
+# linking these symbols into gecko.
+#
+# Any library added here should also be reflected in the about:license page.
+
+SharedLibrary('lgpllibs')
+SHARED_LIBRARY_NAME = 'lgpllibs'
--- a/config/external/moz.build
+++ b/config/external/moz.build
@@ -2,16 +2,17 @@
 # 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/.
 
 external_dirs = []
 
 DIRS += [
+    'lgpllibs',
     'sqlite',
 ]
 if not CONFIG['MOZ_NATIVE_JPEG']:
     external_dirs += ['media/libjpeg']
 
 if CONFIG['MOZ_UPDATER']:
     if not CONFIG['MOZ_NATIVE_BZ2']:
         external_dirs += ['modules/libbz2']
--- a/config/system-headers
+++ b/config/system-headers
@@ -1252,16 +1252,17 @@ X11/X.h
 X11/XKBlib.h
 X11/Xlib.h
 X11/Xlibint.h
 X11/Xlocale.h
 X11/Xos.h
 X11/Xutil.h
 zmouse.h
 soundtouch/SoundTouch.h
+soundtouch/SoundTouchFactory.h
 #if MOZ_NATIVE_PNG==1
 png.h
 #endif
 #if MOZ_NATIVE_ZLIB==1
 zlib.h
 #endif
 #ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
 libsn/sn.h
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -101,16 +101,17 @@ static RedirEntry kRedirMap[] = {
   },
   {
     "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml",
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+    nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT
   },
   // about:srcdoc is unresolvable by specification.  It is included here
   // because the security manager would disallow srcdoc iframes otherwise.
   {
     "srcdoc", "about:blank",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3191,17 +3191,20 @@ nsDOMWindowUtils::HandleFullscreenReques
   return NS_OK;
 }
 
 nsresult
 nsDOMWindowUtils::ExitFullscreen()
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
-  nsIDocument::ExitFullscreen(nullptr, /* async */ false);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+  NS_ENSURE_STATE(doc);
+
+  nsIDocument::ExitFullscreen(doc, /* async */ false);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SelectAtPoint(float aX, float aY, uint32_t aSelectBehavior,
                                 bool *_retval)
 {
   *_retval = false;
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -93,16 +93,19 @@ nsImageLoadingContent::nsImageLoadingCon
     mCurrentRequestRegistered(false),
     mPendingRequestRegistered(false),
     mFrameCreateCalled(false),
     mVisibleCount(0)
 {
   if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
     mLoadingEnabled = false;
   }
+
+  bool isInconsistent;
+  mMostRecentRequestChange = TimeStamp::ProcessCreation(isInconsistent);
 }
 
 void
 nsImageLoadingContent::DestroyImageLoadingContent()
 {
   // Cancel our requests so they won't hold stale refs to us
   // NB: Don't ask to discard the images here.
   ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_NO_ACTION);
@@ -1230,16 +1233,32 @@ nsImageLoadingContent::FireEvent(const n
   loadBlockingAsyncDispatcher->PostDOMEvent();
 
   return NS_OK;
 }
 
 nsRefPtr<imgRequestProxy>&
 nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
 {
+  nsImageFrame* frame = do_QueryFrame(GetOurPrimaryFrame());
+  if (frame) {
+    // Detect JavaScript-based animations created by changing the |src|
+    // attribute on a timer.
+    TimeStamp now = TimeStamp::Now();
+    TimeDuration threshold =
+      TimeDuration::FromMilliseconds(
+        gfxPrefs::ImageInferSrcAnimationThresholdMS());
+
+    // If the length of time between request changes is less than the threshold,
+    // then force sync decoding to eliminate flicker from the animation.
+    frame->SetForceSyncDecoding(now - mMostRecentRequestChange < threshold);
+
+    mMostRecentRequestChange = now;
+  }
+
   // If we don't have a usable current request, get rid of any half-baked
   // request that might be sitting there and make this one current.
   if (!HaveSize(mCurrentRequest))
     return PrepareCurrentRequest(aImageLoadType);
 
   // Otherwise, make it pending.
   return PreparePendingRequest(aImageLoadType);
 }
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -12,16 +12,17 @@
 
 #ifndef nsImageLoadingContent_h__
 #define nsImageLoadingContent_h__
 
 #include "imgINotificationObserver.h"
 #include "imgIOnloadBlocker.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIRequest.h"
 #include "mozilla/ErrorResult.h"
 #include "nsAutoPtr.h"
 #include "nsIContentPolicy.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/net/ReferrerPolicy.h"
@@ -392,16 +393,18 @@ private:
   ImageObserver mObserverList;
 
   /**
    * When mIsImageStateForced is true, this holds the ImageState that we'll
    * return in ImageState().
    */
   mozilla::EventStates mForcedImageState;
 
+  mozilla::TimeStamp mMostRecentRequestChange;
+
   int16_t mImageBlockingStatus;
   bool mLoadingEnabled : 1;
 
   /**
    * When true, we return mForcedImageState from ImageState().
    */
   bool mIsImageStateForced : 1;
 
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -496,17 +496,21 @@ nsPerformance::Navigation()
     mNavigation = new nsPerformanceNavigation(this);
   }
   return mNavigation;
 }
 
 DOMHighResTimeStamp
 nsPerformance::Now() const
 {
-  return GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now());
+  double nowTimeMs = GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now());
+  // Round down to the nearest 0.005ms (5us), because if the timer is too
+  // accurate people can do nasty timing attacks with it.
+  const double maxResolutionMs = 0.005;
+  return floor(nowTimeMs / maxResolutionMs) * maxResolutionMs;
 }
 
 JSObject*
 nsPerformance::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return PerformanceBinding::Wrap(cx, this, aGivenProto);
 }
 
--- a/dom/base/test/chrome/cpows_child.js
+++ b/dom/base/test/chrome/cpows_child.js
@@ -85,16 +85,20 @@ function make_object()
 function make_json()
 {
   return { check: "ok" };
 }
 
 function parent_test(finish)
 {
   function f(check_func) {
+    // Make sure this doesn't crash.
+    let array = new Uint32Array(10);
+    content.crypto.getRandomValues(array);
+
     let result = check_func(10);
     ok(result == 20, "calling function in parent worked");
     return result;
   }
 
   addMessageListener("cpows:from_parent", (msg) => {
     let obj = msg.objects.obj;
     ok(obj.a == 1, "correct value from parent");
--- a/dom/base/test/test_bug372964-2.html
+++ b/dom/base/test/test_bug372964-2.html
@@ -29,19 +29,27 @@ function runTest() {
   event.initEvent("foo", true, true);
   xhr.dispatchEvent(event);
   is(eventCount, 1, "Should have handled an event");
   ifr.contentDocument.open();
   ifr.contentDocument.close();
   event = ifr.contentDocument.createEvent("Events");
   event.initEvent("foo", true, true);
   xhr.dispatchEvent(event);
-  is(eventCount, 1,
-     "Shouldn't have handled an event because the context has changed");
-  SimpleTest.finish();
+  is(eventCount, 2,
+     "Should have handled the event because open()/close() keep the active document");
+  ifr.onload = function() {
+    event = ifr.contentDocument.createEvent("Events");
+    event.initEvent("foo", true, true);
+    xhr.dispatchEvent(event);
+    is(eventCount, 2,
+      "Shouldn't have handled an event because the context has changed");
+    SimpleTest.finish();
+  };
+  ifr.contentWindow.location = "about:blank";
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTest);
 
 </script>
 </pre>
 <iframe src="data:text/html,<script>function listener() { ++parent.eventCount; } </script>">
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -86,24 +86,23 @@ CallbackObject::CallSetup::CallSetup(Cal
     // GC, so just paper over the necessary dataflow inversion.
     JS::AutoSuppressGCAnalysis nogc;
     if (mIsMainThread) {
       // Now get the global and JSContext for this callback.  Note that for the
       // case of JS-implemented WebIDL we never have a window here.
       nsGlobalWindow* win =
         aIsJSImplementedWebIDL ? nullptr : xpc::WindowGlobalOrNull(realCallback);
       if (win) {
-        // Make sure that if this is a window it's the current inner, since the
-        // nsIScriptContext and hence JSContext are associated with the outer
-        // window.  Which means that if someone holds on to a function from a
-        // now-unloaded document we'd have the new document as the script entry
-        // point...
+        // Make sure that if this is a window it has an active document, since
+        // the nsIScriptContext and hence JSContext are associated with the
+        // outer window.  Which means that if someone holds on to a function
+        // from a now-unloaded document we'd have the new document as the
+        // script entry point...
         MOZ_ASSERT(win->IsInnerWindow());
-        nsPIDOMWindow* outer = win->GetOuterWindow();
-        if (!outer || win != outer->GetCurrentInnerWindow()) {
+        if (!win->HasActiveDocument()) {
           // Just bail out from here
           return;
         }
         cx = win->GetContext() ? win->GetContext()->GetNativeContext()
                                // This happens - Removing it causes
                                // test_bug293235.xul to go orange.
                                : nsContentUtils::GetSafeJSContext();
         globalObject = win;
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -20,16 +20,17 @@ support-files =
 [test_bug862092.html]
 [test_bug1036214.html]
 skip-if = debug == false
 [test_bug963382.html]
 skip-if = debug == false
 [test_bug1041646.html]
 [test_bug1123875.html]
 [test_barewordGetsWindow.html]
+[test_callback_across_document_open.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
 [test_forOf.html]
 [test_integers.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_callback_across_document_open.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for callback invocation for a callback that comes from a
+  no-longer-current window that still has an active document.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe srcdoc='<script>function f() { parent.callCount++; }</script>'></iframe>
+<script>
+  var callCount = 0;
+  var t = async_test("A test of callback invocation in a no-longer-current window with a still-active document");
+  window.addEventListener("load", t.step_func_done(function() {
+    var d = document.createElement("div");
+    d.addEventListener("xyz", frames[0].f);
+    frames[0].document.open();
+    frames[0].document.write("All gone");
+    frames[0].document.close();
+    d.dispatchEvent(new Event("xyz"));
+    assert_equals(callCount, 1, "Callback should have been called");
+  }));
+</script>
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -1190,18 +1190,51 @@ DataStoreService::CheckPermission(nsIPri
     return false;
   }
 
   uint16_t status;
   if (NS_FAILED(aPrincipal->GetAppStatus(&status))) {
     return false;
   }
 
-  // Only support DataStore API for certified apps for now.
-  return status == nsIPrincipal::APP_STATUS_CERTIFIED;
+  // Certified apps are always allowed.
+  if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
+    return true;
+  }
+
+  if (status != nsIPrincipal::APP_STATUS_PRIVILEGED) {
+    return false;
+  }
+
+  // Privileged apps are allowed if they are the homescreen.
+  nsAdoptingString homescreen =
+    Preferences::GetString("dom.mozApps.homescreenURL");
+  if (!homescreen) {
+    return false;
+  }
+
+  uint32_t appId;
+  nsresult rv = aPrincipal->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsCOMPtr<nsIAppsService> appsService =
+    do_GetService("@mozilla.org/AppsService;1");
+  if (NS_WARN_IF(!appsService)) {
+    return false;
+  }
+
+  nsAutoString manifestURL;
+  rv = appsService->GetManifestURLByLocalId(appId, manifestURL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return manifestURL.Equals(homescreen);
 }
 
 NS_IMETHODIMP
 DataStoreService::CheckPermission(nsIPrincipal* aPrincipal,
                                   bool* aResult)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/events/test/bug426082.html
+++ b/dom/events/test/bug426082.html
@@ -106,17 +106,20 @@ function tests() {
   compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Moving the mouse down from the label should have unpressed the button.");
   // ... and up again.
   sendMouseEvent("mousemove", label);
   yield undefined;
   compareSnapshots_(pressedButtonCanvas, currentSnapshot, true, "Moving the mouse back on top of the label should have pressed the button.");
   // Release.
   sendMouseEvent("mouseup", label);
   yield undefined;
-  compareSnapshots_(normalFocusedButtonCanvas, currentSnapshot, true, "Releasing the mouse over the label should have unpressed (and focused) the button.");
+  var focusOnMouse = (navigator.platform.indexOf("Mac") != 0);
+  compareSnapshots_(focusOnMouse ? normalFocusedButtonCanvas : normalButtonCanvas,
+                    currentSnapshot, true, "Releasing the mouse over the label should have unpressed" +
+                                            (focusOnMouse ? " (and focused)" : "") + " the button.");
   // Press the label and remove it.
   sendMouseEvent("mousemove", label);
   sendMouseEvent("mousedown", label);
   yield undefined;
   label.parentNode.removeChild(label);
   yield undefined;
   compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Removing the label should have unpressed the button.");
   sendMouseEvent("mouseup", label);
--- a/dom/events/test/bug656379-1.html
+++ b/dom/events/test/bug656379-1.html
@@ -122,17 +122,20 @@ function tests() {
   compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Moving the mouse down from the label should have unpressed the button.");
   // ... and up again.
   sendMouseEvent("mousemove", label);
   yield undefined;
   compareSnapshots_(pressedButtonCanvas, currentSnapshot, true, "Moving the mouse back on top of the label should have pressed the button.");
   // Release.
   sendMouseEvent("mouseup", label);
   yield undefined;
-  compareSnapshots_(normalFocusedButtonCanvas, currentSnapshot, true, "Releasing the mouse over the label should have unpressed (and focused) the button.");
+  var focusOnMouse = (navigator.platform.indexOf("Mac") != 0);
+  compareSnapshots_(focusOnMouse ? normalFocusedButtonCanvas : normalButtonCanvas,
+                    currentSnapshot, true, "Releasing the mouse over the label should have unpressed" +
+                                            (focusOnMouse ? " (and focused)" : "") + " the button.");
   // Press the label and remove it.
   sendMouseEvent("mousemove", label);
   sendMouseEvent("mousedown", label);
   yield undefined;
   label.parentNode.removeChild(label);
   yield undefined;
   compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Removing the label should have unpressed the button.");
   sendMouseEvent("mouseup", label);
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -164,18 +164,24 @@ HTMLLabelElement::PostHandleEvent(EventC
           if (mouseEvent->clickCount <= 1) {
             nsIFocusManager* fm = nsFocusManager::GetFocusManager();
             if (fm) {
               // Use FLAG_BYMOVEFOCUS here so that the label is scrolled to.
               // Also, within HTMLInputElement::PostHandleEvent, inputs will
               // be selected only when focused via a key or when the navigation
               // flag is used and we want to select the text on label clicks as
               // well.
+              // If the label has been clicked by the user, we also want to
+              // pass FLAG_BYMOUSE so that we get correct focus ring behavior,
+              // but we don't want to pass FLAG_BYMOUSE if this click event was
+              // caused by the user pressing an accesskey.
               nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(content);
-              fm->SetFocus(elem, nsIFocusManager::FLAG_BYMOVEFOCUS);
+              bool byMouse = (mouseEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD);
+              fm->SetFocus(elem, nsIFocusManager::FLAG_BYMOVEFOCUS |
+                                 (byMouse ? nsIFocusManager::FLAG_BYMOUSE : 0));
             }
           }
           // Dispatch a new click event to |content|
           //    (For compatibility with IE, we do only left click.  If
           //    we wanted to interpret the HTML spec very narrowly, we
           //    would do nothing.  If we wanted to do something
           //    sensible, we might send more events through like
           //    this.)  See bug 7554, bug 49897, and bug 96813.
--- a/dom/html/test/test_fullscreen-api-race.html
+++ b/dom/html/test/test_fullscreen-api-race.html
@@ -67,21 +67,39 @@ const ACTION_FUNCS = [
   function exitAndClose(win) {
     info("About to cancel fullscreen and close the window");
     win.document.mozCancelFullScreen();
     win.close();
     return Promise.resolve();
   }
 ];
 
+const DISABLE_LIST = [
+  // Bug 1180574
+  { openWinFunc: "openNewWindow",
+    actionFunc: "navigate",
+    platform: "Linux i686" }
+];
+
 function* testGenerator() {
   for (var openWinFunc of OPEN_WINDOW_FUNCS) {
     for (var actionFunc of ACTION_FUNCS) {
-      info(`Testing ${openWinFunc.name}, ${actionFunc.name}`);
-      yield { openWinFunc: openWinFunc, actionFunc: actionFunc };
+      var skipTest = false;
+      for (var disabledItem of DISABLE_LIST) {
+        if (openWinFunc.name == disabledItem.openWinFunc &&
+            actionFunc.name == disabledItem.actionFunc &&
+            navigator.platform == disabledItem.platform) {
+          skipTest = true;
+          break;
+        }
+      }
+      if (!skipTest) {
+        info(`Testing ${openWinFunc.name}, ${actionFunc.name}`);
+        yield { openWinFunc: openWinFunc, actionFunc: actionFunc };
+      }
     }
   }
 }
 
 var tests = testGenerator();
 
 function next() {
   var test = tests.next().value;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -727,17 +727,17 @@ parent:
 
     PRemoteSpellcheckEngine();
     PDeviceStorageRequest(DeviceStorageParams params);
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
-    sync GetRandomValues(uint32_t length)
+    prio(urgent) sync GetRandomValues(uint32_t length)
         returns (uint8_t[] randomValues);
 
     async GetSystemMemory(uint64_t getterId);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
     sync GetLookAndFeelCache(LookAndFeelInt[] lookAndFeelIntCache);
--- a/dom/ipc/manifestMessages.js
+++ b/dom/ipc/manifestMessages.js
@@ -6,117 +6,87 @@
  * http://www.w3.org/TR/appmanifest/#obtaining
  *
  * It searches a top-level browsing context for
  * a <link rel=manifest> element. Then fetches
  * and processes the linked manifest.
  *
  * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
  */
-/*globals content, sendAsyncMessage, addMessageListener, Components*/
+/*globals Task, ManifestObtainer, ManifestFinder, content, sendAsyncMessage, addMessageListener, Components*/
 'use strict';
 const {
   utils: Cu,
-  classes: Cc,
-  interfaces: Ci
 } = Components;
-const {
-  ManifestProcessor
-} = Cu.import('resource://gre/modules/WebManifest.jsm', {});
-const {
-  Task: {
-    spawn, async
-  }
-} = Components.utils.import('resource://gre/modules/Task.jsm', {});
+Cu.import('resource://gre/modules/ManifestObtainer.jsm');
+Cu.import('resource://gre/modules/ManifestFinder.jsm');
+Cu.import('resource://gre/modules/Task.jsm');
+
+const finder = new ManifestFinder();
+
+const MessageHandler = {
+  registerListeners() {
+    addMessageListener(
+      'DOM:WebManifest:hasManifestLink',
+      this.hasManifestLink.bind(this)
+    );
+    addMessageListener(
+      'DOM:ManifestObtainer:Obtain',
+      this.obtainManifest.bind(this)
+    );
+  },
 
-addMessageListener('DOM:ManifestObtainer:Obtain', async(function* (aMsg) {
-  const response = {
-    msgId: aMsg.data.msgId,
-    success: true,
-    result: undefined
-  };
-  try {
-    response.result = yield fetchManifest();
-  } catch (err) {
-    response.success = false;
-    response.result = cloneError(err);
-  }
-  sendAsyncMessage('DOM:ManifestObtainer:Obtain', response);
-}));
+  /**
+   * Check if the content document includes a link to a web manifest.
+   * @param {Object} aMsg The IPC message.
+   */
+  hasManifestLink: Task.async(function* ({data: {id}}) {
+    const response = this.makeMsgResponse(id);
+    response.result = yield finder.hasManifestLink(content);
+    response.success = true;
+    sendAsyncMessage('DOM:WebManifest:hasManifestLink', response);
+  }),
 
-function cloneError(aError) {
-  const clone = {
-    'fileName': String(aError.fileName),
-    'lineNumber': String(aError.lineNumber),
-    'columnNumber': String(aError.columnNumber),
-    'stack': String(aError.stack),
-    'message': String(aError.message),
-    'name': String(aError.name)
-  };
-  return clone;
-}
-
-function fetchManifest() {
-  return spawn(function* () {
-    if (!content || content.top !== content) {
-      let msg = 'Content window must be a top-level browsing context.';
-      throw new Error(msg);
-    }
-    const elem = content.document.querySelector('link[rel~="manifest"]');
-    if (!elem || !elem.getAttribute('href')) {
-      let msg = 'No manifest to fetch.';
-      throw new Error(msg);
+  /**
+   * Obtains a web manifest from content by using the ManifestObtainer
+   * and messages back the result.
+   * @param {Object} aMsg The IPC message.
+   */
+  obtainManifest: Task.async(function* ({data: {id}}) {
+    const obtainer = new ManifestObtainer();
+    const response = this.makeMsgResponse(id);
+    try {
+      response.result = yield obtainer.obtainManifest(content);
+      response.success = true;
+    } catch (err) {
+      response.result = this.serializeError(err);
     }
-    // Throws on malformed URLs
-    const manifestURL = new content.URL(elem.href, elem.baseURI);
-    if (!canLoadManifest(elem)) {
-      let msg = `Content Security Policy: The page's settings blocked the `;
-      msg += `loading of a resource at ${elem.href}`;
-      throw new Error(msg);
-    }
-    const reqInit = {
-      mode: 'cors'
+    sendAsyncMessage('DOM:ManifestObtainer:Obtain', response);
+  }),
+
+  makeMsgResponse(aId) {
+    return {
+      id: aId,
+      success: false,
+      result: undefined
     };
-    if (elem.crossOrigin === 'use-credentials') {
-      reqInit.credentials = 'include';
-    }
-    const req = new content.Request(manifestURL, reqInit);
-    req.setContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
-    const response = yield content.fetch(req);
-    const manifest = yield processResponse(response, content);
-    return manifest;
-  });
-}
+  },
 
-function canLoadManifest(aElem) {
-  const contentPolicy = Cc['@mozilla.org/layout/content-policy;1']
-    .getService(Ci.nsIContentPolicy);
-  const mimeType = aElem.type || 'application/manifest+json';
-  const elemURI = BrowserUtils.makeURI(
-    aElem.href, aElem.ownerDocument.characterSet
-  );
-  const shouldLoad = contentPolicy.shouldLoad(
-    Ci.nsIContentPolicy.TYPE_WEB_MANIFEST, elemURI,
-    aElem.ownerDocument.documentURIObject,
-    aElem, mimeType, null
-  );
-  return shouldLoad === Ci.nsIContentPolicy.ACCEPT;
-}
-
-function processResponse(aResp, aContentWindow) {
-  return spawn(function* () {
-    const badStatus = aResp.status < 200 || aResp.status >= 300;
-    if (aResp.type === 'error' || badStatus) {
-      let msg =
-        `Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
-      throw new Error(msg);
-    }
-    const text = yield aResp.text();
-    const args = {
-      jsonText: text,
-      manifestURL: aResp.url,
-      docURL: aContentWindow.location.href
+  /**
+   * Utility function to Serializes an JS Error, so it can be transferred over
+   * the message channel.
+   * FIX ME: https://bugzilla.mozilla.org/show_bug.cgi?id=1172586
+   * @param  {Error} aError The error to serialize.
+   * @return {Object} The serialized object.
+   */
+  serializeError(aError) {
+    const clone = {
+      'fileName': aError.fileName,
+      'lineNumber': aError.lineNumber,
+      'columnNumber': aError.columnNumber,
+      'stack': aError.stack,
+      'message': aError.message,
+      'name': aError.name
     };
-    const processor = new ManifestProcessor();
-    const manifest = processor.process(args);
-    return Cu.cloneInto(manifest, content);
-  });
-}
+    return clone;
+  },
+};
+MessageHandler.registerListeners();
rename from dom/manifest/ImageObjectProcessor.js
rename to dom/manifest/ImageObjectProcessor.jsm
new file mode 100644
--- /dev/null
+++ b/dom/manifest/ManifestFinder.jsm
@@ -0,0 +1,58 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+/* globals Components, Task, PromiseMessage */
+'use strict';
+const {
+  utils: Cu
+} = Components;
+Cu.import('resource://gre/modules/PromiseMessage.jsm');
+Cu.import('resource://gre/modules/Task.jsm');
+
+/**
+ * @constructor
+ */
+function ManifestFinder() {}
+
+/**
+ * checks if a browser window's document has a conforming
+ * manifest link relationship.
+ * @param aWindowOrBrowser the XUL browser or window to check.
+ * @return {Promise}
+ */
+ManifestFinder.prototype.hasManifestLink = Task.async(
+  function* (aWindowOrBrowser) {
+    const msgKey = 'DOM:WebManifest:hasManifestLink';
+    if (!(aWindowOrBrowser && (aWindowOrBrowser.namespaceURI || aWindowOrBrowser.location))) {
+      throw new TypeError('Invalid input.');
+    }
+    if (isXULBrowser(aWindowOrBrowser)) {
+      const mm = aWindowOrBrowser.messageManager;
+      const reply = yield PromiseMessage.send(mm, msgKey);
+      return reply.data.result;
+    }
+    return checkForManifest(aWindowOrBrowser);
+  }
+);
+
+function isXULBrowser(aBrowser) {
+  const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+  return (aBrowser.namespaceURI && aBrowser.namespaceURI === XUL);
+}
+
+function checkForManifest(aWindow) {
+  // Only top-level browsing contexts are valid.
+  if (!aWindow || aWindow.top !== aWindow) {
+    return false;
+  }
+  const elem = aWindow.document.querySelector('link[rel~="manifest"]');
+  // Only if we have an element and a non-empty href attribute.
+  if (!elem || !elem.getAttribute('href')) {
+    return false;
+  }
+  return true;
+}
+
+this.EXPORTED_SYMBOLS = [ // jshint ignore:line
+  'ManifestFinder'
+];
deleted file mode 100644
--- a/dom/manifest/ManifestObtainer.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* 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/.
- *
- * ManifestObtainer is an implementation of:
- * http://w3c.github.io/manifest/#obtaining
- *
- * Exposes public method `.obtainManifest(browserWindow)`, which returns
- * a promise. If successful, you get back a manifest (string).
- *
- * For e10s compat, this JSM relies on the following to do
- * the nessesary IPC:
- *   dom/ipc/manifestMessages.js
- *
- * whose internal URL is:
- *   'chrome://global/content/manifestMessages.js'
- *
- * Which is injected into every browser instance via browser.js.
- *
- * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
- * exported ManifestObtainer
- */
-'use strict';
-const MSG_KEY = 'DOM:ManifestObtainer:Obtain';
-let messageCounter = 0;
-// FIXME: Ideally, we would store a reference to the
-//        message manager in a weakmap instead of needing a
-//        browserMap. However, trying to store a messageManager
-//        results in a TypeError because of:
-//        https://bugzilla.mozilla.org/show_bug.cgi?id=888600
-const browsersMap = new WeakMap();
-
-function ManifestObtainer() {}
-
-ManifestObtainer.prototype = {
-  obtainManifest(aBrowserWindow) {
-    if (!aBrowserWindow) {
-      const err = new TypeError('Invalid input. Expected xul browser.');
-      return Promise.reject(err);
-    }
-    const mm = aBrowserWindow.messageManager;
-    const onMessage = function(aMsg) {
-      const msgId = aMsg.data.msgId;
-      const {
-        resolve, reject
-      } = browsersMap.get(aBrowserWindow).get(msgId);
-      browsersMap.get(aBrowserWindow).delete(msgId);
-      // If we we've processed all messages,
-      // stop listening.
-      if (!browsersMap.get(aBrowserWindow).size) {
-        browsersMap.delete(aBrowserWindow);
-        mm.removeMessageListener(MSG_KEY, onMessage);
-      }
-      if (aMsg.data.success) {
-        return resolve(aMsg.data.result);
-      }
-      reject(toError(aMsg.data.result));
-    };
-    // If we are not already listening for messages
-    // start listening.
-    if (!browsersMap.has(aBrowserWindow)) {
-      browsersMap.set(aBrowserWindow, new Map());
-      mm.addMessageListener(MSG_KEY, onMessage);
-    }
-    return new Promise((resolve, reject) => {
-      const msgId = messageCounter++;
-      browsersMap.get(aBrowserWindow).set(msgId, {
-        resolve: resolve,
-        reject: reject
-      });
-      mm.sendAsyncMessage(MSG_KEY, {
-        msgId: msgId
-      });
-    });
-
-    function toError(aErrorClone) {
-      let error;
-      switch (aErrorClone.name) {
-      case 'TypeError':
-        error = new TypeError();
-        break;
-      default:
-        error = new Error();
-      }
-      Object.getOwnPropertyNames(aErrorClone)
-        .forEach(name => error[name] = aErrorClone[name]);
-      return error;
-    }
-  }
-};
-this.ManifestObtainer = ManifestObtainer; // jshint ignore:line
-this.EXPORTED_SYMBOLS = ['ManifestObtainer']; // jshint ignore:line
new file mode 100644
--- /dev/null
+++ b/dom/manifest/ManifestObtainer.jsm
@@ -0,0 +1,170 @@
+/* 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/.
+ */
+ /*
+ * ManifestObtainer is an implementation of:
+ * http://w3c.github.io/manifest/#obtaining
+ *
+ * Exposes public method `.obtainManifest(Window)`, which returns
+ * a promise. If successful, you get back a manifest object.
+ *
+ * Import it with URL:
+ *   'chrome://global/content/manifestMessages.js'
+ *
+ * e10s IPC messaage from this components are handled by:
+ *   dom/ipc/manifestMessages.js
+ *
+ * Which is injected into every browser instance via browser.js.
+ *
+ * exported ManifestObtainer
+ */
+/*globals Components, Task, PromiseMessage, XPCOMUtils, ManifestProcessor, BrowserUtils*/
+'use strict';
+const {
+  utils: Cu,
+  classes: Cc,
+  interfaces: Ci
+} = Components;
+Cu.import('resource://gre/modules/Task.jsm');
+Cu.import('resource://gre/modules/PromiseMessage.jsm');
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import('resource://gre/modules/ManifestProcessor.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'BrowserUtils',  // jshint ignore:line
+  'resource://gre/modules/BrowserUtils.jsm');
+
+const processor = new ManifestProcessor();
+
+/**
+ * Asynchronously processes the result of response after having fetched
+ * a manifest.
+ * @param {Response} aResp Response from fetch().
+ * @param {Window} aContentWindow The content window.
+ * @return {Promise<Object>} The processed manifest.
+ */
+const processResponse = Task.async(function* (aResp, aContentWindow) {
+  const badStatus = aResp.status < 200 || aResp.status >= 300;
+  if (aResp.type === 'error' || badStatus) {
+    const msg =
+      `Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
+    throw new Error(msg);
+  }
+  const text = yield aResp.text();
+  const args = {
+    jsonText: text,
+    manifestURL: aResp.url,
+    docURL: aContentWindow.location.href
+  };
+  const manifest = processor.process(args);
+  return manifest;
+});
+
+/**
+ * Asynchronously fetches a web manifest.
+ * @param {Window} a The content Window from where to extract the manifest.
+ * @return {Promise<Object>}
+ */
+const fetchManifest = Task.async(function* (aWindow) {
+  if (!aWindow || aWindow.top !== aWindow) {
+    let msg = 'Window must be a top-level browsing context.';
+    throw new Error(msg);
+  }
+  const elem = aWindow.document.querySelector('link[rel~="manifest"]');
+  if (!elem || !elem.getAttribute('href')) {
+    let msg = `No manifest to fetch at ${aWindow.location}`;
+    throw new Error(msg);
+  }
+  // Throws on malformed URLs
+  const manifestURL = new aWindow.URL(elem.href, elem.baseURI);
+  if (!canLoadManifest(elem)) {
+    let msg = `Content Security Policy: The page's settings blocked the `;
+    msg += `loading of a resource at ${elem.href}`;
+    throw new Error(msg);
+  }
+  const reqInit = {
+    mode: 'cors'
+  };
+  if (elem.crossOrigin === 'use-credentials') {
+    reqInit.credentials = 'include';
+  }
+  const req = new aWindow.Request(manifestURL, reqInit);
+  req.setContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
+  const response = yield aWindow.fetch(req);
+  const manifest = yield processResponse(response, aWindow);
+  return manifest;
+});
+
+/**
+ * Checks against security manager if we can load the web manifest.
+ * @param  {HTMLLinkElement} aElem The HTML element to security check.
+ * @return {Boolean} True if it can, false if it can't.
+ */
+function canLoadManifest(aElem) {
+  const contentPolicy = Cc['@mozilla.org/layout/content-policy;1']
+    .getService(Ci.nsIContentPolicy);
+  const mimeType = aElem.type || 'application/manifest+json';
+  const elemURI = BrowserUtils.makeURI(
+    aElem.href, aElem.ownerDocument.characterSet
+  );
+  const shouldLoad = contentPolicy.shouldLoad(
+    Ci.nsIContentPolicy.TYPE_WEB_MANIFEST, elemURI,
+    aElem.ownerDocument.documentURIObject,
+    aElem, mimeType, null
+  );
+  return shouldLoad === Ci.nsIContentPolicy.ACCEPT;
+}
+
+/**
+ * ManifestObtainer
+ * @constructor
+ */
+function ManifestObtainer() {
+}
+
+/**
+ * Public interface for obtaining a web manifest.
+ * @param  {XULWindow or DOMWindow} aWindow The Window from which to fetch
+ *                                          the manifest.
+ * @return {Promise<Object>} The processed manifest.
+ */
+ManifestObtainer.prototype.obtainManifest = Task.async(
+  function* (aWindowOrBrowser) {
+    const msgKey = 'DOM:ManifestObtainer:Obtain';
+    if (!(aWindowOrBrowser && (aWindowOrBrowser.namespaceURI || aWindowOrBrowser.location))) {
+      throw new TypeError('Invalid input.');
+    }
+    if (isXULBrowser(aWindowOrBrowser)) {
+      const mm = aWindowOrBrowser.messageManager;
+      const {data: {success, result}} = yield PromiseMessage.send(mm, msgKey);
+      if (!success) {
+        const error = toError(result);
+        throw error;
+      }
+      return result;
+    }
+    const manifest = yield fetchManifest(aWindowOrBrowser);
+    return manifest;
+  }
+);
+
+function toError(aErrorClone) {
+  let error;
+  switch (aErrorClone.name) {
+  case 'TypeError':
+    error = new TypeError();
+    break;
+  default:
+    error = new Error();
+  }
+  Object.getOwnPropertyNames(aErrorClone)
+    .forEach(name => error[name] = aErrorClone[name]);
+  return error;
+}
+
+function isXULBrowser(aBrowser) {
+  const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+  return (aBrowser.namespaceURI && aBrowser.namespaceURI === XUL);
+}
+
+this.ManifestObtainer = ManifestObtainer; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ['ManifestObtainer']; // jshint ignore:line
rename from dom/manifest/ManifestProcessor.js
rename to dom/manifest/ManifestProcessor.jsm
--- a/dom/manifest/ManifestProcessor.js
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -14,43 +14,35 @@
  *
  * Depends on ImageObjectProcessor to process things like
  * icons and splash_screens.
  *
  * TODO: The constructor should accept the UA's supported orientations.
  * TODO: The constructor should accept the UA's supported display modes.
  * TODO: hook up developer tools to console. (1086997).
  */
-/*globals Components*/
+/*globals Components, ValueExtractor, ImageObjectProcessor, ConsoleAPI*/
 'use strict';
 const {
-  utils: Cu,
-  interfaces: Ci,
-  classes: Cc
+  utils: Cu
 } = Components;
 Cu.importGlobalProperties(['URL']);
 const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
   'browser'
 ]);
 const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
   'portrait-primary', 'portrait-secondary', 'landscape-primary',
   'landscape-secondary'
 ]);
-const {
-  ConsoleAPI
-} = Cu.import('resource://gre/modules/devtools/Console.jsm', {});
+Cu.import('resource://gre/modules/devtools/Console.jsm');
 // ValueExtractor is used by the various processors to get values
 // from the manifest and to report errors.
-const {
-  ValueExtractor
-} = Cu.import('resource://gre/modules/ValueExtractor.js', {});
+Cu.import('resource://gre/modules/ValueExtractor.jsm');
 // ImageObjectProcessor is used to process things like icons and images
-const {
-  ImageObjectProcessor
-} = Cu.import('resource://gre/modules/ImageObjectProcessor.js', {});
+Cu.import('resource://gre/modules/ImageObjectProcessor.jsm');
 
 function ManifestProcessor() {}
 
 // Static getters
 Object.defineProperties(ManifestProcessor, {
   'defaultDisplayMode': {
     get: function() {
       return 'browser';
rename from dom/manifest/ValueExtractor.js
rename to dom/manifest/ValueExtractor.jsm
--- a/dom/manifest/ValueExtractor.js
+++ b/dom/manifest/ValueExtractor.jsm
@@ -20,19 +20,17 @@ ValueExtractor.prototype = {
   // This function takes a 'spec' object and destructures
   // it to extract a value. If the value is of th wrong type, it
   // warns the developer and returns undefined.
   //  expectType: is the type of a JS primitive (string, number, etc.)
   //  object: is the object from which to extract the value.
   //  objectName: string used to construct the developer warning.
   //  property: the name of the property being extracted.
   //  trim: boolean, if the value should be trimmed (used by string type).
-  extractValue({
-      expectedType, object, objectName, property, trim
-    }) {
+  extractValue({expectedType, object, objectName, property, trim}) {
     const value = object[property];
     const isArray = Array.isArray(value);
     // We need to special-case "array", as it's not a JS primitive.
     const type = (isArray) ? 'array' : typeof value;
     if (type !== expectedType) {
       if (type !== 'undefined') {
         let msg = `Expected the ${objectName}'s ${property} `;
         msg += `member to be a ${expectedType}.`;
deleted file mode 100644
--- a/dom/manifest/WebManifest.jsm
+++ /dev/null
@@ -1,19 +0,0 @@
-/* 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 https://mozilla.org/MPL/2.0/. */
-/*exported EXPORTED_SYMBOLS, ManifestProcessor, ManifestObtainer*/
-/*globals Components */
-'use strict';
-const {
-  utils: Cu
-} = Components;
-
-this.EXPORTED_SYMBOLS = [
-  'ManifestObtainer',
-  'ManifestProcessor'
-];
-
-// Export public interfaces
-for (let symbl of EXPORTED_SYMBOLS) {
-  Cu.import(`resource://gre/modules/${symbl}.js`);
-}
--- a/dom/manifest/moz.build
+++ b/dom/manifest/moz.build
@@ -1,16 +1,16 @@
 # -*- 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/.
 
 EXTRA_JS_MODULES += [
-    'ImageObjectProcessor.js',
-    'ManifestObtainer.js',
-    'ManifestProcessor.js',
-    'ValueExtractor.js',
-    'WebManifest.jsm'
+    'ImageObjectProcessor.jsm',
+    'ManifestFinder.jsm',
+    'ManifestObtainer.jsm',
+    'ManifestProcessor.jsm',
+    'ValueExtractor.jsm',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/dom/manifest/test/browser.ini
+++ b/dom/manifest/test/browser.ini
@@ -1,2 +1,3 @@
 [DEFAULT]
-[browser_ManifestObtainer_obtain.js]
\ No newline at end of file
+[browser_hasManifestLink.js]
+[browser_ManifestObtainer_obtain.js]
--- a/dom/manifest/test/browser_ManifestObtainer_obtain.js
+++ b/dom/manifest/test/browser_ManifestObtainer_obtain.js
@@ -1,14 +1,14 @@
 //Used by JSHint:
 /*global Cu, BrowserTestUtils, add_task, SpecialPowers, gBrowser, Assert*/
 'use strict';
 const {
   ManifestObtainer
-} = Cu.import('resource://gre/modules/WebManifest.jsm', {});
+} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {});
 
 requestLongerTimeout(4); // e10s tests take time.
 const defaultURL =
   'http://example.org/tests/dom/manifest/test/resource.sjs';
 const remoteURL =
   'http://mochi.test:8888/tests/dom/manifest/test/resource.sjs';
 const tests = [
   // Fetch tests.
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/browser_hasManifestLink.js
@@ -0,0 +1,109 @@
+//Used by JSHint:
+/*global Cu, BrowserTestUtils, is, ok, add_task, gBrowser, ManifestFinder */
+'use strict';
+Cu.import('resource://gre/modules/ManifestFinder.jsm', this);  // jshint ignore:line
+
+const finder = new ManifestFinder();
+const defaultURL =
+  'http://example.org/tests/dom/manifest/test/resource.sjs';
+const tests = [{
+  expected: 'Document has a web manifest.',
+  get tabURL() {
+    let query = [
+      `body=<h1>${this.expected}</h1>`,
+      'Content-Type=text/html; charset=utf-8',
+    ];
+    const URL = `${defaultURL}?${query.join('&')}`;
+    return URL;
+  },
+  run(result) {
+    is(result, true, this.expected);
+  },
+  testData: `
+      <link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>
+      <link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"value"}'>
+      <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
+}, {
+  expected: 'Document does not have a web manifest.',
+  get tabURL() {
+    let query = [
+      `body=<h1>${this.expected}</h1>`,
+      'Content-Type=text/html; charset=utf-8',
+    ];
+    const URL = `${defaultURL}?${query.join('&')}`;
+    return URL;
+  },
+  run(result) {
+    is(result, false, this.expected);
+  },
+  testData: `
+      <link rel="amanifista" href='${defaultURL}?body={"name":"fail"}'>
+      <link rel="foo bar manifesto bar test" href='${defaultURL}?body={"name":"pass-1"}'>
+      <link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>`
+}, {
+  expected: 'Manifest link is has empty href.',
+  get tabURL() {
+    let query = [
+      `body=<h1>${this.expected}</h1>`,
+      'Content-Type=text/html; charset=utf-8',
+    ];
+    const URL = `${defaultURL}?${query.join('&')}`;
+    return URL;
+  },
+  run(result) {
+    is(result, false, this.expected);
+  },
+  testData: `
+  <link rel="manifest" href="">
+  <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
+}, {
+  expected: 'Manifest link is missing.',
+  get tabURL() {
+    let query = [
+      `body=<h1>${this.expected}</h1>`,
+      'Content-Type=text/html; charset=utf-8',
+    ];
+    const URL = `${defaultURL}?${query.join('&')}`;
+    return URL;
+  },
+  run(result) {
+    is(result, false, this.expected);
+  },
+  testData: `
+    <link rel="manifest">
+    <link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
+}];
+
+/**
+ * Test basic API error conditions
+ */
+add_task(function* () {
+  let expected = 'Invalid types should throw a TypeError.';
+  for (let invalidValue of [undefined, null, 1, {}, 'test']) {
+    try {
+      yield finder.hasManifestLink(invalidValue);
+      ok(false, expected);
+    } catch (e) {
+      is(e.name, 'TypeError', expected);
+    }
+  }
+});
+
+add_task(function* () {
+  for (let test of tests) {
+    let tabOptions = {
+      gBrowser: gBrowser,
+      url: test.tabURL,
+    };
+    yield BrowserTestUtils.withNewTab(
+      tabOptions,
+      browser => testHasManifest(browser, test)
+    );
+  }
+
+  function* testHasManifest(aBrowser, aTest) {
+    aBrowser.contentWindowAsCPOW.document.head.innerHTML = aTest.testData;
+    const result = yield finder.hasManifestLink(aBrowser);
+    aTest.run(result);
+  }
+});
--- a/dom/manifest/test/common.js
+++ b/dom/manifest/test/common.js
@@ -1,16 +1,16 @@
 /**
  * Common infrastructure for manifest tests.
  **/
-
+/*globals SpecialPowers, ManifestProcessor*/
 'use strict';
 const {
   ManifestProcessor
-} = SpecialPowers.Cu.import('resource://gre/modules/WebManifest.jsm');
+} = SpecialPowers.Cu.import('resource://gre/modules/ManifestProcessor.jsm');
 const processor = new ManifestProcessor();
 const manifestURL = new URL(document.location.origin + '/manifest.json');
 const docURL = document.location;
 const seperators = '\u2028\u2029\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000';
 const lineTerminators = '\u000D\u000A\u2028\u2029';
 const whiteSpace = `${seperators}${lineTerminators}`;
 const typeTests = [1, null, {},
   [], false
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -10,17 +10,16 @@
 #include "prdtoa.h"
 #include "AudioStream.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Snprintf.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
-#include "soundtouch/SoundTouch.h"
 #include "Latency.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #ifdef XP_MACOSX
 #include <sys/sysctl.h>
 #endif
 
 namespace mozilla {
@@ -125,16 +124,17 @@ private:
 AudioStream::AudioStream()
   : mMonitor("AudioStream")
   , mInRate(0)
   , mOutRate(0)
   , mChannels(0)
   , mOutChannels(0)
   , mWritten(0)
   , mAudioClock(this)
+  , mTimeStretcher(nullptr)
   , mLatencyRequest(HighLatency)
   , mReadPoint(0)
   , mDumpFile(nullptr)
   , mBytesPerFrame(0)
   , mState(INITIALIZED)
   , mNeedsStart(false)
   , mShouldDropFrames(false)
   , mPendingAudioInitTask(false)
@@ -147,16 +147,19 @@ AudioStream::AudioStream()
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
   MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
              "Should've called Shutdown() before deleting an AudioStream");
   if (mDumpFile) {
     fclose(mDumpFile);
   }
+  if (mTimeStretcher) {
+    soundtouch::destroySoundTouchObj(mTimeStretcher);
+  }
 }
 
 size_t
 AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = aMallocSizeOf(this);
 
   // Possibly add in the future:
@@ -169,17 +172,17 @@ AudioStream::SizeOfIncludingThis(MallocS
 
   return amount;
 }
 
 nsresult AudioStream::EnsureTimeStretcherInitializedUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mTimeStretcher) {
-    mTimeStretcher = new soundtouch::SoundTouch();
+    mTimeStretcher = soundtouch::createSoundTouchObj();
     mTimeStretcher->setSampleRate(mInRate);
     mTimeStretcher->setChannels(mOutChannels);
     mTimeStretcher->setPitch(1.0);
   }
   return NS_OK;
 }
 
 nsresult AudioStream::SetPlaybackRate(double aPlaybackRate)
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -10,20 +10,17 @@
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "Latency.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "CubebUtils.h"
-
-namespace soundtouch {
-class SoundTouch;
-}
+#include "soundtouch/SoundTouchFactory.h"
 
 namespace mozilla {
 
 struct CubebDestroyPolicy
 {
   void operator()(cubeb_stream* aStream) const {
     cubeb_stream_destroy(aStream);
   }
@@ -327,17 +324,17 @@ private:
   int mChannels;
   int mOutChannels;
 #if defined(__ANDROID__)
   dom::AudioChannel mAudioChannel;
 #endif
   // Number of frames written to the buffers.
   int64_t mWritten;
   AudioClock mAudioClock;
-  nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
+  soundtouch::SoundTouch* mTimeStretcher;
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
 
   // copy of Latency logger's starting time for offset calculations
   TimeStamp mStartTime;
   // Whether we are playing a low latency stream, or a normal stream.
   LatencyRequest mLatencyRequest;
   // Where in the current mInserts[0] block cubeb has read to
   int64_t mReadPoint;
--- a/dom/media/TimeUnits.h
+++ b/dom/media/TimeUnits.h
@@ -114,16 +114,24 @@ public:
   static TimeUnit FromNanoseconds(int64_t aValue) {
     return TimeUnit(aValue / 1000);
   }
 
   static TimeUnit FromInfinity() {
     return TimeUnit(INT64_MAX);
   }
 
+  static TimeUnit Invalid() {
+    TimeUnit ret;
+    ret.mValue = CheckedInt64(INT64_MAX);
+    // Force an overflow to render the CheckedInt invalid.
+    ret.mValue += 1;
+    return ret;
+  }
+
   int64_t ToMicroseconds() const {
     return mValue.value();
   }
 
   int64_t ToNanoseconds() const {
     return mValue.value() * 1000;
   }
 
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -24,16 +24,20 @@ namespace mozilla {
 using layers::PlanarYCbCrImage;
 
 // Converts from number of audio frames to microseconds, given the specified
 // audio rate.
 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
   return (CheckedInt64(aFrames) * USECS_PER_S) / aRate;
 }
 
+media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate) {
+  return (media::TimeUnit::FromMicroseconds(aFrames) * USECS_PER_S) / aRate;
+}
+
 // Converts from microseconds to number of audio frames, given the specified
 // audio rate.
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
   return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S;
 }
 
 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) {
   if (aSeconds * double(USECS_PER_S) > INT64_MAX) {
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -122,20 +122,21 @@ class MediaResource;
 // do file I/O, and can be used when we don't have detailed knowledge
 // of the byte->time mapping of a resource. aDurationUsecs is the duration
 // of the media in microseconds. Estimated buffered ranges are stored in
 // aOutBuffered. Ranges are 0-normalized, i.e. in the range of (0,duration].
 media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
                                                     int64_t aDurationUsecs);
 
 // Converts from number of audio frames (aFrames) to microseconds, given
-// the specified audio rate (aRate). Stores result in aOutUsecs. Returns true
-// if the operation succeeded, or false if there was an integer overflow
-// while calulating the conversion.
+// the specified audio rate (aRate).
 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
+// Converts from number of audio frames (aFrames) TimeUnit, given
+// the specified audio rate (aRate).
+media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate);
 
 // Converts from microseconds (aUsecs) to number of audio frames, given the
 // specified audio rate (aRate). Stores the result in aOutFrames. Returns
 // true if the operation succeeded, or false if there was an integer
 // overflow while calulating the conversion.
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
 
 // Converts milliseconds to seconds.
--- a/dom/media/compiledtest/moz.build
+++ b/dom/media/compiledtest/moz.build
@@ -9,8 +9,12 @@ GeckoCppUnitTests([
     'TestAudioMixer'
 ])
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '..',
 ]
+
+USE_LIBS += [
+    'lgpllibs',
+]
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -418,17 +418,17 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
       new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
 #endif // MOZ_EME
     // Add init data to info, will get sent from HTMLMediaElement::MetadataLoaded
     // (i.e., when transitioning from HAVE_NOTHING to HAVE_METADATA).
     mInfo.mCrypto.AddInitData(NS_LITERAL_STRING("cenc"), Move(initData));
   }
 
   // Get the duration, and report it to the decoder if we have it.
-  Microseconds duration;
+  mp4_demuxer::Microseconds duration;
   {
     MonitorAutoLock lock(mDemuxerMonitor);
     duration = mDemuxer->Duration();
   }
   if (duration != -1) {
     mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
   }
 
@@ -556,17 +556,17 @@ MP4Reader::GetDecoderData(TrackType aTra
   MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
              aTrack == TrackInfo::kVideoTrack);
   if (aTrack == TrackInfo::kAudioTrack) {
     return mAudio;
   }
   return mVideo;
 }
 
-Microseconds
+mp4_demuxer::Microseconds
 MP4Reader::GetNextKeyframeTime()
 {
   MonitorAutoLock mon(mDemuxerMonitor);
   return mVideo.mTrackDemuxer->GetNextKeyframeTime();
 }
 
 void
 MP4Reader::DisableHardwareAcceleration()
@@ -591,17 +591,17 @@ MP4Reader::DisableHardwareAcceleration()
 bool
 MP4Reader::ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
 {
   // The MP4Reader doesn't do normal skip-to-next-keyframe if the demuxer
   // has exposes where the next keyframe is. We can then instead skip only
   // if the time threshold (the current playback position) is after the next
   // keyframe in the stream. This means we'll only skip frames that we have
   // no hope of ever playing.
-  Microseconds nextKeyframe = -1;
+  mp4_demuxer::Microseconds nextKeyframe = -1;
   if (!sDemuxSkipToNextKeyframe ||
       (nextKeyframe = GetNextKeyframeTime()) == -1) {
     return aSkipToNextKeyframe;
   }
   return nextKeyframe < aTimeThreshold && nextKeyframe >= 0;
 }
 
 nsRefPtr<MediaDecoderReader::VideoDataPromise>
@@ -1085,17 +1085,17 @@ MP4Reader::GetBuffered()
   UpdateIndex();
   NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
 
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> ranges;
   nsresult rv = resource->GetCachedRanges(ranges);
 
   if (NS_SUCCEEDED(rv)) {
-    nsTArray<Interval<Microseconds>> timeRanges;
+    nsTArray<Interval<mp4_demuxer::Microseconds>> timeRanges;
     mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
     for (size_t i = 0; i < timeRanges.Length(); i++) {
       buffered += media::TimeInterval(
         media::TimeUnit::FromMicroseconds(timeRanges[i].start - StartTime()),
         media::TimeUnit::FromMicroseconds(timeRanges[i].end - StartTime()));
     }
   }
 
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -113,17 +113,17 @@ private:
   void Error(TrackType aTrack);
   void Flush(TrackType aTrack);
   void DrainComplete(TrackType aTrack);
   void UpdateIndex();
   bool IsSupportedAudioMimeType(const nsACString& aMimeType);
   bool IsSupportedVideoMimeType(const nsACString& aMimeType);
   virtual bool IsWaitingOnCDMResource() override;
 
-  Microseconds GetNextKeyframeTime();
+  mp4_demuxer::Microseconds GetNextKeyframeTime();
   bool ShouldSkip(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
 
   size_t SizeOfQueue(TrackType aTrack);
 
   nsRefPtr<MP4Stream> mStream;
   nsRefPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
   nsRefPtr<PlatformDecoderModule> mPlatform;
   mp4_demuxer::CryptoFile mCrypto;
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -22,17 +22,16 @@ class MediaRawData;
 namespace layers {
 class ImageContainer;
 }
 
 class MediaDataDecoder;
 class MediaDataDecoderCallback;
 class FlushableMediaTaskQueue;
 class CDMProxy;
-typedef int64_t Microseconds;
 
 // The PlatformDecoderModule interface is used by the MP4Reader to abstract
 // access to the H264 and Audio (AAC/MP3) decoders provided by various platforms.
 // It may be extended to support other codecs in future. Each platform (Windows,
 // MacOSX, Linux, B2G etc) must implement a PlatformDecoderModule to provide
 // access to its decoders in order to get decompressed H.264/AAC from the
 // MP4Reader.
 //
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -8,16 +8,17 @@
 #include "PlatformDecoderModule.h"
 #include "nsRect.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/CheckedInt.h"
 #include "VideoUtils.h"
 #include "ImageContainer.h"
 #include "MediaInfo.h"
 #include "MediaTaskQueue.h"
+#include "TimeUnits.h"
 
 namespace mozilla {
 
 // Decoder that uses a passed in object's Create function to create blank
 // MediaData objects.
 template<class BlankMediaDataCreator>
 class BlankMediaDataDecoder : public MediaDataDecoder {
 public:
@@ -46,19 +47,20 @@ public:
                 BlankMediaDataCreator* aCreator)
       : mSample(aSample)
       , mCreator(aCreator)
       , mCallback(aCallback)
     {
     }
     NS_IMETHOD Run() override
     {
-      nsRefPtr<MediaData> data = mCreator->Create(mSample->mTime,
-                                                  mSample->mDuration,
-                                                  mSample->mOffset);
+      nsRefPtr<MediaData> data =
+        mCreator->Create(media::TimeUnit::FromMicroseconds(mSample->mTime),
+                         media::TimeUnit::FromMicroseconds(mSample->mDuration),
+                         mSample->mOffset);
       mCallback->Output(data);
       return NS_OK;
     }
   private:
     nsRefPtr<MediaRawData> mSample;
     BlankMediaDataCreator* mCreator;
     MediaDataDecoderCallback* mCallback;
   };
@@ -98,17 +100,17 @@ public:
     , mFrameHeight(aFrameHeight)
     , mImageContainer(aImageContainer)
   {
     mInfo.mDisplay = nsIntSize(mFrameWidth, mFrameHeight);
     mPicture = gfx::IntRect(0, 0, mFrameWidth, mFrameHeight);
   }
 
   already_AddRefed<MediaData>
-  Create(Microseconds aDTS, Microseconds aDuration, int64_t aOffsetInStream)
+  Create(const media::TimeUnit& aDTS, const media::TimeUnit& aDuration, int64_t aOffsetInStream)
   {
     // Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane,
     // with a U and V plane that are half the size of the Y plane, i.e 8 bit,
     // 2x2 subsampled. Have the data pointers of each frame point to the
     // first plane, they'll always be zero'd memory anyway.
     nsAutoArrayPtr<uint8_t> frame(new uint8_t[mFrameWidth * mFrameHeight]);
     memset(frame, 0, mFrameWidth * mFrameHeight);
     VideoData::YCbCrBuffer buffer;
@@ -136,21 +138,21 @@ public:
     buffer.mPlanes[2].mWidth = mFrameWidth / 2;
     buffer.mPlanes[2].mOffset = 0;
     buffer.mPlanes[2].mSkip = 0;
 
     return VideoData::Create(mInfo,
                              mImageContainer,
                              nullptr,
                              aOffsetInStream,
-                             aDTS,
-                             aDuration,
+                             aDTS.ToMicroseconds(),
+                             aDuration.ToMicroseconds(),
                              buffer,
                              true,
-                             aDTS,
+                             aDTS.ToMicroseconds(),
                              mPicture);
   }
 private:
   VideoInfo mInfo;
   gfx::IntRect mPicture;
   uint32_t mFrameWidth;
   uint32_t mFrameHeight;
   RefPtr<layers::ImageContainer> mImageContainer;
@@ -159,23 +161,24 @@ private:
 
 class BlankAudioDataCreator {
 public:
   BlankAudioDataCreator(uint32_t aChannelCount, uint32_t aSampleRate)
     : mFrameSum(0), mChannelCount(aChannelCount), mSampleRate(aSampleRate)
   {
   }
 
-  MediaData* Create(Microseconds aDTS,
-                    Microseconds aDuration,
+  MediaData* Create(const media::TimeUnit& aDTS,
+                    const media::TimeUnit& aDuration,
                     int64_t aOffsetInStream)
   {
     // Convert duration to frames. We add 1 to duration to account for
     // rounding errors, so we get a consistent tone.
-    CheckedInt64 frames = UsecsToFrames(aDuration+1, mSampleRate);
+    CheckedInt64 frames =
+      UsecsToFrames(aDuration.ToMicroseconds()+1, mSampleRate);
     if (!frames.isValid() ||
         !mChannelCount ||
         !mSampleRate ||
         frames.value() > (UINT32_MAX / mChannelCount)) {
       return nullptr;
     }
     AudioDataValue* samples = new AudioDataValue[frames.value() * mChannelCount];
     // Fill the sound buffer with an A4 tone.
@@ -184,18 +187,18 @@ public:
     for (int i = 0; i < frames.value(); i++) {
       float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate);
       for (unsigned c = 0; c < mChannelCount; c++) {
         samples[i * mChannelCount + c] = AudioDataValue(f);
       }
       mFrameSum++;
     }
     return new AudioData(aOffsetInStream,
-                         aDTS,
-                         aDuration,
+                         aDTS.ToMicroseconds(),
+                         aDuration.ToMicroseconds(),
                          uint32_t(frames.value()),
                          samples,
                          mChannelCount,
                          mSampleRate);
   }
 
 private:
   int64_t mFrameSum;
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -100,17 +100,18 @@ public:
     EGLImage eglImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(), eglContext,
                                                  LOCAL_EGL_GL_TEXTURE_2D_KHR,
                                                  (EGLClientBuffer)tex, attribs);
     mGLContext->fDeleteTextures(1, &tex);
 
     return eglImage;
   }
 
-  virtual nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat, Microseconds aDuration) override {
+  virtual nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
+                              const media::TimeUnit& aDuration) override {
     if (!EnsureGLContext()) {
       return NS_ERROR_FAILURE;
     }
 
     nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
     layers::SurfaceTextureImage::Data data;
     data.mSurfTex = mSurfaceTexture.get();
     data.mSize = mConfig.mDisplay;
@@ -163,17 +164,17 @@ public:
     int64_t presentationTimeUs;
     NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv);
 
     nsRefPtr<VideoData> v =
       VideoData::CreateFromImage(mConfig,
                                  mImageContainer,
                                  offset,
                                  presentationTimeUs,
-                                 aDuration,
+                                 aDuration.ToMicroseconds(),
                                  img,
                                  isSync,
                                  presentationTimeUs,
                                  gfx::IntRect(0, 0,
                                               mConfig.mDisplay.width,
                                               mConfig.mDisplay.height));
     ENVOKE_CALLBACK(Output, v);
     return NS_OK;
@@ -208,17 +209,19 @@ public:
 
     if (!buffer && aConfig.mCodecSpecificConfig->Length() >= 2) {
       buffer = jni::Object::LocalRef::Adopt(env, env->NewDirectByteBuffer(aConfig.mCodecSpecificConfig->Elements(),
                                                                           aConfig.mCodecSpecificConfig->Length()));
       NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"), buffer));
     }
   }
 
-  nsresult Output(BufferInfo::Param aInfo, void* aBuffer, MediaFormat::Param aFormat, Microseconds aDuration) {
+  nsresult Output(BufferInfo::Param aInfo, void* aBuffer,
+                  MediaFormat::Param aFormat,
+                  const media::TimeUnit& aDuration) {
     // The output on Android is always 16-bit signed
 
     nsresult rv;
     int32_t numChannels;
     NS_ENSURE_SUCCESS(rv =
         aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &numChannels), rv);
 
     int32_t sampleRate;
@@ -234,17 +237,17 @@ public:
 
     int32_t offset;
     NS_ENSURE_SUCCESS(rv = aInfo->Offset(&offset), rv);
 
     int64_t presentationTimeUs;
     NS_ENSURE_SUCCESS(rv = aInfo->PresentationTimeUs(&presentationTimeUs), rv);
 
     nsRefPtr<AudioData> data = new AudioData(offset, presentationTimeUs,
-                                             aDuration,
+                                             aDuration.ToMicroseconds(),
                                              numFrames,
                                              audio,
                                              numChannels,
                                              sampleRate);
     ENVOKE_CALLBACK(Output, data);
     return NS_OK;
   }
 };
@@ -480,17 +483,17 @@ void MediaCodecDataDecoder::DecoderLoop(
         }
 
         PodCopy((uint8_t*)directBuffer, sample->mData, sample->mSize);
 
         res = mDecoder->QueueInputBuffer(inputIndex, 0, sample->mSize,
                                          sample->mTime, 0);
         HANDLE_DECODER_ERROR();
 
-        mDurations.push(sample->mDuration);
+        mDurations.push(media::TimeUnit::FromMicroseconds(sample->mDuration));
         sample = nullptr;
         outputDone = false;
       }
     }
 
     if (!outputDone) {
       BufferInfo::LocalRef bufferInfo;
       res = BufferInfo::New(&bufferInfo);
@@ -538,17 +541,17 @@ void MediaCodecDataDecoder::DecoderLoop(
           outputDone = true;
 
           // We only queue empty EOF frames, so we're done for now
           continue;
         }
 
         MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued");
 
-        Microseconds duration = 0;
+        media::TimeUnit duration;
         if (!mDurations.empty()) {
           duration = mDurations.front();
           mDurations.pop();
         }
 
         auto buffer = jni::Object::LocalRef::Adopt(
             frame.GetEnv()->GetObjectArrayElement(mOutputBuffers.Get(), outputStatus));
         if (buffer) {
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -4,16 +4,17 @@
 
 #ifndef AndroidDecoderModule_h_
 #define AndroidDecoderModule_h_
 
 #include "PlatformDecoderModule.h"
 #include "AndroidSurfaceTexture.h"
 
 #include "MediaCodec.h"
+#include "TimeUnits.h"
 #include "mozilla/Monitor.h"
 
 #include <queue>
 
 namespace mozilla {
 
 typedef std::queue<nsRefPtr<MediaRawData>> SampleQueue;
 
@@ -76,22 +77,23 @@ protected:
 
   // Only these members are protected by mMonitor.
   Monitor mMonitor;
   bool mFlushing;
   bool mDraining;
   bool mStopping;
 
   SampleQueue mQueue;
-  std::queue<Microseconds> mDurations;
+  // Durations are stored in microseconds.
+  std::queue<media::TimeUnit> mDurations;
 
   virtual nsresult InitDecoder(widget::sdk::Surface::Param aSurface);
 
-  virtual nsresult Output(widget::sdk::BufferInfo::Param aInfo, void* aBuffer, widget::sdk::MediaFormat::Param aFormat, Microseconds aDuration) { return NS_OK; }
-  virtual nsresult PostOutput(widget::sdk::BufferInfo::Param aInfo, widget::sdk::MediaFormat::Param aFormat, Microseconds aDuration) { return NS_OK; }
+  virtual nsresult Output(widget::sdk::BufferInfo::Param aInfo, void* aBuffer, widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) { return NS_OK; }
+  virtual nsresult PostOutput(widget::sdk::BufferInfo::Param aInfo, widget::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration) { return NS_OK; }
   virtual void Cleanup() {};
 
   nsresult ResetInputBuffers();
   nsresult ResetOutputBuffers();
 
   void DecoderLoop();
   nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer);
   virtual void ClearQueue();
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -256,33 +256,33 @@ AppleATDecoder::DecodeSample(MediaRawDat
   } while (true);
 
   if (outputData.IsEmpty()) {
     return NS_OK;
   }
 
   size_t numFrames = outputData.Length() / channels;
   int rate = mOutputFormat.mSampleRate;
-  CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
-  if (!duration.isValid()) {
+  media::TimeUnit duration = FramesToTimeUnit(numFrames, rate);
+  if (!duration.IsValid()) {
     NS_WARNING("Invalid count of accumulated audio samples");
     return NS_ERROR_FAILURE;
   }
 
 #ifdef LOG_SAMPLE_DECODE
   LOG("pushed audio at time %lfs; duration %lfs\n",
       (double)aSample->mTime / USECS_PER_S,
-      (double)duration.value() / USECS_PER_S);
+      duration.ToSeconds());
 #endif
 
   nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
   PodCopy(data.get(), &outputData[0], outputData.Length());
   nsRefPtr<AudioData> audio = new AudioData(aSample->mOffset,
                                             aSample->mTime,
-                                            duration.value(),
+                                            duration.ToMicroseconds(),
                                             numFrames,
                                             data.forget(),
                                             channels,
                                             rate);
   mCallback->Output(audio);
   return NS_OK;
 }
 
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -190,34 +190,35 @@ PlatformCallback(void* decompressionOutp
     (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_DTS"));
   AutoCFRelease<CFNumberRef> durref =
     (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_DURATION"));
   AutoCFRelease<CFNumberRef> boref =
     (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_OFFSET"));
   AutoCFRelease<CFNumberRef> kfref =
     (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_KEYFRAME"));
 
-  Microseconds dts;
-  Microseconds pts;
-  Microseconds duration;
+  int64_t dts;
+  int64_t pts;
+  int64_t duration;
   int64_t byte_offset;
   char is_sync_point;
 
   CFNumberGetValue(ptsref, kCFNumberSInt64Type, &pts);
   CFNumberGetValue(dtsref, kCFNumberSInt64Type, &dts);
   CFNumberGetValue(durref, kCFNumberSInt64Type, &duration);
   CFNumberGetValue(boref, kCFNumberSInt64Type, &byte_offset);
   CFNumberGetValue(kfref, kCFNumberSInt8Type, &is_sync_point);
 
   nsAutoPtr<AppleVDADecoder::AppleFrameRef> frameRef(
-    new AppleVDADecoder::AppleFrameRef(dts,
-    pts,
-    duration,
-    byte_offset,
-    is_sync_point == 1));
+    new AppleVDADecoder::AppleFrameRef(
+      media::TimeUnit::FromMicroseconds(dts),
+      media::TimeUnit::FromMicroseconds(pts),
+      media::TimeUnit::FromMicroseconds(duration),
+      byte_offset,
+      is_sync_point == 1));
 
   // Forward the data back to an object method which can access
   // the correct MP4Reader callback.
   decoder->OutputFrame(image, frameRef);
 }
 
 AppleVDADecoder::AppleFrameRef*
 AppleVDADecoder::CreateAppleFrameRef(const MediaRawData* aSample)
@@ -247,19 +248,19 @@ nsresult
 AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
                              nsAutoPtr<AppleVDADecoder::AppleFrameRef> aFrameRef)
 {
   IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
   MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
 
   LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
     aFrameRef->byte_offset,
-    aFrameRef->decode_timestamp,
-    aFrameRef->composition_timestamp,
-    aFrameRef->duration,
+    aFrameRef->decode_timestamp.ToMicroseconds(),
+    aFrameRef->composition_timestamp.ToMicroseconds(),
+    aFrameRef->duration.ToMicroseconds(),
     aFrameRef->is_sync_point ? " keyframe" : ""
   );
 
   nsRefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
   // Bounds.
   VideoInfo info;
   info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
   gfx::IntRect visible = gfx::IntRect(0,
@@ -272,20 +273,21 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
   layers::MacIOSurfaceImage* videoImage =
     static_cast<layers::MacIOSurfaceImage*>(image.get());
   videoImage->SetSurface(macSurface);
 
   nsRefPtr<VideoData> data;
   data = VideoData::CreateFromImage(info,
                                     mImageContainer,
                                     aFrameRef->byte_offset,
-                                    aFrameRef->composition_timestamp,
-                                    aFrameRef->duration, image.forget(),
+                                    aFrameRef->composition_timestamp.ToMicroseconds(),
+                                    aFrameRef->duration.ToMicroseconds(),
+                                    image.forget(),
                                     aFrameRef->is_sync_point,
-                                    aFrameRef->decode_timestamp,
+                                    aFrameRef->decode_timestamp.ToMicroseconds(),
                                     visible);
 
   if (!data) {
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -8,49 +8,50 @@
 #define mozilla_AppleVDADecoder_h
 
 #include "PlatformDecoderModule.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "MP4Reader.h"
 #include "MP4Decoder.h"
 #include "nsIThread.h"
 #include "ReorderQueue.h"
+#include "TimeUnits.h"
 
 #include "VideoDecodeAcceleration/VDADecoder.h"
 
 namespace mozilla {
 
 class FlushableMediaTaskQueue;
 class MediaDataDecoderCallback;
 namespace layers {
   class ImageContainer;
 }
 
 class AppleVDADecoder : public MediaDataDecoder {
 public:
   class AppleFrameRef {
   public:
-    Microseconds decode_timestamp;
-    Microseconds composition_timestamp;
-    Microseconds duration;
+    media::TimeUnit decode_timestamp;
+    media::TimeUnit composition_timestamp;
+    media::TimeUnit duration;
     int64_t byte_offset;
     bool is_sync_point;
 
     explicit AppleFrameRef(const MediaRawData& aSample)
-      : decode_timestamp(aSample.mTimecode)
-      , composition_timestamp(aSample.mTime)
-      , duration(aSample.mDuration)
+      : decode_timestamp(media::TimeUnit::FromMicroseconds(aSample.mTimecode))
+      , composition_timestamp(media::TimeUnit::FromMicroseconds(aSample.mTime))
+      , duration(media::TimeUnit::FromMicroseconds(aSample.mDuration))
       , byte_offset(aSample.mOffset)
       , is_sync_point(aSample.mKeyframe)
     {
     }
 
-    AppleFrameRef(Microseconds aDts,
-                  Microseconds aPts,
-                  Microseconds aDuration,
+    AppleFrameRef(const media::TimeUnit& aDts,
+                  const media::TimeUnit& aPts,
+                  const media::TimeUnit& aDuration,
                   int64_t aByte_offset,
                   bool aIs_sync_point)
       : decode_timestamp(aDts)
       , composition_timestamp(aPts)
       , duration(aDuration)
       , byte_offset(aByte_offset)
       , is_sync_point(aIs_sync_point)
     {
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -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/. */
 
 #include "MediaTaskQueue.h"
 #include "FFmpegRuntimeLinker.h"
 
 #include "FFmpegAudioDecoder.h"
+#include "TimeUnits.h"
 
 #define MAX_CHANNELS 16
 
 namespace mozilla
 {
 
 FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
   FlushableMediaTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
@@ -92,17 +93,17 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePac
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
     mCallback->Error();
     return;
   }
 
   int64_t samplePosition = aSample->mOffset;
-  Microseconds pts = aSample->mTime;
+  media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aSample->mTime);
 
   while (packet.size > 0) {
     int decoded;
     int bytesConsumed =
       avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet);
 
     if (bytesConsumed < 0) {
       NS_WARNING("FFmpeg audio decoder error.");
@@ -112,33 +113,38 @@ FFmpegAudioDecoder<LIBAV_VER>::DecodePac
 
     if (decoded) {
       uint32_t numChannels = mCodecContext->channels;
       uint32_t samplingRate = mCodecContext->sample_rate;
 
       nsAutoArrayPtr<AudioDataValue> audio(
         CopyAndPackAudio(mFrame, numChannels, mFrame->nb_samples));
 
-      CheckedInt<Microseconds> duration =
-        FramesToUsecs(mFrame->nb_samples, samplingRate);
-      if (!duration.isValid()) {
+      media::TimeUnit duration =
+        FramesToTimeUnit(mFrame->nb_samples, samplingRate);
+      if (!duration.IsValid()) {
         NS_WARNING("Invalid count of accumulated audio samples");
         mCallback->Error();
         return;
       }
 
       nsRefPtr<AudioData> data = new AudioData(samplePosition,
-                                               pts,
-                                               duration.value(),
+                                               pts.ToMicroseconds(),
+                                               duration.ToMicroseconds(),
                                                mFrame->nb_samples,
                                                audio.forget(),
                                                numChannels,
                                                samplingRate);
       mCallback->Output(data);
-      pts += duration.value();
+      pts += duration;
+      if (!pts.IsValid()) {
+        NS_WARNING("Invalid count of accumulated audio samples");
+        mCallback->Error();
+        return;
+      }
     }
     packet.data += bytesConsumed;
     packet.size -= bytesConsumed;
     samplePosition += bytesConsumed;
   }
 
   if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMFAudioMFTManager.h"
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
+#include "TimeUnits.h"
 
 #include "mozilla/Logging.h"
 
 PRLogModuleInfo* GetDemuxerLog();
 #define LOG(...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
@@ -285,35 +286,36 @@ WMFAudioMFTManager::Output(int64_t aStre
 
   int16_t* pcm = (int16_t*)data;
   for (int32_t i = 0; i < numSamples; ++i) {
     audioData[i] = AudioSampleToFloat(pcm[i]);
   }
 
   buffer->Unlock();
 
-  CheckedInt64 timestamp = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate);
-  NS_ENSURE_TRUE(timestamp.isValid(), E_FAIL);
+  media::TimeUnit timestamp =
+    FramesToTimeUnit(mAudioFrameOffset + mAudioFrameSum, mAudioRate);
+  NS_ENSURE_TRUE(timestamp.IsValid(), E_FAIL);
 
   mAudioFrameSum += numFrames;
 
-  CheckedInt64 duration = FramesToUsecs(numFrames, mAudioRate);
-  NS_ENSURE_TRUE(duration.isValid(), E_FAIL);
+  media::TimeUnit duration = FramesToTimeUnit(numFrames, mAudioRate);
+  NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
 
   aOutData = new AudioData(aStreamOffset,
-                           timestamp.value(),
-                           duration.value(),
+                           timestamp.ToMicroseconds(),
+                           duration.ToMicroseconds(),
                            numFrames,
                            audioData.forget(),
                            mAudioChannels,
                            mAudioRate);
 
   #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
-      timestamp, duration, currentLength);
+      timestamp.ToMicroseconds(), duration.ToMicroseconds(), currentLength);
   #endif
 
   return S_OK;
 }
 
 void
 WMFAudioMFTManager::Shutdown()
 {
--- a/dom/media/platforms/wmf/WMFUtils.cpp
+++ b/dom/media/platforms/wmf/WMFUtils.cpp
@@ -65,33 +65,33 @@ GetDefaultStride(IMFMediaType *aType, ui
 }
 
 int32_t
 MFOffsetToInt32(const MFOffset& aOffset)
 {
   return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
 }
 
-int64_t
+media::TimeUnit
 GetSampleDuration(IMFSample* aSample)
 {
-  NS_ENSURE_TRUE(aSample, -1);
+  NS_ENSURE_TRUE(aSample, media::TimeUnit::Invalid());
   int64_t duration = 0;
   aSample->GetSampleDuration(&duration);
-  return HNsToUsecs(duration);
+  return media::TimeUnit::FromMicroseconds(HNsToUsecs(duration));
 }
 
-int64_t
+media::TimeUnit
 GetSampleTime(IMFSample* aSample)
 {
-  NS_ENSURE_TRUE(aSample, -1);
+  NS_ENSURE_TRUE(aSample, media::TimeUnit::Invalid());
   LONGLONG timestampHns = 0;
   HRESULT hr = aSample->GetSampleTime(&timestampHns);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), -1);
-  return HNsToUsecs(timestampHns);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), media::TimeUnit::Invalid());
+  return media::TimeUnit::FromMicroseconds(HNsToUsecs(timestampHns));
 }
 
 // Gets the sub-region of the video frame that should be displayed.
 // See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
 HRESULT
 GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion)
 {
   // Determine if "pan and scan" is enabled for this media. If it is, we
--- a/dom/media/platforms/wmf/WMFUtils.h
+++ b/dom/media/platforms/wmf/WMFUtils.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WMFUtils_h
 #define WMFUtils_h
 
 #include "WMF.h"
 #include "nsString.h"
 #include "nsRect.h"
+#include "TimeUnits.h"
 #include "VideoUtils.h"
 
 // Various utilities shared by WMF backend files.
 
 namespace mozilla {
 
 // Converts from microseconds to hundreds of nanoseconds.
 // We use microseconds for our timestamps, whereas WMF uses
@@ -41,24 +42,24 @@ GetDefaultStride(IMFMediaType *aType, ui
 int32_t
 MFOffsetToInt32(const MFOffset& aOffset);
 
 // Gets the sub-region of the video frame that should be displayed.
 // See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
 HRESULT
 GetPictureRegion(IMFMediaType* aMediaType, nsIntRect& aOutPictureRegion);
 
-// Returns the duration of a IMFSample in microseconds.
-// Returns -1 on failure.
-int64_t
+// Returns the duration of a IMFSample in TimeUnit.
+// Returns media::TimeUnit::Invalid() on failure.
+media::TimeUnit
 GetSampleDuration(IMFSample* aSample);
 
-// Returns the presentation time of a IMFSample in microseconds.
-// Returns -1 on failure.
-int64_t
+// Returns the presentation time of a IMFSample in TimeUnit.
+// Returns media::TimeUnit::Invalid() on failure.
+media::TimeUnit
 GetSampleTime(IMFSample* aSample);
 
 inline bool
 IsFlagSet(DWORD flags, DWORD pattern) {
   return (flags & pattern) == pattern;
 }
 
 } // namespace mozilla
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -405,34 +405,36 @@ WMFVideoMFTManager::CreateBasicVideoFram
   // V plane (Cr)
   b.mPlanes[2].mData = data + y_size;
   b.mPlanes[2].mStride = halfStride;
   b.mPlanes[2].mHeight = halfHeight;
   b.mPlanes[2].mWidth = halfWidth;
   b.mPlanes[2].mOffset = 0;
   b.mPlanes[2].mSkip = 0;
 
-  Microseconds pts = GetSampleTime(aSample);
-  Microseconds duration = GetSampleDuration(aSample);
+  media::TimeUnit pts = GetSampleTime(aSample);
+  NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
+  media::TimeUnit duration = GetSampleDuration(aSample);
+  NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
 
   nsRefPtr<layers::PlanarYCbCrImage> image =
     new IMFYCbCrImage(buffer, twoDBuffer);
 
   VideoData::SetVideoDataToImage(image,
                                  mVideoInfo,
                                  b,
                                  mPictureRegion,
                                  false);
 
   nsRefPtr<VideoData> v =
     VideoData::CreateFromImage(mVideoInfo,
                                mImageContainer,
                                aStreamOffset,
-                               std::max(0LL, pts),
-                               duration,
+                               pts.ToMicroseconds(),
+                               duration.ToMicroseconds(),
                                image.forget(),
                                false,
                                -1,
                                mPictureRegion);
 
   v.forget(aOutVideoData);
   return S_OK;
 }
@@ -453,23 +455,25 @@ WMFVideoMFTManager::CreateD3DVideoFrame(
   nsRefPtr<Image> image;
   hr = mDXVA2Manager->CopyToImage(aSample,
                                   mPictureRegion,
                                   mImageContainer,
                                   getter_AddRefs(image));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   NS_ENSURE_TRUE(image, E_FAIL);
 
-  Microseconds pts = GetSampleTime(aSample);
-  Microseconds duration = GetSampleDuration(aSample);
+  media::TimeUnit pts = GetSampleTime(aSample);
+  NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
+  media::TimeUnit duration = GetSampleDuration(aSample);
+  NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
   nsRefPtr<VideoData> v = VideoData::CreateFromImage(mVideoInfo,
                                                      mImageContainer,
                                                      aStreamOffset,
-                                                     pts,
-                                                     duration,
+                                                     pts.ToMicroseconds(),
+                                                     duration.ToMicroseconds(),
                                                      image.forget(),
                                                      false,
                                                      -1,
                                                      mPictureRegion);
 
   NS_ENSURE_TRUE(v, E_FAIL);
   v.forget(aOutVideoData);
 
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -199,58 +199,63 @@ PluginPRLibrary::NPP_New(NPMIMEType plug
 
   MAIN_THREAD_JNI_REF_GUARD;
   *error = mNPP_New(pluginType, instance, mode, argc, argn, argv, saved);
   return NS_OK;
 }
 
 nsresult
 PluginPRLibrary::NPP_ClearSiteData(const char* site, uint64_t flags,
-                                   uint64_t maxAge)
+                                   uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback)
 {
   if (!mNPP_ClearSiteData) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   MAIN_THREAD_JNI_REF_GUARD;
   NPError result = mNPP_ClearSiteData(site, flags, maxAge);
 
+  nsresult rv;
   switch (result) {
   case NPERR_NO_ERROR:
-    return NS_OK;
+    rv = NS_OK;
+    break;
   case NPERR_TIME_RANGE_NOT_SUPPORTED:
-    return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
+    rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
+    break;
   case NPERR_MALFORMED_SITE:
-    return NS_ERROR_INVALID_ARG;
+    rv = NS_ERROR_INVALID_ARG;
+    break;
   default:
-    return NS_ERROR_FAILURE;
+    rv = NS_ERROR_FAILURE;
   }
+  callback->Callback(rv);
+  return NS_OK;
 }
 
 nsresult
-PluginPRLibrary::NPP_GetSitesWithData(InfallibleTArray<nsCString>& result)
+PluginPRLibrary::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
 {
   if (!mNPP_GetSitesWithData) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  result.Clear();
-
   MAIN_THREAD_JNI_REF_GUARD;
   char** sites = mNPP_GetSitesWithData();
   if (!sites) {
     return NS_OK;
   }
-
+  InfallibleTArray<nsCString> result;
   char** iterator = sites;
   while (*iterator) {
     result.AppendElement(*iterator);
     free(*iterator);
     ++iterator;
   }
+  callback->SitesWithData(result);
   free(sites);
 
   return NS_OK;
 }
 
 nsresult
 PluginPRLibrary::AsyncSetWindow(NPP instance, NPWindow* window)
 {
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -100,18 +100,18 @@ public:
 #endif
 
     virtual nsresult NPP_New(NPMIMEType aPluginType, NPP aInstance,
                              uint16_t aMode, int16_t aArgc, char* aArgn[],
                              char* aArgv[], NPSavedData* aSaved,
                              NPError* aError) override;
 
     virtual nsresult NPP_ClearSiteData(const char* aSite, uint64_t aFlags,
-                                       uint64_t aMaxAge) override;
-    virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) override;
+                                       uint64_t aMaxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) override;
+    virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
 
     virtual nsresult AsyncSetWindow(NPP aInstance, NPWindow* aWindow) override;
     virtual nsresult GetImageContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
     virtual nsresult GetImageSize(NPP aInstance, nsIntSize* aSize) override;
     virtual bool IsOOP() override { return false; }
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP aInstance, bool* aDrawing) override;
     virtual nsresult ContentsScaleFactorChanged(NPP aInstance, double aContentsScaleFactor) override;
--- a/dom/plugins/base/nsIPluginHost.idl
+++ b/dom/plugins/base/nsIPluginHost.idl
@@ -22,17 +22,26 @@ interface nsIPluginPlayPreviewInfo : nsI
 
   /**
    * Checks if pageURI and objectURI matches once of the entries in
    * the whitelist. If whitelist is empty, returns true. 
    */
   boolean checkWhitelist(in AUTF8String pageURI, in AUTF8String objectURI);
 };
 
-[scriptable, uuid(d7d5b2e0-105b-4c9d-8558-b6b31f28b7df)]
+[scriptable, function, uuid(9c311778-7c2c-4ad8-b439-b8a2786a20dd)]
+interface nsIClearSiteDataCallback : nsISupports
+{
+    /**
+     * callback with the result from a call to clearSiteData
+     */
+    void callback(in nsresult rv);
+};
+
+[scriptable, uuid(a884e736-5396-4400-bc82-9a23d871d12c)]
 interface nsIPluginHost : nsISupports
 {
   /**
    * Causes the plugins directory to be searched again for new plugin 
    * libraries.
    */
   void reloadPlugins();
 
@@ -72,17 +81,18 @@ interface nsIPluginHost : nsISupports
    *                cleared.
    *
    * @throws NS_ERROR_INVALID_ARG if the domain argument is malformed.
    * @throws NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED if maxAge is a value other
    *         than -1 and the plugin does not support clearing by timerange in
    *         general or for that particular site and/or flag combination.
    */
   void clearSiteData(in nsIPluginTag plugin, in AUTF8String domain,
-                     in uint64_t flags, in int64_t maxAge);
+                     in uint64_t flags, in int64_t maxAge,
+                     in nsIClearSiteDataCallback callback);
 
   /*
    * Determine if a plugin has stored data for a given site.
    *
    * @param plugin: the plugin to query, such as one returned by
    *                nsIPluginHost.getPluginTags.
    * @param domain: the domain to test. If this argument is null, test if data
    *                is stored for any site. The base domain for the given domain
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1964,17 +1964,17 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
         e.removeFront();
       }
     }
 
     sJSObjWrappersAccessible = true;
   }
 
   if (sNPObjWrappers) {
-    for (auto i = sNPObjWrappers->RemovingIter(); !i.Done(); i.Next()) {
+    for (auto i = sNPObjWrappers->Iter(); !i.Done(); i.Next()) {
       auto entry = static_cast<NPObjWrapperHashEntry*>(i.Get());
 
       if (entry->mNpp == npp) {
         // HACK: temporarily hide the table we're enumerating so that
         // invalidate() and deallocate() don't touch it.
         PLDHashTable *tmp = sNPObjWrappers;
         sNPObjWrappers = nullptr;
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1529,20 +1529,87 @@ nsPluginHost::GetPlayPreviewInfo(const n
       NS_ADDREF(*aResult);
       return NS_OK;
     }
   }
   *aResult = nullptr;
   return NS_ERROR_NOT_AVAILABLE;
 }
 
+#define ClearDataFromSitesClosure_CID {0x9fb21761, 0x2403, 0x41ad, {0x9e, 0xfd, 0x36, 0x7e, 0xc4, 0x4f, 0xa4, 0x5e}}
+
+
+// Class to hold all the data we need need for IterateMatchesAndClear and ClearDataFromSites
+class ClearDataFromSitesClosure : public nsIClearSiteDataCallback, public nsIGetSitesWithDataCallback {
+public:
+  ClearDataFromSitesClosure(nsIPluginTag* plugin, const nsACString& domain, uint64_t flags,
+                            int64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback,
+                            nsPluginHost* host) :
+    domain(domain), callback(callback), tag(plugin), flags(flags), maxAge(maxAge), host(host) {}
+  NS_DECL_ISUPPORTS
+
+  // Callback from NPP_ClearSiteData, continue to iterate the matches and clear
+  NS_IMETHOD Callback(nsresult rv) override {
+    if (NS_FAILED(rv)) {
+      callback->Callback(rv);
+      return NS_OK;
+    }
+    if (!matches.Length()) {
+      callback->Callback(NS_OK);
+      return NS_OK;
+    }
+
+    const nsCString match(matches[0]);
+    matches.RemoveElement(match);
+    PluginLibrary* library = static_cast<nsPluginTag*>(tag)->mPlugin->GetLibrary();
+    rv = library->NPP_ClearSiteData(match.get(), flags, maxAge, this);
+    if (NS_FAILED(rv)) {
+      callback->Callback(rv);
+      return NS_OK;
+    }
+    return NS_OK;
+  }
+
+  // Callback from NPP_GetSitesWithData, kick the iteration off to clear the data
+  NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override
+  {
+    // Enumerate the sites and build a list of matches.
+    nsresult rv = host->EnumerateSiteData(domain, sites, matches, false);
+    Callback(rv);
+    return NS_OK;
+  }
+
+  nsCString domain;
+  nsCOMPtr<nsIClearSiteDataCallback> callback;
+  InfallibleTArray<nsCString> matches;
+  nsIPluginTag* tag;
+  uint64_t flags;
+  int64_t maxAge;
+  nsPluginHost* host;
+  NS_DECLARE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure_CID)
+  private:
+  virtual ~ClearDataFromSitesClosure() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(ClearDataFromSitesClosure, ClearDataFromSitesClosure_CID)
+
+NS_IMPL_ADDREF(ClearDataFromSitesClosure)
+NS_IMPL_RELEASE(ClearDataFromSitesClosure)
+
+NS_INTERFACE_MAP_BEGIN(ClearDataFromSitesClosure)
+  NS_INTERFACE_MAP_ENTRY(nsIClearSiteDataCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIGetSitesWithDataCallback)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearSiteDataCallback)
+NS_INTERFACE_MAP_END
+
 NS_IMETHODIMP
 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
-                            uint64_t flags, int64_t maxAge)
+                            uint64_t flags, int64_t maxAge, nsIClearSiteDataCallback* callbackFunc)
 {
+  nsCOMPtr<nsIClearSiteDataCallback> callback(callbackFunc);
   // maxAge must be either a nonnegative integer or -1.
   NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
 
   // Caller may give us a tag object that is no longer live.
   if (!IsLiveTag(plugin)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -1564,39 +1631,81 @@ nsPluginHost::ClearSiteData(nsIPluginTag
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   PluginLibrary* library = tag->mPlugin->GetLibrary();
 
   // If 'domain' is the null string, clear everything.
   if (domain.IsVoid()) {
-    return library->NPP_ClearSiteData(nullptr, flags, maxAge);
+    return library->NPP_ClearSiteData(nullptr, flags, maxAge, callback);
   }
-
-  // Get the list of sites from the plugin.
-  InfallibleTArray<nsCString> sites;
-  rv = library->NPP_GetSitesWithData(sites);
+  nsCOMPtr<nsIGetSitesWithDataCallback> closure(new ClearDataFromSitesClosure(plugin, domain, flags,
+                                                                              maxAge, callback, this));
+  rv = library->NPP_GetSitesWithData(closure);
   NS_ENSURE_SUCCESS(rv, rv);
-
-  // Enumerate the sites and build a list of matches.
-  InfallibleTArray<nsCString> matches;
-  rv = EnumerateSiteData(domain, sites, matches, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Clear the matches.
-  for (uint32_t i = 0; i < matches.Length(); ++i) {
-    const nsCString& match = matches[i];
-    rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   return NS_OK;
 }
 
+#define GetSitesClosure_CID {0x4c9268ac, 0x2fd1, 0x4f2a, {0x9a, 0x10, 0x7a, 0x09, 0xf1, 0xb7, 0x60, 0x3a}}
+
+// Closure to contain the data needed to handle the callback from NPP_GetSitesWithData
+class GetSitesClosure : public nsIGetSitesWithDataCallback {
+public:
+  NS_DECL_ISUPPORTS
+  GetSitesClosure(const nsACString& domain, nsPluginHost* host)
+  : domain(domain), host(host), keepWaiting(true)
+  {
+  }
+  NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& sites) override {
+    retVal = HandleGetSites(sites);
+    keepWaiting = false;
+    return NS_OK;
+  }
+
+  nsresult HandleGetSites(InfallibleTArray<nsCString>& sites) {
+    // If there's no data, we're done.
+    if (sites.IsEmpty()) {
+      result = false;
+      return NS_OK;
+    }
+
+    // If 'domain' is the null string, and there's data for at least one site,
+    // we're done.
+    if (domain.IsVoid()) {
+      result = true;
+      return NS_OK;
+    }
+
+    // Enumerate the sites and determine if there's a match.
+    InfallibleTArray<nsCString> matches;
+    nsresult rv = host->EnumerateSiteData(domain, sites, matches, true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    result = !matches.IsEmpty();
+    return NS_OK;
+  }
+
+  nsCString domain;
+  nsRefPtr<nsPluginHost> host;
+  bool result;
+  bool keepWaiting;
+  nsresult retVal;
+  NS_DECLARE_STATIC_IID_ACCESSOR(GetSitesClosure_CID)
+  private:
+  virtual ~GetSitesClosure() {
+  }
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(GetSitesClosure, GetSitesClosure_CID)
+
+NS_IMPL_ISUPPORTS(GetSitesClosure, GetSitesClosure, nsIGetSitesWithDataCallback)
+
+// This will spin the event loop while waiting on an async
+// call to GetSitesWithData
 NS_IMETHODIMP
 nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
                           bool* result)
 {
   // Caller may give us a tag object that is no longer live.
   if (!IsLiveTag(plugin)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -1614,41 +1723,26 @@ nsPluginHost::SiteHasData(nsIPluginTag* 
   // Make sure the plugin is loaded.
   nsresult rv = EnsurePluginLoaded(tag);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   PluginLibrary* library = tag->mPlugin->GetLibrary();
 
-  // Get the list of sites from the plugin.
-  InfallibleTArray<nsCString> sites;
-  rv = library->NPP_GetSitesWithData(sites);
+  // Get the list of sites from the plugin
+  nsCOMPtr<GetSitesClosure> closure(new GetSitesClosure(domain, this));
+  rv = library->NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback>(do_QueryInterface(closure)));
   NS_ENSURE_SUCCESS(rv, rv);
-
-  // If there's no data, we're done.
-  if (sites.IsEmpty()) {
-    *result = false;
-    return NS_OK;
+  // Spin the event loop while we wait for the async call to GetSitesWithData
+  while (closure->keepWaiting) {
+    NS_ProcessNextEvent(nullptr, true);
   }
-
-  // If 'domain' is the null string, and there's data for at least one site,
-  // we're done.
-  if (domain.IsVoid()) {
-    *result = true;
-    return NS_OK;
-  }
-
-  // Enumerate the sites and determine if there's a match.
-  InfallibleTArray<nsCString> matches;
-  rv = EnumerateSiteData(domain, sites, matches, true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *result = !matches.IsEmpty();
-  return NS_OK;
+  *result = closure->result;
+  return closure->retVal;
 }
 
 nsPluginHost::SpecialType
 nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
 {
   if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
       aMIMEType.LowerCaseEqualsASCII("application/futuresplash")) {
     return eSpecialType_Flash;
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -238,16 +238,21 @@ public:
   void NotifyContentModuleDestroyed(uint32_t aPluginId);
 
   nsresult NewPluginStreamListener(nsIURI* aURL,
                                    nsNPAPIPluginInstance* aInstance,
                                    nsIStreamListener **aStreamListener);
 
   void CreateWidget(nsPluginInstanceOwner* aOwner);
 
+  nsresult EnumerateSiteData(const nsACString& domain,
+                             const InfallibleTArray<nsCString>& sites,
+                             InfallibleTArray<nsCString>& result,
+                             bool firstMatchOnly);
+
 private:
   friend class nsPluginUnloadRunnable;
 
   void DestroyRunningInstances(nsPluginTag* aPluginTag);
 
   // Writes updated plugins settings to disk and unloads the plugin
   // if it is now disabled. Should only be called by the plugin tag in question
   void UpdatePluginInfo(nsPluginTag* aPluginTag);
@@ -361,20 +366,16 @@ private:
   nsCOMPtr<nsIWindowsRegKey> mRegKeyHKCU;
 #endif
 
   nsCOMPtr<nsIEffectiveTLDService> mTLDService;
   nsCOMPtr<nsIIDNService> mIDNService;
 
   // Helpers for ClearSiteData and SiteHasData.
   nsresult NormalizeHostname(nsCString& host);
-  nsresult EnumerateSiteData(const nsACString& domain,
-                             const InfallibleTArray<nsCString>& sites,
-                             InfallibleTArray<nsCString>& result,
-                             bool firstMatchOnly);
 
   nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
 
   // This epoch increases each time we load the list of plugins from disk.
   // In the chrome process, this stores the actual epoch.
   // In the content process, this stores the last epoch value observed
   // when reading plugins from chrome.
   uint32_t mPluginEpoch;
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -71,21 +71,19 @@ child:
 
   intr NP_Shutdown()
     returns (NPError rv);
 
   intr OptionalFunctionsSupported()
     returns (bool aURLRedirectNotify, bool aClearSiteData,
              bool aGetSitesWithData);
 
-  intr NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge)
-    returns (NPError rv);
+  async NPP_ClearSiteData(nsCString site, uint64_t flags, uint64_t maxAge, uint64_t aCallbackId);
 
-  intr NPP_GetSitesWithData()
-    returns (nsCString[] sites);
+  async NPP_GetSitesWithData(uint64_t aCallbackId);
 
   // Windows specific message to set up an audio session in the plugin process
   async SetAudioSessionData(nsID aID,
                             nsString aDisplayName,
                             nsString aIconPath);
 
   async SetParentHangTimeout(uint32_t seconds);
 
@@ -143,12 +141,18 @@ parent:
   async NPN_ReloadPlugins(bool aReloadPages);
 
   // Notifies the chrome process that a PluginModuleChild linked to a content
   // process was destroyed. The chrome process may choose to asynchronously shut
   // down the plugin process in response.
   async NotifyContentModuleDestroyed();
 
   async Profile(nsCString aProfile);
+
+  // Answers to request about site data
+  async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
+
+  async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
+
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -23,16 +23,25 @@ class nsNPAPIPlugin;
 
 namespace mozilla {
 namespace layers {
 class Image;
 class ImageContainer;
 }
 }
 
+class nsIClearSiteDataCallback;
+
+#define nsIGetSitesWithDataCallback_CID {0xd0028b83, 0xfdf9, 0x4c53, {0xb7, 0xbb, 0x47, 0x46, 0x0f, 0x6b, 0x83, 0x6c}}
+class nsIGetSitesWithDataCallback : public nsISupports {
+public:
+  NS_IMETHOD SitesWithData(InfallibleTArray<nsCString>& result) = 0;
+  NS_DECLARE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback_CID)
+};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIGetSitesWithDataCallback, nsIGetSitesWithDataCallback_CID)
 
 namespace mozilla {
 
 class PluginLibrary
 {
 public:
   virtual ~PluginLibrary() { }
 
@@ -57,18 +66,18 @@ public:
   virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) = 0;
 #endif
   virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                            uint16_t mode, int16_t argc, char* argn[],
                            char* argv[], NPSavedData* saved,
                            NPError* error) = 0;
 
   virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
-                                     uint64_t maxAge) = 0;
-  virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) = 0;
+                                     uint64_t maxAge, nsCOMPtr<nsIClearSiteDataCallback> callback) = 0;
+  virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) = 0;
 
   virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0;
   virtual nsresult GetImageContainer(NPP instance, mozilla::layers::ImageContainer** aContainer) = 0;
   virtual nsresult GetImageSize(NPP instance, nsIntSize* aSize) = 0;
   virtual bool IsOOP() = 0;
 #if defined(XP_MACOSX)
   virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
   virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -729,41 +729,44 @@ PluginModuleChild::AnswerOptionalFunctio
 {
     *aURLRedirectNotify = !!mFunctions.urlredirectnotify;
     *aClearSiteData = !!mFunctions.clearsitedata;
     *aGetSitesWithData = !!mFunctions.getsiteswithdata;
     return true;
 }
 
 bool
-PluginModuleChild::AnswerNPP_ClearSiteData(const nsCString& aSite,
+PluginModuleChild::RecvNPP_ClearSiteData(const nsCString& aSite,
                                            const uint64_t& aFlags,
                                            const uint64_t& aMaxAge,
-                                           NPError* aResult)
+                                           const uint64_t& aCallbackId)
 {
-    *aResult =
+    NPError result =
         mFunctions.clearsitedata(NullableStringGet(aSite), aFlags, aMaxAge);
+    SendReturnClearSiteData(result, aCallbackId);
     return true;
 }
 
 bool
-PluginModuleChild::AnswerNPP_GetSitesWithData(InfallibleTArray<nsCString>* aResult)
+PluginModuleChild::RecvNPP_GetSitesWithData(const uint64_t& aCallbackId)
 {
     char** result = mFunctions.getsiteswithdata();
-    if (!result)
+    InfallibleTArray<nsCString> array;
+    if (!result) {
+        SendReturnSitesWithData(array, aCallbackId);
         return true;
-
+    }
     char** iterator = result;
     while (*iterator) {
-        aResult->AppendElement(*iterator);
+        array.AppendElement(*iterator);
         free(*iterator);
         ++iterator;
     }
+    SendReturnSitesWithData(array, aCallbackId);
     free(result);
-
     return true;
 }
 
 bool
 PluginModuleChild::RecvSetAudioSessionData(const nsID& aId,
                                            const nsString& aDisplayName,
                                            const nsString& aIconPath)
 {
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -107,23 +107,23 @@ protected:
     AnswerNP_Shutdown(NPError *rv) override;
 
     virtual bool
     AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify,
                                      bool *aClearSiteData,
                                      bool *aGetSitesWithData) override;
 
     virtual bool
-    AnswerNPP_ClearSiteData(const nsCString& aSite,
+    RecvNPP_ClearSiteData(const nsCString& aSite,
                             const uint64_t& aFlags,
                             const uint64_t& aMaxAge,
-                            NPError* aResult) override;
+                            const uint64_t& aCallbackId) override;
 
     virtual bool
-    AnswerNPP_GetSitesWithData(InfallibleTArray<nsCString>* aResult) override;
+    RecvNPP_GetSitesWithData(const uint64_t& aCallbackId) override;
 
     virtual bool
     RecvSetAudioSessionData(const nsID& aId,
                             const nsString& aDisplayName,
                             const nsString& aIconPath) override;
 
     virtual bool
     RecvSetParentHangTimeout(const uint32_t& aSeconds) override;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2649,45 +2649,44 @@ PluginModuleParent::NPP_NewInternal(NPMI
 
 void
 PluginModuleChromeParent::UpdatePluginTimeout()
 {
     TimeoutChanged(kParentTimeoutPref, this);
 }
 
 nsresult
-PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
-                                      uint64_t maxAge)
+PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
+                                      nsCOMPtr<nsIClearSiteDataCallback> callback)
 {
     if (!mClearSiteDataSupported)
         return NS_ERROR_NOT_AVAILABLE;
 
-    NPError result;
-    if (!CallNPP_ClearSiteData(NullableString(site), flags, maxAge, &result))
-        return NS_ERROR_FAILURE;
-
-    switch (result) {
-    case NPERR_NO_ERROR:
-        return NS_OK;
-    case NPERR_TIME_RANGE_NOT_SUPPORTED:
-        return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
-    case NPERR_MALFORMED_SITE:
-        return NS_ERROR_INVALID_ARG;
-    default:
+    static uint64_t callbackId = 0;
+    callbackId++;
+    mClearSiteDataCallbacks[callbackId] = callback;
+
+    if (!SendNPP_ClearSiteData(NullableString(site), flags, maxAge, callbackId)) {
         return NS_ERROR_FAILURE;
     }
+    return NS_OK;
 }
 
+
 nsresult
-PluginModuleParent::NPP_GetSitesWithData(InfallibleTArray<nsCString>& result)
+PluginModuleParent::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback)
 {
     if (!mGetSitesWithDataSupported)
         return NS_ERROR_NOT_AVAILABLE;
 
-    if (!CallNPP_GetSitesWithData(&result))
+    static uint64_t callbackId = 0;
+    callbackId++;
+    mSitesWithDataCallbacks[callbackId] = callback;
+
+    if (!SendNPP_GetSitesWithData(callbackId))
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 #if defined(XP_MACOSX)
 nsresult
 PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
@@ -2931,16 +2930,59 @@ PluginModuleChromeParent::RecvNotifyCont
 {
     nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
     if (host) {
         host->NotifyContentModuleDestroyed(mPluginId);
     }
     return true;
 }
 
+bool
+PluginModuleParent::RecvReturnClearSiteData(const NPError& aRv,
+                                            const uint64_t& aCallbackId)
+{
+    if (mClearSiteDataCallbacks.find(aCallbackId) == mClearSiteDataCallbacks.end()) {
+        return true;
+    }
+    if (!!mClearSiteDataCallbacks[aCallbackId]) {
+        nsresult rv;
+        switch (aRv) {
+        case NPERR_NO_ERROR:
+            rv = NS_OK;
+            break;
+        case NPERR_TIME_RANGE_NOT_SUPPORTED:
+            rv = NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
+            break;
+        case NPERR_MALFORMED_SITE:
+            rv = NS_ERROR_INVALID_ARG;
+            break;
+        default:
+            rv = NS_ERROR_FAILURE;
+        }
+        mClearSiteDataCallbacks[aCallbackId]->Callback(rv);
+    }
+    mClearSiteDataCallbacks.erase(aCallbackId);
+    return true;
+}
+
+bool
+PluginModuleParent::RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
+                                            const uint64_t& aCallbackId)
+{
+    if (mSitesWithDataCallbacks.find(aCallbackId) == mSitesWithDataCallbacks.end()) {
+        return true;
+    }
+
+    if (!!mSitesWithDataCallbacks[aCallbackId]) {
+        mSitesWithDataCallbacks[aCallbackId]->SitesWithData(aSites);
+    }
+    mSitesWithDataCallbacks.erase(aCallbackId);
+    return true;
+}
+
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 // We only add the crash reporter to subprocess which have the filename
 // FlashPlayerPlugin*
 #define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
 
 static DWORD
 GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -195,16 +195,22 @@ protected:
     static void TimeoutChanged(const char* aPref, void* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual bool RecvNotifyContentModuleDestroyed() override { return true; }
 
     virtual bool RecvProfile(const nsCString& aProfile) override { return true; }
 
+    virtual bool RecvReturnClearSiteData(const NPError& aRv,
+                                         const uint64_t& aCallbackId) override;
+
+    virtual bool RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
+                                         const uint64_t& aCallbackId) override;
+
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode,
                              InfallibleTArray<nsCString>& names,
                              InfallibleTArray<nsCString>& values,
                              NPSavedData* saved, NPError* error);
 
     // NPP-like API that Gecko calls are trampolined into.  These 
@@ -259,19 +265,25 @@ protected:
                                  void *aValue, NPError* error) override;
 #if defined(XP_WIN) || defined(XP_MACOSX)
     virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
 #endif
     virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                              uint16_t mode, int16_t argc, char* argn[],
                              char* argv[], NPSavedData* saved,
                              NPError* error) override;
-    virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
-                                       uint64_t maxAge) override;
-    virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result) override;
+    virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge,
+                                       nsCOMPtr<nsIClearSiteDataCallback> callback) override;
+    virtual nsresult NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> callback) override;
+
+private:
+    std::map<uint64_t, nsCOMPtr<nsIClearSiteDataCallback>> mClearSiteDataCallbacks;
+    std::map<uint64_t, nsCOMPtr<nsIGetSitesWithDataCallback>> mSitesWithDataCallbacks;
+
+public:
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) override;
     virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) override;
 #endif
 
     void InitAsyncSurrogates();
 
--- a/dom/plugins/test/mochitest/test_clear_site_data.html
+++ b/dom/plugins/test/mochitest/test_clear_site_data.html
@@ -23,19 +23,17 @@
 
     var p = document.getElementById("plugin1");
 
     // Since we're running with chrome permissions, accessing the plugin wont
     // synchronously spawn it -- wait for the async spawning to finish.
     SimpleTest.executeSoon(function() {
       // Make sure clearing by timerange is supported.
       p.setSitesWithDataCapabilities(true);
-
       ok(PluginUtils.withTestPlugin(runTest), "Test plugin found");
-      SimpleTest.finish();
     });
 
     function stored(needles) {
       var something = pluginHost.siteHasData(this.pluginTag, null);
       if (!needles)
         return something;
 
       if (!something)
@@ -54,155 +52,191 @@
         throw new Error("bad exception");
       } catch (e) {
         is(e.result, result, "Correct exception thrown");
       }
     }
 
     function runTest(pluginTag) {
       this.pluginTag = pluginTag;
-
       p.setSitesWithData(
         "foo.com:0:5," +
         "foo.com:0:7," +
         "bar.com:0:10," +
         "baz.com:0:10," +
         "foo.com:1:7," +
         "qux.com:1:5," +
         "quz.com:1:8"
       );
-
-      ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
-        "Data stored for sites");
-
-      // Clear nothing.
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 4);
       ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
          "Data stored for sites");
 
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 4);
+      // Clear nothing.
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 4, {callback: function() { test1(); }});
+    }
+    function test1() {
+      ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
+         "Data stored for sites");
+
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 4, {callback: function() { test2(); }});
+    }
+    function test2() {
       ok(stored(["foo.com","bar.com","baz.com","qux.com","quz.com"]),
          "Data stored for sites");
 
       // Clear cache data 5 seconds or older.
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 5);
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 5, {callback: function() { test3(); }});
+    }
+    function test3() {
       ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
          "Data stored for sites");
       ok(!stored(["qux.com"]), "Data cleared for qux.com");
-
       // Clear cache data for foo.com, but leave non-cache data.
-      pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_CACHE, 20);
+      pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_CACHE, 20, {callback: function() { test4(); }});
+    }
+    function test4() {
       ok(stored(["foo.com","bar.com","baz.com","quz.com"]),
          "Data stored for sites");
 
       // Clear all data 7 seconds or older.
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 7);
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 7, {callback: function() { test5(); }});
+    }
+    function test5() {
       ok(stored(["bar.com","baz.com","quz.com"]), "Data stored for sites");
       ok(!stored(["foo.com"]), "Data cleared for foo.com");
       ok(!stored(["qux.com"]), "Data cleared for qux.com");
 
       // Clear all cache data.
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20);
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function() { test6(); }});
+    }
+    function test6() {
       ok(stored(["bar.com","baz.com"]), "Data stored for sites");
       ok(!stored(["quz.com"]), "Data cleared for quz.com");
 
       // Clear all data for bar.com.
-      pluginHost.clearSiteData(pluginTag, "bar.com", FLAG_CLEAR_ALL, 20);
+      pluginHost.clearSiteData(pluginTag, "bar.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) { test7(rv); }});
+    }
+    function test7(rv) {
       ok(stored(["baz.com"]), "Data stored for baz.com");
       ok(!stored(["bar.com"]), "Data cleared for bar.com");
 
       // Disable clearing by age.
       p.setSitesWithDataCapabilities(false);
 
-      // Attempt to clear data by age.
-      checkThrows(function() {
-        pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 20);
-      }, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
-
-      checkThrows(function() {
-        pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20);
-      }, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
-
-      checkThrows(function() {
-        pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, 20);
-      }, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
-
-      checkThrows(function() {
-        pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, 20);
-      }, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
-
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_ALL, 20, {callback: function(rv) {
+        is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+        test8(rv);
+      }});
+    }
+    function test8(rv) {
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
+        is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+        test9(rv);
+      }});
+    }
+    function test9(rv) {
+      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, 20, {callback: function(rv) {
+        is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+        test10(rv);
+      }});
+    }
+    function test10(rv) {
+      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, 20, {callback: function(rv) {
+        is(rv, Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED);
+          test11();
+          }});
+    }
+    function test11() {
       // Clear cache for baz.com and globally for all ages.
-      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, -1);
-      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, -1);
-
+      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test12()}});
+    }
+    function test12() {
+      pluginHost.clearSiteData(pluginTag, null, FLAG_CLEAR_CACHE, -1, {callback: function(rv) { test13()}});
+    }
+    function test13() {
       // Check that all of the above were no-ops.
       ok(stored(["baz.com"]), "Data stored for baz.com");
 
       // Clear everything for baz.com.
-      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "baz.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test14()}});
+    }
+    function test14() {
       ok(!stored(["baz.com"]), "Data cleared for baz.com");
       ok(!stored(null), "All data cleared");
 
       // Set data to test subdomains, IP literals, and 'localhost'-like hosts.
       p.setSitesWithData(
         "foo.com:0:0," +
         "bar.foo.com:0:0," +
         "baz.foo.com:0:0," +
         "bar.com:0:0," +
         "[192.168.1.1]:0:0," +
         "localhost:0:0"
       );
-
       ok(stored(["foo.com","nonexistent.foo.com","bar.com","192.168.1.1","localhost"]),
-        "Data stored for sites");
+         "Data stored for sites");
 
       // Clear data for "foo.com" and its subdomains.
-      pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "foo.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test15()}});
+    }
+    function test15() {
       ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
       ok(!stored(["foo.com"]), "Data cleared for foo.com");
       ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
 
       // Clear data for "bar.com" using a subdomain.
-      pluginHost.clearSiteData(pluginTag, "foo.bar.com", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "foo.bar.com", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test16()}});
+    }
+    function test16() {
       ok(!stored(["bar.com"]), "Data cleared for bar.com");
 
       // Clear data for "192.168.1.1".
-      pluginHost.clearSiteData(pluginTag, "192.168.1.1", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "192.168.1.1", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test17()}});
+    }
+    function test17() {
       ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
 
       // Clear data for "localhost".
-      pluginHost.clearSiteData(pluginTag, "localhost", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "localhost", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test18()}});
+    }
+    function test18() {
       ok(!stored(null), "All data cleared");
 
       // Set data to test international domains.
       p.setSitesWithData(
         "b\u00FCcher.es:0:0," +
         "b\u00FCcher.uk:0:0," +
         "xn--bcher-kva.NZ:0:0"
       );
-
-      // Check that both the ACE and UTF-8 representations register.
+        // Check that both the ACE and UTF-8 representations register.
       ok(stored(["b\u00FCcher.es","xn--bcher-kva.es","b\u00FCcher.uk","xn--bcher-kva.uk"]),
-        "Data stored for sites");
+         "Data stored for sites");
 
       // Clear data for the UTF-8 version.
-      pluginHost.clearSiteData(pluginTag, "b\u00FCcher.es", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "b\u00FCcher.es", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test19()}});
+    }
+    function test19() {
       ok(!stored(["b\u00FCcher.es"]), "Data cleared for UTF-8 representation");
       ok(!stored(["xn--bcher-kva.es"]), "Data cleared for ACE representation");
 
       // Clear data for the ACE version.
-      pluginHost.clearSiteData(pluginTag, "xn--bcher-kva.uk", FLAG_CLEAR_ALL, -1);
+      pluginHost.clearSiteData(pluginTag, "xn--bcher-kva.uk", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test20()}});
+    }
+    function test20() {
       ok(!stored(["b\u00FCcher.uk"]), "Data cleared for UTF-8 representation");
       ok(!stored(["xn--bcher-kva.uk"]), "Data cleared for ACE representation");
 
       // The NPAPI spec requires that the plugin report sites in normalized
       // UTF-8. We do happen to normalize the result anyway, so while that's not
       // strictly required, we test it here.
       ok(stored(["b\u00FCcher.nz","xn--bcher-kva.nz"]),
-        "Data stored for sites");
-      pluginHost.clearSiteData(pluginTag, "b\u00FCcher.nz", FLAG_CLEAR_ALL, -1);
+         "Data stored for sites");
+      pluginHost.clearSiteData(pluginTag, "b\u00FCcher.nz", FLAG_CLEAR_ALL, -1, {callback: function(rv) { test21()}});
+    }
+    function test21() {
       ok(!stored(["b\u00FCcher.nz"]), "Data cleared for UTF-8 representation");
       ok(!stored(["xn--bcher-kva.nz"]), "Data cleared for ACE representation");
       ok(!stored(null), "All data cleared");
+      SimpleTest.finish();
     }
   </script>
 </body>
 </html>
--- a/dom/security/test/csp/browser_test_web_manifest.js
+++ b/dom/security/test/csp/browser_test_web_manifest.js
@@ -1,30 +1,24 @@
 /*
  * Description of the tests:
  *   These tests check for conformance to the CSP spec as they relate to Web Manifests.
  *
  *   In particular, the tests check that default-src and manifest-src directives are
  *   are respected by the ManifestObtainer.
  */
-/*globals Components*/
+/*globals SpecialPowers, requestLongerTimeout, ok, Cu, is, add_task, gBrowser, BrowserTestUtils, ManifestObtainer*/
 'use strict';
 requestLongerTimeout(10); // e10s tests take time.
-const {
-  ManifestObtainer
-} = Components.utils.import('resource://gre/modules/WebManifest.jsm', {});
+Cu.import('resource://gre/modules/ManifestObtainer.jsm', this); // jshint ignore:line
 const path = '/tests/dom/security/test/csp/';
 const testFile = `file=${path}file_web_manifest.html`;
 const remoteFile = `file=${path}file_web_manifest_remote.html`;
-const httpsManifest = `file=${path}file_web_manifest_https.html`;
-const mixedContent = `file=${path}file_web_manifest_mixed_content.html`;
 const server = 'file_testserver.sjs';
 const defaultURL = `http://example.org${path}${server}`;
-const remoteURL = `http://mochi.test:8888`;
-const secureURL = `https://example.com${path}${server}`;
 const tests = [
   // CSP block everything, so trying to load a manifest
   // will result in a policy violation.
   {
     expected: `default-src 'none' blocks fetching manifest.`,
     get tabURL() {
       let queryParts = [
         `csp=default-src 'none'`,
@@ -243,17 +237,17 @@ add_task(function* () {
 
 // Helper object used to observe policy violations. It waits 10 seconds
 // for a response, and then times out causing its associated test to fail.
 function NetworkObserver(test) {
   let finishedTest;
   let success = false;
   this.finished = new Promise((resolver) => {
     finishedTest = resolver;
-  })
+  });
   this.observe = function observer(subject, topic) {
     SpecialPowers.removeObserver(this, 'csp-on-violate-policy');
     test.run(topic);
     finishedTest();
     success = true;
   };
   SpecialPowers.addObserver(this, 'csp-on-violate-policy', false);
   setTimeout(() => {
--- a/dom/security/test/csp/browser_test_web_manifest_mixed_content.js
+++ b/dom/security/test/csp/browser_test_web_manifest_mixed_content.js
@@ -1,17 +1,19 @@
 /*
  * Description of the test:
  *   Check that mixed content blocker works prevents fetches of
  *   mixed content manifests.
  */
+/*globals Cu, add_task, ok, gBrowser, BrowserTestUtils, ManifestObtainer*/
 'use strict';
 const {
   ManifestObtainer
-} = Components.utils.import('resource://gre/modules/WebManifest.jsm', {});
+} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', this); // jshint ignore:line
+const obtainer = new ManifestObtainer();
 const path = '/tests/dom/security/test/csp/';
 const mixedContent = `file=${path}file_web_manifest_mixed_content.html`;
 const server = 'file_testserver.sjs';
 const secureURL = `https://example.com${path}${server}`;
 const tests = [
   // Trying to load mixed content in file_web_manifest_mixed_content.html
   // needs to result in an error.
   {
@@ -26,30 +28,30 @@ const tests = [
       // Check reason for error.
       const check = /blocked the loading of a resource/.test(error.message);
       ok(check, this.expected);
     }
   }
 ];
 
 //jscs:disable
-add_task(function*() {
+add_task(function* () {
   //jscs:enable
   for (let test of tests) {
     let tabOptions = {
       gBrowser: gBrowser,
       url: test.tabURL,
     };
     yield BrowserTestUtils.withNewTab(
       tabOptions,
       browser => testObtainingManifest(browser, test)
     );
   }
 
   function* testObtainingManifest(aBrowser, aTest) {
-    const obtainer = new ManifestObtainer();
+    let manifest;
     try {
       yield obtainer.obtainManifest(aBrowser);
     } catch (e) {
-      aTest.run(e)
+      return aTest.run(e);
     }
   }
 });
--- a/dom/tests/mochitest/chrome/window_focus.xul
+++ b/dom/tests/mochitest/chrome/window_focus.xul
@@ -142,20 +142,23 @@ function expectFocusShift(callback, expe
         expectedEvents += " ";
       expectedEvents += "focus: " + id + "-document " +
                         "focus: " + id + "-window";
     }
 
     // for this test which fires a mouse event on a label, the document will
     // be focused first and then the label code will focus the related
     // control. This doesn't result in different focus events, but a command
-    // update will occur for the document and then a secon command update will
-    // occur when the control is focused.
-    if (testid == "mouse on html label with content inside")
+    // update will occur for the document and then a second command update will
+    // occur when the control is focused. However, this will only happen on
+    // platforms or controls where mouse clicks cause trigger focus.
+    if (testid == "mouse on html label with content inside" &&
+        mouseWillTriggerFocus(expectedElement)) {
       expectedEvents += " commandupdate: cu";
+    }
 
     if (expectedElement &&
         (!gNewExpectedWindow || gNewExpectedWindow.document.documentElement != expectedElement)) {
       if (!gNewExpectedWindow) {
         if (expectedEvents)
           expectedEvents += " ";
         expectedEvents += "commandupdate: cu";
       }
@@ -273,35 +276,46 @@ function getTopWindow(win)
 {
   return win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
              getInterface(Components.interfaces.nsIWebNavigation).
              QueryInterface(Components.interfaces.nsIDocShellTreeItem).rootTreeItem.
              QueryInterface(Components.interfaces.nsIInterfaceRequestor).
              getInterface(Components.interfaces.nsIDOMWindow);
 }
 
+function mouseWillTriggerFocus(element)
+{
+  if (!element) {
+    return false;
+  }
+
+  if (navigator.platform.indexOf("Mac") != 0) {
+    return true;
+  }
+
+  if (element.namespaceURI == "http://www.w3.org/1999/xhtml") {
+    // links are special. They can be focused but show no focus ring
+    if (element.localName == "a" || element.localName == "div" ||
+        element.localName == "select" ||
+        element.localName == "input" && (element.type == "text" ||
+                                         element.type == "password")) {
+      return true;
+    }
+  } else if (element.localName == "listbox") {
+    return true;
+  }
+
+  return false;
+}
+
 function mouseOnElement(element, expectedElement, focusChanged, testid)
 {
   var expectedWindow = (element.ownerDocument.defaultView == gChildWindow) ? gChildWindow : window;
   // on Mac, form elements are not focused when clicking, except for lists and textboxes.
-  var noFocusOnMouse = (navigator.platform.indexOf("Mac") == 0);
-  if (noFocusOnMouse) {
-    if (element.namespaceURI == "http://www.w3.org/1999/xhtml") {
-      // links are special. They can be focused but show no focus ring
-      if (element.localName == "a" || element.localName == "div" ||
-          element.localName == "select" ||
-          element.localName == "input" && (element.type == "text" ||
-                                           element.type == "password")) {
-        noFocusOnMouse = false;
-      }
-    }
-    else if (element.localName == "listbox") {
-      noFocusOnMouse = false;
-    }
-  }
+  var noFocusOnMouse = !mouseWillTriggerFocus(element)
 
   if (noFocusOnMouse) {
     // no focus so the last focus method will be 0
     gLastFocusMethod = 0;
     expectFocusShift(function () synthesizeMouse(element, 4, 4, { }, element.ownerDocument.defaultView),
                      expectedWindow, null, true, testid);
     gLastFocusMethod = fm.FLAG_BYMOUSE;
   }
@@ -547,21 +561,19 @@ function startTest()
       accessKeyDetails.altKey = isContent;
     }
 
     expectFocusShift(function () synthesizeKey(key, accessKeyDetails),
                      null, getById(keys[k]), true, "accesskey " + key);
   }
 
   // clicking on the labels
-  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS;
-  expectFocusShift(function () synthesizeMouse(getById("ad"), 2, 2, { }, gChildWindow),
-                   null, getById("t29"), true, "mouse on html label with content inside");
-  expectFocusShift(function () synthesizeMouse(getById("ag"), 2, 2, { }, gChildWindow),
-                   null, getById("n6"), true, "mouse on html label with for attribute");
+  gLastFocusMethod = fm.FLAG_BYMOVEFOCUS | fm.FLAG_BYMOUSE;
+  mouseOnElement(getById("ad"), getById("t29"), true, "mouse on html label with content inside");
+  mouseOnElement(getById("ag"), getById("n6"), true, "mouse on html label with for attribute");
   gLastFocusMethod = 0;
   expectFocusShift(function () synthesizeMouse(getById("aj"), 2, 2, { }),
                    null, getById("o9"), true, "mouse on xul label with content inside");
   expectFocusShift(function () synthesizeMouse(getById("ak"), 2, 2, { }),
                    null, getById("n4"), true, "mouse on xul label with control attribute");
 
   // test accesskeys that shouldn't work
   k = "o".charCodeAt(0);
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -267,16 +267,21 @@ LayerManagerComposite::EndTransaction(co
   Log();
 #endif
 
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return;
   }
 
+  // Set composition timestamp here because we need it in
+  // ComputeEffectiveTransforms (so the correct video frame size is picked) and
+  // also to compute invalid regions properly.
+  mCompositor->SetCompositionTime(aTimeStamp);
+
   if (mRoot && mClonedLayerTreeProperties) {
     MOZ_ASSERT(!mTarget);
     nsIntRegion invalid =
       mClonedLayerTreeProperties->ComputeDifferences(mRoot, nullptr, &mGeometryChanged);
     mClonedLayerTreeProperties = nullptr;
 
     mInvalidRegion.Or(mInvalidRegion, invalid);
   } else if (!mTarget) {
@@ -285,19 +290,16 @@ LayerManagerComposite::EndTransaction(co
 
   if (mInvalidRegion.IsEmpty() && !mTarget) {
     // Composition requested, but nothing has changed. Don't do any work.
     return;
   }
 
  if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
     MOZ_ASSERT(!aTimeStamp.IsNull());
-    // Set composition timestamp here because we need it in
-    // ComputeEffectiveTransforms (so the correct video frame size is picked)
-    mCompositor->SetCompositionTime(aTimeStamp);
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
     nsIntRegion opaque;
     ApplyOcclusionCulling(mRoot, opaque);
 
     Render();
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -434,17 +434,21 @@ CreateBoxShadow(DrawTarget& aDT, SourceS
   gfxPlatform* platform = gfxPlatform::GetPlatform();
   RefPtr<DrawTarget> boxShadowDT =
     platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
 
   if (!boxShadowDT) {
     return nullptr;
   }
 
-  MOZ_ASSERT(boxShadowDT->GetType() == aDT.GetType());
+  if (boxShadowDT->GetType() != aDT.GetType()) {
+    printf_stderr("Box shadow type: %d, dest draw target type: %d\n",
+      (int) boxShadowDT->GetType(), (int) aDT.GetType());
+    MOZ_ASSERT(false, "Box shadows are incorrect type\n");
+  }
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
 }
 
 SourceSurface*
 GetBlur(DrawTarget& aDT,
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -261,16 +261,17 @@ private:
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-only-on-draw.enabled",     ImageDecodeOnlyOnDrawEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
   DECL_GFX_PREF(Once, "image.decode.retry-on-alloc-failure",   ImageDecodeRetryOnAllocFailure, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
   DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
+  DECL_GFX_PREF(Live, "image.infer-src-animation.threshold-ms", ImageInferSrcAnimationThresholdMS, uint32_t, 2000);
   DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time",      ImageMemDecodeBytesAtATime, uint32_t, 200000);
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Live, "image.mozsamplesize.enabled",           ImageMozSampleSizeEnabled, bool, false);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -118,17 +118,17 @@ NativeRegExpMacroAssembler::GenerateCode
 
     // Finalize code - write the entry point code now we know how many
     // registers we need.
     masm.bind(&entry_label_);
 
 #ifdef JS_CODEGEN_ARM64
     // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
     MOZ_ASSERT(!masm.GetStackPointer64().Is(sp));
-    masm.moveStackPtrTo(masm.getStackPointer());
+    masm.Mov(masm.GetStackPointer64(), sp);
 #endif
 
     // Push non-volatile registers which might be modified by jitcode.
     size_t pushedNonVolatileRegisters = 0;
     for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter) {
         masm.Push(*iter);
         pushedNonVolatileRegisters++;
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1090,29 +1090,36 @@ IonBuilder::initParameters()
 {
     if (!info().funMaybeLazy())
         return;
 
     // If we are doing OSR on a frame which initially executed in the
     // interpreter and didn't accumulate type information, try to use that OSR
     // frame to determine possible initial types for 'this' and parameters.
 
-    if (thisTypes->empty() && baselineFrame_)
-        thisTypes->addType(baselineFrame_->thisType, alloc_->lifoAlloc());
+    if (thisTypes->empty() && baselineFrame_) {
+        TypeSet::Type type = baselineFrame_->thisType;
+        if (type.isSingletonUnchecked())
+            checkNurseryObject(type.singleton());
+        thisTypes->addType(type, alloc_->lifoAlloc());
+    }
 
     MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32_t i = 0; i < info().nargs(); i++) {
         TemporaryTypeSet* types = &argTypes[i];
         if (types->empty() && baselineFrame_ &&
             !script_->baselineScript()->modifiesArguments())
         {
-            types->addType(baselineFrame_->argTypes[i], alloc_->lifoAlloc());
+            TypeSet::Type type = baselineFrame_->argTypes[i];
+            if (type.isSingletonUnchecked())
+                checkNurseryObject(type.singleton());
+            types->addType(type, alloc_->lifoAlloc());
         }
 
         param = MParameter::New(alloc(), i, types);
         current->add(param);
         current->initSlot(info().argSlotUnchecked(i), param);
     }
 }
 
@@ -6990,16 +6997,19 @@ IonBuilder::newPendingLoopHeader(MBasicB
             uint32_t var = i - info().firstLocalSlot();
             if (info().funMaybeLazy() && i == info().thisSlot())
                 existingType = baselineFrame_->thisType;
             else if (arg < info().nargs())
                 existingType = baselineFrame_->argTypes[arg];
             else
                 existingType = baselineFrame_->varTypes[var];
 
+            if (existingType.isSingletonUnchecked())
+                checkNurseryObject(existingType.singleton());
+
             // Extract typeset from value.
             LifoAlloc* lifoAlloc = alloc().lifoAlloc();
             TemporaryTypeSet* typeSet =
                 lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc, existingType);
             if (!typeSet)
                 return nullptr;
             MIRType type = typeSet->getKnownMIRType();
             if (!phi->addBackedgeType(type, typeSet))
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -18,16 +18,20 @@
 
 #include <algorithm>  // for std::max
 #include <fcntl.h>
 
 #ifdef XP_UNIX
 # include <unistd.h>
 #endif
 
+#ifdef XP_WIN
+# include "jswin.h"
+#endif
+
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jslibmath.h"
 #include "jstypes.h"
 #include "prmjtime.h"
 
@@ -735,17 +739,38 @@ random_generateSeed()
     union {
         uint8_t     u8[8];
         uint32_t    u32[2];
         uint64_t    u64;
     } seed;
     seed.u64 = 0;
 
 #if defined(XP_WIN)
+    /*
+     * Temporary diagnostic for bug 1167248: Test whether the injected hooks
+     * react differently to LoadLibraryW / LoadLibraryExW.
+     */
+    HMODULE oldWay = LoadLibraryW(L"ADVAPI32.DLL");
+    HMODULE newWay = LoadLibraryExW(L"ADVAPI32.DLL",
+                                    nullptr,
+                                    LOAD_LIBRARY_SEARCH_SYSTEM32);
+    /* Fallback for older versions of Windows */
+    if (!newWay && GetLastError() == ERROR_INVALID_PARAMETER)
+        newWay = LoadLibraryExW(L"ADVAPI32.DLL", nullptr, 0);
+
+    if (oldWay && !newWay)
+        MOZ_CRASH();
+
     errno_t error = rand_s(&seed.u32[0]);
+
+    if (oldWay)
+        FreeLibrary(oldWay);
+    if (newWay)
+        FreeLibrary(newWay);
+
     MOZ_ASSERT(error == 0, "rand_s() error?!");
 
     error = rand_s(&seed.u32[1]);
     MOZ_ASSERT(error == 0, "rand_s() error?!");
 #elif defined(HAVE_ARC4RANDOM)
     seed.u32[0] = arc4random();
     seed.u32[1] = arc4random();
 #elif defined(XP_UNIX)
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -803,48 +803,48 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             // compartments being collected. Currently, though, NativeInterfaces
             // are shared between compartments. This ought to be fixed.
             bool doSweep = !isCompartmentGC;
 
             // We don't want to sweep the JSClasses at shutdown time.
             // At this point there may be JSObjects using them that have
             // been removed from the other maps.
             if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
-                for (auto i = self->mNativeScriptableSharedMap->RemovingIter(); !i.Done(); i.Next()) {
+                for (auto i = self->mNativeScriptableSharedMap->Iter(); !i.Done(); i.Next()) {
                     auto entry = static_cast<XPCNativeScriptableSharedMap::Entry*>(i.Get());
                     XPCNativeScriptableShared* shared = entry->key;
                     if (shared->IsMarked()) {
                         shared->Unmark();
                     } else if (doSweep) {
                         delete shared;
                         i.Remove();
                     }
                 }
             }
 
             if (!isCompartmentGC) {
-                for (auto i = self->mClassInfo2NativeSetMap->RemovingIter(); !i.Done(); i.Next()) {
+                for (auto i = self->mClassInfo2NativeSetMap->Iter(); !i.Done(); i.Next()) {
                     auto entry = static_cast<ClassInfo2NativeSetMap::Entry*>(i.Get());
                     if (!entry->value->IsMarked())
                         i.Remove();
                 }
             }
 
-            for (auto i = self->mNativeSetMap->RemovingIter(); !i.Done(); i.Next()) {
+            for (auto i = self->mNativeSetMap->Iter(); !i.Done(); i.Next()) {
                 auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
                 XPCNativeSet* set = entry->key_value;
                 if (set->IsMarked()) {
                     set->Unmark();
                 } else if (doSweep) {
                     XPCNativeSet::DestroyInstance(set);
                     i.Remove();
                 }
             }
 
-            for (auto i = self->mIID2NativeInterfaceMap->RemovingIter(); !i.Done(); i.Next()) {
+            for (auto i = self->mIID2NativeInterfaceMap->Iter(); !i.Done(); i.Next()) {
                 auto entry = static_cast<IID2NativeInterfaceMap::Entry*>(i.Get());
                 XPCNativeInterface* iface = entry->value;
                 if (iface->IsMarked()) {
                     iface->Unmark();
                 } else if (doSweep) {
                     XPCNativeInterface::DestroyInstance(iface);
                     i.Remove();
                 }
@@ -899,17 +899,17 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp*
             // finalized and destroyed. We *do* know that the protos'
             // JSObjects would not have been finalized if there were any
             // wrappers that referenced the proto but where not themselves
             // slated for finalization in this gc cycle. So... at this point
             // we know that any and all wrappers that might have been
             // referencing the protos in the dying list are themselves dead.
             // So, we can safely delete all the protos in the list.
 
-            for (auto i = self->mDyingWrappedNativeProtoMap->RemovingIter(); !i.Done(); i.Next()) {
+            for (auto i = self->mDyingWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
                 auto entry = static_cast<XPCWrappedNativeProtoMap::Entry*>(i.Get());
                 delete static_cast<const XPCWrappedNativeProto*>(entry->key);
                 i.Remove();
             }
 
             MOZ_ASSERT(self->mGCIsRunning, "bad state");
             self->mGCIsRunning = false;
 
@@ -3653,17 +3653,17 @@ XPCJSRuntime::DebugDump(int16_t depth)
                         mThisTranslatorMap, mThisTranslatorMap->Count()));
 
         XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)",
                         mNativeSetMap, mNativeSetMap->Count()));
 
         // iterate sets...
         if (depth && mNativeSetMap->Count()) {
             XPC_LOG_INDENT();
-            for (auto i = mNativeSetMap->RemovingIter(); !i.Done(); i.Next()) {
+            for (auto i = mNativeSetMap->Iter(); !i.Done(); i.Next()) {
                 auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
                 entry->key_value->DebugDump(depth);
             }
             XPC_LOG_OUTDENT();
         }
 
         XPC_LOG_OUTDENT();
 #endif
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -145,18 +145,16 @@ public:
                    "problems!");
 #endif
         PL_DHashTableRemove(mTable, wrapper->GetIdentityObject());
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
     PLDHashTable::Iterator Iter() const { return PLDHashTable::Iterator(mTable); }
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
-
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~Native2WrappedNativeMap();
 private:
     Native2WrappedNativeMap();    // no implementation
     explicit Native2WrappedNativeMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
@@ -258,17 +256,17 @@ public:
     inline void Remove(XPCNativeInterface* iface)
     {
         NS_PRECONDITION(iface,"bad param");
         PL_DHashTableRemove(mTable, iface->GetIID());
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~IID2NativeInterfaceMap();
 private:
     IID2NativeInterfaceMap();    // no implementation
     explicit IID2NativeInterfaceMap(int size);
 
@@ -314,17 +312,17 @@ public:
     inline void Remove(nsIClassInfo* info)
     {
         NS_PRECONDITION(info,"bad param");
         PL_DHashTableRemove(mTable, info);
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     // ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets.
     // So we don't want to count those XPCNativeSets, because they are better
     // counted elsewhere (i.e. in XPCJSRuntime::mNativeSetMap, which holds
     // pointers to *all* XPCNativeSets).  Hence the "Shallow".
     size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~ClassInfo2NativeSetMap();
@@ -372,17 +370,17 @@ public:
     {
         NS_PRECONDITION(info,"bad param");
         PL_DHashTableRemove(mTable, info);
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
     PLDHashTable::Iterator Iter() const { return PLDHashTable::Iterator(mTable); }
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~ClassInfo2WrappedNativeProtoMap();
 private:
     ClassInfo2WrappedNativeProtoMap();    // no implementation
     explicit ClassInfo2WrappedNativeProtoMap(int size);
 
@@ -443,17 +441,17 @@ public:
 
         XPCNativeSetKey key(set, nullptr, 0);
         PL_DHashTableRemove(mTable, &key);
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
     PLDHashTable::Iterator Iter() const { return PLDHashTable::Iterator(mTable); }
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~NativeSetMap();
 private:
     NativeSetMap();    // no implementation
     explicit NativeSetMap(int size);
 
@@ -540,17 +538,17 @@ public:
     };
 
     static XPCNativeScriptableSharedMap* newMap(int length);
 
     bool GetNewOrUsed(uint32_t flags, char* name, XPCNativeScriptableInfo* si);
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     ~XPCNativeScriptableSharedMap();
 private:
     XPCNativeScriptableSharedMap();    // no implementation
     explicit XPCNativeScriptableSharedMap(int size);
 private:
     PLDHashTable* mTable;
 };
@@ -581,17 +579,17 @@ public:
     {
         NS_PRECONDITION(proto,"bad param");
         PL_DHashTableRemove(mTable, proto);
     }
 
     inline uint32_t Count() { return mTable->EntryCount(); }
 
     PLDHashTable::Iterator Iter() const { return PLDHashTable::Iterator(mTable); }
-    PLDHashTable::RemovingIterator RemovingIter() { return PLDHashTable::RemovingIterator(mTable); }
+    PLDHashTable::Iterator Iter() { return PLDHashTable::Iterator(mTable); }
 
     ~XPCWrappedNativeProtoMap();
 private:
     XPCWrappedNativeProtoMap();    // no implementation
     explicit XPCWrappedNativeProtoMap(int size);
 private:
     PLDHashTable* mTable;
 };
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -646,22 +646,22 @@ XPCWrappedNativeScope::SystemIsBeingShut
 
     for (cur = gDyingScopes; cur; cur = cur->mNext) {
         // Give the Components object a chance to try to clean up.
         if (cur->mComponents)
             cur->mComponents->SystemIsBeingShutDown();
 
         // Walk the protos first. Wrapper shutdown can leave dangling
         // proto pointers in the proto map.
-        for (auto i = cur->mWrappedNativeProtoMap->RemovingIter(); !i.Done(); i.Next()) {
+        for (auto i = cur->mWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
             auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(i.Get());
             entry->value->SystemIsBeingShutDown();
             i.Remove();
         }
-        for (auto i = cur->mWrappedNativeMap->RemovingIter(); !i.Done(); i.Next()) {
+        for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
             auto entry = static_cast<Native2WrappedNativeMap::Entry*>(i.Get());
             XPCWrappedNative* wrapper = entry->value;
             if (wrapper->IsValid()) {
                 wrapper->SystemIsBeingShutDown();
             }
             i.Remove();
         }
     }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -7338,16 +7338,17 @@ UnionBorderBoxes(nsIFrame* aFrame, bool 
         bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
       return u;
     }
   }
   const nsStyleDisplay* disp = aFrame->StyleDisplay();
   nsIAtom* fType = aFrame->GetType();
   if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
       fType == nsGkAtoms::scrollFrame ||
+      fType == nsGkAtoms::listControlFrame ||
       fType == nsGkAtoms::svgOuterSVGFrame) {
     return u;
   }
 
   nsRect clipPropClipRect;
   bool hasClipPropClip =
     aFrame->GetClipPropClipRect(disp, &clipPropClipRect, bounds.Size());
 
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -577,18 +577,20 @@ public:
                                nsIFrame** aContainingBlock = nullptr);
 
   /**
    * Returns true if aFrame should apply overflow clipping.
    */
   static bool ShouldApplyOverflowClipping(const nsIFrame* aFrame,
                                           const nsStyleDisplay* aDisp)
   {
-    // clip overflow:-moz-hidden-unscrollable ...
-    if (MOZ_UNLIKELY(aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP)) {
+    // clip overflow:-moz-hidden-unscrollable, except for nsListControlFrame,
+    // which is an nsHTMLScrollFrame.
+    if (MOZ_UNLIKELY(aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP &&
+                     aFrame->GetType() != nsGkAtoms::listControlFrame)) {
       return true;
     }
 
     // and overflow:hidden that we should interpret as -moz-hidden-unscrollable
     if (aDisp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN &&
         aDisp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
       // REVIEW: these are the frame types that set up clipping.
       nsIAtom* type = aFrame->GetType();
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -134,17 +134,18 @@ NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
 
 
 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
   ImageFrameSuper(aContext),
   mComputedSize(0, 0),
   mIntrinsicRatio(0, 0),
   mDisplayingIcon(false),
   mFirstFrameComplete(false),
-  mReflowCallbackPosted(false)
+  mReflowCallbackPosted(false),
+  mForceSyncDecoding(false)
 {
   // We assume our size is not constrained and we haven't gotten an
   // initial reflow yet, so don't touch those flags.
   mIntrinsicSize.width.SetCoordValue(0);
   mIntrinsicSize.height.SetCoordValue(0);
 }
 
 nsImageFrame::~nsImageFrame()
@@ -1616,21 +1617,26 @@ nsImageFrame::PaintImage(nsRenderingCont
 
   nsPoint anchorPoint;
   nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
                                                      mIntrinsicSize,
                                                      mIntrinsicRatio,
                                                      StylePosition(),
                                                      &anchorPoint);
 
+  uint32_t flags = aFlags;
+  if (mForceSyncDecoding) {
+    flags |= imgIContainer::FLAG_SYNC_DECODE;
+  }
+
   DrawResult result =
     nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
       PresContext(), aImage,
       nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
-      nullptr, aFlags, &anchorPoint);
+      nullptr, flags, &anchorPoint);
 
   nsImageMap* map = GetImageMap();
   if (map) {
     gfxPoint devPixelOffset =
       nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
                                      PresContext()->AppUnitsPerDevPixel());
     AutoRestoreTransform autoRestoreTransform(drawTarget);
     drawTarget->SetTransform(
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -228,16 +228,19 @@ protected:
   nsresult OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect);
   nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
 
   /**
    * Notification that aRequest will now be the current request.
    */
   void NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus);
 
+  /// Always sync decode our image when painting if @aForce is true.
+  void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
+
 private:
   // random helpers
   inline void SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
                         nsIURI **aURI);
 
   inline void GetLoadGroup(nsPresContext *aPresContext,
                            nsILoadGroup **aLoadGroup);
   nscoord GetContinuationOffset() const;
@@ -303,16 +306,17 @@ private:
   nsCOMPtr<imgIContainer> mImage;
   nsSize mComputedSize;
   mozilla::IntrinsicSize mIntrinsicSize;
   nsSize mIntrinsicRatio;
 
   bool mDisplayingIcon;
   bool mFirstFrameComplete;
   bool mReflowCallbackPosted;
+  bool mForceSyncDecoding;
 
   static nsIIOService* sIOService;
   
   /* loading / broken image icon support */
 
   // XXXbz this should be handled by the prescontext, I think; that
   // way we would have a single iconload per mozilla session instead
   // of one per document...
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -362,18 +362,18 @@ Declaration::GetValue(nsCSSProperty aPro
       }
       // tweak aProperty and fall through
       aProperty = eCSSProperty_border_top;
     }
     case eCSSProperty_border_top:
     case eCSSProperty_border_right:
     case eCSSProperty_border_bottom:
     case eCSSProperty_border_left:
-    case eCSSProperty_border_start:
-    case eCSSProperty_border_end:
+    case eCSSProperty_border_inline_start:
+    case eCSSProperty_border_inline_end:
     case eCSSProperty_border_block_start:
     case eCSSProperty_border_block_end:
     case eCSSProperty__moz_column_rule:
     case eCSSProperty_outline: {
       const nsCSSProperty* subprops =
         nsCSSProps::SubpropertyEntryFor(aProperty);
       MOZ_ASSERT(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
                                 NS_LITERAL_CSTRING("-color")),
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -6959,25 +6959,25 @@ static const nsCSSProperty kBorderBottom
   eCSSProperty_border_bottom_style,
   eCSSProperty_border_bottom_color
 };
 static const nsCSSProperty kBorderLeftIDs[] = {
   eCSSProperty_border_left_width,
   eCSSProperty_border_left_style,
   eCSSProperty_border_left_color
 };
-static const nsCSSProperty kBorderStartIDs[] = {
-  eCSSProperty_border_start_width,
-  eCSSProperty_border_start_style,
-  eCSSProperty_border_start_color
+static const nsCSSProperty kBorderInlineStartIDs[] = {
+  eCSSProperty_border_inline_start_width,
+  eCSSProperty_border_inline_start_style,
+  eCSSProperty_border_inline_start_color
 };
-static const nsCSSProperty kBorderEndIDs[] = {
-  eCSSProperty_border_end_width,
-  eCSSProperty_border_end_style,
-  eCSSProperty_border_end_color
+static const nsCSSProperty kBorderInlineEndIDs[] = {
+  eCSSProperty_border_inline_end_width,
+  eCSSProperty_border_inline_end_style,
+  eCSSProperty_border_inline_end_color
 };
 static const nsCSSProperty kBorderBlockStartIDs[] = {
   eCSSProperty_border_block_start_width,
   eCSSProperty_border_block_start_style,
   eCSSProperty_border_block_start_color
 };
 static const nsCSSProperty kBorderBlockEndIDs[] = {
   eCSSProperty_border_block_end_width,
@@ -10061,20 +10061,20 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_border_style:
     return ParseBorderStyle();
   case eCSSProperty_border_block_end:
     return ParseBorderSide(kBorderBlockEndIDs, false);
   case eCSSProperty_border_block_start:
     return ParseBorderSide(kBorderBlockStartIDs, false);
   case eCSSProperty_border_bottom:
     return ParseBorderSide(kBorderBottomIDs, false);
-  case eCSSProperty_border_end:
-    return ParseBorderSide(kBorderEndIDs, false);
-  case eCSSProperty_border_start:
-    return ParseBorderSide(kBorderStartIDs, false);
+  case eCSSProperty_border_inline_end:
+    return ParseBorderSide(kBorderInlineEndIDs, false);
+  case eCSSProperty_border_inline_start:
+    return ParseBorderSide(kBorderInlineStartIDs, false);
   case eCSSProperty_border_left:
     return ParseBorderSide(kBorderLeftIDs, false);
   case eCSSProperty_border_right:
     return ParseBorderSide(kBorderRightIDs, false);
   case eCSSProperty_border_top:
     return ParseBorderSide(kBorderTopIDs, false);
   case eCSSProperty_border_bottom_colors:
   case eCSSProperty_border_left_colors:
--- a/layout/style/nsCSSPropAliasList.h
+++ b/layout/style/nsCSSPropAliasList.h
@@ -122,56 +122,56 @@ CSS_PROP_ALIAS(-moz-box-sizing,
 CSS_PROP_ALIAS(-moz-font-feature-settings,
                font_feature_settings,
                MozFontFeatureSettings,
                "layout.css.prefixes.font-features")
 CSS_PROP_ALIAS(-moz-font-language-override,
                font_language_override,
                MozFontLanguageOverride,
                "layout.css.prefixes.font-features")
-CSS_PROP_ALIAS(padding-inline-end,
-               padding_end,
-               PaddingInlineEnd,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(padding-inline-start,
-               padding_start,
-               PaddingInlineStart,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(margin-inline-end,
-               margin_end,
-               MarginInlineEnd,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(margin-inline-start,
-               margin_start,
-               MarginInlineStart,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-end,
-               border_end,
-               BorderInlineEnd,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-end-color,
-               border_end_color,
-               BorderInlineEndColor,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-end-style,
-               border_end_style,
-               BorderInlineEndStyle,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-end-width,
-               border_end_width,
-               BorderInlineEndWidth,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-start,
-               border_start,
-               BorderInlineStart,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-start-color,
-               border_start_color,
-               BorderInlineStartColor,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-start-style,
-               border_start_style,
-               BorderInlineStartStyle,
-               "layout.css.vertical-text.enabled")
-CSS_PROP_ALIAS(border-inline-start-width,
-               border_start_width,
-               BorderInlineStartWidth,
-               "layout.css.vertical-text.enabled")
+CSS_PROP_ALIAS(-moz-padding-end,
+               padding_inline_end,
+               MozPaddingEnd,
+               "")
+CSS_PROP_ALIAS(-moz-padding-start,
+               padding_inline_start,
+               MozPaddingStart,
+               "")
+CSS_PROP_ALIAS(-moz-margin-end,
+               margin_inline_end,
+               MozMarginEnd,
+               "")
+CSS_PROP_ALIAS(-moz-margin-start,
+               margin_inline_start,
+               MozMarginStart,
+               "")
+CSS_PROP_ALIAS(-moz-border-end,
+               border_inline_end,
+               MozBorderEnd,
+               "")
+CSS_PROP_ALIAS(-moz-border-end-color,
+               border_inline_end_color,
+               MozBorderEndColor,
+               "")
+CSS_PROP_ALIAS(-moz-border-end-style,
+               border_inline_end_style,
+               MozBorderEndStyle,
+               "")
+CSS_PROP_ALIAS(-moz-border-end-width,
+               border_inline_end_width,
+               MozBorderEndWidth,
+               "")
+CSS_PROP_ALIAS(-moz-border-start,
+               border_inline_start,
+               MozBorderStart,
+               "")
+CSS_PROP_ALIAS(-moz-border-start-color,
+               border_inline_start_color,
+               MozBorderStartColor,
+               "")
+CSS_PROP_ALIAS(-moz-border-start-style,
+               border_inline_start_style,
+               MozBorderStartStyle,
+               "")
+CSS_PROP_ALIAS(-moz-border-start-width,
+               border_inline_start_width,
+               MozBorderStartWidth,
+               "")
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -98,17 +98,17 @@
 // exclude internal properties that are not represented in the DOM (only
 // the DOM style code defines this).
 
 // When capturing all properties by defining CSS_PROP, callers must also
 // define one of the following three macros:
 //
 //   CSS_PROP_LIST_EXCLUDE_LOGICAL
 //     Does not include logical properties (defined with CSS_PROP_LOGICAL,
-//     such as -moz-margin-start) when capturing properties to CSS_PROP.
+//     such as margin-inline-start) when capturing properties to CSS_PROP.
 //
 //   CSS_PROP_LIST_INCLUDE_LOGICAL
 //     Does include logical properties when capturing properties to
 //     CSS_PROP.
 //
 //   CSS_PROP_LOGICAL
 //     Captures logical properties separately to CSS_PROP_LOGICAL.
 //
@@ -944,106 +944,106 @@ CSS_PROP_BORDER(
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER,
     "",
     0,
     kBorderImageRepeatKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
-    -moz-border-end,
-    border_end,
-    CSS_PROP_DOMPROP_PREFIXED(BorderEnd),
+    border-inline-end,
+    border_inline_end,
+    BorderInlineEnd,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_LOGICAL(
-    -moz-border-end-color,
-    border_end_color,
-    CSS_PROP_DOMPROP_PREFIXED(BorderEndColor),
+    border-inline-end-color,
+    border_inline_end_color,
+    BorderInlineEndColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_LOGICAL |
         CSS_PROPERTY_LOGICAL_END_EDGE,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
     BorderColor,
     Border,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-border-end-style,
-    border_end_style,
-    CSS_PROP_DOMPROP_PREFIXED(BorderEndStyle),
+    border-inline-end-style,
+    border_inline_end_style,
+    BorderInlineEndStyle,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_LOGICAL |
         CSS_PROPERTY_LOGICAL_END_EDGE,
     "",
     VARIANT_HK,
     kBorderStyleKTable,
     BorderStyle,
     Border,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-border-end-width,
-    border_end_width,
-    CSS_PROP_DOMPROP_PREFIXED(BorderEndWidth),
+    border-inline-end-width,
+    border_inline_end_width,
+    BorderInlineEndWidth,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
         CSS_PROPERTY_LOGICAL |
         CSS_PROPERTY_LOGICAL_END_EDGE,
     "",
     VARIANT_HKL | VARIANT_CALC,
     kBorderWidthKTable,
     BorderWidth,
     Border,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
-    -moz-border-start,
-    border_start,
-    CSS_PROP_DOMPROP_PREFIXED(BorderStart),
+    border-inline-start,
+    border_inline_start,
+    BorderInlineStart,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_LOGICAL(
-    -moz-border-start-color,
-    border_start_color,
-    CSS_PROP_DOMPROP_PREFIXED(BorderStartColor),
+    border-inline-start-color,
+    border_inline_start_color,
+    BorderInlineStartColor,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_LOGICAL,
     "",
     VARIANT_HCK,
     kBorderColorKTable,
     BorderColor,
     Border,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-border-start-style,
-    border_start_style,
-    CSS_PROP_DOMPROP_PREFIXED(BorderStartStyle),
+    border-inline-start-style,
+    border_inline_start_style,
+    BorderInlineStartStyle,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_LOGICAL,
     "",
     VARIANT_HK,
     kBorderStyleKTable,
     BorderStyle,
     Border,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-border-start-width,
-    border_start_width,
-    CSS_PROP_DOMPROP_PREFIXED(BorderStartWidth),
+    border-inline-start-width,
+    border_inline_start_width,
+    BorderInlineStartWidth,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_LOGICAL,
     "",
     VARIANT_HKL | VARIANT_CALC,
     kBorderWidthKTable,
@@ -2313,36 +2313,36 @@ CSS_PROP_MARGIN(
         CSS_PROPERTY_APPLIES_TO_PAGE_RULE |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
     nullptr,
     offsetof(nsStyleMargin, mMargin),
     eStyleAnimType_Sides_Bottom)
 CSS_PROP_LOGICAL(
-    -moz-margin-end,
-    margin_end,
-    CSS_PROP_DOMPROP_PREFIXED(MarginEnd),
+    margin-inline-end,
+    margin_inline_end,
+    MarginInlineEnd,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_APPLIES_TO_PAGE_RULE |
         CSS_PROPERTY_LOGICAL |
         CSS_PROPERTY_LOGICAL_END_EDGE,
     "",
     VARIANT_AHLP | VARIANT_CALC,
     nullptr,
     Margin,
     Margin,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-margin-start,
-    margin_start,
-    CSS_PROP_DOMPROP_PREFIXED(MarginStart),
+    margin-inline-start,
+    margin_inline_start,
+    MarginInlineStart,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_APPLIES_TO_PAGE_RULE |
         CSS_PROPERTY_LOGICAL,
     "",
     VARIANT_AHLP | VARIANT_CALC,
     nullptr,
@@ -2841,19 +2841,19 @@ CSS_PROP_PADDING(
         CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
     nullptr,
     offsetof(nsStylePadding, mPadding),
     eStyleAnimType_Sides_Bottom)
 CSS_PROP_LOGICAL(
-    -moz-padding-end,
-    padding_end,
-    CSS_PROP_DOMPROP_PREFIXED(PaddingEnd),
+    padding-inline-end,
+    padding_inline_end,
+    PaddingInlineEnd,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         // This is required by the UA stylesheet and can't be overridden.
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
         CSS_PROPERTY_LOGICAL |
@@ -2861,19 +2861,19 @@ CSS_PROP_LOGICAL(
     "",
     VARIANT_HLP | VARIANT_CALC,
     nullptr,
     Padding,
     Padding,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_LOGICAL(
-    -moz-padding-start,
-    padding_start,
-    CSS_PROP_DOMPROP_PREFIXED(PaddingStart),
+    padding-inline-start,
+    padding_inline_start,
+    PaddingInlineStart,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         // This is required by the UA stylesheet and can't be overridden.
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
         CSS_PROPERTY_LOGICAL,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -2373,22 +2373,22 @@ static const nsCSSProperty gBorderColorS
   // Code relies on these matching the NS_SIDE_* constants.
   eCSSProperty_border_top_color,
   eCSSProperty_border_right_color,
   eCSSProperty_border_bottom_color,
   eCSSProperty_border_left_color,
   eCSSProperty_UNKNOWN
 };
 
-static const nsCSSProperty gBorderEndSubpropTable[] = {
+static const nsCSSProperty gBorderInlineEndSubpropTable[] = {
   // Declaration.cpp output the subproperties in this order.
   // It also depends on the color being third.
-  eCSSProperty_border_end_width,
-  eCSSProperty_border_end_style,
-  eCSSProperty_border_end_color,
+  eCSSProperty_border_inline_end_width,
+  eCSSProperty_border_inline_end_style,
+  eCSSProperty_border_inline_end_color,
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSProperty gBorderLeftSubpropTable[] = {
   // Declaration.cpp outputs the subproperties in this order.
   // It also depends on the color being third.
   eCSSProperty_border_left_width,
   eCSSProperty_border_left_style,
@@ -2400,22 +2400,22 @@ static const nsCSSProperty gBorderRightS
   // Declaration.cpp outputs the subproperties in this order.
   // It also depends on the color being third.
   eCSSProperty_border_right_width,
   eCSSProperty_border_right_style,
   eCSSProperty_border_right_color,
   eCSSProperty_UNKNOWN
 };
 
-static const nsCSSProperty gBorderStartSubpropTable[] = {
+static const nsCSSProperty gBorderInlineStartSubpropTable[] = {
   // Declaration.cpp outputs the subproperties in this order.
   // It also depends on the color being third.
-  eCSSProperty_border_start_width,
-  eCSSProperty_border_start_style,
-  eCSSProperty_border_start_color,
+  eCSSProperty_border_inline_start_width,
+  eCSSProperty_border_inline_start_style,
+  eCSSProperty_border_inline_start_color,
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSProperty gBorderStyleSubpropTable[] = {
   // Code relies on these being in top-right-bottom-left order.
   eCSSProperty_border_top_style,
   eCSSProperty_border_right_style,
   eCSSProperty_border_bottom_style,
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -9370,17 +9370,17 @@ nsRuleNode::SweepChildren(nsTArray<nsRul
                "missing DestroyIfNotMarked() call");
   NS_ASSERTION(HaveChildren(),
                "why call SweepChildren with no children?");
   uint32_t childrenDestroyed = 0;
   nsRuleNode* survivorsWithChildren = nullptr;
   if (ChildrenAreHashed()) {
     PLDHashTable* children = ChildrenHash();
     uint32_t oldChildCount = children->EntryCount();
-    for (auto iter = children->RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = children->Iter(); !iter.Done(); iter.Next()) {
       auto entry = static_cast<ChildrenHashEntry*>(iter.Get());
       nsRuleNode* node = entry->mRuleNode;
       if (node->DestroyIfNotMarked()) {
         iter.Remove();
       } else if (node->HaveChildren()) {
         // When children are hashed mNextSibling is not normally used but we
         // use it here to build a list of children that needs to be swept.
         nsRuleNode** headQ = &survivorsWithChildren;
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -683,53 +683,53 @@ var gCSSProperties = {
   "-moz-border-bottom-colors": {
     domProp: "MozBorderBottomColors",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ],
     invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ]
   },
-  "-moz-border-end": {
-    domProp: "MozBorderEnd",
+  "border-inline-end": {
+    domProp: "borderInlineEnd",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
-    subproperties: [ "-moz-border-end-color", "-moz-border-end-style", "-moz-border-end-width" ],
+    subproperties: [ "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ],
     initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
     other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
     invalid_values: [ "5%", "5", "5 green none" ]
   },
-  "-moz-border-end-color": {
-    domProp: "MozBorderEndColor",
+  "border-inline-end-color": {
+    domProp: "borderInlineEndColor",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     initial_values: [ "currentColor" ],
     other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
     invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
   },
-  "-moz-border-end-style": {
-    domProp: "MozBorderEndStyle",
+  "border-inline-end-style": {
+    domProp: "borderInlineEndStyle",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* XXX hidden is sometimes the same as initial */
     initial_values: [ "none" ],
     other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
     invalid_values: []
   },
-  "-moz-border-end-width": {
-    domProp: "MozBorderEndWidth",
+  "border-inline-end-width": {
+    domProp: "borderInlineEndWidth",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
-    prerequisites: { "-moz-border-end-style": "solid" },
+    prerequisites: { "border-inline-end-style": "solid" },
     initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
     other_values: [ "thin", "thick", "1px", "2em",
       "calc(2px)",
       "calc(-2px)",
       "calc(0em)",
       "calc(0px)",
       "calc(5em)",
       "calc(3*25px)",
@@ -929,53 +929,53 @@ var gCSSProperties = {
   "-moz-border-right-colors": {
     domProp: "MozBorderRightColors",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ],
     invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ]
   },
-  "-moz-border-start": {
-    domProp: "MozBorderStart",
+  "border-inline-start": {
+    domProp: "borderInlineStart",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
-    subproperties: [ "-moz-border-start-color", "-moz-border-start-style", "-moz-border-start-width" ],
+    subproperties: [ "border-inline-start-color", "border-inline-start-style", "border-inline-start-width" ],
     initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
     other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
     invalid_values: [ "5%", "5", "5 green solid" ]
   },
-  "-moz-border-start-color": {
-    domProp: "MozBorderStartColor",
+  "border-inline-start-color": {
+    domProp: "borderInlineStartColor",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     initial_values: [ "currentColor" ],
     other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
     invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
   },
-  "-moz-border-start-style": {
-    domProp: "MozBorderStartStyle",
+  "border-inline-start-style": {
+    domProp: "borderInlineStartStyle",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* XXX hidden is sometimes the same as initial */
     initial_values: [ "none" ],
     other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
     invalid_values: []
   },
-  "-moz-border-start-width": {
-    domProp: "MozBorderStartWidth",
+  "border-inline-start-width": {
+    domProp: "borderInlineStartWidth",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
-    prerequisites: { "-moz-border-start-style": "solid" },
+    prerequisites: { "border-inline-start-style": "solid" },
     initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
     other_values: [ "thin", "thick", "1px", "2em",
       "calc(2px)",
       "calc(-2px)",
       "calc(0em)",
       "calc(0px)",
       "calc(5em)",
       "calc(3*25px)",
@@ -1433,18 +1433,18 @@ var gCSSProperties = {
   "-moz-image-region": {
     domProp: "MozImageRegion",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "auto" ],
     other_values: [ "rect(3px 20px 15px 4px)", "rect(17px, 21px, 33px, 2px)" ],
     invalid_values: [ "rect(17px, 21px, 33, 2px)" ]
   },
-  "-moz-margin-end": {
-    domProp: "MozMarginEnd",
+  "margin-inline-end": {
+    domProp: "marginInlineEnd",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* no subproperties */
     /* auto may or may not be initial */
     initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)" ],
     other_values: [ "1px", "3em",
@@ -1452,18 +1452,18 @@ var gCSSProperties = {
       "calc(-2px)",
       "calc(50%)",
       "calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
     ],
     invalid_values: [ "5" ]
   },
-  "-moz-margin-start": {
-    domProp: "MozMarginStart",
+  "margin-inline-start": {
+    domProp: "marginInlineStart",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* no subproperties */
     /* auto may or may not be initial */
     initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)" ],
     other_values: [ "1px", "3em",
@@ -1569,35 +1569,35 @@ var gCSSProperties = {
       "calc(3*25px) 5px",
       "5px calc(3*25px)",
       "calc(20%) calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
             ],
     invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ]
   },
-  "-moz-padding-end": {
-    domProp: "MozPaddingEnd",
+  "padding-inline-end": {
+    domProp: "paddingInlineEnd",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* no subproperties */
     initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
     other_values: [ "1px", "3em",
       "calc(2px)",
       "calc(50%)",
       "calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
     ],
     invalid_values: [ "5" ]
   },
-  "-moz-padding-start": {
-    domProp: "MozPaddingStart",
+  "padding-inline-start": {
+    domProp: "paddingInlineStart",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     get_computed: logical_box_prop_get_computed,
     /* no subproperties */
     initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
     other_values: [ "1px", "3em",
       "calc(2px)",
@@ -4829,105 +4829,105 @@ if (SpecialPowers.getBoolPref("layout.cs
         "calc(0px)",
         "calc(5em)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 5em)",
       ],
       invalid_values: [ "5%", "5" ]
     },
-    "border-inline-end": {
-      domProp: "borderInlineEnd",
+    "-moz-border-end": {
+      domProp: "MozBorderEnd",
       inherited: false,
       type: CSS_TYPE_TRUE_SHORTHAND,
-      alias_for: "-moz-border-end",
-      subproperties: [ "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ],
+      alias_for: "border-inline-end",
+      subproperties: [ "-moz-border-end-color", "-moz-border-end-style", "-moz-border-end-width" ],
       initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
       other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
       invalid_values: [ "5%", "5", "5 green none" ]
     },
-    "border-inline-end-color": {
-      domProp: "borderInlineEndColor",
+    "-moz-border-end-color": {
+      domProp: "MozBorderEndColor",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-end-color",
+      alias_for: "border-inline-end-color",
       get_computed: logical_box_prop_get_computed,
       initial_values: [ "currentColor" ],
       other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
       invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
     },
-    "border-inline-end-style": {
-      domProp: "borderInlineEndStyle",
+    "-moz-border-end-style": {
+      domProp: "MozBorderEndStyle",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-end-style",
+      alias_for: "border-inline-end-style",
       get_computed: logical_box_prop_get_computed,
       /* XXX hidden is sometimes the same as initial */
       initial_values: [ "none" ],
       other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
       invalid_values: []
     },
-    "border-inline-end-width": {
-      domProp: "borderInlineEndWidth",
+    "-moz-border-end-width": {
+      domProp: "MozBorderEndWidth",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-end-width",
+      alias_for: "border-inline-end-width",
       get_computed: logical_box_prop_get_computed,
-      prerequisites: { "border-inline-end-style": "solid" },
+      prerequisites: { "-moz-border-end-style": "solid" },
       initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
       other_values: [ "thin", "thick", "1px", "2em",
         "calc(2px)",
         "calc(-2px)",
         "calc(0em)",
         "calc(0px)",
         "calc(5em)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 5em)",
       ],
       invalid_values: [ "5%", "5" ]
     },
-    "border-inline-start": {
-      domProp: "borderInlineStart",
+    "-moz-border-start": {
+      domProp: "MozBorderStart",
       inherited: false,
       type: CSS_TYPE_TRUE_SHORTHAND,
-      alias_for: "-moz-border-start",
-      subproperties: [ "border-inline-start-color", "border-inline-start-style", "border-inline-start-width" ],
+      alias_for: "border-inline-start",
+      subproperties: [ "-moz-border-start-color", "-moz-border-start-style", "-moz-border-start-width" ],
       initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
       other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
       invalid_values: [ "5%", "5", "5 green solid" ]
     },
-    "border-inline-start-color": {
-      domProp: "borderInlineStartColor",
+    "-moz-border-start-color": {
+      domProp: "MozBorderStartColor",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-start-color",
+      alias_for: "border-inline-start-color",
       get_computed: logical_box_prop_get_computed,
       initial_values: [ "currentColor" ],
       other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
       invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000" ]
     },
-    "border-inline-start-style": {
-      domProp: "borderInlineStartStyle",
+    "-moz-border-start-style": {
+      domProp: "MozBorderStartStyle",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-start-style",
+      alias_for: "border-inline-start-style",
       get_computed: logical_box_prop_get_computed,
       /* XXX hidden is sometimes the same as initial */
       initial_values: [ "none" ],
       other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ],
       invalid_values: []
     },
-    "border-inline-start-width": {
-      domProp: "borderInlineStartWidth",
+    "-moz-border-start-width": {
+      domProp: "MozBorderStartWidth",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-border-start-width",
+      alias_for: "border-inline-start-width",
       get_computed: logical_box_prop_get_computed,
-      prerequisites: { "border-inline-start-style": "solid" },
+      prerequisites: { "-moz-border-start-style": "solid" },
       initial_values: [ "medium", "3px", "calc(4px - 1px)" ],
       other_values: [ "thin", "thick", "1px", "2em",
         "calc(2px)",
         "calc(-2px)",
         "calc(0em)",
         "calc(0px)",
         "calc(5em)",
         "calc(3*25px)",
@@ -4994,39 +4994,39 @@ if (SpecialPowers.getBoolPref("layout.cs
         "calc(-2px)",
         "calc(50%)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 50%)",
       ],
       invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
     },
-    "margin-inline-end": {
-      domProp: "marginInlineEnd",
+    "-moz-margin-end": {
+      domProp: "MozMarginEnd",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-margin-end",
+      alias_for: "margin-inline-end",
       get_computed: logical_box_prop_get_computed,
       /* XXX testing auto has prerequisites */
       initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
       other_values: [ "1px", "2em", "5%",
         "calc(2px)",
         "calc(-2px)",
         "calc(50%)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 50%)",
       ],
       invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ],
     },
-    "margin-inline-start": {
-      domProp: "marginInlineStart",
+    "-moz-margin-start": {
+      domProp: "MozMarginStart",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-margin-start",
+      alias_for: "margin-inline-start",
       get_computed: logical_box_prop_get_computed,
       /* XXX testing auto has prerequisites */
       initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ],
       other_values: [ "1px", "2em", "5%",
         "calc(2px)",
         "calc(-2px)",
         "calc(50%)",
         "calc(3*25px)",
@@ -5222,37 +5222,37 @@ if (SpecialPowers.getBoolPref("layout.cs
         "calc(2px)",
         "calc(50%)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 50%)",
       ],
       invalid_values: [ ],
     },
-    "padding-inline-end": {
-      domProp: "paddingInlineEnd",
+    "-moz-padding-end": {
+      domProp: "MozPaddingEnd",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-padding-end",
+      alias_for: "padding-inline-end",
       get_computed: logical_box_prop_get_computed,
       initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
       other_values: [ "1px", "2em", "5%",
         "calc(2px)",
         "calc(50%)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 50%)",
       ],
       invalid_values: [ ],
     },
-    "padding-inline-start": {
-      domProp: "paddingInlineStart",
+    "-moz-padding-start": {
+      domProp: "MozPaddingStart",
       inherited: false,
       type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
-      alias_for: "-moz-padding-start",
+      alias_for: "padding-inline-start",
       get_computed: logical_box_prop_get_computed,
       initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ],
       other_values: [ "1px", "2em", "5%",
         "calc(2px)",
         "calc(50%)",
         "calc(3*25px)",
         "calc(25px*3)",
         "calc(3*25px + 50%)",
--- a/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
+++ b/media/gmp-clearkey/0.1/ClearKeyAsyncShutdown.h
@@ -12,17 +12,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyAsyncShutdown_h__
 #define __ClearKeyAsyncShutdown_h__
 
-#include "gmp-async-shutdown.h"
+#include "gmp-api/gmp-async-shutdown.h"
 #include "RefCounted.h"
 
 class ClearKeyAsyncShutdown : public GMPAsyncShutdown
                             , public RefCounted
 {
 public:
   explicit ClearKeyAsyncShutdown(GMPAsyncShutdownHost *aHostAPI);
 
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include <string.h>
 #include <vector>
 
 #include "ClearKeyDecryptionManager.h"
-#include "gmp-decryption.h"
+#include "gmp-api/gmp-decryption.h"
 #include <assert.h>
 
 class ClearKeyDecryptor : public RefCounted
 {
 public:
   ClearKeyDecryptor();
 
   void InitKey(const Key& aKey);
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyPersistence_h__
 #define __ClearKeyPersistence_h__
 
 #include <string>
-#include "gmp-decryption.h"
+#include "gmp-api/gmp-decryption.h"
 
 class ClearKeySessionManager;
 
 class ClearKeyPersistence {
 public:
   static void EnsureInitialized();
 
   static std::string GetNewSessionId(GMPSessionType aSessionType);
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeySession.h"
 #include "ClearKeyUtils.h"
 #include "ClearKeyStorage.h"
 #include "gmp-task-utils.h"
-
 #include "gmp-api/gmp-decryption.h"
 #include "Endian.h"
 #include <assert.h>
 #include <string.h>
 
 using namespace mozilla;
 
 ClearKeySession::ClearKeySession(const std::string& aSessionId,
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeySession_h__
 #define __ClearKeySession_h__
 
 #include "ClearKeyUtils.h"
-#include "gmp-decryption.h"
+#include "gmp-api/gmp-decryption.h"
 
 class GMPBuffer;
 class GMPDecryptorCallback;
 class GMPDecryptorHost;
 class GMPEncryptedBufferMetadata;
 
 class ClearKeySession
 {
--- a/media/gmp-clearkey/0.1/ClearKeyStorage.h
+++ b/media/gmp-clearkey/0.1/ClearKeyStorage.h
@@ -12,18 +12,18 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __ClearKeyStorage_h__
 #define __ClearKeyStorage_h__
 
-#include "gmp-errors.h"
-#include "gmp-platform.h"
+#include "gmp-api/gmp-errors.h"
+#include "gmp-api/gmp-platform.h"
 #include <string>
 #include <vector>
 #include <stdint.h>
 
 class GMPTask;
 
 // Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
 void StoreData(const std::string& aRecordName,
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.h
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h
@@ -16,17 +16,17 @@
 
 #ifndef __ClearKeyUtils_h__
 #define __ClearKeyUtils_h__
 
 #include <stdint.h>
 #include <string>
 #include <vector>
 #include <assert.h>
-#include "gmp-decryption.h"
+#include "gmp-api/gmp-decryption.h"
 
 #define CLEARKEY_KEY_LEN ((size_t)16)
 
 #if 0
 void CK_Log(const char* aFmt, ...);
 #define CK_LOGE(...) CK_Log(__VA_ARGS__)
 #define CK_LOGD(...) CK_Log(__VA_ARGS__)
 #define CK_LOGW(...) CK_Log(__VA_ARGS__)
--- a/media/gmp-clearkey/0.1/gmp-task-utils.h
+++ b/media/gmp-clearkey/0.1/gmp-task-utils.h
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
 // Original author: ekr@rtfm.com
 
 #ifndef gmp_task_utils_h_
 #define gmp_task_utils_h_
 
-#include "gmp-platform.h"
+#include "gmp-api/gmp-platform.h"
 
 class gmp_task_args_base : public GMPTask {
 public:
   virtual void Destroy() { delete this; }
   virtual void Run() = 0;
 };
 
 // The generated file contains four major function templates
--- a/media/libsoundtouch/README_MOZILLA
+++ b/media/libsoundtouch/README_MOZILLA
@@ -1,8 +1,8 @@
 These files are from the SoundTouch library (http://www.surina.net/soundtouch/),
-and are extracted from the revision r198 of the svn repository at
+and are extracted from the revision r222 of the svn repository at
 https://soundtouch.svn.sourceforge.net/svnroot/soundtouch/trunk.
 
 The whole library is not used, only the relevant files are imported in the tree,
 using the script `update.sh`. Some changes have been made to the files, using
 the patch `moz-libsoundtouch.patch`. We also use a custom soundtouch_config.h.
 
--- a/media/libsoundtouch/moz-libsoundtouch.patch
+++ b/media/libsoundtouch/moz-libsoundtouch.patch
@@ -51,75 +51,50 @@ diff -u /src/cpu_detect_x86.cpp /src/cpu
 +    // Compatible with GCC but no cpuid.h.
 +    return 0;
  #endif
  
      return res & ~_dwDisabledISA;
 diff -u /src/STTypes.h /src/STTypes.h
 --- /src/STTypes.h
 +++ /src/STTypes.h
-@@ -54,12 +54,17 @@
+@@ -54,12 +54,13 @@
  #define SOUNDTOUCH_ALIGN_POINTER_16(x)      ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
  
  
 -#if (defined(__GNUC__) && !defined(ANDROID))
 -    // In GCC, include soundtouch_config.h made by config scritps.
 -    // Skip this in Android compilation that uses GCC but without configure scripts.
 -    #include "soundtouch_config.h"
 -#endif
 +#include "soundtouch_config.h"
  
-+#if defined(WIN32) && defined(GKMEDIAS_SHARED_LIBRARY)
-+#ifdef BUILDING_SOUNDTOUCH
++#if defined(WIN32)
 +#define EXPORT __declspec(dllexport)
 +#else
-+#define EXPORT __declspec(dllimport)
-+#endif
-+#else
 +#define EXPORT
 +#endif
  
  namespace soundtouch
  {
-@@ -164,7 +169,7 @@
- };
- 
- // define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
--// #define ST_NO_EXCEPTION_HANDLING    1
-+#define ST_NO_EXCEPTION_HANDLING    1
- #ifdef ST_NO_EXCEPTION_HANDLING
-     // Exceptions disabled. Throw asserts instead if enabled.
-     #include <assert.h>
 diff -u /src/SoundTouch.h /src/SoundTouch.h
 --- /src/SoundTouch.h
 +++ /src/SoundTouch.h
 @@ -141,7 +141,7 @@
  ///   tempo/pitch/rate/samplerate settings.
  #define SETTING_NOMINAL_OUTPUT_SEQUENCE		7
  
 -class SoundTouch : public FIFOProcessor
 +class EXPORT SoundTouch : public FIFOProcessor
  {
  private:
      /// Rate transposer class instance
 diff -u /src/FIRFilter.cpp /src/FIRFilter.cpp
 --- /src/FIRFilter.cpp
 +++ /src/FIRFilter.cpp
-@@ -46,6 +46,11 @@
- #include "FIRFilter.h"
- #include "cpu_detect.h"
- 
-+#ifdef _MSC_VER
-+#include <malloc.h>
-+#define alloca _alloca
-+#endif
-+
- using namespace soundtouch;
- 
- /*****************************************************************************
 @@ -291,9 +296,11 @@
  
  FIRFilter * FIRFilter::newInstance()
  {
 +#if defined(SOUNDTOUCH_ALLOW_MMX) || defined(SOUNDTOUCH_ALLOW_SSE)
      uint uExtensions;
  
      uExtensions = detectCPUextensions();
@@ -138,22 +113,8 @@ diff -u /src/TDStretch.cpp /src/TDStretc
      uint uExtensions;
  
      uExtensions = detectCPUextensions();
 +#endif
  
      // Check if MMX/SSE instruction set extensions supported by CPU
  
 diff -u /src/SoundTouch.cpp /src/SoundTouch.cpp
---- /src/SoundTouch.cpp
-+++ /src/SoundTouch.cpp
-@@ -80,6 +80,11 @@
- #include "RateTransposer.h"
- #include "cpu_detect.h"
- 
-+#ifdef _MSC_VER
-+#include <malloc.h>
-+#define alloca _alloca
-+#endif
-+
- using namespace soundtouch;
-     
- /// test if two floating point numbers are equal
--- a/media/libsoundtouch/src/AAFilter.cpp
+++ b/media/libsoundtouch/src/AAFilter.cpp
@@ -7,17 +7,17 @@
 /// transposing the sample rate with interpolation.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-05 15:40:22 -0600 (Sun, 05 Jan 2014) $
+// Last changed  : $Date: 2014-01-05 21:40:22 +0000 (Sun, 05 Jan 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/AAFilter.h
+++ b/media/libsoundtouch/src/AAFilter.h
@@ -8,17 +8,17 @@
 /// transposing the sample rate with interpolation.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-07 13:41:23 -0600 (Tue, 07 Jan 2014) $
+// Last changed  : $Date: 2014-01-07 19:41:23 +0000 (Tue, 07 Jan 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/FIFOSampleBuffer.cpp
+++ b/media/libsoundtouch/src/FIFOSampleBuffer.cpp
@@ -10,17 +10,17 @@
 /// whenever necessary.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2012-11-08 12:53:01 -0600 (Thu, 08 Nov 2012) $
+// Last changed  : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
 // File revision : $Revision: 4 $
 //
 // $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/FIFOSampleBuffer.h
+++ b/media/libsoundtouch/src/FIFOSampleBuffer.h
@@ -10,17 +10,17 @@
 /// whenever necessary.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-05 15:40:22 -0600 (Sun, 05 Jan 2014) $
+// Last changed  : $Date: 2014-01-05 21:40:22 +0000 (Sun, 05 Jan 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/FIFOSamplePipe.h
+++ b/media/libsoundtouch/src/FIFOSamplePipe.h
@@ -12,17 +12,17 @@
 /// may be either another processing stage, or a fifo sample buffer object.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2012-06-13 14:29:53 -0500 (Wed, 13 Jun 2012) $
+// Last changed  : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
 // File revision : $Revision: 4 $
 //
 // $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/FIRFilter.cpp
+++ b/media/libsoundtouch/src/FIRFilter.cpp
@@ -6,20 +6,20 @@
 /// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2013-06-12 10:24:44 -0500 (Wed, 12 Jun 2013) $
+// Last changed  : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
 // File revision : $Revision: 4 $
 //
-// $Id: FIRFilter.cpp 171 2013-06-12 15:24:44Z oparviai $
+// $Id: FIRFilter.cpp 202 2015-02-21 21:24:29Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -41,21 +41,16 @@
 
 #include <memory.h>
 #include <assert.h>
 #include <math.h>
 #include <stdlib.h>
 #include "FIRFilter.h"
 #include "cpu_detect.h"
 
-#ifdef _MSC_VER
-#include <malloc.h>
-#define alloca _alloca
-#endif
-
 using namespace soundtouch;
 
 /*****************************************************************************
  *
  * Implementation of the class 'FIRFilter'
  *
  *****************************************************************************/
 
@@ -72,34 +67,36 @@ FIRFilter::FIRFilter()
 FIRFilter::~FIRFilter()
 {
     delete[] filterCoeffs;
 }
 
 // Usual C-version of the filter routine for stereo sound
 uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
 {
-    uint i, j, end;
-    LONG_SAMPLETYPE suml, sumr;
+    int j, end;
 #ifdef SOUNDTOUCH_FLOAT_SAMPLES
     // when using floating point samples, use a scaler instead of a divider
     // because division is much slower operation than multiplying.
     double dScaler = 1.0 / (double)resultDivider;
 #endif
 
     assert(length != 0);
     assert(src != NULL);
     assert(dest != NULL);
     assert(filterCoeffs != NULL);
 
     end = 2 * (numSamples - length);
 
+    #pragma omp parallel for
     for (j = 0; j < end; j += 2) 
     {
         const SAMPLETYPE *ptr;
+        LONG_SAMPLETYPE suml, sumr;
+        uint i;
 
         suml = sumr = 0;
         ptr = src + j;
 
         for (i = 0; i < length; i += 4) 
         {
             // loop is unrolled by factor of 4 here for efficiency
             suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
@@ -130,101 +127,105 @@ uint FIRFilter::evaluateFilterStereo(SAM
 }
 
 
 
 
 // Usual C-version of the filter routine for mono sound
 uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
 {
-    uint i, j, end;
-    LONG_SAMPLETYPE sum;
+    int j, end;
 #ifdef SOUNDTOUCH_FLOAT_SAMPLES
     // when using floating point samples, use a scaler instead of a divider
     // because division is much slower operation than multiplying.
     double dScaler = 1.0 / (double)resultDivider;
 #endif
 
-
     assert(length != 0);
 
     end = numSamples - length;
+    #pragma omp parallel for
     for (j = 0; j < end; j ++) 
     {
+        const SAMPLETYPE *pSrc = src + j;
+        LONG_SAMPLETYPE sum;
+        uint i;
+
         sum = 0;
         for (i = 0; i < length; i += 4) 
         {
             // loop is unrolled by factor of 4 here for efficiency
-            sum += src[i + 0] * filterCoeffs[i + 0] + 
-                   src[i + 1] * filterCoeffs[i + 1] + 
-                   src[i + 2] * filterCoeffs[i + 2] + 
-                   src[i + 3] * filterCoeffs[i + 3];
+            sum += pSrc[i + 0] * filterCoeffs[i + 0] + 
+                   pSrc[i + 1] * filterCoeffs[i + 1] + 
+                   pSrc[i + 2] * filterCoeffs[i + 2] + 
+                   pSrc[i + 3] * filterCoeffs[i + 3];
         }
 #ifdef SOUNDTOUCH_INTEGER_SAMPLES
         sum >>= resultDivFactor;
         // saturate to 16 bit integer limits
         sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
 #else
         sum *= dScaler;
 #endif // SOUNDTOUCH_INTEGER_SAMPLES
         dest[j] = (SAMPLETYPE)sum;
-        src ++;
     }
     return end;
 }
 
 
-uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
 {
-    uint i, j, end, c;
-    LONG_SAMPLETYPE *sum=(LONG_SAMPLETYPE*)alloca(numChannels*sizeof(*sum));
+    int j, end;
+
 #ifdef SOUNDTOUCH_FLOAT_SAMPLES
     // when using floating point samples, use a scaler instead of a divider
     // because division is much slower operation than multiplying.
     double dScaler = 1.0 / (double)resultDivider;
 #endif
 
     assert(length != 0);
     assert(src != NULL);
     assert(dest != NULL);
     assert(filterCoeffs != NULL);
+    assert(numChannels < 16);
 
     end = numChannels * (numSamples - length);
 
-    for (c = 0; c < numChannels; c ++)
-    {
-        sum[c] = 0;
-    }
-
+    #pragma omp parallel for
     for (j = 0; j < end; j += numChannels)
     {
         const SAMPLETYPE *ptr;
+        LONG_SAMPLETYPE sums[16];
+        uint c, i;
+
+        for (c = 0; c < numChannels; c ++)
+        {
+            sums[c] = 0;
+        }
 
         ptr = src + j;
 
         for (i = 0; i < length; i ++)
         {
             SAMPLETYPE coef=filterCoeffs[i];
             for (c = 0; c < numChannels; c ++)
             {
-                sum[c] += ptr[0] * coef;
+                sums[c] += ptr[0] * coef;
                 ptr ++;
             }
         }
         
         for (c = 0; c < numChannels; c ++)
         {
 #ifdef SOUNDTOUCH_INTEGER_SAMPLES
-            sum[c] >>= resultDivFactor;
+            sums[c] >>= resultDivFactor;
 #else
-            sum[c] *= dScaler;
+            sums[c] *= dScaler;
 #endif // SOUNDTOUCH_INTEGER_SAMPLES
-            *dest = (SAMPLETYPE)sum[c];
-            dest++;
-            sum[c] = 0;
+            dest[j+c] = (SAMPLETYPE)sums[c];
         }
     }
     return numSamples - length;
 }
 
 
 // Set filter coeffiecients and length.
 //
@@ -253,17 +254,17 @@ uint FIRFilter::getLength() const
 }
 
 
 
 // Applies the filter to the given sequence of samples. 
 //
 // Note : The amount of outputted samples is by value of 'filter_length' 
 // smaller than the amount of input samples.
-uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) 
 {
     assert(length > 0);
     assert(lengthDiv8 * 8 == length);
 
     if (numSamples < length) return 0;
 
 #ifndef USE_MULTICH_ALWAYS
     if (numChannels == 1)
--- a/media/libsoundtouch/src/FIRFilter.h
+++ b/media/libsoundtouch/src/FIRFilter.h
@@ -6,20 +6,20 @@
 /// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2013-06-12 10:24:44 -0500 (Wed, 12 Jun 2013) $
+// Last changed  : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
 // File revision : $Revision: 4 $
 //
-// $Id: FIRFilter.h 171 2013-06-12 15:24:44Z oparviai $
+// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -66,17 +66,17 @@ protected:
     SAMPLETYPE *filterCoeffs;
 
     virtual uint evaluateFilterStereo(SAMPLETYPE *dest, 
                                       const SAMPLETYPE *src, 
                                       uint numSamples) const;
     virtual uint evaluateFilterMono(SAMPLETYPE *dest, 
                                     const SAMPLETYPE *src, 
                                     uint numSamples) const;
-    virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const;
+    virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
 
 public:
     FIRFilter();
     virtual ~FIRFilter();
 
     /// Operator 'new' is overloaded so that it automatically creates a suitable instance 
     /// depending on if we've a MMX-capable CPU available or not.
     static void * operator new(size_t s);
@@ -86,17 +86,17 @@ public:
     /// Applies the filter to the given sequence of samples. 
     /// Note : The amount of outputted samples is by value of 'filter_length' 
     /// smaller than the amount of input samples.
     ///
     /// \return Number of samples copied to 'dest'.
     uint evaluate(SAMPLETYPE *dest, 
                   const SAMPLETYPE *src, 
                   uint numSamples, 
-                  uint numChannels) const;
+                  uint numChannels);
 
     uint getLength() const;
 
     virtual void setCoefficients(const SAMPLETYPE *coeffs, 
                                  uint newLength, 
                                  uint uResultDivFactor);
 };
 
--- a/media/libsoundtouch/src/RateTransposer.cpp
+++ b/media/libsoundtouch/src/RateTransposer.cpp
@@ -5,17 +5,17 @@
 /// alias filtering should be quite adequate for this application)
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: RateTransposer.cpp 195 2014-04-06 15:57:21Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/RateTransposer.h
+++ b/media/libsoundtouch/src/RateTransposer.h
@@ -9,17 +9,17 @@
 /// algorithm implementation.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: RateTransposer.h 195 2014-04-06 15:57:21Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/STTypes.h
+++ b/media/libsoundtouch/src/STTypes.h
@@ -3,20 +3,20 @@
 /// Common type definitions for SoundTouch audio processing library.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2015-05-18 15:25:07 +0000 (Mon, 18 May 2015) $
 // File revision : $Revision: 3 $
 //
-// $Id: STTypes.h 195 2014-04-06 15:57:21Z oparviai $
+// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -51,23 +51,19 @@ typedef unsigned long   ulong;
 
 
 // Helper macro for aligning pointer up to next 16-byte boundary
 #define SOUNDTOUCH_ALIGN_POINTER_16(x)      ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
 
 
 #include "soundtouch_config.h"
 
-#if defined(WIN32) && defined(GKMEDIAS_SHARED_LIBRARY)
-#ifdef BUILDING_SOUNDTOUCH
+#if defined(WIN32)
 #define EXPORT __declspec(dllexport)
 #else
-#define EXPORT __declspec(dllimport)
-#endif
-#else
 #define EXPORT
 #endif
 
 namespace soundtouch
 {
     /// Activate these undef's to overrule the possible sampletype 
     /// setting inherited from some other header file:
     //#undef SOUNDTOUCH_INTEGER_SAMPLES
@@ -75,17 +71,17 @@ namespace soundtouch
 
     /// If following flag is defined, always uses multichannel processing 
     /// routines also for mono and stero sound. This is for routine testing 
     /// purposes; output should be same with either routines, yet disabling 
     /// the dedicated mono/stereo processing routines will result in slower 
     /// runtime performance so recommendation is to keep this off.
     // #define USE_MULTICH_ALWAYS
 
-    #if (defined(__SOFTFP__))
+    #if (defined(__SOFTFP__) && defined(ANDROID))
         // For Android compilation: Force use of Integer samples in case that
         // compilation uses soft-floating point emulation - soft-fp is way too slow
         #undef  SOUNDTOUCH_FLOAT_SAMPLES
         #define SOUNDTOUCH_INTEGER_SAMPLES      1
     #endif
 
     #if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
        
@@ -164,24 +160,25 @@ namespace soundtouch
             #define SOUNDTOUCH_ALLOW_SSE       1
         #endif
 
     #endif  // SOUNDTOUCH_INTEGER_SAMPLES
 
 };
 
 // define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
-#define ST_NO_EXCEPTION_HANDLING    1
+// #define ST_NO_EXCEPTION_HANDLING    1
 #ifdef ST_NO_EXCEPTION_HANDLING
     // Exceptions disabled. Throw asserts instead if enabled.
     #include <assert.h>
     #define ST_THROW_RT_ERROR(x)    {assert((const char *)x);}
 #else
     // use c++ standard exceptions
     #include <stdexcept>
+    #include <string>
     #define ST_THROW_RT_ERROR(x)    {throw std::runtime_error(x);}
 #endif
 
 // When this #define is active, eliminates a clicking sound when the "rate" or "pitch" 
 // parameter setting crosses from value <1 to >=1 or vice versa during processing. 
 // Default is off as such crossover is untypical case and involves a slight sound 
 // quality compromise.
 //#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER   1
--- a/media/libsoundtouch/src/SoundTouch.cpp
+++ b/media/libsoundtouch/src/SoundTouch.cpp
@@ -36,20 +36,20 @@
 ///   combining the two other controls.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2014-10-08 15:26:57 +0000 (Wed, 08 Oct 2014) $
 // File revision : $Revision: 4 $
 //
-// $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $
+// $Id: SoundTouch.cpp 201 2014-10-08 15:26:57Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -75,21 +75,16 @@
 #include <math.h>
 #include <stdio.h>
 
 #include "SoundTouch.h"
 #include "TDStretch.h"
 #include "RateTransposer.h"
 #include "cpu_detect.h"
 
-#ifdef _MSC_VER
-#include <malloc.h>
-#define alloca _alloca
-#endif
-
 using namespace soundtouch;
     
 /// test if two floating point numbers are equal
 #define TEST_FLOAT_EQUAL(a, b)  (fabs(a - b) < 1e-10)
 
 
 /// Print library version string for autoconf
 extern "C" void soundtouch_ac_test()
@@ -348,18 +343,18 @@ void SoundTouch::putSamples(const SAMPLE
 // stream. This function may introduce additional blank samples in the end
 // of the sound stream, and thus it's not recommended to call this function
 // in the middle of a sound stream.
 void SoundTouch::flush()
 {
     int i;
     int nUnprocessed;
     int nOut;
-    SAMPLETYPE *buff=(SAMPLETYPE*)alloca(64*channels*sizeof(SAMPLETYPE));
-
+    SAMPLETYPE *buff = new SAMPLETYPE[64 * channels];
+    
     // check how many samples still await processing, and scale
     // that by tempo & rate to get expected output sample count
     nUnprocessed = numUnprocessedSamples();
     nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
 
     nOut = numSamples();        // ready samples currently in buffer ...
     nOut += nUnprocessed;       // ... and how many we expect there to be in the end
     
@@ -378,16 +373,18 @@ void SoundTouch::flush()
             // back to maximum "nOut" samples to improve duration accuracy 
             adjustAmountOfSamples(nOut);
 
             // finish
             break;  
         }
     }
 
+    delete[] buff;
+
     // Clear working buffers
     pRateTransposer->clear();
     pTDStretch->clearInput();
     // yet leave the 'tempoChanger' output intouched as that's where the
     // flushed samples are!
 }
 
 
--- a/media/libsoundtouch/src/SoundTouch.h
+++ b/media/libsoundtouch/src/SoundTouch.h
@@ -36,20 +36,20 @@
 ///   combining the two other controls.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2015-05-18 15:28:41 +0000 (Mon, 18 May 2015) $
 // File revision : $Revision: 4 $
 //
-// $Id: SoundTouch.h 195 2014-04-06 15:57:21Z oparviai $
+// $Id: SoundTouch.h 216 2015-05-18 15:28:41Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -74,20 +74,20 @@
 
 #include "FIFOSamplePipe.h"
 #include "STTypes.h"
 
 namespace soundtouch
 {
 
 /// Soundtouch library version string
-#define SOUNDTOUCH_VERSION          "1.8.0"
+#define SOUNDTOUCH_VERSION          "1.9.0"
 
 /// SoundTouch library version id
-#define SOUNDTOUCH_VERSION_ID       (10800)
+#define SOUNDTOUCH_VERSION_ID       (10900)
 
 //
 // Available setting IDs for the 'setSetting' & 'get_setting' functions:
 
 /// Enable/disable anti-alias filter in pitch transposer (0 = disable)
 #define SETTING_USE_AA_FILTER       0
 
 /// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
new file mode 100644
--- /dev/null
+++ b/media/libsoundtouch/src/SoundTouchFactory.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <soundtouch/SoundTouch.h>
+
+namespace soundtouch
+{
+
+EXPORT
+soundtouch::SoundTouch*
+createSoundTouchObj()
+{
+  return new soundtouch::SoundTouch();
+}
+
+EXPORT
+void
+destroySoundTouchObj(soundtouch::SoundTouch* aObj)
+{
+  // SoundTouch runs deletes in its destructor, meaning they need to be run in
+  // the DLL context. Gecko should send its SoundTouch obj pointers here to be
+  // cleaned up.
+  if (aObj) {
+    delete aObj;
+  }
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/libsoundtouch/src/SoundTouchFactory.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Code for dealing with creating/deleting SoundTouch objects across DLL
+// boundaries.
+
+#include <soundtouch/STTypes.h>
+#include <soundtouch/SoundTouch.h>
+
+namespace soundtouch
+{
+EXPORT
+soundtouch::SoundTouch*
+createSoundTouchObj();
+
+EXPORT
+void
+destroySoundTouchObj(soundtouch::SoundTouch* aObj);
+}
--- a/media/libsoundtouch/src/TDStretch.cpp
+++ b/media/libsoundtouch/src/TDStretch.cpp
@@ -8,20 +8,20 @@
 /// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2015-02-22 15:07:12 +0000 (Sun, 22 Feb 2015) $
 // File revision : $Revision: 1.12 $
 //
-// $Id: TDStretch.cpp 195 2014-04-06 15:57:21Z oparviai $
+// $Id: TDStretch.cpp 205 2015-02-22 15:07:12Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -287,43 +287,58 @@ inline void TDStretch::overlap(SAMPLETYP
 // routine
 //
 // The best position is determined as the position where the two overlapped
 // sample sequences are 'most alike', in terms of the highest cross-correlation
 // value over the overlapping period
 int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos) 
 {
     int bestOffs;
-    double bestCorr, corr;
+    double bestCorr;
+    int i;
     double norm;
-    int i;
 
     bestCorr = FLT_MIN;
     bestOffs = 0;
 
     // Scans for the best correlation value by testing each possible position
     // over the permitted range.
     bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
+
+    #pragma omp parallel for
     for (i = 1; i < seekLength; i ++) 
     {
-        // Calculates correlation value for the mixing position corresponding
-        // to 'i'. Now call "calcCrossCorrAccumulate" that is otherwise same as
-        // "calcCrossCorr", but saves time by reusing & updating previously stored 
+        double corr;
+        // Calculates correlation value for the mixing position corresponding to 'i'
+#ifdef _OPENMP
+        // in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
+        // iterate the loop in sequential order
+        corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
+#else
+        // In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
+        // as "calcCrossCorr", but saves time by reusing & updating previously stored 
         // "norm" value
         corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
-
+#endif
         // heuristic rule to slightly favour values close to mid of the range
         double tmp = (double)(2 * i - seekLength) / (double)seekLength;
         corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
 
         // Checks for the highest correlation value
         if (corr > bestCorr) 
         {
-            bestCorr = corr;
-            bestOffs = i;
+            // For optimal performance, enter critical section only in case that best value found.
+            // in such case repeat 'if' condition as it's possible that parallel execution may have
+            // updated the bestCorr value in the mean time
+            #pragma omp critical
+            if (corr > bestCorr)
+            {
+                bestCorr = corr;
+                bestOffs = i;
+            }
         }
     }
     // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
     clearCrossCorrState();
 
     return bestOffs;
 }
 
@@ -878,19 +893,20 @@ void TDStretch::calculateOverlapLength(i
     // must be divisible by 8
     newOvl -= newOvl % 8;
 
     acceptNewOverlapLength(newOvl);
 }
 
 
 /// Calculate cross-correlation
-double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const
+double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm) const
 {
     double corr;
+    double norm;
     int i;
 
     corr = norm = 0;
     // Same routine for stereo and mono. For Stereo, unroll by factor of 2.
     // For mono it's same routine yet unrollsd by factor of 4.
     for (i = 0; i < channels * overlapLength; i += 4) 
     {
         corr += mixingPos[i] * compare[i] +
@@ -902,16 +918,17 @@ double TDStretch::calcCrossCorr(const fl
         // unroll the loop for better CPU efficiency:
         corr += mixingPos[i + 2] * compare[i + 2] +
                 mixingPos[i + 3] * compare[i + 3];
 
         norm += mixingPos[i + 2] * mixingPos[i + 2] +
                 mixingPos[i + 3] * mixingPos[i + 3];
     }
 
+    anorm = norm;
     return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
 }
 
 
 /// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
 double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
 {
     double corr;
--- a/media/libsoundtouch/src/TDStretch.h
+++ b/media/libsoundtouch/src/TDStretch.h
@@ -8,17 +8,17 @@
 /// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
+// Last changed  : $Date: 2014-04-06 15:57:21 +0000 (Sun, 06 Apr 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: TDStretch.h 195 2014-04-06 15:57:21Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/cpu_detect.h
+++ b/media/libsoundtouch/src/cpu_detect.h
@@ -7,17 +7,17 @@
 /// platforms, respectively.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2008-02-10 10:26:55 -0600 (Sun, 10 Feb 2008) $
+// Last changed  : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
 // File revision : $Revision: 4 $
 //
 // $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/cpu_detect_x86.cpp
+++ b/media/libsoundtouch/src/cpu_detect_x86.cpp
@@ -6,17 +6,17 @@
 /// for the Microsoft compiler version.
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-07 12:24:28 -0600 (Tue, 07 Jan 2014) $
+// Last changed  : $Date: 2014-01-07 18:24:28 +0000 (Tue, 07 Jan 2014) $
 // File revision : $Revision: 4 $
 //
 // $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
--- a/media/libsoundtouch/src/mmx_optimized.cpp
+++ b/media/libsoundtouch/src/mmx_optimized.cpp
@@ -15,20 +15,20 @@
 /// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-07 12:25:40 -0600 (Tue, 07 Jan 2014) $
+// Last changed  : $Date: 2015-02-22 15:10:38 +0000 (Sun, 22 Feb 2015) $
 // File revision : $Revision: 4 $
 //
-// $Id: mmx_optimized.cpp 184 2014-01-07 18:25:40Z oparviai $
+// $Id: mmx_optimized.cpp 206 2015-02-22 15:10:38Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -282,16 +282,17 @@ void TDStretchMMX::overlapStereo(short *
 //
 //////////////////////////////////////////////////////////////////////////////
 
 #include "FIRFilter.h"
 
 
 FIRFilterMMX::FIRFilterMMX() : FIRFilter()
 {
+    filterCoeffsAlign = NULL;
     filterCoeffsUnalign = NULL;
 }
 
 
 FIRFilterMMX::~FIRFilterMMX()
 {
     delete[] filterCoeffsUnalign;
 }
--- a/media/libsoundtouch/src/moz.build
+++ b/media/libsoundtouch/src/moz.build
@@ -3,41 +3,46 @@
 # 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/.
 
 EXPORTS.soundtouch += [
     'FIFOSamplePipe.h',
     'SoundTouch.h',
     'soundtouch_config.h',
+    'SoundTouchFactory.h',
     'STTypes.h',
 ]
 
 UNIFIED_SOURCES += [
     'AAFilter.cpp',
     'cpu_detect_x86.cpp',
     'FIFOSampleBuffer.cpp',
     'FIRFilter.cpp',
     'InterpolateCubic.cpp',
     'InterpolateLinear.cpp',
     'InterpolateShannon.cpp',
     'RateTransposer.cpp',
     'SoundTouch.cpp',
+    'SoundTouchFactory.cpp',
     'TDStretch.cpp',
 ]
 
 if CONFIG['INTEL_ARCHITECTURE']:
     if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
         SOURCES += ['sse_optimized.cpp']
         SOURCES['sse_optimized.cpp'].flags += CONFIG['SSE2_FLAGS']
     else:
         SOURCES += ['mmx_optimized.cpp']
         SOURCES['mmx_optimized.cpp'].flags += CONFIG['MMX_FLAGS']
 
-if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
-    NO_VISIBILITY_FLAGS = True
+if CONFIG['OS_ARCH'] != 'WINNT':
+    # GCC/Clang require permissions to be explicitly set for the soundtouch
+    # header.
+    CXXFLAGS += ['-include', 'soundtouch_perms.h']
+else:
+    # Windows need alloca renamed to _alloca
+    DEFINES['alloca'] = '_alloca'
 
-FINAL_LIBRARY = 'gkmedias'
+FINAL_LIBRARY = 'lgpllibs'
 
 # Use abort() instead of exception in SoundTouch.
 DEFINES['ST_NO_EXCEPTION_HANDLING'] = 1
-
-DEFINES['BUILDING_SOUNDTOUCH'] = True
new file mode 100644
--- /dev/null
+++ b/media/libsoundtouch/src/soundtouch_perms.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Include file for fixing symbol visibility on non-windows platforms, until
+// system headers wrappers work uniformly across all of them.
+
+#ifndef MOZILLA_SOUNDTOUCH_PERMS_H
+#define MOZILLA_SOUNDTOUCH_PERMS_H
+
+#pragma GCC visibility push(default)
+#include "SoundTouch.h"
+#include "SoundTouchFactory.h"
+#pragma GCC visibility pop
+
+#endif // MOZILLA_SOUNDTOUCH_PERMS_H
--- a/media/libsoundtouch/src/sse_optimized.cpp
+++ b/media/libsoundtouch/src/sse_optimized.cpp
@@ -18,20 +18,20 @@
 /// perform a search with keywords "processor pack".
 ///
 /// Author        : Copyright (c) Olli Parviainen
 /// Author e-mail : oparviai 'at' iki.fi
 /// SoundTouch WWW: http://www.surina.net/soundtouch
 ///
 ////////////////////////////////////////////////////////////////////////////////
 //
-// Last changed  : $Date: 2014-01-07 12:25:40 -0600 (Tue, 07 Jan 2014) $
+// Last changed  : $Date: 2015-02-21 21:24:29 +0000 (Sat, 21 Feb 2015) $
 // File revision : $Revision: 4 $
 //
-// $Id: sse_optimized.cpp 184 2014-01-07 18:25:40Z oparviai $
+// $Id: sse_optimized.cpp 202 2015-02-21 21:24:29Z oparviai $
 //
 ////////////////////////////////////////////////////////////////////////////////
 //
 // License :
 //
 //  SoundTouch audio processing library
 //  Copyright (c) Olli Parviainen
 //
@@ -66,17 +66,17 @@ using namespace soundtouch;
 //
 //////////////////////////////////////////////////////////////////////////////
 
 #include "TDStretch.h"
 #include <xmmintrin.h>
 #include <math.h>
 
 // Calculates cross correlation of two buffers
-double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &norm) const
+double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm) const
 {
     int i;
     const float *pVec1;
     const __m128 *pVec2;
     __m128 vSum, vNorm;
 
     // Note. It means a major slow-down if the routine needs to tolerate 
     // unaligned __m128 memory accesses. It's way faster if we can skip 
@@ -136,17 +136,18 @@ double TDStretchSSE::calcCrossCorr(const
         vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
 
         pVec1 += 16;
         pVec2 += 4;
     }
 
     // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
     float *pvNorm = (float*)&vNorm;
-    norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
+    float norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
+    anorm = norm;
 
     float *pvSum = (float*)&vSum;
     return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
 
     /* This is approximately corresponding routine in C-language yet without normalization:
     double corr, norm;
     uint i;
 
@@ -253,24 +254,27 @@ uint FIRFilterSSE::evaluateFilterStereo(
 
     assert(source != NULL);
     assert(dest != NULL);
     assert((length % 8) == 0);
     assert(filterCoeffsAlign != NULL);
     assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
 
     // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
+    #pragma omp parallel for
     for (j = 0; j < count; j += 2)
     {
         const float *pSrc;
+        float *pDest;
         const __m128 *pFil;
         __m128 sum1, sum2;
         uint i;
 
-        pSrc = (const float*)source;              // source audio data
+        pSrc = (const float*)source + j * 2;      // source audio data
+        pDest = dest + j * 2;                     // destination audio data
         pFil = (const __m128*)filterCoeffsAlign;  // filter coefficients. NOTE: Assumes coefficients 
                                                   // are aligned to 16-byte boundary
         sum1 = sum2 = _mm_setzero_ps();
 
         for (i = 0; i < length / 8; i ++) 
         {
             // Unroll loop for efficiency & calculate filter for 2*2 stereo samples 
             // at each pass
@@ -293,22 +297,20 @@ uint FIRFilterSSE::evaluateFilterStereo(
             pSrc += 16;
             pFil += 4;
         }
 
         // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
         // to sum the two hi- and lo-floats of these registers together.
 
         // post-shuffle & add the filtered values and store to dest.
-        _mm_storeu_ps(dest, _mm_add_ps(
+        _mm_storeu_ps(pDest, _mm_add_ps(
                     _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)),   // s2_1 s2_0 s1_3 s1_2
                     _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0))    // s2_3 s2_2 s1_1 s1_0
                     ));
-        source += 4;
-        dest += 4;
     }
 
     // Ideas for further improvement:
     // 1. If it could be guaranteed that 'source' were always aligned to 16-byte 
     //    boundary, a faster aligned '_mm_load_ps' instruction could be used.
     // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte 
     //    boundary, a faster '_mm_store_ps' instruction could be used.
 
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -37,16 +37,17 @@
 #ifdef MOZ_DMD
 @BINPATH@/@DLL_PREFIX@dmd@DLL_SUFFIX@
 #endif
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
 #endif
+@BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugin@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugingb@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugingb235@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxpluginhc@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxpluginkk@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
 
 @BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4038,16 +4038,23 @@ pref("image.high_quality_downscaling.ena
 // The minimum percent downscaling we'll use high-quality downscaling on,
 // interpreted as a floating-point number / 1000.
 pref("image.high_quality_downscaling.min_factor", 1000);
 
 // The maximum memory size which we'll use high-quality uspcaling on,
 // interpreted as number of decoded bytes.
 pref("image.high_quality_upscaling.max_size", 20971520);
 
+// The threshold for inferring that changes to an <img> element's |src|
+// attribute by JavaScript represent an animation, in milliseconds. If the |src|
+// attribute is changing more frequently than this value, then we enter a
+// special "animation mode" which is designed to eliminate flicker. Set to 0 to
+// disable.
+pref("image.infer-src-animation.threshold-ms", 2000);
+
 // Should we optimize away the surfaces of single-color images?
 pref("image.single-color-optimization.enabled", true);
 
 //
 // Image memory management prefs
 //
 
 // Discards inactive image frames and re-decodes them on demand from
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -550,17 +550,17 @@ PREF_DeleteBranch(const char *branch_nam
     nsAutoCString branch_dot(branch_name);
     if ((len > 1) && branch_name[len - 1] != '.')
         branch_dot += '.';
 
     /* Delete a branch. Used for deleting mime types */
     const char *to_delete = branch_dot.get();
     MOZ_ASSERT(to_delete);
     len = strlen(to_delete);
-    for (auto iter = gHashTable->RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
         auto entry = static_cast<PrefHashEntry*>(iter.Get());
 
         /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
             and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
         if (PL_strncmp(entry->key, to_delete, (uint32_t) len) == 0 ||
             (len-1 == (int)strlen(entry->key) &&
              PL_strncmp(entry->key, to_delete, (uint32_t)(len-1)) == 0)) {
             iter.Remove();
@@ -599,17 +599,17 @@ PREF_ClearAllUserPrefs()
 #ifndef MOZ_B2G
     MOZ_ASSERT(NS_IsMainThread());
 #endif
 
     if (!gHashTable)
         return NS_ERROR_NOT_INITIALIZED;
 
     std::vector<std::string> prefStrings;
-    for (auto iter = gHashTable->RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
         auto pref = static_cast<PrefHashEntry*>(iter.Get());
 
         if (PREF_HAS_USER_VALUE(pref)) {
             prefStrings.push_back(std::string(pref->key));
 
             pref->flags &= ~PREF_USERSET;
             if (!(pref->flags & PREF_HAS_DEFAULT)) {
                 iter.Remove();
--- a/netwerk/cache/nsCacheEntry.cpp
+++ b/netwerk/cache/nsCacheEntry.cpp
@@ -458,27 +458,21 @@ nsCacheEntryHashTable::RemoveEntry( nsCa
     // XXX debug code to make sure we have the entry we're trying to remove
     nsCacheEntry *check = GetEntry(&(cacheEntry->mKey));
     NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
 #endif
     PL_DHashTableRemove(&table, &(cacheEntry->mKey));
 }
 
 PLDHashTable::Iterator
-nsCacheEntryHashTable::Iter() const
+nsCacheEntryHashTable::Iter()
 {
     return PLDHashTable::Iterator(&table);
 }
 
-PLDHashTable::RemovingIterator
-nsCacheEntryHashTable::RemovingIter()
-{
-    return PLDHashTable::RemovingIterator(&table);
-}
-
 /**
  *  hash table operation callback functions
  */
 
 PLDHashNumber
 nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key)
 {
     return HashString(*static_cast<const nsCString *>(key));
--- a/netwerk/cache/nsCacheEntry.h
+++ b/netwerk/cache/nsCacheEntry.h
@@ -269,18 +269,17 @@ public:
 
     void          Init();
     void          Shutdown();
 
     nsCacheEntry *GetEntry( const nsCString * key);
     nsresult      AddEntry( nsCacheEntry *entry);
     void          RemoveEntry( nsCacheEntry *entry);
 
-    PLDHashTable::Iterator Iter() const;
-    PLDHashTable::RemovingIterator RemovingIter();
+    PLDHashTable::Iterator Iter();
 
 private:
     // PLDHashTable operation callbacks
     static PLDHashNumber  HashKey( PLDHashTable *table, const void *key);
 
     static bool           MatchEntry( PLDHashTable *           table,
                                       const PLDHashEntryHdr *  entry,
                                       const void *             key);
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -2914,17 +2914,17 @@ nsCacheService::ClearDoomList()
     }
 }
 
 void
 nsCacheService::DoomActiveEntries(DoomCheckFn check)
 {
     nsAutoTArray<nsCacheEntry*, 8> array;
 
-    for (auto iter = mActiveEntries.RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
         nsCacheEntry* entry =
             static_cast<nsCacheEntryHashTableEntry*>(iter.Get())->cacheEntry;
 
         if (check && !check(entry)) {
             continue;
         }
 
         array.AppendElement(entry);
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -620,17 +620,17 @@ nsHostResolver::FlushCache()
             node = node->next;
             PR_REMOVE_AND_INIT_LINK(rec);
             PL_DHashTableRemove(&mDB, (nsHostKey *) rec);
             NS_RELEASE(rec);
         }
     }
 
     // Refresh the cache entries that are resolving RIGHT now, remove the rest.
-    for (auto iter = mDB.RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mDB.Iter(); !iter.Done(); iter.Next()) {
         auto entry = static_cast<nsHostDBEnt *>(iter.Get());
         // Try to remove the record, or mark it for refresh.
         if (entry->rec->RemoveOrRefresh()) {
             PR_REMOVE_LINK(entry->rec);
             iter.Remove();
         }
     }
 }
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -141,19 +141,21 @@ def is_read_allowed(path, config):
     assert os.path.isabs(config.topsrcdir)
 
     path = mozpath.normpath(path)
     topsrcdir = mozpath.normpath(config.topsrcdir)
 
     if mozpath.basedir(path, [topsrcdir]):
         return True
 
-    if config.external_source_dir and \
-            mozpath.basedir(path, [config.external_source_dir]):
-        return True
+    if config.external_source_dir:
+        external_dir = os.path.normcase(config.external_source_dir)
+        norm_path = os.path.normcase(path)
+        if mozpath.basedir(norm_path, [external_dir]):
+            return True
 
     return False
 
 
 class SandboxCalledError(SandboxError):
     """Represents an error resulting from calling the error() function."""
 
     def __init__(self, file_stack, message):
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -712,17 +712,18 @@ class GTestCommands(MachCommandBase):
     @CommandArgument('--debugger-args', default=None, metavar='params', type=str,
         group='debugging',
         help='Command-line arguments to pass to the debugger itself; split as the Bourne shell would.')
 
     def gtest(self, shuffle, jobs, gtest_filter, tbpl_parser, debug, debugger,
               debugger_args):
 
         # We lazy build gtest because it's slow to link
-        self._run_make(directory="testing/gtest", target='gtest', ensure_exit_code=True)
+        self._run_make(directory="testing/gtest", target='gtest',
+                       print_directory=False, ensure_exit_code=True)
 
         app_path = self.get_binary_path('app')
         args = [app_path, '-unittest'];
 
         if debug or debugger or debugger_args:
             args = self.prepend_debugger_args(args, debugger, debugger_args)
 
         cwd = os.path.join(self.topobjdir, '_tests', 'gtest')
--- a/rdf/base/nsInMemoryDataSource.cpp
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -1803,17 +1803,17 @@ InMemoryDataSource::Sweep()
     return NS_OK;
 }
 
 
 void
 InMemoryDataSource::SweepForwardArcsEntries(PLDHashTable* aTable,
                                             SweepInfo* aInfo)
 {
-    for (auto iter = aTable->RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
         auto entry = static_cast<Entry*>(iter.Get());
 
         Assertion* as = entry->mAssertions;
         if (as && (as->mHashEntry)) {
             // Stuff in sub-hashes must be swept recursively (max depth: 1)
             SweepForwardArcsEntries(as->u.hash.mPropertyHash, aInfo);
 
             // If the sub-hash is now empty, clean it up.
--- a/security/manager/ssl/nsNSSShutDown.cpp
+++ b/security/manager/ssl/nsNSSShutDown.cpp
@@ -172,17 +172,17 @@ nsresult nsNSSShutDownList::evaporateAll
 
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("now evaporating NSS resources\n"));
 
   // Never free more than one entry, because other threads might be calling
   // us and remove themselves while we are iterating over the list,
   // and the behaviour of changing the list while iterating is undefined.
   while (true) {
     MutexAutoLock lock(mListLock);
-    auto iter = mObjects.RemovingIter();
+    auto iter = mObjects.Iter();
     if (iter.Done()) {
       break;
     }
     auto entry = static_cast<ObjectHashEntry*>(iter.Get());
     {
       MutexAutoUnlock unlock(singleton->mListLock);
       entry->obj->shutdown(nsNSSShutDownObject::calledFromList);
     }
--- a/testing/config/mozharness/linux_config.py
+++ b/testing/config/mozharness/linux_config.py
@@ -18,16 +18,35 @@ config = {
                 "--no-slow",
                 "--no-progress",
                 "--format=automation",
                 "--jitflags=all"
             ],
             "run_filename": "jit_test.py",
             "testsdir": "jit-test/jit-test"
         },
+        "luciddream-emulator": {
+            "options": [
+                "--startup-timeout=300",
+                "--log-raw=%(raw_log_file)s",
+                "--browser-path=%(browser_path)s",
+                "--b2gpath=%(emulator_path)s",
+                "%(test_manifest)s"
+            ],
+        },
+        "luciddream-b2gdt": {
+            "options": [
+                "--startup-timeout=300",
+                "--log-raw=%(raw_log_file)s",
+                "--browser-path=%(browser_path)s",
+                "--b2g-desktop-path=%(fxos_desktop_path)s",
+                "--gaia-profile=%(gaia_profile)s",
+                "%(test_manifest)s"
+            ],
+        },
         "mochitest": {
             "options": [
                 "--appname=%(binary_path)s",
                 "--utility-path=tests/bin",
                 "--extra-profile-file=tests/bin/plugins",
                 "--symbols-path=%(symbols_path)s",
                 "--certificate-path=tests/certs",
                 "--setpref=webgl.force-enabled=true",
--- a/testing/luciddream/luciddream/runluciddream.py
+++ b/testing/luciddream/luciddream/runluciddream.py
@@ -50,16 +50,19 @@ def parse_args(in_args):
                         help='path to B2G repo or qemu dir')
     parser.add_argument('--b2g-desktop-path', dest='b2g_desktop_path',
                         action='store',
                         help='path to B2G desktop binary')
     parser.add_argument('--browser-path', dest='browser_path', action='store',
                         help='path to Firefox binary')
     parser.add_argument('--gaia-profile', dest='gaia_profile', action='store',
                         help='path to Gaia profile')
+    parser.add_argument('--startup-timeout', dest='startup_timeout', action='store',
+                        default=60,  type=int,
+                        help='max time to wait for Marionette to be available after launching binary')
     parser.add_argument('manifest', metavar='MANIFEST', action='store',
                         help='path to manifest of tests to run')
     structured.commandline.add_logging_group(parser)
 
     args = parser.parse_args(in_args)
     try:
         validate_options(args)
         return args
@@ -71,48 +74,51 @@ def parse_args(in_args):
 
 class LucidDreamTestRunner(BaseMarionetteTestRunner):
     def __init__(self, **kwargs):
         BaseMarionetteTestRunner.__init__(self, **kwargs)
         #TODO: handle something like MarionetteJSTestCase
         self.test_handlers = [LucidDreamTestCase]
 
 
-def start_browser(browser_path, app_args):
+def start_browser(browser_path, app_args, startup_timeout):
     '''
     Start a Firefox browser and return a Marionette instance that
     can talk to it.
     '''
     marionette = Marionette(
         bin=browser_path,
         # Need to avoid the browser and emulator's ports stepping
         # on each others' toes.
         port=2929,
         app_args=app_args,
-        gecko_log="firefox.log"
+        gecko_log="firefox.log",
+        startup_timeout=startup_timeout
     )
     runner = marionette.runner
     if runner:
         runner.start()
     marionette.wait_for_port()
     marionette.start_session()
     marionette.set_context(marionette.CONTEXT_CHROME)
     return marionette
 
 
 #TODO: make marionette/client/marionette/runtests.py importable so we can
 # just use cli from there. A lot of this is copy/paste from that function.
-def run(browser_path=None, b2g_desktop_path=None, emulator_path=None, emulator_arch=None, gaia_profile=None, manifest=None, browser_args=None, **kwargs):
+def run(browser_path=None, b2g_desktop_path=None, emulator_path=None,
+        emulator_arch=None, gaia_profile=None, manifest=None, browser_args=None,
+        **kwargs):
     # It's sort of debatable here whether the marionette instance managed
     # by the test runner should be the browser or the emulator. Right now
     # it's the emulator because it feels like there's more fiddly setup around
     # that, but longer-term if we want to run tests against different
     # (non-B2G) targets this won't match up very well, so maybe it ought to
     # be the browser?
-    browser = start_browser(browser_path, browser_args)
+    browser = start_browser(browser_path, browser_args, kwargs['startup_timeout'])
 
     kwargs["browser"] = browser
     if not "logger" in kwargs:
         logger = structured.commandline.setup_logging(
             "luciddream", kwargs, {"tbpl": sys.stdout})
         kwargs["logger"] = logger
 
     if emulator_path:
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -355,16 +355,21 @@ class BaseMarionetteOptions(OptionParser
         self.add_option('--symbols-path',
                         dest='symbols_path',
                         action='store',
                         help='absolute path to directory containing breakpad symbols, or the url of a zip file containing symbols')
         self.add_option('--timeout',
                         dest='timeout',
                         type=int,
                         help='if a --timeout value is given, it will set the default page load timeout, search timeout and script timeout to the given value. If not passed in, it will use the default values of 30000ms for page load, 0ms for search timeout and 10000ms for script timeout')
+        self.add_option('--startup-timeout',
+                        dest='startup_timeout',
+                        type=int,
+                        default=60,
+                        help='the max number of seconds to wait for a Marionette connection after launching a binary')
         self.add_option('--shuffle',
                         action='store_true',
                         dest='shuffle',
                         default=False,
                         help='run tests in a random order')
         self.add_option('--shuffle-seed',
                         dest='shuffle_seed',
                         type=int,
@@ -501,17 +506,17 @@ class BaseMarionetteTestRunner(object):
                  logger=None, no_window=False, logdir=None, logcat_stdout=False,
                  xml_output=None, repeat=0, testvars=None, tree=None, type=None,
                  device_serial=None, symbols_path=None, timeout=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
                  sdcard=None, this_chunk=1, total_chunks=1, sources=None,
                  server_root=None, gecko_log=None, result_callbacks=None,
                  adb_host=None, adb_port=None, prefs=None, test_tags=None,
                  socket_timeout=BaseMarionetteOptions.socket_timeout_default,
-                 **kwargs):
+                 startup_timeout=None, **kwargs):
         self.address = address
         self.emulator = emulator
         self.emulator_binary = emulator_binary
         self.emulator_img = emulator_img
         self.emulator_res = emulator_res
         self.homedir = homedir
         self.app = app
         self.app_args = app_args or []
@@ -546,16 +551,17 @@ class BaseMarionetteTestRunner(object):
         self.mixin_run_tests = []
         self.manifest_skipped_tests = []
         self.tests = []
         self.result_callbacks = result_callbacks if result_callbacks is not None else []
         self._adb_host = adb_host
         self._adb_port = adb_port
         self.prefs = prefs or {}
         self.test_tags = test_tags
+        self.startup_timeout = startup_timeout
 
         def gather_debug(test, status):
             rv = {}
             marionette = test._marionette_weakref()
 
             # In the event we're gathering debug without starting a session, skip marionette commands
             if marionette.session is not None:
                 try:
@@ -645,16 +651,17 @@ class BaseMarionetteTestRunner(object):
         kwargs = {
             'device_serial': self.device_serial,
             'symbols_path': self.symbols_path,
             'timeout': self.timeout,
             'socket_timeout': self.socket_timeout,
             'adb_host': self._adb_host,
             'adb_port': self._adb_port,
             'prefs': self.prefs,
+            'startup_timeout': self.startup_timeout,
         }
         if self.bin:
             kwargs.update({
                 'host': 'localhost',
                 'port': 2828,
                 'app': self.app,
                 'app_args': self.app_args,
                 'bin': self.bin,
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -545,17 +545,17 @@ class Marionette(object):
     TIMEOUT_PAGE = 'page load'
 
     def __init__(self, host='localhost', port=2828, app=None, app_args=None, bin=None,
                  profile=None, emulator=None, sdcard=None, emulator_img=None,
                  emulator_binary=None, emulator_res=None, connect_to_running_emulator=False,
                  gecko_log=None, homedir=None, baseurl=None, no_window=False, logdir=None,
                  busybox=None, symbols_path=None, timeout=None, socket_timeout=360,
                  device_serial=None, adb_path=None, process_args=None,
-                 adb_host=None, adb_port=None, prefs=None):
+                 adb_host=None, adb_port=None, prefs=None, startup_timeout=60):
         self.host = host
         self.port = self.local_port = port
         self.bin = bin
         self.profile = profile
         self.instance = None
         self.session = None
         self.session_id = None
         self.window = None
@@ -594,17 +594,17 @@ class Marionette(object):
                         ConfigParser.NoSectionError,
                         KeyError):
                     instance_class = geckoinstance.GeckoInstance
             self.instance = instance_class(host=self.host, port=self.port,
                                            bin=self.bin, profile=self.profile,
                                            app_args=app_args, symbols_path=symbols_path,
                                            gecko_log=gecko_log, prefs=prefs)
             self.instance.start()
-            assert(self.wait_for_port()), "Timed out waiting for port!"
+            assert(self.wait_for_port(timeout=startup_timeout)), "Timed out waiting for port!"
 
         if emulator:
             self.runner = B2GEmulatorRunner(b2g_home=homedir,
                                             no_window=self.no_window,
                                             logdir=logdir,
                                             arch=emulator,
                                             sdcard=sdcard,
                                             symbols_path=symbols_path,
--- a/testing/taskcluster/scripts/builder/build-sm.sh
+++ b/testing/taskcluster/scripts/builder/build-sm.sh
@@ -1,22 +1,22 @@
 #!/bin/bash
 
 set -x
 
 : SPIDERMONKEY_VARIANT ${SPIDERMONKEY_VARIANT:-plain}
 
-UPLOAD_DIR=$HOME/workspace/artifacts/
+UPLOAD_DIR=$HOME/artifacts/
 
 # cd into the correct directory
 cd $HOME/workspace/
 
 # Run the script
 ./build/src/js/src/devtools/automation/autospider.sh $SPIDERMONKEY_VARIANT
 BUILD_STATUS=$?
 
 # Ensure upload dir exists
 mkdir -p $UPLOAD_DIR
 
-# Move artifacts for upload by TaskCluster
-mv ./build/src/obj-spider/dist/* $UPLOAD_DIR
+# Copy artifacts for upload by TaskCluster
+cp -rL ./build/src/obj-spider/dist/bin/{js,jsapi-tests,js-gdb.py} $UPLOAD_DIR
 
 exit $BUILD_STATUS
--- a/testing/taskcluster/tasks/branches/try/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/try/job_flags.yml
@@ -125,16 +125,62 @@ builds:
   linux64:
     platforms:
       - Linux64
     types:
       opt:
         task: tasks/builds/opt_linux64.yml
       debug:
         task: tasks/builds/dbg_linux64.yml
+  sm-plain:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_plain.yml
+      debug:
+       task: tasks/builds/sm_plaindebug.yml
+  sm-arm-sim:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_arm_sim.yml
+  sm-arm-sim-osx:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_arm_sim_osx.yml
+  sm-compacting:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_compacting.yml
+  sm-generational:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_generational.yml
+  sm-rootanalysis:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_rootanalysis.yml
+  sm-warnaserr:
+    platforms:
+      - Linux64
+    types:
+      opt:
+        task: tasks/builds/sm_warnaserr.yml
+      debug:
+        task: tasks/builds/sm_warnaserrdebug.yml
 
 tests:
   cppunit:
     allowed_build_tasks:
       tasks/builds/b2g_emulator_ics_opt.yml:
         task: tasks/tests/b2g_emulator_cpp_unit.yml
       tasks/builds/b2g_emulator_ics_debug.yml:
         task: tasks/tests/b2g_emulator_cpp_unit.yml
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_arm_sim.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-arm-sim'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+        SPIDERMONKEY_VARIANT: 'arm-sim'
+  metadata:
+    name: '[TC] Spidermonkey ARM sim'
+    description: 'Spidermonkey ARM sim'
+  extra:
+    treeherder:
+      symbol: arm
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_arm_sim_osx.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-arm-sim-osx'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'arm-sim-osx'
+  metadata:
+    name: '[TC] Spidermonkey ARM Simulator OSX'
+    description: 'Spidermonkey ARM Simulator OSX'
+  extra:
+    treeherder:
+      symbol: arm
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_base.yml
@@ -0,0 +1,43 @@
+$inherits:
+  from: 'tasks/build.yml'
+task:
+  workerType: spidermonkey
+
+  routes:
+    - 'index.ffledgling.test.spidermonkey'
+    - 'index.buildbot.branches.{{project}}.sm-plain'
+    - 'index.buildbot.revisions.{{head_rev}}.{{project}}.sm-plain'
+
+  scopes:
+    - 'docker-worker:cache:build-spidermonkey-workspace'
+
+  payload:
+    image: '{{#docker_image}}desktop-build{{/docker_image}}'
+    cache:
+      build-spidermonkey-workspace: '/home/worker/workspace'
+
+    env:
+      GECKO_REPOSITORY: '{{base_repository}}'
+      MOZHARNESS_DISABLE: 'true'
+      TOOLS_DISABLE: 'true'
+
+    maxRunTime: 36000
+
+    command: ["/bin/bash", "-c", "cd /home/worker/ && ./bin/checkout-sources.sh && ./workspace/build/src/testing/taskcluster/scripts/builder/build-sm.sh" ]
+
+  extra:
+    treeherderEnv:
+      - staging
+      - production
+    treeherder:
+      groupSymbol: SM-tc
+      groupName: Spider Monkey, submitted by taskcluster
+      machine:
+        # see https://github.com/mozilla/treeherder/blob/master/ui/js/values.js
+        platform: linux64
+    # Rather then enforcing particular conventions we require that all build
+    # tasks provide the "build" extra field to specify where the build and tests
+    # files are located.
+    locations:
+      build: 'public/build/target.spidermonkey.tar.bz2'
+      tests: 'public/build/target.spidermonkey.tar.bz2'
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_compacting.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-compacting'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'compacting'
+  metadata:
+    name: '[TC] Spidermonkey Compactng'
+    description: 'Spidermonkey Compacting'
+  extra:
+    treeherder:
+      symbol: cgc
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_generational.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-generational'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'generational'
+  metadata:
+    name: '[TC] Spidermonkey Generational'
+    description: 'Spidermonkey Generational'
+  extra:
+    treeherder:
+      symbol: ggc
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_plain.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-plain'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'plain'
+  metadata:
+    name: '[TC] Spidermonkey Plain'
+    description: 'Spidermonkey Plain'
+  extra:
+    treeherder:
+      symbol: p
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_plaindebug.yml
@@ -0,0 +1,17 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-plaindebug'
+    build_type: 'debug'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'plaindebug'
+  metadata:
+    name: '[TC] Spidermonkey Plain Debug'
+    description: 'Spidermonkey Plain Debug'
+  extra:
+    treeherder:
+      symbol: p
+      collection:
+        debug: true
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_rootanalysis.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-rootanalysis'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'rootanalysis'
+  metadata:
+    name: '[TC] Spidermonkey Root Analysis'
+    description: 'Spidermonkey Root Analysis'
+  extra:
+    treeherder:
+      symbol: r
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_warnaserr.yml
@@ -0,0 +1,15 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-warnaserr'
+    build_type: 'opt'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'warnaserr'
+  metadata:
+    name: '[TC] Spidermonkey Fail-On-Warnings Build'
+    description: 'Spidermonkey Fail-On-Warnings Build'
+  extra:
+    treeherder:
+      symbol: e
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/sm_warnaserrdebug.yml
@@ -0,0 +1,17 @@
+$inherits:
+  from: 'tasks/builds/sm_base.yml'
+  variables:
+    build_name: 'sm-warnaserrdebug'
+    build_type: 'debug'
+task:
+  payload:
+    env:
+      SPIDERMONKEY_VARIANT: 'warnaserrdebug'
+  metadata:
+    name: '[TC] Spidermonkey Fail-On-Warnings Build Debug'
+    description: 'Spidermonkey Fail-On-Warnings Build Debug'
+  extra:
+    treeherder:
+      symbol: e
+      collection:
+        debug: true
--- a/testing/taskcluster/tasks/job_flags.yml
+++ b/testing/taskcluster/tasks/job_flags.yml
@@ -4,21 +4,28 @@
 # List of all possible flags for each category of tests used in the case where
 # "all" is specified.
 flags:
   builds:
     - emulator
     - emulator-jb
     - emulator-kk
     - emulator-x86-kk
-    - linux32_gecko  # b2g desktop linux 32 bit
-    - linux64_gecko  # b2g desktop linux 64 bit
-    - linux64-mulet  # Firefox desktop - b2g gecko linux 64 bit
-    - macosx64_gecko # b2g desktop osx 64 bit
-    - win32_gecko    # b2g desktop win 32 bit
+    - linux32_gecko   # b2g desktop linux 32 bit
+    - linux64_gecko   # b2g desktop linux 64 bit
+    - linux64-mulet   # Firefox desktop - b2g gecko linux 64 bit
+    - macosx64_gecko  # b2g desktop osx 64 bit
+    - win32_gecko     # b2g desktop win 32 bit
+    - sm-plain        # spidermonkey plain
+    - sm-arm-sim      # spidermonkey arm-sim
+    - sm-arm-sim-osx  # spidermonkey arm-sim-osx
+    - sm-compacting   # spidermonkey compacting
+    - sm-generational # spidermonkey generational
+    - sm-rootanalysis # spidermonkey rootanalysis
+    - sm-warnaserr    # spidermonkey warnings-as-errors
 
   tests:
     - cppunit
     - crashtest
     - crashtest-ipc
     - gaia-build
     - gaia-build-unit
     - gaia-js-integration
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/016.html.ini
+++ /dev/null
@@ -1,15 +0,0 @@
-[016.html]
-  type: testharness
-  expected: TIMEOUT
-  [Timeout on original window, scope]
-    expected: NOTRUN
-
-  [Timeout on original window, this object]
-    expected: NOTRUN
-
-  [Timeout on new window, scope]
-    expected: NOTRUN
-
-  [Timeout on new window, this object]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/094.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[094.html]
-  type: testharness
-  expected: TIMEOUT
deleted file mode 100644
--- a/testing/web-platform/meta/old-tests/submission/Opera/script_scheduling/101.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[101.html]
-  type: testharness
-  expected: TIMEOUT
-  [ scheduler: defer script after initial onload event]
-    expected: NOTRUN
-
--- a/testing/web-platform/tests/old-tests/submission/Opera/script_scheduling/094.html
+++ b/testing/web-platform/tests/old-tests/submission/Opera/script_scheduling/094.html
@@ -3,19 +3,21 @@
 	<title> scheduler: parser-created defer script after document load</title>
 	<script src="/resources/testharness.js"></script>
 	<script src="/resources/testharnessreport.js"></script>
 	<script src="testlib/testlib.js"></script>
 </head>
 <body>
 
 	<div id="log">FAILED (This TC requires JavaScript enabled)</div>
+	<iframe id="myFrame"></iframe>
 
 	<script>
           var t = async_test(undefined, {timeout:3500});
           onload = t.step_func(function() {
-            document.open();
-            document.write("<title> scheduler: parser-created defer script after document load</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><script>var t=async_test()<\/script><div id=log></div><script defer src='data:text/javascript,t.done();'><\/script>");
-            document.close();
-            setTimeout(t.step_func(function() {assert_unreached()}, 500));
+            var doc = document.getElementById("myFrame").contentDocument;
+            var win = document.getElementById("myFrame").contentWindow;
+            doc.open();
+            doc.write("<title> scheduler: parser-created defer script after document load</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><script>var t=async_test()<\/script><div id=log></div><script defer src='data:text/javascript,parent.t.done();'><\/script>");
+            doc.close();
           })
         </script>
 </body></html>
--- a/testing/web-platform/tests/old-tests/submission/Opera/script_scheduling/101.html
+++ b/testing/web-platform/tests/old-tests/submission/Opera/script_scheduling/101.html
@@ -3,28 +3,31 @@
 	<title> scheduler: defer script after initial onload event</title>
 	<script src="/resources/testharness.js"></script>
 	<script src="/resources/testharnessreport.js"></script>
 	<script src="testlib/testlib.js"></script>
 </head>
 <body>
 
 	<div id="log">FAILED (This TC requires JavaScript enabled)</div>
+	<iframe id="myFrame"></iframe>
 
 	<script>
           var t = async_test();
           onload = t.step_func(
             function() {
-              document.open();
-              document.write("<title> scheduler: defer script after initial onload event</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><div id='log'>document.written content</div><script>log('inline script #1'); t = async_test();<\/script><script src='scripts/include-1.js'><\/script><script defer src='scripts/include-2.js'><\/script>");
-              document.close();
+              var doc = document.getElementById("myFrame").contentDocument;
+              var win = document.getElementById("myFrame").contentWindow;
+              doc.open();
+              doc.write("<title> scheduler: defer script after initial onload event</title><script src='/resources/testharness.js'><\/script><script src='/resources/testharnessreport.js'><\/script><script src='testlib/testlib.js'><\/script><div id='log'>document.written content</div><script>log('inline script #1'); t = async_test();<\/script><script src='scripts/include-1.js'><\/script><script defer src='scripts/include-2.js'><\/script>");
+              doc.close();
               //Note that the *window* object has changed but the *global scope* of the script has not.
-              window.setTimeout(
+              win.setTimeout(
                 function() {
                   window.t.step(
                     function() {
-                      window.assert_array_equals(window.eventOrder, ['inline script #1', 'external script #1', 'external script #2']);
+                      window.assert_array_equals(win.eventOrder, ['inline script #1', 'external script #1', 'external script #2']);
                       window.t.done();
                 })}, 1000);
             });
         </script>
 </body>
 </html>
--- a/toolkit/components/addoncompat/tests/Makefile.in
+++ b/toolkit/components/addoncompat/tests/Makefile.in
@@ -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/.
 
 include $(topsrcdir)/config/rules.mk
 
-# This is so hacky. Waiting on bug 988938.
-addondir = $(srcdir)/addon
 TESTROOT = $(CURDIR)/$(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 testdir = $(TESTROOT)/browser
 
+# This is so hacky. Waiting on bug 988938.
 libs::
 	$(EXIT_ON_ERROR) \
 	$(NSINSTALL) -D $(testdir); \
-	(cd $(addondir) && zip -qr $(testdir)/addon.xpi *)
+	(cd $(srcdir)/addon && zip -qr $(testdir)/addon.xpi *); \
+	(cd $(srcdir)/compat-addon && zip -qr $(testdir)/compat-addon.xpi *)
--- a/toolkit/components/addoncompat/tests/browser/browser_addonShims.js
+++ b/toolkit/components/addoncompat/tests/browser/browser_addonShims.js
@@ -1,11 +1,13 @@
 let {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const ADDON_URL = "http://example.com/browser/toolkit/components/addoncompat/tests/browser/addon.xpi";
+const COMPAT_ADDON_URL = "http://example.com/browser/toolkit/components/addoncompat/tests/browser/compat-addon.xpi";
 
 // Install a test add-on that will exercise e10s shims.
 //   url: Location of the add-on.
 function addAddon(url)
 {
   info("Installing add-on: " + url);
 
   return new Promise(function(resolve, reject) {
@@ -51,9 +53,15 @@ add_task(function* test_addon_shims() {
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({set: [["dom.ipc.shims.enabledWarnings", true]]},
                              resolve);
   });
 
   let addon = yield addAddon(ADDON_URL);
   yield window.runAddonShimTests({ok: ok, is: is, info: info});
   yield removeAddon(addon);
+
+  if (Services.appinfo.browserTabsRemoteAutostart) {
+    addon = yield addAddon(COMPAT_ADDON_URL);
+    yield window.runAddonTests({ok: ok, is: is, info: info});
+    yield removeAddon(addon);
+  }
 });
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/compat-addon/bootstrap.js
@@ -0,0 +1,101 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/BrowserUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const baseURL = "http://mochi.test:8888/browser/" +
+  "toolkit/components/addoncompat/tests/browser/";
+
+function forEachWindow(f)
+{
+  let wins = Services.ww.getWindowEnumerator("navigator:browser");
+  while (wins.hasMoreElements()) {
+    let win = wins.getNext();
+    if (win.gBrowser) {
+      f(win);
+    }
+  }
+}
+
+function addLoadListener(target, listener)
+{
+  function frameScript() {
+    addEventListener("load", function handler(event) {
+      removeEventListener("load", handler, true);
+      sendAsyncMessage("compat-test:loaded");
+    }, true);
+  }
+  target.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")()", false);
+  target.messageManager.addMessageListener("compat-test:loaded", function handler() {
+    target.messageManager.removeMessageListener("compat-test:loaded", handler);
+    listener();
+  });
+}
+
+let gWin;
+let gBrowser;
+let ok, is, info;
+
+// Make sure that the shims for window.content, browser.contentWindow,
+// and browser.contentDocument are working.
+function testContentWindow()
+{
+  return new Promise(function(resolve, reject) {
+    const url = baseURL + "browser_addonShims_testpage.html";
+    let tab = gBrowser.addTab("about:blank");
+    gBrowser.selectedTab = tab;
+    let browser = tab.linkedBrowser;
+    addLoadListener(browser, function handler() {
+      ok(!gWin.content, "content is defined on chrome window");
+      ok(!browser.contentWindow, "contentWindow is defined");
+      ok(!browser.contentDocument, "contentWindow is defined");
+
+      gBrowser.removeTab(tab);
+      resolve();
+    });
+    browser.loadURI(url);
+  });
+}
+
+function runTests(win, funcs)
+{
+  ok = funcs.ok;
+  is = funcs.is;
+  info = funcs.info;
+
+  gWin = win;
+  gBrowser = win.gBrowser;
+
+  return testContentWindow();
+}
+
+/*
+ bootstrap.js API
+*/
+
+function startup(aData, aReason)
+{
+  forEachWindow(win => {
+    win.runAddonTests = (funcs) => runTests(win, funcs);
+  });
+}
+
+function shutdown(aData, aReason)
+{
+  forEachWindow(win => {
+    delete win.runAddonTests;
+  });
+}
+
+function install(aData, aReason)
+{
+}
+
+function uninstall(aData, aReason)
+{
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/compat-addon/install.rdf
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>test-addon-shim-2@tests.mozilla.org</em:id>
+    <em:version>1</em:version>
+    <em:type>2</em:type>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>Test addon shims 2</em:name>
+    <em:description>Test an add-on that doesn't need multiprocess shims.</em:description>
+    <em:multiprocessCompatible>true</em:multiprocessCompatible>
+
+    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>toolkit@mozilla.org</em:id>
+        <em:minVersion>10.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -85,22 +85,30 @@ this.ForgetAboutSite = {
                getService(Ci.mozIGeckoMediaPluginChromeService);
     mps.forgetThisSite(aDomain);
 
     // Plugin data
     const phInterface = Ci.nsIPluginHost;
     const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
     let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
     let tags = ph.getPluginTags();
+    let promises = [];
     for (let i = 0; i < tags.length; i++) {
-      try {
-        ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1);
-      } catch (e) {
-        // Ignore errors from the plugin
-      }
+      let promise = new Promise(resolve => {
+        let tag = tags[i];
+        try {
+          ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1, function(rv) {
+            resolve();
+          });
+        } catch (e) {
+          // Ignore errors from the plugin, but resolve the promise
+          resolve();
+        }
+      });
+      promises.push(promise);
     }
 
     // Downloads
     Task.spawn(function*() {
       let list = yield Downloads.getList(Downloads.ALL);
       list.removeFinished(download => hasRootDomain(
            NetUtil.newURI(download.source.url).host, aDomain));
     }).then(null, Cu.reportError);
@@ -163,10 +171,11 @@ this.ForgetAboutSite = {
       handleError: function() {}
     });
 
     // Predictive network data - like cache, no way to clear this per
     // domain, so just trash it all
     let np = Cc["@mozilla.org/network/predictor;1"].
              getService(Ci.nsINetworkPredictor);
     np.reset();
+    return Promise.all(promises);
   }
 };
--- a/toolkit/forgetaboutsite/test/browser/browser_clearplugindata.js
+++ b/toolkit/forgetaboutsite/test/browser/browser_clearplugindata.js
@@ -77,33 +77,42 @@ function do_test()
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     setFinishedCallback(function() {
       ok(stored(["192.168.1.1","foo.com","nonexistent.foo.com","bar.com","localhost"]),
         "Data stored for sites");
 
       // Clear data for "foo.com" and its subdomains.
-      ForgetAboutSite.removeDataFromDomain("foo.com");
+      ForgetAboutSite.removeDataFromDomain("foo.com").then(test1);
+    });
+    function test1() {
+      dump("test1\n");
       ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
       ok(!stored(["foo.com"]), "Data cleared for foo.com");
       ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
 
       // Clear data for "bar.com" using a subdomain.
-      ForgetAboutSite.removeDataFromDomain("foo.bar.com");
+      ForgetAboutSite.removeDataFromDomain("foo.bar.com").then(test2);
+    }
+    function test2() {
       ok(!stored(["bar.com"]), "Data cleared for bar.com");
 
       // Clear data for "192.168.1.1".
-      ForgetAboutSite.removeDataFromDomain("192.168.1.1");
+      ForgetAboutSite.removeDataFromDomain("192.168.1.1").then(test3);
+    }
+    function test3() {
       ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
 
       // Clear data for "localhost".
-      ForgetAboutSite.removeDataFromDomain("localhost");
+      ForgetAboutSite.removeDataFromDomain("localhost").then(test4);
+    }
+    function test4() {
       ok(!stored(null), "All data cleared");
 
       gBrowser.removeCurrentTab();
 
       executeSoon(finish);
-    });
+    }
   }, true);
   content.location = testURL;
 }
 
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -17,9 +17,9 @@ ifdef COMPILE_ENVIRONMENT
 target:: $(FINAL_TARGET)/dependentlibs.list
 endif
 
 $(FINAL_TARGET)/dependentlibs.list: $(topsrcdir)/toolkit/library/dependentlibs.py $(SHARED_LIBRARY) $(wildcard $(if $(wildcard $(FINAL_TARGET)/dependentlibs.list),$(addprefix $(FINAL_TARGET)/,$(shell cat $(FINAL_TARGET)/dependentlibs.list))))
 	$(PYTHON) $< $(SHARED_LIBRARY) -L $(FINAL_TARGET) $(if $(TOOLCHAIN_PREFIX),$(addprefix -p ,$(TOOLCHAIN_PREFIX))) > $@
 
 .PHONY: gtestxul
 gtestxul:
-	$(MAKE) -C gtest target LINK_GTEST=1
+	$(MAKE) -C $(DEPTH) toolkit/library/gtest/target LINK_GTEST=1
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -103,16 +103,17 @@ USE_LIBS += [
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
     USE_LIBS += [
         'sandboxbroker',
     ]
 
 USE_LIBS += [
     'gkmedias',
+    'lgpllibs',
     'nspr',
     'nss',
     'sqlite',
     'zlib',
 ]
 
 if CONFIG['USE_ICU']:
     USE_LIBS += [
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/PromiseMessage.jsm
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+this.EXPORTED_SYMBOLS = ['PromiseMessage'];
+
+let msgId = 0;
+
+let PromiseMessage = {
+  send(messageManager, name, data = {}) {
+    let id = msgId++;
+
+    // Make a copy of data so that the caller doesn't see us setting 'id'.
+    let dataCopy = {};
+    for (let prop in data) {
+      dataCopy[prop] = data[prop];
+    }
+    dataCopy.id = id;
+
+    // Send the message.
+    messageManager.sendAsyncMessage(name, dataCopy);
+
+    // Return a promise that resolves when we get a reply (a message of the same name).
+    return new Promise(resolve => {
+      messageManager.addMessageListener(name, function listener(reply) {
+        if (reply.data.id !== id) {
+          return;
+        }
+        messageManager.removeMessageListener(name, listener);
+        resolve(reply);
+      });
+    });
+  }
+};
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -43,16 +43,17 @@ EXTRA_JS_MODULES += [
     'PageMetadata.jsm',
     'PermissionsUtils.jsm',
     'PopupNotifications.jsm',
     'Preferences.jsm',
     'PrivateBrowsingUtils.jsm',
     'ProfileAge.jsm',
     'Promise-backend.js',
     'Promise.jsm',
+    'PromiseMessage.jsm',
     'PromiseUtils.jsm',
     'PropertyListUtils.jsm',
     'RemoteController.jsm',
     'RemoteFinder.jsm',
     'RemotePageManager.jsm',
     'RemoteSecurityUI.jsm',
     'RemoteWebNavigation.jsm',
     'RemoteWebProgress.jsm',
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -705,17 +705,18 @@ XPCOMUtils.defineLazyServiceGetter(this,
 function EM_R(aProperty) {
   return gRDF.GetResource(PREFIX_NS_EM + aProperty);
 }
 
 function createAddonDetails(id, aAddon) {
   return {
     id: id || aAddon.id,
     type: aAddon.type,
-    version: aAddon.version
+    version: aAddon.version,
+    multiprocessCompatible: aAddon.multiprocessCompatible
   };
 }
 
 /**
  * Converts an RDF literal, resource or integer into a string.
  *
  * @param  aLiteral
  *         The RDF object to convert
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -282,16 +282,17 @@ endif
 DIST_FILES += libnss3.so
 ifndef MOZ_FOLD_LIBS
 DIST_FILES += \
   libssl3.so \
   libsmime3.so \
   $(NULL)
 endif
 DIST_FILES += \
+  liblgpllibs.so \
   libxul.so \
   libnssckbi.so \
   libfreebl3.so \
   libsoftokn3.so \
   resources.arsc \
   AndroidManifest.xml \
   chrome \
   components \
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -2873,16 +2873,18 @@ nsNativeThemeCocoa::DrawWidgetBackground
       }
       break;
     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
       if (ScrollbarTrackAndThumbDrawSeparately()) {
         BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars();
         BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL);
         BOOL isRolledOver = IsParentScrollbarRolledOver(aFrame);
+        nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
+        bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
         if (isOverlay && (!nsCocoaFeatures::OnMountainLionOrLater() || !isRolledOver)) {
           if (isHorizontal) {
             macRect.origin.y += 4;
             macRect.size.height -= 4;
           } else {
             if (aFrame->StyleVisibility()->mDirection !=
                 NS_STYLE_DIRECTION_RTL) {
               macRect.origin.x += 4;
@@ -2891,17 +2893,17 @@ nsNativeThemeCocoa::DrawWidgetBackground
           }
         }
         const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
         // Scrollbar thumbs have a too high minimum width when rendered through
         // NSAppearance on 10.10, so we call RenderWithCoreUILegacy here.
         RenderWithCoreUILegacy(macRect, cgContext,
                 [NSDictionary dictionaryWithObjectsAndKeys:
                   (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
-                  @"regular", @"size",
+                  (isSmall ? @"small" : @"regular"), @"size",
                   (isRolledOver ? @"rollover" : @"normal"), @"state",
                   (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
                   (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
                   [NSNumber numberWithBool:YES], @"indiconly",
                   [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
                   [NSNumber numberWithBool:YES], @"is.flipped",
                   nil]);
       }
@@ -2921,16 +2923,18 @@ nsNativeThemeCocoa::DrawWidgetBackground
 #endif
     break;
     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
       if (ScrollbarTrackAndThumbDrawSeparately()) {
         BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars();
         if (!isOverlay || IsParentScrollbarRolledOver(aFrame)) {
           BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
+          nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
+          bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
           if (isOverlay && !nsCocoaFeatures::OnMountainLionOrLater()) {
             // On OSX 10.7, scrollbars don't grow when hovered.
             // The adjustments below were obtained by trial and error.
             if (isHorizontal) {
               macRect.origin.y += 2.0;
             } else {
               if (aFrame->StyleVisibility()->mDirection !=
                     NS_STYLE_DIRECTION_RTL) {
@@ -2939,17 +2943,17 @@ nsNativeThemeCocoa::DrawWidgetBackground
                 macRect.origin.x -= 1.0;
               }
             }
           }
           const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
           RenderWithCoreUILegacy(macRect, cgContext,
                   [NSDictionary dictionaryWithObjectsAndKeys:
                     (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget",
-                    @"regular", @"size",
+                    (isSmall ? @"small" : @"regular"), @"size",
                     (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
                     (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
                     [NSNumber numberWithBool:YES], @"noindicator",
                     [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
                     [NSNumber numberWithBool:YES], @"is.flipped",
                     nil]);
         }
       }
--- a/xpcom/glue/nsBaseHashtable.h
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -163,17 +163,17 @@ public:
   /**
    * enumerate entries in the hashtable, without allowing changes
    * @param aEnumFunc enumeration callback
    * @param aUserArg passed unchanged to the EnumReadFunction
    */
   uint32_t EnumerateRead(EnumReadFunction aEnumFunc, void* aUserArg) const
   {
     uint32_t n = 0;
-    for (auto iter = this->mTable.Iter(); !iter.Done(); iter.Next()) {
+    for (auto iter = this->mTable.ConstIter(); !iter.Done(); iter.Next()) {
       auto entry = static_cast<EntryType*>(iter.Get());
       PLDHashOperator op = aEnumFunc(entry->GetKey(), entry->mData, aUserArg);
       n++;
       MOZ_ASSERT(!(op & PL_DHASH_REMOVE));
       if (op & PL_DHASH_STOP) {
         break;
       }
     }
@@ -199,17 +199,17 @@ public:
    * enumerate entries in the hashtable, allowing changes. This
    * functions write-locks the hashtable.
    * @param aEnumFunc enumeration callback
    * @param aUserArg passed unchanged to the EnumFunction
    */
   uint32_t Enumerate(EnumFunction aEnumFunc, void* aUserArg)
   {
     uint32_t n = 0;
-    for (auto iter = this->mTable.RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = this->mTable.Iter(); !iter.Done(); iter.Next()) {
       auto entry = static_cast<EntryType*>(iter.Get());
       PLDHashOperator op = aEnumFunc(entry->GetKey(), entry->mData, aUserArg);
       n++;
       if (op & PL_DHASH_REMOVE) {
         iter.Remove();
       }
       if (op & PL_DHASH_STOP) {
         break;
--- a/xpcom/glue/nsTHashtable.h
+++ b/xpcom/glue/nsTHashtable.h
@@ -208,17 +208,17 @@ public:
    * @param     enumFunc the <code>Enumerator</code> function to call
    * @param     userArg a pointer to pass to the
    *            <code>Enumerator</code> function
    * @return    the number of entries actually enumerated
    */
   uint32_t EnumerateEntries(Enumerator aEnumFunc, void* aUserArg)
   {
     uint32_t n = 0;
-    for (auto iter = mTable.RemovingIter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
       auto entry = static_cast<EntryType*>(iter.Get());
       PLDHashOperator op = aEnumFunc(entry, aUserArg);
       n++;
       if (op & PL_DHASH_REMOVE) {
         iter.Remove();
       }
       if (op & PL_DHASH_STOP) {
         break;
--- a/xpcom/glue/pldhash.cpp
+++ b/xpcom/glue/pldhash.cpp
@@ -748,17 +748,17 @@ PLDHashTable::SizeOfExcludingThis(
 #endif
 
   if (!mEntryStore) {
     return 0;
   }
 
   size_t n = aMallocSizeOf(mEntryStore);
   if (aSizeOfEntryExcludingThis) {
-    for (auto iter = Iter(); !iter.Done(); iter.Next()) {
+    for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
       n += aSizeOfEntryExcludingThis(iter.Get(), aMallocSizeOf, aArg);
     }
   }
 
   return n;
 }
 
 MOZ_ALWAYS_INLINE size_t
@@ -789,45 +789,51 @@ PL_DHashTableSizeOfIncludingThis(
   return aTable->SizeOfIncludingThis(aSizeOfEntryExcludingThis,
                                      aMallocSizeOf, aArg);
 }
 
 PLDHashTable::Iterator::Iterator(Iterator&& aOther)
   : mTable(aOther.mTable)
   , mCurrent(aOther.mCurrent)
   , mLimit(aOther.mLimit)
+  , mHaveRemoved(aOther.mHaveRemoved)
 {
   // No need to change |mChecker| here.
   aOther.mTable = nullptr;
   aOther.mCurrent = nullptr;
   aOther.mLimit = nullptr;
+  aOther.mHaveRemoved = false;
 }
 
-PLDHashTable::Iterator::Iterator(const PLDHashTable* aTable)
+PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
   : mTable(aTable)
   , mCurrent(mTable->mEntryStore)
   , mLimit(mTable->mEntryStore + mTable->Capacity() * mTable->mEntrySize)
+  , mHaveRemoved(false)
 {
 #ifdef DEBUG
   mTable->mChecker.StartReadOp();
 #endif
 
   // Advance to the first live entry, or to the end if there are none.
   while (IsOnNonLiveEntry()) {
     mCurrent += mTable->mEntrySize;
   }
 }
 
 PLDHashTable::Iterator::~Iterator()
 {
+  if (mTable) {
+    if (mHaveRemoved) {
+      mTable->ShrinkIfAppropriate();
+    }
 #ifdef DEBUG
-  if (mTable) {
     mTable->mChecker.EndReadOp();
+#endif
   }
-#endif
 }
 
 bool
 PLDHashTable::Iterator::Done() const
 {
   return mCurrent == mLimit;
 }
 
@@ -852,51 +858,21 @@ PLDHashTable::Iterator::Next()
 {
   MOZ_ASSERT(!Done());
 
   do {
     mCurrent += mTable->mEntrySize;
   } while (IsOnNonLiveEntry());
 }
 
-PLDHashTable::RemovingIterator::RemovingIterator(RemovingIterator&& aOther)
-  : Iterator(mozilla::Move(aOther))
-  , mHaveRemoved(aOther.mHaveRemoved)
-{
-  // Do nothing with mChecker here. We treat RemovingIterator like Iterator --
-  // i.e. as a read operation -- until the very end. Then, if any elements have
-  // been removed, we temporarily treat it as a write operation.
-}
-
-PLDHashTable::RemovingIterator::RemovingIterator(PLDHashTable* aTable)
-  : Iterator(aTable)
-  , mHaveRemoved(false)
-{
-}
-
-PLDHashTable::RemovingIterator::~RemovingIterator()
-{
-  if (mHaveRemoved) {
-#ifdef DEBUG
-    AutoIteratorRemovalOp op(mTable->mChecker);
-#endif
-
-    // Why is this cast needed? In Iterator, |mTable| is const. In
-    // RemovingIterator it should be non-const, but it inherits from Iterator
-    // so that's not possible. But it's ok because RemovingIterator's
-    // constructor takes a pointer to a non-const table in the first place.
-    const_cast<PLDHashTable*>(mTable)->ShrinkIfAppropriate();
-  }
-}
-
 void
-PLDHashTable::RemovingIterator::Remove()
+PLDHashTable::Iterator::Remove()
 {
   // This cast is needed for the same reason as the one in the destructor.
-  const_cast<PLDHashTable*>(mTable)->RawRemove(Get());
+  mTable->RawRemove(Get());
   mHaveRemoved = true;
 }
 
 #ifdef DEBUG
 MOZ_ALWAYS_INLINE void
 PLDHashTable::MarkImmutable()
 {
   mChecker.SetNonWritable();
--- a/xpcom/glue/pldhash.h
+++ b/xpcom/glue/pldhash.h
@@ -343,91 +343,81 @@ public:
 #ifdef DEBUG
   void MarkImmutable();
 #endif
 
   void MoveEntryStub(const PLDHashEntryHdr* aFrom, PLDHashEntryHdr* aTo);
 
   void ClearEntryStub(PLDHashEntryHdr* aEntry);
 
-  // This is an iterator for PLDHashtable. It is not safe to modify the
-  // table while it is being iterated over; on debug builds, attempting to do
-  // so will result in an assertion failure.
+  // This is an iterator for PLDHashtable. Assertions will detect some, but not
+  // all, mid-iteration table modifications that might invalidate (e.g.
+  // reallocate) the entry storage.
+  //
+  // Any element can be removed during iteration using Remove(). If any
+  // elements are removed, the table may be resized once iteration ends.
   //
   // Example usage:
   //
   //   for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
   //     auto entry = static_cast<FooEntry*>(iter.Get());
   //     // ... do stuff with |entry| ...
+  //     // ... possibly call iter.Remove() once ...
   //   }
   //
   // or:
   //
   //   for (PLDHashTable::Iterator iter(&table); !iter.Done(); iter.Next()) {
   //     auto entry = static_cast<FooEntry*>(iter.Get());
   //     // ... do stuff with |entry| ...
+  //     // ... possibly call iter.Remove() once ...
   //   }
   //
   // The latter form is more verbose but is easier to work with when
   // making subclasses of Iterator.
   //
   class Iterator
   {
   public:
-    explicit Iterator(const PLDHashTable* aTable);
+    explicit Iterator(PLDHashTable* aTable);
     Iterator(Iterator&& aOther);
     ~Iterator();
+
     bool Done() const;                // Have we finished?
     PLDHashEntryHdr* Get() const;     // Get the current entry.
     void Next();                      // Advance to the next entry.
 
+    // Remove the current entry. Must only be called once per entry, and Get()
+    // must not be called on that entry afterwards.
+    void Remove();
+
   protected:
-    const PLDHashTable* mTable;       // Main table pointer.
+    PLDHashTable* mTable;             // Main table pointer.
 
   private:
     char* mCurrent;                   // Pointer to the current entry.
     char* mLimit;                     // One past the last entry.
 
+    bool mHaveRemoved;                // Have any elements been removed?
+
     bool IsOnNonLiveEntry() const;
 
     Iterator() = delete;
     Iterator(const Iterator&) = delete;
     Iterator& operator=(const Iterator&) = delete;
     Iterator& operator=(const Iterator&&) = delete;
   };
 
-  Iterator Iter() const { return Iterator(this); }
-
-  // This is an iterator that allows elements to be removed during iteration.
-  // If any elements are removed, the table may be resized once iteration ends.
-  // Its usage is similar to that of Iterator, with the addition that Remove()
-  // can be called once per element.
-  class RemovingIterator : public Iterator
-  {
-  public:
-    explicit RemovingIterator(PLDHashTable* aTable);
-    RemovingIterator(RemovingIterator&& aOther);
-    ~RemovingIterator();
+  Iterator Iter() { return Iterator(this); }
 
-    // Remove the current entry. Must only be called once per entry, and Get()
-    // must not be called on that entry afterwards.
-    void Remove();
-
-  private:
-    bool mHaveRemoved;      // Have any elements been removed?
-
-    RemovingIterator() = delete;
-    RemovingIterator(const RemovingIterator&) = delete;
-    RemovingIterator& operator=(const RemovingIterator&) = delete;
-    RemovingIterator& operator=(const RemovingIterator&&) = delete;
-  };
-
-  RemovingIterator RemovingIter() const
+  // Use this if you need to initialize an Iterator in a const method. If you
+  // use this case, you should not call Remove() on the iterator.
+  Iterator ConstIter() const
   {
-    return RemovingIterator(const_cast<PLDHashTable*>(this));
+    return Iterator(const_cast<PLDHashTable*>(this));
   }
 
 private:
   static bool EntryIsFree(PLDHashEntryHdr* aEntry);
 
   // We store mHashShift rather than sizeLog2 to optimize the collision-free
   // case in SearchTable.
   uint32_t CapacityFromHashShift() const
rename from xpcom/tests/TestPLDHash.cpp
rename to xpcom/tests/gtest/TestPLDHash.cpp
--- a/xpcom/tests/TestPLDHash.cpp
+++ b/xpcom/tests/gtest/TestPLDHash.cpp
@@ -1,90 +1,65 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include <stdio.h>
 #include "pldhash.h"
+#include "gtest/gtest.h"
 
 // This test mostly focuses on edge cases. But more coverage of normal
 // operations wouldn't be a bad thing.
 
-namespace TestPLDHash {
-
-static bool test_pldhash_Init_capacity_ok()
+TEST(PLDHashTableTest, InitCapacityOk)
 {
   // Try the largest allowed capacity.  With PL_DHASH_MAX_CAPACITY==1<<26, this
   // would allocate (if we added an element) 0.5GB of entry store on 32-bit
   // platforms and 1GB on 64-bit platforms.
   //
   // Ideally we'd also try (a) a too-large capacity, and (b) a large capacity
   // combined with a large entry size that when multipled overflow. But those
   // cases would cause the test to abort immediately.
   //
   // Furthermore, ideally we'd also try a large-but-ok capacity that almost but
   // doesn't quite overflow, but that would result in allocating just under 4GB
   // of entry storage.  That's very likely to fail on 32-bit platforms, so such
   // a test wouldn't be reliable.
   //
   PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
                  PL_DHASH_MAX_INITIAL_LENGTH);
-
-  return true;
 }
 
-static bool test_pldhash_lazy_storage()
+TEST(PLDHashTableTest, LazyStorage)
 {
   PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub));
 
   // PLDHashTable allocates entry storage lazily. Check that all the non-add
   // operations work appropriately when the table is empty and the storage
   // hasn't yet been allocated.
 
-  if (t.Capacity() != 0) {
-    return false;
-  }
-
-  if (t.EntrySize() != sizeof(PLDHashEntryStub)) {
-    return false;
-  }
+  ASSERT_EQ(t.Capacity(), 0u);
+  ASSERT_EQ(t.EntrySize(), sizeof(PLDHashEntryStub));
+  ASSERT_EQ(t.EntryCount(), 0u);
+  ASSERT_EQ(t.Generation(), 0u);
 
-  if (t.EntryCount() != 0) {
-    return false;
-  }
-
-  if (t.Generation() != 0) {
-    return false;
-  }
-
-  if (PL_DHashTableSearch(&t, (const void*)1)) {
-    return false;   // search succeeded?
-  }
+  ASSERT_TRUE(!PL_DHashTableSearch(&t, (const void*)1));
 
   // No result to check here, but call it to make sure it doesn't crash.
   PL_DHashTableRemove(&t, (const void*)2);
 
   for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
-    return false; // shouldn't hit this on an empty table
-  }
-
-  for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
-    return false; // shouldn't hit this on an empty table
+    ASSERT_TRUE(false); // shouldn't hit this on an empty table
   }
 
   // Using a null |mallocSizeOf| should be fine because it shouldn't be called
   // for an empty table.
   mozilla::MallocSizeOf mallocSizeOf = nullptr;
-  if (PL_DHashTableSizeOfExcludingThis(&t, nullptr, mallocSizeOf) != 0) {
-    return false;   // size is non-zero?
-  }
-
-  return true;
+  ASSERT_EQ(PL_DHashTableSizeOfExcludingThis(&t, nullptr, mallocSizeOf), 0u);
 }
 
 // A trivial hash function is good enough here. It's also super-fast for
 // test_pldhash_grow_to_max_capacity() because we insert the integers 0..,
 // which means it's collision-free.
 static PLDHashNumber
 TrivialHash(PLDHashTable *table, const void *key)
 {
@@ -101,17 +76,17 @@ TrivialInitEntry(PLDHashEntryHdr* aEntry
 static const PLDHashTableOps trivialOps = {
   TrivialHash,
   PL_DHashMatchEntryStub,
   PL_DHashMoveEntryStub,
   PL_DHashClearEntryStub,
   TrivialInitEntry
 };
 
-static bool test_pldhash_move_semantics()
+TEST(PLDHashTableTest, MoveSemantics)
 {
   PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
   PL_DHashTableAdd(&t1, (const void*)88);
   PLDHashTable t2(&trivialOps, sizeof(PLDHashEntryStub));
   PL_DHashTableAdd(&t2, (const void*)99);
 
   t1 = mozilla::Move(t1);   // self-move
 
@@ -130,79 +105,63 @@ static bool test_pldhash_move_semantics(
   t5 = mozilla::Move(t6);   // empty overwritten with non-empty
 
   PLDHashTable t7(&trivialOps, sizeof(PLDHashEntryStub));
   PLDHashTable t8(mozilla::Move(t7));  // new table constructed with uninited
 
   PLDHashTable t9(&trivialOps, sizeof(PLDHashEntryStub));
   PL_DHashTableAdd(&t9, (const void*)88);
   PLDHashTable t10(mozilla::Move(t9));  // new table constructed with inited
-
-  return true;
 }
 
-static bool test_pldhash_Clear()
+TEST(PLDHashTableTest, Clear)
 {
   PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
 
   t1.Clear();
-  if (t1.EntryCount() != 0) {
-    return false;
-  }
+  ASSERT_EQ(t1.EntryCount(), 0u);
 
   t1.ClearAndPrepareForLength(100);
-  if (t1.EntryCount() != 0) {
-    return false;
-  }
+  ASSERT_EQ(t1.EntryCount(), 0u);
 
   PL_DHashTableAdd(&t1, (const void*)77);
   PL_DHashTableAdd(&t1, (const void*)88);
   PL_DHashTableAdd(&t1, (const void*)99);
-  if (t1.EntryCount() != 3) {
-    return false;
-  }
+  ASSERT_EQ(t1.EntryCount(), 3u);
 
   t1.Clear();
-  if (t1.EntryCount() != 0) {
-    return false;
-  }
+  ASSERT_EQ(t1.EntryCount(), 0u);
 
   PL_DHashTableAdd(&t1, (const void*)55);
   PL_DHashTableAdd(&t1, (const void*)66);
   PL_DHashTableAdd(&t1, (const void*)77);
   PL_DHashTableAdd(&t1, (const void*)88);
   PL_DHashTableAdd(&t1, (const void*)99);
-  if (t1.EntryCount() != 5) {
-    return false;
-  }
+  ASSERT_EQ(t1.EntryCount(), 5u);
 
   t1.ClearAndPrepareForLength(8192);
-  if (t1.EntryCount() != 0) {
-    return false;
-  }
-
-  return true;
+  ASSERT_EQ(t1.EntryCount(), 0u);
 }
 
-static bool test_pldhash_Iterator()
+TEST(PLDHashTableIterator, Iterator)
 {
   PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
 
   // Explicitly test the move constructor. We do this because, due to copy
   // elision, compilers might optimize away move constructor calls for normal
   // iterator use.
   {
     PLDHashTable::Iterator iter1(&t);
     PLDHashTable::Iterator iter2(mozilla::Move(iter1));
   }
 
   // Iterate through the empty table.
   for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
     (void) iter.Get();
-    return false;   // shouldn't hit this
+    ASSERT_TRUE(false); // shouldn't hit this
   }
 
   // Add three entries.
   PL_DHashTableAdd(&t, (const void*)77);
   PL_DHashTableAdd(&t, (const void*)88);
   PL_DHashTableAdd(&t, (const void*)99);
 
   // Check the iterator goes through each entry once.
@@ -216,92 +175,71 @@ static bool test_pldhash_Iterator()
     if (entry->key == (const void*)88) {
       saw88 = true;
     }
     if (entry->key == (const void*)99) {
       saw99 = true;
     }
     n++;
   }
-  if (!saw77 || !saw88 || !saw99 || n != 3) {
-    return false;
-  }
-
-  return true;
-}
+  ASSERT_TRUE(saw77 && saw88 && saw99 && n == 3);
 
-static bool test_pldhash_RemovingIterator()
-{
-  PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
-
-  // Explicitly test the move constructor. We do this because, due to copy
-  // elision, compilers might optimize away move constructor calls for normal
-  // iterator use.
-  {
-    PLDHashTable::RemovingIterator iter1(&t);
-    PLDHashTable::RemovingIterator iter2(mozilla::Move(iter1));
-  }
+  t.Clear();
 
   // First, we insert 64 items, which results in a capacity of 128, and a load
   // factor of 50%.
   for (intptr_t i = 0; i < 64; i++) {
     PL_DHashTableAdd(&t, (const void*)i);
   }
-  if (t.EntryCount() != 64 || t.Capacity() != 128) {
-    return false;
-  }
+  ASSERT_EQ(t.EntryCount(), 64u);
+  ASSERT_EQ(t.Capacity(), 128u);
 
   // The first removing iterator does no removing; capacity and entry count are
   // unchanged.
-  for (PLDHashTable::RemovingIterator iter(&t); !iter.Done(); iter.Next()) {
+  for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
     (void) iter.Get();
   }
-  if (t.EntryCount() != 64 || t.Capacity() != 128) {
-    return false;
-  }
+  ASSERT_EQ(t.EntryCount(), 64u);
+  ASSERT_EQ(t.Capacity(), 128u);
 
   // The second removing iterator removes 16 items. This reduces the load
   // factor to 37.5% (48 / 128), which isn't low enough to shrink the table.
-  for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
+  for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
     if ((intptr_t)(entry->key) % 4 == 0) {
       iter.Remove();
     }
   }
-  if (t.EntryCount() != 48 || t.Capacity() != 128) {
-    return false;
-  }
+  ASSERT_EQ(t.EntryCount(), 48u);
+  ASSERT_EQ(t.Capacity(), 128u);
 
   // The third removing iterator removes another 16 items. This reduces
   // the load factor to 25% (32 / 128), so the table is shrunk.
-  for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
+  for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
     auto entry = static_cast<PLDHashEntryStub*>(iter.Get());
     if ((intptr_t)(entry->key) % 2 == 0) {
       iter.Remove();
     }
   }
-  if (t.EntryCount() != 32 || t.Capacity() != 64) {
-    return false;
-  }
+  ASSERT_EQ(t.EntryCount(), 32u);
+  ASSERT_EQ(t.Capacity(), 64u);
 
   // The fourth removing iterator removes all remaining items. This reduces
   // the capacity to the minimum.
-  for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
+  for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
     iter.Remove();
   }
-  if (t.EntryCount() != 0 || t.Capacity() != PL_DHASH_MIN_CAPACITY) {
-    return false;
-  }
-
-  return true;
+  ASSERT_EQ(t.EntryCount(), 0u);
+  ASSERT_EQ(t.Capacity(), unsigned(PL_DHASH_MIN_CAPACITY));
 }
 
-// See bug 931062, we skip this test on Android due to OOM.
+// See bug 931062, we skip this test on Android due to OOM. Also, it's slow,
+// and so should always be last.
 #ifndef MOZ_WIDGET_ANDROID
-static bool test_pldhash_grow_to_max_capacity()
+TEST(PLDHashTableTest, GrowToMaxCapacity)
 {
   // This is infallible.
   PLDHashTable* t =
     new PLDHashTable(&trivialOps, sizeof(PLDHashEntryStub), 128);
 
   // Keep inserting elements until failure occurs because the table is full.
   size_t numInserted = 0;
   while (true) {
@@ -310,54 +248,15 @@ static bool test_pldhash_grow_to_max_cap
     }
     numInserted++;
   }
 
   // We stop when the element count is 96.875% of PL_DHASH_MAX_SIZE (see
   // MaxLoadOnGrowthFailure()).
   if (numInserted != PL_DHASH_MAX_CAPACITY - (PL_DHASH_MAX_CAPACITY >> 5)) {
     delete t;
-    return false;
+    ASSERT_TRUE(false);
   }
 
   delete t;
-  return true;
 }
 #endif
 
-//----
-
-typedef bool (*TestFunc)();
-#define DECL_TEST(name) { #name, name }
-
-static const struct Test {
-  const char* name;
-  TestFunc    func;
-} tests[] = {
-  DECL_TEST(test_pldhash_Init_capacity_ok),
-  DECL_TEST(test_pldhash_lazy_storage),
-  DECL_TEST(test_pldhash_move_semantics),
-  DECL_TEST(test_pldhash_Clear),
-  DECL_TEST(test_pldhash_Iterator),
-  DECL_TEST(test_pldhash_RemovingIterator),
-// See bug 931062, we skip this test on Android due to OOM. Also, it's slow,
-// and so should always be last.
-#ifndef MOZ_WIDGET_ANDROID
-  DECL_TEST(test_pldhash_grow_to_max_capacity),
-#endif
-  { nullptr, nullptr }
-};
-
-} // namespace TestPLDHash
-
-using namespace TestPLDHash;
-
-int main(int argc, char *argv[])
-{
-  bool success = true;
-  for (const Test* t = tests; t->name != nullptr; ++t) {
-    bool test_result = t->func();
-    printf("%35s : %s\n", t->name, test_result ? "SUCCESS" : "FAILURE");
-    if (!test_result)
-      success = false;
-  }
-  return success ? 0 : -1;
-}
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -6,16 +6,17 @@
 
 UNIFIED_SOURCES += [
     'Helpers.cpp',
     'TestCloneInputStream.cpp',
     'TestCRT.cpp',
     'TestEncoding.cpp',
     'TestExpirationTracker.cpp',
     'TestPipes.cpp',
+    'TestPLDHash.cpp',
     'TestPriorityQueue.cpp',
     'TestSnappyStreams.cpp',
     'TestStorageStream.cpp',
     'TestStrings.cpp',
     'TestStringStream.cpp',
     'TestSynchronization.cpp',
     'TestTArray.cpp',
     'TestThreadPool.cpp',
--- a/xpcom/tests/moz.build
+++ b/xpcom/tests/moz.build
@@ -61,17 +61,16 @@ GeckoCppUnitTests([
     'TestDeque',
     'TestFile',
     'TestHashtables',
     'TestID',
     'TestNsRefPtr',
     'TestObserverArray',
     'TestObserverService',
     'TestPipe',
-    'TestPLDHash',
     'TestStringAPI',
     'TestTArray',
     'TestTextFormatter',
     'TestThreadUtils'
 ])
 
 if CONFIG['MOZ_MEMORY']:
     GeckoCppUnitTests([