Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 28 Oct 2015 17:00:22 -0700
changeset 305182 1e700005a0ddf2b17803213e1f3f8d78a7a618b8
parent 305123 769f29c92bb2aaf8310720f9a0628fb2896086c5 (current diff)
parent 305181 8431c19a40067d9dbfe199f76d392b54d227da07 (diff)
child 305192 81a9c6849571682b5bc7c4b61e0d8cf95eda4322
child 305215 10e8f85d539b3666037f06183cfb025991837aa7
child 305307 b82942655a6364a50060fd310d3caae450ac005a
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
1e700005a0dd / 44.0a1 / 20151029030258 / files
nightly linux64
1e700005a0dd / 44.0a1 / 20151029030258 / files
nightly mac
1e700005a0dd / 44.0a1 / 20151029030258 / files
nightly win32
1e700005a0dd / 44.0a1 / 20151029030258 / files
nightly win64
1e700005a0dd / 44.0a1 / 20151029030258 / 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 inbound to central, a=merge
js/src/vm/Debugger.cpp
netwerk/protocol/websocket/PWebSocketFrameListener.ipdl
netwerk/protocol/websocket/WebSocketFrameListenerChild.cpp
netwerk/protocol/websocket/WebSocketFrameListenerChild.h
netwerk/protocol/websocket/WebSocketFrameListenerParent.cpp
netwerk/protocol/websocket/WebSocketFrameListenerParent.h
netwerk/protocol/websocket/WebSocketFrameService.cpp
netwerk/protocol/websocket/WebSocketFrameService.h
netwerk/protocol/websocket/nsIWebSocketFrameService.idl
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1217261 - Update mp4parse to v0.1.2
+Bug 479520 - Implement IDNA2008 standard for International Domain Names
--- a/browser/components/migration/nsEdgeReadingListExtractor.cpp
+++ b/browser/components/migration/nsEdgeReadingListExtractor.cpp
@@ -96,26 +96,26 @@ nsEdgeReadingListExtractor::Extract(cons
   NS_HANDLE_JET_ERROR(err)
   tableOpened = true;
   err = JetGetColumnInfo(sesid, dbid, "ReadingList", "URL", &urlColumnInfo,
                          sizeof(urlColumnInfo), JET_ColInfo);
   NS_HANDLE_JET_ERROR(err)
   if (urlColumnInfo.cbMax > MAX_URL_LENGTH) {
     nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (consoleService) {
-      consoleService->LogStringMessage(NS_LITERAL_STRING("Edge migration: URL column size increased").get());
+      consoleService->LogStringMessage(MOZ_UTF16("Edge migration: URL column size increased"));
     }
   }
   err = JetGetColumnInfo(sesid, dbid, "ReadingList", "Title", &titleColumnInfo,
                          sizeof(titleColumnInfo), JET_ColInfo);
   NS_HANDLE_JET_ERROR(err)
   if (titleColumnInfo.cbMax > MAX_TITLE_LENGTH) {
     nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (consoleService) {
-      consoleService->LogStringMessage(NS_LITERAL_STRING("Edge migration: Title column size increased").get());
+      consoleService->LogStringMessage(MOZ_UTF16("Edge migration: Title column size increased"));
     }
   }
   err = JetGetColumnInfo(sesid, dbid, "ReadingList", "AddedDate", &addedDateColumnInfo,
                          sizeof(addedDateColumnInfo), JET_ColInfo);
   NS_HANDLE_JET_ERROR(err)
 
   // verify the column types are what we expect:
   if (urlColumnInfo.coltyp != JET_coltypLongText ||
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -137,17 +137,16 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_USING_ICU_NAMESPACE=0"
         # don't include obsolete header files
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_NO_DEFAULT_INCLUDE_UTF_HEADERS=1"
         # remove chunks of the library that we don't need (yet)
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_LEGACY_CONVERSION"
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_TRANSLITERATION"
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_REGULAR_EXPRESSIONS"
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_BREAK_ITERATION"
-        ICU_CPPFLAGS="$ICU_CPPFLAGS -DUCONFIG_NO_IDNA"
         # we don't need to pass data to and from legacy char* APIs
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_CHARSET_IS_UTF8"
         # make sure to not accidentally pick up system-icu headers
         ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n"
 
         ICU_CROSS_BUILD_OPT=""
 
         if test "$CROSS_COMPILE"; then
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -807,17 +807,28 @@ exports.openFileStream = function (fileP
 
         resolve(stream);
       }
     );
   });
 }
 
 exports.isGenerator = function (fn) {
-  return typeof fn === "function" && fn.isGenerator();
+  if (typeof fn !== "function") {
+    return false;
+  }
+  let proto = Object.getPrototypeOf(fn);
+  if (!proto) {
+    return false;
+  }
+  let ctor = proto.constructor;
+  if (!ctor) {
+    return false;
+  }
+  return ctor.name == "GeneratorFunction";
 };
 
 exports.isPromise = function (p) {
   return p && typeof p.then === "function";
 };
 
 /**
  * Return true if `thing` is a SavedFrame, false otherwise.
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -126,18 +126,18 @@ public:
  NS_IMETHOD Run()
  {
     nsCOMPtr<nsIObserverService> observerService =
       services::GetObserverService();
     if (observerService) {
       observerService->NotifyObservers(
         ToSupports(mWindow),
         "audio-playback",
-        mActive ? NS_LITERAL_STRING("active").get()
-                : NS_LITERAL_STRING("inactive").get());
+        mActive ? MOZ_UTF16("active")
+                : MOZ_UTF16("inactive"));
     }
 
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMWindow> mWindow;
   bool mActive;
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -133,54 +133,54 @@ class EncodingRunnable : public nsRunnab
 {
   virtual ~EncodingRunnable() {}
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   EncodingRunnable(const nsAString& aType,
                    const nsAString& aOptions,
-                   uint8_t* aImageBuffer,
+                   UniquePtr<uint8_t[]> aImageBuffer,
                    layers::Image* aImage,
                    imgIEncoder* aEncoder,
                    EncodingCompleteEvent* aEncodingCompleteEvent,
                    int32_t aFormat,
                    const nsIntSize aSize,
                    bool aUsingCustomOptions)
     : mType(aType)
     , mOptions(aOptions)
-    , mImageBuffer(aImageBuffer)
+    , mImageBuffer(Move(aImageBuffer))
     , mImage(aImage)
     , mEncoder(aEncoder)
     , mEncodingCompleteEvent(aEncodingCompleteEvent)
     , mFormat(aFormat)
     , mSize(aSize)
     , mUsingCustomOptions(aUsingCustomOptions)
   {}
 
   nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
   {
     nsCOMPtr<nsIInputStream> stream;
     nsresult rv = ImageEncoder::ExtractDataInternal(mType,
                                                     mOptions,
-                                                    mImageBuffer,
+                                                    mImageBuffer.get(),
                                                     mFormat,
                                                     mSize,
                                                     mImage,
                                                     nullptr,
                                                     nullptr,
                                                     getter_AddRefs(stream),
                                                     mEncoder);
 
     // If there are unrecognized custom parse options, we should fall back to
     // the default values for the encoder without any options at all.
     if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
       rv = ImageEncoder::ExtractDataInternal(mType,
                                              EmptyString(),
-                                             mImageBuffer,
+                                             mImageBuffer.get(),
                                              mFormat,
                                              mSize,
                                              mImage,
                                              nullptr,
                                              nullptr,
                                              getter_AddRefs(stream),
                                              mEncoder);
     }
@@ -215,17 +215,17 @@ public:
     }
 
     return rv;
   }
 
 private:
   nsAutoString mType;
   nsAutoString mOptions;
-  nsAutoArrayPtr<uint8_t> mImageBuffer;
+  UniquePtr<uint8_t[]> mImageBuffer;
   RefPtr<layers::Image> mImage;
   nsCOMPtr<imgIEncoder> mEncoder;
   RefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
   int32_t mFormat;
   const nsIntSize mSize;
   bool mUsingCustomOptions;
 };
 
@@ -282,17 +282,17 @@ ImageEncoder::ExtractDataFromLayersImage
   return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
 /* static */
 nsresult
 ImageEncoder::ExtractDataAsync(nsAString& aType,
                                const nsAString& aOptions,
                                bool aUsingCustomOptions,
-                               uint8_t* aImageBuffer,
+                               UniquePtr<uint8_t[]> aImageBuffer,
                                int32_t aFormat,
                                const nsIntSize aSize,
                                EncodeCompleteCallback* aEncodeCallback)
 {
   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   if (!encoder) {
     return NS_IMAGELIB_ERROR_NO_ENCODER;
   }
@@ -301,17 +301,17 @@ ImageEncoder::ExtractDataAsync(nsAString
   nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<EncodingCompleteEvent> completeEvent =
     new EncodingCompleteEvent(encoderThread, aEncodeCallback);
 
   nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
                                                      aOptions,
-                                                     aImageBuffer,
+                                                     Move(aImageBuffer),
                                                      nullptr,
                                                      encoder,
                                                      completeEvent,
                                                      aFormat,
                                                      aSize,
                                                      aUsingCustomOptions);
   return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
 }
--- a/dom/base/ImageEncoder.h
+++ b/dom/base/ImageEncoder.h
@@ -6,16 +6,17 @@
 
 #ifndef ImageEncoder_h
 #define ImageEncoder_h
 
 #include "imgIEncoder.h"
 #include "nsError.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
+#include "mozilla/UniquePtr.h"
 #include "nsLayoutUtils.h"
 #include "nsSize.h"
 
 class nsICanvasRenderingContextInternal;
 
 namespace mozilla {
 
 namespace layers {
@@ -53,17 +54,17 @@ public:
   // successful dispatching of the extraction step to the encoding thread.
   // aEncodeCallback will be called on main thread when encoding process is
   // success.
   // Note: The callback has to set a valid parent for content for the generated
   // Blob object.
   static nsresult ExtractDataAsync(nsAString& aType,
                                    const nsAString& aOptions,
                                    bool aUsingCustomOptions,
-                                   uint8_t* aImageBuffer,
+                                   UniquePtr<uint8_t[]> aImageBuffer,
                                    int32_t aFormat,
                                    const nsIntSize aSize,
                                    EncodeCompleteCallback* aEncodeCallback);
 
   // Extract an Image asynchronously. Its function is same as ExtractDataAsync
   // except for the parameters. aImage is the uncompressed data. aEncodeCallback
   // will be called on main thread when encoding process is success.
   // Note: The callback has to set a valid parent for content for the generated
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -1558,18 +1558,18 @@ WebSocketImpl::Init(JSContext* aCx,
 
     // upgrade the request from ws:// to wss:// and mark as secure
     mURI.ReplaceSubstring("ws://", "wss://");
     if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
       return;
     }
     mSecure = true;
 
-    const char16_t* params[] = { reportSpec.get(), NS_LITERAL_STRING("wss").get() };
-    CSP_LogLocalizedStr(NS_LITERAL_STRING("upgradeInsecureRequest").get(),
+    const char16_t* params[] = { reportSpec.get(), MOZ_UTF16("wss") };
+    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         mInnerWindowID);
   }
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -10,17 +10,16 @@ support-files =
   file_use_counter_svg_fill_pattern.svg
   file_use_counter_svg_fill_pattern_internal.svg
   file_use_counter_svg_fill_pattern_data.svg
 
 [browser_bug593387.js]
 skip-if = e10s # Bug ?????? - test directly touches content (contentWindow.iframe.addEventListener)
 [browser_bug902350.js]
 tags = mcb
-skip-if = e10s # Bug ?????? - test e10s utils don't support load events from iframe etc, which this test relies on.
 [browser_messagemanager_loadprocessscript.js]
 [browser_messagemanager_targetframeloader.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_messagemanager_unload.js]
 [browser_state_notifications.js]
 # skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
 skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -1298,17 +1298,17 @@ nsGonkCameraControl::StopRecordingImpl()
     ~RecordingComplete() { }
 
     NS_IMETHODIMP
     Run()
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get());
+      obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified"));
       return NS_OK;
     }
 
   private:
     RefPtr<DeviceStorageFile> mFile;
   };
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1641,36 +1641,34 @@ CanvasRenderingContext2D::SetContextOpti
 
   if (!attributes.mAlpha) {
     SetIsOpaque(true);
   }
 
   return NS_OK;
 }
 
-void
-CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
-                                         int32_t* aFormat)
-{
-  *aImageBuffer = nullptr;
+UniquePtr<uint8_t[]>
+CanvasRenderingContext2D::GetImageBuffer(int32_t* aFormat)
+{
   *aFormat = 0;
 
   EnsureTarget();
   RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
   if (!snapshot) {
-    return;
+    return nullptr;
   }
 
   RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
   if (!data || data->GetSize() != IntSize(mWidth, mHeight)) {
-    return;
-  }
-
-  *aImageBuffer = SurfaceToPackedBGRA(data);
+    return nullptr;
+  }
+
   *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+  return SurfaceToPackedBGRA(data);
 }
 
 nsString CanvasRenderingContext2D::GetHitRegion(const mozilla::gfx::Point& aPoint)
 {
   for (size_t x = 0 ; x < mHitRegionsOptions.Length(); x++) {
     RegionInfo& info = mHitRegionsOptions[x];
     if (info.mPath->ContainsPoint(aPoint, Matrix())) {
       return info.mId;
@@ -1686,25 +1684,25 @@ CanvasRenderingContext2D::GetInputStream
 {
   nsCString enccid("@mozilla.org/image/encoder;2?type=");
   enccid += aMimeType;
   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   if (!encoder) {
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoArrayPtr<uint8_t> imageBuffer;
   int32_t format = 0;
-  GetImageBuffer(getter_Transfers(imageBuffer), &format);
+  UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
   if (!imageBuffer) {
     return NS_ERROR_FAILURE;
   }
 
-  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
-                                      encoder, aEncoderOptions, aStream);
+  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(),
+                                      format, encoder, aEncoderOptions,
+                                      aStream);
 }
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
 }
 
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -15,16 +15,17 @@
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "gfxTextRun.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/UniquePtr.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 #include "FilterSupport.h"
 #include "nsSVGEffects.h"
 #include "Layers.h"
 
@@ -521,17 +522,17 @@ public:
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
 
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) override;
+  virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
 
 
   // Given a point, return hit region ID if it exists
   nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
 
 
   // return true and fills in the bound rect if element has a hit region.
   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CanvasRenderingContextHelper.h"
 #include "ImageEncoder.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtr.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
 #include "WebGL1Context.h"
 #include "WebGL2Context.h"
 
 namespace mozilla {
@@ -44,20 +45,20 @@ CanvasRenderingContextHelper::ToBlob(JSC
          (elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
         (elementSize.height != mCurrentContext->GetHeight() &&
          (elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
   }
 
-  uint8_t* imageBuffer = nullptr;
+  UniquePtr<uint8_t[]> imageBuffer;
   int32_t format = 0;
   if (mCurrentContext) {
-    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
+    imageBuffer = mCurrentContext->GetImageBuffer(&format);
   }
 
   // Encoder callback when encoding is complete.
   class EncodeCallback : public EncodeCompleteCallback
   {
   public:
     EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
       : mGlobal(aGlobal)
@@ -94,17 +95,17 @@ CanvasRenderingContextHelper::ToBlob(JSC
   };
 
   RefPtr<EncodeCompleteCallback> callback =
     new EncodeCallback(aGlobal, &aCallback);
 
   aRv = ImageEncoder::ExtractDataAsync(type,
                                        params,
                                        usingCustomParseOptions,
-                                       imageBuffer,
+                                       Move(imageBuffer),
                                        format,
                                        GetWidthHeight(),
                                        callback);
 }
 
 already_AddRefed<nsICanvasRenderingContextInternal>
 CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
 {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1047,35 +1047,35 @@ WebGLContext::LoseOldestWebGLContextIfLi
     } else if (numContexts > kMaxWebGLContexts) {
         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
                         kMaxWebGLContexts);
         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
         const_cast<WebGLContext*>(oldestContext)->LoseContext();
     }
 }
 
-void
-WebGLContext::GetImageBuffer(uint8_t** out_imageBuffer, int32_t* out_format)
+UniquePtr<uint8_t[]>
+WebGLContext::GetImageBuffer(int32_t* out_format)
 {
-    *out_imageBuffer = nullptr;
     *out_format = 0;
 
     // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
     bool premult;
     RefPtr<SourceSurface> snapshot =
       GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
-    if (!snapshot)
-        return;
+    if (!snapshot) {
+        return nullptr;
+    }
 
     MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
 
     RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
 
     return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
-                                    out_imageBuffer, out_format);
+                                    out_format);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* mimeType,
                              const char16_t* encoderOptions,
                              nsIInputStream** out_stream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -232,18 +232,17 @@ public:
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     NS_IMETHOD Reset() override {
         /* (InitializeWithSurface) */
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
-    virtual void GetImageBuffer(uint8_t** out_imageBuffer,
-                                int32_t* out_format) override;
+    virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* out_format) override;
     NS_IMETHOD GetInputStream(const char* mimeType,
                               const char16_t* encoderOptions,
                               nsIInputStream** out_stream) override;
 
     already_AddRefed<mozilla::gfx::SourceSurface>
     GetSurfaceSnapshot(bool* out_premultAlpha) override;
 
     NS_IMETHOD SetIsOpaque(bool) override { return NS_OK; };
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -9,16 +9,17 @@
 #include "mozilla/gfx/2D.h"
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/OffscreenCanvas.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
 { 0xb84f2fed, 0x9d4b, 0x430b, \
   { 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
 
 class gfxASurface;
 class nsDisplayListBuilder;
 
@@ -91,17 +92,17 @@ public:
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Creates an image buffer. Returns null on failure.
-  virtual void GetImageBuffer(uint8_t** imageBuffer, int32_t* format) = 0;
+  virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* format) = 0;
 
   // Gives you a stream containing the image represented by this context.
   // The format is given in mimeTime, for example "image/png".
   //
   // If the image format does not support transparency or includeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *mimeType,
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1759,17 +1759,17 @@ HTMLFormElement::GetActionURL(nsIURI** a
 
     // let's log a message to the console that we are upgrading a request
     nsAutoCString scheme;
     rv = actionURL->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
     const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
-    CSP_LogLocalizedStr(NS_LITERAL_STRING("upgradeInsecureRequest").get(),
+    CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         document->InnerWindowID());
   }
--- a/dom/media/omx/MediaCodecProxy.cpp
+++ b/dom/media/omx/MediaCodecProxy.cpp
@@ -101,53 +101,32 @@ MediaCodecProxy::MediaCodecProxy(sp<ALoo
 }
 
 MediaCodecProxy::~MediaCodecProxy()
 {
   ReleaseMediaCodec();
 }
 
 bool
-MediaCodecProxy::AskMediaCodecAndWait()
+MediaCodecProxy::AllocateAudioMediaCodec()
 {
   if (mResourceClient || mCodec.get()) {
     return false;
   }
 
-  if (strncasecmp(mCodecMime.get(), "video/", 6) == 0) {
-    mozilla::MediaSystemResourceType type =
-      mCodecEncoder ? mozilla::MediaSystemResourceType::VIDEO_ENCODER :
-                      mozilla::MediaSystemResourceType::VIDEO_DECODER;
-    mResourceClient = new mozilla::MediaSystemResourceClient(type);
-    mResourceClient->SetListener(this);
-  } else if (strncasecmp(mCodecMime.get(), "audio/", 6) == 0) {
+  if (strncasecmp(mCodecMime.get(), "audio/", 6) == 0) {
     if (allocateCodec()) {
       return true;
     }
   }
-
-  if (!mResourceClient) {
-    return false;
-  }
-
-  mozilla::MonitorAutoLock mon(mMediaCodecLock);
-  mPendingRequestMediaResource = true;
-  // request video codec
-  mResourceClient->Acquire();
-
-  while (mPendingRequestMediaResource) {
-    mMediaCodecLock.Wait();
-  }
-  MCP_LOG("AskMediaCodecAndWait complete");
-
-  return true;
+  return false;
 }
 
 bool
-MediaCodecProxy::AsyncAskMediaCodec()
+MediaCodecProxy::AsyncAllocateVideoMediaCodec()
 {
   if (mResourceClient || mCodec.get()) {
     return false;
   }
 
   if (strncasecmp(mCodecMime.get(), "video/", 6) != 0) {
     return false;
   }
--- a/dom/media/omx/MediaCodecProxy.h
+++ b/dom/media/omx/MediaCodecProxy.h
@@ -133,24 +133,21 @@ public:
   status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
   bool Prepare();
   void ReleaseMediaResources();
   // This updates mOutputBuffer when receiving INFO_OUTPUT_BUFFERS_CHANGED event.
   bool UpdateOutputBuffers();
 
   void ReleaseMediaBuffer(MediaBuffer* abuffer);
 
-  // It asks for the OMX codec and blocked until the resource is grant to be
-  // allocated.
-  // Audio codec allocation should use this.
-  bool AskMediaCodecAndWait();
+  // It allocates audio MediaCodec synchronously.
+  bool AllocateAudioMediaCodec();
 
-  // It asks for the OMX codec asynchronously.
-  // Only video codec is supported.
-  bool AsyncAskMediaCodec();
+  // It allocates video MediaCodec asynchronously.
+  bool AsyncAllocateVideoMediaCodec();
 
   // Free the OMX codec so others can allocate it.
   void ReleaseMediaCodec();
 
 protected:
   virtual ~MediaCodecProxy();
 
   // MediaResourceReservationListener
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -1298,38 +1298,37 @@ MediaCodecReader::CreateTaskQueues()
 }
 
 RefPtr<MediaOmxCommonReader::MediaResourcePromise>
 MediaCodecReader::CreateMediaCodecs()
 {
   bool isWaiting = false;
   RefPtr<MediaResourcePromise> p = mMediaResourcePromise.Ensure(__func__);
 
-  if (!CreateMediaCodec(mLooper, mAudioTrack, false, isWaiting, nullptr)) {
+  if (!CreateMediaCodec(mLooper, mAudioTrack, isWaiting, nullptr)) {
     mMediaResourcePromise.Reject(true, __func__);
     return p;
   }
 
-  if (!CreateMediaCodec(mLooper, mVideoTrack, true, isWaiting, mVideoListener)) {
+  if (!CreateMediaCodec(mLooper, mVideoTrack, isWaiting, mVideoListener)) {
     mMediaResourcePromise.Reject(true, __func__);
     return p;
   }
 
   if (!isWaiting) {
     // No MediaCodec allocation wait.
     mMediaResourcePromise.Resolve(true, __func__);
   }
 
   return p;
 }
 
 bool
 MediaCodecReader::CreateMediaCodec(sp<ALooper>& aLooper,
                                    Track& aTrack,
-                                   bool aAsync,
                                    bool& aIsWaiting,
                                    wp<MediaCodecProxy::CodecResourceListener> aListener)
 {
   if (aTrack.mSource != nullptr && aTrack.mCodec == nullptr) {
     sp<MetaData> sourceFormat = aTrack.mSource->getFormat();
 
     const char* mime;
     if (sourceFormat->findCString(kKeyMIMEType, &mime)) {
@@ -1357,26 +1356,26 @@ MediaCodecReader::CreateMediaCodec(sp<AL
       GonkBufferQueue::createBufferQueue(&producer, &consumer);
       aTrack.mNativeWindow = new GonkNativeWindow(consumer);
       aTrack.mGraphicBufferProducer = producer;
 #else
       aTrack.mNativeWindow = new GonkNativeWindow();
 #endif
     }
 
-    if (!aAsync && aTrack.mCodec->AskMediaCodecAndWait()) {
+    if (aTrack.mType == Track::kAudio && aTrack.mCodec->AllocateAudioMediaCodec()) {
       // Pending configure() and start() to codecReserved() if the creation
       // should be asynchronous.
       if (!aTrack.mCodec->allocated() || !ConfigureMediaCodec(aTrack)){
         NS_WARNING("Couldn't create and configure MediaCodec synchronously");
         DestroyMediaCodec(aTrack);
         return false;
       }
-    } else if (aAsync) {
-      if (aTrack.mCodec->AsyncAskMediaCodec()) {
+    } else if (aTrack.mType == Track::kVideo) {
+      if (aTrack.mCodec->AsyncAllocateVideoMediaCodec()) {
         aIsWaiting = true;
       } else {
         NS_WARNING("Couldn't request MediaCodec asynchronously");
         DestroyMediaCodec(aTrack);
         return false;
       }
     }
   }
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -335,17 +335,16 @@ private:
   void DestroyExtractor();
 
   bool CreateMediaSources();
   void DestroyMediaSources();
 
   RefPtr<MediaResourcePromise> CreateMediaCodecs();
   static bool CreateMediaCodec(android::sp<android::ALooper>& aLooper,
                                Track& aTrack,
-                               bool aAsync,
                                bool& aIsWaiting,
                                android::wp<android::MediaCodecProxy::CodecResourceListener> aListener);
   static bool ConfigureMediaCodec(Track& aTrack);
   void DestroyMediaCodecs();
   static void DestroyMediaCodec(Track& aTrack);
 
   bool CreateTaskQueues();
   void ShutdownTaskQueues();
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
@@ -69,17 +69,17 @@ GonkAudioDecoderManager::InitMediaCodecP
   if (!InitLoopers(MediaData::AUDIO_DATA)) {
     return false;
   }
 
   mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false, nullptr);
   if (!mDecoder.get()) {
     return false;
   }
-  if (!mDecoder->AskMediaCodecAndWait())
+  if (!mDecoder->AllocateAudioMediaCodec())
   {
     mDecoder = nullptr;
     return false;
   }
   sp<AMessage> format = new AMessage;
   // Fixed values
   GADM_LOG("Configure audio mime type:%s, chan no:%d, sample-rate:%d, profile:%d",
            mMimeType.get(), mAudioChannels, mAudioRate, mAudioProfile);
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -119,17 +119,17 @@ GonkVideoDecoderManager::Init()
       [self] (bool) -> void {
         self->mVideoCodecRequest.Complete();
         self->codecReserved();
       }, [self] (bool) -> void {
         self->mVideoCodecRequest.Complete();
         self->codecCanceled();
       }));
   mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false, mVideoListener);
-  mDecoder->AsyncAskMediaCodec();
+  mDecoder->AsyncAllocateVideoMediaCodec();
 
   uint32_t capability = MediaCodecProxy::kEmptyCapability;
   if (mDecoder->getCapability(&capability) == OK && (capability &
       MediaCodecProxy::kCanExposeGraphicBuffer)) {
     mNativeWindow = new GonkNativeWindow();
   }
 
   return p;
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -156,16 +156,22 @@ CamerasParent::Observe(nsISupports *aSub
   obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
   StopVideoCapture();
   return NS_OK;
 }
 
 nsresult
 CamerasParent::DispatchToVideoCaptureThread(nsRunnable *event)
 {
+  // Don't try to dispatch if we're already on the right thread.
+  // There's a potential deadlock because the mThreadMonitor is likely
+  // to be taken already.
+  MOZ_ASSERT(!mVideoCaptureThread ||
+             mVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
+
   MonitorAutoLock lock(mThreadMonitor);
 
   while(mChildIsAlive && mWebRTCAlive &&
         (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning())) {
     mThreadMonitor.Wait();
   }
   if (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning()) {
     return NS_ERROR_FAILURE;
@@ -179,22 +185,27 @@ void
 CamerasParent::StopVideoCapture()
 {
   // We are called from the main thread (xpcom-shutdown) or
   // from PBackground (when the Actor shuts down).
   // Shut down the WebRTC stack (on the capture thread)
   RefPtr<CamerasParent> self(this);
   RefPtr<nsRunnable> webrtc_runnable =
     media::NewRunnableFrom([self]() -> nsresult {
-        MonitorAutoLock lock(self->mThreadMonitor);
-        self->CloseEngines();
-        self->mThreadMonitor.NotifyAll();
-        return NS_OK;
-      });
-  DispatchToVideoCaptureThread(webrtc_runnable);
+      MonitorAutoLock lock(self->mThreadMonitor);
+      self->CloseEngines();
+      self->mThreadMonitor.NotifyAll();
+      return NS_OK;
+    });
+  DebugOnly<nsresult> rv = DispatchToVideoCaptureThread(webrtc_runnable);
+#ifdef DEBUG
+  // It's ok for the dispatch to fail if the cleanup it has to do
+  // has been done already.
+  MOZ_ASSERT(NS_SUCCEEDED(rv) || !mWebRTCAlive);
+#endif
   // Hold here until the WebRTC thread is gone. We need to dispatch
   // the thread deletion *now*, or there will be no more possibility
   // to get to the main thread.
   MonitorAutoLock lock(mThreadMonitor);
   while (mWebRTCAlive) {
     mThreadMonitor.Wait();
   }
   // After closing the WebRTC stack, clean up the
@@ -388,18 +399,18 @@ CamerasParent::CloseEngines()
   }
   MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
 
   // Stop the callers
   while (mCallbacks.Length()) {
     auto capEngine = mCallbacks[0]->mCapEngine;
     auto capNum = mCallbacks[0]->mCapturerId;
     LOG(("Forcing shutdown of engine %d, capturer %d", capEngine, capNum));
-    RecvStopCapture(capEngine, capNum);
-    RecvReleaseCaptureDevice(capEngine, capNum);
+    StopCapture(capEngine, capNum);
+    unused << ReleaseCaptureDevice(capEngine, capNum);
   }
 
   for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
     if (mEngines[i].mEngineIsRunning) {
       LOG(("Being closed down while engine %d is running!", i));
     }
     if (mEngines[i].mPtrViERender) {
       mEngines[i].mPtrViERender->Release();
@@ -646,30 +657,38 @@ CamerasParent::RecvAllocateCaptureDevice
         });
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
+int
+CamerasParent::ReleaseCaptureDevice(const int& aCapEngine,
+                                    const int& capnum)
+{
+  int error = -1;
+  if (EnsureInitialized(aCapEngine)) {
+    error = mEngines[aCapEngine].mPtrViECapture->ReleaseCaptureDevice(capnum);
+  }
+  return error;
+}
+
 bool
 CamerasParent::RecvReleaseCaptureDevice(const int& aCapEngine,
                                         const int& numdev)
 {
   LOG((__PRETTY_FUNCTION__));
   LOG(("RecvReleaseCamera device nr %d", numdev));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<nsRunnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, numdev]() -> nsresult {
-      int error = -1;
-      if (self->EnsureInitialized(aCapEngine)) {
-        error = self->mEngines[aCapEngine].mPtrViECapture->ReleaseCaptureDevice(numdev);
-      }
+      int error = self->ReleaseCaptureDevice(aCapEngine, numdev);
       RefPtr<nsIRunnable> ipc_runnable =
         media::NewRunnableFrom([self, error, numdev]() -> nsresult {
           if (self->IsShuttingDown()) {
             return NS_ERROR_FAILURE;
           }
           if (error) {
             unused << self->SendReplyFailure();
             return NS_ERROR_FAILURE;
@@ -743,46 +762,58 @@ CamerasParent::RecvStartCapture(const in
         });
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
       return NS_OK;
     });
   DispatchToVideoCaptureThread(webrtc_runnable);
   return true;
 }
 
+void
+CamerasParent::StopCapture(const int& aCapEngine,
+                           const int& capnum)
+{
+  if (EnsureInitialized(aCapEngine)) {
+    mEngines[aCapEngine].mPtrViECapture->StopCapture(capnum);
+    mEngines[aCapEngine].mPtrViERender->StopRender(capnum);
+    mEngines[aCapEngine].mPtrViERender->RemoveRenderer(capnum);
+    mEngines[aCapEngine].mEngineIsRunning = false;
+
+    for (size_t i = 0; i < mCallbacks.Length(); i++) {
+      if (mCallbacks[i]->mCapEngine == aCapEngine
+          && mCallbacks[i]->mCapturerId == capnum) {
+        delete mCallbacks[i];
+        mCallbacks.RemoveElementAt(i);
+        break;
+      }
+    }
+  }
+}
+
 bool
 CamerasParent::RecvStopCapture(const int& aCapEngine,
                                const int& capnum)
 {
   LOG((__PRETTY_FUNCTION__));
 
   RefPtr<CamerasParent> self(this);
   RefPtr<nsRunnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, capnum]() -> nsresult {
-      if (self->EnsureInitialized(aCapEngine)) {
-        self->mEngines[aCapEngine].mPtrViECapture->StopCapture(capnum);
-        self->mEngines[aCapEngine].mPtrViERender->StopRender(capnum);
-        self->mEngines[aCapEngine].mPtrViERender->RemoveRenderer(capnum);
-        self->mEngines[aCapEngine].mEngineIsRunning = false;
-
-        for (size_t i = 0; i < self->mCallbacks.Length(); i++) {
-          if (self->mCallbacks[i]->mCapEngine == aCapEngine
-              && self->mCallbacks[i]->mCapturerId == capnum) {
-            delete self->mCallbacks[i];
-            self->mCallbacks.RemoveElementAt(i);
-            break;
-          }
-        }
-      }
+      self->StopCapture(aCapEngine, capnum);
       return NS_OK;
     });
-  if (NS_SUCCEEDED(DispatchToVideoCaptureThread(webrtc_runnable))) {
-    return SendReplySuccess();
+  nsresult rv = DispatchToVideoCaptureThread(webrtc_runnable);
+  if (self->IsShuttingDown()) {
+    return NS_SUCCEEDED(rv);
   } else {
-    return SendReplyFailure();
+    if (NS_SUCCEEDED(rv)) {
+      return SendReplySuccess();
+    } else {
+      return SendReplyFailure();
+    }
   }
 }
 
 void
 CamerasParent::StopIPC()
 {
   MOZ_ASSERT(!mDestroyed);
   // Release shared memory now, it's our last chance
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -114,19 +114,23 @@ public:
                           int64_t render_time);
 
 
   CamerasParent();
 
 protected:
   virtual ~CamerasParent();
 
+  // We use these helpers for shutdown and for the respective IPC commands.
+  void StopCapture(const int& aCapEngine, const int& capnum);
+  int ReleaseCaptureDevice(const int& aCapEngine, const int& capnum);
+
   bool SetupEngine(CaptureEngine aCapEngine);
+  bool EnsureInitialized(int aEngine);
   void CloseEngines();
-  bool EnsureInitialized(int aEngine);
   void StopIPC();
   void StopVideoCapture();
   nsresult DispatchToVideoCaptureThread(nsRunnable *event);
 
   EngineHelper mEngines[CaptureEngine::MaxEngine];
   nsTArray<CallbackHelper*> mCallbacks;
 
   // image buffers
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -74,18 +74,18 @@ MediaEngineGonkVideoSource::NotifyPull(M
   MonitorAutoLock lock(mMonitor);
   // B2G does AddTrack, but holds kStarted until the hardware changes state.
   // So mState could be kReleased here. We really don't care about the state,
   // though.
 
   // Note: we're not giving up mImage here
   RefPtr<layers::Image> image = mImage;
   StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
-  LOGFRAME(("NotifyPull, desired = %ld, delta = %ld %s", (int64_t) aDesiredTime,
-            (int64_t) delta, image ? "" : "<null>"));
+  LOGFRAME(("NotifyPull, desired = %" PRIi64 ", delta = %" PRIi64 " %s",
+            (int64_t) aDesiredTime, (int64_t) delta, image ? "" : "<null>"));
 
   // Bug 846188 We may want to limit incoming frames to the requested frame rate
   // mFps - if you want 30FPS, and the camera gives you 60FPS, this could
   // cause issues.
   // We may want to signal if the actual frame rate is below mMinFPS -
   // cameras often don't return the requested frame rate especially in low
   // light; we should consider surfacing this so that we can switch to a
   // lower resolution (which may up the frame rate)
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -314,18 +314,18 @@ MediaEngineRemoteVideoSource::DeliverFra
   data.mPicY = 0;
   data.mPicSize = IntSize(mWidth, mHeight);
   data.mStereoMode = StereoMode::MONO;
 
   videoImage->SetData(data);
 
 #ifdef DEBUG
   static uint32_t frame_num = 0;
-  LOGFRAME(("frame %d (%dx%d); timestamp %u, ntp_time %lu, render_time %lu", frame_num++,
-            mWidth, mHeight, time_stamp, ntp_time, render_time));
+  LOGFRAME(("frame %d (%dx%d); timestamp %u, ntp_time %" PRIu64 ", render_time %" PRIu64,
+            frame_num++, mWidth, mHeight, time_stamp, ntp_time, render_time));
 #endif
 
   // we don't touch anything in 'this' until here (except for snapshot,
   // which has it's own lock)
   MonitorAutoLock lock(mMonitor);
 
   // implicitly releases last image
   mImage = image.forget();
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -920,16 +920,19 @@ this.PushService = {
   getAllUnexpired: function() {
     return this._db.getAllUnexpired();
   },
 
   _sendRequest: function(action, aRecord) {
     if (this._state == PUSH_SERVICE_CONNECTION_DISABLE) {
       return Promise.reject({state: 0, error: "Service not active"});
     } else if (this._state == PUSH_SERVICE_ACTIVE_OFFLINE) {
+      if (this._service.serviceType() == "WebSocket" && action == "unregister") {
+        return Promise.resolve();
+      }
       return Promise.reject({state: 0, error: "NetworkError"});
     }
     return this._service.request(action, aRecord);
   },
 
   /**
    * Called on message from the child process. aPageRecord is an object sent by
    * navigator.push, identifying the sending page and other fields.
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -436,16 +436,20 @@ this.PushServiceHttp2 = {
   newPushDB: function() {
     return new PushDB(kPUSHHTTP2DB_DB_NAME,
                       kPUSHHTTP2DB_DB_VERSION,
                       kPUSHHTTP2DB_STORE_NAME,
                       "subscriptionUri",
                       PushRecordHttp2);
   },
 
+  serviceType: function() {
+    return "http2";
+  },
+
   hasmainPushService: function() {
     return this._mainPushService !== null;
   },
 
   checkServerURI: function(serverURL) {
     if (!serverURL) {
       debug("No dom.push.serverURL found!");
       return;
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -152,16 +152,20 @@ this.PushServiceWebSocket = {
   newPushDB: function() {
     return new PushDB(kPUSHWSDB_DB_NAME,
                       kPUSHWSDB_DB_VERSION,
                       kPUSHWSDB_STORE_NAME,
                       "channelID",
                       PushRecordWebSocket);
   },
 
+  serviceType: function() {
+    return "WebSocket";
+  },
+
   disconnect: function() {
     this._shutdownWS();
   },
 
   observe: function(aSubject, aTopic, aData) {
 
     switch (aTopic) {
     case "nsPref:changed":
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -771,17 +771,17 @@ nsCSPContext::SendReports(nsISupports* a
   for (uint32_t r = 0; r < reportURIs.Length(); r++) {
     nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
     // try to create a new uri from every report-uri string
     rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
                      reportURICstring.get()));
-      CSP_LogLocalizedStr(NS_LITERAL_STRING("triedToSendReport").get(),
+      CSP_LogLocalizedStr(MOZ_UTF16("triedToSendReport"),
                           params, ArrayLength(params),
                           aSourceFile, aScriptSample, aLineNum, 0,
                           nsIScriptError::errorFlag, "CSP", mInnerWindowID);
       continue; // don't return yet, there may be more URIs
     }
 
     nsIDocShell* docShell = nullptr;
 
@@ -815,17 +815,17 @@ nsCSPContext::SendReports(nsISupports* a
 
     // log a warning to console if scheme is not http or https
     bool isHttpScheme =
       (NS_SUCCEEDED(reportURI->SchemeIs("http", &isHttpScheme)) && isHttpScheme) ||
       (NS_SUCCEEDED(reportURI->SchemeIs("https", &isHttpScheme)) && isHttpScheme);
 
     if (!isHttpScheme) {
       const char16_t* params[] = { reportURIs[r].get() };
-      CSP_LogLocalizedStr(NS_LITERAL_STRING("reportURInotHttpsOrHttp2").get(),
+      CSP_LogLocalizedStr(MOZ_UTF16("reportURInotHttpsOrHttp2"),
                           params, ArrayLength(params),
                           aSourceFile, aScriptSample, aLineNum, 0,
                           nsIScriptError::errorFlag, "CSP", mInnerWindowID);
     }
 
     // make sure this is an anonymous request (no cookies) so in case the
     // policy URI is injected, it can't be abused for CSRF.
     nsLoadFlags flags;
@@ -899,17 +899,17 @@ nsCSPContext::SendReports(nsISupports* a
     // AsyncOpen should not fail, but could if there's no load group (like if
     // SetRequestContext is not given a channel).  This should fail quietly and
     // not return an error since it's really ok if reports don't go out, but
     // it's good to log the error locally.
 
     if (NS_FAILED(rv)) {
       const char16_t* params[] = { reportURIs[r].get() };
       CSPCONTEXTLOG(("AsyncOpen failed for report URI %s", params[0]));
-      CSP_LogLocalizedStr(NS_LITERAL_STRING("triedToSendReport").get(),
+      CSP_LogLocalizedStr(MOZ_UTF16("triedToSendReport"),
                           params, ArrayLength(params),
                           aSourceFile, aScriptSample, aLineNum, 0,
                           nsIScriptError::errorFlag, "CSP", mInnerWindowID);
     } else {
       CSPCONTEXTLOG(("Sent violation report to URI %s", reportURICstring.get()));
     }
   }
   return NS_OK;
@@ -989,18 +989,18 @@ class CSPReportSenderRunnable final : pu
         blockedString->GetData(blockedDataStr);
       }
 
       if (blockedDataStr.Length() > 0) {
         nsString blockedDataChar16 = NS_ConvertUTF8toUTF16(blockedDataStr);
         const char16_t* params[] = { mViolatedDirective.get(),
                                      blockedDataChar16.get() };
 
-        CSP_LogLocalizedStr(mReportOnlyFlag ? NS_LITERAL_STRING("CSPROViolationWithURI").get() :
-                                              NS_LITERAL_STRING("CSPViolationWithURI").get(),
+        CSP_LogLocalizedStr(mReportOnlyFlag ? MOZ_UTF16("CSPROViolationWithURI") :
+                                              MOZ_UTF16("CSPViolationWithURI"),
                             params, ArrayLength(params),
                             mSourceFile, mScriptSample, mLineNum, 0,
                             nsIScriptError::errorFlag, "CSP", mInnerWindowID);
       }
       return NS_OK;
     }
 
   private:
@@ -1308,17 +1308,17 @@ CSPReportRedirectSink::AsyncOnChannelRed
   nsCOMPtr<nsIURI> uri;
   rv = aOldChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   NS_ASSERTION(observerService, "Observer service required to log CSP violations");
   observerService->NotifyObservers(uri,
                                    CSP_VIOLATION_TOPIC,
-                                   NS_LITERAL_STRING("denied redirect while sending violation report").get());
+                                   MOZ_UTF16("denied redirect while sending violation report"));
 
   return NS_BINDING_REDIRECTED;
 }
 
 NS_IMETHODIMP
 CSPReportRedirectSink::GetInterface(const nsIID& aIID, void** aResult)
 {
   if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -1007,34 +1007,34 @@ nsCSPParser::directive()
 
   CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
                NS_ConvertUTF16toUTF8(mCurToken).get(),
                NS_ConvertUTF16toUTF8(mCurValue).get()));
 
   // Make sure that the directive-srcs-array contains at least
   // one directive and one src.
   if (mCurDir.Length() < 1) {
-    const char16_t* params[] = { NS_LITERAL_STRING("directive missing").get() };
+    const char16_t* params[] = { MOZ_UTF16("directive missing") };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
                              params, ArrayLength(params));
     return;
   }
 
   // Try to create a new CSPDirective
   nsCSPDirective* cspDir = directiveName();
   if (!cspDir) {
     // if we can not create a CSPDirective, we can skip parsing the srcs for that array
     return;
   }
 
   // special case handling for upgrade-insecure-requests, which is only specified
   // by a directive name but does not include any srcs.
   if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
     if (mCurDir.Length() > 1) {
-      const char16_t* params[] = { NS_LITERAL_STRING("upgrade-insecure-requests").get() };
+      const char16_t* params[] = { MOZ_UTF16("upgrade-insecure-requests") };
       logWarningErrorToConsole(nsIScriptError::warningFlag,
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
     return;
   }
@@ -1057,17 +1057,17 @@ nsCSPParser::directive()
 
   // if a hash or nonce is specified within script-src, then
   // unsafe-inline should be ignored, see:
   // http://www.w3.org/TR/CSP2/#directive-script-src
   if (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) &&
       mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
     mUnsafeInlineKeywordSrc->invalidate();
     // log to the console that unsafe-inline will be ignored
-    const char16_t* params[] = { NS_LITERAL_STRING("'unsafe-inline'").get() };
+    const char16_t* params[] = { MOZ_UTF16("'unsafe-inline'") };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptSrc",
                              params, ArrayLength(params));
   }
 
   // Add the newly created srcs to the directive and add the directive to the policy
   cspDir->addSrcs(srcs);
   mPolicy->addDirective(cspDir);
 }
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -41,16 +41,17 @@
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
+using namespace mozilla::dom;
 using namespace mozilla::dom::gonk;
 using namespace android;
 using namespace mozilla;
 using namespace mozilla::dom::bluetooth;
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args)
 
@@ -76,23 +77,57 @@ static const uint32_t sMaxStreamVolumeTb
   15,  // enforced audible
   15,  // DTMF
   15,  // TTS
 #if ANDROID_VERSION < 19
   15,  // FM
 #endif
 };
 
-// Use a half value of each volume category as the default volume.
-static const uint32_t sDefaultVolumeCategoriesTbl[VOLUME_TOTAL_NUMBER] = {
-  8, // VOLUME_MEDIA
-  8, // VOLUME_NOTIFICATION
-  8, // VOLUME_ALARM
-  3, // VOLUME_TELEPHONY
-  8, // VOLUME_BLUETOOTH_SCO
+static const uint32_t sDefaultStreamVolumeTbl[AUDIO_STREAM_CNT] = {
+  3,  // voice call
+  8,  // system
+  8,  // ring
+  8,  // music
+  8,  // alarm
+  8,  // notification
+  8,  // BT SCO
+  15, // enforced audible  // XXX Handle as fixed maximum audio setting
+  8,  // DTMF
+  8,  // TTS
+#if ANDROID_VERSION < 19
+  8,  // FM
+#endif
+};
+
+static const int32_t sStreamVolumeAliasTbl[AUDIO_STREAM_CNT] = {
+  AUDIO_STREAM_VOICE_CALL,      // voice call
+  AUDIO_STREAM_NOTIFICATION,    // system
+  AUDIO_STREAM_NOTIFICATION,    // ring
+  AUDIO_STREAM_MUSIC,           // music
+  AUDIO_STREAM_ALARM,           // alarm
+  AUDIO_STREAM_NOTIFICATION,    // notification
+  AUDIO_STREAM_BLUETOOTH_SCO,   // BT SCO
+  AUDIO_STREAM_ENFORCED_AUDIBLE,// enforced audible
+  AUDIO_STREAM_DTMF,            // DTMF
+  AUDIO_STREAM_TTS,             // TTS
+#if ANDROID_VERSION < 19
+  AUDIO_STREAM_MUSIC,           // FM
+#endif
+};
+
+static const uint32_t sChannelStreamTbl[NUMBER_OF_AUDIO_CHANNELS] = {
+  AUDIO_STREAM_MUSIC,           // AudioChannel::Normal
+  AUDIO_STREAM_MUSIC,           // AudioChannel::Content
+  AUDIO_STREAM_NOTIFICATION,    // AudioChannel::Notification
+  AUDIO_STREAM_ALARM,           // AudioChannel::Alarm
+  AUDIO_STREAM_VOICE_CALL,      // AudioChannel::Telephony
+  AUDIO_STREAM_RING,            // AudioChannel::Ringer
+  AUDIO_STREAM_ENFORCED_AUDIBLE,// AudioChannel::Publicnotification
+  AUDIO_STREAM_SYSTEM,          // AudioChannel::System
 };
 
 // Mappings AudioOutputProfiles to strings.
 static const nsAttrValue::EnumTable kAudioOutputProfilesTable[] = {
   { "primary",   DEVICE_PRIMARY },
   { "headset",   DEVICE_HEADSET },
   { "bluetooth", DEVICE_BLUETOOTH },
   { nullptr }
@@ -101,22 +136,36 @@ static const nsAttrValue::EnumTable kAud
 static const int kBtSampleRate = 8000;
 
 typedef MozPromise<bool, const char*, true> VolumeInitPromise;
 
 namespace mozilla {
 namespace dom {
 namespace gonk {
 
-static const VolumeData gVolumeData[VOLUME_TOTAL_NUMBER] = {
-  {"audio.volume.content",      VOLUME_MEDIA},
-  {"audio.volume.notification", VOLUME_NOTIFICATION},
-  {"audio.volume.alarm",        VOLUME_ALARM},
-  {"audio.volume.telephony",    VOLUME_TELEPHONY},
-  {"audio.volume.bt_sco",       VOLUME_BLUETOOTH_SCO}
+/**
+ * We have five sound volume settings from UX spec,
+ * You can see more informations in Bug1068219.
+ * (1) Media : music, video, FM ...
+ * (2) Notification : ringer, notification ...
+ * (3) Alarm : alarm
+ * (4) Telephony : GSM call, WebRTC call
+ * (5) Bluetooth SCO : SCO call
+ **/
+struct VolumeData {
+  const char* mChannelName;
+  int32_t mStreamType;
+};
+
+static const VolumeData gVolumeData[] = {
+  {"audio.volume.content",      AUDIO_STREAM_MUSIC},
+  {"audio.volume.notification", AUDIO_STREAM_NOTIFICATION},
+  {"audio.volume.alarm",        AUDIO_STREAM_ALARM},
+  {"audio.volume.telephony",    AUDIO_STREAM_VOICE_CALL},
+  {"audio.volume.bt_sco",       AUDIO_STREAM_BLUETOOTH_SCO}
 };
 
 class RunnableCallTask : public Task
 {
 public:
   explicit RunnableCallTask(nsIRunnable* aRunnable)
     : mRunnable(aRunnable) {}
 
@@ -140,90 +189,84 @@ GetSettingServiceLock()
   nsCOMPtr<nsISettingsServiceLock> lock;
   rv = service->CreateLock(nullptr, getter_AddRefs(lock));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
   return lock.forget();
 }
 
-class AudioProfileData final
+#if ANDROID_VERSION >= 21
+class GonkAudioPortCallback : public AudioSystem::AudioPortCallback
 {
 public:
-  explicit AudioProfileData(AudioOutputProfiles aProfile)
-    : mProfile(aProfile)
-    , mActive(false)
+  virtual void onAudioPortListUpdate()
   {
-    for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
-      mVolumeTable.AppendElement(0);
-    }
-  };
-
-  AudioOutputProfiles GetProfile() const
-  {
-    return mProfile;
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableFunction([]() {
+        MOZ_ASSERT(NS_IsMainThread());
+        RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
+        NS_ENSURE_TRUE(audioManager.get(), );
+        audioManager->UpdateCachedActiveDevicesForStreams();
+        audioManager->MaybeUpdateVolumeSettingToDatabase();
+      });
+    NS_DispatchToMainThread(runnable);
   }
-
-  void SetActive(bool aActive)
-  {
-    mActive = aActive;
-  }
-
-  bool GetActive() const
-  {
-    return mActive;
-  }
-
-  nsTArray<uint32_t> mVolumeTable;
-private:
-  const AudioOutputProfiles mProfile;
-  bool mActive;
+  virtual void onAudioPatchListUpdate() { }
+  virtual void onServiceDied() { }
 };
+#endif
 
 void
 AudioManager::HandleAudioFlingerDied()
 {
+  //Disable volume change notification
+  mIsVolumeInited = false;
+
   uint32_t attempt;
   for (attempt = 0; attempt < 50; attempt++) {
     if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) {
       break;
     }
-
     LOG("AudioPolicyService is dead! attempt=%d", attempt);
     usleep(1000 * 200);
   }
 
   MOZ_RELEASE_ASSERT(attempt < 50);
 
-  for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
-    AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop),
-                                  0,
-                                  sMaxStreamVolumeTbl[loop]);
-    uint32_t index;
-    GetStreamVolumeIndex(loop, &index);
-    SetStreamVolumeIndex(loop, index);
+  // Indicate to audio HAL that we start the reconfiguration phase after a media
+  // server crash
+  AudioSystem::setParameters(0, String8("restarting=true"));
+
+  // Restore device connection states
+  SetAllDeviceConnectionStates();
+
+  // Restore call state
+#if ANDROID_VERSION < 17
+  AudioSystem::setPhoneState(mPhoneState);
+#else
+  AudioSystem::setPhoneState(static_cast<audio_mode_t>(mPhoneState));
+#endif
+
+  // Restore master volume
+  AudioSystem::setMasterVolume(1.0);
+
+  // Restore stream volumes
+  for (uint32_t streamType = 0; streamType < AUDIO_STREAM_CNT; ++streamType) {
+    mStreamStates[streamType]->InitStreamVolume();
+    mStreamStates[streamType]->RestoreVolumeIndexToAllDevices();
   }
 
-  if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-    UpdateHeadsetConnectionState(hal::SWITCH_STATE_HEADSET);
-  } else if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
-    UpdateHeadsetConnectionState(hal::SWITCH_STATE_HEADPHONE);
-  } else {
-    UpdateHeadsetConnectionState(hal::SWITCH_STATE_OFF);
-  }
+  // Indicate the end of reconfiguration phase to audio HAL
+  AudioSystem::setParameters(0, String8("restarting=true"));
 
-  int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID;
-  GetPhoneState(&phoneState);
-#if ANDROID_VERSION < 17
-  AudioSystem::setPhoneState(phoneState);
-#else
-  AudioSystem::setPhoneState(static_cast<audio_mode_t>(phoneState));
-#endif
-
-  AudioSystem::get_audio_flinger();
+  // Enable volume change notification
+  mIsVolumeInited = true;
+  mAudioOutProfileUpdated = 0;
+  MaybeUpdateVolumeSettingToDatabase(true);
 }
 
 class VolumeInitCallback final : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   VolumeInitCallback()
@@ -236,33 +279,32 @@ public:
   {
     return mPromise;
   }
 
   NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   {
     RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
     MOZ_ASSERT(audioManager);
-    for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
+    for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
       NS_ConvertASCIItoUTF16 volumeType(gVolumeData[idx].mChannelName);
       if (StringBeginsWith(aName, volumeType)) {
         AudioOutputProfiles profile = GetProfileFromSettingName(aName);
         MOZ_ASSERT(profile != DEVICE_ERROR);
-
-        uint32_t category = gVolumeData[idx].mCategory;
+        int32_t stream = gVolumeData[idx].mStreamType;
         uint32_t volIndex = aResult.isInt32() ?
-                  aResult.toInt32() : sDefaultVolumeCategoriesTbl[category];
-        nsresult rv = audioManager->ValidateVolumeIndex(category, volIndex);
+                  aResult.toInt32() : sDefaultStreamVolumeTbl[stream];
+        nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           mPromiseHolder.Reject("Error : invalid volume index.", __func__);
           return rv;
         }
 
-        audioManager->InitProfileVolume(profile, category, volIndex);
-        if (++mInitCounter == DEVICE_TOTAL_NUMBER * VOLUME_TOTAL_NUMBER) {
+        audioManager->InitVolumeForProfile(profile, stream, volIndex);
+        if (++mInitCounter == DEVICE_TOTAL_NUMBER * MOZ_ARRAY_LENGTH(gVolumeData)) {
           mPromiseHolder.Resolve(true, __func__);
         }
         return NS_OK;
       }
     }
     mPromiseHolder.Reject("Error : unexpected audio init event.", __func__);
     return NS_OK;
   }
@@ -307,137 +349,179 @@ BinderDeadCallback(status_t aErr)
       RefPtr<AudioManager> audioManager = AudioManager::GetInstance();
       NS_ENSURE_TRUE(audioManager.get(), );
       audioManager->HandleAudioFlingerDied();
     });
 
   NS_DispatchToMainThread(runnable);
 }
 
-static bool
-IsDeviceOn(audio_devices_t device)
+bool
+AudioManager::IsFmOutConnected()
 {
-#if ANDROID_VERSION >= 15
-  return AudioSystem::getDeviceConnectionState(device, "") ==
-           AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
-#else
-  return false;
-#endif
+  return mConnectedDevices.Get(AUDIO_DEVICE_OUT_FM, nullptr);
 }
 
 NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver)
 
 void
+AudioManager::AudioOutProfileUpdated(AudioOutputProfiles aProfile)
+{
+  mAudioOutProfileUpdated |= (1 << aProfile);
+}
+
+void
 AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState)
 {
+  bool headphoneConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+                                                  nullptr);
+  bool headsetConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADSET,
+                                                nullptr);
+  if (aState == hal::SWITCH_STATE_HEADSET) {
+    UpdateDeviceConnectionState(true,
+                                AUDIO_DEVICE_OUT_WIRED_HEADSET,
+                                NS_LITERAL_CSTRING(""));
+  } else if (aState == hal::SWITCH_STATE_HEADPHONE) {
+    UpdateDeviceConnectionState(true,
+                                AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+                                NS_LITERAL_CSTRING(""));
+  } else if (aState == hal::SWITCH_STATE_OFF) {
+    if (headsetConnected) {
+      UpdateDeviceConnectionState(false,
+                                  AUDIO_DEVICE_OUT_WIRED_HEADSET,
+                                  NS_LITERAL_CSTRING(""));
+    }
+    if (headphoneConnected) {
+      UpdateDeviceConnectionState(false,
+                                  AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+                                  NS_LITERAL_CSTRING(""));
+    }
+  }
+}
+
+void
+AudioManager::UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName)
+{
 #if ANDROID_VERSION >= 15
-  if (aState == hal::SWITCH_STATE_HEADSET) {
-    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
-                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
-    mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET;
-  } else if (aState == hal::SWITCH_STATE_HEADPHONE) {
-    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
-                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE, "");
-    mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
-  } else if (aState == hal::SWITCH_STATE_OFF) {
-    if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET,
-                                            AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
-    }
-    if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
-                                            AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
-    }
-    mHeadsetState = 0;
+  bool isConnected = mConnectedDevices.Get(aDevice, nullptr);
+  if (isConnected && !aIsConnected) {
+    AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice),
+                                          AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                          aDeviceName.get());
+    mConnectedDevices.Remove(aDevice);
+  } else if(!isConnected && aIsConnected) {
+    AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(aDevice),
+                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                                          aDeviceName.get());
+    mConnectedDevices.Put(aDevice, aDeviceName);
   }
-
+#if ANDROID_VERSION < 21
+  // Manually call it, since AudioPortCallback is not supported.
+  // Current volumes might be changed by updating active devices in android
+  // AudioPolicyManager.
+  MaybeUpdateVolumeSettingToDatabase();
+#endif
 #else
   NS_NOTREACHED("Doesn't support audio routing on GB version");
 #endif
 }
 
 void
+AudioManager::SetAllDeviceConnectionStates()
+{
+  for (auto iter = mConnectedDevices.Iter(); !iter.Done(); iter.Next()) {
+    const uint32_t& device = iter.Key();
+    nsCString& deviceAddress = iter.Data();
+    AudioSystem::setDeviceConnectionState(static_cast<audio_devices_t>(device),
+                                          AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                                          deviceAddress.get());
+  }
+#if ANDROID_VERSION < 21
+  // Manually call it, since AudioPortCallback is not supported.
+  // Current volumes might be changed by updating active devices in android
+  // AudioPolicyManager.
+  MaybeUpdateVolumeSettingToDatabase(true);
+#endif
+}
+
+void
 AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject,
                                            const char* aTopic,
                                            const nsCString aAddress)
 {
 #ifdef MOZ_B2G_BT
-  bool status;
+  bool isConnected = false;
   if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
     BluetoothHfpManagerBase* hfp =
       static_cast<BluetoothHfpManagerBase*>(aSubject);
-    status = hfp->IsScoConnected();
+    isConnected = hfp->IsScoConnected();
   } else {
     BluetoothProfileManagerBase* profile =
       static_cast<BluetoothProfileManagerBase*>(aSubject);
-    status = profile->IsConnected();
+    isConnected = profile->IsConnected();
   }
 
-  audio_policy_dev_state_t audioState = status ?
-    AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
-    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
-
   if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) {
-    if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+    if (isConnected) {
       String8 cmd;
       cmd.appendFormat("bt_samplerate=%d", kBtSampleRate);
       AudioSystem::setParameters(0, cmd);
       SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO);
-      SwitchProfileData(DEVICE_BLUETOOTH, true);
     } else {
       int32_t force;
       GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force);
       if (force == nsIAudioManager::FORCE_BT_SCO) {
         SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE);
       }
-      SwitchProfileData(DEVICE_BLUETOOTH, false);
     }
   } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) {
-    if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && mA2dpSwitchDone) {
+    if (!isConnected && mA2dpSwitchDone) {
       RefPtr<AudioManager> self = this;
       nsCOMPtr<nsIRunnable> runnable =
-        NS_NewRunnableFunction([self, audioState, aAddress]() {
+        NS_NewRunnableFunction([self, isConnected, aAddress]() {
           if (self->mA2dpSwitchDone) {
             return;
           }
-          AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
-                                                audioState,
-                                                aAddress.get());
+          self->UpdateDeviceConnectionState(isConnected,
+                                            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+                                            aAddress);
+
           String8 cmd("bluetooth_enabled=false");
           AudioSystem::setParameters(0, cmd);
           cmd.setTo("A2dpSuspended=true");
           AudioSystem::setParameters(0, cmd);
           self->mA2dpSwitchDone = true;
         });
       MessageLoop::current()->PostDelayedTask(
         FROM_HERE, new RunnableCallTask(runnable), 1000);
 
       mA2dpSwitchDone = false;
-      SwitchProfileData(DEVICE_BLUETOOTH, false);
     } else {
-      AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
-                                            audioState, aAddress.get());
+      UpdateDeviceConnectionState(isConnected,
+                                  AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+                                  aAddress);
       String8 cmd("bluetooth_enabled=true");
       AudioSystem::setParameters(0, cmd);
       cmd.setTo("A2dpSuspended=false");
       AudioSystem::setParameters(0, cmd);
       mA2dpSwitchDone = true;
-      SwitchProfileData(DEVICE_BLUETOOTH, true);
 #if ANDROID_VERSION >= 17
       if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
         SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
       }
 #endif
     }
-    mBluetoothA2dpEnabled = audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE;
+    mBluetoothA2dpEnabled = isConnected;
   } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) {
-    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
-                                          audioState, aAddress.get());
-    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
-                                          audioState, aAddress.get());
+    UpdateDeviceConnectionState(isConnected,
+                                AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+                                aAddress);
+    UpdateDeviceConnectionState(isConnected,
+                                AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+                                aAddress);
   } else if (!strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID)) {
       String8 cmd;
       BluetoothHfpManagerBase* hfp =
           static_cast<BluetoothHfpManagerBase*>(aSubject);
       if (hfp->IsNrecEnabled()) {
           cmd.setTo("bt_headset_name=<unknown>;bt_headset_nrec=on");
           AudioSystem::setParameters(0, cmd);
       } else {
@@ -508,25 +592,19 @@ AudioManager::Observe(nsISupports* aSubj
     if (!StringBeginsWith(setting.mKey, NS_LITERAL_STRING("audio.volume."))) {
       return NS_OK;
     }
     if (!setting.mValue.isNumber()) {
       return NS_OK;
     }
 
     uint32_t volIndex = setting.mValue.toNumber();
-    for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
+    for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
       if (setting.mKey.EqualsASCII(gVolumeData[idx].mChannelName)) {
-        SetVolumeByCategory(gVolumeData[idx].mCategory, volIndex);
-        nsCOMPtr<nsISettingsServiceLock> lock = GetSettingServiceLock();
-        UpdateVolumeSettingToDatabase(lock.get(),
-                                      AppendProfileToVolumeSetting(
-                                        gVolumeData[idx].mChannelName,
-                                        mPresentProfile).get(),
-                                      volIndex);
+        SetStreamVolumeIndex(gVolumeData[idx].mStreamType, volIndex);
         return NS_OK;
       }
     }
   }
 
   NS_WARNING("Unexpected topic in AudioManager");
   return NS_ERROR_FAILURE;
 }
@@ -570,73 +648,74 @@ AudioManager::HandleHeadphoneSwitchEvent
       NS_NewRunnableFunction([self]() {
         if (self->mSwitchDone) {
           return;
         }
         self->UpdateHeadsetConnectionState(hal::SWITCH_STATE_OFF);
         self->mSwitchDone = true;
     });
     MessageLoop::current()->PostDelayedTask(FROM_HERE, new RunnableCallTask(runnable), 1000);
-
-    SwitchProfileData(DEVICE_HEADSET, false);
     mSwitchDone = false;
   } else if (aEvent.status() != hal::SWITCH_STATE_OFF) {
     UpdateHeadsetConnectionState(aEvent.status());
-    SwitchProfileData(DEVICE_HEADSET, true);
     mSwitchDone = true;
   }
   // Handle the coexistence of a2dp / headset device, latest one wins.
 #if ANDROID_VERSION >= 17
   int32_t forceUse = 0;
   GetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, &forceUse);
   if (aEvent.status() != hal::SWITCH_STATE_OFF && mBluetoothA2dpEnabled) {
     SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NO_BT_A2DP);
   } else if (forceUse == AUDIO_POLICY_FORCE_NO_BT_A2DP) {
     SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE);
   }
 #endif
 }
 
 AudioManager::AudioManager()
   : mPhoneState(PHONE_STATE_CURRENT)
-  , mHeadsetState(0)
+  , mIsVolumeInited(false)
+  , mAudioOutProfileUpdated(0)
   , mSwitchDone(true)
 #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
   , mBluetoothA2dpEnabled(false)
 #endif
 #ifdef MOZ_B2G_BT
   , mA2dpSwitchDone(true)
 #endif
   , mObserver(new HeadphoneSwitchObserver())
 #ifdef MOZ_B2G_RIL
   , mMuteCallToRIL(false)
 #endif
 {
-  hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
+  AudioSystem::setErrorCallback(BinderDeadCallback);
+#if ANDROID_VERSION >= 21
+  android::sp<GonkAudioPortCallback> callback = new GonkAudioPortCallback();
+  AudioSystem::setAudioPortCallback(callback);
+#endif
 
+  // Create VolumeStreamStates
+  for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
+    VolumeStreamState* streamState =
+      new VolumeStreamState(*this, static_cast<audio_stream_type_t>(loop));
+    mStreamStates.AppendElement(streamState);
+  }
+  UpdateCachedActiveDevicesForStreams();
+
+  RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
+  // Initialize headhone/heaset status
   UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
   NotifyHeadphonesStatus(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
 
-  for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) {
-    AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(loop), 0,
-                                  sMaxStreamVolumeTbl[loop]);
-    mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop];
-  }
-  // Force publicnotification to output at maximal volume
-  SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE,
-                       sMaxStreamVolumeTbl[AUDIO_STREAM_ENFORCED_AUDIBLE]);
-  CreateAudioProfilesData();
-
   // Get the initial volume index from settings DB during boot up.
   InitVolumeFromDatabase();
 
   // Gecko only control stream volume not master so set to default value
   // directly.
   AudioSystem::setMasterVolume(1.0);
-  AudioSystem::setErrorCallback(BinderDeadCallback);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
   if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID, false))) {
     NS_WARNING("Failed to add bluetooth sco status changed observer!");
   }
   if (NS_FAILED(obs->AddObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID, false))) {
     NS_WARNING("Failed to add bluetooth a2dp status changed observer!");
@@ -659,16 +738,20 @@ AudioManager::AudioManager()
   property_get("ro.moz.mute.call.to_ril", value, "false");
   if (!strcmp(value, "true")) {
     mMuteCallToRIL = true;
   }
 #endif
 }
 
 AudioManager::~AudioManager() {
+  AudioSystem::setErrorCallback(nullptr);
+#if ANDROID_VERSION >= 21
+  AudioSystem::setAudioPortCallback(nullptr);
+#endif
   hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
   if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_SCO_STATUS_CHANGED_ID))) {
     NS_WARNING("Failed to remove bluetooth sco status changed observer!");
   }
   if (NS_FAILED(obs->RemoveObserver(this, BLUETOOTH_A2DP_STATUS_CHANGED_ID))) {
@@ -767,27 +850,39 @@ AudioManager::SetPhoneState(int32_t aSta
 #if ANDROID_VERSION < 17
   if (AudioSystem::setPhoneState(aState)) {
 #else
   if (AudioSystem::setPhoneState(static_cast<audio_mode_t>(aState))) {
 #endif
     return NS_ERROR_FAILURE;
   }
 
+#if ANDROID_VERSION < 21
+  // Manually call it, since AudioPortCallback is not supported.
+  // Current volumes might be changed by updating active devices in android
+  // AudioPolicyManager.
+  MaybeUpdateVolumeSettingToDatabase();
+#endif
   mPhoneState = aState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce)
 {
 #if ANDROID_VERSION >= 15
   status_t status = AudioSystem::setForceUse(
                       (audio_policy_force_use_t)aUsage,
                       (audio_policy_forced_cfg_t)aForce);
+#if ANDROID_VERSION < 21
+  // Manually call it, since AudioPortCallback is not supported.
+  // Current volumes might be changed by updating active devices in android
+  // AudioPolicyManager.
+  MaybeUpdateVolumeSettingToDatabase();
+#endif
   return status ? NS_ERROR_FAILURE : NS_OK;
 #else
   NS_NOTREACHED("Doesn't support force routing on GB version");
   return NS_ERROR_UNEXPECTED;
 #endif
 }
 
 NS_IMETHODIMP
@@ -799,318 +894,172 @@ AudioManager::GetForceForUse(int32_t aUs
   NS_NOTREACHED("Doesn't support force routing on GB version");
   return NS_ERROR_UNEXPECTED;
 #endif
 }
 
 NS_IMETHODIMP
 AudioManager::GetFmRadioAudioEnabled(bool *aFmRadioAudioEnabled)
 {
-  *aFmRadioAudioEnabled = IsDeviceOn(AUDIO_DEVICE_OUT_FM);
+  *aFmRadioAudioEnabled = IsFmOutConnected();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
 {
-  AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM,
-    aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
-    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
-  UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES));
+  UpdateDeviceConnectionState(aFmRadioAudioEnabled,
+                              AUDIO_DEVICE_OUT_FM,
+                              NS_LITERAL_CSTRING(""));
   // AUDIO_STREAM_FM is not used on recent gonk.
   // AUDIO_STREAM_MUSIC is used for FM radio volume control.
 #if ANDROID_VERSION < 19
   // sync volume with music after powering on fm radio
   if (aFmRadioAudioEnabled) {
-    uint32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
-    SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex);
-    mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex;
+    uint32_t volIndex = mStreamStates[AUDIO_STREAM_MUSIC]->GetVolumeIndex();
+    nsresult rv = mStreamStates[AUDIO_STREAM_FM]->
+      SetVolumeIndex(volIndex, AUDIO_DEVICE_OUT_FM);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 #endif
   return NS_OK;
 }
 
+NS_IMETHODIMP
+AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex)
+{
+  if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  return SetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex);
+}
+
+NS_IMETHODIMP
+AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex)
+{
+  if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!aIndex) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  return GetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex);
+}
+
+NS_IMETHODIMP
+AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex)
+{
+  if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!aMaxIndex) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  *aMaxIndex = mStreamStates[sChannelStreamTbl[aChannel]]->GetMaxIndex();
+   return NS_OK;
+}
+
 nsresult
-AudioManager::ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const
+AudioManager::ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const
 {
-  uint32_t maxIndex = GetMaxVolumeByCategory(aCategory);
+  if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  uint32_t maxIndex = mStreamStates[aStream]->GetMaxIndex();
   if (aIndex > maxIndex) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
-AudioManager::SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex)
+AudioManager::SetStreamVolumeForProfile(AudioOutputProfiles aProfile,
+                                        int32_t aStream,
+                                        uint32_t aIndex)
 {
-  nsresult status;
-  switch (static_cast<AudioVolumeCategories>(aCategory)) {
-    case VOLUME_MEDIA:
-      // AUDIO_STREAM_FM is not used on recent gonk.
-      // AUDIO_STREAM_MUSIC is used for FM radio volume control.
-#if ANDROID_VERSION < 19
-      // sync FMRadio's volume with content channel.
-      if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) {
-        status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex);
-        if (NS_WARN_IF(NS_FAILED(status))) {
-          return status;
-        }
-      }
-#endif
-      status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex);
+  if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  int32_t streamAlias = sStreamVolumeAliasTbl[aStream];
+  VolumeStreamState* state = mStreamStates[streamAlias].get();
+  // Rescaling of index is not necessary.
+  switch (aProfile) {
+    case DEVICE_PRIMARY:
+      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
       break;
-    case VOLUME_NOTIFICATION:
-      status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
-      if (NS_WARN_IF(NS_FAILED(status))) {
-        return status;
-      }
-      status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
-      if (NS_WARN_IF(NS_FAILED(status))) {
-        return status;
-      }
-      status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex);
+    case DEVICE_HEADSET:
+      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET);
       break;
-    case VOLUME_ALARM:
-      status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex);
-      break;
-    case VOLUME_TELEPHONY:
-      status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex);
-    case VOLUME_BLUETOOTH_SCO:
-      status = SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, aIndex);
+    case DEVICE_BLUETOOTH:
+      state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
       break;
     default:
-      return NS_ERROR_INVALID_ARG;
-  }
-  return status;
-}
-
-uint32_t
-AudioManager::GetVolumeByCategory(uint32_t aCategory) const
-{
-  switch (static_cast<AudioVolumeCategories>(aCategory)) {
-    case VOLUME_MEDIA:
-      return mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
-    case VOLUME_NOTIFICATION:
-      MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
-                 mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]);
-      MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
-                 mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
-      return mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
-    case VOLUME_ALARM:
-      return mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM];
-    case VOLUME_TELEPHONY:
-      return mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
-    case VOLUME_BLUETOOTH_SCO:
-      return mCurrentStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO];
-    default:
-      NS_WARNING("Can't get volume from error volume category.");
-      return 0;
-  }
-}
-
-uint32_t
-AudioManager::GetMaxVolumeByCategory(uint32_t aCategory) const
-{
-  switch (static_cast<AudioVolumeCategories>(aCategory)) {
-    case VOLUME_MEDIA:
-      return sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC];
-    case VOLUME_NOTIFICATION:
-      MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
-                 sMaxStreamVolumeTbl[AUDIO_STREAM_RING]);
-      MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
-                 sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
-      return sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
-    case VOLUME_ALARM:
-      return sMaxStreamVolumeTbl[AUDIO_STREAM_ALARM];
-    case VOLUME_TELEPHONY:
-      return sMaxStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
-    case VOLUME_BLUETOOTH_SCO:
-      return sMaxStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO];
-    default:
-      NS_WARNING("Can't get max volume from error volume category.");
-      return 0;
-  }
-}
-
-NS_IMETHODIMP
-AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex)
-{
-  nsresult status;
-  AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
-                                    VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
-  switch (static_cast<AudioChannel>(aChannel)) {
-    case AudioChannel::Normal:
-    case AudioChannel::Content:
-      status = SetVolumeByCategory(VOLUME_MEDIA, aIndex);
       break;
-    case AudioChannel::Notification:
-    case AudioChannel::Ringer:
-    case AudioChannel::Publicnotification:
-    case AudioChannel::System:
-      status = SetVolumeByCategory(VOLUME_NOTIFICATION, aIndex);
-      break;
-    case AudioChannel::Alarm:
-      status = SetVolumeByCategory(VOLUME_ALARM, aIndex);
-      break;
-    case AudioChannel::Telephony:
-      status = SetVolumeByCategory(category, aIndex);
-      break;
-    default:
-      return NS_ERROR_INVALID_ARG;
-  }
-  return status;
-}
-
-NS_IMETHODIMP
-AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex)
-{
-  if (!aIndex) {
-    return NS_ERROR_NULL_POINTER;
-  }
-  AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
-                                    VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
-  switch (static_cast<AudioChannel>(aChannel)) {
-    case AudioChannel::Normal:
-    case AudioChannel::Content:
-      *aIndex = GetVolumeByCategory(VOLUME_MEDIA);
-      break;
-    case AudioChannel::Notification:
-    case AudioChannel::Ringer:
-    case AudioChannel::Publicnotification:
-    case AudioChannel::System:
-      *aIndex = GetVolumeByCategory(VOLUME_NOTIFICATION);
-      break;
-    case AudioChannel::Alarm:
-      *aIndex = GetVolumeByCategory(VOLUME_ALARM);
-      break;
-    case AudioChannel::Telephony:
-      *aIndex = GetVolumeByCategory(category);
-      break;
-    default:
-      return NS_ERROR_INVALID_ARG;
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex)
-{
-  if (!aMaxIndex) {
-    return NS_ERROR_NULL_POINTER;
-  }
-  AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ?
-                                    VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY;
-  switch (static_cast<AudioChannel>(aChannel)) {
-    case AudioChannel::Normal:
-    case AudioChannel::Content:
-      *aMaxIndex = GetMaxVolumeByCategory(VOLUME_MEDIA);
-      break;
-    case AudioChannel::Notification:
-    case AudioChannel::Ringer:
-    case AudioChannel::Publicnotification:
-    case AudioChannel::System:
-      *aMaxIndex = GetMaxVolumeByCategory(VOLUME_NOTIFICATION);
-      break;
-    case AudioChannel::Alarm:
-      *aMaxIndex = GetMaxVolumeByCategory(VOLUME_ALARM);
-      break;
-    case AudioChannel::Telephony:
-      *aMaxIndex = GetMaxVolumeByCategory(category);
-      break;
-    default:
-      return NS_ERROR_INVALID_ARG;
   }
   return NS_OK;
 }
 
 nsresult
-AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex) {
-  if (aIndex > sMaxStreamVolumeTbl[aStream]) {
+AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex)
+{
+  if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
-  mCurrentStreamVolumeTbl[aStream] = aIndex;
-  status_t status;
-#if ANDROID_VERSION < 17
-   status = AudioSystem::setStreamVolumeIndex(
-              static_cast<audio_stream_type_t>(aStream),
-              aIndex);
-   return status ? NS_ERROR_FAILURE : NS_OK;
-#else
+
+  int32_t streamAlias = sStreamVolumeAliasTbl[aStream];
+
+  nsresult rv;
+  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
+    if (streamAlias == sStreamVolumeAliasTbl[streamType]) {
+      rv = mStreamStates[streamType]->SetVolumeIndexToActiveDevices(aIndex);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  // AUDIO_STREAM_FM is not used on recent gonk.
+  // AUDIO_STREAM_MUSIC is used for FM radio volume control.
 #if ANDROID_VERSION < 19
-  if (aStream == AUDIO_STREAM_FM) {
-    status = AudioSystem::setStreamVolumeIndex(
-               static_cast<audio_stream_type_t>(aStream),
-               aIndex,
-               AUDIO_DEVICE_OUT_FM);
-    return status ? NS_ERROR_FAILURE : NS_OK;
+  if (streamAlias == AUDIO_STREAM_MUSIC && IsFmOutConnected()) {
+    rv = mStreamStates[AUDIO_STREAM_FM]->
+      SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_FM);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 #endif
-  if (mPresentProfile == DEVICE_PRIMARY) {
-    status = AudioSystem::setStreamVolumeIndex(
-            static_cast<audio_stream_type_t>(aStream),
-            aIndex,
-            AUDIO_DEVICE_OUT_SPEAKER);
-    status += AudioSystem::setStreamVolumeIndex(
-            static_cast<audio_stream_type_t>(aStream),
-            aIndex,
-            AUDIO_DEVICE_OUT_EARPIECE);
-  } else if (mPresentProfile == DEVICE_HEADSET) {
-    status = AudioSystem::setStreamVolumeIndex(
-              static_cast<audio_stream_type_t>(aStream),
-              aIndex,
-              AUDIO_DEVICE_OUT_WIRED_HEADSET);
-    status += AudioSystem::setStreamVolumeIndex(
-              static_cast<audio_stream_type_t>(aStream),
-              aIndex,
-              AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
-  } else if (mPresentProfile == DEVICE_BLUETOOTH) {
-    status = AudioSystem::setStreamVolumeIndex(
-             static_cast<audio_stream_type_t>(aStream),
-             aIndex,
-             AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
-    status += AudioSystem::setStreamVolumeIndex(
-              static_cast<audio_stream_type_t>(aStream),
-              aIndex,
-              AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
-  } else {
-    NS_WARNING("Can't set stream volume on error profile!");
-  }
-  return status ? NS_ERROR_FAILURE : NS_OK;
-#endif
+
+  return NS_OK;
 }
 
 nsresult
-AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) {
+AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex)
+{
   if (!aIndex) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  *aIndex = mCurrentStreamVolumeTbl[aStream];
-
+  *aIndex = mStreamStates[aStream]->GetVolumeIndex();
   return NS_OK;
 }
 
-AudioProfileData*
-AudioManager::FindAudioProfileData(AudioOutputProfiles aProfile)
-{
-  uint32_t profilesNum = mAudioProfiles.Length();
-  MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!");
-  for (uint32_t idx = 0; idx < profilesNum; ++idx) {
-    if (mAudioProfiles[idx]->GetProfile() == aProfile) {
-      return mAudioProfiles[idx];
-    }
-  }
-  NS_WARNING("Can't find audio profile data");
-  return nullptr;
-}
-
 nsAutoCString
 AudioManager::AppendProfileToVolumeSetting(const char* aName, AudioOutputProfiles aProfile)
 {
   nsAutoCString topic;
   topic.Assign(aName);
   for (uint32_t idx = 0; kAudioOutputProfilesTable[idx].tag; ++idx) {
     if (kAudioOutputProfilesTable[idx].value == aProfile) {
       topic.Append(".");
@@ -1137,158 +1086,435 @@ AudioManager::InitVolumeFromDatabase()
   }
 
   RefPtr<VolumeInitCallback> callback = new VolumeInitCallback();
   MOZ_ASSERT(callback);
   callback->GetPromise()->Then(AbstractThread::MainThread(), __func__, this,
                                &AudioManager::InitProfileVolumeSucceeded,
                                &AudioManager::InitProfileVolumeFailed);
 
-  for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
-    for (uint32_t pIdx = 0; pIdx < DEVICE_TOTAL_NUMBER; ++pIdx) {
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
+    for (uint32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) {
       lock->Get(AppendProfileToVolumeSetting(gVolumeData[idx].mChannelName,
-                  static_cast<AudioOutputProfiles>(pIdx)).get(), callback);
+                  static_cast<AudioOutputProfiles>(profile)).get(), callback);
     }
   }
 }
 
 void
 AudioManager::InitProfileVolumeSucceeded()
 {
-  SendVolumeChangeNotification(FindAudioProfileData(mPresentProfile));
+  mIsVolumeInited = true;
+  MaybeUpdateVolumeSettingToDatabase(true);
 }
 
 void
 AudioManager::InitProfileVolumeFailed(const char* aError)
 {
+  // Initialize stream volumes with default values
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
+    for (int32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) {
+      int32_t stream = gVolumeData[idx].mStreamType;
+      uint32_t volIndex = sDefaultStreamVolumeTbl[stream];
+      InitVolumeForProfile(static_cast<AudioOutputProfiles>(profile),
+                           stream,
+                           volIndex);
+    }
+  }
+  mIsVolumeInited = true;
+  MaybeUpdateVolumeSettingToDatabase(true);
   NS_WARNING(aError);
 }
 
 void
-AudioManager::UpdateVolumeSettingToDatabase(nsISettingsServiceLock* aLock,
-                                            const char* aTopic,
-                                            uint32_t aVolIndex)
+AudioManager::MaybeUpdateVolumeSettingToDatabase(bool aForce)
 {
-  MOZ_ASSERT(aLock);
+  if (!mIsVolumeInited) {
+    return;
+  }
 
+  nsCOMPtr<nsISettingsServiceLock> lock = GetSettingServiceLock();
+  if (NS_WARN_IF(!lock)) {
+    return;
+  }
+
+  // Send events to update the Gaia volumes
   mozilla::AutoSafeJSContext cx;
   JS::Rooted<JS::Value> value(cx);
-  value.setInt32(aVolIndex);
-  aLock->Set(aTopic, value, nullptr, nullptr);
-}
+  uint32_t volume = 0;
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
+    int32_t streamType = gVolumeData[idx].mStreamType;
+    VolumeStreamState* streamState = mStreamStates[streamType].get();
+    if(!aForce && !streamState->IsDevicesChanged()) {
+      continue;
+    }
+    // Get volume index of active device.
+    volume = streamState->GetVolumeIndex();
+    value.setInt32(volume);
+    lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr);
+  }
 
-void
-AudioManager::SendVolumeChangeNotification(AudioProfileData* aProfileData)
-{
-  MOZ_ASSERT(aProfileData);
+  // Clear device changed flags
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
+    int32_t  streamType = gVolumeData[idx].mStreamType;
+    mStreamStates[streamType]->ClearDevicesChanged();
+  }
+
+  if (!mAudioOutProfileUpdated) {
+    return;
+  }
+
+  // For reducing the code dependency, Gaia doesn't need to know the
+  // profile volume, it only need to care about different volume categories.
+  // However, we need to send the setting volume to the permanent database,
+  // so that we can store the volume setting even if the phone reboots.
 
-  // Change the value of the current volume setting, so that the Gaia can get
-  // correct volume values and update the volume UI. In addition, for reducing
-  // the code dependency, Gaia doesn't need to know the current profile, it
-  // only need to care about different volume categories.
-  nsCOMPtr<nsISettingsServiceLock> lock = GetSettingServiceLock();
-  for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
-    uint32_t volSetting = gVolumeData[idx].mCategory;
-    UpdateVolumeSettingToDatabase(lock.get(),
-                                  gVolumeData[idx].mChannelName,
-                                  aProfileData->mVolumeTable[volSetting]);
+  for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) {
+    int32_t  streamType = gVolumeData[idx].mStreamType;
+    VolumeStreamState* streamState = mStreamStates[streamType].get();
+
+    if (mAudioOutProfileUpdated & (1 << DEVICE_PRIMARY)) {
+      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_SPEAKER);
+      value.setInt32(volume);
+      lock->Set(AppendProfileToVolumeSetting(
+                  gVolumeData[idx].mChannelName,
+                  DEVICE_PRIMARY).get(),
+                  value, nullptr, nullptr);
+    }
+    if (mAudioOutProfileUpdated & (1 << DEVICE_HEADSET)) {
+      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_WIRED_HEADSET);
+      value.setInt32(volume);
+      lock->Set(AppendProfileToVolumeSetting(
+                  gVolumeData[idx].mChannelName,
+                  DEVICE_HEADSET).get(),
+                  value, nullptr, nullptr);
+    }
+    if (mAudioOutProfileUpdated & (1 << DEVICE_BLUETOOTH)) {
+      volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
+      value.setInt32(volume);
+      lock->Set(AppendProfileToVolumeSetting(
+                  gVolumeData[idx].mChannelName,
+                  DEVICE_BLUETOOTH).get(),
+                  value, nullptr, nullptr);
+    }
   }
-}
 
-void
-AudioManager::CreateAudioProfilesData()
-{
-  MOZ_ASSERT(mAudioProfiles.IsEmpty(), "mAudioProfiles should be empty!");
-  for (uint32_t idx = 0; idx < DEVICE_TOTAL_NUMBER; ++idx) {
-    AudioProfileData* profile = new AudioProfileData(static_cast<AudioOutputProfiles>(idx));
-    mAudioProfiles.AppendElement(profile);
-  }
-  UpdateProfileState(DEVICE_PRIMARY, true);
+  // Clear mAudioOutProfileUpdated
+  mAudioOutProfileUpdated = 0;
 }
 
 void
-AudioManager::InitProfileVolume(AudioOutputProfiles aProfile,
-                                 uint32_t aCategory,
-                                 uint32_t aIndex)
-{
-  AudioProfileData* profileData = FindAudioProfileData(aProfile);
-  MOZ_ASSERT(profileData);
-  profileData->mVolumeTable[aCategory] = aIndex;
-  SetVolumeByCategory(aCategory, aIndex);
-}
-
-void
-AudioManager::SwitchProfileData(AudioOutputProfiles aProfile,
-                                bool aActive)
+AudioManager::InitVolumeForProfile(AudioOutputProfiles aProfile,
+                                   int32_t aStreamType,
+                                   uint32_t aIndex)
 {
-  MOZ_ASSERT(DEVICE_PRIMARY <= aProfile &&
-             aProfile < DEVICE_TOTAL_NUMBER, "Error profile type!");
-
-  // Save the present profile volume data.
-  AudioOutputProfiles oldProfile = mPresentProfile;
-  AudioProfileData* profileData = FindAudioProfileData(oldProfile);
-  MOZ_ASSERT(profileData);
-  UpdateVolumeToProfile(profileData);
-  UpdateProfileState(aProfile, aActive);
-
-  AudioOutputProfiles newProfile = mPresentProfile;
-  if (oldProfile == newProfile) {
-    return;
-  }
-
-  // Update new profile volume data and send the changing event.
-  profileData = FindAudioProfileData(newProfile);
-  MOZ_ASSERT(profileData);
-  UpdateVolumeFromProfile(profileData);
-  SendVolumeChangeNotification(profileData);
-}
-
-void
-AudioManager::UpdateProfileState(AudioOutputProfiles aProfile, bool aActive)
-{
-  MOZ_ASSERT(DEVICE_PRIMARY <= aProfile && aProfile < DEVICE_TOTAL_NUMBER,
-             "Error profile type!");
-  if (aProfile == DEVICE_PRIMARY && !aActive) {
-    NS_WARNING("Can't turn off the primary profile!");
-    return;
-  }
-
-  mAudioProfiles[aProfile]->SetActive(aActive);
-  if (aActive) {
-    mPresentProfile = aProfile;
-    return;
-  }
-
-  // The primary profile has the lowest priority. We will check whether there
-  // are other profiles. The bluetooth and headset have the same priotity.
-  uint32_t profilesNum = mAudioProfiles.Length();
-  MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!");
-  for (int32_t idx = profilesNum - 1; idx >= 0; --idx) {
-    if (mAudioProfiles[idx]->GetActive()) {
-      mPresentProfile = static_cast<AudioOutputProfiles>(idx);
-      break;
+  // Set volume to streams of aStreamType and devices of Profile.
+  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
+    if (aStreamType == sStreamVolumeAliasTbl[streamType]) {
+      SetStreamVolumeForProfile(aProfile, streamType, aIndex);
     }
   }
 }
 
 void
-AudioManager::UpdateVolumeToProfile(AudioProfileData* aProfileData)
+AudioManager::UpdateCachedActiveDevicesForStreams()
+{
+  // This function updates cached active devices for streams.
+  // It is used for optimization of GetDevicesForStream() since L.
+  // AudioManager could know when active devices
+  // are changed in AudioPolicyManager by onAudioPortListUpdate().
+  // Except it, AudioManager normally do not need to ask AuidoPolicyManager
+  // about current active devices of streams and could use cached values.
+  // Before L, onAudioPortListUpdate() does not exist and GetDevicesForStream()
+  // does not use the cache. Therefore this function do nothing.
+#if ANDROID_VERSION >= 21
+  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
+    // Update cached active devices of stream
+    mStreamStates[streamType]->IsDevicesChanged(false /* aFromCache */);
+  }
+#endif
+}
+
+uint32_t
+AudioManager::GetDevicesForStream(int32_t aStream, bool aFromCache)
+{
+#if ANDROID_VERSION >= 21
+  // Since Lollipop, devices update could be notified by AudioPortCallback.
+  // Cached values can be used if there is no update.
+  if (aFromCache) {
+    return mStreamStates[aStream]->GetLastDevices();
+  }
+#endif
+
+#if ANDROID_VERSION >= 17
+  audio_devices_t devices =
+    AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(aStream));
+
+  return static_cast<uint32_t>(devices);
+#else
+  return AUDIO_DEVICE_OUT_DEFAULT;
+#endif
+}
+
+uint32_t
+AudioManager::GetDeviceForStream(int32_t aStream)
 {
-  MOZ_ASSERT(aProfileData);
-  for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
-    uint32_t volume = GetVolumeByCategory(gVolumeData[idx].mCategory);
-    aProfileData->mVolumeTable[gVolumeData[idx].mCategory] = volume;
+  uint32_t devices =
+    GetDevicesForStream(static_cast<audio_stream_type_t>(aStream));
+  uint32_t device = SelectDeviceFromDevices(devices);
+  return device;
+}
+
+/* static */ uint32_t
+AudioManager::SelectDeviceFromDevices(uint32_t aOutDevices)
+{
+  uint32_t device = aOutDevices;
+
+  // See android AudioService.getDeviceForStream().
+  // AudioPolicyManager expects it.
+  // See also android AudioPolicyManager::getDeviceForVolume().
+  if ((device & (device - 1)) != 0) {
+    // Multiple device selection.
+    if ((device & AUDIO_DEVICE_OUT_SPEAKER) != 0) {
+      device = AUDIO_DEVICE_OUT_SPEAKER;
+#if ANDROID_VERSION >= 21
+    } else if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) != 0) {
+      device = AUDIO_DEVICE_OUT_HDMI_ARC;
+    } else if ((device & AUDIO_DEVICE_OUT_SPDIF) != 0) {
+      device = AUDIO_DEVICE_OUT_SPDIF;
+    } else if ((device & AUDIO_DEVICE_OUT_AUX_LINE) != 0) {
+       device = AUDIO_DEVICE_OUT_AUX_LINE;
+#endif
+    } else {
+       device &= AUDIO_DEVICE_OUT_ALL_A2DP;
+    }
   }
+  return device;
+}
+AudioManager::VolumeStreamState::VolumeStreamState(AudioManager& aManager,
+                                                   int32_t aStreamType)
+  : mManager(aManager)
+  , mStreamType(aStreamType)
+  , mLastDevices(0)
+  , mIsDevicesChanged(true)
+{
+  InitStreamVolume();
+}
+
+bool
+AudioManager::VolumeStreamState::IsDevicesChanged(bool aFromCache)
+{
+  uint32_t devices = mManager.GetDevicesForStream(mStreamType, aFromCache);
+  if (devices != mLastDevices) {
+    mLastDevices = devices;
+    mIsDevicesChanged = true;
+  }
+  return mIsDevicesChanged;
+}
+
+void
+AudioManager::VolumeStreamState::ClearDevicesChanged()
+{
+  mIsDevicesChanged = false;
 }
 
 void
-AudioManager::UpdateVolumeFromProfile(AudioProfileData* aProfileData)
+AudioManager::VolumeStreamState::InitStreamVolume()
+{
+  AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(mStreamType),
+                                0,
+                                GetMaxIndex());
+}
+
+uint32_t
+AudioManager::VolumeStreamState::GetMaxIndex()
+{
+  return sMaxStreamVolumeTbl[mStreamType];
+}
+
+uint32_t
+AudioManager::VolumeStreamState::GetDefaultIndex()
+{
+  return sDefaultStreamVolumeTbl[mStreamType];
+}
+
+uint32_t
+AudioManager::VolumeStreamState::GetVolumeIndex()
+{
+  uint32_t device = mManager.GetDeviceForStream(mStreamType);
+  return GetVolumeIndex(device);
+}
+
+uint32_t
+AudioManager::VolumeStreamState::GetVolumeIndex(uint32_t aDevice)
+{
+  uint32_t index = 0;
+  bool ret = mVolumeIndexes.Get(aDevice, &index);
+  if (!ret) {
+    index = mVolumeIndexes.Get(AUDIO_DEVICE_OUT_DEFAULT);
+  }
+  return index;
+}
+
+nsresult
+AudioManager::VolumeStreamState::SetVolumeIndexToActiveDevices(uint32_t aIndex)
+{
+  uint32_t device = mManager.GetDeviceForStream(mStreamType);
+
+  // Update volume index for device
+  uint32_t oldVolumeIndex = 0;
+  bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex);
+  if (exist && aIndex == oldVolumeIndex) {
+    // No update
+    return NS_OK;
+  }
+
+  // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active
+  // devices for stream.
+  nsresult rv = SetVolumeIndexToAliasDevices(aIndex, device);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Workaround to make audio volume control consisitent.
+  // Active devices of AUDIO_STREAM_NOTIFICATION are affected by
+  // AUDIO_STREAM_MUSIC's activity. It makes audio volume control inconsistent.
+  // See Bug 1196724
+  if (device != AUDIO_DEVICE_OUT_SPEAKER &&
+      mStreamType == AUDIO_STREAM_NOTIFICATION) {
+      // Rescaling of index is not necessary.
+      rv = SetVolumeIndexToAliasDevices(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+AudioManager::VolumeStreamState::SetVolumeIndexToAliasStreams(uint32_t aIndex,
+                                                              uint32_t aDevice)
 {
-  MOZ_ASSERT(aProfileData);
-  for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) {
-    SetVolumeByCategory(gVolumeData[idx].mCategory,
-                        aProfileData->mVolumeTable[gVolumeData[idx].mCategory]);
+  uint32_t oldVolumeIndex = 0;
+  bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex);
+  if (exist && aIndex == oldVolumeIndex) {
+    // No update
+    return NS_OK;
+  }
+  nsresult rv = SetVolumeIndexToAliasDevices(aIndex, aDevice);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
+    if ((streamType != mStreamType) &&
+         sStreamVolumeAliasTbl[streamType] == mStreamType) {
+      // Rescaling of index is not necessary.
+      rv = mManager.mStreamStates[streamType]->
+        SetVolumeIndexToAliasStreams(aIndex, aDevice);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+  return NS_OK;
+}
+
+nsresult
+AudioManager::VolumeStreamState::SetVolumeIndexToAliasDevices(uint32_t aIndex,
+                                                              uint32_t aDevice)
+{
+#if ANDROID_VERSION >= 17
+  nsresult rv = NS_ERROR_FAILURE;
+  switch (aDevice) {
+    case AUDIO_DEVICE_OUT_EARPIECE:
+    case AUDIO_DEVICE_OUT_SPEAKER:
+      // Apply volume index of DEVICE_PRIMARY devices
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      mManager.AudioOutProfileUpdated(DEVICE_PRIMARY);
+      break;
+    case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+    case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+      // Apply volume index of DEVICE_HEADSET devices
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      mManager.AudioOutProfileUpdated(DEVICE_HEADSET);
+      break;
+    case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+    case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
+      // Apply volume index of DEVICE_BLUETOOTH devices
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      mManager.AudioOutProfileUpdated(DEVICE_BLUETOOTH);
+      break;
+    default:
+      rv = SetVolumeIndex(aIndex, aDevice);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      break;
+  }
+#else
+  SetVolumeIndex(aIndex, aDevice);
+#endif
+  return NS_OK;
+}
+
+nsresult
+AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex,
+                                                uint32_t aDevice,
+                                                bool aUpdateCache)
+{
+  status_t rv;
+#if ANDROID_VERSION >= 17
+  if (aUpdateCache) {
+    mVolumeIndexes.Put(aDevice, aIndex);
+  }
+
+  rv = AudioSystem::setStreamVolumeIndex(
+         static_cast<audio_stream_type_t>(mStreamType),
+         aIndex,
+         aDevice);
+  return rv ? NS_ERROR_FAILURE : NS_OK;
+#else
+  if (aUpdateCache) {
+    mVolumeIndexes.Put(AUDIO_DEVICE_OUT_DEFAULT, aIndex);
+  }
+  rv = AudioSystem::setStreamVolumeIndex(
+         static_cast<audio_stream_type_t>(mStreamType),
+         aIndex);
+  return rv ? NS_ERROR_FAILURE : NS_OK;
+#endif
+}
+
+void
+AudioManager::VolumeStreamState::RestoreVolumeIndexToAllDevices()
+{
+  for (auto iter = mVolumeIndexes.Iter(); !iter.Done(); iter.Next()) {
+    const uint32_t& key = iter.Key();
+    uint32_t& index = iter.Data();
+    SetVolumeIndex(key, index, /* aUpdateCache */ false);
   }
 }
 
 } /* namespace gonk */
 } /* namespace dom */
 } /* namespace mozilla */
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -13,17 +13,19 @@
  * limitations under the License.
  */
 
 #ifndef mozilla_dom_system_b2g_audiomanager_h__
 #define mozilla_dom_system_b2g_audiomanager_h__
 
 #include "mozilla/HalTypes.h"
 #include "mozilla/Observer.h"
+#include "mozilla/UniquePtr.h"
 #include "nsAutoPtr.h"
+#include "nsDataHashtable.h"
 #include "nsIAudioManager.h"
 #include "nsIObserver.h"
 #include "android_audio/AudioSystem.h"
 
 // {b2b51423-502d-4d77-89b3-7786b562b084}
 #define NS_AUDIOMANAGER_CID {0x94f6fd70, 0x7615, 0x4af9, \
       {0x89, 0x10, 0xf9, 0x3c, 0x55, 0xe6, 0x62, 0xec}}
 #define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
@@ -48,145 +50,152 @@ namespace gonk {
 enum AudioOutputProfiles {
   DEVICE_ERROR        = -1,
   DEVICE_PRIMARY      = 0,
   DEVICE_HEADSET      = 1,
   DEVICE_BLUETOOTH    = 2,
   DEVICE_TOTAL_NUMBER = 3,
 };
 
-/**
- * We have five sound volume settings from UX spec,
- * You can see more informations in Bug1068219.
- * (1) Media : music, video, FM ...
- * (2) Notification : ringer, notification ...
- * (3) Alarm : alarm
- * (4) Telephony : GSM call, WebRTC call
- * (5) Bluetooth SCO : SCO call
- **/
-enum AudioVolumeCategories {
-  VOLUME_MEDIA         = 0,
-  VOLUME_NOTIFICATION  = 1,
-  VOLUME_ALARM         = 2,
-  VOLUME_TELEPHONY     = 3,
-  VOLUME_BLUETOOTH_SCO = 4,
-  VOLUME_TOTAL_NUMBER  = 5,
-};
-
-struct VolumeData {
-  const char* mChannelName;
-  uint32_t mCategory;
-};
-
-class RecoverTask;
 class VolumeInitCallback;
-class AudioProfileData;
 
 class AudioManager final : public nsIAudioManager
                          , public nsIObserver
 {
 public:
   static already_AddRefed<AudioManager> GetInstance();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAUDIOMANAGER
   NS_DECL_NSIOBSERVER
 
-  // When audio backend is dead, recovery task needs to read all volume
-  // settings then set back into audio backend.
-  friend class RecoverTask;
-  friend class VolumeInitCallback;
-
-  // Open or close the specific profile
-  void SwitchProfileData(AudioOutputProfiles aProfile, bool aActive);
-
   // Validate whether the volume index is within the range
-  nsresult ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const;
+  nsresult ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const;
 
   // Called when android AudioFlinger in mediaserver is died
   void HandleAudioFlingerDied();
 
   void HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent);
 
+  class VolumeStreamState {
+  public:
+    explicit VolumeStreamState(AudioManager& aManager, int32_t aStreamType);
+    int32_t GetStreamType()
+    {
+      return mStreamType;
+    }
+    bool IsDevicesChanged(bool aFromCache = true);
+    void ClearDevicesChanged();
+    uint32_t GetLastDevices()
+    {
+      return mLastDevices;
+    }
+    void InitStreamVolume();
+    uint32_t GetMaxIndex();
+    uint32_t GetDefaultIndex();
+    uint32_t GetVolumeIndex();
+    uint32_t GetVolumeIndex(uint32_t aDevice);
+    void ClearCurrentVolumeUpdated();
+    // Set volume index to all active devices.
+    // Active devices are chosen by android AudioPolicyManager.
+    nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex);
+    // Set volume index to all alias streams. Alias streams have same volume.
+    // It is used to update volume based on audio output profile data.
+    nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice);
+    // Set volume index to all alias devices in audio output profile.
+    // Alias devices have same volume.
+    nsresult SetVolumeIndexToAliasDevices(uint32_t aIndex, uint32_t aDevice);
+    nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true);
+    // Restore volume index to all devices. Called when AudioFlinger is restarted.
+    void RestoreVolumeIndexToAllDevices();
+  private:
+    AudioManager& mManager;
+    const int32_t mStreamType;
+    uint32_t mLastDevices;
+    bool mIsDevicesChanged;
+    nsDataHashtable<nsUint32HashKey, uint32_t> mVolumeIndexes;
+  };
+
 protected:
   int32_t mPhoneState;
 
-  // A bitwise variable for recording what kind of headset/headphone is attached.
-  int32_t mHeadsetState;
+  bool mIsVolumeInited;
+
+  // A bitwise variable for volume update of audio output profiles
+  uint32_t mAudioOutProfileUpdated;
+
+  // Connected devices that are controlled by setDeviceConnectionState()
+  nsDataHashtable<nsUint32HashKey, nsCString> mConnectedDevices;
 
   bool mSwitchDone;
 
 #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17
   bool mBluetoothA2dpEnabled;
 #endif
 #ifdef MOZ_B2G_BT
   bool mA2dpSwitchDone;
 #endif
-  uint32_t mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT];
+  nsTArray<UniquePtr<VolumeStreamState> > mStreamStates;
+  uint32_t mLastChannelVolume[AUDIO_STREAM_CNT];
 
+  bool IsFmOutConnected();
+
+  nsresult SetStreamVolumeForProfile(AudioOutputProfiles aProfile,
+                                     int32_t aStream,
+                                     uint32_t aIndex);
   nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex);
   nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex);
 
+  void UpdateCachedActiveDevicesForStreams();
+  uint32_t GetDevicesForStream(int32_t aStream, bool aFromCache = true);
+  uint32_t GetDeviceForStream(int32_t aStream);
+  // Choose one device as representative of active devices.
+  static uint32_t SelectDeviceFromDevices(uint32_t aOutDevices);
+
 private:
   nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
 #ifdef MOZ_B2G_RIL
   bool                                    mMuteCallToRIL;
   // mIsMicMuted is only used for toggling mute call to RIL.
   bool                                    mIsMicMuted;
 #endif
-  nsTArray<nsAutoPtr<AudioProfileData>>   mAudioProfiles;
-  AudioOutputProfiles mPresentProfile;
 
   void HandleBluetoothStatusChanged(nsISupports* aSubject,
                                     const char* aTopic,
                                     const nsCString aAddress);
   void HandleAudioChannelProcessChanged();
 
-  void CreateAudioProfilesData();
-
-  // Init the volume setting from the init setting callback
-  void InitProfileVolume(AudioOutputProfiles aProfile,
-                        uint32_t aCatogory, uint32_t aIndex);
-
-  // Update volume data of profiles
-  void UpdateVolumeToProfile(AudioProfileData* aProfileData);
-
-  // Apply the volume data to device
-  void UpdateVolumeFromProfile(AudioProfileData* aProfileData);
+  // Initialize volume index for audio output profile
+  void InitVolumeForProfile(AudioOutputProfiles aProfile,
+                            int32_t aStreamType,
+                            uint32_t aIndex);
 
-  // Send the volume changing event to Gaia
-  void SendVolumeChangeNotification(AudioProfileData* aProfileData);
-
-  // Update the mPresentProfile and profiles active status
-  void UpdateProfileState(AudioOutputProfiles aProfile, bool aActive);
-
-  // Volume control functions
-  nsresult SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex);
-  uint32_t GetVolumeByCategory(uint32_t aCategory) const;
-  uint32_t GetMaxVolumeByCategory(uint32_t aCategory) const;
-
-  AudioProfileData* FindAudioProfileData(AudioOutputProfiles aProfile);
-
-  // Append the profile to the volume setting string.
+  // Append the audio output profile to the volume setting string.
   nsAutoCString AppendProfileToVolumeSetting(const char* aName,
                                              AudioOutputProfiles aProfile);
 
   // We store the volume setting in the database, these are related functions.
   void InitVolumeFromDatabase();
-  void UpdateVolumeSettingToDatabase(nsISettingsServiceLock* aLock,
-                                     const char* aTopic,
-                                     uint32_t aVolIndex);
+  void MaybeUpdateVolumeSettingToDatabase(bool aForce = false);
 
   // Promise functions.
   void InitProfileVolumeSucceeded();
   void InitProfileVolumeFailed(const char* aError);
 
+  void AudioOutProfileUpdated(AudioOutputProfiles aProfile);
+
   void UpdateHeadsetConnectionState(hal::SwitchState aState);
+  void UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName);
+  void SetAllDeviceConnectionStates();
 
   AudioManager();
   ~AudioManager();
+
+  friend class VolumeInitCallback;
+  friend class VolumeStreamState;
+  friend class GonkAudioPortCallback;
 };
 
 } /* namespace gonk */
 } /* namespace dom */
 } /* namespace mozilla */
 
 #endif // mozilla_dom_system_b2g_audiomanager_h__
--- a/dom/system/gonk/android_audio/AudioSystem.h
+++ b/dom/system/gonk/android_audio/AudioSystem.h
@@ -964,16 +964,19 @@ public:
     static status_t getStreamVolumeIndex(audio_stream_type_t stream,
                                          int *index,
                                          audio_devices_t device);
 #endif
     static status_t getStreamVolumeIndex(stream_type stream, int *index);
     static status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index);
 
     static uint32_t getStrategyForStream(stream_type stream);
+#if ANDROID_VERSION >= 17
+    static audio_devices_t getDevicesForStream(audio_stream_type_t stream);
+#endif
 
     static audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
     static status_t registerEffect(effect_descriptor_t *desc,
                                     audio_io_handle_t output,
                                     uint32_t strategy,
                                     int session,
                                     int id);
     static status_t unregisterEffect(int id);
@@ -990,16 +993,33 @@ public:
     static bool isSeperatedStream(stream_type stream);
     static bool isLowVisibility(stream_type stream);
     static bool isOutputChannel(uint32_t channel);
     static bool isInputChannel(uint32_t channel);
     static bool isValidFormat(uint32_t format);
     static bool isLinearPCM(uint32_t format);
     static bool isModeInCall();
 
+#if ANDROID_VERSION >= 21
+    class AudioPortCallback : public RefBase
+    {
+    public:
+
+                AudioPortCallback() {}
+        virtual ~AudioPortCallback() {}
+
+        virtual void onAudioPortListUpdate() = 0;
+        virtual void onAudioPatchListUpdate() = 0;
+        virtual void onServiceDied() = 0;
+
+    };
+
+    static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+#endif
+
 private:
 
     class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
     {
     public:
         AudioFlingerClient() {
         }
 
--- a/dom/xul/templates/nsXULContentUtils.cpp
+++ b/dom/xul/templates/nsXULContentUtils.cpp
@@ -90,17 +90,17 @@ nsXULContentUtils::Init()
 #define XUL_RESOURCE(ident, uri)                              \
   PR_BEGIN_MACRO                                              \
    rv = gRDF->GetResource(NS_LITERAL_CSTRING(uri), &(ident)); \
    if (NS_FAILED(rv)) return rv;                              \
   PR_END_MACRO
 
 #define XUL_LITERAL(ident, val)                                   \
   PR_BEGIN_MACRO                                                  \
-   rv = gRDF->GetLiteral(NS_LITERAL_STRING(val).get(), &(ident)); \
+   rv = gRDF->GetLiteral(MOZ_UTF16(val), &(ident));               \
    if (NS_FAILED(rv)) return rv;                                  \
   PR_END_MACRO
 
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
     rv = CallCreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &gFormat);
--- a/gfx/2d/DataSurfaceHelpers.cpp
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -105,45 +105,45 @@ CopyBGRXSurfaceDataToPackedBGRArray(uint
       srcPx += 4;
       dstPx += 3;
     }
     srcPx = aSrc += aSrcStride;
     dstPx = aDst += packedStride;
   }
 }
 
-uint8_t*
+UniquePtr<uint8_t[]>
 SurfaceToPackedBGRA(DataSourceSurface *aSurface)
 {
   SurfaceFormat format = aSurface->GetFormat();
   if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
     return nullptr;
   }
 
   IntSize size = aSurface->GetSize();
 
-  uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)];
+  UniquePtr<uint8_t[]> imageBuffer(
+    new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]);
   if (!imageBuffer) {
     return nullptr;
   }
 
   DataSourceSurface::MappedSurface map;
   if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
-    delete [] imageBuffer;
     return nullptr;
   }
 
-  CopySurfaceDataToPackedArray(map.mData, imageBuffer, size,
+  CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size,
                                map.mStride, 4 * sizeof(uint8_t));
 
   aSurface->Unmap();
 
   if (format == SurfaceFormat::B8G8R8X8) {
     // Convert BGRX to BGRA by setting a to 255.
-    ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t));
+    ConvertBGRXToBGRA(imageBuffer.get(), size, size.width * sizeof(uint32_t));
   }
 
   return imageBuffer;
 }
 
 uint8_t*
 SurfaceToPackedBGR(DataSourceSurface *aSurface)
 {
--- a/gfx/2d/DataSurfaceHelpers.h
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _MOZILLA_GFX_DATASURFACEHELPERS_H
 #define _MOZILLA_GFX_DATASURFACEHELPERS_H
 
 #include "2D.h"
 
+#include "mozilla/UniquePtr.h"
+
 namespace mozilla {
 namespace gfx {
 
 void
 ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
 
 /**
  * Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
@@ -20,21 +22,19 @@ ConvertBGRXToBGRA(uint8_t* aData, const 
  * surface. Callers are responsible for making sure that aDst is big enough to
  * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
  */
 void
 CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
                              int32_t aSrcStride, int32_t aBytesPerPixel);
 
 /**
- * Convert aSurface to a packed buffer in BGRA format. The pixel data is
- * returned in a buffer allocated with new uint8_t[]. The caller then has
- * ownership of the buffer and is responsible for delete[]'ing it.
+ * Convert aSurface to a packed buffer in BGRA format.
  */
-uint8_t*
+UniquePtr<uint8_t[]>
 SurfaceToPackedBGRA(DataSourceSurface *aSurface);
 
 /**
  * Convert aSurface to a packed buffer in BGR format. The pixel data is
  * returned in a buffer allocated with new uint8_t[]. The caller then has
  * ownership of the buffer and is responsible for delete[]'ing it.
  *
  * This function is currently only intended for use with surfaces of format
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -105,28 +105,28 @@ struct LayerPropertiesBase : public Laye
   explicit LayerPropertiesBase(Layer* aLayer)
     : mLayer(aLayer)
     , mMaskLayer(nullptr)
     , mVisibleRegion(aLayer->GetVisibleRegion())
     , mInvalidRegion(aLayer->GetInvalidRegion())
     , mPostXScale(aLayer->GetPostXScale())
     , mPostYScale(aLayer->GetPostYScale())
     , mOpacity(aLayer->GetLocalOpacity())
-    , mUseClipRect(!!aLayer->GetClipRect())
+    , mUseClipRect(!!aLayer->GetEffectiveClipRect())
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
     if (aLayer->GetMaskLayer()) {
       mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
     }
     for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
       Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
       mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
     }
     if (mUseClipRect) {
-      mClipRect = *aLayer->GetClipRect();
+      mClipRect = *aLayer->GetEffectiveClipRect();
     }
     mTransform = aLayer->GetLocalTransform();
   }
   LayerPropertiesBase()
     : mLayer(nullptr)
     , mMaskLayer(nullptr)
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
@@ -143,17 +143,17 @@ struct LayerPropertiesBase : public Laye
   virtual void MoveBy(const IntPoint& aOffset);
 
   nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
                             bool& aGeometryChanged)
   {
     bool transformChanged = !mTransform.FuzzyEqualsMultiplicative(mLayer->GetLocalTransform()) ||
                             mLayer->GetPostXScale() != mPostXScale ||
                             mLayer->GetPostYScale() != mPostYScale;
-    const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetClipRect();
+    const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetEffectiveClipRect();
     nsIntRegion result;
 
     bool ancestorMaskChanged = mAncestorMaskLayers.Length() != mLayer->GetAncestorMaskLayerCount();
     if (!ancestorMaskChanged) {
       for (size_t i = 0; i < mAncestorMaskLayers.Length(); i++) {
         if (mLayer->GetAncestorMaskLayerAt(i) != mAncestorMaskLayers[i]->mLayer) {
           ancestorMaskChanged = true;
           break;
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -683,16 +683,17 @@ APZCTreeManager::ReceiveInputEvent(Input
   if (aOutInputBlockId) {
     *aOutInputBlockId = InputBlockState::NO_BLOCK_ID;
   }
   nsEventStatus result = nsEventStatus_eIgnore;
   HitTestResult hitResult = HitNothing;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
+      touchInput.mHandledByAPZ = true;
       result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
     } case MOUSE_INPUT: {
       MouseInput& mouseInput = aEvent.AsMouseInput();
 
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
                                                             &hitResult);
       if (apzc) {
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -92,17 +92,18 @@ InputQueue::ReceiveTouchInput(const RefP
       // If the behaviours aren't set, but the main-thread response timer on
       // the block is expired we still treat it as though it has behaviors,
       // because in that case we still want to interrupt the fast-fling and
       // use the default behaviours.
       haveBehaviors |= CurrentTouchBlock()->IsContentResponseTimerExpired();
     }
 
     block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
-    INPQ_LOG("started new touch block %p for target %p\n", block, aTarget.get());
+    INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
+        block, block->GetBlockId(), aTarget.get());
 
     // XXX using the chain from |block| here may be wrong in cases where the
     // target isn't confirmed and the real target turns out to be something
     // else. For now assume this is rare enough that it's not an issue.
     if (block == CurrentBlock() &&
         aEvent.mTouches.Length() == 1 &&
         block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
         haveBehaviors) {
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -257,31 +257,37 @@ APZEventState::ProcessTouchEvent(const W
 {
   if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = TouchManager::gPreventMouseEvents ||
       aEvent.mFlags.mMultipleActionsPrevented;
   bool sentContentResponse = false;
+  APZES_LOG("Handling event type %d\n", aEvent.mMessage);
   switch (aEvent.mMessage) {
   case eTouchStart: {
     mTouchEndCancelled = false;
-    if (mPendingTouchPreventedResponse) {
-      // We can enter here if we get two TOUCH_STARTs in a row and didn't
-      // respond to the first one. Respond to it now.
-      mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
-          mPendingTouchPreventedBlockId, false);
-      sentContentResponse = true;
-      mPendingTouchPreventedResponse = false;
+    sentContentResponse = SendPendingTouchPreventedResponse(false);
+    // sentContentResponse can be true here if we get two TOUCH_STARTs in a row
+    // and just responded to the first one.
+    if (!aEvent.mFlags.mHandledByAPZ) {
+      // This condition being true means this touchstart is synthetic and is
+      // coming from TabParent.injectTouchEvent.
+      // Since APZ doesn't know about it we don't want to send a response for
+      // this block; we want to just skip over it from the point of view of
+      // prevent-default notifications.
+      break;
     }
     if (isTouchPrevented) {
       mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented);
       sentContentResponse = true;
     } else {
+      APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n",
+        aInputBlockId, Stringify(aGuid).c_str());
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
       mPendingTouchPreventedBlockId = aInputBlockId;
     }
     break;
   }
 
   case eTouchEnd:
@@ -405,16 +411,18 @@ APZEventState::ProcessAPZStateChange(con
     break;
   }
 }
 
 bool
 APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
 {
   if (mPendingTouchPreventedResponse) {
+    APZES_LOG("Sending response %d for pending guid: %s\n", aPreventDefault,
+      Stringify(mPendingTouchPreventedGuid).c_str());
     mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
         mPendingTouchPreventedBlockId, aPreventDefault);
     mPendingTouchPreventedResponse = false;
     return true;
   }
   return false;
 }
 
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -64,16 +64,17 @@ public:
   }
 
 public:
   RefPtr<gfx::DataSourceSurface> mSurface;
 };
 
 BasicCompositor::BasicCompositor(nsIWidget *aWidget)
   : mWidget(aWidget)
+  , mDidExternalComposition(false)
 {
   MOZ_COUNT_CTOR(BasicCompositor);
 
   mMaxTextureSize =
     Factory::GetMaxSurfaceSize(gfxPlatform::GetPlatform()->GetContentBackendFor(LayersBackend::LAYERS_BASIC));
 }
 
 BasicCompositor::~BasicCompositor()
@@ -504,19 +505,26 @@ BasicCompositor::BeginFrame(const nsIntR
                             const gfx::Rect& aRenderBounds,
                             gfx::Rect *aClipRectOut /* = nullptr */,
                             gfx::Rect *aRenderBoundsOut /* = nullptr */)
 {
   mWidgetSize = mWidget->GetClientSize();
   IntRect intRect = gfx::IntRect(IntPoint(), mWidgetSize);
   Rect rect = Rect(0, 0, intRect.width, intRect.height);
 
-  // Sometimes the invalid region is larger than we want to draw.
   nsIntRegion invalidRegionSafe;
-  invalidRegionSafe.And(aInvalidRegion, intRect);
+  if (mDidExternalComposition) {
+    // We do not know rendered region during external composition, just redraw
+    // whole widget.
+    invalidRegionSafe = intRect;
+    mDidExternalComposition = false;
+  } else {
+    // Sometimes the invalid region is larger than we want to draw.
+    invalidRegionSafe.And(aInvalidRegion, intRect);
+  }
 
   IntRect invalidRect = invalidRegionSafe.GetBounds();
   mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
   mInvalidRegion = invalidRegionSafe;
 
   if (aRenderBoundsOut) {
     *aRenderBoundsOut = Rect();
   }
@@ -605,10 +613,21 @@ BasicCompositor::EndFrame()
   if (!mTarget) {
     mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
   }
 
   mDrawTarget = nullptr;
   mRenderTarget = nullptr;
 }
 
+void
+BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
+{
+  MOZ_ASSERT(!mTarget);
+  MOZ_ASSERT(!mDrawTarget);
+  MOZ_ASSERT(!mRenderTarget);
+
+  mDidExternalComposition = true;
+}
+
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -85,22 +85,17 @@ public:
   virtual void ClearRect(const gfx::Rect& aRect) override;
 
   virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
                           const gfx::Rect *aClipRectIn,
                           const gfx::Rect& aRenderBounds,
                           gfx::Rect *aClipRectOut = nullptr,
                           gfx::Rect *aRenderBoundsOut = nullptr) override;
   virtual void EndFrame() override;
-  virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override
-  {
-      MOZ_ASSERT(!mTarget);
-      MOZ_ASSERT(!mDrawTarget);
-      MOZ_ASSERT(!mRenderTarget);
-  }
+  virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override;
 
   virtual bool SupportsPartialTextureUpdate() override { return true; }
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override { return true; }
   virtual int32_t GetMaxTextureSize() const override;
   virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
   
   virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override {
   }
@@ -129,16 +124,17 @@ private:
 
   // The final destination surface
   RefPtr<gfx::DrawTarget> mDrawTarget;
   // The current render target for drawing
   RefPtr<BasicCompositingRenderTarget> mRenderTarget;
 
   gfx::IntRect mInvalidRect;
   nsIntRegion mInvalidRegion;
+  bool mDidExternalComposition;
 
   uint32_t mMaxTextureSize;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -426,33 +426,31 @@ ShadowLayerForwarder::RemoveTextureFromC
   HoldUntilTransaction(aTexture);
 }
 
 void
 ShadowLayerForwarder::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                      CompositableClient* aCompositable,
                                                      TextureClient* aTexture)
 {
+#ifdef MOZ_WIDGET_GONK
+  mPendingAsyncMessages.push_back(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
+                                  aAsyncTransactionTracker->GetId(),
+                                  nullptr, aCompositable->GetIPDLActor(),
+                                  nullptr, aTexture->GetIPDLActor()));
+#else
   if (mTxn->Opened() && !aCompositable->IsDestroyed()) {
     mTxn->AddEdit(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
                                        aAsyncTransactionTracker->GetId(),
                                        nullptr, aCompositable->GetIPDLActor(),
                                        nullptr, aTexture->GetIPDLActor()));
   } else {
-    // If the function is called outside of transaction,
-    // OpRemoveTextureAsync message is stored as pending message.
-#ifdef MOZ_WIDGET_GONK
-    mPendingAsyncMessages.push_back(OpRemoveTextureAsync(CompositableClient::GetTrackersHolderId(aCompositable->GetIPDLActor()),
-                                    aAsyncTransactionTracker->GetId(),
-                                    nullptr, aCompositable->GetIPDLActor(),
-                                    nullptr, aTexture->GetIPDLActor()));
-#else
     NS_RUNTIMEABORT("not reached");
+  }
 #endif
-  }
   CompositableClient::HoldUntilComplete(aCompositable->GetIPDLActor(),
                                         aAsyncTransactionTracker);
 }
 
 bool
 ShadowLayerForwarder::InWorkerThread()
 {
   return MessageLoop::current() && (GetMessageLoop()->id() == MessageLoop::current()->id());
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -1542,76 +1542,73 @@ gfxUtils::CopyAsDataURI(DrawTarget* aDT)
   RefPtr<SourceSurface> surface = aDT->Snapshot();
   if (surface) {
     CopyAsDataURI(surface);
   } else {
     NS_WARNING("Failed to get surface!");
   }
 }
 
-/* static */ void
+/* static */ UniquePtr<uint8_t[]>
 gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
                          bool aIsAlphaPremultiplied,
-                         uint8_t** outImageBuffer,
                          int32_t* outFormat)
 {
-    *outImageBuffer = nullptr;
     *outFormat = 0;
 
     DataSourceSurface::MappedSurface map;
     if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
-        return;
+        return nullptr;
 
     uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
-    uint8_t* imageBuffer = new (fallible) uint8_t[bufferSize];
+    UniquePtr<uint8_t[]> imageBuffer(new (fallible) uint8_t[bufferSize]);
     if (!imageBuffer) {
         aSurface->Unmap();
-        return;
+        return nullptr;
     }
-    memcpy(imageBuffer, map.mData, bufferSize);
+    memcpy(imageBuffer.get(), map.mData, bufferSize);
 
     aSurface->Unmap();
 
     int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
     if (!aIsAlphaPremultiplied) {
         // We need to convert to INPUT_FORMAT_RGBA, otherwise
         // we are automatically considered premult, and unpremult'd.
         // Yes, it is THAT silly.
         // Except for different lossy conversions by color,
         // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(imageBuffer, bufferSize);
+        gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize);
         format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    *outImageBuffer = imageBuffer;
     *outFormat = format;
+    return imageBuffer;
 }
 
 /* static */ nsresult
 gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
                          bool aIsAlphaPremultiplied,
                          const char* aMimeType,
                          const char16_t* aEncoderOptions,
                          nsIInputStream** outStream)
 {
     nsCString enccid("@mozilla.org/image/encoder;2?type=");
     enccid += aMimeType;
     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
     if (!encoder)
         return NS_ERROR_FAILURE;
 
-    nsAutoArrayPtr<uint8_t> imageBuffer;
     int32_t format = 0;
-    GetImageBuffer(aSurface, aIsAlphaPremultiplied, getter_Transfers(imageBuffer), &format);
+    UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format);
     if (!imageBuffer)
         return NS_ERROR_FAILURE;
 
     return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
                                              aSurface->GetSize().height,
-                                             imageBuffer, format,
+                                             imageBuffer.get(), format,
                                              encoder, aEncoderOptions, outStream);
 }
 
 class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
 {
 public:
     GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
                              const nsCOMPtr<nsIGfxInfo>& gfxInfo,
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -5,16 +5,17 @@
 
 #ifndef GFX_UTILS_H
 #define GFX_UTILS_H
 
 #include "gfxTypes.h"
 #include "imgIContainer.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
 #include "nsColor.h"
 #include "nsPrintfCString.h"
 #include "mozilla/gfx/Rect.h"
 
 class gfxASurface;
 class gfxDrawable;
 class nsIInputStream;
 class nsIGfxInfo;
@@ -277,20 +278,19 @@ public:
     static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
     static inline void DumpAsDataURI(DrawTarget* aDT) {
         DumpAsDataURI(aDT, stdout);
     }
     static nsCString GetAsDataURI(SourceSurface* aSourceSurface);
     static nsCString GetAsDataURI(DrawTarget* aDT);
     static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
 
-    static void GetImageBuffer(DataSourceSurface* aSurface,
-                               bool aIsAlphaPremultiplied,
-                               uint8_t** outImageBuffer,
-                               int32_t* outFormat);
+    static mozilla::UniquePtr<uint8_t[]> GetImageBuffer(DataSourceSurface* aSurface,
+                                                        bool aIsAlphaPremultiplied,
+                                                        int32_t* outFormat);
 
     static nsresult GetInputStream(DataSourceSurface* aSurface,
                                    bool aIsAlphaPremultiplied,
                                    const char* aMimeType,
                                    const char16_t* aEncoderOptions,
                                    nsIInputStream** outStream);
 
     static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -214,17 +214,22 @@ elif [[ "$VARIANT" = "arm-sim" ||
 elif [[ "$VARIANT" = arm64* ]]; then
     # The ARM64 JIT is not yet fully functional, and asm.js does not work.
     # Just run "make check" and jsapi-tests.
     RUN_JITTEST=false
     RUN_JSTESTS=false
 fi
 
 $COMMAND_PREFIX $MAKE check || exit 1
+
+RESULT=0
+
 if $RUN_JITTEST; then
-    $COMMAND_PREFIX $MAKE check-jit-test || exit 1
+    $COMMAND_PREFIX $MAKE check-jit-test || RESULT=$?
 fi
 if $RUN_JSAPITESTS; then
-    $COMMAND_PREFIX $OBJDIR/dist/bin/jsapi-tests || exit 1
+    $COMMAND_PREFIX $OBJDIR/dist/bin/jsapi-tests || RESULT=$?
 fi
 if $RUN_JSTESTS; then
-    $COMMAND_PREFIX $MAKE check-jstests || exit 1
+    $COMMAND_PREFIX $MAKE check-jstests || RESULT=$?
 fi
+
+exit $RESULT
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3140,26 +3140,25 @@ Parser<ParseHandler>::maybeParseDirectiv
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc->setExplicitUseStrict();
             if (!pc->sc->strict()) {
                 if (pc->sc->isFunctionBox()) {
                     // Request that this function be reparsed as strict.
                     pc->newDirectives->setStrict();
                     return false;
-                } else {
-                    // We don't reparse global scopes, so we keep track of the
-                    // one possible strict violation that could occur in the
-                    // directive prologue -- octal escapes -- and complain now.
-                    if (tokenStream.sawOctalEscape()) {
-                        report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
-                        return false;
-                    }
-                    pc->sc->strictScript = true;
                 }
+                // We don't reparse global scopes, so we keep track of the one
+                // possible strict violation that could occur in the directive
+                // prologue -- octal escapes -- and complain now.
+                if (tokenStream.sawOctalEscape()) {
+                    report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
+                    return false;
+                }
+                pc->sc->strictScript = true;
             }
         } else if (directive == context->names().useAsm) {
             if (pc->sc->isFunctionBox())
                 return asmJS(list);
             return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js
@@ -0,0 +1,24 @@
+function O() {
+    this.x = 1;
+    this.y = 2;
+}
+function testUnboxed() {
+    var arr = [];
+    for (var i=0; i<100; i++)
+	arr.push(new O);
+
+    var o = arr[arr.length-1];
+    o[0] = 0;
+    o[2] = 2;
+    var sym = Symbol();
+    o[sym] = 1;
+    o.z = 3;
+    Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false});
+    o[4] = 4;
+
+    var props = Reflect.ownKeys(o);
+    assertEq(props[props.length-1], sym);
+
+    assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz");
+}
+testUnboxed();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1215678.js
@@ -0,0 +1,10 @@
+// |jit-test| error: ReferenceError
+if (!('oomTest' in this))
+    a;
+
+enableShellObjectMetadataCallback()
+oomTest(() => {
+  newGlobal()
+})
+gczeal(9, 1);
+a;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1216157.js
@@ -0,0 +1,12 @@
+if (!('oomAfterAllocations' in this))
+    quit();
+gcslice(0); // Start IGC, but don't mark anything.
+function f(str) {
+    for (var i = 0; i < 10; i++) {
+        arr = /foo(ba(r))?/.exec(str);
+        var x = arr[oomAfterAllocations(100)] + " " + arr[1] + " " + 1899;
+    }
+}
+try {
+    f("foo");
+} catch(e) {}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1218065.js
@@ -0,0 +1,25 @@
+function f() {
+    const b = 0;
+    switch (1) {
+      case b = 0:
+    }
+}
+var err;
+try {
+    f();
+} catch(e) {
+    err = e;
+}
+assertEq(err.name, "TypeError");
+
+function g() {
+    const z = 0;
+    while (z = 1) {}
+}
+err = null;
+try {
+    g();
+} catch(e) {
+    err = e;
+}
+assertEq(err.name, "TypeError");
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1436,16 +1436,18 @@ JitCompartment::generateRegExpExecStub(J
     // Use an undefined value to signal to the caller that the OOL stub needs to be called.
     masm.bind(&oolEntry);
     masm.moveValue(UndefinedValue(), result);
     masm.ret();
 
     Linker linker(masm);
     AutoFlushICache afc("RegExpExecStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
+    if (!code)
+        return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExpExecStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true);
 
@@ -1567,16 +1569,18 @@ JitCompartment::generateRegExpTestStub(J
 
     masm.bind(&done);
     masm.freeStack(sizeof(irregexp::InputOutputData));
     masm.ret();
 
     Linker linker(masm);
     AutoFlushICache afc("RegExpTestStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
+    if (!code)
+        return nullptr;
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "RegExpTestStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1581,20 +1581,16 @@ IonBuilder::snoopControlFlow(JSOp op)
 
       case JSOP_RETURN:
       case JSOP_RETRVAL:
         return processReturn(op);
 
       case JSOP_THROW:
         return processThrow();
 
-      case JSOP_THROWSETCONST:
-      case JSOP_THROWSETALIASEDCONST:
-        return processThrowSetConst();
-
       case JSOP_GOTO:
       {
         jssrcnote* sn = info().getNote(gsn, pc);
         switch (sn ? SN_TYPE(sn) : SRC_NULL) {
           case SRC_BREAK:
           case SRC_BREAK2LABEL:
             return processBreak(op, sn);
 
@@ -1773,16 +1769,20 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_GETLOCAL:
         current->pushLocal(GET_LOCALNO(pc));
         return true;
 
       case JSOP_SETLOCAL:
         current->setLocal(GET_LOCALNO(pc));
         return true;
 
+      case JSOP_THROWSETCONST:
+      case JSOP_THROWSETALIASEDCONST:
+        return jsop_throwsetconst();
+
       case JSOP_CHECKLEXICAL:
         return jsop_checklexical();
 
       case JSOP_INITLEXICAL:
         current->setLocal(GET_LOCALNO(pc));
         return true;
 
       case JSOP_INITGLEXICAL: {
@@ -12735,30 +12735,23 @@ IonBuilder::jsop_deffun(uint32_t index)
     MOZ_ASSERT(analysis().usesScopeChain());
 
     MDefFun* deffun = MDefFun::New(alloc(), fun, current->scopeChain());
     current->add(deffun);
 
     return resumeAfter(deffun);
 }
 
-IonBuilder::ControlStatus
-IonBuilder::processThrowSetConst()
+bool
+IonBuilder::jsop_throwsetconst()
 {
     current->peek(-1)->setImplicitlyUsedUnchecked();
     MInstruction* lexicalError = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN);
     current->add(lexicalError);
-    if (!resumeAfter(lexicalError))
-        return ControlStatus_Error;
-
-    current->end(MUnreachable::New(alloc()));
-
-    // Make sure no one tries to use this block now.
-    setCurrent(nullptr);
-    return processControlEnd();
+    return resumeAfter(lexicalError);
 }
 
 bool
 IonBuilder::jsop_checklexical()
 {
     uint32_t slot = info().localSlot(GET_LOCALNO(pc));
     MDefinition* lexical = addLexicalCheck(current->getSlot(slot));
     if (!lexical)
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -258,17 +258,16 @@ class IonBuilder
     ControlStatus processCondSwitchBody(CFGState& state);
     ControlStatus processSwitchBreak(JSOp op);
     ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
     ControlStatus processAndOrEnd(CFGState& state);
     ControlStatus processLabelEnd(CFGState& state);
     ControlStatus processTryEnd(CFGState& state);
     ControlStatus processReturn(JSOp op);
     ControlStatus processThrow();
-    ControlStatus processThrowSetConst();
     ControlStatus processContinue(JSOp op);
     ControlStatus processBreak(JSOp op, jssrcnote* sn);
     ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
     bool pushLoop(CFGState::State state, jsbytecode* stopAt, MBasicBlock* entry, bool osr,
                   jsbytecode* loopHead, jsbytecode* initialPc,
                   jsbytecode* bodyStart, jsbytecode* bodyEnd, jsbytecode* exitpc,
                   jsbytecode* continuepc = nullptr);
     bool analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecode* end);
@@ -654,16 +653,17 @@ class IonBuilder
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_tostring();
     bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deflexical(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
+    bool jsop_throwsetconst();
     bool jsop_checklexical();
     bool jsop_checkaliasedlet(ScopeCoordinate sc);
     bool jsop_funcall(uint32_t argc);
     bool jsop_funapply(uint32_t argc);
     bool jsop_funapplyarguments(uint32_t argc);
     bool jsop_call(uint32_t argc, bool constructing);
     bool jsop_eval(uint32_t argc);
     bool jsop_ifeq(JSOp op);
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -415,22 +415,24 @@ class MacroAssembler : public MacroAssem
     //
     // It is maintained by all stack manipulation functions below.
     uint32_t framePushed_;
 
   public:
     // ===============================================================
     // Stack manipulation functions.
 
-    void PushRegsInMask(LiveRegisterSet set) PER_SHARED_ARCH;
+    void PushRegsInMask(LiveRegisterSet set)
+                            DEFINED_ON(arm, arm64, mips32, x86_shared);
     void PushRegsInMask(LiveGeneralRegisterSet set);
 
     void PopRegsInMask(LiveRegisterSet set);
     void PopRegsInMask(LiveGeneralRegisterSet set);
-    void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore) PER_SHARED_ARCH;
+    void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
+                                 DEFINED_ON(arm, arm64, mips32, x86_shared);
 
     void Push(const Operand op) DEFINED_ON(x86_shared);
     void Push(Register reg) PER_SHARED_ARCH;
     void Push(const Imm32 imm) PER_SHARED_ARCH;
     void Push(const ImmWord imm) PER_SHARED_ARCH;
     void Push(const ImmPtr imm) PER_SHARED_ARCH;
     void Push(const ImmGCPtr ptr) PER_SHARED_ARCH;
     void Push(FloatRegister reg) PER_SHARED_ARCH;
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -789,73 +789,16 @@ MacroAssemblerMIPSShared::asMasm() const
     return *static_cast<const MacroAssembler*>(this);
 }
 
 //{{{ check_macroassembler_style
 // ===============================================================
 // Stack manipulation functions.
 
 void
-MacroAssembler::PushRegsInMask(LiveRegisterSet set)
-{
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-
-    reserveStack(diffG);
-    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-        diffG -= sizeof(intptr_t);
-        storePtr(*iter, Address(StackPointer, diffG));
-    }
-    MOZ_ASSERT(diffG == 0);
-
-    // Double values have to be aligned. We reserve extra space so that we can
-    // start writing from the first aligned location.
-    // We reserve a whole extra double so that the buffer has even size.
-    ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
-    reserveStack(diffF + sizeof(double));
-
-    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
-        if ((*iter).code() % 2 == 0)
-            as_sd(*iter, SecondScratchReg, -diffF);
-        diffF -= sizeof(double);
-    }
-    MOZ_ASSERT(diffF == 0);
-}
-
-void
-MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
-{
-    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
-    int32_t diffF = set.fpus().getPushSizeInBytes();
-    const int32_t reservedG = diffG;
-    const int32_t reservedF = diffF;
-
-    // Read the buffer form the first aligned location.
-    ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double)));
-    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
-
-    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
-        if (!ignore.has(*iter) && ((*iter).code() % 2 == 0))
-            // Use assembly l.d because we have alligned the stack.
-            as_ld(*iter, SecondScratchReg, -diffF);
-        diffF -= sizeof(double);
-    }
-    freeStack(reservedF + sizeof(double));
-    MOZ_ASSERT(diffF == 0);
-
-    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
-        diffG -= sizeof(intptr_t);
-        if (!ignore.has(*iter))
-            loadPtr(Address(StackPointer, diffG), *iter);
-    }
-    freeStack(reservedG);
-    MOZ_ASSERT(diffG == 0);
-}
-
-void
 MacroAssembler::Push(Register reg)
 {
     ma_push(reg);
     adjustFrame(sizeof(intptr_t));
 }
 
 void
 MacroAssembler::Push(const Imm32 imm)
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2493,16 +2493,73 @@ MacroAssemblerMIPSCompat::profilerExitFr
     branch(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
 }
 
 //{{{ check_macroassembler_style
 // ===============================================================
 // Stack manipulation functions.
 
 void
+MacroAssembler::PushRegsInMask(LiveRegisterSet set)
+{
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+
+    reserveStack(diffG);
+    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+        diffG -= sizeof(intptr_t);
+        storePtr(*iter, Address(StackPointer, diffG));
+    }
+    MOZ_ASSERT(diffG == 0);
+
+    // Double values have to be aligned. We reserve extra space so that we can
+    // start writing from the first aligned location.
+    // We reserve a whole extra double so that the buffer has even size.
+    ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
+    reserveStack(diffF + sizeof(double));
+
+    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
+        if ((*iter).code() % 2 == 0)
+            as_sd(*iter, SecondScratchReg, -diffF);
+        diffF -= sizeof(double);
+    }
+    MOZ_ASSERT(diffF == 0);
+}
+
+void
+MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
+{
+    int32_t diffG = set.gprs().size() * sizeof(intptr_t);
+    int32_t diffF = set.fpus().getPushSizeInBytes();
+    const int32_t reservedG = diffG;
+    const int32_t reservedF = diffF;
+
+    // Read the buffer form the first aligned location.
+    ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double)));
+    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
+
+    for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
+        if (!ignore.has(*iter) && ((*iter).code() % 2 == 0))
+            // Use assembly l.d because we have alligned the stack.
+            as_ld(*iter, SecondScratchReg, -diffF);
+        diffF -= sizeof(double);
+    }
+    freeStack(reservedF + sizeof(double));
+    MOZ_ASSERT(diffF == 0);
+
+    for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
+        diffG -= sizeof(intptr_t);
+        if (!ignore.has(*iter))
+            loadPtr(Address(StackPointer, diffG), *iter);
+    }
+    freeStack(reservedG);
+    MOZ_ASSERT(diffG == 0);
+}
+
+void
 MacroAssembler::reserveStack(uint32_t amount)
 {
     if (amount)
         subPtr(Imm32(amount), StackPointer);
     adjustFrame(amount);
 }
 
 // ===============================================================
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -549,20 +549,18 @@ JitRuntime::generateArgumentsRectifier(J
     // Discard pushed arguments.
     masm.addPtr(t2, StackPointer);
 
     masm.ret();
     Linker linker(masm);
     AutoFlushICache afc("ArgumentsRectifier");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
-    CodeOffsetLabel returnLabel(returnOffset);
-    returnLabel.fixup(&masm);
     if (returnAddrOut)
-        *returnAddrOut = (void*) (code->raw() + returnLabel.offset());
+        *returnAddrOut = (void*) (code->raw() + returnOffset);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
 #endif
 
     return code;
 }
 
@@ -1192,17 +1190,17 @@ JitRuntime::generateProfilerExitFrameTai
     //              CalleeToken     |- JitFrameLayout::Size()
     //              Descriptor      |
     //    FP -----> ReturnAddr      |
     //
     masm.bind(&handle_Rectifier);
     {
         // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size();
         masm.as_daddu(scratch2, StackPointer, scratch1);
-        masm.add32(Imm32(JitFrameLayout::Size()), scratch2);
+        masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
         masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3);
         masm.ma_dsrl(scratch1, scratch3, Imm32(FRAMESIZE_SHIFT));
         masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3);
 
         // Now |scratch1| contains Rect-Descriptor.Size
         // and |scratch2| points to Rectifier frame
         // and |scratch3| contains Rect-Descriptor.Type
 
@@ -1213,17 +1211,17 @@ JitRuntime::generateProfilerExitFrameTai
 
         // Handle Rectifier <- IonJS
         // scratch3 := RectFrame[ReturnAddr]
         masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3);
         masm.storePtr(scratch3, lastProfilingCallSite);
 
         // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size()
         masm.as_daddu(scratch3, scratch2, scratch1);
-        masm.add32(Imm32(RectifierFrameLayout::Size()), scratch3);
+        masm.addPtr(Imm32(RectifierFrameLayout::Size()), scratch3);
         masm.storePtr(scratch3, lastProfilingFrame);
         masm.ret();
 
         // Handle Rectifier <- BaselineStub <- BaselineJS
         masm.bind(&handle_Rectifier_BaselineStub);
 #ifdef DEBUG
         {
             Label checkOk;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -442,17 +442,16 @@ JSCompartment::wrap(JSContext* cx, Mutab
         if (!obj)
             return false;
     }
     MOZ_ASSERT(obj == GetOuterObject(cx, obj));
 
     if (obj->compartment() == this)
         return true;
 
-
     // If we already have a wrapper for this value, use it.
     RootedValue key(cx, ObjectValue(*obj));
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
         obj.set(&p->value().get().toObject());
         MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
         return true;
     }
 
@@ -463,25 +462,37 @@ JSCompartment::wrap(JSContext* cx, Mutab
             // Note: Class asserted above, so all that's left to check is callability
             existing->isCallable() ||
             obj->isCallable())
         {
             existing = nullptr;
         }
     }
 
-    obj.set(cb->wrap(cx, existing, obj));
-    if (!obj)
+    RootedObject wrapper(cx, cb->wrap(cx, existing, obj));
+    if (!wrapper)
         return false;
 
     // We maintain the invariant that the key in the cross-compartment wrapper
     // map is always directly wrapped by the value.
-    MOZ_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject());
+    MOZ_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject());
 
-    return putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*obj));
+    if (!putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*wrapper))) {
+        // Enforce the invariant that all cross-compartment wrapper object are
+        // in the map by nuking the wrapper if we couldn't add it.
+        // Unfortunately it's possible for the wrapper to still be marked if we
+        // took this path, for example if the object metadata callback stashes a
+        // reference to it.
+        if (wrapper->is<CrossCompartmentWrapperObject>())
+            NukeCrossCompartmentWrapper(cx, wrapper);
+        return false;
+    }
+
+    obj.set(wrapper);
+    return true;
 }
 
 bool
 JSCompartment::wrap(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
 {
     if (!wrap(cx, desc.object()))
         return false;
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -131,28 +131,55 @@ Enumerate(JSContext* cx, HandleObject po
         return true;
     if (!enumerable && !(flags & JSITER_HIDDEN))
         return true;
 
     return props->append(id);
 }
 
 static bool
+EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
+                         AutoIdVector* props)
+{
+    MOZ_ASSERT(obj->getOps()->enumerate);
+
+    AutoIdVector properties(cx);
+    bool enumerableOnly = !(flags & JSITER_HIDDEN);
+    if (!obj->getOps()->enumerate(cx, obj, properties, enumerableOnly))
+        return false;
+
+    RootedId id(cx);
+    for (size_t n = 0; n < properties.length(); n++) {
+        id = properties[n];
+
+        // The enumerate hook does not indicate whether the properties
+        // it returns are enumerable or not. Since we already passed
+        // `enumerableOnly` to the hook to filter out non-enumerable
+        // properties, it doesn't really matter what we pass here.
+        bool enumerable = true;
+        if (!Enumerate(cx, obj, id, enumerable, flags, ht, props))
+            return false;
+    }
+
+    return true;
+}
+
+static bool
 SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp)
 {
     uint32_t indexA, indexB;
     MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
     MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
     *lessOrEqualp = (indexA <= indexB);
     return true;
 }
 
 static bool
 EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
-                          AutoIdVector* props)
+                          AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr)
 {
     bool enumerateSymbols;
     if (flags & JSITER_SYMBOLSONLY) {
         enumerateSymbols = true;
     } else {
         /* Collect any dense elements from this object. */
         size_t initlen = pobj->getDenseInitializedLength();
         const Value* vp = pobj->getDenseElements();
@@ -202,16 +229,26 @@ EnumerateNativeProperties(JSContext* cx,
             if (!tmp.resize(n))
                 return false;
             PodCopy(tmp.begin(), ids, n);
 
             if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds))
                 return false;
         }
 
+        if (unboxed) {
+            // If |unboxed| is set then |pobj| is the expando for an unboxed
+            // plain object we are enumerating. Add the unboxed properties
+            // themselves here since they are all property names that were
+            // given to the object before any of the expando's properties.
+            MOZ_ASSERT(pobj->is<UnboxedExpandoObject>());
+            if (!EnumerateExtraProperties(cx, unboxed, flags, ht, props))
+                return false;
+        }
+
         size_t initialLength = props->length();
 
         /* Collect all unique property names from this object's shape. */
         bool symbolsFound = false;
         Shape::Range<NoGC> r(pobj->lastProperty());
         for (; !r.empty(); r.popFront()) {
             Shape& shape = r.front();
             jsid id = shape.propid();
@@ -326,38 +363,33 @@ static bool
 Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
 {
     // We initialize |ht| lazily (in Enumerate()) because it ends up unused
     // anywhere from 67--99.9% of the time.
     Maybe<IdSet> ht;
     RootedObject pobj(cx, pobj_);
 
     do {
-        if (JSNewEnumerateOp enumerate = pobj->getOps()->enumerate) {
-            AutoIdVector properties(cx);
-            bool enumerableOnly = !(flags & JSITER_HIDDEN);
-            if (!enumerate(cx, pobj, properties, enumerableOnly))
-                 return false;
-
-            RootedId id(cx);
-            for (size_t n = 0; n < properties.length(); n++) {
-                id = properties[n];
+        if (pobj->getOps()->enumerate) {
+            if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
+                // Special case unboxed objects with an expando object.
+                RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
+                if (!EnumerateNativeProperties(cx, expando, flags, ht, props,
+                                               pobj.as<UnboxedPlainObject>()))
+                {
+                    return false;
+                }
+            } else {
+                if (!EnumerateExtraProperties(cx, pobj, flags, ht, props))
+                    return false;
 
-                // The enumerate hook does not indicate whether the properties
-                // it returns are enumerable or not. Since we already passed
-                // `enumerableOnly` to the hook to filter out non-enumerable
-                // properties, it doesn't really matter what we pass here.
-                bool enumerable = true;
-                if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props))
-                    return false;
-            }
-
-            if (pobj->isNative()) {
-                if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
-                    return false;
+                if (pobj->isNative()) {
+                    if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
+                        return false;
+                }
             }
         } else if (pobj->isNative()) {
             // Give the object a chance to resolve all lazy properties
             if (JSEnumerateOp enumerate = pobj->getClass()->enumerate) {
                 if (!enumerate(cx, pobj.as<NativeObject>()))
                     return false;
             }
             if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props))
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -417,18 +417,16 @@ BytecodeFallsThrough(JSOp op)
 {
     switch (op) {
       case JSOP_GOTO:
       case JSOP_DEFAULT:
       case JSOP_RETURN:
       case JSOP_RETRVAL:
       case JSOP_FINALYIELDRVAL:
       case JSOP_THROW:
-      case JSOP_THROWSETCONST:
-      case JSOP_THROWSETALIASEDCONST:
       case JSOP_TABLESWITCH:
         return false;
       case JSOP_GOSUB:
         /* These fall through indirectly, after executing a 'finally'. */
         return true;
       default:
         return true;
     }
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -632,17 +632,17 @@ js::proxy_Trace(JSTracer* trc, JSObject*
 ProxyObject::trace(JSTracer* trc, JSObject* obj)
 {
     ProxyObject* proxy = &obj->as<ProxyObject>();
 
     TraceEdge(trc, &proxy->shape, "ProxyObject_shape");
 
 #ifdef DEBUG
     if (trc->runtime()->gc.isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
-        JSObject* referent = MaybeForwarded(&proxy->private_().toObject());
+        JSObject* referent = MaybeForwarded(proxy->target());
         if (referent->compartment() != proxy->compartment()) {
             /*
              * Assert that this proxy is tracked in the wrapper map. We maintain
              * the invariant that the wrapped object is the key in the wrapper map.
              */
             Value key = ObjectValue(*referent);
             WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
             MOZ_ASSERT(p);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -8017,17 +8017,17 @@ DebuggerEnv_getCallee(JSContext* cx, uns
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get callee", args, envobj, env, dbg);
 
     args.rval().setNull();
 
     if (!env->is<DebugScopeObject>())
         return true;
 
     JSObject& scope = env->as<DebugScopeObject>().scope();
-    if (!scope.is<CallObject>() || scope.is<ModuleEnvironmentObject>())
+    if (!scope.is<CallObject>())
         return true;
 
     CallObject& callobj = scope.as<CallObject>();
     if (callobj.isForEval())
         return true;
 
     JSFunction& callee = callobj.callee();
     if (IsInternalFunctionObject(callee))
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -22,17 +22,17 @@ NearestEnclosingExtensibleLexicalScope(J
     while (!IsExtensibleLexicalScope(scope))
         scope = scope->enclosingScope();
     return scope->as<ClonedBlockObject>();
 }
 
 inline void
 ScopeObject::setAliasedVar(JSContext* cx, ScopeCoordinate sc, PropertyName* name, const Value& v)
 {
-    MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
+    MOZ_ASSERT(is<LexicalScopeBase>() || is<ClonedBlockObject>());
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
 
     // name may be null if we don't need to track side effects on the object.
     MOZ_ASSERT_IF(isSingleton(), name);
 
     if (isSingleton()) {
         MOZ_ASSERT(name);
         AddTypePropertyId(cx, this, NameToId(name), v);
@@ -43,42 +43,44 @@ ScopeObject::setAliasedVar(JSContext* cx
             shape->setOverwritten();
         }
     }
 
     setSlot(sc.slot(), v);
 }
 
 inline void
-CallObject::setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name, const Value& v)
+LexicalScopeBase::setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
+                                const Value& v)
 {
     MOZ_ASSERT(name == fi->name());
     setSlot(fi.scopeSlot(), v);
     if (isSingleton())
         AddTypePropertyId(cx, this, NameToId(name), v);
 }
 
 inline void
-CallObject::setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id, const Value& v)
+LexicalScopeBase::setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
+                                             const Value& v)
 {
     setSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue), v);
     if (isSingleton())
         AddTypePropertyId(cx, this, id, v);
 }
 
 inline void
-CallObject::initRemainingSlotsToUninitializedLexicals(uint32_t begin)
+LexicalScopeBase::initRemainingSlotsToUninitializedLexicals(uint32_t begin)
 {
     uint32_t end = slotSpan();
     for (uint32_t slot = begin; slot < end; slot++)
         initSlot(slot, MagicValue(JS_UNINITIALIZED_LEXICAL));
 }
 
 inline void
-CallObject::initAliasedLexicalsToThrowOnTouch(JSScript* script)
+LexicalScopeBase::initAliasedLexicalsToThrowOnTouch(JSScript* script)
 {
     initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin());
 }
 
 template <AllowGC allowGC>
 inline void
 StaticScopeIter<allowGC>::operator++(int)
 {
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -120,17 +120,17 @@ js::ScopeCoordinateFunctionScript(JSScri
     return ssi.funScript();
 }
 
 /*****************************************************************************/
 
 void
 ScopeObject::setEnclosingScope(HandleObject obj)
 {
-    MOZ_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
+    MOZ_ASSERT_IF(obj->is<LexicalScopeBase>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
                   obj->isDelegate());
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
 CallObject*
 CallObject::create(JSContext* cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin)
 {
     MOZ_ASSERT(!group->singleton(),
@@ -1750,19 +1750,17 @@ class DebugScopeProxy : public BaseProxy
 
     static bool isArguments(JSContext* cx, jsid id)
     {
         return id == NameToId(cx->names().arguments);
     }
 
     static bool isFunctionScope(const JSObject& scope)
     {
-        return scope.is<CallObject>() &&
-               !scope.is<ModuleEnvironmentObject>() &&
-               !scope.as<CallObject>().isForEval();
+        return scope.is<CallObject>() && !scope.as<CallObject>().isForEval();
     }
 
     /*
      * In theory, every function scope contains an 'arguments' bindings.
      * However, the engine only adds a binding if 'arguments' is used in the
      * function body. Thus, from the debugger's perspective, 'arguments' may be
      * missing from the list of bindings.
      */
@@ -2164,17 +2162,17 @@ DebugScopeObject::initSnapshot(ArrayObje
     MOZ_ASSERT(maybeSnapshot() == nullptr);
     setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
 }
 
 bool
 DebugScopeObject::isForDeclarative() const
 {
     ScopeObject& s = scope();
-    return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
+    return s.is<LexicalScopeBase>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
 }
 
 bool
 DebugScopeObject::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp)
 {
     Rooted<DebugScopeObject*> self(cx, this);
     return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp);
 }
@@ -2185,17 +2183,17 @@ DebugScopeObject::isOptimizedOut() const
     ScopeObject& s = scope();
 
     if (DebugScopes::hasLiveScope(s))
         return false;
 
     if (s.is<ClonedBlockObject>())
         return !s.as<ClonedBlockObject>().staticBlock().needsClone();
 
-    if (s.is<CallObject>() && !s.is<ModuleEnvironmentObject>()) {
+    if (s.is<CallObject>()) {
         return !s.as<CallObject>().isForEval() &&
                !s.as<CallObject>().callee().needsCallObject() &&
                !maybeSnapshot();
     }
 
     return false;
 }
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -216,17 +216,19 @@ ScopeCoordinateFunctionScript(JSScript* 
  *   ScopeObject---+---+           Engine-internal scope
  *     |   |   |   |   |
  *     |   |   |   |  StaticNonSyntacticScopeObjects  See "Non-syntactic scope objects"
  *     |   |   |   |
  *     |   |   |  StaticEvalObject  Placeholder so eval scopes may be iterated through
  *     |   |   |
  *     |   |  DeclEnvObject         Holds name of recursive/needsCallObject named lambda
  *     |   |
- *     |  CallObject                Scope of entire function or strict eval
+ *     |  LexicalScopeBase          Shared base for function and modules scopes
+ *     |   |   |
+ *     |   |  CallObject            Scope of entire function or strict eval
  *     |   |
  *     |  ModuleEnvironmentObject   Module top-level scope on run-time scope chain
  *     |
  *   NestedScopeObject              Statement scopes; don't cross script boundaries
  *     |   |   |
  *     |   |  StaticWithObject      Template for "with" object in static scope chain
  *     |   |
  *     |  DynamicWithObject         Run-time "with" object on scope chain
@@ -282,27 +284,53 @@ class ScopeObject : public NativeObject
         return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
     }
 
     static size_t enclosingScopeSlot() {
         return SCOPE_CHAIN_SLOT;
     }
 };
 
-class CallObject : public ScopeObject
+class LexicalScopeBase : public ScopeObject
+{
+  protected:
+    inline void initRemainingSlotsToUninitializedLexicals(uint32_t begin);
+    inline void initAliasedLexicalsToThrowOnTouch(JSScript* script);
+
+  public:
+    /* Get/set the aliased variable referred to by 'fi'. */
+    const Value& aliasedVar(AliasedFormalIter fi) {
+        return getSlot(fi.scopeSlot());
+    }
+    inline void setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
+                              const Value& v);
+
+    /*
+     * When an aliased var (var accessed by nested closures) is also aliased by
+     * the arguments object, it must of course exist in one canonical location
+     * and that location is always the CallObject. For this to work, the
+     * ArgumentsObject stores special MagicValue in its array for forwarded-to-
+     * CallObject variables. This MagicValue's payload is the slot of the
+     * CallObject to access.
+     */
+    const Value& aliasedVarFromArguments(const Value& argsValue) {
+        return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
+    }
+    inline void setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
+                                           const Value& v);
+};
+
+class CallObject : public LexicalScopeBase
 {
   protected:
     static const uint32_t CALLEE_SLOT = 1;
 
     static CallObject*
     create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
 
-    inline void initRemainingSlotsToUninitializedLexicals(uint32_t begin);
-    inline void initAliasedLexicalsToThrowOnTouch(JSScript* script);
-
   public:
     static const Class class_;
 
     /* These functions are internal and are exposed only for JITs. */
 
     /*
      * Construct a bare-bones call object given a shape and a non-singleton
      * group.  The call object must be further initialized to be usable.
@@ -342,54 +370,35 @@ class CallObject : public ScopeObject
      * Returns the function for which this CallObject was created. (This may
      * only be called if !isForEval.)
      */
     JSFunction& callee() const {
         MOZ_ASSERT(!is<ModuleEnvironmentObject>());
         return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
     }
 
-    /* Get/set the aliased variable referred to by 'bi'. */
-    const Value& aliasedVar(AliasedFormalIter fi) {
-        return getSlot(fi.scopeSlot());
-    }
-    inline void setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
-                              const Value& v);
-
-    /*
-     * When an aliased var (var accessed by nested closures) is also aliased by
-     * the arguments object, it must of course exist in one canonical location
-     * and that location is always the CallObject. For this to work, the
-     * ArgumentsObject stores special MagicValue in its array for forwarded-to-
-     * CallObject variables. This MagicValue's payload is the slot of the
-     * CallObject to access.
-     */
-    const Value& aliasedVarFromArguments(const Value& argsValue) {
-        return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
-    }
-    inline void setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
-                                           const Value& v);
-
     /* For jit access. */
     static size_t offsetOfCallee() {
         return getFixedSlotOffset(CALLEE_SLOT);
     }
 
     static size_t calleeSlot() {
         return CALLEE_SLOT;
     }
 };
 
-class ModuleEnvironmentObject : public CallObject
+class ModuleEnvironmentObject : public LexicalScopeBase
 {
-    static const uint32_t MODULE_SLOT = CallObject::CALLEE_SLOT;
+    static const uint32_t MODULE_SLOT = 1;
 
   public:
     static const Class class_;
 
+    static const uint32_t RESERVED_SLOTS = 2;
+
     static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
     ModuleObject& module();
     IndirectBindingMap& importBindings();
 
     bool createImportBinding(JSContext* cx, HandleAtom importName, HandleModuleObject module,
                              HandleAtom exportName);
 
   private:
@@ -1257,27 +1266,27 @@ JSObject::is<js::NestedScopeObject>() co
 {
     return is<js::BlockObject>() ||
            is<js::StaticWithObject>() ||
            is<js::DynamicWithObject>();
 }
 
 template<>
 inline bool
-JSObject::is<js::CallObject>() const
+JSObject::is<js::LexicalScopeBase>() const
 {
-    return getClass() == &js::CallObject::class_ ||
+    return is<js::CallObject>() ||
            is<js::ModuleEnvironmentObject>();
 }
 
 template<>
 inline bool
 JSObject::is<js::ScopeObject>() const
 {
-    return is<js::CallObject>() ||
+    return is<js::LexicalScopeBase>() ||
            is<js::DeclEnvObject>() ||
            is<js::NestedScopeObject>() ||
            is<js::RuntimeLexicalErrorObject>() ||
            is<js::NonSyntacticVariablesObject>();
 }
 
 template<>
 inline bool
@@ -1338,17 +1347,17 @@ inline bool
 IsStaticGlobalLexicalScope(JSObject* scope)
 {
     return scope->is<StaticBlockObject>() && scope->as<StaticBlockObject>().isGlobal();
 }
 
 inline const Value&
 ScopeObject::aliasedVar(ScopeCoordinate sc)
 {
-    MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
+    MOZ_ASSERT(is<LexicalScopeBase>() || is<ClonedBlockObject>());
     return getSlot(sc.slot());
 }
 
 inline NestedScopeObject*
 NestedScopeObject::enclosingNestedScope() const
 {
     JSObject* obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
     return obj && obj->is<NestedScopeObject>() ? &obj->as<NestedScopeObject>() : nullptr;
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -30,20 +30,17 @@ namespace js {
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
 static inline bool
 IsCacheableNonGlobalScope(JSObject* obj)
 {
-    bool cacheable =
-        (obj->is<CallObject>() && !obj->is<ModuleEnvironmentObject>()) ||
-        obj->is<BlockObject>() ||
-        obj->is<DeclEnvObject>();
+    bool cacheable = obj->is<CallObject>() || obj->is<BlockObject>() || obj->is<DeclEnvObject>();
 
     MOZ_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
     return cacheable;
 }
 
 inline HandleObject
 InterpreterFrame::scopeChain() const
 {
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -888,47 +888,25 @@ UnboxedPlainObject::obj_watch(JSContext*
         return false;
     return WatchProperty(cx, obj, id, callable);
 }
 
 /* static */ bool
 UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                                   bool enumerableOnly)
 {
-    UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
-
-    // Add dense elements in the expando first, for consistency with plain objects.
-    if (expando) {
-        for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
-            if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
-                if (!properties.append(INT_TO_JSID(i)))
-                    return false;
-            }
-        }
-    }
+    // Ignore expando properties here, they are special-cased by the property
+    // enumeration code.
 
     const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
     for (size_t i = 0; i < unboxed.length(); i++) {
         if (!properties.append(NameToId(unboxed[i].name)))
             return false;
     }
 
-    if (expando) {
-        Vector<jsid> ids(cx);
-        for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
-            if (enumerableOnly && !r.front().enumerable())
-                continue;
-            if (!ids.append(r.front().propid()))
-                return false;
-        }
-        ::Reverse(ids.begin(), ids.end());
-        if (!properties.append(ids.begin(), ids.length()))
-            return false;
-    }
-
     return true;
 }
 
 const Class UnboxedExpandoObject::class_ = {
     "UnboxedExpandoObject",
     0
 };
 
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -58,8 +58,16 @@ skip-if((B2G&&browserIsRemote)||Mulet) =
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-001.xul object-fit-scale-down-svg-001-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-002.xul object-fit-scale-down-svg-002-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-003.xul object-fit-scale-down-svg-003-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-004.xul object-fit-scale-down-svg-004-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-005.xul object-fit-scale-down-svg-005-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-006.xul object-fit-scale-down-svg-006-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-position-png-001.xul object-position-png-001-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == object-position-png-002.xul object-position-png-002-ref.html
+
+# Tests for rendering SVG images in a XUL <treecell>:
+# XXXdholbert: These are marked as "random" right now, since they might not
+# render the images they trying to test in time for the reftest snapshot, per
+# bug 1218954. Once that bug is fixed, we should replace the "random"
+# annotation with "skip-if((B2G&&browserIsRemote)||Mulet)", like above tests.
+skip == treecell-image-svg-1a.xul treecell-image-svg-1-ref.xul # bug 1218954
+skip == treecell-image-svg-1b.xul treecell-image-svg-1-ref.xul # bug 1218954
new file mode 100644
--- /dev/null
+++ b/layout/reftests/xul/treecell-image-svg-1-ref.xul
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <tree seltype="single" flex="1">
+    <treecols>
+      <treecol flex="1"/>
+    </treecols>
+    <treechildren>
+      <treeitem>
+        <treerow>
+          <treecell src="colors-16x8.png"/>
+        </treerow>
+      </treeitem>
+    </treechildren>
+  </tree>
+</window>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/xul/treecell-image-svg-1a.xul
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <tree seltype="single" flex="1">
+    <treecols>
+      <treecol flex="1"/>
+    </treecols>
+    <treechildren>
+      <treeitem>
+        <treerow>
+          <treecell src="colors-16x8.svg"/>
+        </treerow>
+      </treeitem>
+    </treechildren>
+  </tree>
+</window>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/xul/treecell-image-svg-1b.xul
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <tree seltype="single" flex="1">
+    <treecols>
+      <treecol flex="1"/>
+    </treecols>
+    <treechildren>
+      <treeitem>
+        <treerow>
+          <treecell src="colors-16x8-noSize.svg"/>
+        </treerow>
+      </treeitem>
+    </treechildren>
+  </tree>
+</window>
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -3602,36 +3602,45 @@ nsTreeBodyFrame::PaintImage(int32_t     
       // Adjust the destRect y accordingly.
       destRect.y += (imageRect.height - destRect.height)/2;
     }
 
     // It's almost time to paint the image.
     // Deflate destRect for the border and padding.
     destRect.Deflate(bp);
 
-    // Get the image source rectangle - the rectangle containing the part of
-    // the image that we are going to display.
-    // sourceRect will be passed as the aSrcRect argument in the DrawImage method.
-    nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
-
-    // Let's say that the image is 100 pixels tall and
-    // that the CSS has specified that the destination height should be 50
-    // pixels tall. Let's say that the cell height is only 20 pixels. So, in
-    // those 20 visible pixels, we want to see the top 20/50ths of the image.
-    // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
-    // Essentially, we are scaling the image as dictated by the CSS destination
-    // height and width, and we are then clipping the scaled image by the cell
-    // width and height.
+    // Compute the area where our whole image would be mapped, to get the
+    // desired subregion onto our actual destRect:
+    nsRect wholeImageDest;
     CSSIntSize rawImageCSSIntSize;
-    image->GetWidth(&rawImageCSSIntSize.width);
-    image->GetHeight(&rawImageCSSIntSize.height);
-    nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize));
-    nsRect wholeImageDest =
-      nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
-          nsRect(destRect.TopLeft(), imageDestSize));
+    if (NS_SUCCEEDED(image->GetWidth(&rawImageCSSIntSize.width)) &&
+        NS_SUCCEEDED(image->GetHeight(&rawImageCSSIntSize.height))) {
+      // Get the image source rectangle - the rectangle containing the part of
+      // the image that we are going to display.  sourceRect will be passed as
+      // the aSrcRect argument in the DrawImage method.
+      nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
+
+      // Let's say that the image is 100 pixels tall and that the CSS has
+      // specified that the destination height should be 50 pixels tall. Let's
+      // say that the cell height is only 20 pixels. So, in those 20 visible
+      // pixels, we want to see the top 20/50ths of the image.  So, the
+      // sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
+      // Essentially, we are scaling the image as dictated by the CSS
+      // destination height and width, and we are then clipping the scaled
+      // image by the cell width and height.
+      nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize));
+      wholeImageDest =
+        nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
+                                                nsRect(destRect.TopLeft(),
+                                                       imageDestSize));
+    } else {
+      // GetWidth/GetHeight failed, so we can't easily map a subregion of the
+      // source image onto the destination area. So, just draw the whole image.
+      wholeImageDest = destRect;
+    }
 
     gfxContext* ctx = aRenderingContext.ThebesContext();
     if (opacity != 1.0f) {
       ctx->PushGroup(gfxContentType::COLOR_ALPHA);
     }
 
     result &=
       nsLayoutUtils::DrawImage(*ctx, aPresContext, image,
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5097,17 +5097,17 @@ var BrowserEventHandler = {
     while (elem) {
       /* Element is scrollable if its scroll-size exceeds its client size, and:
        * - It has overflow other than 'hidden', or
        * - It's a textarea node, or
        * - It's a text input, or
        * - It's a select element showing multiple rows
        */
       if (checkElem) {
-        if ((elem.scrollTopMin != elem.scrollTopMin ||
+        if ((elem.scrollTopMin != elem.scrollTopMax ||
              elem.scrollLeftMin != elem.scrollLeftMax) &&
             (this._hasScrollableOverflow(elem) ||
              elem.matches("textarea")) ||
             (elem instanceof HTMLInputElement && elem.mozIsTextField(false)) ||
             (elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
           scrollable = true;
           break;
         }
--- a/netwerk/base/CaptivePortalService.cpp
+++ b/netwerk/base/CaptivePortalService.cpp
@@ -64,17 +64,17 @@ CaptivePortalService::PerformCheck()
         LOG(("Unable to get a captive portal detector\n"));
         return rv;
     }
   }
 
   LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
   mRequestInProgress = true;
   mCaptivePortalDetector->CheckCaptivePortal(
-    NS_LITERAL_STRING(kInterfaceName).get(), this);
+    MOZ_UTF16(kInterfaceName), this);
   return NS_OK;
 }
 
 nsresult
 CaptivePortalService::RearmTimer()
 {
   // Start a timer to recheck
   if (mTimer) {
@@ -159,17 +159,17 @@ CaptivePortalService::Stop()
 
   if (mTimer) {
     mTimer->Cancel();
   }
   mTimer = nullptr;
   mRequestInProgress = false;
   mStarted = false;
   if (mCaptivePortalDetector) {
-    mCaptivePortalDetector->Abort(NS_LITERAL_STRING(kInterfaceName).get());
+    mCaptivePortalDetector->Abort(MOZ_UTF16(kInterfaceName));
   }
   mCaptivePortalDetector = nullptr;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // CaptivePortalService::nsICaptivePortalService
 //-----------------------------------------------------------------------------
@@ -276,17 +276,17 @@ CaptivePortalService::Observe(nsISupport
 // CaptivePortalService::nsICaptivePortalCallback
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 CaptivePortalService::Prepare()
 {
   LOG(("CaptivePortalService::Prepare\n"));
   // XXX: Finish preparation shouldn't be called until dns and routing is available.
   if (mCaptivePortalDetector) {
-    mCaptivePortalDetector->FinishPreparation(NS_LITERAL_STRING(kInterfaceName).get());
+    mCaptivePortalDetector->FinishPreparation(MOZ_UTF16(kInterfaceName));
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CaptivePortalService::Complete(bool success)
 {
   LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success, mState));
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -1045,17 +1045,17 @@ nsIOService::SetOffline(bool offline)
                 mProxyService->ReloadPAC();
 
             mLastOfflineStateChange = PR_IntervalNow();
             // don't care if notification fails
             // Only send the ONLINE notification if there is connectivity
             if (observerService && mConnectivity) {
                 observerService->NotifyObservers(subject,
                                                  NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
-                                                 NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
+                                                 MOZ_UTF16(NS_IOSERVICE_ONLINE));
             }
         }
     }
 
     // Don't notify here, as the above notifications (if used) suffice.
     if ((mShutdown || mOfflineForProfileChange) && mOffline) {
         // be sure to try and shutdown both (even if the first fails)...
         // shutdown dns service first, because it has callbacks for socket transport
@@ -1128,17 +1128,17 @@ nsIOService::SetConnectivityInternal(boo
     }
 
     if (aConnectivity) {
         // If we were previously offline due to connectivity=false,
         // send the ONLINE notification
         observerService->NotifyObservers(
             static_cast<nsIIOService *>(this),
             NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
-            NS_LITERAL_STRING(NS_IOSERVICE_ONLINE).get());
+            MOZ_UTF16(NS_IOSERVICE_ONLINE));
     } else {
         // If we were previously online and lost connectivity
         // send the OFFLINE notification
         const nsLiteralString offlineString(MOZ_UTF16(NS_IOSERVICE_OFFLINE));
         observerService->NotifyObservers(static_cast<nsIIOService *>(this),
                                          NS_IOSERVICE_GOING_OFFLINE_TOPIC,
                                          offlineString.get());
         observerService->NotifyObservers(static_cast<nsIIOService *>(this),
--- a/netwerk/dns/moz.build
+++ b/netwerk/dns/moz.build
@@ -35,17 +35,16 @@ SOURCES += [
 
 UNIFIED_SOURCES += [
     'ChildDNSService.cpp',
     'DNS.cpp',
     'DNSListenerProxy.cpp',
     'DNSRequestChild.cpp',
     'DNSRequestParent.cpp',
     'GetAddrInfo.cpp',
-    'nameprep.c',
     'nsDNSService2.cpp',
     'nsIDNService.cpp',
     'punycode.c',
 ]
 
 IPDL_SOURCES = [
     'PDNSRequest.ipdl',
     'PDNSRequestParams.ipdlh',
@@ -64,8 +63,18 @@ etld_data.inputs = ['effective_tld_names
 
 # need to include etld_data.inc
 LOCAL_INCLUDES += [
     '/netwerk/base',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] > '19':
     CXXFLAGS += ['-I%s/bionic/libc/dns/include' % CONFIG['ANDROID_SOURCE']]
+
+if CONFIG['ENABLE_INTL_API']:
+    DEFINES['IDNA2008'] = True
+    CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    USE_LIBS += ['icu']
+else:
+    UNIFIED_SOURCES += [
+        'nameprep.c',
+    ]
--- a/netwerk/dns/nsIDNService.cpp
+++ b/netwerk/dns/nsIDNService.cpp
@@ -12,16 +12,26 @@
 #include "harfbuzz/hb.h"
 #include "nsIServiceManager.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "punycode.h"
 
+#ifdef IDNA2008
+// Currently we use the transitional processing option -- see
+// http://unicode.org/reports/tr46/
+// To switch to non-transitional processing, change the value of this flag
+// and kTransitionalProcessing in netwerk/test/unit/test_idna2008.js to false
+// (patch in bug 1218179).
+const bool kIDNA2008_TransitionalProcessing = true;
+
+#include "ICUUtils.h"
+#endif
 
 using namespace mozilla::unicode;
 
 //-----------------------------------------------------------------------------
 // RFC 1034 - 3.1. Name space specifications and terminology
 static const uint32_t kMaxDNSNodeLen = 63;
 // RFC 3490 - 5.   ACE prefix
 static const char kACEPrefix[] = "xn--";
@@ -118,28 +128,101 @@ void nsIDNService::prefsChanged(nsIPrefB
     } else {
       mRestrictionProfile = eASCIIOnlyProfile;
     }
   }
 }
 
 nsIDNService::nsIDNService()
 {
+#ifdef IDNA2008
+  uint32_t IDNAOptions = UIDNA_CHECK_BIDI | UIDNA_CHECK_CONTEXTJ;
+  if (!kIDNA2008_TransitionalProcessing) {
+    IDNAOptions |= UIDNA_NONTRANSITIONAL_TO_UNICODE;
+  }
+  UErrorCode errorCode = U_ZERO_ERROR;
+  mIDNA = uidna_openUTS46(IDNAOptions, &errorCode);
+#else
   if (idn_success != idn_nameprep_create(nullptr, &mNamePrepHandle))
     mNamePrepHandle = nullptr;
 
   mNormalizer = do_GetService(NS_UNICODE_NORMALIZER_CONTRACTID);
   /* member initializers and constructor code */
+#endif
 }
 
 nsIDNService::~nsIDNService()
 {
+#ifdef IDNA2008
+  uidna_close(mIDNA);
+#else
   idn_nameprep_destroy(mNamePrepHandle);
+#endif
 }
 
+#ifdef IDNA2008
+nsresult
+nsIDNService::IDNA2008ToUnicode(const nsACString& input, nsAString& output)
+{
+  NS_ConvertUTF8toUTF16 inputStr(input);
+  UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+  UErrorCode errorCode = U_ZERO_ERROR;
+  int32_t inLen = inputStr.Length();
+  int32_t outMaxLen = inLen - kACEPrefixLen + 1;
+  UChar outputBuffer[kMaxDNSNodeLen + 1];
+
+  int32_t outLen = uidna_labelToUnicode(mIDNA, (const UChar*)inputStr.get(),
+                                        inLen, outputBuffer, outMaxLen,
+                                        &info, &errorCode);
+  if (info.errors != 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (U_SUCCESS(errorCode)) {
+    ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
+  }
+
+  return ICUUtils::UErrorToNsResult(errorCode);
+}
+
+nsresult
+nsIDNService::IDNA2008StringPrep(const nsAString& input,
+                                 nsAString& output,
+                                 stringPrepFlag flag)
+{
+  UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+  UErrorCode errorCode = U_ZERO_ERROR;
+  int32_t inLen = input.Length();
+  int32_t outMaxLen = kMaxDNSNodeLen + 1;
+  UChar outputBuffer[kMaxDNSNodeLen + 1];
+
+  int32_t outLen =
+    uidna_labelToUnicode(mIDNA, (const UChar*)PromiseFlatString(input).get(),
+                         inLen, outputBuffer, outMaxLen, &info, &errorCode);
+  nsresult rv = ICUUtils::UErrorToNsResult(errorCode);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Output the result of nameToUnicode even if there were errors
+  ICUUtils::AssignUCharArrayToString(outputBuffer, outLen, output);
+
+  if (flag == eStringPrepIgnoreErrors) {
+    return NS_OK;
+  }
+
+  if (info.errors != 0) {
+    if (flag == eStringPrepForDNS) {
+      output.Truncate();
+    }
+    rv = NS_ERROR_FAILURE;
+  }
+
+  return rv;
+}
+#endif
+
 NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace)
 {
   return UTF8toACE(input, ace, eStringPrepForDNS);
 }
 
 nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace,
                                  stringPrepFlag flag)
 {
@@ -391,28 +474,30 @@ static nsresult utf16ToUcs4(const nsAStr
     if (i >= outBufLen)
       return NS_ERROR_FAILURE;
   }
   out[i] = (uint32_t)'\0';
   *outLen = i;
   return NS_OK;
 }
 
+#ifndef IDNA2008
 static void ucs4toUtf16(const uint32_t *in, nsAString& out)
 {
   while (*in) {
     if (!IS_IN_BMP(*in)) {
       out.Append((char16_t) H_SURROGATE(*in));
       out.Append((char16_t) L_SURROGATE(*in));
     }
     else
       out.Append((char16_t) *in);
     in++;
   }
 }
+#endif
 
 static nsresult punycode(const nsAString& in, nsACString& out)
 {
   uint32_t ucs4Buf[kMaxDNSNodeLen + 1];
   uint32_t ucs4Len;
   nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -457,16 +542,19 @@ static nsresult punycode(const nsAString
 //
 // 5) Check unassigned code points -- If allowUnassigned is false, check for
 // any unassigned Unicode points and if any are found return an error.
 // This is described in section 7.
 //
 nsresult nsIDNService::stringPrep(const nsAString& in, nsAString& out,
                                   stringPrepFlag flag)
 {
+#ifdef IDNA2008
+  return IDNA2008StringPrep(in, out, flag);
+#else
   if (!mNamePrepHandle || !mNormalizer)
     return NS_ERROR_FAILURE;
 
   uint32_t ucs4Buf[kMaxDNSNodeLen + 1];
   uint32_t ucs4Len;
   nsresult rv = utf16ToUcs4(in, ucs4Buf, kMaxDNSNodeLen, &ucs4Len);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -518,16 +606,17 @@ nsresult nsIDNService::stringPrep(const 
     }
   }
 
   if (flag == eStringPrepForDNS && NS_FAILED(rv)) {
     out.Truncate();
   }
 
   return rv;
+#endif
 }
 
 nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
                                         stringPrepFlag flag)
 {
   nsresult rv = NS_OK;
 
   out.Truncate();
@@ -607,16 +696,21 @@ nsresult nsIDNService::decodeACE(const n
 {
   bool isAce;
   IsACE(in, &isAce);
   if (!isAce) {
     out.Assign(in);
     return NS_OK;
   }
 
+  nsAutoString utf16;
+#ifdef IDNA2008
+  nsresult result = IDNA2008ToUnicode(in, utf16);
+  NS_ENSURE_SUCCESS(result, result);
+#else
   // RFC 3490 - 4.2 ToUnicode
   // The ToUnicode output never contains more code points than its input.
   punycode_uint output_length = in.Length() - kACEPrefixLen + 1;
   punycode_uint *output = new punycode_uint[output_length];
   NS_ENSURE_TRUE(output, NS_ERROR_OUT_OF_MEMORY);
 
   enum punycode_status status = punycode_decode(in.Length() - kACEPrefixLen,
                                                 PromiseFlatCString(in).get() + kACEPrefixLen,
@@ -625,19 +719,19 @@ nsresult nsIDNService::decodeACE(const n
                                                 nullptr);
   if (status != punycode_success) {
     delete [] output;
     return NS_ERROR_FAILURE;
   }
 
   // UCS4 -> UTF8
   output[output_length] = 0;
-  nsAutoString utf16;
   ucs4toUtf16(output, utf16);
   delete [] output;
+#endif
   if (flag != eStringPrepForUI || isLabelSafe(utf16)) {
     CopyUTF16toUTF8(utf16, out);
   } else {
     out.Assign(in);
     return NS_OK;
   }
 
   // Validation: encode back to ACE and compare the strings
--- a/netwerk/dns/nsIDNService.h
+++ b/netwerk/dns/nsIDNService.h
@@ -5,18 +5,24 @@
 
 #ifndef nsIDNService_h__
 #define nsIDNService_h__
 
 #include "nsIIDNService.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
+
+#ifdef IDNA2008
+#include "unicode/uidna.h"
+#else
 #include "nsIUnicodeNormalizer.h"
 #include "nsIDNKitInterface.h"
+#endif
+
 #include "nsString.h"
 
 class nsIPrefBranch;
 
 //-----------------------------------------------------------------------------
 // nsIDNService
 //-----------------------------------------------------------------------------
 
@@ -138,18 +144,33 @@ private:
    *  Latin + Han + Bopomofo; or
    *  Latin + Han + Hangul
    *
    * For the "Moderately restrictive" profile, Latin is also allowed
    *  with other scripts except Cyrillic and Greek
    */
   bool illegalScriptCombo(int32_t script, int32_t& savedScript);
 
+#ifdef IDNA2008
+  /**
+   * Convert a DNS label from ASCII to Unicode using IDNA2008
+   */
+  nsresult IDNA2008ToUnicode(const nsACString& input, nsAString& output);
+
+  /**
+   * Convert a DNS label to a normalized form conforming to IDNA2008
+   */
+  nsresult IDNA2008StringPrep(const nsAString& input, nsAString& output,
+                              stringPrepFlag flag);
+
+  UIDNA* mIDNA;
+#else
   idn_nameprep_t mNamePrepHandle;
   nsCOMPtr<nsIUnicodeNormalizer> mNormalizer;
+#endif
   nsXPIDLString mIDNBlacklist;
 
   /**
    * Flag set by the pref network.IDN_show_punycode. When it is true,
    * IDNs containing non-ASCII characters are always displayed to the
    * user in punycode
    */
   bool mShowPunycode;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -353,17 +353,17 @@ nsHttpChannel::Connect()
                 mURI->GetScheme(scheme);
                 // append the additional 's' for security to the scheme :-)
                 scheme.AppendASCII("s");
                 NS_ConvertUTF8toUTF16 reportSpec(spec);
                 NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
                 const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
                 uint32_t innerWindowId = mLoadInfo ? mLoadInfo->GetInnerWindowID() : 0;
-                CSP_LogLocalizedStr(NS_LITERAL_STRING("upgradeInsecureRequest").get(),
+                CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
                                     params, ArrayLength(params),
                                     EmptyString(), // aSourceFile
                                     EmptyString(), // aScriptSample
                                     0, // aLineNumber
                                     0, // aColumnNumber
                                     nsIScriptError::warningFlag, "CSP",
                                     innerWindowId);
 
--- a/netwerk/standalone/moz.build
+++ b/netwerk/standalone/moz.build
@@ -38,13 +38,14 @@ src_list += [
 SOURCES += sorted(src_list)
 
 LOCAL_INCLUDES = [
     '../base',
     '../build',
     '../dns',
 ]
 
+DEFINES['IDNA2008'] = False
 DEFINES['MOZILLA_INTERNAL_API'] = True
 DEFINES['MOZILLA_XPCOMRT_API'] = True
 DEFINES['MOZILLA_EXTERNAL_LINKAGE'] = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/netwerk/test/unit/test_bug427957.js
+++ b/netwerk/test/unit/test_bug427957.js
@@ -87,14 +87,20 @@ function run_test() {
   /*
    * 3) If a string contains any RandALCat character, a RandALCat
    *    character MUST be the first character of the string, and a
    *    RandALCat character MUST be the last character of the string.
    */
 
   // www.1מיץ.com is invalid
   expected_fail("www.1\u05DE\u05D9\u05E5.com");
-  // www.מיץ1.com is invalid
-  expected_fail("www.\u05DE\u05D9\u05E51.com");
+  // www.!מיץ.com is invalid
+  expected_fail("www.!\u05DE\u05D9\u05E5.com");
+  // www.מיץ!.com is invalid
+  expected_fail("www.\u05DE\u05D9\u05E5!.com");
+
+  // XXX TODO: add a test for an RTL label ending with a digit. This was
+  //           invalid in IDNA2003 but became valid in IDNA2008
+
   // But www.מיץ1פטל.com is fine
   expected_pass("www.\u05DE\u05D9\u05E51\u05E4\u05D8\u05DC.com");
 }
   
--- a/netwerk/test/unit/test_idn_blacklist.js
+++ b/netwerk/test/unit/test_idn_blacklist.js
@@ -139,26 +139,32 @@ function run_test() {
         var isASCII = {};
 
 	var result;
 	try {
 	    result = idnService.convertToDisplayIDN(URL, isASCII);
 	} catch(e) {
 	    result = ".com";
 	}
-        if (punycodeURL.substr(0, 4) == "xn--") {
-            // test convertToDisplayIDN with a Unicode URL and with a
-            //  Punycode URL if we have one
-            do_check_eq(escape(result), escape(punycodeURL));
+        // If the punycode URL is equivalent to \ufffd.com (i.e. the
+        // blacklisted character has been replaced by a unicode
+        // REPLACEMENT CHARACTER, skip the test
+        if (result != "xn--zn7c.com") {
+
+            if (punycodeURL.substr(0, 4) == "xn--") {
+                // test convertToDisplayIDN with a Unicode URL and with a
+                //  Punycode URL if we have one
+                equal(escape(result), escape(punycodeURL));
 
-            result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
-            do_check_eq(escape(result), escape(punycodeURL));
-        } else {
-            // The "punycode" URL isn't punycode. This happens in testcases
-            // where the Unicode URL has become normalized to an ASCII URL,
-            // so, even though expectedUnicode is true, the expected result
-            // is equal to punycodeURL
-            do_check_eq(escape(result), escape(punycodeURL));
+                result = idnService.convertToDisplayIDN(punycodeURL, isASCII);
+                equal(escape(result), escape(punycodeURL));
+            } else {
+                // The "punycode" URL isn't punycode. This happens in testcases
+                // where the Unicode URL has become normalized to an ASCII URL,
+                // so, even though expectedUnicode is true, the expected result
+                // is equal to punycodeURL
+                equal(escape(result), escape(punycodeURL));
+            }
         }
     }
     pbi.setBoolPref("network.IDN.whitelist.com", oldWhitelistCom);
     pbi.setCharPref("network.IDN.restriction_profile", oldProfile);
 }
--- a/netwerk/test/unit/test_idn_urls.js
+++ b/netwerk/test/unit/test_idn_urls.js
@@ -7,18 +7,25 @@ const testcases = [
     ["cuillère", "xn--cuillre-6xa",                  false, true,  true],
 
     // repeated non-spacing marks
     ["gruz̀̀ere",  "xn--gruzere-ogea",                 false, false, false],
 
     // non-XID character
     ["I♥NY",     "xn--iny-zx5a",                     false, false, false],
 
+/*
+  Behaviour of this test changed in IDNA2008, replacing the non-XID
+  character with U+FFFD replacement character - when all platforms use
+  IDNA2008 it can be uncommented and the punycode URL changed to
+   "xn--mgbl3eb85703a"
+
     // new non-XID character in Unicode 6.3
     ["حلا\u061cل", "xn--bgbvr6gc",                    false, false, false],
+*/
 
     // U+30FB KATAKANA MIDDLE DOT is excluded from non-XID characters (bug 857490)
     ["乾燥肌・石けん", "xn--08j4gylj12hz80b0uhfup",     false, true,  true],
 
     // Cyrillic alone
     ["толсто́й",  "xn--lsa83dealbred",                false, true,  true],
 
     // Mixed script Cyrillic/Latin
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_idna2008.js
@@ -0,0 +1,60 @@
+const kTransitionalProcessing = true;
+
+// Four characters map differently under non-transitional processing:
+const labels = [
+  // U+00DF LATIN SMALL LETTER SHARP S to "ss"
+  "stra\u00dfe",
+  // U+03C2 GREEK SMALL LETTER FINAL SIGMA to U+03C3 GREEK SMALL LETTER SIGMA
+  "\u03b5\u03bb\u03bb\u03ac\u03c2",
+  // U+200C ZERO WIDTH NON-JOINER in Indic script
+  "\u0646\u0627\u0645\u0647\u200c\u0627\u06cc",
+  // U+200D ZERO WIDTH JOINER in Arabic script
+  "\u0dc1\u0dca\u200d\u0dbb\u0dd3",
+
+  // But CONTEXTJ rules prohibit ZWJ and ZWNJ in non-Arabic or Indic scripts
+  // U+200C ZERO WIDTH NON-JOINER in Latin script
+  "m\200cn",
+  // U+200D ZERO WIDTH JOINER in Latin script
+  "p\200dq",
+];
+
+const transitionalExpected = [
+  "strasse",
+  "xn--hxarsa5b",
+  "xn--mgba3gch31f",
+  "xn--10cl1a0b",
+  "",
+  ""
+];
+
+const nonTransitionalExpected = [
+  "xn--strae-oqa",
+  "xn--hxarsa0b",
+  "xn--mgba3gch31f060k",
+  "xn--10cl1a0b660p",
+  "",
+  ""
+];
+
+// Test options for converting IDN URLs under IDNA2008
+function run_test()
+{
+  var idnService = Components.classes["@mozilla.org/network/idn-service;1"]
+                             .getService(Components.interfaces.nsIIDNService);
+
+
+  for (var i = 0; i < labels.length; ++i) {
+    var result;
+    try {
+        result = idnService.convertUTF8toACE(labels[i]);
+    } catch(e) {
+        result = "";
+    }
+
+    if (kTransitionalProcessing) {
+      equal(result, transitionalExpected[i]);
+    } else {
+      equal(result, nonTransitionalExpected[i]);
+    }
+  }
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -210,16 +210,17 @@ skip-if = bits != 32
 [test_http_headers.js]
 [test_httpauth.js]
 [test_httpcancel.js]
 [test_httpResponseTimeout.js]
 [test_httpsuspend.js]
 [test_idnservice.js]
 [test_idn_blacklist.js]
 [test_idn_urls.js]
+[test_idna2008.js]
 [test_invalidport.js]
 [test_localstreams.js]
 [test_mismatch_last-modified.js]
 [test_MIME_params.js]
 [test_mozTXTToHTMLConv.js]
 [test_multipart_byteranges.js]
 [test_multipart_streamconv.js]
 [test_multipart_streamconv_missing_lead_boundary.js]
--- a/rdf/datasource/nsFileSystemDataSource.cpp
+++ b/rdf/datasource/nsFileSystemDataSource.cpp
@@ -328,17 +328,17 @@ FileSystemDataSource::GetTarget(nsIRDFRe
             if (NS_FAILED(rv)) return(rv);
             if (isFavorite || !url) rv = NS_RDF_NO_VALUE;
             if (rv == NS_RDF_NO_VALUE)  return(rv);
             
             const char16_t *uni = nullptr;
             url->GetValueConst(&uni);
             if (!uni)   return(NS_RDF_NO_VALUE);
             nsAutoString    urlStr;
-            urlStr.Assign(NS_LITERAL_STRING(NS_MOZICON_SCHEME).get());
+            urlStr.AssignLiteral(NS_MOZICON_SCHEME);
             urlStr.Append(uni);
 
             rv = mRDFService->GetLiteral(urlStr.get(), getter_AddRefs(url));
             if (NS_FAILED(rv) || !url)    return(NS_RDF_NO_VALUE);
             return url->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) target);
         }
         else if (property == mNC_Length)
         {
--- a/security/manager/tools/PreloadedHPKPins.json
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -35,18 +35,19 @@
       // Use the larger google_root_pems pinset instead of google
       "google": "google_root_pems"
     },
     "production_pinsets": [
       "google_root_pems",
       "facebook"
     ],
     "production_domains": [
-      // Chrome's test domain.
+      // Chrome's test domains.
       "pinningtest.appspot.com",
+      "pinning-test.badssl.com",
       // Dropbox
       "dropbox.com",
       "www.dropbox.com",
       // Twitter
       "api.twitter.com",
       "business.twitter.com",
       "dev.twitter.com",
       "mobile.twitter.com",
--- a/testing/mozbase/docs/mozdevice.rst
+++ b/testing/mozbase/docs/mozdevice.rst
@@ -165,65 +165,81 @@ Device Shell methods
 ++++++++++++++++++++
 .. automethod:: ADBDevice.shell(self, cmd, env=None, cwd=None, timeout=None, root=False)
 .. automethod:: ADBDevice.shell_bool(self, cmd, env=None, cwd=None, timeout=None, root=False)
 .. automethod:: ADBDevice.shell_output(self, cmd, env=None, cwd=None, timeout=None, root=False)
 
 Informational methods
 +++++++++++++++++++++
 .. automethod:: ADBDevice.clear_logcat
+.. automethod:: ADBDevice.get_battery_percentage
+.. automethod:: ADBDevice.get_info
 .. automethod:: ADBDevice.get_logcat
-.. automethod:: ADBDevice.get_prop(self, prop, timeout=None)
-.. automethod:: ADBDevice.get_state(self, timeout=None)
+.. automethod:: ADBDevice.get_prop
+.. automethod:: ADBDevice.get_state
+
+System control methods
+++++++++++++++++++++++
+.. automethod:: ADBDevice.is_device_ready
+.. automethod:: ADBDevice.reboot
 
 File management methods
 +++++++++++++++++++++++
-.. automethod:: ADBDevice.chmod(self, path, recursive=False, mask="777", timeout=None, root=False)
-.. automethod:: ADBDevice.exists(self, path, timeout=None, root=False)
-.. automethod:: ADBDevice.is_dir(self, path, timeout=None, root=False)
-.. automethod:: ADBDevice.is_file(self, path, timeout=None, root=False)
-.. automethod:: ADBDevice.list_files(self, path, timeout=None, root=False)
-.. automethod:: ADBDevice.mkdir(self, path, parents=False, timeout=None, root=False)
-.. automethod:: ADBDevice.push(self, local, remote, timeout=None)
-.. automethod:: ADBDevice.rm(self, path, recursive=False, force=False, timeout=None, root=False)
-.. automethod:: ADBDevice.rmdir(self, path, timeout=None, root=False)
+.. automethod:: ADBDevice.chmod
+.. automethod:: ADBDevice.cp
+.. automethod:: ADBDevice.exists
+.. automethod:: ADBDevice.is_dir
+.. automethod:: ADBDevice.is_file
+.. automethod:: ADBDevice.list_files
+.. automethod:: ADBDevice.mkdir
+.. automethod:: ADBDevice.mv
+.. automethod:: ADBDevice.push
+.. automethod:: ADBDevice.rm
+.. automethod:: ADBDevice.rmdir
 .. autoattribute:: ADBDevice.test_root
 
 Process management methods
 ++++++++++++++++++++++++++
-.. automethod:: ADBDevice.get_process_list(self, timeout=None)
-.. automethod:: ADBDevice.kill(self, pids, sig=None,   attempts=3, wait=5, timeout=None, root=False)
-.. automethod:: ADBDevice.pkill(self, appname, sig=None,   attempts=3, wait=5, timeout=None, root=False)
-.. automethod:: ADBDevice.process_exist(self, process_name, timeout=None)
-
+.. automethod:: ADBDevice.get_process_list
+.. automethod:: ADBDevice.kill
+.. automethod:: ADBDevice.pkill
+.. automethod:: ADBDevice.process_exist
 
 ADBAndroid
 ``````````
 .. autoclass:: ADBAndroid
 
 Informational methods
 +++++++++++++++++++++
-.. automethod:: ADBAndroid.get_battery_percentage(self, timeout=None)
+.. automethod:: ADBAndroid.get_battery_percentage
 
 System control methods
 ++++++++++++++++++++++
-.. automethod:: ADBAndroid.is_device_ready(self, timeout=None)
-.. automethod:: ADBAndroid.power_on(self, timeout=None)
-.. automethod:: ADBAndroid.reboot(self, timeout=None)
+.. automethod:: ADBAndroid.is_device_ready
+.. automethod:: ADBAndroid.power_on
 
 Application management methods
 ++++++++++++++++++++++++++++++
-.. automethod:: ADBAndroid.install_app(self, apk_path, timeout=None)
-.. automethod:: ADBAndroid.is_app_installed(self, app_name, timeout=None)
-.. automethod:: ADBAndroid.launch_application(self, app_name, activity_name, intent, url=None, extras=None, wait=True, fail_if_running=True, timeout=None)
-.. automethod:: ADBAndroid.launch_fennec(self, app_name, intent="android.intent.action.VIEW", moz_env=None, extra_args=None, url=None, wait=True, fail_if_running=True, timeout=None)
-.. automethod:: ADBAndroid.stop_application(self, app_name, timeout=None, root=False)
-.. automethod:: ADBAndroid.uninstall_app(self, app_name, reboot=False, timeout=None)
-.. automethod:: ADBAndroid.update_app(self, apk_path, timeout=None)
+.. automethod:: ADBAndroid.install_app
+.. automethod:: ADBAndroid.is_app_installed
+.. automethod:: ADBAndroid.launch_application
+.. automethod:: ADBAndroid.launch_fennec
+.. automethod:: ADBAndroid.stop_application
+.. automethod:: ADBAndroid.uninstall_app
+.. automethod:: ADBAndroid.update_app
 
+ADBB2G
+``````
+.. autoclass:: ADBB2G
+
+Informational methods
++++++++++++++++++++++
+.. automethod:: ADBB2G.get_battery_percentage
+.. automethod:: ADBB2G.get_info
+.. automethod:: ADBB2G.get_memory_total
 
 ADBProcess
 ``````````
 .. autoclass:: mozdevice.ADBProcess
 
 ADBError
 ````````
 .. autoexception:: mozdevice.ADBError
--- a/testing/mozbase/mozdevice/mozdevice/__init__.py
+++ b/testing/mozbase/mozdevice/mozdevice/__init__.py
@@ -1,10 +1,11 @@
 # 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/.
 
 from adb import ADBError, ADBRootError, ADBTimeoutError, ADBProcess, ADBCommand, ADBHost, ADBDevice
 from adb_android import ADBAndroid
+from adb_b2g import ADBB2G
 from devicemanager import DeviceManager, DMError, ZeroconfListener
 from devicemanagerADB import DeviceManagerADB
 from devicemanagerSUT import DeviceManagerSUT
 from droid import DroidADB, DroidSUT, DroidConnectByHWID
--- a/testing/mozbase/mozdevice/mozdevice/adb.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb.py
@@ -1,25 +1,24 @@
 # 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/.
 
+from abc import ABCMeta, abstractmethod
 import os
 import posixpath
 import re
 import subprocess
 import tempfile
 import time
 import traceback
 
 
 class ADBProcess(object):
-    """ADBProcess encapsulates the data related to executing the adb process.
-
-    """
+    """ADBProcess encapsulates the data related to executing the adb process."""
     def __init__(self, args):
         #: command argument argument list.
         self.args = args
         #: Temporary file handle to be used for stdout.
         self.stdout_file = tempfile.TemporaryFile()
         #: Temporary file handle to be used for stderr.
         self.stderr_file = tempfile.TemporaryFile()
         #: boolean indicating if the command timed out.
@@ -59,26 +58,24 @@ class ADBProcess(object):
 # differently in order that unhandled ADBRootErrors and
 # ADBTimeoutErrors can be handled distinctly from ADBErrors.
 
 class ADBError(Exception):
     """ADBError is raised in situations where a command executed on a
     device either exited with a non-zero exitcode or when an
     unexpected error condition has occurred. Generally, ADBErrors can
     be handled and the device can continue to be used.
-
     """
     pass
 
 class ADBRootError(Exception):
     """ADBRootError is raised when a shell command is to be executed as
     root but the device does not support it. This error is fatal since
     there is no recovery possible by the script. You must either root
     your device or change your scripts to not require running as root.
-
     """
     pass
 
 class ADBTimeoutError(Exception):
     """ADBTimeoutError is raised when either a host command or shell
     command takes longer than the specified timeout to execute. The
     timeout value is set in the ADBCommand constructor and is 300 seconds by
     default. This error is typically fatal since the host is having
@@ -89,17 +86,16 @@ class ADBTimeoutError(Exception):
 
     * Killing and restarting the adb server via
       ::
 
           adb kill-server; adb start-server
 
     * Rebooting the device manually.
     * Rebooting the host.
-
     """
     pass
 
 
 class ADBCommand(object):
     """ADBCommand provides a basic interface to adb commands
     which is used to provide the 'command' methods for the
     classes ADBHost and ADBDevice.
@@ -112,17 +108,16 @@ class ADBCommand(object):
     ::
 
        from mozdevice import ADBCommand
 
        try:
            adbcommand = ADBCommand()
        except NotImplementedError:
            print "ADBCommand can not be instantiated."
-
     """
 
     def __init__(self,
                  adb='adb',
                  adb_host=None,
                  adb_port=None,
                  logger_name='adb',
                  timeout=300,
@@ -133,17 +128,16 @@ class ADBCommand(object):
         :param adb_host: host of the adb server.
         :type adb_host: str or None
         :param adb_port: port of the adb server.
         :type adb_port: integer or None
         :param str logger_name: logging logger name. Defaults to 'adb'.
 
         :raises: * ADBError
                  * ADBTimeoutError
-
         """
         if self.__class__ == ADBCommand:
             raise NotImplementedError
 
         self._logger = self._get_logger(logger_name)
         self._verbose = verbose
         self._adb_path = adb
         self._adb_host = adb_host
@@ -206,17 +200,16 @@ class ADBCommand(object):
         timeout period in seconds.
 
         A subprocess is spawned to execute adb with stdout and stderr
         directed to temporary files. If the process takes longer than
         the specified timeout, the process is terminated.
 
         It is the caller's responsibilty to clean up by closing
         the stdout and stderr temporary files.
-
         """
         args = [self._adb_path]
         if self._adb_host:
             args.extend(['-H', self._adb_host])
         if self._adb_port:
             args.extend(['-P', str(self._adb_port)])
         if device_serial:
             args.extend(['-s', device_serial, 'wait-for-device'])
@@ -258,17 +251,16 @@ class ADBCommand(object):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBCommand constructor is used.
         :type timeout: integer or None
         :returns: string - content of stdout.
 
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         adb_process = None
         try:
             # Need to force the use of the ADBCommand class's command
             # since ADBDevice will redefine command and call its
             # own version otherwise.
             adb_process = ADBCommand.command(self, cmds,
                                              device_serial=device_serial,
@@ -301,17 +293,16 @@ class ADBHost(ADBCommand):
     which do not target a specific device.
 
     ::
 
        from mozdevice import ADBHost
 
        adbhost = ADBHost()
        adbhost.start_server()
-
     """
     def __init__(self,
                  adb='adb',
                  adb_host=None,
                  adb_port=None,
                  logger_name='adb',
                  timeout=300,
                  verbose=False):
@@ -321,17 +312,16 @@ class ADBHost(ADBCommand):
         :param adb_host: host of the adb server.
         :type adb_host: str or None
         :param adb_port: port of the adb server.
         :type adb_port: integer or None
         :param str logger_name: logging logger name. Defaults to 'adb'.
 
         :raises: * ADBError
                  * ADBTimeoutError
-
         """
         ADBCommand.__init__(self, adb=adb, adb_host=adb_host,
                             adb_port=adb_port, logger_name=logger_name,
                             timeout=timeout, verbose=verbose)
 
     def command(self, cmds, timeout=None):
         """Executes an adb command on the host.
 
@@ -357,17 +347,16 @@ class ADBHost(ADBCommand):
         timeout period in seconds.
 
         A subprocess is spawned to execute adb with stdout and stderr
         directed to temporary files. If the process takes longer than
         the specified timeout, the process is terminated.
 
         It is the caller's responsibilty to clean up by closing
         the stdout and stderr temporary files.
-
         """
         return ADBCommand.command(self, cmds, timeout=timeout)
 
     def command_output(self, cmds, timeout=None):
         """Executes an adb command on the host returning stdout.
 
         :param list cmds: The command and its arguments to be
             executed.
@@ -377,17 +366,16 @@ class ADBHost(ADBCommand):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBHost constructor is used.
         :type timeout: integer or None
         :returns: string - content of stdout.
 
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         return ADBCommand.command_output(self, cmds, timeout=timeout)
 
     def start_server(self, timeout=None):
         """Starts the adb server.
 
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
@@ -410,32 +398,30 @@ class ADBHost(ADBCommand):
         If you wish the remote adb server to restart automatically, you can
         enclose the command in a loop as in:
 
         .. code-block:: shell
 
             while true; do
               adb -a fork-server server
             done
-
         """
         self.command_output(["start-server"], timeout=timeout)
 
     def kill_server(self, timeout=None):
         """Kills the adb server.
 
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.  This timeout is per adb call. The
             total time spent may exceed this value. If it is not
             specified, the value set in the ADBHost constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         self.command_output(["kill-server"], timeout=timeout)
 
     def devices(self, timeout=None):
         """Executes adb devices -l and returns a list of objects describing attached devices.
 
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
@@ -452,17 +438,16 @@ class ADBHost(ADBCommand):
             $ adb devices -l
             List of devices attached
             b313b945               device usb:1-7 product:d2vzw model:SCH_I535 device:d2vzw
 
         is parsed and placed into an object as in
 
         [{'device_serial': 'b313b945', 'state': 'device', 'product': 'd2vzw',
           'usb': '1-7', 'device': 'd2vzw', 'model': 'SCH_I535' }]
-
         """
         # b313b945               device usb:1-7 product:d2vzw model:SCH_I535 device:d2vzw
         # from Android system/core/adb/transport.c statename()
         re_device_info = re.compile(r'([^\s]+)\s+(offline|bootloader|device|host|recovery|sideload|no permissions|unauthorized|unknown)')
         devices = []
         lines = self.command_output(["devices", "-l"], timeout=timeout).split('\n')
         for line in lines:
             if line == 'List of devices attached ':
@@ -481,32 +466,22 @@ class ADBHost(ADBCommand):
                     except ValueError:
                         self._logger.warning('devices: Unable to parse '
                                              'remainder for device %s' % line)
                 devices.append(device)
         return devices
 
 
 class ADBDevice(ADBCommand):
-    """ADBDevice provides methods which can be used to interact with
-    the associated Android-based device.
-
-    Android specific features such as Application management are not
-    included but are provided via the ADBAndroid interface.
-
-    ::
-
-       from mozdevice import ADBDevice
-
-       adbdevice = ADBDevice()
-       print adbdevice.list_files("/mnt/sdcard")
-       if adbdevice.process_exist("org.mozilla.fennec"):
-           print "Fennec is running"
-
+    """ADBDevice is an abstract base class which provides methods which
+    can be used to interact with the associated Android or B2G based
+    device. It must be used via one of the concrete implementations in
+    :class:`ADBAndroid` or :class:`ADBB2G`.
     """
+    __metaclass__ = ABCMeta
 
     def __init__(self,
                  device=None,
                  adb='adb',
                  adb_host=None,
                  adb_port=None,
                  test_root='',
                  logger_name='adb',
@@ -542,18 +517,16 @@ class ADBDevice(ADBCommand):
             between attempts to check if the device is ready after a
             reboot.
         :param integer device_ready_retry_attempts: number of attempts when
             checking if a device is ready.
 
         :raises: * ADBError
                  * ADBTimeoutError
                  * ValueError
-
-
         """
         ADBCommand.__init__(self, adb=adb, adb_host=adb_host,
                             adb_port=adb_port, logger_name=logger_name,
                             timeout=timeout, verbose=verbose)
         self._device_serial = self._get_device_serial(device)
         self._initial_test_root = test_root
         self._test_root = None
         self._device_ready_retry_wait = device_ready_retry_wait
@@ -599,16 +572,19 @@ class ADBDevice(ADBCommand):
         else:
             raise ADBError("ADBDevice.__init__: ls not found")
         try:
             self.shell_output("%s -1A /" % self._ls)
             self._ls += " -1A"
         except ADBError:
             self._ls += " -a"
 
+        # Do we have cp?
+        self._have_cp = self.shell_bool("type cp")
+
         self._logger.debug("ADBDevice: %s" % self.__dict__)
 
     def _get_device_serial(self, device):
         if device is None:
             devices = ADBHost(adb=self._adb_path, adb_host=self._adb_host,
                               adb_port=self._adb_port).devices()
             if len(devices) > 1:
                 raise ValueError("ADBDevice called with multiple devices "
@@ -639,17 +615,16 @@ class ADBDevice(ADBCommand):
 
         raise ValueError("Unable to get device serial")
 
 
     @staticmethod
     def _escape_command_line(cmd):
         """Utility function to return escaped and quoted version of command
         line.
-
         """
         quoted_cmd = []
 
         for arg in cmd:
             arg.replace('&', '\&')
 
             needs_quoting = False
             for char in [' ', '(', ')', '"', '&']:
@@ -662,17 +637,16 @@ class ADBDevice(ADBCommand):
             quoted_cmd.append(arg)
 
         return " ".join(quoted_cmd)
 
     @staticmethod
     def _get_exitcode(file_obj):
         """Get the exitcode from the last line of the file_obj for shell
         commands.
-
         """
         file_obj.seek(0, os.SEEK_END)
 
         line = ''
         length = file_obj.tell()
         offset = 1
         while length - offset >= 0:
             file_obj.seek(-offset, os.SEEK_END)
@@ -801,17 +775,16 @@ class ADBDevice(ADBCommand):
 
         A subprocess is spawned to execute adb for the device with
         stdout and stderr directed to temporary files. If the process
         takes longer than the specified timeout, the process is
         terminated.
 
         It is the caller's responsibilty to clean up by closing
         the stdout and stderr temporary files.
-
         """
 
         return ADBCommand.command(self, cmds,
                                   device_serial=self._device_serial,
                                   timeout=timeout)
 
     def command_output(self, cmds, timeout=None):
         """Executes an adb command on the host against the device returning
@@ -824,17 +797,16 @@ class ADBDevice(ADBCommand):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: string - content of stdout.
 
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         return ADBCommand.command_output(self, cmds,
                                          device_serial=self._device_serial,
                                          timeout=timeout)
 
     # Port forwarding methods
 
     def _validate_port(self, port, is_local=True):
@@ -975,17 +947,16 @@ class ADBDevice(ADBCommand):
         A subprocess is spawned to execute adb shell for the device
         with stdout and stderr directed to temporary files. If the
         process takes longer than the specified timeout, the process
         is terminated. The return code is extracted from the stdout
         and is then removed from the file.
 
         It is the caller's responsibilty to clean up by closing
         the stdout and stderr temporary files.
-
         """
         if root:
             ld_library_path='LD_LIBRARY_PATH=/vendor/lib:/system/lib'
             cmd = '%s %s' % (ld_library_path, cmd)
             if self._have_root_shell:
                 pass
             elif self._have_su:
                 cmd = "su -c \"%s\"" % cmd
@@ -1053,17 +1024,16 @@ class ADBDevice(ADBCommand):
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :returns: boolean
 
         :raises: * ADBTimeoutError
                  * ADBRootError
-
         """
         adb_process = None
         try:
             adb_process = self.shell(cmd, env=env, cwd=cwd,
                                      timeout=timeout, root=root)
             if adb_process.timedout:
                 raise ADBTimeoutError("%s" % adb_process)
             return adb_process.exitcode == 0
@@ -1088,17 +1058,16 @@ class ADBDevice(ADBCommand):
             in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command
             should be executed as root.
         :returns: string - content of stdout.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         adb_process = None
         try:
             adb_process = self.shell(cmd, env=env, cwd=cwd,
                                      timeout=timeout, root=root)
             if adb_process.timedout:
                 raise ADBTimeoutError("%s" % adb_process)
             elif adb_process.exitcode:
@@ -1146,17 +1115,16 @@ class ADBDevice(ADBCommand):
             adb call. The total time spent may exceed this
             value. If it is not specified, the value set
             in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param list buffers: Log buffers to clear. Valid buffers are
             "radio", "events", and "main". Defaults to "main".
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         buffers = self._get_logcat_buffer_args(buffers)
         cmds = ["logcat", "-c"] + buffers
         self.command_output(cmds, timeout=timeout)
 
     def get_logcat(self,
                    filter_specs=[
                        "dalvikvm:I",
@@ -1183,17 +1151,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param list buffers: Log buffers to retrieve. Valid buffers are
             "radio", "events", and "main". Defaults to "main".
         :returns: list of lines logcat output.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         buffers = self._get_logcat_buffer_args(buffers)
         cmds = ["logcat", "-v", format, "-d"] + buffers + filter_specs
         lines = self.command_output(cmds, timeout=timeout).split('\r')
 
         for regex in filter_out_regexps:
             lines = [line for line in lines if not re.search(regex, line)]
 
@@ -1208,17 +1175,16 @@ class ADBDevice(ADBCommand):
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: string value of property.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         output = self.shell_output('getprop %s' % prop, timeout=timeout)
         return output
 
     def get_state(self, timeout=None):
         """Returns the device's state via adb get-state.
 
         :param timeout: The maximum time in
@@ -1226,17 +1192,16 @@ class ADBDevice(ADBCommand):
             an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: string value of adb get-state.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         output = self.command_output(["get-state"], timeout=timeout).strip()
         return output
 
     def get_ip_address(self, interfaces=None, timeout=None):
         """Returns the device's ip address, or None if it doesn't have one
 
         :param interfaces: Interfaces to allow, or None to allow any
@@ -1248,17 +1213,16 @@ class ADBDevice(ADBCommand):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: string ip address of the device or None if it could not
             be found.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         ip_regexp = re.compile(r'(\w+)\s+UP\s+([1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
         data = self.shell_output('netcfg')
         for line in data.split("\n"):
             match = ip_regexp.search(line)
             if match:
                 interface, ip = match.groups()
 
@@ -1304,17 +1268,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         path = posixpath.normpath(path.strip())
         self._logger.debug('chmod: path=%s, recursive=%s, mask=%s, root=%s' %
                            (path, recursive, mask, root))
         self.shell_output("chmod %s %s" % (mask, path),
                           timeout=timeout, root=root)
         if recursive and self.is_dir(path, timeout=timeout, root=root):
             files = self.list_files(path, timeout=timeout, root=root)
@@ -1351,17 +1314,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should be
             executed as root.
         :returns: boolean - True if path exists.
         :raises: * ADBTimeoutError
                  * ADBRootError
-
         """
         path = posixpath.normpath(path)
         return self.shell_bool('ls -a %s' % path, timeout=timeout, root=root)
 
     def is_dir(self, path, timeout=None, root=False):
         """Returns True if path is an existing directory on the device.
 
         :param str path: The path on the device.
@@ -1373,17 +1335,16 @@ class ADBDevice(ADBCommand):
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :returns: boolean - True if path exists on the device and is a
             directory.
         :raises: * ADBTimeoutError
                  * ADBRootError
-
         """
         path = posixpath.normpath(path)
         return self.shell_bool('ls -a %s/' % path, timeout=timeout, root=root)
 
     def is_file(self, path, timeout=None, root=False):
         """Returns True if path is an existing file on the device.
 
         :param str path: The file name on the device.
@@ -1395,17 +1356,16 @@ class ADBDevice(ADBCommand):
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :returns: boolean - True if path exists on the device and is a
             file.
         :raises: * ADBTimeoutError
                  * ADBRootError
-
         """
         path = posixpath.normpath(path)
         return (
             self.exists(path, timeout=timeout, root=root) and
             not self.is_dir(path, timeout=timeout, root=root))
 
     def list_files(self, path, timeout=None, root=False):
         """Return a list of files/directories contained in a directory
@@ -1419,17 +1379,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :returns: list of files/directories contained in the directory.
         :raises: * ADBTimeoutError
                  * ADBRootError
-
         """
         path = posixpath.normpath(path.strip())
         data = []
         if self.is_dir(path, timeout=timeout, root=root):
             try:
                 data = self.shell_output("%s %s" % (self._ls, path),
                                          timeout=timeout,
                                          root=root).split('\r\n')
@@ -1456,17 +1415,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         path = posixpath.normpath(path)
         if parents:
             if self._mkdir_p is None or self._mkdir_p:
                 # Use shell_bool to catch the possible
                 # non-zero exitcode if -p is not supported.
                 if self.shell_bool('mkdir -p %s' % path, timeout=timeout):
                     self._mkdir_p = True
@@ -1499,17 +1457,16 @@ class ADBDevice(ADBCommand):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         self.command_output(["push", os.path.realpath(local), remote],
                             timeout=timeout)
 
     def pull(self, remote, local, timeout=None):
         """Pulls a file or directory from the device.
 
         :param str remote: The path of the remote file or
@@ -1520,17 +1477,16 @@ class ADBDevice(ADBCommand):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         self.command_output(["pull", remote, os.path.realpath(local)],
                             timeout=timeout)
 
     def rm(self, path, recursive=False, force=False, timeout=None, root=False):
         """Delete files or directories on the device.
 
         :param str path: The path of the remote file or directory.
@@ -1546,17 +1502,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         cmd = "rm"
         if recursive:
             cmd += " -r"
         try:
             self.shell_output("%s %s" % (cmd, path), timeout=timeout, root=root)
             if self.is_file(path, timeout=timeout, root=root):
                 raise ADBError('rm("%s") failed to remove file.' % path)
@@ -1575,17 +1530,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         self.shell_output("rmdir %s" % path, timeout=timeout, root=root)
         if self.is_dir(path, timeout=timeout, root=root):
             raise ADBError('rmdir("%s") failed to remove directory.' % path)
 
     # Process management methods
 
     def get_process_list(self, timeout=None):
@@ -1598,17 +1552,16 @@ class ADBDevice(ADBCommand):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified,
             the value set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: list of (pid, name, user) tuples for running processes
             on the device.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         adb_process = None
         try:
             adb_process = self.shell("ps", timeout=timeout)
             if adb_process.timedout:
                 raise ADBTimeoutError("%s" % adb_process)
             elif adb_process.exitcode:
                 raise ADBError("%s" % adb_process)
@@ -1663,17 +1616,16 @@ class ADBDevice(ADBCommand):
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         pid_list = [str(pid) for pid in pids]
         for attempt in range(attempts):
             args = ["kill"]
             if sig:
                 args.append("-%d" % sig)
             args.extend(pid_list)
             try:
@@ -1714,17 +1666,16 @@ class ADBDevice(ADBCommand):
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should
             be executed as root.
 
         :raises: * ADBTimeoutError
                  * ADBRootError
                  * ADBError
-
         """
         procs = self.get_process_list(timeout=timeout)
         # limit the comparion to the first 75 characters due to a
         # limitation in processname length in android.
         pids = [proc[0] for proc in procs if proc[1] == appname[:75]]
         if not pids:
             return
 
@@ -1748,17 +1699,16 @@ class ADBDevice(ADBCommand):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: boolean - True if process exists.
 
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         if not isinstance(process_name, basestring):
             raise ADBError("Process name %s is not a string" % process_name)
 
         # Filter out extra spaces.
         parts = [x for x in process_name.split(' ') if x != '']
         process_name = ' '.join(parts)
 
@@ -1778,8 +1728,209 @@ class ADBDevice(ADBCommand):
 
         for proc in proc_list:
             proc_name = proc[1].split('/')[-1]
             # limit the comparion to the first 75 characters due to a
             # limitation in processname length in android.
             if proc_name == app[:75]:
                 return True
         return False
+
+    def cp(self, source, destination, recursive=False, timeout=None,
+           root=False):
+        """Copies a file or directory on the device.
+
+        :param source: string containing the path of the source file or
+            directory.
+        :param destination: string containing the path of the destination file
+            or directory.
+        :param recursive: optional boolean indicating if a recursive copy is to
+            be performed. Required if the source is a directory. Defaults to
+            False. Think cp -R source destination.
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBRootError
+                 * ADBError
+        """
+        source = posixpath.normpath(source)
+        destination = posixpath.normpath(destination)
+        if self._have_cp:
+            r = '-R' if recursive else ''
+            self.shell_output('cp %s %s %s' % (r, source, destination),
+                              timeout=timeout, root=root)
+            return
+
+        # Emulate cp behavior depending on if source and destination
+        # already exists and whether they are a directory or file.
+        if not self.exists(source, timeout=timeout, root=root):
+            raise ADBError("cp: can't stat '%s': No such file or directory" %
+                           source)
+
+        if self.is_file(source, timeout=timeout, root=root):
+            if self.is_dir(destination, timeout=timeout, root=root):
+                # Copy the source file into the destination directory
+                destination = posixpath.join(destination,
+                                             posixpath.basename(source))
+            self.shell_output('dd if=%s of=%s' % (source, destination),
+                              timeout=timeout, root=root)
+            return
+
+        if self.is_file(destination, timeout=timeout, root=root):
+            raise ADBError('cp: %s: Not a directory' % destination)
+
+        if not recursive:
+            raise ADBError("cp: omitting directory '%s'" % source)
+
+        if self.is_dir(destination, timeout=timeout, root=root):
+            # Copy the source directory into the destination directory.
+            destination_dir = posixpath.join(destination,
+                                             posixpath.basename(source))
+        else:
+            # Copy the contents of the source directory into the
+            # destination directory.
+            destination_dir = destination
+
+        try:
+            # Do not create parent directories since cp does not.
+            self.mkdir(destination_dir, timeout=timeout, root=root)
+        except ADBError as e:
+            if 'File exists' not in e.message:
+                raise
+
+        for i in self.list_files(source, timeout=timeout, root=root):
+            self.cp(posixpath.join(source, i),
+                    posixpath.join(destination_dir, i),
+                    recursive=recursive,
+                    timeout=timeout, root=root)
+
+    def mv(self, source, destination, timeout=None, root=False):
+        """Moves a file or directory on the device.
+
+        :param source: string containing the path of the source file or
+            directory.
+        :param destination: string containing the path of the destination file
+            or directory.
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBRootError
+                 * ADBError
+        """
+        source = posixpath.normpath(source)
+        destination = posixpath.normpath(destination)
+        self.shell_output('mv %s %s' % (source, destination), timeout=timeout,
+                          root=root)
+
+    def reboot(self, timeout=None):
+        """Reboots the device.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADB constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBError
+
+        reboot() reboots the device, issues an adb wait-for-device in order to
+        wait for the device to complete rebooting, then calls is_device_ready()
+        to determine if the device has completed booting.
+        """
+        self.command_output(["reboot"], timeout=timeout)
+        self.command_output(["wait-for-device"], timeout=timeout)
+        return self.is_device_ready(timeout=timeout)
+
+    @abstractmethod
+    def is_device_ready(self, timeout=None):
+        """Abstract class that returns True if the device is ready.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADB constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        return
+
+    @abstractmethod
+    def get_battery_percentage(self, timeout=None):
+        """Abstract class that returns the battery charge as a percentage.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :returns: battery charge as a percentage.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        return
+
+    def get_info(self, directive=None, timeout=None):
+        """
+        Returns a dictionary of information strings about the device.
+
+        :param directive: information you want to get. Options are:
+             - `battery` - battery charge as a percentage
+             - `disk` - total, free, available bytes on disk
+             - `id` - unique id of the device
+             - `os` - name of the os
+             - `process` - list of running processes (same as ps)
+             - `systime` - system time of the device
+             - `uptime` - uptime of the device
+
+            If `directive` is `None`, will return all available information
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADB constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        directives = ['battery', 'disk', 'id', 'os', 'process', 'systime',
+                      'uptime']
+
+        if (directive in directives):
+            directives = [directive]
+
+        info = {}
+        if 'battery' in directives:
+            info['battery'] = self.get_battery_percentage(timeout=timeout)
+        if 'disk' in directives:
+            info['disk'] = self.shell_output('df /data /system /sdcard',
+                                             timeout=timeout).splitlines()
+        if 'id' in directives:
+            info['id'] = self.command_output(['get-serialno'], timeout=timeout)
+        if 'os' in directives:
+            info['os'] = self.shell_output('getprop ro.build.display.id',
+                                           timeout=timeout)
+        if 'process' in directives:
+            ps = self.shell_output('ps', timeout=timeout)
+            info['process'] = ps.splitlines()
+        if 'systime' in directives:
+            info['systime'] = self.shell_output('date', timeout=timeout)
+        if 'uptime' in directives:
+            uptime = self.shell_output('uptime', timeout=timeout)
+            if uptime:
+                m = re.match('up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})',
+                             uptime)
+                if m:
+                    uptime = '%d days %d hours %d minutes %d seconds' % tuple(
+                        [int(g or 0) for g in m.groups()[1:]])
+                info['uptime'] = uptime
+        return info
--- a/testing/mozbase/mozdevice/mozdevice/adb_android.py
+++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py
@@ -5,35 +5,45 @@
 import os
 import re
 import time
 
 from adb import ADBDevice, ADBError
 from distutils.version import StrictVersion
 
 
-class ADBAndroidMixin(object):
-    """Mixin to extend ADB with Android-specific functionality"""
+class ADBAndroid(ADBDevice):
+    """ADBAndroid implements :class:`ADBDevice` providing Android-specific
+    functionality.
+
+    ::
+
+       from mozdevice import ADBAndroid
+
+       adbdevice = ADBAndroid()
+       print adbdevice.list_files("/mnt/sdcard")
+       if adbdevice.process_exist("org.mozilla.fennec"):
+           print "Fennec is running"
+    """
 
     # Informational methods
 
     def get_battery_percentage(self, timeout=None):
         """Returns the battery charge as a percentage.
 
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADBDevice constructor is used.
         :type timeout: integer or None
         :returns: battery charge as a percentage.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         level = None
         scale = None
         percentage = 0
         cmd = "dumpsys battery"
         re_parameter = re.compile(r'\s+(\w+):\s+(\d+)')
         lines = self.shell_output(cmd, timeout=timeout).split('\r')
         for line in lines:
@@ -62,17 +72,16 @@ class ADBAndroidMixin(object):
             in seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         self.command_output(["wait-for-device"], timeout=timeout)
         pm_error_string = "Error: Could not access the Package Manager"
         pm_list_commands = ["packages", "permission-groups", "permissions",
                             "instrumentation", "features", "libraries"]
         ready_path = os.path.join(self.test_root, "ready")
         for attempt in range(self._device_ready_retry_attempts):
             failure = 'Unknown failure'
@@ -115,68 +124,41 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         try:
             self.shell_output('svc power stayon true', timeout=timeout)
         except ADBError, e:
             # Executing this via adb shell errors, but not interactively.
             # Any other exitcode is a real error.
             if 'exitcode: 137' not in e.message:
                 raise
             self._logger.warning('Unable to set power stayon true: %s' % e)
 
-    def reboot(self, timeout=None):
-        """Reboots the device.
-
-        This method uses the Android only package manager to determine
-        if the device is ready after the reboot.
-
-        :param timeout: The maximum time in
-            seconds for any spawned adb process to complete before
-            throwing an ADBTimeoutError.
-            This timeout is per adb call. The total time spent
-            may exceed this value. If it is not specified, the value
-            set in the ADB constructor is used.
-        :type timeout: integer or None
-        :raises: * ADBTimeoutError
-                 * ADBError
-
-        reboot() reboots the device, issues an adb wait-for-device in order to
-        wait for the device to complete rebooting, then calls is_device_ready()
-        to determine if the device has completed booting.
-
-        """
-        self.command_output(["reboot"], timeout=timeout)
-        self.command_output(["wait-for-device"], timeout=timeout)
-        return self.is_device_ready(timeout=timeout)
-
     # Application management methods
 
     def install_app(self, apk_path, timeout=None):
         """Installs an app on the device.
 
         :param str apk_path: The apk file name to be installed.
         :param timeout: The maximum time in
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         data = self.command_output(["install", apk_path], timeout=timeout)
         if data.find('Success') == -1:
             raise ADBError("install failed for %s. Got: %s" %
                            (apk_path, data))
 
     def is_app_installed(self, app_name, timeout=None):
         """Returns True if an app is installed on the device.
@@ -186,17 +168,16 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         pm_error_string = 'Error: Could not access the Package Manager'
         data = self.shell_output("pm list package %s" % app_name, timeout=timeout)
         if pm_error_string in data:
             raise ADBError(pm_error_string)
         if app_name not in data:
             return False
         return True
@@ -221,17 +202,16 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         # If fail_if_running is True, we throw an exception here. Only one
         # instance of an application can be running at once on Android,
         # starting a new instance may not be what we want depending on what
         # we want to do
         if fail_if_running and self.process_exist(app_name, timeout=timeout):
             raise ADBError("Only one instance of an application may be running "
                            "at once")
@@ -282,17 +262,16 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         extras = {}
 
         if moz_env:
             # moz_env is expected to be a dictionary of environment variables:
             # Fennec itself will set them when launched
             for (env_count, (env_key, env_val)) in enumerate(moz_env.iteritems()):
                 extras["env" + str(env_count)] = env_key + "=" + env_val
@@ -322,17 +301,16 @@ class ADBAndroidMixin(object):
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :param bool root: Flag specifying if the command should be
             executed as root.
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         version = self.shell_output("getprop ro.build.version.release",
                                     timeout=timeout, root=root)
         if StrictVersion(version) >= StrictVersion('3.0'):
             self.shell_output("am force-stop %s" % app_name,
                               timeout=timeout, root=root)
         else:
             num_tries = 0
@@ -362,17 +340,16 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         if self.is_app_installed(app_name, timeout=timeout):
             data = self.command_output(["uninstall", app_name], timeout=timeout)
             if data.find('Success') == -1:
                 self._logger.debug('uninstall_app failed: %s' % data)
                 raise ADBError("uninstall failed for %s. Got: %s" % (app_name, data))
             if reboot:
                 self.reboot(timeout=timeout)
@@ -386,32 +363,13 @@ class ADBAndroidMixin(object):
             seconds for any spawned adb process to complete before
             throwing an ADBTimeoutError.
             This timeout is per adb call. The total time spent
             may exceed this value. If it is not specified, the value
             set in the ADB constructor is used.
         :type timeout: integer or None
         :raises: * ADBTimeoutError
                  * ADBError
-
         """
         output = self.command_output(["install", "-r", apk_path],
                                      timeout=timeout)
         self.reboot(timeout=timeout)
         return output
-
-
-class ADBAndroid(ADBDevice, ADBAndroidMixin):
-    """ADBAndroid provides all of the methods of :class:`mozdevice.ADB` with
-    Android specific extensions useful for that platform.
-
-    ::
-
-        from mozdevice import ADBAndroid as ADBDevice
-
-        adb = ADBDevice(...)
-
-        if adb.is_device_ready():
-            adb.install_app("/tmp/build.apk")
-            adb.launch_fennec("org.mozilla.fennec")
-
-    """
-    pass
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozdevice/mozdevice/adb_b2g.py
@@ -0,0 +1,122 @@
+# 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/.
+
+import traceback
+
+import mozfile
+
+from adb import ADBDevice, ADBError
+
+
+class ADBB2G(ADBDevice):
+    """ADBB2G implements :class:`ADBDevice` providing B2G-specific
+    functionality.
+
+    ::
+
+       from mozdevice import ADBB2G
+
+       adbdevice = ADBB2G()
+       print adbdevice.list_files("/mnt/sdcard")
+       if adbdevice.process_exist("b2g"):
+           print "B2G is running"
+    """
+
+    def get_battery_percentage(self, timeout=None):
+        """Returns the battery charge as a percentage.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :returns: battery charge as a percentage.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        with mozfile.NamedTemporaryFile() as tf:
+            self.pull('/sys/class/power_supply/battery/capacity', tf.name,
+                      timeout=timeout)
+            try:
+                with open(tf.name) as tf2:
+                    return tf2.read().splitlines()[0]
+            except Exception as e:
+                raise ADBError(traceback.format_exception_only(
+                    type(e), e)[0].strip())
+
+    def get_memory_total(self, timeout=None):
+        """Returns the total memory available with units.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADBDevice constructor is used.
+        :returns: memory total with units.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        meminfo = {}
+        with mozfile.NamedTemporaryFile() as tf:
+            self.pull('/proc/meminfo', tf.name, timeout=timeout)
+            try:
+                with open(tf.name) as tf2:
+                    for line in tf2.read().splitlines():
+                        key, value = line.split(':')
+                        meminfo[key] = value.strip()
+            except Exception as e:
+                raise ADBError(traceback.format_exception_only(
+                    type(e), e)[0].strip())
+        return meminfo['MemTotal']
+
+    def get_info(self, directive=None, timeout=None):
+        """
+        Returns a dictionary of information strings about the device.
+
+        :param directive: information you want to get. Options are:
+             - `battery` - battery charge as a percentage
+             - `disk` - total, free, available bytes on disk
+             - `id` - unique id of the device
+             - `memtotal` - total memory available on the device
+             - `os` - name of the os
+             - `process` - list of running processes (same as ps)
+             - `systime` - system time of the device
+             - `uptime` - uptime of the device
+
+            If `directive` is `None`, will return all available information
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADB constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        info = super(ADBB2G, self).get_info(directive=directive,
+                                            timeout=timeout)
+
+        directives = ['memtotal']
+        if (directive in directives):
+            directives = [directive]
+
+        if 'memtotal' in directives:
+            info['memtotal'] = self.get_memory_total(timeout=timeout)
+        return info
+
+    def is_device_ready(self, timeout=None):
+        """Returns True if the device is ready.
+
+        :param timeout: optional integer specifying the maximum time in
+            seconds for any spawned adb process to complete before
+            throwing an ADBTimeoutError.
+            This timeout is per adb call. The total time spent
+            may exceed this value. If it is not specified, the value
+            set in the ADB constructor is used.
+        :raises: * ADBTimeoutError
+                 * ADBError
+        """
+        return self.shell_bool('ls /sbin', timeout=timeout)
--- a/testing/mozharness/configs/releases/bouncer_firefox_release.py
+++ b/testing/mozharness/configs/releases/bouncer_firefox_release.py
@@ -18,16 +18,20 @@ config = {
                 "macosx64": {
                     "path": "/firefox/releases/%(version)s/mac/:lang/Firefox%%20%(version)s.dmg",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
                 "opensolaris-i386": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-i386.tar.bz2",
                     "bouncer-platform": "opensolaris-i386",
                 },
                 "opensolaris-sparc": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-sparc.tar.bz2",
                     "bouncer-platform": "opensolaris-sparc",
                 },
@@ -57,16 +61,20 @@ config = {
                 "macosx64": {
                     "path": "/firefox/releases/%(version)s/mac/:lang/Firefox%%20%(version)s.dmg",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
                 "opensolaris-i386": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-i386.tar.bz2",
                     "bouncer-platform": "opensolaris-i386",
                 },
                 "opensolaris-sparc": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-sparc.tar.bz2",
                     "bouncer-platform": "opensolaris-sparc",
                 },
@@ -84,16 +92,20 @@ config = {
             "product-name": "Firefox-%(version)s-stub",
             "ssl-only": True,
             "add-locales": True,
             "paths": {
                 "win32": {
                     "path": "/firefox/releases/%(version)s/win32/:lang/Firefox%%20Setup%%20Stub%%20%(version)s.exe",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20Stub%%20%(version)s.exe",
+                    "bouncer-platform": "win64",
+                },
             },
         },
         "complete-mar": {
             "product-name": "Firefox-%(version)s-Complete",
             "ssl-only": False,
             "add-locales": True,
             "paths": {
                 "linux": {
@@ -107,16 +119,20 @@ config = {
                 "macosx64": {
                     "path": "/firefox/releases/%(version)s/update/mac/:lang/firefox-%(version)s.complete.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/releases/%(version)s/update/win32/:lang/firefox-%(version)s.complete.mar",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/releases/%(version)s/update/win64/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "win64",
+                },
                 "opensolaris-i386": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-i386.complete.mar",
                     "bouncer-platform": "opensolaris-i386",
                 },
                 "opensolaris-sparc": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(version)s.en-US.opensolaris-sparc.complete.mar",
                     "bouncer-platform": "opensolaris-sparc",
                 },
@@ -146,16 +162,20 @@ config = {
                 "macosx64": {
                     "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/mac/:lang/firefox-%(version)s.complete.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/win32/:lang/firefox-%(version)s.complete.mar",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/win64/:lang/firefox-%(version)s.complete.mar",
+                    "bouncer-platform": "win64",
+                },
             },
         },
     },
     "partials": {
         "releases-dir": {
             "product-name": "Firefox-%(version)s-Partial-%(prev_version)s",
             "ssl-only": False,
             "add-locales": True,
@@ -171,16 +191,20 @@ config = {
                 "macosx64": {
                     "path": "/firefox/releases/%(version)s/update/mac/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/releases/%(version)s/update/win32/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/releases/%(version)s/update/win64/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "win64",
+                },
                 "opensolaris-i386": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(prev_version)s-%(version)s.en-US.opensolaris-i386.partial.mar",
                     "bouncer-platform": "opensolaris-i386",
                 },
                 "opensolaris-sparc": {
                     "path": "/firefox/releases/%(version)s/contrib/solaris_tarball/firefox-%(prev_version)s-%(version)s.en-US.opensolaris-sparc.partial.mar",
                     "bouncer-platform": "opensolaris-sparc",
                 },
@@ -210,12 +234,16 @@ config = {
                 "macosx64": {
                     "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/mac/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "osx",
                 },
                 "win32": {
                     "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/win32/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
                     "bouncer-platform": "win",
                 },
+                "win64": {
+                    "path": "/firefox/candidates/%(version)s-candidates/build%(build_number)s/update/win64/:lang/firefox-%(prev_version)s-%(version)s.partial.mar",
+                    "bouncer-platform": "win64",
+                },
             },
         },
     },
 }
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -853,16 +853,20 @@ or run without that action (ie: --no-{ac
                 # with python, not with bash so we need to fix the slashes here
                 env['MOZ_SIGN_CMD'] = moz_sign_cmd.replace('\\', '\\\\\\\\')
             else:
                 self.warning("signing disabled because MOZ_SIGNING_SERVERS is not set")
 
         # to activate the right behaviour in mozonfigs while we transition
         if c.get('enable_release_promotion'):
             env['ENABLE_RELEASE_PROMOTION'] = "1"
+            update_channel = c.get('update_channel', self.branch)
+            self.info("Release promotion update channel: %s"
+                      % (update_channel,))
+            env["MOZ_UPDATE_CHANNEL"] = update_channel
 
         # we can't make env an attribute of self because env can change on
         # every call for reasons like MOZ_SIGN_CMD
         return env
 
     def query_mach_build_env(self, multiLocale=None):
         c = self.config
         if multiLocale is None and self.query_is_nightly():
--- a/testing/mozharness/scripts/release/antivirus.py
+++ b/testing/mozharness/scripts/release/antivirus.py
@@ -150,17 +150,18 @@ class AntivirusScan(BaseScript, Virtuale
         def worker(item):
             source, destination = item
 
             self.info("Downloading {} to {}".format(source, destination))
             key = bucket.get_key(source)
             return retry(key.get_contents_to_filename,
                          args=(destination, ),
                          sleeptime=5, max_sleeptime=60,
-                         retry_exceptions=(S3CopyError, S3ResponseError))
+                         retry_exceptions=(S3CopyError, S3ResponseError,
+                                           IOError))
 
         def find_release_files():
             candidates_prefix = self._get_candidates_prefix()
             self.info("Getting key names from candidates")
             for key in bucket.list(prefix=candidates_prefix):
                 keyname = key.name
                 if self._matches_exclude(keyname):
                     self.debug("Excluding {}".format(keyname))
--- a/testing/web-platform/harness/wptrunner/browsers/b2g.py
+++ b/testing/web-platform/harness/wptrunner/browsers/b2g.py
@@ -187,17 +187,17 @@ class B2GBrowser(Browser):
 
 class B2GExecutorBrowser(ExecutorBrowser):
     # The following methods are called from a different process
     def __init__(self, *args, **kwargs):
         ExecutorBrowser.__init__(self, *args, **kwargs)
 
         import sys, subprocess
 
-        self.device = mozdevice.ADBDevice()
+        self.device = mozdevice.ADBB2G()
         self.device.forward("tcp:%s" % self.marionette_port,
                             "tcp:2828")
         self.executor = None
         self.marionette = None
         self.gaia_device = None
         self.gaia_apps = None
 
     def after_connect(self, executor):
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -108,17 +108,17 @@ public:
   bool    mOnlyChromeDispatch : 1;
   // If mWantReplyFromContentProcess is true, the event will be redispatched
   // in the parent process after the content process has handled it. Useful
   // for when the parent process need the know first how the event was used
   // by content before handling it itself.
   bool mWantReplyFromContentProcess : 1;
   // The event's action will be handled by APZ. The main thread should not
   // perform its associated action. This is currently only relevant for
-  // wheel events.
+  // wheel and touch events.
   bool mHandledByAPZ : 1;
 
   // If the event is being handled in target phase, returns true.
   inline bool InTargetPhase() const
   {
     return (mInBubblingPhase && mInCapturePhase);
   }
 
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -81,16 +81,17 @@ MouseInput::TransformToLocal(const gfx::
   mLocalOrigin = *point;
 
   return true;
 }
 
 MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
   : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.timeStamp,
               aTouchEvent.modifiers)
+  , mHandledByAPZ(aTouchEvent.mFlags.mHandledByAPZ)
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "Can only copy from WidgetTouchEvent on main thread");
 
   switch (aTouchEvent.mMessage) {
     case eTouchStart:
       mType = MULTITOUCH_START;
       break;
@@ -158,16 +159,17 @@ MultiTouchInput::ToWidgetTouchEvent(nsIW
   WidgetTouchEvent event(true, touchEventMessage, aWidget);
   if (touchEventMessage == eVoidEvent) {
     return event;
   }
 
   event.modifiers = this->modifiers;
   event.time = this->mTime;
   event.timeStamp = this->mTimeStamp;
+  event.mFlags.mHandledByAPZ = mHandledByAPZ;
 
   for (size_t i = 0; i < mTouches.Length(); i++) {
     *event.touches.AppendElement() = mTouches[i].ToNewDOMTouch();
   }
 
   return event;
 }
 
@@ -200,16 +202,17 @@ MultiTouchInput::ToWidgetMouseEvent(nsIW
   const SingleTouchData& firstTouch = mTouches[0];
   event.refPoint.x = firstTouch.mScreenPoint.x;
   event.refPoint.y = firstTouch.mScreenPoint.y;
 
   event.time = mTime;
   event.button = WidgetMouseEvent::eLeftButton;
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   event.modifiers = modifiers;
+  event.mFlags.mHandledByAPZ = mHandledByAPZ;
 
   if (mouseEventMessage != eMouseMove) {
     event.clickCount = 1;
   }
 
   return event;
 }
 
@@ -228,16 +231,17 @@ MultiTouchInput::IndexOfTouch(int32_t aT
 // the B2G emulator we can only receive mouse events, but we need to be able
 // to pan correctly. To do this, we convert the events into a format that the
 // panning code can handle. This code is very limited and only supports
 // SingleTouchData. It also sends garbage for the identifier, radius, force
 // and rotation angle.
 MultiTouchInput::MultiTouchInput(const WidgetMouseEvent& aMouseEvent)
   : InputData(MULTITOUCH_INPUT, aMouseEvent.time, aMouseEvent.timeStamp,
               aMouseEvent.modifiers)
+  , mHandledByAPZ(aMouseEvent.mFlags.mHandledByAPZ)
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "Can only copy from WidgetMouseEvent on main thread");
   switch (aMouseEvent.mMessage) {
   case eMouseDown:
     mType = MULTITOUCH_START;
     break;
   case eMouseMove:
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -203,30 +203,33 @@ public:
     MULTITOUCH_START,
     MULTITOUCH_MOVE,
     MULTITOUCH_END,
     MULTITOUCH_CANCEL
   };
 
   MultiTouchInput(MultiTouchType aType, uint32_t aTime, TimeStamp aTimeStamp,
                   Modifiers aModifiers)
-    : InputData(MULTITOUCH_INPUT, aTime, aTimeStamp, aModifiers),
-      mType(aType)
+    : InputData(MULTITOUCH_INPUT, aTime, aTimeStamp, aModifiers)
+    , mType(aType)
+    , mHandledByAPZ(false)
   {
   }
 
   MultiTouchInput()
     : InputData(MULTITOUCH_INPUT)
+    , mHandledByAPZ(false)
   {
   }
 
   MultiTouchInput(const MultiTouchInput& aOther)
     : InputData(MULTITOUCH_INPUT, aOther.mTime,
                 aOther.mTimeStamp, aOther.modifiers)
     , mType(aOther.mType)
+    , mHandledByAPZ(aOther.mHandledByAPZ)
   {
     mTouches.AppendElements(aOther.mTouches);
   }
 
   explicit MultiTouchInput(const WidgetTouchEvent& aTouchEvent);
   WidgetTouchEvent ToWidgetTouchEvent(nsIWidget* aWidget) const;
   WidgetMouseEvent ToWidgetMouseEvent(nsIWidget* aWidget) const;
 
@@ -241,16 +244,17 @@ public:
   // SingleTouchData. It also sends garbage for the identifier, radius, force
   // and rotation angle.
   explicit MultiTouchInput(const WidgetMouseEvent& aMouseEvent);
 
   bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   MultiTouchType mType;
   nsTArray<SingleTouchData> mTouches;
+  bool mHandledByAPZ;
 };
 
 class MouseInput : public InputData
 {
 public:
   enum MouseType
   {
     MOUSE_MOVE,
--- a/widget/TouchEvents.h
+++ b/widget/TouchEvents.h
@@ -169,16 +169,17 @@ public:
     : WidgetInputEvent(aOther.mFlags.mIsTrusted, aOther.mMessage, aOther.widget,
                        eTouchEventClass)
   {
     modifiers = aOther.modifiers;
     time = aOther.time;
     timeStamp = aOther.timeStamp;
     touches.AppendElements(aOther.touches);
     mFlags.mCancelable = mMessage != eTouchCancel;
+    mFlags.mHandledByAPZ = aOther.mFlags.mHandledByAPZ;
     MOZ_COUNT_CTOR(WidgetTouchEvent);
   }
 
   WidgetTouchEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget)
     : WidgetInputEvent(aIsTrusted, aMessage, aWidget, eTouchEventClass)
   {
     MOZ_COUNT_CTOR(WidgetTouchEvent);
     mFlags.mCancelable = mMessage != eTouchCancel;
--- a/widget/cocoa/OSXNotificationCenter.mm
+++ b/widget/cocoa/OSXNotificationCenter.mm
@@ -247,43 +247,43 @@ OSXNotificationCenter::ShowAlertNotifica
   nsAlertsUtils::GetSourceHostPort(aPrincipal, hostPort);
   nsCOMPtr<nsIStringBundle> bundle;
   nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   nsresult rv = sbs->CreateBundle("chrome://alerts/locale/alert.properties", getter_AddRefs(bundle));
 
   if (!hostPort.IsEmpty()) {
     const char16_t* formatStrings[] = { hostPort.get() };
     nsXPIDLString notificationSource;
-    bundle->FormatStringFromName(NS_LITERAL_STRING("source.label").get(),
+    bundle->FormatStringFromName(MOZ_UTF16("source.label"),
                                  formatStrings,
                                  ArrayLength(formatStrings),
                                  getter_Copies(notificationSource));
     notification.subtitle = nsCocoaUtils::ToNSString(notificationSource);
   }
 
   notification.informativeText = nsCocoaUtils::ToNSString(aAlertText);
   notification.soundName = NSUserNotificationDefaultSoundName;
   notification.hasActionButton = NO;
 
   // If this is not an application/extension alert, show additional actions dealing with permissions.
   if (nsAlertsUtils::IsActionablePrincipal(aPrincipal)) {
     if (NS_SUCCEEDED(rv)) {
       nsXPIDLString closeButtonTitle, actionButtonTitle, disableButtonTitle, settingsButtonTitle;
-      bundle->GetStringFromName(NS_LITERAL_STRING("closeButton.title").get(),
+      bundle->GetStringFromName(MOZ_UTF16("closeButton.title"),
                                 getter_Copies(closeButtonTitle));
-      bundle->GetStringFromName(NS_LITERAL_STRING("actionButton.label").get(),
+      bundle->GetStringFromName(MOZ_UTF16("actionButton.label"),
                                 getter_Copies(actionButtonTitle));
       if (!hostPort.IsEmpty()) {
         const char16_t* formatStrings[] = { hostPort.get() };
-        bundle->FormatStringFromName(NS_LITERAL_STRING("webActions.disableForOrigin.label").get(),
+        bundle->FormatStringFromName(MOZ_UTF16("webActions.disableForOrigin.label"),
                                      formatStrings,
                                      ArrayLength(formatStrings),
                                      getter_Copies(disableButtonTitle));
       }
-      bundle->GetStringFromName(NS_LITERAL_STRING("webActions.settings.label").get(),
+      bundle->GetStringFromName(MOZ_UTF16("webActions.settings.label"),
                                 getter_Copies(settingsButtonTitle));
 
       notification.hasActionButton = YES;
       notification.otherButtonTitle = nsCocoaUtils::ToNSString(closeButtonTitle);
       notification.actionButtonTitle = nsCocoaUtils::ToNSString(actionButtonTitle);
       [(NSObject*)notification setValue:@(YES) forKey:@"_showsButtons"];
       [(NSObject*)notification setValue:@(YES) forKey:@"_alwaysShowAlternateActionMenu"];
       [(NSObject*)notification setValue:@[
--- a/widget/cocoa/nsFilePicker.mm
+++ b/widget/cocoa/nsFilePicker.mm
@@ -116,17 +116,17 @@ NSView* nsFilePicker::GetAccessoryView()
   NSString* label = @"Format:";
 
   // Try to get the localized string.
   nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   nsCOMPtr<nsIStringBundle> bundle;
   nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle));
   if (NS_SUCCEEDED(rv)) {
     nsXPIDLString locaLabel;
-    bundle->GetStringFromName(NS_LITERAL_STRING("formatLabel").get(),
+    bundle->GetStringFromName(MOZ_UTF16("formatLabel"),
 			      getter_Copies(locaLabel));
     if (locaLabel) {
       label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get())
                                       length:locaLabel.Length()];
     }
   }
 
   // set up label text field
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1207,62 +1207,62 @@ AsyncFaviconDataReady::OnComplete(nsIURI
     size.width = surface->GetSize().width;
     size.height = surface->GetSize().height;
     dataSurface = surface->GetDataSurface();
     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   }
 
   // Allocate a new buffer that we own and can use out of line in
   // another thread.
-  uint8_t *data = SurfaceToPackedBGRA(dataSurface);
+  UniquePtr<uint8_t[]> data = SurfaceToPackedBGRA(dataSurface);
   if (!data) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   int32_t stride = 4 * size.width;
   int32_t dataLength = stride * size.height;
 
   // AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
-  nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(path, data,
+  nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(path, Move(data),
                                                             dataLength,
                                                             stride,
                                                             size.width,
                                                             size.height,
                                                             mURLShortcut);
   mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 #endif
 
 // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed in
 AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(const nsAString &aIconPath,
-                                                 uint8_t *aBuffer,
+                                                 UniquePtr<uint8_t[]> aBuffer,
                                                  uint32_t aBufferLength,
                                                  uint32_t aStride,
                                                  uint32_t aWidth,
                                                  uint32_t aHeight,
                                                  const bool aURLShortcut) :
   mURLShortcut(aURLShortcut),
   mIconPath(aIconPath),
-  mBuffer(aBuffer),
+  mBuffer(Move(aBuffer)),
   mBufferLength(aBufferLength),
   mStride(aStride),
   mWidth(aWidth),
   mHeight(aHeight)
 {
 }
 
 NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run()
 {
   NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
 
   // Note that since we're off the main thread we can't use
   // gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
   RefPtr<DataSourceSurface> surface =
-    Factory::CreateWrappingDataSourceSurface(mBuffer, mStride,
+    Factory::CreateWrappingDataSourceSurface(mBuffer.get(), mStride,
                                              IntSize(mWidth, mHeight),
                                              SurfaceFormat::B8G8R8A8);
 
   FILE* file = fopen(NS_ConvertUTF16toUTF8(mIconPath).get(), "wb");
   if (!file) {
     // Maybe the directory doesn't exist; try creating it, then fopen again.
     nsresult rv = NS_ERROR_FAILURE;
     nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -31,16 +31,17 @@
 #endif
 #include "nsIDownloader.h"
 #include "nsIURI.h"
 #include "nsIWidget.h"
 #include "nsIThread.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/UniquePtr.h"
 
 /**
  * NS_INLINE_DECL_IUNKNOWN_REFCOUNTING should be used for defining and
  * implementing AddRef() and Release() of IUnknown interface.
  * This depends on xpcom/glue/nsISupportsImpl.h.
  */
 
 #define NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(_class)                           \
@@ -454,25 +455,25 @@ class AsyncEncodeAndWriteIcon : public n
 {
 public:
   const bool mURLShortcut;
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
   // Warning: AsyncEncodeAndWriteIcon assumes ownership of the aData buffer passed in
   AsyncEncodeAndWriteIcon(const nsAString &aIconPath,
-                          uint8_t *aData, uint32_t aDataLen, uint32_t aStride,
-                          uint32_t aWidth, uint32_t aHeight,
+                          UniquePtr<uint8_t[]> aData, uint32_t aDataLen,
+                          uint32_t aStride, uint32_t aWidth, uint32_t aHeight,
                           const bool aURLShortcut);
 
 private:
   virtual ~AsyncEncodeAndWriteIcon();
 
   nsAutoString mIconPath;
-  nsAutoArrayPtr<uint8_t> mBuffer;
+  UniquePtr<uint8_t[]> mBuffer;
   HMODULE sDwmDLL;
   uint32_t mBufferLength;
   uint32_t mStride;
   uint32_t mWidth;
   uint32_t mHeight;
 };
 
 
--- a/widget/windows/nsWindowGfx.cpp
+++ b/widget/windows/nsWindowGfx.cpp
@@ -635,30 +635,31 @@ nsresult nsWindowGfx::CreateIcon(imgICon
     dataSurface = surface->GetDataSurface();
     NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
     mappedOK = dataSurface->Map(DataSourceSurface::MapType::READ, &map);
   }
   NS_ENSURE_TRUE(dataSurface && mappedOK, NS_ERROR_FAILURE);
   MOZ_ASSERT(dataSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
 
   uint8_t* data = nullptr;
-  nsAutoArrayPtr<uint8_t> autoDeleteArray;
+  UniquePtr<uint8_t[]> autoDeleteArray;
   if (map.mStride == BytesPerPixel(dataSurface->GetFormat()) * iconSize.width) {
     // Mapped data is already packed
     data = map.mData;
   } else {
     // We can't use map.mData since the pixels are not packed (as required by
     // CreateDIBitmap, which is called under the DataToBitmap call below).
     //
     // We must unmap before calling SurfaceToPackedBGRA because it needs access
     // to the pixel data.
     dataSurface->Unmap();
     map.mData = nullptr;
 
-    data = autoDeleteArray = SurfaceToPackedBGRA(dataSurface);
+    autoDeleteArray = SurfaceToPackedBGRA(dataSurface);
+    data = autoDeleteArray.get();
     NS_ENSURE_TRUE(data, NS_ERROR_FAILURE);
   }
 
   HBITMAP bmp = DataToBitmap(data, iconSize.width, -iconSize.height, 32);
   uint8_t* a1data = Data32BitTo1Bit(data, iconSize.width, iconSize.height);
   if (map.mData) {
     dataSurface->Unmap();
   }
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -186,17 +186,17 @@ nsresult GetInstallYear(uint32_t& aYear)
   }
 
   nsAutoRegKey key(hKey);
 
   DWORD type = 0;
   time_t raw_time = 0;
   DWORD time_size = sizeof(time_t);
 
-  status = RegQueryValueExW(hKey, NS_LITERAL_STRING("InstallDate").get(),
+  status = RegQueryValueExW(hKey, L"InstallDate",
                             nullptr, &type, (LPBYTE)&raw_time, &time_size);
 
   if (status != ERROR_SUCCESS) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (type != REG_DWORD) {
     return NS_ERROR_UNEXPECTED;
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -567,17 +567,17 @@ ParseManifest(NSLocationType aType, File
     nsTextFormatter::ssprintf(osVersion, MOZ_UTF16("%ld.%ld"),
                               info.dwMajorVersion,
                               info.dwMinorVersion);
   }
 #pragma warning(pop)
 #elif defined(MOZ_WIDGET_COCOA)
   SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor();
   SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor();
-  nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(),
+  nsTextFormatter::ssprintf(osVersion, MOZ_UTF16("%ld.%ld"),
                             majorVersion,
                             minorVersion);
 #elif defined(MOZ_WIDGET_GTK)
   nsTextFormatter::ssprintf(osVersion, MOZ_UTF16("%ld.%ld"),
                             gtk_major_version,
                             gtk_minor_version);
 #elif defined(MOZ_WIDGET_ANDROID)
   bool isTablet = false;