Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 24 Sep 2015 16:46:34 -0700
changeset 264300 eee4266046984718e4daa99d94ce820f3fd86d32
parent 264299 e9aea2b7d9fd1e4c4507ce2bb8459d306e04b497 (current diff)
parent 264241 e22824771c7f6e72980fd6a2fee9bc49dfbc2c31 (diff)
child 264302 737517ce8115b2b5ecea745ff52918a7192dd092
push id65590
push userkwierso@gmail.com
push dateFri, 25 Sep 2015 00:14:23 +0000
treeherdermozilla-inbound@0ab67cace54f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
testing/taskcluster/tasks/job_flags.yml
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -405,16 +405,18 @@
 @RESPATH@/components/PermissionSettings.js
 @RESPATH@/components/PermissionSettings.manifest
 @RESPATH@/components/PermissionPromptService.js
 @RESPATH@/components/PermissionPromptService.manifest
 @RESPATH@/components/AlarmsManager.js
 @RESPATH@/components/AlarmsManager.manifest
 @RESPATH@/components/FeedProcessor.manifest
 @RESPATH@/components/FeedProcessor.js
+@RESPATH@/components/PackagedAppUtils.manifest
+@RESPATH@/components/PackagedAppUtils.js
 @RESPATH@/components/BrowserFeeds.manifest
 @RESPATH@/components/FeedConverter.js
 @RESPATH@/components/FeedWriter.js
 @RESPATH@/components/fuelApplication.manifest
 @RESPATH@/components/fuelApplication.js
 @RESPATH@/components/WebContentConverter.js
 @RESPATH@/components/BrowserComponents.manifest
 @RESPATH@/components/nsBrowserContentHandler.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -364,16 +364,18 @@
 @RESPATH@/components/ChromeNotifications.js
 @RESPATH@/components/ChromeNotifications.manifest
 @RESPATH@/components/ConsoleAPI.manifest
 @RESPATH@/components/ConsoleAPIStorage.js
 @RESPATH@/components/BrowserElementParent.manifest
 @RESPATH@/components/BrowserElementParent.js
 @RESPATH@/components/FeedProcessor.manifest
 @RESPATH@/components/FeedProcessor.js
+@RESPATH@/components/PackagedAppUtils.js
+@RESPATH@/components/PackagedAppUtils.manifest
 @RESPATH@/browser/components/BrowserFeeds.manifest
 @RESPATH@/browser/components/FeedConverter.js
 @RESPATH@/browser/components/FeedWriter.js
 @RESPATH@/browser/components/fuelApplication.manifest
 @RESPATH@/browser/components/fuelApplication.js
 @RESPATH@/browser/components/WebContentConverter.js
 @RESPATH@/browser/components/BrowserComponents.manifest
 @RESPATH@/browser/components/nsBrowserContentHandler.js
--- a/devtools/shared/heapsnapshot/HeapSnapshot.h
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.h
@@ -7,16 +7,17 @@
 #define mozilla_devtools_HeapSnapshot__
 
 #include "js/HashTable.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/devtools/DeserializedNode.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/RefCounted.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 
 #include "CoreDump.pb.h"
 #include "nsCOMPtr.h"
 #include "nsCRTGlue.h"
 #include "nsCycleCollectionParticipant.h"
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -937,16 +937,17 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
   NS_INTERFACE_MAP_ENTRY(nsILoadContext)
   NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
   NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
   NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
   NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
+  NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 NS_IMETHODIMP
 nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
 {
   NS_PRECONDITION(aSink, "null out param");
 
   *aSink = nullptr;
@@ -14055,8 +14056,18 @@ nsDocShell::InFrameSwap()
   do {
     if (shell->mInFrameSwap) {
       return true;
     }
     shell = shell->GetParentDocshell();
   } while (shell);
   return false;
 }
+
+NS_IMETHODIMP
+nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
+{
+  nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
+  if (doc) {
+    doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
+  }
+  return NS_OK;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -53,16 +53,17 @@
 #include "nsIWebShellServices.h"
 #include "nsILinkHandler.h"
 #include "nsIClipboardCommands.h"
 #include "nsITabParent.h"
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 #include "Units.h"
+#include "nsIDeprecationWarner.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
 typedef uint32_t ScreenOrientationInternal;
 } // namespace dom
 } // namespace mozilla
 
@@ -140,16 +141,17 @@ class nsDocShell final
   , public nsIWebPageDescriptor
   , public nsIAuthPromptProvider
   , public nsILoadContext
   , public nsIWebShellServices
   , public nsILinkHandler
   , public nsIClipboardCommands
   , public nsIDOMStorageManager
   , public nsINetworkInterceptController
+  , public nsIDeprecationWarner
   , public mozilla::SupportsWeakPtr<nsDocShell>
 {
   friend class nsDSURIContentListener;
 
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsDocShell)
 
   nsDocShell();
@@ -171,16 +173,17 @@ public:
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_NSIREFRESHURI
   NS_DECL_NSICONTENTVIEWERCONTAINER
   NS_DECL_NSIWEBPAGEDESCRIPTOR
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSICLIPBOARDCOMMANDS
   NS_DECL_NSIWEBSHELLSERVICES
   NS_DECL_NSINETWORKINTERCEPTCONTROLLER
+  NS_DECL_NSIDEPRECATIONWARNER
   NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
 
   NS_IMETHOD Stop() override
   {
     // Need this here because otherwise nsIWebNavigation::Stop
     // overrides the docloader's Stop()
     return nsDocLoader::Stop();
   }
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -247,16 +247,28 @@ this.AlarmService = {
       aAlarm.alarmFiredCb(this._publicAlarm(aAlarm));
     }
   },
 
   _onAlarmFired: function _onAlarmFired() {
     debug("_onAlarmFired()");
 
     if (this._currentAlarm) {
+      let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
+
+      // If a alarm fired before the actual time that the current
+      // alarm should occur, we reset this current alarm.
+      if (currentAlarmTime > Date.now()) {
+        let currentAlarm = this._currentAlarm;
+        this._currentAlarm = currentAlarm;
+
+        this._debugCurrentAlarm();
+        return;
+      }
+
       this._removeAlarmFromDb(this._currentAlarm.id, null);
       this._notifyAlarmObserver(this._currentAlarm);
       this._currentAlarm = null;
     }
 
     // Reset the next alarm from the queue.
     let alarmQueue = this._alarmQueue;
     while (alarmQueue.length > 0) {
@@ -409,28 +421,37 @@ this.AlarmService = {
         debug("Callback after adding alarm in database.");
 
         aNewAlarm['id'] = aNewId;
 
         // Now that the alarm has been added to the database, we can tack on
         // the non-serializable callback to the in-memory object.
         aNewAlarm['alarmFiredCb'] = aAlarmFiredCb;
 
+        // If the new alarm already expired at this moment, we directly
+        // notify this alarm
+        let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
+        if (aNewAlarmTime < Date.now()) {
+          aSuccessCb(aNewId);
+          this._removeAlarmFromDb(aNewAlarm.id, null);
+          this._notifyAlarmObserver(aNewAlarm);
+          return;
+        }
+
         // If there is no alarm being set in system, set the new alarm.
         if (this._currentAlarm == null) {
           this._currentAlarm = aNewAlarm;
           this._debugCurrentAlarm();
           aSuccessCb(aNewId);
           return;
         }
 
         // If the new alarm is earlier than the current alarm, swap them and
         // push the previous alarm back to the queue.
         let alarmQueue = this._alarmQueue;
-        let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
         let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
         if (aNewAlarmTime < currentAlarmTime) {
           alarmQueue.unshift(this._currentAlarm);
           this._currentAlarm = aNewAlarm;
           this._debugCurrentAlarm();
           aSuccessCb(aNewId);
           return;
         }
--- a/dom/apps/StoreTrustAnchor.jsm
+++ b/dom/apps/StoreTrustAnchor.jsm
@@ -11,16 +11,17 @@ this.EXPORTED_SYMBOLS = [
   "TrustedRootCertificate"
 ];
 
 const APP_TRUSTED_ROOTS= ["AppMarketplaceProdPublicRoot",
                           "AppMarketplaceProdReviewersRoot",
                           "AppMarketplaceDevPublicRoot",
                           "AppMarketplaceDevReviewersRoot",
                           "AppMarketplaceStageRoot",
+                          "PrivilegedPackageRoot",
                           "AppXPCShellRoot"];
 
 this.TrustedRootCertificate = {
   _index: Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot,
   get index() {
     return this._index;
   },
   set index(aIndex) {
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -36,8 +36,9 @@ DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
 DEPRECATED_OPERATION(DataContainerEvent)
 DEPRECATED_OPERATION(Window_Controllers)
 DEPRECATED_OPERATION(ImportXULIntoContent)
 DEPRECATED_OPERATION(PannerNodeDoppler)
+DEPRECATED_OPERATION(AppCache)
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -153,18 +153,20 @@ Window_ControllersWarning=window.control
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
 # LOCALIZATION NOTE: Do not translate "IndexedDB".
 IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
 # LOCALIZATION NOTE: Do not translate Will-change, %1$S,%2$S are numbers.
 IgnoringWillChangeOverBudgetWarning=Will-change memory consumption is too high. Budget limit is the document surface area multiplied by %1$S (%2$S px). Occurrences of will-change over the budget will be ignored.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker".
 HittingMaxWorkersPerDomain=A ServiceWorker could not be started immediately because other documents in the same origin are already using the maximum number of workers. The ServiceWorker is now queued and will be started after some of the other workers have completed.
-# LOCALIZATION NOTE: Do no translate "setVelocity", "PannerNode", "AudioListener", "speedOfSound" and "dopplerFactor"
+# LOCALIZATION NOTE: Do not translate "setVelocity", "PannerNode", "AudioListener", "speedOfSound" and "dopplerFactor"
 PannerNodeDopplerWarning=Use of setVelocity on the PannerNode and AudioListener, and speedOfSound and dopplerFactor on the AudioListener are deprecated and those members will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/AudioListener#Deprecated_features
+# LOCALIZATION NOTE: Do not translate "Application Cache API", "AppCache" and "ServiceWorker".
+AppCacheWarning=The Application Cache API (AppCache) is deprecated and will be removed at a future date.  Please consider using ServiceWorker for offline support.
 # LOCALIZATION NOTE: Do not translate "Worker".
 EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker".
 InterceptionFailed=ServiceWorker network interception failed due to an unexpected error.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "opaque", or "Response".
 OpaqueInterceptionDisabled=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while opaque interception is disabled.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "FetchEvent.request.type", "same-origin", "cors", "no-cors", "opaque", "Response", or "RequestMode".
 BadOpaqueInterceptionRequestMode=A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while the FetchEvent.request.type was either "same-origin" or "cors". Opaque Response objects are only valid when the RequestMode is "no-cors".
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -925,18 +925,20 @@ MediaDecoderStateMachine::OnVideoDecoded
           // We are doing a fastSeek, but we ended up *before* the previous
           // playback position. This is surprising UX, so switch to an accurate
           // seek and decode to the seek target. This is not conformant to the
           // spec, fastSeek should always be fast, but until we get the time to
           // change all Readers to seek to the keyframe after the currentTime
           // in this case, we'll just decode forward. Bug 1026330.
           mCurrentSeek.mTarget.mType = SeekTarget::Accurate;
         }
-        if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint) {
-          // Non-precise seek; we can stop the seek at the first sample.
+        if (mCurrentSeek.mTarget.mType == SeekTarget::PrevSyncPoint ||
+            mPendingSeek.Exists()) {
+          // Non-precise seek; or a pending seek exists ; we can stop the seek
+          // at the first sample.
           Push(video, MediaData::VIDEO_DATA);
         } else {
           // We're doing an accurate seek. We still need to discard
           // MediaData up to the one containing exact seek target.
           if (NS_FAILED(DropVideoUpToSeekTarget(video))) {
             DecodeError();
             return;
           }
@@ -2723,61 +2725,65 @@ nsresult
 MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   nsRefPtr<AudioData> audio(aSample->As<AudioData>());
   MOZ_ASSERT(audio &&
              mCurrentSeek.Exists() &&
              mCurrentSeek.mTarget.mType == SeekTarget::Accurate);
 
-  CheckedInt64 startFrame = UsecsToFrames(audio->mTime,
-                                          mInfo.mAudio.mRate);
-  CheckedInt64 targetFrame = UsecsToFrames(mCurrentSeek.mTarget.mTime,
-                                           mInfo.mAudio.mRate);
-  if (!startFrame.isValid() || !targetFrame.isValid()) {
+  CheckedInt64 sampleDuration =
+    FramesToUsecs(audio->mFrames, mInfo.mAudio.mRate);
+  if (!sampleDuration.isValid()) {
     return NS_ERROR_FAILURE;
   }
-  if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
+
+  if (audio->mTime + sampleDuration.value() <= mCurrentSeek.mTarget.mTime) {
     // Our seek target lies after the frames in this AudioData. Don't
     // push it onto the audio queue, and keep decoding forwards.
     return NS_OK;
   }
-  if (startFrame.value() > targetFrame.value()) {
+
+  if (audio->mTime > mCurrentSeek.mTarget.mTime) {
     // The seek target doesn't lie in the audio block just after the last
     // audio frames we've seen which were before the seek target. This
     // could have been the first audio data we've seen after seek, i.e. the
     // seek terminated after the seek target in the audio stream. Just
     // abort the audio decode-to-target, the state machine will play
     // silence to cover the gap. Typically this happens in poorly muxed
     // files.
     DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
     Push(audio, MediaData::AUDIO_DATA);
     return NS_OK;
   }
 
   // The seek target lies somewhere in this AudioData's frames, strip off
   // any frames which lie before the seek target, so we'll begin playback
   // exactly at the seek target.
-  NS_ASSERTION(targetFrame.value() >= startFrame.value(),
+  NS_ASSERTION(mCurrentSeek.mTarget.mTime >= audio->mTime,
                "Target must at or be after data start.");
-  NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames,
+  NS_ASSERTION(mCurrentSeek.mTarget.mTime < audio->mTime + sampleDuration.value(),
                "Data must end after target.");
 
-  int64_t framesToPrune = targetFrame.value() - startFrame.value();
-  if (framesToPrune > audio->mFrames) {
+  CheckedInt64 framesToPrune =
+    UsecsToFrames(mCurrentSeek.mTarget.mTime - audio->mTime, mInfo.mAudio.mRate);
+  if (!framesToPrune.isValid()) {
+    return NS_ERROR_FAILURE;
+  }
+  if (framesToPrune.value() > audio->mFrames) {
     // We've messed up somehow. Don't try to trim frames, the |frames|
     // variable below will overflow.
     DECODER_WARN("Can't prune more frames that we have!");
     return NS_ERROR_FAILURE;
   }
-  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
+  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
   uint32_t channels = audio->mChannels;
   nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
   memcpy(audioData.get(),
-         audio->mAudioData.get() + (framesToPrune * channels),
+         audio->mAudioData.get() + (framesToPrune.value() * channels),
          frames * channels * sizeof(AudioDataValue));
   CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
   if (!duration.isValid()) {
     return NS_ERROR_FAILURE;
   }
   nsRefPtr<AudioData> data(new AudioData(audio->mOffset,
                                          mCurrentSeek.mTarget.mTime,
                                          duration.value(),
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1480,30 +1480,31 @@ MediaManager::EnumerateRawDevices(uint64
   }));
   return p.forget();
 }
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
+  mPrefs.mFreq   = 1000; // 1KHz test tone
   mPrefs.mWidth  = 0; // adaptive default
   mPrefs.mHeight = 0; // adaptive default
   mPrefs.mFPS    = MediaEngine::DEFAULT_VIDEO_FPS;
   mPrefs.mMinFPS = MediaEngine::DEFAULT_VIDEO_MIN_FPS;
   nsresult rv;
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
     if (branch) {
       GetPrefs(branch, nullptr);
     }
   }
-  LOG(("%s: default prefs: %dx%d @%dfps (min %d)", __FUNCTION__,
-       mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
+  LOG(("%s: default prefs: %dx%d @%dfps (min %d), %dHz test tones", __FUNCTION__,
+       mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS, mPrefs.mFreq));
 }
 
 NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
 
 /* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
 
 #ifdef DEBUG
 /* static */ bool
@@ -2507,16 +2508,17 @@ MediaManager::GetPrefBool(nsIPrefBranch 
 
 void
 MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData)
 {
   GetPref(aBranch, "media.navigator.video.default_width", aData, &mPrefs.mWidth);
   GetPref(aBranch, "media.navigator.video.default_height", aData, &mPrefs.mHeight);
   GetPref(aBranch, "media.navigator.video.default_fps", aData, &mPrefs.mFPS);
   GetPref(aBranch, "media.navigator.video.default_minfps", aData, &mPrefs.mMinFPS);
+  GetPref(aBranch, "media.navigator.audio.fake_frequency", aData, &mPrefs.mFreq);
 }
 
 nsresult
 MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   const char16_t* aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread");
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -2539,16 +2541,17 @@ MediaManager::Observe(nsISupports* aSubj
     obs->RemoveObserver(this, "getUserMedia:revoke");
 
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->RemoveObserver("media.navigator.video.default_width", this);
       prefs->RemoveObserver("media.navigator.video.default_height", this);
       prefs->RemoveObserver("media.navigator.video.default_fps", this);
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
+      prefs->RemoveObserver("media.navigator.audio.fake_frequency", this);
     }
 
     // Close off any remaining active windows.
     GetActiveWindows()->Clear();
     mActiveCallbacks.Clear();
     mCallIds.Clear();
     {
       MutexAutoLock lock(mMutex);
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -20,30 +20,39 @@
 #include "nsIServiceManager.h"
 
 #include <stdint.h>
 
 namespace mozilla {
 
 using layers::PlanarYCbCrImage;
 
+static inline CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv) {
+  int64_t major = aValue / aDiv;
+  int64_t remainder = aValue % aDiv;
+  return CheckedInt64(remainder) * aMul / aDiv + major * aMul;
+}
+
 // Converts from number of audio frames to microseconds, given the specified
 // audio rate.
 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
-  return (CheckedInt64(aFrames) * USECS_PER_S) / aRate;
+  return SaferMultDiv(aFrames, USECS_PER_S, aRate);
 }
 
 media::TimeUnit FramesToTimeUnit(int64_t aFrames, uint32_t aRate) {
-  return (media::TimeUnit::FromMicroseconds(aFrames) * USECS_PER_S) / aRate;
+  int64_t major = aFrames / aRate;
+  int64_t remainder = aFrames % aRate;
+  return media::TimeUnit::FromMicroseconds(major) * USECS_PER_S +
+    (media::TimeUnit::FromMicroseconds(remainder) * USECS_PER_S) / aRate;
 }
 
 // Converts from microseconds to number of audio frames, given the specified
 // audio rate.
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
-  return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S;
+  return SaferMultDiv(aUsecs, aRate, USECS_PER_S);
 }
 
 // Format TimeUnit as number of frames at given rate.
 CheckedInt64 TimeUnitToFrames(const media::TimeUnit& aTime, uint32_t aRate) {
   return UsecsToFrames(aTime.ToMicroseconds(), aRate);
 }
 
 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) {
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -49,16 +49,22 @@ public:
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_VIDEO_MIN_FPS = 10;
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
   static const int DEFAULT_169_VIDEO_WIDTH = 1280;
   static const int DEFAULT_169_VIDEO_HEIGHT = 720;
   static const int DEFAULT_AUDIO_TIMER_MS = 10;
 
+#ifndef MOZ_B2G
+  static const int DEFAULT_SAMPLE_RATE = 32000;
+#else
+  static const int DEFAULT_SAMPLE_RATE = 16000;
+#endif
+
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
@@ -192,16 +198,17 @@ protected:
  * Video source and friends.
  */
 class MediaEnginePrefs {
 public:
   int32_t mWidth;
   int32_t mHeight;
   int32_t mFPS;
   int32_t mMinFPS;
+  int32_t mFreq; // for test tones (fake:true)
 
   // mWidth and/or mHeight may be zero (=adaptive default), so use functions.
 
   int32_t GetWidth(bool aHD = false) const {
     return mWidth? mWidth : (mHeight?
                              (mHeight * GetDefWidth(aHD)) / GetDefHeight(aHD) :
                              GetDefWidth(aHD));
   }
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -21,17 +21,17 @@
 #include "AndroidBridge.h"
 #include "nsISupportsUtils.h"
 #endif
 
 #ifdef MOZ_WEBRTC
 #include "YuvStamper.h"
 #endif
 
-#define AUDIO_RATE 16000
+#define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE
 #define AUDIO_FRAME_LENGTH ((AUDIO_RATE * MediaEngine::DEFAULT_AUDIO_TIMER_MS) / 1000)
 namespace mozilla {
 
 using namespace mozilla::gfx;
 
 // Enable the testing flag fakeTracks and fake in MediaStreamConstraints, will
 // return you a MediaStream with additional fake video tracks and audio tracks.
 static const int kFakeVideoTrackCount = 2;
@@ -297,25 +297,26 @@ MediaEngineDefaultVideoSource::NotifyPul
   }
 }
 
 // generate 1k sine wave per second
 class SineWaveGenerator
 {
 public:
   static const int bytesPerSample = 2;
-  static const int millisecondsPerSecond = 1000;
-  static const int frequency = 1000;
+  static const int millisecondsPerSecond = PR_MSEC_PER_SEC;
 
-  explicit SineWaveGenerator(int aSampleRate) :
-    mTotalLength(aSampleRate / frequency),
+  explicit SineWaveGenerator(uint32_t aSampleRate, uint32_t aFrequency) :
+    mTotalLength(aSampleRate / aFrequency),
     mReadLength(0) {
-    MOZ_ASSERT(mTotalLength * frequency == aSampleRate);
+    // If we allow arbitrary frequencies, there's no guarantee we won't get rounded here
+    // We could include an error term and adjust for it in generation; not worth the trouble
+    //MOZ_ASSERT(mTotalLength * aFrequency == aSampleRate);
     mAudioBuffer = new int16_t[mTotalLength];
-    for(int i = 0; i < mTotalLength; i++) {
+    for (int i = 0; i < mTotalLength; i++) {
       // Set volume to -20db. It's from 32768.0 * 10^(-20/20) = 3276.8
       mAudioBuffer[i] = (3276.8f * sin(2 * M_PI * i / mTotalLength));
     }
   }
 
   // NOTE: only safely called from a single thread (MSG callback)
   void generate(int16_t* aBuffer, int16_t aLengthInSamples) {
     int16_t remaining = aLengthInSamples;
@@ -392,18 +393,19 @@ MediaEngineDefaultAudioSource::Allocate(
                                         const MediaEnginePrefs &aPrefs,
                                         const nsString& aDeviceId)
 {
   if (mState != kReleased) {
     return NS_ERROR_FAILURE;
   }
 
   mState = kAllocated;
-  // generate 1Khz sine wave
-  mSineGenerator = new SineWaveGenerator(AUDIO_RATE);
+  // generate sine wave (default 1KHz)
+  mSineGenerator = new SineWaveGenerator(AUDIO_RATE,
+                                         static_cast<uint32_t>(aPrefs.mFreq ? aPrefs.mFreq : 1000));
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Deallocate()
 {
   if (mState != kStopped && mState != kAllocated) {
     return NS_ERROR_FAILURE;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -132,16 +132,17 @@ public:
     : MediaEngineAudioSource(kReleased)
     , mVoiceEngine(aVoiceEnginePtr)
     , mMonitor("WebRTCMic.Monitor")
     , mThread(aThread)
     , mCapIndex(aIndex)
     , mChannel(-1)
     , mInitDone(false)
     , mStarted(false)
+    , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
     , mEchoOn(false), mAgcOn(false), mNoiseOn(false)
     , mEchoCancel(webrtc::kEcDefault)
     , mAGC(webrtc::kAgcDefault)
     , mNoiseSuppress(webrtc::kNsDefault)
     , mPlayoutDelay(0)
     , mNullTransport(nullptr) {
     MOZ_ASSERT(aVoiceEnginePtr);
     mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
@@ -220,16 +221,17 @@ private:
   int mChannel;
   TrackID mTrackID;
   bool mInitDone;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
+  uint32_t mSampleFrequency;
   bool mEchoOn, mAgcOn, mNoiseOn;
   webrtc::EcModes  mEchoCancel;
   webrtc::AgcModes mAGC;
   webrtc::NsModes  mNoiseSuppress;
   int32_t mPlayoutDelay;
 
   NullTransport *mNullTransport;
 };
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -14,19 +14,18 @@
 #undef FF
 #endif
 #include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
 
 #define CHANNELS 1
 #define ENCODING "L16"
 #define DEFAULT_PORT 5555
 
-#define SAMPLE_RATE 256000
-#define SAMPLE_FREQUENCY 16000
-#define SAMPLE_LENGTH ((SAMPLE_FREQUENCY*10)/1000)
+#define SAMPLE_RATE(freq) ((freq)*2*8) // bps, 16-bit samples
+#define SAMPLE_LENGTH(freq) (((freq)*10)/1000)
 
 // These are restrictions from the webrtc.org code
 #define MAX_CHANNELS 2
 #define MAX_SAMPLING_FREQ 48000 // Hz - multiple of 100
 
 #define MAX_AEC_FIFO_DEPTH 200 // ms - multiple of 10
 static_assert(!(MAX_AEC_FIFO_DEPTH % 10), "Invalid MAX_AEC_FIFO_DEPTH");
 
@@ -340,17 +339,17 @@ MediaEngineWebRTCMicrophoneSource::Start
   }
 
   {
     MonitorAutoLock lock(mMonitor);
     mSources.AppendElement(aStream);
   }
 
   AudioSegment* segment = new AudioSegment();
-  aStream->AddAudioTrack(aID, SAMPLE_FREQUENCY, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
+  aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
 
   // XXX Make this based on the pref.
   aStream->RegisterForAudioMixing();
   LOG(("Start audio for stream %p", aStream));
 
   if (mState == kStarted) {
     MOZ_ASSERT(aID == mTrackID);
     return NS_OK;
@@ -465,16 +464,19 @@ MediaEngineWebRTCMicrophoneSource::Init(
   if (mChannel < 0) {
     return;
   }
   mNullTransport = new NullTransport();
   if (mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) {
     return;
   }
 
+  mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE;
+  LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
+
   // Check for availability.
   ScopedCustomReleasePtr<webrtc::VoEHardware> ptrVoEHw(webrtc::VoEHardware::GetInterface(mVoiceEngine));
   if (!ptrVoEHw || ptrVoEHw->SetRecordingDevice(mCapIndex)) {
     return;
   }
 
 #ifndef MOZ_B2G
   // Because of the permission mechanism of B2G, we need to skip the status
@@ -490,19 +492,20 @@ MediaEngineWebRTCMicrophoneSource::Init(
   ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
   if (!ptrVoECodec) {
     return;
   }
 
   webrtc::CodecInst codec;
   strcpy(codec.plname, ENCODING);
   codec.channels = CHANNELS;
-  codec.rate = SAMPLE_RATE;
-  codec.plfreq = SAMPLE_FREQUENCY;
-  codec.pacsize = SAMPLE_LENGTH;
+  MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
+  codec.rate = SAMPLE_RATE(mSampleFrequency);
+  codec.plfreq = mSampleFrequency;
+  codec.pacsize = SAMPLE_LENGTH(mSampleFrequency);
   codec.pltype = 0; // Default payload type
 
   if (!ptrVoECodec->SetSendCodec(mChannel, codec)) {
     mInitDone = true;
   }
 }
 
 void
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -10,17 +10,16 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkGPSGeolocationProvider.h"
-#include "mozstumbler/MozStumbler.h"
 
 #include <pthread.h>
 #include <hardware/gps.h>
 
 #include "GeolocationUtil.h"
 #include "mozstumbler/MozStumbler.h"
 #include "mozilla/Constants.h"
 #include "mozilla/Preferences.h"
@@ -35,16 +34,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "prtime.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
+#include "mozstumbler/MozStumbler.h"
 #include "nsIIccInfo.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileCellInfo.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIRadioInterfaceLayer.h"
 #include "nsIIccService.h"
 #include "nsIDataCallManager.h"
@@ -143,17 +143,19 @@ GonkGPSGeolocationProvider::LocationCall
                                         location->latitude,
                                         location->longitude,
                                         location->accuracy);
   }
 
   nsRefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
   NS_DispatchToMainThread(event);
 
+#ifdef MOZ_B2G_RIL
   MozStumble(somewhere);
+#endif
 }
 
 void
 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
 {
   if (gDebug_isLoggingEnabled) {
     switch (status->status) {
       case GPS_STATUS_NONE:
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -30,34 +30,29 @@ XPIDL_SOURCES += [
     'nsIWorkerHolder.idl',
 ]
 
 XPIDL_MODULE = 'dom_system_gonk'
 
 EXPORTS += [
     'GeolocationUtil.h',
     'GonkGPSGeolocationProvider.h',
-    'mozstumbler/MozStumbler.h',
     'nsVolume.h',
     'nsVolumeService.h',
 ]
 UNIFIED_SOURCES += [
     'AudioChannelManager.cpp',
     'AudioManager.cpp',
     'AutoMounter.cpp',
     'AutoMounterSetting.cpp',
     'GeolocationUtil.cpp',
     'GonkGPSGeolocationProvider.cpp',
     'MozMtpDatabase.cpp',
     'MozMtpServer.cpp',
     'MozMtpStorage.cpp',
-    'mozstumbler/MozStumbler.cpp',
-    'mozstumbler/StumblerLogging.cpp',
-    'mozstumbler/UploadStumbleRunnable.cpp',
-    'mozstumbler/WriteStumbleOnThread.cpp',
     'NetIdManager.cpp',
     'NetworkUtils.cpp',
     'NetworkWorker.cpp',
     'nsVolume.cpp',
     'nsVolumeMountLock.cpp',
     'nsVolumeService.cpp',
     'nsVolumeStat.cpp',
     'OpenFileFinder.cpp',
@@ -88,16 +83,25 @@ EXTRA_COMPONENTS += [
     'TetheringService.js',
     'TetheringService.manifest',
 ]
 EXTRA_JS_MODULES += [
     'systemlibs.js',
 ]
 
 if CONFIG['MOZ_B2G_RIL']:
+    EXPORTS += [
+         'mozstumbler/MozStumbler.h',
+    ]
+    UNIFIED_SOURCES += [
+          'mozstumbler/MozStumbler.cpp',
+          'mozstumbler/StumblerLogging.cpp',
+          'mozstumbler/UploadStumbleRunnable.cpp',
+          'mozstumbler/WriteStumbleOnThread.cpp'
+    ]
     XPIDL_SOURCES += [
         'nsIDataCallInterfaceService.idl',
         'nsIDataCallManager.idl',
         'nsIGonkDataCallInterfaceService.idl',
         'nsIRadioInterfaceLayer.idl',
     ]
     EXTRA_COMPONENTS += [
         'DataCallManager.js',
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -3526,17 +3526,17 @@ nsHTMLEditor::SelectAll()
     rootContent = anchorContent->GetSelectionRootContent(ps);
   }
 
   NS_ENSURE_TRUE(rootContent, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Maybe<mozilla::dom::Selection::AutoApplyUserSelectStyle> userSelection;
+  Maybe<mozilla::dom::Selection::AutoUserInitiated> userSelection;
   if (!rootContent->IsEditable()) {
     userSelection.emplace(selection);
   }
   return selection->SelectAllChildren(rootElement);
 }
 
 
 // this will NOT find aAttribute unless aAttribute has a non-null value
--- a/gfx/2d/GenericRefCounted.h
+++ b/gfx/2d/GenericRefCounted.h
@@ -6,16 +6,17 @@
 // This header provides virtual, non-templated alternatives to MFBT's RefCounted<T>.
 // It intentionally uses MFBT coding style with the intention of moving there
 // should there be other use cases for it.
 
 #ifndef MOZILLA_GENERICREFCOUNTED_H_
 #define MOZILLA_GENERICREFCOUNTED_H_
 
 #include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
 
 namespace mozilla {
 
 /**
  * Common base class for GenericRefCounted and GenericAtomicRefCounted.
  *
  * Having this shared base class, common to both the atomic and non-atomic
  * cases, allows to have RefPtr's that don't care about whether the
--- a/gfx/layers/apz/src/AsyncPanZoomAnimation.h
+++ b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_AsyncPanZoomAnimation_h_
 #define mozilla_layers_AsyncPanZoomAnimation_h_
 
 #include "base/message_loop.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Vector.h"
 #include "FrameMetrics.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 namespace layers {
 
 class WheelScrollAnimation;
 
--- a/gfx/layers/apz/src/WheelScrollAnimation.cpp
+++ b/gfx/layers/apz/src/WheelScrollAnimation.cpp
@@ -1,16 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WheelScrollAnimation.h"
 
+#include "AsyncPanZoomController.h"
+#include "gfxPrefs.h"
+#include "nsPoint.h"
+
 namespace mozilla {
 namespace layers {
 
 WheelScrollAnimation::WheelScrollAnimation(AsyncPanZoomController& aApzc, const nsPoint& aInitialPosition)
   : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZSmoothScrollRepaintInterval()))
   , AsyncScrollBase(aInitialPosition)
   , mApzc(aApzc)
   , mFinalDestination(aInitialPosition)
--- a/gfx/layers/apz/src/WheelScrollAnimation.h
+++ b/gfx/layers/apz/src/WheelScrollAnimation.h
@@ -8,16 +8,18 @@
 #define mozilla_layers_WheelScrollAnimation_h_
 
 #include "AsyncPanZoomAnimation.h"
 #include "AsyncScrollBase.h"
 
 namespace mozilla {
 namespace layers {
 
+class AsyncPanZoomController;
+
 class WheelScrollAnimation
   : public AsyncPanZoomAnimation,
     public AsyncScrollBase
 {
 public:
   WheelScrollAnimation(AsyncPanZoomController& aApzc, const nsPoint& aInitialPosition);
 
   bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TextureHost.h"
 
 #include "CompositableHost.h"           // for CompositableHost
+#include "LayerScope.h"
 #include "LayersLogging.h"              // for AppendToString
 #include "mozilla/gfx/2D.h"             // for DataSourceSurface, Factory
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
@@ -18,16 +19,17 @@
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include "nsAString.h"
 #include "mozilla/nsRefPtr.h"                   // for nsRefPtr
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "mozilla/layers/PTextureParent.h"
 #include "mozilla/unused.h"
 #include <limits>
 #include "../opengl/CompositorOGL.h"
+#include "gfxPrefs.h"
 #include "gfxUtils.h"
 
 #ifdef MOZ_ENABLE_D3D10_LAYER
 #include "../d3d11/CompositorD3D11.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "../opengl/GrallocTextureClient.h"
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -173,32 +173,27 @@ CompositorChild::AllocPLayerTransactionC
                                              bool*)
 {
   MOZ_ASSERT(mCanSend);
   LayerTransactionChild* c = new LayerTransactionChild(aId);
   c->AddIPDLReference();
   return c;
 }
 
-/*static*/ PLDHashOperator
-CompositorChild::RemoveSharedMetricsForLayersId(const uint64_t& aKey,
-                                                nsAutoPtr<SharedFrameMetricsData>& aData,
-                                                void* aLayerTransactionChild)
-{
-  uint64_t childId = static_cast<LayerTransactionChild*>(aLayerTransactionChild)->GetId();
-  if (aData->GetLayersId() == childId) {
-    return PLDHashOperator::PL_DHASH_REMOVE;
-  }
-  return PLDHashOperator::PL_DHASH_NEXT;
-}
-
 bool
 CompositorChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor)
 {
-  mFrameMetricsTable.Enumerate(RemoveSharedMetricsForLayersId, actor);
+  uint64_t childId = static_cast<LayerTransactionChild*>(actor)->GetId();
+
+  for (auto iter = mFrameMetricsTable.Iter(); !iter.Done(); iter.Next()) {
+    nsAutoPtr<SharedFrameMetricsData>& data = iter.Data();
+    if (data->GetLayersId() == childId) {
+      iter.Remove();
+    }
+  }
   static_cast<LayerTransactionChild*>(actor)->ReleaseIPDLReference();
   return true;
 }
 
 bool
 CompositorChild::RecvInvalidateAll()
 {
   if (mLayerManager) {
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -169,20 +169,16 @@ private:
     // the shared FrameMetrics
     nsRefPtr<mozilla::ipc::SharedMemoryBasic> mBuffer;
     CrossProcessMutex* mMutex;
     uint64_t mLayersId;
     // Unique ID of the APZC that is sharing the FrameMetrics
     uint32_t mAPZCId;
   };
 
-  static PLDHashOperator RemoveSharedMetricsForLayersId(const uint64_t& aKey,
-                                                        nsAutoPtr<SharedFrameMetricsData>& aData,
-                                                        void* aLayerTransactionChild);
-
   nsRefPtr<ClientLayerManager> mLayerManager;
   // When not multi-process, hold a reference to the CompositorParent to keep it
   // alive. This reference should be null in multi-process.
   nsRefPtr<CompositorParent> mCompositorParent;
 
   // The ViewID of the FrameMetrics is used as the key for this hash table.
   // While this should be safe to use since the ViewID is unique
   nsClassHashtable<nsUint64HashKey, SharedFrameMetricsData> mFrameMetricsTable;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1110,32 +1110,29 @@ gfxFT2FontList::AppendFacesFromOmnijarEn
     if (aCache && !faceList.IsEmpty()) {
         aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
     }
 }
 
 // Called on each family after all fonts are added to the list;
 // this will sort faces to give priority to "standard" font files
 // if aUserArg is non-null (i.e. we're using it as a boolean flag)
-static PLDHashOperator
+static void
 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
                          nsRefPtr<gfxFontFamily>& aFamily,
-                         void* aUserArg)
+                         bool aSortFaces)
 {
     gfxFontFamily *family = aFamily.get();
-    bool sortFaces = (aUserArg != nullptr);
 
     family->SetHasStyles(true);
 
-    if (sortFaces) {
+    if (aSortFaces) {
         family->SortAvailableFonts();
     }
     family->CheckForSimpleFamily();
-
-    return PL_DHASH_NEXT;
 }
 
 void
 gfxFT2FontList::FindFonts()
 {
     gfxFontCache *fc = gfxFontCache::GetCache();
     if (fc)
         fc->AgeAllGenerations();
@@ -1152,18 +1149,27 @@ gfxFT2FontList::FindFonts()
         for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
             // We don't need to identify "standard" font files here,
             // as the faces are already sorted.
             AppendFaceFromFontListEntry(fonts[i], kUnknown);
         }
         // Passing null for userdata tells Finalize that it does not need
         // to sort faces (because they were already sorted by chrome,
         // so we just maintain the existing order)
-        mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
-        mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
+        for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            nsStringHashKey::KeyType key = iter.Key();
+            nsRefPtr<gfxFontFamily>& family = iter.Data();
+            FinalizeFamilyMemberList(key, family, /* aSortFaces */ false);
+        }
+        for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            nsStringHashKey::KeyType key = iter.Key();
+            nsRefPtr<gfxFontFamily>& family = iter.Data();
+            FinalizeFamilyMemberList(key, family, /* aSortFaces */ false );
+        }
+
         LOG(("got font list from chrome process: %d faces in %d families "
              "and %d in hidden families",
             fonts.Length(), mFontFamilies.Count(),
             mHiddenFontFamilies.Count()));
         return;
     }
 
     // Chrome process: get the cached list (if any)
@@ -1232,18 +1238,26 @@ gfxFT2FontList::FindFonts()
         if (NS_SUCCEEDED(rv)) {
             FindFontsInDir(localPath, &fnc, FT2FontFamily::kVisible);
         }
     }
 
     // Finalize the families by sorting faces into standard order
     // and marking "simple" families.
     // Passing non-null userData here says that we want faces to be sorted.
-    mFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
-    mHiddenFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsStringHashKey::KeyType key = iter.Key();
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
+    }
+    for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsStringHashKey::KeyType key = iter.Key();
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
+    }
 }
 
 void
 gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
                                FontNameCache *aFNC,
                                FT2FontFamily::Visibility aVisibility)
 {
     static const char* sStandardFonts[] = {
@@ -1323,69 +1337,46 @@ gfxFT2FontList::AppendFaceFromFontListEn
             }
         }
         family->AddFontEntry(fe);
 
         fe->CheckForBrokenFont(family);
     }
 }
 
-static PLDHashOperator
-AddFamilyToFontList(nsStringHashKey::KeyType aKey,
-                    nsRefPtr<gfxFontFamily>& aFamily,
-                    void* aUserArg)
-{
-    InfallibleTArray<FontListEntry>* fontlist =
-        reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
-
-    FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
-    family->AddFacesToFontList(fontlist, FT2FontFamily::kVisible);
-
-    return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator
-AddHiddenFamilyToFontList(nsStringHashKey::KeyType aKey,
-                          nsRefPtr<gfxFontFamily>& aFamily,
-                          void* aUserArg)
-{
-    InfallibleTArray<FontListEntry>* fontlist =
-        reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
-
-    FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
-    family->AddFacesToFontList(fontlist, FT2FontFamily::kHidden);
-
-    return PL_DHASH_NEXT;
-}
-
 void
 gfxFT2FontList::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
 {
-    mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
-    mHiddenFontFamilies.Enumerate(AddHiddenFamilyToFontList, retValue);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        auto family = static_cast<FT2FontFamily*>(iter.Data().get());
+        family->AddFacesToFontList(retValue, FT2FontFamily::kVisible);
+    }
+    for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        auto family = static_cast<FT2FontFamily*>(iter.Data().get());
+        family->AddFacesToFontList(retValue, FT2FontFamily::kHidden);
+    }
 }
 
 static void
 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
 {
     nsAutoTArray<nsString, 5> skiplist;
     gfxFontUtils::GetPrefsFontList(
         "font.whitelist.skip_default_features_space_check",
         skiplist);
     uint32_t numFonts = skiplist.Length();
     for (uint32_t i = 0; i < numFonts; i++) {
         ToLowerCase(skiplist[i]);
         aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
     }
 }
 
-static PLDHashOperator
+void
 PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
-                       nsRefPtr<gfxFontFamily>& aFamily,
-                       void* aUserArg)
+                       nsRefPtr<gfxFontFamily>& aFamily)
 {
     gfxFontFamily *family = aFamily.get();
 
     auto& faces = family->GetFontList();
     size_t count = faces.Length();
     for (size_t i = 0; i < count; ++i) {
         FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
         if (fe->mFTFontIndex != 0) {
@@ -1428,111 +1419,100 @@ PreloadAsUserFontFaces(nsStringHashKey::
         fe->mUserFontData->mRealName = fe->Name();
         fe->mUserFontData->mCRC32 = crc;
         fe->mUserFontData->mLength = buf.st_size;
 
         // Stash it persistently in the user-font cache.
         gfxUserFontSet::UserFontCache::CacheFont(
             fe, gfxUserFontSet::UserFontCache::kPersistent);
     }
-
-    return PL_DHASH_NEXT;
 }
 
 nsresult
 gfxFT2FontList::InitFontList()
 {
     // reset font lists
     gfxPlatformFontList::InitFontList();
     mHiddenFontFamilies.Clear();
-    
+
     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
 
     FindFonts();
 
-    mHiddenFontFamilies.Enumerate(PreloadAsUserFontFaces, this);
-
+    for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsStringHashKey::KeyType key = iter.Key();
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        PreloadAsUserFontFaces(key, family);
+    }
     return NS_OK;
 }
 
-struct FullFontNameSearch {
-    FullFontNameSearch(const nsAString& aFullName)
-        : mFullName(aFullName), mFontEntry(nullptr)
-    { }
-
-    nsString     mFullName;
-    FT2FontEntry *mFontEntry;
-};
-
-// callback called for each family name, based on the assumption that the 
+// called for each family name, based on the assumption that the
 // first part of the full name is the family name
-static PLDHashOperator
-FindFullName(nsStringHashKey::KeyType aKey,
-             nsRefPtr<gfxFontFamily>& aFontFamily,
-             void* userArg)
-{
-    FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg);
-
-    // does the family name match up to the length of the family name?
-    const nsString& family = aFontFamily->Name();
 
-    nsString fullNameFamily;
-    data->mFullName.Left(fullNameFamily, family.Length());
-
-    // if so, iterate over faces in this family to see if there is a match
-    if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
-        nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList();
-        int index, len = fontList.Length();
-        for (index = 0; index < len; index++) {
-            gfxFontEntry* fe = fontList[index];
-            if (!fe) {
-                continue;
-            }
-            if (fe->Name().Equals(data->mFullName,
-                                  nsCaseInsensitiveStringComparator())) {
-                data->mFontEntry = static_cast<FT2FontEntry*>(fe);
-                return PL_DHASH_STOP;
-            }
-        }
-    }
-
-    return PL_DHASH_NEXT;
-}
-
-gfxFontEntry* 
+gfxFontEntry*
 gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
                                 uint16_t aWeight,
                                 int16_t aStretch,
                                 bool aItalic)
 {
     // walk over list of names
-    FullFontNameSearch data(aFontName);
+    FT2FontEntry* fontEntry = nullptr;
+    nsString fullName(aFontName);
 
     // Note that we only check mFontFamilies here, not mHiddenFontFamilies;
     // hence @font-face { src:local(...) } will not find hidden fonts.
-    mFontFamilies.Enumerate(FindFullName, &data);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        // Check family name, based on the assumption that the
+        // first part of the full name is the family name
+        nsRefPtr<gfxFontFamily>& fontFamily = iter.Data();
+
+        // does the family name match up to the length of the family name?
+        const nsString& family = fontFamily->Name();
+        nsString fullNameFamily;
+
+        fullName.Left(fullNameFamily, family.Length());
 
-    if (!data.mFontEntry) {
+        // if so, iterate over faces in this family to see if there is a match
+        if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
+            nsTArray<nsRefPtr<gfxFontEntry> >& fontList = fontFamily->GetFontList();
+            int index, len = fontList.Length();
+            for (index = 0; index < len; index++) {
+                gfxFontEntry* fe = fontList[index];
+                if (!fe) {
+                    continue;
+                }
+                if (fe->Name().Equals(fullName,
+                                      nsCaseInsensitiveStringComparator())) {
+                    fontEntry = static_cast<FT2FontEntry*>(fe);
+                    goto searchDone;
+                }
+            }
+        }
+    }
+
+searchDone:
+    if (!fontEntry) {
         return nullptr;
     }
 
     // Clone the font entry so that we can then set its style descriptors
     // from the userfont entry rather than the actual font.
 
     // Ensure existence of mFTFace in the original entry
-    data.mFontEntry->CairoFontFace();
-    if (!data.mFontEntry->mFTFace) {
+    fontEntry->CairoFontFace();
+    if (!fontEntry->mFTFace) {
         return nullptr;
     }
 
     FT2FontEntry* fe =
-        FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace,
-                                      data.mFontEntry->mFilename.get(),
-                                      data.mFontEntry->mFTFontIndex,
-                                      data.mFontEntry->Name(), nullptr);
+        FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
+                                      fontEntry->mFilename.get(),
+                                      fontEntry->mFTFontIndex,
+                                      fontEntry->Name(), nullptr);
     if (fe) {
         fe->mItalic = aItalic;
         fe->mWeight = aWeight;
         fe->mStretch = aStretch;
         fe->mIsLocalUserFont = true;
     }
 
     return fe;
@@ -1564,26 +1544,20 @@ gfxFT2FontList::MakePlatformFont(const n
 {
     // The FT2 font needs the font data to persist, so we do NOT free it here
     // but instead pass ownership to the font entry.
     // Deallocation will happen later, when the font face is destroyed.
     return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
                                          aItalic, aFontData, aLength);
 }
 
-static PLDHashOperator
-AppendFamily(nsStringHashKey::KeyType aKey,
-             nsRefPtr<gfxFontFamily>& aFamily,
-             void* aUserArg)
-{
-    nsTArray<nsRefPtr<gfxFontFamily> > * familyArray =
-        reinterpret_cast<nsTArray<nsRefPtr<gfxFontFamily>>*>(aUserArg);
-
-    familyArray->AppendElement(aFamily);
-    return PL_DHASH_NEXT;
-}
-
 void
 gfxFT2FontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
 {
-    mFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
-    mHiddenFontFamilies.Enumerate(AppendFamily, &aFamilyArray);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        aFamilyArray.AppendElement(family);
+    }
+    for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        aFamilyArray.AppendElement(family);
+    }
 }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -423,17 +423,17 @@ LookupAlternateValues(gfxFontFeatureValu
 
 /* static */ void
 gfxFontShaper::MergeFontFeatures(
     const gfxFontStyle *aStyle,
     const nsTArray<gfxFontFeature>& aFontFeatures,
     bool aDisableLigatures,
     const nsAString& aFamilyName,
     bool aAddSmallCaps,
-    PLDHashOperator (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
+    void (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
     void* aHandleFeatureData)
 {
     uint32_t numAlts = aStyle->alternateValues.Length();
     const nsTArray<gfxFontFeature>& styleRuleFeatures =
         aStyle->featureSettings;
 
     // Bail immediately if nothing to do, which is the common case.
     if (styleRuleFeatures.IsEmpty() &&
@@ -525,17 +525,19 @@ gfxFontShaper::MergeFontFeatures(
     // add feature values from style rules
     count = styleRuleFeatures.Length();
     for (i = 0; i < count; i++) {
         const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
         mergedFeatures.Put(feature.mTag, feature.mValue);
     }
 
     if (mergedFeatures.Count() != 0) {
-        mergedFeatures.Enumerate(aHandleFeature, aHandleFeatureData);
+        for (auto iter = mergedFeatures.Iter(); !iter.Done(); iter.Next()) {
+            aHandleFeature(iter.Key(), iter.Data(), aHandleFeatureData);
+        }
     }
 }
 
 void
 gfxShapedText::SetupClusterBoundaries(uint32_t        aOffset,
                                       const char16_t *aString,
                                       uint32_t        aLength)
 {
@@ -1737,17 +1739,18 @@ gfxFont::DrawGlyphs(gfxShapedText       
                     const FontDrawParams&     aFontParams)
 {
     bool emittedGlyphs = false;
     GlyphBufferAzure buffer(aRunParams, aFontParams);
 
     gfxFloat& inlineCoord = aFontParams.isVerticalFont ? aPt->y : aPt->x;
 
     if (aRunParams.spacing) {
-        inlineCoord += aRunParams.direction * aRunParams.spacing[0].mBefore;
+        inlineCoord += aRunParams.isRTL ? -aRunParams.spacing[0].mBefore
+                                        : aRunParams.spacing[0].mBefore;
     }
 
     const gfxShapedText::CompressedGlyph *glyphData =
         &aShapedText->GetCharacterGlyphs()[aOffset];
 
     for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
         if (glyphData->IsSimpleGlyph()) {
             DrawOneGlyph(glyphData->GetSimpleGlyph(),
@@ -1816,27 +1819,27 @@ gfxFont::DrawGlyphs(gfxShapedText       
                         } else {
                             glyphXY.x += details->mXOffset;
                             glyphXY.y += details->mYOffset;
                         }
                         DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
                                      buffer, &emittedGlyphs);
                     }
 
-                    inlineCoord += aRunParams.direction * advance;
+                    inlineCoord += aRunParams.isRTL ? -advance : advance;
                 }
             }
         }
 
         if (aRunParams.spacing) {
             double space = aRunParams.spacing[i].mAfter;
             if (i + 1 < aCount) {
                 space += aRunParams.spacing[i + 1].mBefore;
             }
-            inlineCoord += aRunParams.direction * space;
+            inlineCoord += aRunParams.isRTL ? -space : space;
         }
     }
 
     return emittedGlyphs;
 }
 
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
@@ -1869,20 +1872,25 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
     if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
         sideways = true;
         aRunParams.context->Save();
         gfxPoint p(aPt->x * aRunParams.devPerApp,
                    aPt->y * aRunParams.devPerApp);
         const Metrics& metrics = GetMetrics(eHorizontal);
         // Get a matrix we can use to draw the (horizontally-shaped) textrun
         // with 90-degree CW rotation.
-        gfxMatrix mat = aRunParams.context->CurrentMatrix().
-            Translate(p).       // translate origin for rotation
-            Rotate(M_PI / 2.0). // turn 90deg clockwise
-            Translate(-p);      // undo the translation
+        const gfxFloat
+            rotation = (aOrientation ==
+                        gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
+                       ? -M_PI / 2.0 : M_PI / 2.0;
+        gfxMatrix mat =
+            aRunParams.context->CurrentMatrix().
+            Translate(p).     // translate origin for rotation
+            Rotate(rotation). // turn 90deg CCW (sideways-left) or CW (*-right)
+            Translate(-p);    // undo the translation
 
         // If we're drawing rotated horizontal text for an element styled
         // text-orientation:mixed, the dominant baseline will be vertical-
         // centered. So in this case, we need to adjust the position so that
         // the rotated horizontal text (which uses an alphabetic baseline) will
         // look OK when juxtaposed with upright glyphs (rendered on a centered
         // vertical baseline). The adjustment here is somewhat ad hoc; we
         // should eventually look for baseline tables[1] in the fonts and use
@@ -1982,17 +1990,24 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
         aRunParams.callbacks->NotifyGlyphPathEmitted();
     }
 
     aRunParams.dt->SetTransform(oldMat);
     aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
 
     if (sideways) {
         aRunParams.context->Restore();
-        *aPt = gfxPoint(origPt.x, origPt.y + (aPt->x - origPt.x));
+        // adjust updated aPt to account for the transform we were using
+        gfxFloat advance = aPt->x - origPt.x;
+        if (aOrientation ==
+            gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) {
+            *aPt = gfxPoint(origPt.x, origPt.y - advance);
+        } else {
+            *aPt = gfxPoint(origPt.x, origPt.y + advance);
+        }
     }
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const
 {
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -631,18 +631,18 @@ public:
     gfxFont *GetFont() const { return mFont; }
 
     static void
     MergeFontFeatures(const gfxFontStyle *aStyle,
                       const nsTArray<gfxFontFeature>& aFontFeatures,
                       bool aDisableLigatures,
                       const nsAString& aFamilyName,
                       bool aAddSmallCaps,
-                      PLDHashOperator (*aHandleFeature)(const uint32_t&,
-                                                        uint32_t&, void*),
+                      void (*aHandleFeature)(const uint32_t&,
+                                             uint32_t&, void*),
                       void* aHandleFeatureData);
 
 protected:
     // the font this shaper is working with. The font owns a nsAutoPtr reference
     // to this object, and will destroy it before it dies. Thus, mFont will always
     // be valid.
     gfxFont* MOZ_NON_OWNING_REF mFont;
 };
@@ -960,18 +960,29 @@ public:
         return orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED ||
                orient == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
 
     bool IsRightToLeft() const {
         return (GetFlags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
     }
 
+    bool IsSidewaysLeft() const {
+        return (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK) ==
+               gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
+    }
+
+    // Return true if the logical inline direction is reversed compared to
+    // normal physical coordinates (i.e. if it is leftwards or upwards)
+    bool IsInlineReversed() const {
+        return IsSidewaysLeft() != IsRightToLeft();
+    }
+
     gfxFloat GetDirection() const {
-        return IsRightToLeft() ? -1.0f : 1.0f;
+        return IsInlineReversed() ? -1.0f : 1.0f;
     }
 
     bool DisableLigatures() const {
         return (GetFlags() &
                 gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
     }
 
     bool TextIs8Bit() const {
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -141,23 +141,16 @@ gfxFontEntry::gfxFontEntry(const nsAStri
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0)
 {
     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
 }
 
-static PLDHashOperator
-DestroyHBSet(const uint32_t& aTag, hb_set_t*& aSet, void *aUserArg)
-{
-    hb_set_destroy(aSet);
-    return PL_DHASH_NEXT;
-}
-
 gfxFontEntry::~gfxFontEntry()
 {
     if (mCOLR) {
         hb_blob_destroy(mCOLR);
     }
 
     if (mCPAL) {
         hb_blob_destroy(mCPAL);
@@ -165,17 +158,20 @@ gfxFontEntry::~gfxFontEntry()
 
     // For downloaded fonts, we need to tell the user font cache that this
     // entry is being deleted.
     if (mIsDataUserFont) {
         gfxUserFontSet::UserFontCache::ForgetFont(this);
     }
 
     if (mFeatureInputs) {
-        mFeatureInputs->Enumerate(DestroyHBSet, nullptr);
+        for (auto iter = mFeatureInputs->Iter(); !iter.Done(); iter.Next()) {
+            hb_set_t*& set = iter.Data();
+            hb_set_destroy(set);
+        }
     }
 
     // By the time the entry is destroyed, all font instances that were
     // using it should already have been deleted, and so the HB and/or Gr
     // face objects should have been released.
     MOZ_ASSERT(!mHBFace);
     MOZ_ASSERT(!mGrFaceInitialized);
 }
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -67,26 +67,25 @@ MakeGraphiteLangTag(uint32_t aTag)
     return grLangTag;
 }
 
 struct GrFontFeatures {
     gr_face        *mFace;
     gr_feature_val *mFeatures;
 };
 
-static PLDHashOperator
+static void
 AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
 {
     GrFontFeatures *f = static_cast<GrFontFeatures*>(aUserArg);
 
     const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag);
     if (fref) {
         gr_fref_set_feature_value(fref, aValue, f->mFeatures);
     }
-    return PL_DHASH_NEXT;
 }
 
 bool
 gfxGraphiteShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1219,26 +1219,25 @@ HBUnicodeDecompose(hb_unicode_funcs_t *u
 
 #else // no ICU available, use the old nsUnicodeNormalizer
 
     return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
 
 #endif
 }
 
-static PLDHashOperator
+static void
 AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
 {
     nsTArray<hb_feature_t>* features = static_cast<nsTArray<hb_feature_t>*> (aUserArg);
 
     hb_feature_t feat = { 0, 0, 0, UINT_MAX };
     feat.tag = aTag;
     feat.value = aValue;
     features->AppendElement(feat);
-    return PL_DHASH_NEXT;
 }
 
 /*
  * gfxFontShaper override to initialize the text run using HarfBuzz
  */
 
 static hb_font_funcs_t * sHBFontFuncs = nullptr;
 static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -232,144 +232,103 @@ gfxPlatformFontList::InitFontList()
 
 void
 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
 }
 
-struct InitOtherNamesData {
-    InitOtherNamesData(gfxPlatformFontList *aFontList,
-                       TimeStamp aStartTime)
-        : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false)
-    {}
+#define OTHERNAMES_TIMEOUT 200
 
-    gfxPlatformFontList *mFontList;
-    TimeStamp mStartTime;
-    bool mTimedOut;
-};
-
-void 
+void
 gfxPlatformFontList::InitOtherFamilyNames()
 {
     if (mOtherFamilyNamesInitialized) {
         return;
     }
 
     TimeStamp start = TimeStamp::Now();
-
-    // iterate over all font families and read in other family names
-    InitOtherNamesData otherNamesData(this, start);
+    bool timedOut = false;
 
-    mFontFamilies.Enumerate(gfxPlatformFontList::InitOtherFamilyNamesProc,
-                            &otherNamesData);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        family->ReadOtherFamilyNames(this);
+        TimeDuration elapsed = TimeStamp::Now() - start;
+        if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
+            timedOut = true;
+            break;
+        }
+    }
 
-    if (!otherNamesData.mTimedOut) {
+    if (!timedOut) {
         mOtherFamilyNamesInitialized = true;
     }
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
                                    start, end);
 
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
         LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
                       elapsed.ToMilliseconds(),
-                      (otherNamesData.mTimedOut ? "timeout" : "")));
+                      (timedOut ? "timeout" : "")));
     }
 }
 
-#define OTHERNAMES_TIMEOUT 200
-
-PLDHashOperator
-gfxPlatformFontList::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
-                                              nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                              void* userArg)
-{
-    InitOtherNamesData *data = static_cast<InitOtherNamesData*>(userArg);
-
-    aFamilyEntry->ReadOtherFamilyNames(data->mFontList);
-    TimeDuration elapsed = TimeStamp::Now() - data->mStartTime;
-    if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
-        data->mTimedOut = true;
-        return PL_DHASH_STOP;
-    }
-    return PL_DHASH_NEXT;
-}
- 
-struct ReadFaceNamesData {
-    ReadFaceNamesData(gfxPlatformFontList *aFontList, TimeStamp aStartTime)
-        : mFontList(aFontList), mStartTime(aStartTime), mTimedOut(false),
-          mFirstChar(0)
-    {}
-
-    gfxPlatformFontList *mFontList;
-    TimeStamp mStartTime;
-    bool mTimedOut;
-
-    // if mFirstChar is not 0, only load facenames for families
-    // that start with this character
-    char16_t mFirstChar;
-};
+// time limit for loading facename lists (ms)
+#define NAMELIST_TIMEOUT  200
 
 gfxFontEntry*
 gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
 {
     TimeStamp start = TimeStamp::Now();
+    bool timedOut = false;
+    // if mFirstChar is not 0, only load facenames for families
+    // that start with this character
+    char16_t firstChar = 0;
     gfxFontEntry *lookup = nullptr;
 
-    ReadFaceNamesData faceNameListsData(this, start);
+    // iterate over familes starting with the same letter
+    firstChar = ToLowerCase(aFaceName.CharAt(0));
+
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsStringHashKey::KeyType key = iter.Key();
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
 
-    // iterate over familes starting with the same letter
-    faceNameListsData.mFirstChar = ToLowerCase(aFaceName.CharAt(0));
-    mFontFamilies.Enumerate(gfxPlatformFontList::ReadFaceNamesProc,
-                            &faceNameListsData);
+        // when filtering, skip names that don't start with the filter character
+        if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
+            continue;
+        }
+
+        family->ReadFaceNames(this, NeedFullnamePostscriptNames());
+
+        TimeDuration elapsed = TimeStamp::Now() - start;
+        if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
+           timedOut = true;
+           break;
+        }
+    }
+
     lookup = FindFaceName(aFaceName);
 
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
                                    start, end);
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = end - start;
         LOG_FONTINIT(("(fontinit) SearchFamiliesForFaceName took %8.2f ms %s %s",
                       elapsed.ToMilliseconds(),
                       (lookup ? "found name" : ""),
-                      (faceNameListsData.mTimedOut ? "timeout" : "")));
+                      (timedOut ? "timeout" : "")));
     }
 
     return lookup;
 }
 
-// time limit for loading facename lists (ms)
-#define NAMELIST_TIMEOUT  200
-
-PLDHashOperator
-gfxPlatformFontList::ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
-                                       nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                       void* userArg)
-{
-    ReadFaceNamesData *data = static_cast<ReadFaceNamesData*>(userArg);
-    gfxPlatformFontList *fc = data->mFontList;
-
-    // when filtering, skip names that don't start with the filter character
-    if (data->mFirstChar && ToLowerCase(aKey.CharAt(0)) != data->mFirstChar) {
-        return PL_DHASH_NEXT;
-    }
-
-    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
-
-    TimeDuration elapsed = TimeStamp::Now() - data->mStartTime;
-    if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
-        data->mTimedOut = true;
-        return PL_DHASH_STOP;
-    }
-    return PL_DHASH_NEXT;
-}
-
 gfxFontEntry*
 gfxPlatformFontList::FindFaceName(const nsAString& aFaceName)
 {
     gfxFontEntry *lookup;
 
     // lookup in name lookup tables, return null if not found
     if (mExtraNames &&
         ((lookup = mExtraNames->mPostscriptNames.GetWeak(aFaceName)) ||
@@ -444,94 +403,59 @@ gfxPlatformFontList::LoadBadUnderlineLis
 
 void
 gfxPlatformFontList::UpdateFontList()
 {
     InitFontList();
     RebuildLocalFonts();
 }
 
-struct FontListData {
-    FontListData(nsIAtom *aLangGroup,
-                 const nsACString& aGenericFamily,
-                 nsTArray<nsString>& aListOfFonts) :
-        mLangGroup(aLangGroup), mGenericFamily(aGenericFamily),
-        mListOfFonts(aListOfFonts) {}
-    nsIAtom *mLangGroup;
-    const nsACString& mGenericFamily;
-    nsTArray<nsString>& mListOfFonts;
-};
-
-PLDHashOperator
-gfxPlatformFontList::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
-                                             nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                             void *aUserArg)
-{
-    FontListData *data = static_cast<FontListData*>(aUserArg);
-
-    // use the first variation for now.  This data should be the same
-    // for all the variations and should probably be moved up to
-    // the Family
-    gfxFontStyle style;
-    style.language = data->mLangGroup;
-    bool needsBold;
-    nsRefPtr<gfxFontEntry> aFontEntry = aFamilyEntry->FindFontForStyle(style, needsBold);
-    NS_ASSERTION(aFontEntry, "couldn't find any font entry in family");
-    if (!aFontEntry)
-        return PL_DHASH_NEXT;
-
-    /* skip symbol fonts */
-    if (aFontEntry->IsSymbolFont())
-        return PL_DHASH_NEXT;
-
-    if (aFontEntry->SupportsLangGroup(data->mLangGroup) &&
-        aFontEntry->MatchesGenericFamily(data->mGenericFamily)) {
-        nsAutoString localizedFamilyName;
-        aFamilyEntry->LocalizedName(localizedFamilyName);
-        data->mListOfFonts.AppendElement(localizedFamilyName);
-    }
-
-    return PL_DHASH_NEXT;
-}
-
 void
 gfxPlatformFontList::GetFontList(nsIAtom *aLangGroup,
                                  const nsACString& aGenericFamily,
                                  nsTArray<nsString>& aListOfFonts)
 {
-    FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        // use the first variation for now.  This data should be the same
+        // for all the variations and should probably be moved up to
+        // the Family
+        gfxFontStyle style;
+        style.language = aLangGroup;
+        bool needsBold;
+        nsRefPtr<gfxFontEntry> fontEntry = family->FindFontForStyle(style, needsBold);
+        NS_ASSERTION(fontEntry, "couldn't find any font entry in family");
+        if (!fontEntry) {
+            continue;
+        }
 
-    mFontFamilies.Enumerate(gfxPlatformFontList::HashEnumFuncForFamilies, &data);
+        /* skip symbol fonts */
+        if (fontEntry->IsSymbolFont()) {
+            continue;
+        }
+
+        if (fontEntry->SupportsLangGroup(aLangGroup) &&
+            fontEntry->MatchesGenericFamily(aGenericFamily)) {
+            nsAutoString localizedFamilyName;
+            family->LocalizedName(localizedFamilyName);
+            aListOfFonts.AppendElement(localizedFamilyName);
+        }
+    }
 
     aListOfFonts.Sort();
     aListOfFonts.Compact();
 }
 
-struct FontFamilyListData {
-    explicit FontFamilyListData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray) 
-        : mFamilyArray(aFamilyArray)
-    {}
-
-    static PLDHashOperator AppendFamily(nsStringHashKey::KeyType aKey,
-                                        nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                        void *aUserArg)
-    {
-        FontFamilyListData *data = static_cast<FontFamilyListData*>(aUserArg);
-        data->mFamilyArray.AppendElement(aFamilyEntry);
-        return PL_DHASH_NEXT;
-    }
-
-    nsTArray<nsRefPtr<gfxFontFamily> >& mFamilyArray;
-};
-
 void
 gfxPlatformFontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
 {
-    FontFamilyListData data(aFamilyArray);
-    mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        aFamilyArray.AppendElement(family);
+    }
 }
 
 gfxFontEntry*
 gfxPlatformFontList::SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
                                            int32_t aRunScript,
                                            const gfxFontStyle* aStyle)
  {
     gfxFontEntry* fontEntry = nullptr;
@@ -611,28 +535,16 @@ gfxPlatformFontList::SystemFindFontForCh
 
     // track the script for which fallback occurred (incremented one make it
     // 1-based)
     Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT, aRunScript + 1);
 
     return fontEntry;
 }
 
-PLDHashOperator 
-gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry,
-     void *userArg)
-{
-    GlobalFontMatch *data = static_cast<GlobalFontMatch*>(userArg);
-
-    // evaluate all fonts in this family for a match
-    aFamilyEntry->FindFontForChar(data);
-
-    return PL_DHASH_NEXT;
-}
-
 #define NUM_FALLBACK_FONTS        8
 
 gfxFontEntry*
 gfxPlatformFontList::CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
                                         int32_t aRunScript,
                                         const gfxFontStyle* aMatchStyle,
                                         gfxFontFamily** aMatchedFamily)
 {
@@ -672,17 +584,21 @@ gfxPlatformFontList::GlobalFontFallback(
                                         const gfxFontStyle* aMatchStyle,
                                         uint32_t& aCmapCount,
                                         gfxFontFamily** aMatchedFamily)
 {
     // otherwise, try to find it among local fonts
     GlobalFontMatch data(aCh, aRunScript, aMatchStyle);
 
     // iterate over all font families to find a font that support the character
-    mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data);
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+      nsRefPtr<gfxFontFamily>& family = iter.Data();
+      // evaluate all fonts in this family for a match
+      family->FindFontForChar(&data);
+    }
 
     aCmapCount = data.mCmapsTested;
     *aMatchedFamily = data.mMatchedFamily;
 
     return data.mBestMatch;
 }
 
 #ifdef XP_WIN
@@ -880,32 +796,26 @@ gfxPlatformFontList::RemoveCmap(const gf
     // cmap needs to match the entry *and* be the same ptr before removing
     CharMapHashKey *found =
         mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
     if (found && found->GetKey() == aCharMap) {
         mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
     }
 }
 
-static PLDHashOperator AppendFamilyToList(nsStringHashKey::KeyType aKey,
-                                          nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                          void *aUserArg)
+void
+gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
 {
-    nsTArray<nsString> *familyNames = static_cast<nsTArray<nsString> *>(aUserArg);
-    familyNames->AppendElement(aFamilyEntry->Name());
-    return PL_DHASH_NEXT;
+    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+        nsRefPtr<gfxFontFamily>& family = iter.Data();
+        aFontFamilyNames.AppendElement(family->Name());
+    }
 }
 
 void
-gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
-{
-    mFontFamilies.Enumerate(AppendFamilyToList, &aFontFamilyNames);
-}
-
-void 
 gfxPlatformFontList::InitLoader()
 {
     GetFontFamilyNames(mFontInfo->mFontFamiliesToLoad);
     mStartIndex = 0;
     mNumFamilies = mFontInfo->mFontFamiliesToLoad.Length();
     memset(&(mFontInfo->mLoadStats), 0, sizeof(mFontInfo->mLoadStats));
 }
 
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -207,20 +207,16 @@ protected:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
 
     explicit gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true);
 
     static gfxPlatformFontList *sPlatformFontList;
 
-    static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey,
-                                               nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                               void* userArg);
-
     // Lookup family name in global family list without substitutions or
     // localized family name lookup. Used for common font fallback families.
     gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) {
         nsAutoString key;
         gfxFontFamily *familyEntry;
         GenerateFontListKey(aFamily, key);
         if ((familyEntry = mFontFamilies.GetWeak(key))) {
             return CheckFamily(familyEntry);
@@ -246,31 +242,21 @@ protected:
     virtual bool UsesSystemFallback() { return false; }
 
     // verifies that a family contains a non-zero font count
     gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
 
     // initialize localized family names
     void InitOtherFamilyNames();
 
-    static PLDHashOperator
-    InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
-                             nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                             void* userArg);
-
     // search through font families, looking for a given name, initializing
     // facename lists along the way. first checks all families with names
     // close to face name, then searchs all families if not found.
     gfxFontEntry* SearchFamiliesForFaceName(const nsAString& aFaceName);
 
-    static PLDHashOperator
-    ReadFaceNamesProc(nsStringHashKey::KeyType aKey,
-                      nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                      void* userArg);
-
     // helper method for finding fullname/postscript names in facename lists
     gfxFontEntry* FindFaceName(const nsAString& aFaceName);
 
     // look up a font by name, for cases where platform font list
     // maintains explicit mappings of fullname/psname ==> font
     virtual gfxFontEntry* LookupInFaceNameLists(const nsAString& aFontName);
 
     // commonly used fonts for which the name table should be loaded at startup
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -806,43 +806,43 @@ RasterImage::OnAddedFrame(uint32_t aNewF
       }
     }
     if (aNewFrameCount > 1) {
       mAnim->UnionFirstFrameRefreshArea(aNewRefreshArea);
     }
   }
 }
 
-void
+bool
 RasterImage::SetMetadata(const ImageMetadata& aMetadata,
                          bool aFromMetadataDecode)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mError) {
-    return;
+    return true;
   }
 
   if (aMetadata.HasSize()) {
     IntSize size = aMetadata.GetSize();
     if (size.width < 0 || size.height < 0) {
       NS_WARNING("Image has negative intrinsic size");
       DoError();
-      return;
+      return true;
     }
 
     MOZ_ASSERT(aMetadata.HasOrientation());
     Orientation orientation = aMetadata.GetOrientation();
 
     // If we already have a size, check the new size against the old one.
     if (mHasSize && (size != mSize || orientation != mOrientation)) {
       NS_WARNING("Image changed size or orientation on redecode! "
                  "This should not happen!");
       DoError();
-      return;
+      return true;
     }
 
     // Set the size and flag that we have it.
     mSize = size;
     mOrientation = orientation;
     mHasSize = true;
   }
 
@@ -854,17 +854,17 @@ RasterImage::SetMetadata(const ImageMeta
     // Lock the image and throw away the key.
     LockImage();
 
     if (!aFromMetadataDecode) {
       // The metadata decode reported that this image isn't animated, but we
       // discovered that it actually was during the full decode. This is a
       // rare failure that only occurs for corrupt images. To recover, we need
       // to discard all existing surfaces and redecode.
-      RecoverFromInvalidFrames(mSize, DECODE_FLAGS_DEFAULT);
+      return false;
     }
   }
 
   if (mAnim) {
     mAnim->SetLoopCount(aMetadata.GetLoopCount());
     mAnim->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
   }
 
@@ -876,16 +876,18 @@ RasterImage::SetMetadata(const ImageMeta
     nsCOMPtr<nsISupportsPRUint32> intwrapy =
       do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
     intwrapx->SetData(hotspot.x);
     intwrapy->SetData(hotspot.y);
 
     Set("hotspotX", intwrapx);
     Set("hotspotY", intwrapy);
   }
+
+  return true;
 }
 
 NS_IMETHODIMP
 RasterImage::SetAnimationMode(uint16_t aAnimationMode)
 {
   if (mAnim) {
     mAnim->SetAnimationMode(aAnimationMode);
   }
@@ -1727,17 +1729,28 @@ RasterImage::FinalizeDecoder(Decoder* aD
   bool done = aDecoder->GetDecodeDone();
 
   // If the decoder detected an error, log it to the error console.
   if (aDecoder->ShouldReportError() && !aDecoder->WasAborted()) {
     ReportDecoderError(aDecoder);
   }
 
   // Record all the metadata the decoder gathered about this image.
-  SetMetadata(aDecoder->GetImageMetadata(), wasMetadata);
+  bool metadataOK = SetMetadata(aDecoder->GetImageMetadata(), wasMetadata);
+  if (!metadataOK) {
+    // This indicates a serious error that requires us to discard all existing
+    // surfaces and redecode to recover. We'll drop the results from this
+    // decoder on the floor, since they aren't valid.
+    aDecoder->TakeProgress();
+    aDecoder->TakeInvalidRect();
+    RecoverFromInvalidFrames(mSize,
+                             FromSurfaceFlags(aDecoder->GetSurfaceFlags()));
+    return;
+  }
+
   MOZ_ASSERT(mError || mHasSize || !aDecoder->HasSize(),
              "SetMetadata should've gotten a size");
 
   if (!wasMetadata && aDecoder->GetDecodeDone() && !aDecoder->WasAborted()) {
     // Flag that we've been decoded before.
     mHasBeenDecoded = true;
     if (mAnim) {
       mAnim->SetDoneDecoding(true);
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -319,18 +319,21 @@ private:
    * information about the image gathered during decoding.
    *
    * This function may be called multiple times, but will throw an error if
    * subsequent calls do not match the first.
    *
    * @param aMetadata The metadata to set on this image.
    * @param aFromMetadataDecode True if this metadata came from a metadata
    *                            decode; false if it came from a full decode.
+   * @return |true| unless a catastrophic failure was discovered. If |false| is
+   * returned, it indicates that the image is corrupt in a way that requires all
+   * surfaces to be discarded to recover.
    */
-  void SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
+  bool SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
 
   /**
    * In catastrophic circumstances like a GPU driver crash, the contents of our
    * frames may become invalid.  If the information we gathered during the
    * metadata decode proves to be wrong due to image corruption, the frames we
    * have may violate this class's invariants. Either way, we need to
    * immediately discard the invalid frames and redecode so that callers don't
    * perceive that we've entered an invalid state. 
--- a/image/SourceBuffer.h
+++ b/image/SourceBuffer.h
@@ -12,16 +12,17 @@
 #define mozilla_image_sourcebuffer_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/nsRefPtr.h"
 #include "nsTArray.h"
 
 class nsIInputStream;
 
 namespace mozilla {
 namespace image {
--- a/image/SurfaceFlags.h
+++ b/image/SurfaceFlags.h
@@ -45,12 +45,29 @@ ToSurfaceFlags(uint32_t aFlags)
     flags |= SurfaceFlags::NO_PREMULTIPLY_ALPHA;
   }
   if (aFlags & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) {
     flags |= SurfaceFlags::NO_COLORSPACE_CONVERSION;
   }
   return flags;
 }
 
+/**
+ * Given a set of SurfaceFlags, returns a set of imgIContainer FLAG_* flags with
+ * the corresponding flags set.
+ */
+inline uint32_t
+FromSurfaceFlags(SurfaceFlags aFlags)
+{
+  uint32_t flags = imgIContainer::DECODE_FLAGS_DEFAULT;
+  if (aFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA) {
+    flags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
+  }
+  if (aFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
+    flags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
+  }
+  return flags;
+}
+
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_SurfaceFlags_h
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -492,24 +492,33 @@ VectorImage::SetAnimationStartTime(const
 //------------------------------------------------------------------------------
 // imgIContainer methods
 
 //******************************************************************************
 NS_IMETHODIMP
 VectorImage::GetWidth(int32_t* aWidth)
 {
   if (mError || !mIsFullyLoaded) {
-    *aWidth = -1;
-  } else {
-    SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
-    MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
-                         "loading without errors");
-    *aWidth = rootElem->GetIntrinsicWidth();
+    // XXXdholbert Technically we should leave outparam untouched when we
+    // fail. But since many callers don't check for failure, we set it to 0 on
+    // failure, for sane/predictable results.
+    *aWidth = 0;
+    return NS_ERROR_FAILURE;
   }
-  return *aWidth >= 0 ? NS_OK : NS_ERROR_FAILURE;
+
+  SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
+  MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
+             "loading without errors");
+  int32_t rootElemWidth = rootElem->GetIntrinsicWidth();
+  if (rootElemWidth < 0) {
+    *aWidth = 0;
+    return NS_ERROR_FAILURE;
+  }
+  *aWidth = rootElemWidth;
+  return NS_OK;
 }
 
 //******************************************************************************
 NS_IMETHODIMP_(void)
 VectorImage::RequestRefresh(const TimeStamp& aTime)
 {
   if (HadRecentRefresh(aTime)) {
     return;
@@ -556,24 +565,33 @@ VectorImage::GetImageSpaceInvalidationRe
   return aRect;
 }
 
 //******************************************************************************
 NS_IMETHODIMP
 VectorImage::GetHeight(int32_t* aHeight)
 {
   if (mError || !mIsFullyLoaded) {
-    *aHeight = -1;
-  } else {
-    SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
-    MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
-                         "loading without errors");
-    *aHeight = rootElem->GetIntrinsicHeight();
+    // XXXdholbert Technically we should leave outparam untouched when we
+    // fail. But since many callers don't check for failure, we set it to 0 on
+    // failure, for sane/predictable results.
+    *aHeight = 0;
+    return NS_ERROR_FAILURE;
   }
-  return *aHeight >= 0 ? NS_OK : NS_ERROR_FAILURE;
+
+  SVGSVGElement* rootElem = mSVGDocumentWrapper->GetRootSVGElem();
+  MOZ_ASSERT(rootElem, "Should have a root SVG elem, since we finished "
+             "loading without errors");
+  int32_t rootElemHeight = rootElem->GetIntrinsicHeight();
+  if (rootElemHeight < 0) {
+    *aHeight = 0;
+    return NS_ERROR_FAILURE;
+  }
+  *aHeight = rootElemHeight;
+  return NS_OK;
 }
 
 //******************************************************************************
 NS_IMETHODIMP
 VectorImage::GetIntrinsicSize(nsSize* aSize)
 {
   if (mError || !mIsFullyLoaded) {
     return NS_ERROR_FAILURE;
new file mode 100644
--- /dev/null
+++ b/image/test/crashtests/1205923-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<body>
+</body>
+<script>
+  function createImage(loadHandler) {
+    var newImage = new Image;
+    newImage.id = "thepreviewimage";
+    newImage.setAttribute("src", "unsized-svg.svg");
+    if (loadHandler) {
+      newImage.onload = loadHandler;
+    }
+
+    // Query width & height, and display them in document:
+    physWidth = newImage.width;
+    physHeight = newImage.height;
+    document.documentElement.innerHTML +=
+      physWidth + " x " + physHeight + "<br>";
+  }
+
+  function part2() {
+    // Load image again:
+    createImage();
+
+    // End the crashtest.
+    document.documentElement.removeAttribute("class");
+  }
+
+  function startTest() {
+    // Trigger image load, and call part2() when it's loaded:
+    createImage(part2);
+  }
+
+  startTest();
+</script>
+</html>
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -48,12 +48,14 @@ load 856616.gif
 
 skip-if(AddressSanitizer) skip-if(B2G) load 944353.jpg
 
 # Bug 1160801: Ensure that we handle invalid disposal types.
 load invalid-disposal-method-1.gif
 load invalid-disposal-method-2.gif
 load invalid-disposal-method-3.gif
 
+load 1205923-1.html
+
 # Ensure we handle ICO directory entries which specify the wrong size for the
 # contained resource.
 load invalid_ico_height.ico
 load invalid_ico_width.ico
new file mode 100644
--- /dev/null
+++ b/image/test/crashtests/unsized-svg.svg
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg"></svg>
--- a/ipc/chromium/src/chrome/common/child_thread.cc
+++ b/ipc/chromium/src/chrome/common/child_thread.cc
@@ -27,17 +27,16 @@ ChildThread::~ChildThread() {
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 bool ChildThread::Run() {
   bool r = StartWithOptions(options_);
 #ifdef MOZ_NUWA_PROCESS
-  NS_ASSERTION(NuwaMarkCurrentThread, "NuwaMarkCurrentThread is not defined!");
   if (IsNuwaProcess()) {
       message_loop()->PostTask(FROM_HERE,
                                NewRunnableFunction(&ChildThread::MarkThread));
   }
 #endif
   return r;
 }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3884,23 +3884,18 @@ BytecodeEmitter::emitDestructuringOpsArr
         ParseNode* elem = member;
         if (elem->isKind(PNK_ASSIGN)) {
             pndefault = elem->pn_right;
             elem = elem->pn_left;
         }
 
         if (elem->isKind(PNK_SPREAD)) {
             /* Create a new array with the rest of the iterator */
-            ptrdiff_t off;
-            if (!emitN(JSOP_NEWARRAY, 3, &off))                   // ... OBJ? ITER ARRAY
-                return false;
-            checkTypeSet(JSOP_NEWARRAY);
-            jsbytecode* pc = code(off);
-            SET_UINT24(pc, 0);
-
+            if (!emitUint32Operand(JSOP_NEWARRAY, 0))             // ... OBJ? ITER ARRAY
+                return false;
             if (!emitNumberOp(0))                                 // ... OBJ? ITER ARRAY INDEX
                 return false;
             if (!emitSpread())                                    // ... OBJ? ARRAY INDEX
                 return false;
             if (!emit1(JSOP_POP))                                 // ... OBJ? ARRAY
                 return false;
             needToPopIterator = false;
         } else {
@@ -7209,39 +7204,45 @@ BytecodeEmitter::emitArray(ParseNode* pn
      * array and in source order evaluating each element value and adding
      * it to the array, without invoking latent setters.  We use the
      * JSOP_NEWINIT and JSOP_INITELEM_ARRAY bytecodes to ignore setters and
      * to avoid dup'ing and popping the array as each element is added, as
      * JSOP_SETELEM/JSOP_SETPROP would do.
      */
     MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY);
 
-    int32_t nspread = 0;
+    uint32_t nspread = 0;
     for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
         if (elt->isKind(PNK_SPREAD))
             nspread++;
     }
 
-    ptrdiff_t off;
-    if (!emitN(op, 3, &off))                                        // ARRAY
-        return false;
-    checkTypeSet(op);
-    jsbytecode* pc = code(off);
+    // Array literal's length is limited to NELEMENTS_LIMIT in parser.
+    static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX,
+                  "array literals' maximum length must not exceed limits "
+                  "required by BaselineCompiler::emit_JSOP_NEWARRAY, "
+                  "BaselineCompiler::emit_JSOP_INITELEM_ARRAY, "
+                  "and DoSetElemFallback's handling of JSOP_INITELEM_ARRAY");
+    MOZ_ASSERT(count >= nspread);
+    MOZ_ASSERT(count <= NativeObject::MAX_DENSE_ELEMENTS_COUNT,
+               "the parser must throw an error if the array exceeds maximum "
+               "length");
 
     // For arrays with spread, this is a very pessimistic allocation, the
     // minimum possible final size.
-    SET_UINT24(pc, count - nspread);
+    if (!emitUint32Operand(op, count - nspread))                    // ARRAY
+        return false;
 
     ParseNode* pn2 = pn;
-    jsatomid atomIndex;
+    uint32_t index;
     bool afterSpread = false;
-    for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
+    for (index = 0; pn2; index++, pn2 = pn2->pn_next) {
         if (!afterSpread && pn2->isKind(PNK_SPREAD)) {
             afterSpread = true;
-            if (!emitNumberOp(atomIndex))                           // ARRAY INDEX
+            if (!emitNumberOp(index))                               // ARRAY INDEX
                 return false;
         }
         if (!updateSourceCoordNotes(pn2->pn_pos.begin))
             return false;
         if (pn2->isKind(PNK_ELISION)) {
             if (!emit1(JSOP_HOLE))
                 return false;
         } else {
@@ -7257,22 +7258,21 @@ BytecodeEmitter::emitArray(ParseNode* pn
             if (!emit2(JSOP_PICK, 2))                                    // ITER ARRAY INDEX
                 return false;
             if (!emitSpread())                                           // ARRAY INDEX
                 return false;
         } else if (afterSpread) {
             if (!emit1(JSOP_INITELEM_INC))
                 return false;
         } else {
-            if (!emitN(JSOP_INITELEM_ARRAY, 3, &off))
-                return false;
-            SET_UINT24(code(off), atomIndex);
-        }
-    }
-    MOZ_ASSERT(atomIndex == count);
+            if (!emitUint32Operand(JSOP_INITELEM_ARRAY, index))
+                return false;
+        }
+    }
+    MOZ_ASSERT(index == count);
     if (afterSpread) {
         if (!emit1(JSOP_POP))                                            // ARRAY
             return false;
     }
     return true;
 }
 
 bool
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8762,17 +8762,17 @@ Parser<ParseHandler>::arrayInitializer(Y
         handler.setListFlag(literal, PNX_NONCONST);
     } else {
         tokenStream.ungetToken();
 
         bool spread = false, missingTrailingComma = false;
         uint32_t index = 0;
         TokenStream::Modifier modifier = TokenStream::Operand;
         for (; ; index++) {
-            if (index == NativeObject::NELEMENTS_LIMIT) {
+            if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
                 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt;
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
             if (tt == TOK_RB)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arrays/dense-from-sparse.js
@@ -0,0 +1,40 @@
+// |jit-test| allow-oom
+// Appending elements to a dense array should make the array sparse when the
+// length exceeds the limit.
+
+function test() {
+  const MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
+  const VALUES_PER_HEADER = 2;
+  const MAX_DENSE_ELEMENTS_COUNT = MAX_DENSE_ELEMENTS_ALLOCATION - VALUES_PER_HEADER;
+  const SPARSE_DENSITY_RATIO = 8;
+  const MIN_DENSE = MAX_DENSE_ELEMENTS_COUNT / SPARSE_DENSITY_RATIO;
+  const MARGIN = 16;
+
+  let a = [];
+  // Fill the beginning of array to make it keep dense until length exceeds
+  // MAX_DENSE_ELEMENTS_COUNT.
+  for (let i = 0; i < MIN_DENSE; i++)
+    a[i] = i;
+
+  // Skip from MIN_DENSE to MAX_DENSE_ELEMENTS_COUNT - MARGIN, to reduce the
+  // time taken by test.
+
+  // Fill the ending of array to make it sparse at MAX_DENSE_ELEMENTS_COUNT.
+  for (let i = MAX_DENSE_ELEMENTS_COUNT - MARGIN; i < MAX_DENSE_ELEMENTS_COUNT + MARGIN; i++)
+    a[i] = i;
+
+  // Make sure the last element is defined.
+  assertEq(a.length, MAX_DENSE_ELEMENTS_COUNT + MARGIN);
+  assertEq(a[a.length - 1], MAX_DENSE_ELEMENTS_COUNT + MARGIN - 1);
+
+  // Make sure elements around MAX_DENSE_ELEMENTS_COUNT are also defined.
+  assertEq(a[MAX_DENSE_ELEMENTS_COUNT - 1], MAX_DENSE_ELEMENTS_COUNT - 1);
+  assertEq(a[MAX_DENSE_ELEMENTS_COUNT], MAX_DENSE_ELEMENTS_COUNT);
+  assertEq(a[MAX_DENSE_ELEMENTS_COUNT + 1], MAX_DENSE_ELEMENTS_COUNT + 1);
+}
+
+var config = getBuildConfiguration();
+// Takes too long time on debug build.
+if (!config.debug) {
+  test();
+}
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/BaselineCompiler.h"
 
+#include "mozilla/Casting.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/FixedList.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
@@ -27,16 +28,18 @@
 
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+using mozilla::AssertedCast;
+
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
   : BaselineCompilerSpecific(cx, alloc, script),
     yieldOffsets_(cx),
     modifiesArguments_(false)
 {
 }
 
 bool
@@ -1787,20 +1790,23 @@ BaselineCompiler::emit_JSOP_LINENO()
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_NEWARRAY()
 {
     frame.syncStack(0);
 
-    uint32_t length = GET_UINT24(pc);
+    uint32_t length = GET_UINT32(pc);
+    MOZ_ASSERT(length <= INT32_MAX,
+               "the bytecode emitter must fail to compile code that would "
+               "produce JSOP_NEWARRAY with a length exceeding int32_t range");
 
     // Pass length in R0.
-    masm.move32(Imm32(length), R0.scratchReg());
+    masm.move32(Imm32(AssertedCast<int32_t>(length)), R0.scratchReg());
 
     ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
     if (!group)
         return false;
 
     ICNewArray_Fallback::Compiler stubCompiler(cx, group);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
@@ -1844,17 +1850,22 @@ BaselineCompiler::emit_JSOP_NEWARRAY_COP
 bool
 BaselineCompiler::emit_JSOP_INITELEM_ARRAY()
 {
     // Keep the object and rhs on the stack.
     frame.syncStack(0);
 
     // Load object in R0, index in R1.
     masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0);
-    masm.moveValue(Int32Value(GET_UINT24(pc)), R1);
+    uint32_t index = GET_UINT32(pc);
+    MOZ_ASSERT(index <= INT32_MAX,
+               "the bytecode emitter must fail to compile code that would "
+               "produce JSOP_INITELEM_ARRAY with a length exceeding "
+               "int32_t range");
+    masm.moveValue(Int32Value(AssertedCast<int32_t>(index)), R1);
 
     // Call IC.
     ICSetElem_Fallback::Compiler stubCompiler(cx);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Pop the rhs, so that the object is on the top of the stack.
     frame.pop();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3684,17 +3684,21 @@ DoSetElemFallback(JSContext* cx, Baselin
         oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
         oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
     }
 
     if (op == JSOP_INITELEM) {
         if (!InitElemOperation(cx, obj, index, rhs))
             return false;
     } else if (op == JSOP_INITELEM_ARRAY) {
-        MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
+        MOZ_ASSERT(uint32_t(index.toInt32()) <= INT32_MAX,
+                   "the bytecode emitter must fail to compile code that would "
+                   "produce JSOP_INITELEM_ARRAY with an index exceeding "
+                   "int32_t range");
+        MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT32(pc));
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
             return false;
     } else if (op == JSOP_INITELEM_INC) {
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
             return false;
     } else {
         if (!SetObjectElement(cx, obj, index, rhs, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc))
             return false;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4215,22 +4215,22 @@ CodeGenerator::visitNewArrayCallVM(LNewA
     MOZ_ASSERT(!lir->isCall());
     saveLive(lir);
 
     JSObject* templateObject = lir->mir()->templateObject();
 
     if (templateObject) {
         pushArg(Imm32(lir->mir()->convertDoubleElements()));
         pushArg(ImmGCPtr(templateObject->group()));
-        pushArg(Imm32(lir->mir()->count()));
+        pushArg(Imm32(lir->mir()->length()));
 
         callVM(NewArrayWithGroupInfo, lir);
     } else {
         pushArg(Imm32(GenericObject));
-        pushArg(Imm32(lir->mir()->count()));
+        pushArg(Imm32(lir->mir()->length()));
         pushArg(ImmPtr(lir->mir()->pc()));
         pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
 
         callVM(NewArrayOperationInfo, lir);
     }
 
     if (ReturnReg != objReg)
         masm.movePtr(ReturnReg, objReg);
@@ -4296,19 +4296,19 @@ CodeGenerator::visitHypot(LHypot* lir)
 }
 
 void
 CodeGenerator::visitNewArray(LNewArray* lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
     JSObject* templateObject = lir->mir()->templateObject();
-    DebugOnly<uint32_t> count = lir->mir()->count();
-
-    MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT);
+    DebugOnly<uint32_t> length = lir->mir()->length();
+
+    MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
 
     if (lir->mir()->shouldUseVM()) {
         visitNewArrayCallVM(lir);
         return;
     }
 
     OutOfLineNewArray* ool = new(alloc()) OutOfLineNewArray(lir);
     addOutOfLineCode(ool, lir->mir());
@@ -6941,17 +6941,17 @@ CodeGenerator::visitOutOfLineStoreElemen
     Int32Key key = ToInt32Key(index);
 
     if (unboxedType == JSVAL_TYPE_MAGIC) {
         // Check array capacity.
         masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
                        key, &callStub);
 
         // Update initialized length. The capacity guard above ensures this won't overflow,
-        // due to NELEMENTS_LIMIT.
+        // due to MAX_DENSE_ELEMENTS_COUNT.
         masm.bumpKey(&key, 1);
         masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
 
         // Update length if length < initializedLength.
         Label dontUpdate;
         masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
                        key, &dontUpdate);
         masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength()));
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1789,17 +1789,17 @@ IonBuilder::inspectOpcode(JSOp op)
         return true;
 
       case JSOP_NEWINIT:
         if (GET_UINT8(pc) == JSProto_Array)
             return jsop_newarray(0);
         return jsop_newobject();
 
       case JSOP_NEWARRAY:
-        return jsop_newarray(GET_UINT24(pc));
+        return jsop_newarray(GET_UINT32(pc));
 
       case JSOP_NEWARRAY_COPYONWRITE:
         return jsop_newarray_copyonwrite();
 
       case JSOP_NEWOBJECT:
         return jsop_newobject();
 
       case JSOP_INITELEM:
@@ -6920,32 +6920,32 @@ IonBuilder::compareTrySharedStub(bool* e
     current->add(unbox);
     current->push(unbox);
 
     *emitted = true;
     return true;
 }
 
 bool
-IonBuilder::jsop_newarray(uint32_t count)
+IonBuilder::jsop_newarray(uint32_t length)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
     MConstant* templateConst;
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
         templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     } else {
         heap = gc::DefaultHeap;
         templateConst = MConstant::New(alloc(), NullValue());
     }
     current->add(templateConst);
 
-    MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc);
+    MNewArray* ins = MNewArray::New(alloc(), constraints(), length, templateConst, heap, pc);
     current->add(ins);
     current->push(ins);
 
     ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
     if (templateGroup) {
         TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup);
         ins->setResultTypeSet(types);
     }
@@ -7047,23 +7047,24 @@ IonBuilder::jsop_initelem_array()
             HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
             if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
                 elemTypes.freeze(constraints());
                 needStub = true;
             }
         }
     }
 
+    uint32_t index = GET_UINT32(pc);
     if (needStub) {
-        MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj, GET_UINT24(pc), value);
+        MCallInitElementArray* store = MCallInitElementArray::New(alloc(), obj, index, value);
         current->add(store);
         return resumeAfter(store);
     }
 
-    return initializeArrayElement(obj, GET_UINT24(pc), value, unboxedType, /* addResumePoint = */ true);
+    return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true);
 }
 
 bool
 IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
                                    JSValueType unboxedType,
                                    bool addResumePointAndIncrementInitializedLength)
 {
     MConstant* id = MConstant::New(alloc(), Int32Value(index));
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -691,17 +691,17 @@ class IonBuilder
     bool jsop_arguments_getelem();
     bool jsop_runonce();
     bool jsop_rest();
     bool jsop_not();
     bool jsop_getprop(PropertyName* name);
     bool jsop_setprop(PropertyName* name);
     bool jsop_delprop(PropertyName* name);
     bool jsop_delelem();
-    bool jsop_newarray(uint32_t count);
+    bool jsop_newarray(uint32_t length);
     bool jsop_newarray_copyonwrite();
     bool jsop_newobject();
     bool jsop_initelem();
     bool jsop_initelem_array();
     bool jsop_initelem_getter_setter();
     bool jsop_mutateproto();
     bool jsop_initprop(PropertyName* name);
     bool jsop_initprop_getter_setter(PropertyName* name);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -437,18 +437,19 @@ IonBuilder::inlineArray(CallInfo& callIn
             return InliningStatus_Inlined;
         }
 
         // The next several checks all may fail due to range conditions.
         trackOptimizationOutcome(TrackedOutcome::ArrayRange);
 
         // Negative lengths generate a RangeError, unhandled by the inline path.
         initLength = arg->constantValue().toInt32();
-        if (initLength >= NativeObject::NELEMENTS_LIMIT)
+        if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT)
             return InliningStatus_NotInlined;
+        MOZ_ASSERT(initLength <= INT32_MAX);
 
         // Make sure initLength matches the template object's length. This is
         // not guaranteed to be the case, for instance if we're inlining the
         // MConstant may come from an outer script.
         if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
             return InliningStatus_NotInlined;
 
         // Don't inline large allocations.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4045,17 +4045,17 @@ MObjectState::Copy(TempAllocator& alloc,
     return res;
 }
 
 MArrayState::MArrayState(MDefinition* arr)
 {
     // This instruction is only used as a summary for bailout paths.
     setResultType(MIRType_Object);
     setRecoveredOnBailout();
-    numElements_ = arr->toNewArray()->count();
+    numElements_ = arr->toNewArray()->length();
 }
 
 bool
 MArrayState::init(TempAllocator& alloc, MDefinition* obj, MDefinition* len)
 {
     if (!MVariadicInstruction::init(alloc, numElements() + 2))
         return false;
     // +1, for the Array object.
@@ -4085,20 +4085,20 @@ MArrayState::Copy(TempAllocator& alloc, 
     MArrayState* res = new(alloc) MArrayState(arr);
     if (!res || !res->init(alloc, arr, len))
         return nullptr;
     for (size_t i = 0; i < res->numElements(); i++)
         res->initElement(i, state->getElement(i));
     return res;
 }
 
-MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
+MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
                      gc::InitialHeap initialHeap, jsbytecode* pc)
   : MUnaryInstruction(templateConst),
-    count_(count),
+    length_(length),
     initialHeap_(initialHeap),
     convertDoubleElements_(false),
     pc_(pc)
 {
     setResultType(MIRType_Object);
     if (templateObject()) {
         if (TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject())) {
             setResultTypeSet(types);
@@ -4110,26 +4110,26 @@ MNewArray::MNewArray(CompilerConstraintL
 
 bool
 MNewArray::shouldUseVM() const
 {
     if (!templateObject())
         return true;
 
     if (templateObject()->is<UnboxedArrayObject>()) {
-        MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= count());
+        MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= length());
         return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();
     }
 
-    MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT);
+    MOZ_ASSERT(length() <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
 
     size_t arraySlots =
         gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
 
-    return count() > arraySlots;
+    return length() > arraySlots;
 }
 
 bool
 MLoadFixedSlot::mightAlias(const MDefinition* store) const
 {
     if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot())
         return false;
     return true;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2900,42 +2900,42 @@ typedef CompilerGCPointer<PropertyName*>
 typedef CompilerGCPointer<Shape*> CompilerShape;
 typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
 
 class MNewArray
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   private:
-    // Number of space to allocate for the array.
-    uint32_t count_;
+    // Number of elements to allocate for the array.
+    uint32_t length_;
 
     // Heap where the array should be allocated.
     gc::InitialHeap initialHeap_;
 
     // Whether values written to this array should be converted to double first.
     bool convertDoubleElements_;
 
     jsbytecode* pc_;
 
-    MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
+    MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
               gc::InitialHeap initialHeap, jsbytecode* pc);
 
   public:
     INSTRUCTION_HEADER(NewArray)
 
     static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints,
-                          uint32_t count, MConstant* templateConst,
+                          uint32_t length, MConstant* templateConst,
                           gc::InitialHeap initialHeap, jsbytecode* pc)
     {
-        return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, pc);
-    }
-
-    uint32_t count() const {
-        return count_;
+        return new(alloc) MNewArray(constraints, length, templateConst, initialHeap, pc);
+    }
+
+    uint32_t length() const {
+        return length_;
     }
 
     JSObject* templateObject() const {
         return getOperand(0)->toConstant()->value().toObjectOrNull();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1729,17 +1729,17 @@ MArrayLength::computeRange(TempAllocator
     // nodes when the value is known to be int32 (see the
     // OBJECT_FLAG_LENGTH_OVERFLOW flag).
     setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
 }
 
 void
 MInitializedLength::computeRange(TempAllocator& alloc)
 {
-    setRange(Range::NewUInt32Range(alloc, 0, NativeObject::NELEMENTS_LIMIT));
+    setRange(Range::NewUInt32Range(alloc, 0, NativeObject::MAX_DENSE_ELEMENTS_COUNT));
 }
 
 void
 MTypedArrayLength::computeRange(TempAllocator& alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
 }
 
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1202,17 +1202,17 @@ RNewObject::recover(JSContext* cx, Snaps
     return true;
 }
 
 bool
 MNewArray::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
-    writer.writeUnsigned(count());
+    writer.writeUnsigned(length());
     return true;
 }
 
 RNewArray::RNewArray(CompactBufferReader& reader)
 {
     count_ = reader.readUnsigned();
 }
 
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -890,33 +890,33 @@ IsElementEscaped(MElements* def, uint32_
 //
 // For the moment, this code is dumb as it only supports arrays which are not
 // changing length, with only access with known constants.
 static bool
 IsArrayEscaped(MInstruction* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Object);
     MOZ_ASSERT(ins->isNewArray());
-    uint32_t count = ins->toNewArray()->count();
+    uint32_t length = ins->toNewArray()->length();
 
     JitSpewDef(JitSpew_Escape, "Check array\n", ins);
     JitSpewIndent spewIndent(JitSpew_Escape);
 
     JSObject* obj = ins->toNewArray()->templateObject();
     if (!obj) {
         JitSpew(JitSpew_Escape, "No template object defined.");
         return true;
     }
 
     if (obj->is<UnboxedArrayObject>()) {
         JitSpew(JitSpew_Escape, "Template object is an unboxed plain object.");
         return true;
     }
 
-    if (count >= 16) {
+    if (length >= 16) {
         JitSpew(JitSpew_Escape, "Array has too many elements");
         return true;
     }
 
     // Check if the object is escaped. If the object is not the first argument
     // of either a known Store / Load, then we consider it as escaped. This is a
     // cheap and conservative escape analysis.
     for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
@@ -930,17 +930,17 @@ IsArrayEscaped(MInstruction* ins)
             continue;
         }
 
         MDefinition* def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Op_Elements: {
             MElements *elem = def->toElements();
             MOZ_ASSERT(elem->object() == ins);
-            if (IsElementEscaped(elem, count)) {
+            if (IsElementEscaped(elem, length)) {
                 JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", elem);
                 return true;
             }
 
             break;
           }
 
           // This instruction is a no-op used to verify that scalar replacement
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -167,28 +167,31 @@ GetGCObjectKind(size_t numSlots)
 {
     if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
         return AllocKind::OBJECT16;
     return slotsToThingKind[numSlots];
 }
 
 /* As for GetGCObjectKind, but for dense array allocation. */
 static inline AllocKind
-GetGCArrayKind(size_t numSlots)
+GetGCArrayKind(size_t numElements)
 {
     /*
      * Dense arrays can use their fixed slots to hold their elements array
      * (less two Values worth of ObjectElements header), but if more than the
      * maximum number of fixed slots is needed then the fixed slots will be
      * unused.
      */
     JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
-    if (numSlots > NativeObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
+    if (numElements > NativeObject::MAX_DENSE_ELEMENTS_COUNT ||
+        numElements + ObjectElements::VALUES_PER_HEADER >= SLOTS_TO_THING_KIND_LIMIT)
+    {
         return AllocKind::OBJECT2;
-    return slotsToThingKind[numSlots + 2];
+    }
+    return slotsToThingKind[numElements + ObjectElements::VALUES_PER_HEADER];
 }
 
 static inline AllocKind
 GetGCObjectFixedSlotsKind(size_t numFixedSlots)
 {
     MOZ_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
     return slotsToThingKind[numFixedSlots];
 }
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -786,33 +786,33 @@ inline T*
 NewObjectWithGroup(ExclusiveContext* cx, HandleObjectGroup group,
                    NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp());
     return NewObjectWithGroup<T>(cx, group, allocKind, newKind);
 }
 
 /*
- * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
+ * As for gc::GetGCObjectKind, where numElements is a guess at the final size of
  * the object, zero if the final size is unknown. This should only be used for
  * objects that do not require any fixed slots.
  */
 static inline gc::AllocKind
-GuessObjectGCKind(size_t numSlots)
+GuessObjectGCKind(size_t numElements)
 {
-    if (numSlots)
-        return gc::GetGCObjectKind(numSlots);
+    if (numElements)
+        return gc::GetGCObjectKind(numElements);
     return gc::AllocKind::OBJECT4;
 }
 
 static inline gc::AllocKind
-GuessArrayGCKind(size_t numSlots)
+GuessArrayGCKind(size_t numElements)
 {
-    if (numSlots)
-        return gc::GetGCArrayKind(numSlots);
+    if (numElements)
+        return gc::GetGCArrayKind(numElements);
     return gc::AllocKind::OBJECT8;
 }
 
 // Returns ESClass_Other if the value isn't an object, or if the object
 // isn't of one of the enumerated classes.  Otherwise returns the appropriate
 // class.
 inline bool
 GetClassOfValue(JSContext* cx, HandleValue v, ESClassValue* classValue)
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -5,39 +5,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsversion_h
 #define jsversion_h
 
 /*
  * JS Capability Macros.
  */
-#define JS_HAS_STR_HTML_HELPERS 1       /* has str.anchor, str.bold, etc. */
+#define JS_HAS_STR_HTML_HELPERS 1       /* (no longer used) */
 #define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
 #define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */
 #define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */
 #define JS_HAS_CATCH_GUARD      1       /* has exception handling catch guard */
 #define JS_HAS_UNEVAL           1       /* has uneval() top-level function */
-#define JS_HAS_CONST            1       /* has JS2 const as alternative var */
-#define JS_HAS_FUN_EXPR_STMT    1       /* has function expression statement */
+#define JS_HAS_CONST            1       /* (no longer used) */
+#define JS_HAS_FUN_EXPR_STMT    1       /* (no longer used) */
 #define JS_HAS_NO_SUCH_METHOD   1       /* has o.__noSuchMethod__ handler */
 #define JS_HAS_FOR_EACH_IN      1       /* has for each (lhs in iterable) */
-#define JS_HAS_GENERATORS       1       /* has yield in generator function */
-#define JS_HAS_BLOCK_SCOPE      1       /* has block scope via let/arraycomp */
-#define JS_HAS_DESTRUCTURING    2       /* has [a,b] = ... or {p:a,q:b} = ... */
+#define JS_HAS_GENERATORS       1       /* (no longer used) */
+#define JS_HAS_BLOCK_SCOPE      1       /* (no longer used) */
+#define JS_HAS_DESTRUCTURING    2       /* (no longer used) */
 #define JS_HAS_GENERATOR_EXPRS  1       /* has (expr for (lhs in iterable)) */
 #define JS_HAS_EXPR_CLOSURES    1       /* has function (formals) listexpr */
 
-/* Support for JS_NewGlobalObject. */
+/* (no longer used) */
 #define JS_HAS_NEW_GLOBAL_OBJECT        1
 
-/* Support for JS_MakeSystemObject. */
-#define JS_HAS_MAKE_SYSTEM_OBJECT       1
-
-/* Feature-test macro for evolving destructuring support. */
+/* (no longer used) */
 #define JS_HAS_DESTRUCTURING_SHORTHAND  (JS_HAS_DESTRUCTURING == 2)
 
 /*
  * Feature for Object.prototype.__{define,lookup}{G,S}etter__ legacy support;
  * support likely to be made opt-in at some future time.
  */
 #define JS_OLD_GETTER_SETTER_METHODS    1
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3604,17 +3604,18 @@ CASE(JSOP_NEWINIT)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWINIT)
 
 CASE(JSOP_NEWARRAY)
 CASE(JSOP_SPREADCALLARRAY)
 {
-    JSObject* obj = NewArrayOperation(cx, script, REGS.pc, GET_UINT24(REGS.pc));
+    uint32_t length = GET_UINT32(REGS.pc);
+    JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length);
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 CASE(JSOP_NEWARRAY_COPYONWRITE)
 {
@@ -3700,17 +3701,17 @@ END_CASE(JSOP_INITELEM)
 
 CASE(JSOP_INITELEM_ARRAY)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
     HandleValue val = REGS.stackHandleAt(-1);
 
     ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-2].toObject());
 
-    uint32_t index = GET_UINT24(REGS.pc);
+    uint32_t index = GET_UINT32(REGS.pc);
     if (!InitArrayElemOperation(cx, REGS.pc, obj, index, val))
         goto error;
 
     REGS.sp--;
 }
 END_CASE(JSOP_INITELEM_ARRAY)
 
 CASE(JSOP_INITELEM_INC)
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "vm/NativeObject-inl.h"
 
-#include "mozilla/CheckedInt.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
 
 #include "jswatchpoint.h"
 
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayCommon.h"
 
@@ -20,16 +21,17 @@
 #include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
+using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::RoundUpPow2;
 
 static const ObjectElements emptyElementsHeader(0, 0);
 
 /* Objects with no elements share one empty set of elements. */
 HeapSlot* const js::emptyObjectElements =
@@ -392,17 +394,17 @@ NativeObject::growSlots(ExclusiveContext
     MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
      */
     NativeObject::slotsSizeMustNotOverflow();
-    MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
+    MOZ_ASSERT(newCount <= MAX_SLOTS_COUNT);
 
     if (!oldCount) {
         MOZ_ASSERT(!slots_);
         slots_ = AllocateObjectBuffer<HeapSlot>(cx, this, newCount);
         if (!slots_)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(slots_, newCount);
         return true;
@@ -514,17 +516,17 @@ bool
 NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
 {
     MOZ_ASSERT(isNative());
     MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
 
     uint32_t cap = getDenseCapacity();
     MOZ_ASSERT(requiredCapacity >= cap);
 
-    if (requiredCapacity >= NELEMENTS_LIMIT)
+    if (requiredCapacity > MAX_DENSE_ELEMENTS_COUNT)
         return true;
 
     uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
     if (newElementsHint >= minimalDenseCount)
         return false;
     minimalDenseCount -= newElementsHint;
 
     if (minimalDenseCount > cap)
@@ -588,17 +590,17 @@ NativeObject::maybeDensifySparseElements
             }
         }
         shape = shape->previous();
     }
 
     if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
         return DenseElementResult::Incomplete;
 
-    if (newInitializedLength >= NELEMENTS_LIMIT)
+    if (newInitializedLength > MAX_DENSE_ELEMENTS_COUNT)
         return DenseElementResult::Incomplete;
 
     /*
      * This object meets all necessary restrictions, convert all indexed
      * properties into dense elements.
      */
 
     if (!obj->maybeCopyElementsForWrite(cx))
@@ -649,144 +651,134 @@ NativeObject::maybeDensifySparseElements
      * to grow the object.
      */
     if (!obj->clearFlag(cx, BaseShape::INDEXED))
         return DenseElementResult::Failure;
 
     return DenseElementResult::Success;
 }
 
-// Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the
-// slot count is usually a power-of-two:
-//
-//   8, 16, 32, 64, ..., 256 Ki, 512 Ki, 1 Mi
-//
-// Beyond that, we use this formula:
-//
-//   count(n+1) = Math.ceil(count(n) * 1.125)
-//
-// where |count(n)| is the size of the nth bucket measured in MiSlots.
+// Given a requested allocation amount (in elements) and (potentially) the
+// length of an array for which elements are being allocated, compute an actual
+// allocation amount (in elements).  (Allocation amounts include space for an
+// ObjectElements instance, so a return value of |N| implies
+// |N - ObjectElements::VALUES_PER_HEADER| usable elements.)
 //
-// These counts lets us add N elements to an array in amortized O(N) time.
-// Having the second class means that for bigger arrays the constant factor is
-// higher, but we waste less space.
+// The requested/actual allocation distinction is meant to:
 //
-// There is one exception to the above rule: for the power-of-two cases, if the
-// chosen capacity would be 2/3 or more of the array's length, the chosen
-// capacity is adjusted (up or down) to be equal to the array's length
-// (assuming length is at least as large as the required capacity). This avoids
-// the allocation of excess elements which are unlikely to be needed, either in
-// this resizing or a subsequent one. The 2/3 factor is chosen so that
-// exceptional resizings will at most triple the capacity, as opposed to the
-// usual doubling.
+//   * preserve amortized O(N) time to add N elements;
+//   * minimize the number of unused elements beyond an array's length, and
+//   * provide at least SLOT_CAPACITY_MIN elements no matter what (so adding
+//     the first several elements to small arrays only needs one allocation).
 //
 // Note: the structure and behavior of this method follow along with
 // UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy
 // in one should generally be matched by the other.
 /* static */ uint32_t
-NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0)
+NativeObject::goodElementsAllocationAmount(uint32_t reqAllocated, uint32_t length = 0)
 {
-    static const uint32_t Mebi = 1024 * 1024;
+    // Handle "small" requests primarily by doubling.
+    const uint32_t Mebi = 1 << 20;
+    if (reqAllocated < Mebi) {
+        uint32_t goodAmount = mozilla::AssertedCast<uint32_t>(RoundUpPow2(reqAllocated));
+
+        // If |goodAmount| would be 2/3 or more of the array's length, adjust
+        // it (up or down) to be equal to the array's length.  This avoids
+        // allocating excess elements that aren't likely to be needed, either
+        // in this resizing or a subsequent one.  The 2/3 factor is chosen so
+        // that exceptional resizings will at most triple the capacity, as
+        // opposed to the usual doubling.
+        uint32_t goodCapacity = goodAmount - ObjectElements::VALUES_PER_HEADER;
+        uint32_t reqCapacity = reqAllocated - ObjectElements::VALUES_PER_HEADER;
+        if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
+            goodAmount = length + ObjectElements::VALUES_PER_HEADER;
 
-    // This table was generated with this JavaScript code and a small amount
-    // subsequent reformatting:
+        if (goodAmount < SLOT_CAPACITY_MIN)
+            goodAmount = SLOT_CAPACITY_MIN;
+
+        return goodAmount;
+    }
+
+    // The almost-doubling above wastes a lot of space for larger bucket sizes.
+    // For large amounts, switch to bucket sizes that obey this formula:
     //
-    //   for (let n = 1, i = 0; i < 57; i++) {
-    //     print((n * 1024 * 1024) + ', ');
+    //   count(n+1) = Math.ceil(count(n) * 1.125)
+    //
+    // where |count(n)| is the size of the nth bucket, measured in 2**20 slots.
+    // These bucket sizes still preserve amortized O(N) time to add N elements,
+    // just with a larger constant factor.
+    //
+    // The bucket size table below was generated with this JavaScript (and
+    // manual reformatting):
+    //
+    //   for (let n = 1, i = 0; i < 34; i++) {
+    //     print('0x' + (n * (1 << 20)).toString(16) + ', ');
     //     n = Math.ceil(n * 1.125);
     //   }
-    //   print('0');
+    //   print('MAX_DENSE_ELEMENTS_ALLOCATION');
     //
-    // The final element is a sentinel value.
+    // Dense array elements can't exceed |MAX_DENSE_ELEMENTS_ALLOCATION|, so
+    // |MAX_DENSE_ELEMENTS_ALLOCATION| is the biggest allowed length.
     static const uint32_t BigBuckets[] = {
-        1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608,
-        9437184, 11534336, 13631488, 15728640, 17825792, 20971520, 24117248,
-        27262976, 31457280, 35651584, 40894464, 46137344, 52428800, 59768832,
-        68157440, 77594624, 88080384, 99614720, 112197632, 126877696,
-        143654912, 162529280, 183500800, 206569472, 232783872, 262144000,
-        295698432, 333447168, 375390208, 422576128, 476053504, 535822336,
-        602931200, 678428672, 763363328, 858783744, 966787072, 1088421888,
-        1224736768, 1377828864, 1550843904, 1744830464, 1962934272, 2208301056,
-        2485125120, 2796552192, 3146776576, 3541041152, 3984588800, 0
+        0x100000, 0x200000, 0x300000, 0x400000, 0x500000, 0x600000, 0x700000,
+        0x800000, 0x900000, 0xb00000, 0xd00000, 0xf00000, 0x1100000, 0x1400000,
+        0x1700000, 0x1a00000, 0x1e00000, 0x2200000, 0x2700000, 0x2c00000,
+        0x3200000, 0x3900000, 0x4100000, 0x4a00000, 0x5400000, 0x5f00000,
+        0x6b00000, 0x7900000, 0x8900000, 0x9b00000, 0xaf00000, 0xc500000,
+        0xde00000, 0xfa00000, MAX_DENSE_ELEMENTS_ALLOCATION
     };
-
-    // This code relies very much on |goodAllocated| being a uint32_t.
-    uint32_t goodAllocated = reqAllocated;
-    if (goodAllocated < Mebi) {
-        goodAllocated = RoundUpPow2(goodAllocated);
+    MOZ_ASSERT(BigBuckets[ArrayLength(BigBuckets) - 2] <= MAX_DENSE_ELEMENTS_ALLOCATION);
 
-        // Look for the abovementioned exception.
-        uint32_t goodCapacity = goodAllocated - ObjectElements::VALUES_PER_HEADER;
-        uint32_t reqCapacity = reqAllocated - ObjectElements::VALUES_PER_HEADER;
-        if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
-            goodAllocated = length + ObjectElements::VALUES_PER_HEADER;
-
-        if (goodAllocated < SLOT_CAPACITY_MIN)
-            goodAllocated = SLOT_CAPACITY_MIN;
-
-    } else {
-        uint32_t i = 0;
-        while (true) {
-            uint32_t b = BigBuckets[i++];
-            if (b >= goodAllocated) {
-                // Found the first bucket greater than or equal to
-                // |goodAllocated|.
-                goodAllocated = b;
-                break;
-            } else if (b == 0) {
-                // Hit the end; return the maximum possible goodAllocated.
-                goodAllocated = 0xffffffff;
-                break;
-            }
-        }
+    // Pick the first bucket that'll fit |reqAllocated|.
+    for (uint32_t b : BigBuckets) {
+        if (b >= reqAllocated)
+            return b;
     }
 
-    return goodAllocated;
+    // Otherwise, return the maximum bucket size.
+    return MAX_DENSE_ELEMENTS_ALLOCATION;
 }
 
 bool
 NativeObject::growElements(ExclusiveContext* cx, uint32_t reqCapacity)
 {
+    if (reqCapacity > MAX_DENSE_ELEMENTS_COUNT) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     MOZ_ASSERT(nonProxyIsExtensible());
     MOZ_ASSERT(canHaveNonEmptyElements());
     if (denseElementsAreCopyOnWrite())
         MOZ_CRASH();
 
     uint32_t oldCapacity = getDenseCapacity();
     MOZ_ASSERT(oldCapacity < reqCapacity);
 
-    using mozilla::CheckedInt;
-
-    CheckedInt<uint32_t> checkedOldAllocated =
-        CheckedInt<uint32_t>(oldCapacity) + ObjectElements::VALUES_PER_HEADER;
-    CheckedInt<uint32_t> checkedReqAllocated =
-        CheckedInt<uint32_t>(reqCapacity) + ObjectElements::VALUES_PER_HEADER;
-    if (!checkedOldAllocated.isValid() || !checkedReqAllocated.isValid())
-        return false;
-
-    uint32_t reqAllocated = checkedReqAllocated.value();
-    uint32_t oldAllocated = checkedOldAllocated.value();
+    uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
+    uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
+    MOZ_ASSERT(oldAllocated <= MAX_DENSE_ELEMENTS_ALLOCATION);
 
     uint32_t newAllocated;
     if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
         MOZ_ASSERT(reqCapacity <= as<ArrayObject>().length());
         // Preserve the |capacity <= length| invariant for arrays with
         // non-writable length.  See also js::ArraySetLength which initially
         // enforces this requirement.
         newAllocated = reqAllocated;
     } else {
-        newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
+        newAllocated = goodElementsAllocationAmount(reqAllocated, getElementsHeader()->length);
     }
 
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
     MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
 
-    // Don't let nelements get close to wrapping around uint32_t.
-    if (newCapacity >= NELEMENTS_LIMIT)
-        return false;
+    // If newCapacity exceeds MAX_DENSE_ELEMENTS_COUNT, the array should become
+    // sparse.
+    MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
 
     uint32_t initlen = getDenseInitializedLength();
 
     HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
     HeapSlot* newHeaderSlots;
     if (hasDynamicElements()) {
         newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, oldAllocated, newAllocated);
         if (!newHeaderSlots)
@@ -817,22 +809,23 @@ NativeObject::shrinkElements(ExclusiveCo
     if (!hasDynamicElements())
         return;
 
     uint32_t oldCapacity = getDenseCapacity();
     MOZ_ASSERT(reqCapacity < oldCapacity);
 
     uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
     uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
-    uint32_t newAllocated = goodAllocated(reqAllocated);
+    uint32_t newAllocated = goodElementsAllocationAmount(reqAllocated);
     if (newAllocated == oldAllocated)
         return;  // Leave elements at its old size.
 
     MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
+    MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
 
     HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
     HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
                                                                 oldAllocated, newAllocated);
     if (!newHeaderSlots) {
         cx->recoverFromOutOfMemory();
         return;  // Leave elements at its old size.
     }
@@ -847,22 +840,22 @@ NativeObject::CopyElementsForWrite(Exclu
 {
     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
 
     // The original owner of a COW elements array should never be modified.
     MOZ_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
 
     uint32_t initlen = obj->getDenseInitializedLength();
     uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
-    uint32_t newAllocated = goodAllocated(allocated);
+    uint32_t newAllocated = goodElementsAllocationAmount(allocated);
 
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
 
-    if (newCapacity >= NELEMENTS_LIMIT)
-        return false;
+    // COPY_ON_WRITE flags is set only if obj is a dense array.
+    MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
 
     JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
 
     HeapSlot* newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, obj, newAllocated);
     if (!newHeaderSlots)
         return false;
     ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
     js_memcpy(newheader, obj->getElementsHeader(),
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -551,18 +551,23 @@ class NativeObject : public JSObject
   public:
     bool generateOwnShape(ExclusiveContext* cx, Shape* newShape = nullptr) {
         return replaceWithNewEquivalentShape(cx, lastProperty(), newShape);
     }
 
     bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape);
     bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag);
 
+    // The maximum number of slots in an object.
+    // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
+    // int32_t (see slotsSizeMustNotOverflow).
+    static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
+
     static void slotsSizeMustNotOverflow() {
-        static_assert((NativeObject::NELEMENTS_LIMIT - 1) <= INT32_MAX / sizeof(JS::Value),
+        static_assert(NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value),
                       "every caller of this method requires that a slot "
                       "number (or slot count) count multiplied by "
                       "sizeof(Value) can't overflow uint32_t (and sometimes "
                       "int32_t, too)");
     }
 
     uint32_t numFixedSlots() const {
         return reinterpret_cast<const shadow::Object*>(this)->numFixedSlots();
@@ -877,21 +882,29 @@ class NativeObject : public JSObject
      */
     static uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class* clasp);
     static uint32_t dynamicSlotsCount(Shape* shape) {
         return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), shape->getObjectClass());
     }
 
     /* Elements accessors. */
 
-    /* Upper bound on the number of elements in an object. */
-    static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
+    // The maximum size, in sizeof(Value), of the allocation used for an
+    // object's dense elements.  (This includes space used to store an
+    // ObjectElements instance.)
+    // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
+    // int32_t (see elementsSizeMustNotOverflow).
+    static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
+
+    // The maximum number of usable dense elements in an object.
+    static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
+        MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
 
     static void elementsSizeMustNotOverflow() {
-        static_assert((NativeObject::NELEMENTS_LIMIT - 1) <= INT32_MAX / sizeof(JS::Value),
+        static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value),
                       "every caller of this method require that an element "
                       "count multiplied by sizeof(Value) can't overflow "
                       "uint32_t (and sometimes int32_t ,too)");
     }
 
     ObjectElements * getElementsHeader() const {
         return ObjectElements::fromElements(elements_);
     }
@@ -899,17 +912,17 @@ class NativeObject : public JSObject
     /* Accessors for elements. */
     bool ensureElements(ExclusiveContext* cx, uint32_t capacity) {
         MOZ_ASSERT(!denseElementsAreCopyOnWrite());
         if (capacity > getDenseCapacity())
             return growElements(cx, capacity);
         return true;
     }
 
-    static uint32_t goodAllocated(uint32_t n, uint32_t length);
+    static uint32_t goodElementsAllocationAmount(uint32_t n, uint32_t length);
     bool growElements(ExclusiveContext* cx, uint32_t newcap);
     void shrinkElements(ExclusiveContext* cx, uint32_t cap);
     void setDynamicElements(ObjectElements* header) {
         MOZ_ASSERT(!hasDynamicElements());
         elements_ = header->elements();
         MOZ_ASSERT(hasDynamicElements());
     }
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -842,20 +842,20 @@ 1234567890123456789012345678901234567890
      */ \
     macro(JSOP_NEWINIT,   89, "newinit",    NULL,         5,  0,  1, JOF_UINT8) \
     /*
      * Pushes newly created array onto the stack.
      *
      * This opcode takes the final length, which is preallocated.
      *   Category: Literals
      *   Type: Array
-     *   Operands: uint24_t length
+     *   Operands: uint32_t length
      *   Stack: => obj
      */ \
-    macro(JSOP_NEWARRAY,  90, "newarray",   NULL,         4,  0,  1, JOF_UINT24) \
+    macro(JSOP_NEWARRAY,  90, "newarray",   NULL,         5,  0,  1, JOF_UINT32) \
     /*
      * Pushes newly created object onto the stack.
      *
      * This opcode takes an object with the final shape, which can be set at
      * the start and slots then filled in directly.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t baseobjIndex
@@ -915,20 +915,20 @@ 1234567890123456789012345678901234567890
     \
     /*
      * Initialize an array element.
      *
      * Pops the top two values on the stack as 'val' and 'obj', sets 'index'
      * property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Array
-     *   Operands: uint24_t index
+     *   Operands: uint32_t index
      *   Stack: obj, val => obj
      */ \
-    macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 4,  2,  1,  JOF_UINT24|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 5,  2,  1,  JOF_UINT32|JOF_ELEM|JOF_SET|JOF_DETECTING) \
     \
     /*
      * Initialize a getter in an object literal.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines getter
      * of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
@@ -1272,23 +1272,22 @@ 1234567890123456789012345678901234567890
      *   Stack: receiver, obj, propval => obj[propval]
      */ \
     macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
     /*
      * Pushes newly created array for a spread call onto the stack. This has
      * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid
      * using unboxed arrays in spread calls, which would make compiling spread
      * calls in baseline more complex.
-     *
      *   Category: Literals
      *   Type: Array
-     *   Operands: uint24_t length
+     *   Operands: uint32_t length
      *   Stack: => obj
      */ \
-    macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 4,  0,  1, JOF_UINT24) \
+    macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5,  0,  1, JOF_UINT32) \
     \
     /*
      * Defines the given function on the current scope.
      *
      * This is used for global scripts and also in some cases for function
      * scripts where use of dynamic scoping inhibits optimization.
      *   Category: Variables and Scopes
      *   Type: Variables
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 309;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 310;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 413,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -5113,19 +5113,21 @@ ChooseScaleAndSetTransform(FrameLayerBui
       }
     }
     // If the scale factors are too small, just use 1.0. The content is being
     // scaled out of sight anyway.
     if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
       scale = gfxSize(1.0, 1.0);
     }
     // If this is a transform container layer, then pre-rendering might
-    // mean we try render a layer bigger than the max texture size. Apply
-    // clmaping to prevent this.
-    if (aTransform) {
+    // mean we try render a layer bigger than the max texture size. If we have
+    // tiling, that's not a problem, since we'll automatically choose a tiled
+    // layer for layers of that size. If not, we need to apply clamping to
+    // prevent this.
+    if (aTransform && !gfxPrefs::LayersTilesEnabled()) {
       RestrictScaleToMaxLayerSize(scale, aVisibleRect, aContainerFrame, aLayer);
     }
   } else {
     scale = gfxSize(1.0, 1.0);
   }
 
   // Store the inverse of our resolution-scale on the layer
   aLayer->SetBaseTransform(transform);
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -4601,18 +4601,17 @@ nsCSSRendering::GetTextDecorationRectInt
       offset = aOffset - lineThickness + extra;
       break;
     }
     default:
       NS_ERROR("Invalid decoration value!");
   }
 
   if (aVertical) {
-    r.y = baseline + floor(offset + 0.5); // this will need updating when we
-                                          // support sideways-left orientation
+    r.y = baseline + floor(offset + 0.5);
     Swap(r.x, r.y);
     Swap(r.width, r.height);
   } else {
     r.y = baseline - floor(offset + 0.5);
   }
 
   return r;
 }
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -920,17 +920,18 @@ bool nsCaret::IsMenuPopupHidingCaret()
 }
 
 void
 nsCaret::ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset,
                            nsRect* aCaretRect, nsRect* aHookRect)
 {
   NS_ASSERTION(aFrame, "Should have a frame here");
 
-  bool isVertical = aFrame->GetWritingMode().IsVertical();
+  WritingMode wm = aFrame->GetWritingMode();
+  bool isVertical = wm.IsVertical();
 
   nscoord bidiIndicatorSize;
   *aCaretRect = GetGeometryForFrame(aFrame, aFrameOffset, &bidiIndicatorSize);
 
   // on RTL frames the right edge of mCaretRect must be equal to framePos
   const nsStyleVisibility* vis = aFrame->StyleVisibility();
   if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
     if (isVertical) {
@@ -952,21 +953,30 @@ nsCaret::ComputeCaretRects(nsIFrame* aFr
     // The height of the hook rectangle is the same as the width of the caret
     // rectangle.
     int caretBidiLevel = selection->GetFrameSelection()->GetCaretBidiLevel();
     if (caretBidiLevel & BIDI_LEVEL_UNDEFINED) {
       caretBidiLevel = NS_GET_EMBEDDING_LEVEL(aFrame);
     }
     bool isCaretRTL = caretBidiLevel % 2;
     if (isVertical) {
-      aHookRect->SetRect(aCaretRect->XMost() - bidiIndicatorSize,
-                         aCaretRect->y + (isCaretRTL ? bidiIndicatorSize * -1 :
-                                                       aCaretRect->height),
-                         aCaretRect->height,
-                         bidiIndicatorSize);
+      bool isSidewaysLR = wm.IsVerticalLR() && !wm.IsLineInverted();
+      if (isSidewaysLR) {
+        aHookRect->SetRect(aCaretRect->x + bidiIndicatorSize,
+                           aCaretRect->y + (!isCaretRTL ? bidiIndicatorSize * -1 :
+                                                          aCaretRect->height),
+                           aCaretRect->height,
+                           bidiIndicatorSize);
+      } else {
+        aHookRect->SetRect(aCaretRect->XMost() - bidiIndicatorSize,
+                           aCaretRect->y + (isCaretRTL ? bidiIndicatorSize * -1 :
+                                                         aCaretRect->height),
+                           aCaretRect->height,
+                           bidiIndicatorSize);
+      }
     } else {
       aHookRect->SetRect(aCaretRect->x + (isCaretRTL ? bidiIndicatorSize * -1 :
                                                        aCaretRect->width),
                          aCaretRect->y + bidiIndicatorSize,
                          bidiIndicatorSize,
                          aCaretRect->width);
     }
   }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2608,17 +2608,17 @@ NS_IMETHODIMP nsDocumentViewer::SelectAl
   {
     bodyNode = do_QueryInterface(mDocument->GetRootElement());
   }
   if (!bodyNode) return NS_ERROR_FAILURE;
 
   rv = selection->RemoveAllRanges();
   if (NS_FAILED(rv)) return rv;
 
-  mozilla::dom::Selection::AutoApplyUserSelectStyle userSelection(selection);
+  mozilla::dom::Selection::AutoUserInitiated userSelection(selection);
   rv = selection->SelectAllChildren(bodyNode);
   return rv;
 }
 
 NS_IMETHODIMP nsDocumentViewer::CopySelection()
 {
   nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
                                     mPresShell, nullptr);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6658,20 +6658,19 @@ nsLayoutUtils::GetTextRunOrientFlagsForS
       return gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
     case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
       return gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
     default:
       NS_NOTREACHED("unknown text-orientation");
       return 0;
     }
 
-  /* not yet implemented:
   case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
     return gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
-  */
+
   case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
     return gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
 
   default:
     NS_NOTREACHED("unknown writing-mode");
     return 0;
   }
 }
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -98,17 +98,17 @@ public:
                                nsIPresShell::ScrollAxis aVertical =
                                  nsIPresShell::ScrollAxis(),
                                nsIPresShell::ScrollAxis aHorizontal =
                                  nsIPresShell::ScrollAxis(),
                                int32_t aFlags = 0);
   nsresult      SubtractRange(RangeData* aRange, nsRange* aSubtract,
                               nsTArray<RangeData>* aOutput);
   /**
-   * AddItem adds aRange to this Selection.  If mApplyUserSelectStyle is true,
+   * AddItem adds aRange to this Selection.  If mUserInitiated is true,
    * then aRange is first scanned for -moz-user-select:none nodes and split up
    * into multiple ranges to exclude those before adding the resulting ranges
    * to this Selection.
    */
   nsresult      AddItem(nsRange* aRange, int32_t* aOutIndex, bool aNoStartSelect = false);
   nsresult      RemoveItem(nsRange* aRange);
   nsresult      RemoveCollapsedRanges();
   nsresult      Clear(nsPresContext* aPresContext);
@@ -214,25 +214,25 @@ private:
   nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
 
 public:
   SelectionType GetType(){return mType;}
   void          SetType(SelectionType aType){mType = aType;}
 
   nsresult     NotifySelectionListeners();
 
-  friend struct AutoApplyUserSelectStyle;
-  struct MOZ_RAII AutoApplyUserSelectStyle
+  friend struct AutoUserInitiated;
+  struct MOZ_RAII AutoUserInitiated
   {
-    explicit AutoApplyUserSelectStyle(Selection* aSelection
-                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : mSavedValue(aSelection->mApplyUserSelectStyle)
+    explicit AutoUserInitiated(Selection* aSelection
+                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mSavedValue(aSelection->mUserInitiated)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-      aSelection->mApplyUserSelectStyle = true;
+      aSelection->mUserInitiated = true;
     }
     AutoRestore<bool> mSavedValue;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
 
 private:
   friend struct mozilla::AutoPrepareFocusRange;
   class ScrollSelectionIntoViewEvent;
@@ -316,17 +316,17 @@ private:
   CachedOffsetForFrame *mCachedOffsetForFrame;
   nsDirection mDirection;
   SelectionType mType;
   /**
    * True if the current selection operation was initiated by user action.
    * It determines whether we exclude -moz-user-select:none nodes or not,
    * as well as whether selectstart events will be fired.
    */
-  bool mApplyUserSelectStyle;
+  bool mUserInitiated;
 
   // Non-zero if we don't want any changes we make to the selection to be
   // visible to content. If non-zero, content won't be notified about changes.
   uint32_t mSelectionChangeBlockerCount;
 };
 
 // Stack-class to turn on/off selection batching.
 class MOZ_STACK_CLASS SelectionBatcher final
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -199,17 +199,25 @@ public:
   BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
 
   /**
    * Return the line-relative inline flow direction as a BidiDir
    */
   BidiDir GetBidiDir() const { return BidiDir(mWritingMode & eBidiMask); }
 
   /**
-   * Return true if LTR. (Convenience method)
+   * Return true if the inline flow direction is against physical direction
+   * (i.e. right-to-left or bottom-to-top).
+   * This occurs when writing-mode is sideways-lr OR direction is rtl (but not
+   * if both of those are true).
+   */
+  bool IsInlineReversed() const { return !!(mWritingMode & eInlineFlowMask); }
+
+  /**
+   * Return true if bidi direction is LTR. (Convenience method)
    */
   bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
 
   /**
    * True if vertical-mode block direction is LR (convenience method).
    */
   bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
 
@@ -221,19 +229,17 @@ public:
   /**
    * True if vertical writing mode, i.e. when
    * writing-mode: vertical-lr | vertical-rl.
    */
   bool IsVertical() const { return !!(mWritingMode & eOrientationMask); }
 
   /**
    * True if line-over/line-under are inverted from block-start/block-end.
-   * This is true when
-   *   - writing-mode is vertical-rl && text-orientation is sideways-left
-   *   - writing-mode is vertical-lr && text-orientation is not sideways-left
+   * This is true only when writing-mode is vertical-lr.
    */
   bool IsLineInverted() const { return !!(mWritingMode & eLineOrientMask); }
 
   /**
    * Block-axis flow-relative to line-relative factor.
    * May be used as a multiplication factor for block-axis coordinates
    * to convert between flow- and line-relative coordinate systems (e.g.
    * positioning an over- or under-line decoration).
@@ -310,33 +316,37 @@ public:
 
   mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const
   {
     // indexes are four-bit values:
     //   bit 0 = the eOrientationMask value
     //   bit 1 = the eInlineFlowMask value
     //   bit 2 = the eBlockFlowMask value
     //   bit 3 = the eLineOrientMask value
+    // Not all of these combinations can actually be specified via CSS: there
+    // is no horizontal-bt writing-mode, and no text-orientation value that
+    // produces "inverted" text. (The former 'sideways-left' value, no longer
+    // in the spec, would have produced this in vertical-rl mode.)
     static const mozilla::css::Side kLogicalInlineSides[][2] = {
-      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // horizontal-tb                  ltr
-      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-rl                    ltr
-      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // horizontal-tb                  rtl
-      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-rl                    rtl
-      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // (horizontal-bt)  (inverted)    ltr
-      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-lr      sideways-left rtl
-      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // (horizontal-bt)  (inverted)    rtl
-      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-lr      sideways-left ltr
-      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // horizontal-tb    (inverted)    rtl
-      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-rl      sideways-left rtl
-      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // horizontal-tb    (inverted)    ltr
-      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-rl      sideways-left ltr
-      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // (horizontal-bt)                ltr
-      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-lr                    ltr
-      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // (horizontal-bt)                rtl
-      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-lr                    rtl
+      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // horizontal-tb               ltr
+      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-rl                 ltr
+      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // horizontal-tb               rtl
+      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-rl                 rtl
+      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // (horizontal-bt)  (inverted) ltr
+      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // sideways-lr                 rtl
+      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // (horizontal-bt)  (inverted) rtl
+      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // sideways-lr                 ltr
+      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // horizontal-tb    (inverted) rtl
+      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-rl      (inverted) rtl
+      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // horizontal-tb    (inverted) ltr
+      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-rl      (inverted) ltr
+      { NS_SIDE_LEFT,   NS_SIDE_RIGHT  },  // (horizontal-bt)             ltr
+      { NS_SIDE_TOP,    NS_SIDE_BOTTOM },  // vertical-lr                 ltr
+      { NS_SIDE_RIGHT,  NS_SIDE_LEFT   },  // (horizontal-bt)             rtl
+      { NS_SIDE_BOTTOM, NS_SIDE_TOP    },  // vertical-lr                 rtl
     };
 
     // Inline axis sides depend on all three of writing-mode, text-orientation
     // and direction, which are encoded in the eOrientationMask,
     // eInlineFlowMask, eBlockFlowMask and eLineOrientMask bits.  Use these four
     // bits to index into kLogicalInlineSides.
     static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
                   eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
@@ -421,17 +431,17 @@ public:
   /**
    * Returns the logical side corresponding to the specified
    * line-relative direction, given the current writing mode.
    */
   LogicalSide LogicalSideForLineRelativeDir(LineRelativeDir aDir) const
   {
     auto side = static_cast<LogicalSide>(aDir);
     if (IsInline(side)) {
-      return IsBidiLTR() ? side : GetOppositeSide(side);
+      return !IsInlineReversed() ? side : GetOppositeSide(side);
     }
     return !IsLineInverted() ? side : GetOppositeSide(side);
   }
 
   /**
    * Default constructor gives us a horizontal, LTR writing mode.
    * XXX We will probably eliminate this and require explicit initialization
    *     in all cases once transition is complete.
@@ -681,23 +691,23 @@ public:
   LogicalPoint(WritingMode aWritingMode,
                const nsPoint& aPoint,
                const nsSize& aContainerSize)
 #ifdef DEBUG
     : mWritingMode(aWritingMode)
 #endif
   {
     if (aWritingMode.IsVertical()) {
-      I() = aWritingMode.IsBidiLTR() ? aPoint.y
-                                     : aContainerSize.height - aPoint.y;
+      I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
+                                            : aPoint.y;
       B() = aWritingMode.IsVerticalLR() ? aPoint.x
                                         : aContainerSize.width - aPoint.x;
     } else {
-      I() = aWritingMode.IsBidiLTR() ? aPoint.x
-                                     : aContainerSize.width - aPoint.x;
+      I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
+                                            : aPoint.x;
       B() = aPoint.y;
     }
   }
 
   /**
    * Read-only (const) access to the logical coordinates.
    */
   nscoord I(WritingMode aWritingMode) const // inline-axis
@@ -732,21 +742,21 @@ public:
    */
   nsPoint GetPhysicalPoint(WritingMode aWritingMode,
                            const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
       return nsPoint(aWritingMode.IsVerticalLR()
                      ? B() : aContainerSize.width - B(),
-                     aWritingMode.IsBidiLTR()
-                     ? I() : aContainerSize.height - I());
+                     aWritingMode.IsInlineReversed()
+                     ? aContainerSize.height - I() : I());
     } else {
-      return nsPoint(aWritingMode.IsBidiLTR()
-                     ? I() : aContainerSize.width - I(),
+      return nsPoint(aWritingMode.IsInlineReversed()
+                     ? aContainerSize.width - I() : I(),
                      B());
     }
   }
 
   /**
    * Return the equivalent point in a different writing mode.
    */
   LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
@@ -1092,32 +1102,32 @@ public:
     if (aWritingMode.IsVertical()) {
       if (aWritingMode.IsVerticalLR()) {
         mMargin.top = aPhysicalMargin.left;
         mMargin.bottom = aPhysicalMargin.right;
       } else {
         mMargin.top = aPhysicalMargin.right;
         mMargin.bottom = aPhysicalMargin.left;
       }
-      if (aWritingMode.IsBidiLTR()) {
+      if (aWritingMode.IsInlineReversed()) {
+        mMargin.left = aPhysicalMargin.bottom;
+        mMargin.right = aPhysicalMargin.top;
+      } else {
         mMargin.left = aPhysicalMargin.top;
         mMargin.right = aPhysicalMargin.bottom;
-      } else {
-        mMargin.left = aPhysicalMargin.bottom;
-        mMargin.right = aPhysicalMargin.top;
       }
     } else {
       mMargin.top = aPhysicalMargin.top;
       mMargin.bottom = aPhysicalMargin.bottom;
-      if (aWritingMode.IsBidiLTR()) {
+      if (aWritingMode.IsInlineReversed()) {
+        mMargin.left = aPhysicalMargin.right;
+        mMargin.right = aPhysicalMargin.left;
+      } else {
         mMargin.left = aPhysicalMargin.left;
         mMargin.right = aPhysicalMargin.right;
-      } else {
-        mMargin.left = aPhysicalMargin.right;
-        mMargin.right = aPhysicalMargin.left;
       }
     }
   }
 
   nscoord IStart(WritingMode aWritingMode) const // inline-start margin
   {
     CHECK_WRITING_MODE(aWritingMode);
     return mMargin.left;
@@ -1206,40 +1216,40 @@ public:
   /**
    * Accessors for physical margins, using our writing mode to convert from
    * logical values.
    */
   nscoord Top(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ?
-      (aWritingMode.IsBidiLTR() ? IStart() : IEnd()) : BStart();
+      (aWritingMode.IsInlineReversed() ? IEnd() : IStart()) : BStart();
   }
 
   nscoord Bottom(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ?
-      (aWritingMode.IsBidiLTR() ? IEnd() : IStart()) : BEnd();
+      (aWritingMode.IsInlineReversed() ? IStart() : IEnd()) : BEnd();
   }
 
   nscoord Left(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ?
       (aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
-      (aWritingMode.IsBidiLTR() ? IStart() : IEnd());
+      (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
   }
 
   nscoord Right(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ?
       (aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
-      (aWritingMode.IsBidiLTR() ? IEnd() : IStart());
+      (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
   }
 
   nscoord LeftRight(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
   }
 
@@ -1259,25 +1269,25 @@ public:
   /**
    * Return an nsMargin containing our physical coordinates
    */
   nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     return aWritingMode.IsVertical()
            ? (aWritingMode.IsVerticalLR()
-             ? (aWritingMode.IsBidiLTR()
-               ? nsMargin(IStart(), BEnd(), IEnd(), BStart())
-               : nsMargin(IEnd(), BEnd(), IStart(), BStart()))
-             : (aWritingMode.IsBidiLTR()
-               ? nsMargin(IStart(), BStart(), IEnd(), BEnd())
-               : nsMargin(IEnd(), BStart(), IStart(), BEnd())))
-           : (aWritingMode.IsBidiLTR()
-             ? nsMargin(BStart(), IEnd(), BEnd(), IStart())
-             : nsMargin(BStart(), IStart(), BEnd(), IEnd()));
+             ? (aWritingMode.IsInlineReversed()
+               ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
+               : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
+             : (aWritingMode.IsInlineReversed()
+               ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
+               : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
+           : (aWritingMode.IsInlineReversed()
+             ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
+             : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
   }
 
   /**
    * Return a LogicalMargin representing this margin in a different
    * writing mode
    */
   LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
   {
@@ -1434,23 +1444,23 @@ public:
               const nsSize& aContainerSize)
 #ifdef DEBUG
     : mWritingMode(aWritingMode)
 #endif
   {
     if (aWritingMode.IsVertical()) {
       mRect.y = aWritingMode.IsVerticalLR()
                 ? aRect.x : aContainerSize.width - aRect.XMost();
-      mRect.x = aWritingMode.IsBidiLTR()
-                ? aRect.y : aContainerSize.height - aRect.YMost();
+      mRect.x = aWritingMode.IsInlineReversed()
+                ? aContainerSize.height - aRect.YMost() : aRect.y;
       mRect.height = aRect.width;
       mRect.width = aRect.height;
     } else {
-      mRect.x = aWritingMode.IsBidiLTR()
-                ? aRect.x : aContainerSize.width - aRect.XMost();
+      mRect.x = aWritingMode.IsInlineReversed()
+                ? aContainerSize.width - aRect.XMost() : aRect.x;
       mRect.y = aRect.y;
       mRect.width = aRect.width;
       mRect.height = aRect.height;
     }
   }
 
   /**
    * Inline- and block-dimension geometry.
@@ -1543,27 +1553,27 @@ public:
    */
   nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
       return aWritingMode.IsVerticalLR() ?
              mRect.Y() : aContainerWidth - mRect.YMost();
     } else {
-      return aWritingMode.IsBidiLTR() ?
-             mRect.X() : aContainerWidth - mRect.XMost();
+      return aWritingMode.IsInlineReversed() ?
+             aContainerWidth - mRect.XMost() : mRect.X();
     }
   }
 
   nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
-      return aWritingMode.IsBidiLTR() ? mRect.X()
-                                      : aContainerHeight - mRect.XMost();
+      return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.XMost()
+                                             : mRect.X();
     } else {
       return mRect.Y();
     }
   }
 
   nscoord Width(WritingMode aWritingMode) const
   {
     CHECK_WRITING_MODE(aWritingMode);
@@ -1578,27 +1588,27 @@ public:
 
   nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
       return aWritingMode.IsVerticalLR() ?
              mRect.YMost() : aContainerWidth - mRect.Y();
     } else {
-      return aWritingMode.IsBidiLTR() ?
-             mRect.XMost() : aContainerWidth - mRect.X();
+      return aWritingMode.IsInlineReversed() ?
+             aContainerWidth - mRect.X() : mRect.XMost();
     }
   }
 
   nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
-      return aWritingMode.IsBidiLTR() ? mRect.XMost()
-                                      : aContainerHeight - mRect.x;
+      return aWritingMode.IsInlineReversed() ? aContainerHeight - mRect.x
+                                             : mRect.XMost();
     } else {
       return mRect.YMost();
     }
   }
 
   bool IsEmpty() const
   {
     return mRect.IsEmpty();
@@ -1702,22 +1712,22 @@ public:
    */
   nsRect GetPhysicalRect(WritingMode aWritingMode,
                          const nsSize& aContainerSize) const
   {
     CHECK_WRITING_MODE(aWritingMode);
     if (aWritingMode.IsVertical()) {
       return nsRect(aWritingMode.IsVerticalLR()
                     ? BStart() : aContainerSize.width - BEnd(),
-                    aWritingMode.IsBidiLTR()
-                    ? IStart() : aContainerSize.height - IEnd(),
+                    aWritingMode.IsInlineReversed()
+                    ?  aContainerSize.height - IEnd() : IStart(),
                     BSize(), ISize());
     } else {
-      return nsRect(aWritingMode.IsBidiLTR()
-                    ? IStart() : aContainerSize.width - IEnd(),
+      return nsRect(aWritingMode.IsInlineReversed()
+                    ? aContainerSize.width - IEnd() : IStart(),
                     BStart(), ISize(), BSize());
     }
   }
 
   /**
    * Return a LogicalRect representing this rect in a different writing mode
    */
   LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1182,17 +1182,17 @@ nsImageFrame::DisplayAltText(nsPresConte
     // Display the text
     nsresult rv = NS_ERROR_FAILURE;
 
     if (aPresContext->BidiEnabled()) {
       nsBidiDirection dir;
       nscoord x, y;
 
       if (isVertical) {
-        x = pt.x + maxDescent; // XXX will need update for sideways-left
+        x = pt.x + maxDescent;
         if (wm.IsBidiLTR()) {
           y = aRect.y;
           dir = NSBIDI_LTR;
         } else {
           y = aRect.YMost() - strWidth;
           dir = NSBIDI_RTL;
         }
       } else {
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -345,17 +345,17 @@ struct MOZ_RAII AutoPrepareFocusRange
 
     if (aSelection->mRanges.Length() <= 1) {
       return;
     }
 
     if (aSelection->mFrameSelection->IsUserSelectionReason()) {
       mUserSelect.emplace(aSelection);
     }
-    bool userSelection = aSelection->mApplyUserSelectStyle;
+    bool userSelection = aSelection->mUserInitiated;
 
     nsTArray<RangeData>& ranges = aSelection->mRanges;
     if (!userSelection ||
         (!aContinueSelection && aMultipleSelection)) {
       // Scripted command or the user is starting a new explicit multi-range
       // selection.
       for (RangeData& entry : ranges) {
         entry.mRange->SetIsGenerated(false);
@@ -419,17 +419,17 @@ struct MOZ_RAII AutoPrepareFocusRange
         aSelection->mRanges.RemoveElementAt(i);
       }
     }
     if (aSelection->mFrameSelection) {
       aSelection->mFrameSelection->InvalidateDesiredPos();
     }
   }
 
-  Maybe<Selection::AutoApplyUserSelectStyle> mUserSelect;
+  Maybe<Selection::AutoUserInitiated> mUserSelect;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } // namespace mozilla
 
 ////////////BEGIN nsFrameSelection methods
 
 nsFrameSelection::nsFrameSelection()
@@ -1677,17 +1677,17 @@ nsFrameSelection::TakeFocus(nsIContent* 
   mEndSelectedCell = nullptr;
   mAppendStartSelectedCell = nullptr;
   mHint = aHint;
   
   int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   if (!mDomSelections[index])
     return NS_ERROR_NULL_POINTER;
 
-  Maybe<Selection::AutoApplyUserSelectStyle> userSelect;
+  Maybe<Selection::AutoUserInitiated> userSelect;
   if (IsUserSelectionReason()) {
     userSelect.emplace(mDomSelections[index]);
   }
 
   //traverse through document and unselect crap here
   if (!aContinueSelection) {//single click? setting cursor down
     uint32_t batching = mBatching;//hack to use the collapse code.
     bool changes = mChangesDuringBatching;
@@ -3326,27 +3326,27 @@ nsFrameSelection::DisconnectFromPresShel
 // mozilla::dom::Selection implementation
 
 // note: this can return a nil anchor node
 
 Selection::Selection()
   : mCachedOffsetForFrame(nullptr)
   , mDirection(eDirNext)
   , mType(nsISelectionController::SELECTION_NORMAL)
-  , mApplyUserSelectStyle(false)
+  , mUserInitiated(false)
   , mSelectionChangeBlockerCount(0)
 {
 }
 
 Selection::Selection(nsFrameSelection* aList)
   : mFrameSelection(aList)
   , mCachedOffsetForFrame(nullptr)
   , mDirection(eDirNext)
   , mType(nsISelectionController::SELECTION_NORMAL)
-  , mApplyUserSelectStyle(false)
+  , mUserInitiated(false)
   , mSelectionChangeBlockerCount(0)
 {
 }
 
 Selection::~Selection()
 {
   setAnchorFocusRange(-1);
 
@@ -3695,20 +3695,17 @@ Selection::AddItem(nsRange* aItem, int32
 {
   if (!aItem)
     return NS_ERROR_NULL_POINTER;
   if (!aItem->IsPositioned())
     return NS_ERROR_UNEXPECTED;
 
   NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
 
-  // XXX Rename mApplyUserSelectStyle? Not the best name (as it is also being
-  // used to detect here whether the event is user initiated for the purposes of
-  // dispatching the selectstart event).
-  if (mApplyUserSelectStyle) {
+  if (mUserInitiated) {
     nsAutoTArray<nsRefPtr<nsRange>, 4> rangesToAdd;
     *aOutIndex = -1;
 
     if (!aNoStartSelect && mType == nsISelectionController::SELECTION_NORMAL &&
         nsFrameSelection::sSelectionEventsEnabled && Collapsed() &&
         !IsBlockingSelectionChangeEvents()) {
       // First, we generate the ranges to add with a scratch range, which is a
       // clone of the original range passed in. We do this seperately, because the
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5306,16 +5306,17 @@ nsTextFrame::DrawSelectionDecorations(gf
                                       const TextRangeStyle &aRangeStyle,
                                       const gfxPoint& aPt,
                                       gfxFloat aICoordInFrame,
                                       gfxFloat aWidth,
                                       gfxFloat aAscent,
                                       const gfxFont::Metrics& aFontMetrics,
                                       DrawPathCallbacks* aCallbacks,
                                       bool aVertical,
+                                      gfxFloat aDecorationOffsetDir,
                                       uint8_t aDecoration)
 {
   gfxPoint pt(aPt);
   gfxSize size(aWidth,
                ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(),
                                                aFontMetrics, aType));
   gfxFloat descentLimit =
     ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
@@ -5412,18 +5413,18 @@ nsTextFrame::DrawSelectionDecorations(gf
     }
     default:
       NS_WARNING("Requested selection decorations when there aren't any");
       return;
   }
   size.height *= relativeSize;
   PaintDecorationLine(aContext, aDirtyRect, color, nullptr, pt,
     (aVertical ? (pt.y - aPt.y) : (pt.x - aPt.x)) + aICoordInFrame,
-    size, aAscent, offset, aDecoration, style, eSelectionDecoration,
-    aCallbacks, aVertical, descentLimit);
+    size, aAscent, offset * aDecorationOffsetDir, aDecoration, style,
+    eSelectionDecoration, aCallbacks, aVertical, descentLimit);
 }
 
 /* static */
 bool
 nsTextFrame::GetSelectionTextColors(SelectionType aType,
                                     nsTextPaintStyle& aTextPaintStyle,
                                     const TextRangeStyle &aRangeStyle,
                                     nscolor* aForeground,
@@ -5787,17 +5788,17 @@ nsTextFrame::PaintTextWithSelectionColor
       nscolor foreground, background;
       GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
                              &foreground, &background);
       // Draw background color
       gfxFloat advance = hyphenWidth +
         mTextRun->GetAdvanceWidth(offset, length, &aProvider);
       if (NS_GET_A(background) > 0) {
         gfxRect bgRect;
-        gfxFloat offs = iOffset - (mTextRun->IsRightToLeft() ? advance : 0);
+        gfxFloat offs = iOffset - (mTextRun->IsInlineReversed() ? advance : 0);
         if (vertical) {
           bgRect = gfxRect(aFramePt.x, aFramePt.y + offs,
                            GetSize().width, advance);
         } else {
           bgRect = gfxRect(aFramePt.x + offs, aFramePt.y,
                            advance, GetSize().height);
         }
         PaintSelectionBackground(*aCtx->GetDrawTarget(), background, dirtyRect,
@@ -5824,17 +5825,17 @@ nsTextFrame::PaintTextWithSelectionColor
       gfxPoint(aFramePt.x + iOffset, aTextBaselinePt.y);
 
     // Determine what shadow, if any, to draw - either from textStyle
     // or from the ::-moz-selection pseudo-class if specified there
     nsCSSShadowArray* shadow = textStyle->GetTextShadow();
     GetSelectionTextShadow(this, type, aTextPaintStyle, &shadow);
     if (shadow) {
       nscoord startEdge = iOffset;
-      if (mTextRun->IsRightToLeft()) {
+      if (mTextRun->IsInlineReversed()) {
         startEdge -= mTextRun->GetAdvanceWidth(offset, length, &aProvider) +
             hyphenWidth;
       }
       PaintShadows(shadow, offset, length, dirtyRect, aFramePt, textBaselinePt,
           startEdge, aProvider, foreground, aClipEdges, aCtx);
     }
 
     // Draw text segment
@@ -5914,36 +5915,38 @@ nsTextFrame::PaintTextSelectionDecoratio
   gfxPoint pt;
   if (verticalRun) {
     pt.x = (aTextBaselinePt.x - mAscent) / app;
   } else {
     pt.y = (aTextBaselinePt.y - mAscent) / app;
   }
   gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
                     aDirtyRect.width / app, aDirtyRect.height / app);
+  gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
   SelectionType type;
   TextRangeStyle selectedStyle;
   while (iterator.GetNextSegment(&iOffset, &offset, &length, &hyphenWidth,
                                  &type, &selectedStyle)) {
     gfxFloat advance = hyphenWidth +
       mTextRun->GetAdvanceWidth(offset, length, &aProvider);
     if (type == aSelectionType) {
       if (verticalRun) {
         pt.y = (aFramePt.y + iOffset -
-               (mTextRun->IsRightToLeft() ? advance : 0)) / app;
+               (mTextRun->IsInlineReversed() ? advance : 0)) / app;
       } else {
         pt.x = (aFramePt.x + iOffset -
-               (mTextRun->IsRightToLeft() ? advance : 0)) / app;
+               (mTextRun->IsInlineReversed() ? advance : 0)) / app;
       }
       gfxFloat width = Abs(advance) / app;
       gfxFloat xInFrame = pt.x - (aFramePt.x / app);
       DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType,
                                aTextPaintStyle, selectedStyle, pt, xInFrame,
                                width, mAscent / app, decorationMetrics,
-                               aCallbacks, verticalRun, kDecoration);
+                               aCallbacks, verticalRun, decorationOffsetDir,
+                               kDecoration);
     }
     iterator.UpdateWithAdvance(advance);
   }
 }
 
 bool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
     const gfxPoint& aFramePt,
@@ -6241,47 +6244,48 @@ nsTextFrame::PaintText(nsRenderingContex
     return;
 
   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
   // Trim trailing whitespace, unless we're painting a selection highlight,
   // which should include trailing spaces if present (bug 1146754).
   provider.InitializeForDisplay(!IsSelected());
 
   gfxContext* ctx = aRenderingContext->ThebesContext();
-  const bool rtl = mTextRun->IsRightToLeft();
+  const bool reversed = mTextRun->IsInlineReversed();
   const bool verticalRun = mTextRun->IsVertical();
   WritingMode wm = GetWritingMode();
   const nscoord frameWidth = GetSize().width;
   gfxPoint framePt(aPt.x, aPt.y);
   gfxPoint textBaselinePt;
   if (verticalRun) {
     if (wm.IsVerticalLR()) {
       textBaselinePt.x =
         nsLayoutUtils::GetSnappedBaselineX(this, ctx, aPt.x, mAscent);
     } else {
       textBaselinePt.x =
         nsLayoutUtils::GetSnappedBaselineX(this, ctx, aPt.x + frameWidth,
                                            -mAscent);
     }
-    textBaselinePt.y = rtl ? aPt.y + GetSize().height : aPt.y;
+    textBaselinePt.y = reversed ? aPt.y + GetSize().height : aPt.y;
   } else {
-    textBaselinePt = gfxPoint(rtl ? gfxFloat(aPt.x + frameWidth) : framePt.x,
-             nsLayoutUtils::GetSnappedBaselineY(this, ctx, aPt.y, mAscent));
+    textBaselinePt =
+      gfxPoint(reversed ? gfxFloat(aPt.x + frameWidth) : framePt.x,
+               nsLayoutUtils::GetSnappedBaselineY(this, ctx, aPt.y, mAscent));
   }
   uint32_t startOffset = provider.GetStart().GetSkippedOffset();
   uint32_t maxLength = ComputeTransformedLength(provider);
   nscoord snappedStartEdge, snappedEndEdge;
   if (!MeasureCharClippedText(provider, aItem.mVisIStartEdge, aItem.mVisIEndEdge,
          &startOffset, &maxLength, &snappedStartEdge, &snappedEndEdge)) {
     return;
   }
   if (verticalRun) {
-    textBaselinePt.y += rtl ? -snappedEndEdge : snappedStartEdge;
+    textBaselinePt.y += reversed ? -snappedEndEdge : snappedStartEdge;
   } else {
-    textBaselinePt.x += rtl ? -snappedEndEdge : snappedStartEdge;
+    textBaselinePt.x += reversed ? -snappedEndEdge : snappedStartEdge;
   }
   nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedStartEdge,
                                              snappedEndEdge);
   nsTextPaintStyle textPaintStyle(this);
   textPaintStyle.SetResolveColors(!aCallbacks);
 
   gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
                     aDirtyRect.width, aDirtyRect.height);
@@ -6427,16 +6431,20 @@ nsTextFrame::DrawTextRunAndDecorations(
     }
 
     gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
                       aDirtyRect.Width() / app, aDirtyRect.Height() / app);
 
     nscoord inflationMinFontSize =
       nsLayoutUtils::InflationMinFontSizeFor(this);
 
+    // The decoration-line offsets need to be reversed for sideways-lr mode,
+    // so we will multiply the values from metrics by this factor.
+    gfxFloat decorationOffsetDir = mTextRun->IsSidewaysLeft() ? -1.0 : 1.0;
+
     // Underlines
     for (uint32_t i = aDecorations.mUnderlines.Length(); i-- > 0; ) {
       const LineDecoration& dec = aDecorations.mUnderlines[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
 
       float inflation =
@@ -6445,17 +6453,18 @@ nsTextFrame::DrawTextRunAndDecorations(
         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
                             useVerticalMetrics);
 
       decSize.height = metrics.underlineSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
-        metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
+        decorationOffsetDir * metrics.underlineOffset,
+        NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
         dec.mStyle, eNormalDecoration, aCallbacks, verticalRun);
     }
     // Overlines
     for (uint32_t i = aDecorations.mOverlines.Length(); i-- > 0; ) {
       const LineDecoration& dec = aDecorations.mOverlines[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
@@ -6466,17 +6475,18 @@ nsTextFrame::DrawTextRunAndDecorations(
         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
                             useVerticalMetrics);
 
       decSize.height = metrics.underlineSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
-        metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
+        decorationOffsetDir * metrics.maxAscent,
+        NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
         eNormalDecoration, aCallbacks, verticalRun);
     }
 
     // CSS 2.1 mandates that text be painted after over/underlines, and *then*
     // line-throughs
     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, aTextColor,
                 aAdvanceWidth, aDrawSoftHyphen, aContextPaint, aCallbacks);
 
@@ -6493,17 +6503,18 @@ nsTextFrame::DrawTextRunAndDecorations(
         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
                             useVerticalMetrics);
 
       decSize.height = metrics.strikeoutSize;
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
-        metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
+        decorationOffsetDir * metrics.strikeoutOffset,
+        NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
         dec.mStyle, eNormalDecoration, aCallbacks, verticalRun);
     }
 }
 
 void
 nsTextFrame::DrawText(
     gfxContext* const aCtx, const gfxRect& aDirtyRect,
     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
@@ -6626,19 +6637,19 @@ nsTextFrame::GetCharacterOffsetAtFramePo
 
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   if (!mTextRun)
     return offsets;
 
   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
   // Trim leading but not trailing whitespace if possible
   provider.InitializeForDisplay(false);
-  gfxFloat width = mTextRun->IsVertical() ?
-    (mTextRun->IsRightToLeft() ? mRect.height - aPoint.y : aPoint.y) :
-    (mTextRun->IsRightToLeft() ? mRect.width - aPoint.x : aPoint.x);
+  gfxFloat width = mTextRun->IsVertical()
+    ? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y)
+    : (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x);
   gfxFloat fitWidth;
   uint32_t skippedLength = ComputeTransformedLength(provider);
 
   uint32_t charsFit = CountCharsFit(mTextRun,
       provider.GetStart().GetSkippedOffset(), skippedLength, width, &provider, &fitWidth);
 
   int32_t selectedOffset;
   if (charsFit < skippedLength) {
@@ -6855,23 +6866,23 @@ nsTextFrame::GetPointFromOffset(int32_t 
 
   gfxFloat advance =
     mTextRun->GetAdvanceWidth(properties.GetStart().GetSkippedOffset(),
                               GetSkippedDistance(properties.GetStart(), iter),
                               &properties);
   nscoord iSize = NSToCoordCeilClamped(advance);
 
   if (mTextRun->IsVertical()) {
-    if (mTextRun->IsRightToLeft()) {
+    if (mTextRun->IsInlineReversed()) {
       outPoint->y = mRect.height - iSize;
     } else {
       outPoint->y = iSize;
     }
   } else {
-    if (mTextRun->IsRightToLeft()) {
+    if (mTextRun->IsInlineReversed()) {
       outPoint->x = mRect.width - iSize;
     } else {
       outPoint->x = iSize;
     }
   }
 
   return NS_OK;
 }
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -741,16 +741,17 @@ protected:
                                 const TextRangeStyle &aRangeStyle,
                                 const gfxPoint& aPt,
                                 gfxFloat aICoordInFrame,
                                 gfxFloat aWidth,
                                 gfxFloat aAscent,
                                 const gfxFont::Metrics& aFontMetrics,
                                 DrawPathCallbacks* aCallbacks,
                                 bool aVertical,
+                                gfxFloat aDecorationOffsetDir,
                                 uint8_t aDecoration);
   enum DecorationType
   {
     eNormalDecoration,
     eSelectionDecoration
   };
   void PaintDecorationLine(gfxContext* const aCtx,
                            const gfxRect& aDirtyRect,
copy from layout/reftests/floats/float-in-rtl-vlr-1-ref.html
copy to layout/reftests/floats/float-in-rtl-slr-1-ref.html
--- a/layout/reftests/floats/float-in-rtl-vlr-1-ref.html
+++ b/layout/reftests/floats/float-in-rtl-slr-1-ref.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
 <html>
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+   writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:200px;"></div>
+    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:calc(100vh - 316px);"></div>
   </div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:100px;"></div>
+    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:calc(100vh - 216px);"></div>
   </div>
-  <div class="vlr" style="background:silver; height: calc(100vh - 16px);">
-    <div style="height:100px;">This text should appear ABOVE the red and green blocks.</div>
+  <div class="slr" style="background:silver; height: calc(100vh - 16px);">
+    <div style="height:100px;">This text should appear BELOW the red and green blocks.</div>
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-1a.html
copy to layout/reftests/floats/float-in-rtl-slr-1a.html
--- a/layout/reftests/floats/float-in-rtl-vlr-1a.html
+++ b/layout/reftests/floats/float-in-rtl-slr-1a.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+   writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:300px" dir="rtl">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-1b.html
copy to layout/reftests/floats/float-in-rtl-slr-1b.html
--- a/layout/reftests/floats/float-in-rtl-vlr-1b.html
+++ b/layout/reftests/floats/float-in-rtl-slr-1b.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+   writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div style="height:200px" dir="rtl">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-1c.html
copy to layout/reftests/floats/float-in-rtl-slr-1c.html
--- a/layout/reftests/floats/float-in-rtl-vlr-1c.html
+++ b/layout/reftests/floats/float-in-rtl-slr-1c.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+   writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:300px" dir="rtl">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div style="height:200px" dir="rtl">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-1d.html
copy to layout/reftests/floats/float-in-rtl-slr-1d.html
--- a/layout/reftests/floats/float-in-rtl-vlr-1d.html
+++ b/layout/reftests/floats/float-in-rtl-slr-1d.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div dir="rtl" style="height:300px">
     <div style="height:300px">
       <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
     </div>
     <div style="height:200px">
       <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
     </div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-2-ref.html
copy to layout/reftests/floats/float-in-rtl-slr-2-ref.html
--- a/layout/reftests/floats/float-in-rtl-vlr-2-ref.html
+++ b/layout/reftests/floats/float-in-rtl-slr-2-ref.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
 <html>
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;"></div>
+    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:calc(100vh - 116px);"></div>
   </div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:100px;"></div>
+    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:calc(100vh - 216px);"></div>
   </div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div class="vlr" style="background:silver; height: calc(100vh - 16px);">
-      <div style="margin-top:200px;">This text should appear BELOW the green and red blocks.</div>
+    <div class="slr" style="background:silver; height: calc(100vh - 16px);">
+      <div style="margin-bottom:200px;">This text should appear ABOVE the green and red blocks.</div>
     </div>
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-2a.html
copy to layout/reftests/floats/float-in-rtl-slr-2a.html
--- a/layout/reftests/floats/float-in-rtl-vlr-2a.html
+++ b/layout/reftests/floats/float-in-rtl-slr-2a.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div dir="rtl" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-2b.html
copy to layout/reftests/floats/float-in-rtl-slr-2b.html
--- a/layout/reftests/floats/float-in-rtl-vlr-2b.html
+++ b/layout/reftests/floats/float-in-rtl-slr-2b.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div dir="rtl" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-2c.html
copy to layout/reftests/floats/float-in-rtl-slr-2c.html
--- a/layout/reftests/floats/float-in-rtl-vlr-2c.html
+++ b/layout/reftests/floats/float-in-rtl-slr-2c.html
@@ -1,24 +1,21 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div dir="rtl" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div dir="rtl" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-2d.html
copy to layout/reftests/floats/float-in-rtl-slr-2d.html
--- a/layout/reftests/floats/float-in-rtl-vlr-2d.html
+++ b/layout/reftests/floats/float-in-rtl-slr-2d.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
   <div dir="rtl" style="inline-size:-moz-fit-content">
     <div style="height:300px">
       <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
     </div>
     <div style="height:200px">
       <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
     </div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-3-ref.html
copy to layout/reftests/floats/float-in-rtl-slr-3-ref.html
--- a/layout/reftests/floats/float-in-rtl-vlr-3-ref.html
+++ b/layout/reftests/floats/float-in-rtl-slr-3-ref.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
 <html>
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
- <div style="height:600px;">
+ <div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:300px;"></div>
+    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:200px;"></div>
   </div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:400px;"></div>
+    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:100px;"></div>
   </div>
-  <div class="vlr" style="background:silver; height: 100%;">
-    <div style="padding-top:500px; direction:rtl">This text should appear BELOW the green and red blocks.</div>
+  <div class="slr" style="background:silver; height:calc(100vh - 16px);">
+    <div style="margin-bottom:calc(100vh - 116px); direction:rtl">This text should appear ABOVE the green and red blocks.</div>
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-3a.html
copy to layout/reftests/floats/float-in-rtl-slr-3a.html
--- a/layout/reftests/floats/float-in-rtl-vlr-3a.html
+++ b/layout/reftests/floats/float-in-rtl-slr-3a.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; direction: rtl;">
+ <div style="direction: rtl;">
   <div dir="ltr" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-3b.html
copy to layout/reftests/floats/float-in-rtl-slr-3b.html
--- a/layout/reftests/floats/float-in-rtl-vlr-3b.html
+++ b/layout/reftests/floats/float-in-rtl-slr-3b.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; direction: rtl;">
+ <div style="direction: rtl;">
   <div style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div dir="ltr" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-3c.html
copy to layout/reftests/floats/float-in-rtl-slr-3c.html
--- a/layout/reftests/floats/float-in-rtl-vlr-3c.html
+++ b/layout/reftests/floats/float-in-rtl-slr-3c.html
@@ -1,26 +1,23 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; direction: rtl;">
+ <div style="direction: rtl;">
   <div dir="ltr" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
   </div>
   <div dir="ltr" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left"></div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+This text should appear ABOVE the green and red blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-3d.html
copy to layout/reftests/floats/float-in-rtl-slr-3d.html
--- a/layout/reftests/floats/float-in-rtl-vlr-3d.html
+++ b/layout/reftests/floats/float-in-rtl-slr-3d.html
@@ -1,28 +1,25 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; direction: rtl;">
+ <div style="direction: rtl;">
   <div dir="ltr" style="inline-size:-moz-fit-content">
     <div style="height:300px">
       <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:left;"></div>
     </div>
     <div style="height:200px">
       <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:left;"></div>
     </div>
   </div>
   <div style="background:silver">
-This text should appear BELOW the green and red blocks.
+ This text should appear ABOVE the green and red blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-4-ref.html
copy to layout/reftests/floats/float-in-rtl-slr-4-ref.html
--- a/layout/reftests/floats/float-in-rtl-vlr-4-ref.html
+++ b/layout/reftests/floats/float-in-rtl-slr-4-ref.html
@@ -1,26 +1,24 @@
 <!DOCTYPE html>
 <html>
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
+.slr {
+  writing-mode: sideways-lr;
+  direction: rtl;
 }
 </style>
 </head>
 <body>
- <div style="height:600px;">
+ <div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:500px;"></div>
+    <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);display:inline-block;margin-top:0"></div>
   </div>
   <div style="height:0px;width:0px;overflow:visible">
-    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:400px;"></div>
+    <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);display:inline-block;margin-top:100px"></div>
   </div>
-  <div class="vlr" style="background:silver; height: 100%;">
-    <div style="padding-bottom: 200px; direction:rtl">This text should appear ABOVE the red and green blocks.</div>
+  <div class="slr" style="background:silver; height: calc(100vh - 16px);">
+    <div style="height: calc(100vh - 216px); margin-top: 200px">This text should appear BELOW the red and green blocks.</div>
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-4a.html
copy to layout/reftests/floats/float-in-rtl-slr-4a.html
--- a/layout/reftests/floats/float-in-rtl-vlr-4a.html
+++ b/layout/reftests/floats/float-in-rtl-slr-4a.html
@@ -1,27 +1,24 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
-   direction: rtl;
+.slr {
+  writing-mode: sideways-lr;
+  direction: rtl;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; margin-top:0; margin-bottom:auto;">
+ <div>
   <div dir="ltr" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-4b.html
copy to layout/reftests/floats/float-in-rtl-slr-4b.html
--- a/layout/reftests/floats/float-in-rtl-vlr-4b.html
+++ b/layout/reftests/floats/float-in-rtl-slr-4b.html
@@ -1,27 +1,24 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
-   direction: rtl;
+.slr {
+  writing-mode: sideways-lr;
+  direction: rtl;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; margin-top:0; margin-bottom:auto;">
+ <div>
   <div style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div dir="ltr" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-4c.html
copy to layout/reftests/floats/float-in-rtl-slr-4c.html
--- a/layout/reftests/floats/float-in-rtl-vlr-4c.html
+++ b/layout/reftests/floats/float-in-rtl-slr-4c.html
@@ -1,27 +1,24 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
-   direction: rtl;
+.slr {
+  writing-mode: sideways-lr;
+  direction: rtl;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; margin-top:0; margin-bottom:auto;">
+ <div>
   <div dir="ltr" style="height:300px">
     <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
   </div>
   <div dir="ltr" style="height:200px">
     <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
  </div>
 </body>
 </html>
copy from layout/reftests/floats/float-in-rtl-vlr-4d.html
copy to layout/reftests/floats/float-in-rtl-slr-4d.html
--- a/layout/reftests/floats/float-in-rtl-vlr-4d.html
+++ b/layout/reftests/floats/float-in-rtl-slr-4d.html
@@ -1,29 +1,26 @@
 <!DOCTYPE html>
-<html class="vlr">
+<html class="slr">
 <head>
-<title>Bug 1114329 testcase</title>
 <style>
- .vlr {
-   writing-mode: vertical-lr;
-   -webkit-writing-mode: vertical-lr;
-   writing-mode: tb-lr;
-   direction: rtl;
+.slr {
+  writing-mode: sideways-lr;
+  direction: rtl;
 }
 </style>
 </head>
 <body>
- <div style="height:600px; margin-top:0; margin-bottom:auto;">
+ <div>
   <div dir="ltr" style="inline-size:-moz-fit-content">
     <div style="height:300px">
       <div style="height:100px;width:120px;background:rgba(0,255,0,0.8);float:right;"></div>
     </div>
     <div style="height:200px">
       <div style="height:100px;width:150px;background:rgba(255,0,0,0.8);float:right;"></div>
     </div>
   </div>
   <div style="background:silver">
-This text should appear ABOVE the red and green blocks.
+This text should appear BELOW the red and green blocks.
   </div>
  </div>
 </body>
 </html>
--- a/layout/reftests/floats/reftest.list
+++ b/layout/reftests/floats/reftest.list
@@ -83,8 +83,25 @@ default-preferences pref(layout.css.vert
 == float-in-rtl-vrl-4a.html float-in-rtl-vrl-4-ref.html
 == float-in-rtl-vrl-4b.html float-in-rtl-vrl-4-ref.html
 == float-in-rtl-vrl-4c.html float-in-rtl-vrl-4-ref.html
 == float-in-rtl-vrl-4d.html float-in-rtl-vrl-4-ref.html
 fuzzy-if(OSX==1010,26,7) fuzzy-if(Android,16,2) == orthogonal-floats-1a.html orthogonal-floats-1-ref.html
 fuzzy-if(OSX==1010,26,7) == orthogonal-floats-1b.html orthogonal-floats-1-ref.html
 fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,116,700) HTTP(..) == orthogonal-floats-1c.html orthogonal-floats-1-ref.html
 fuzzy-if(OSX==1010,103,802) fuzzy-if(winWidget,116,700) HTTP(..) == orthogonal-floats-1d.html orthogonal-floats-1-ref.html
+
+== float-in-rtl-slr-1a.html float-in-rtl-slr-1-ref.html
+== float-in-rtl-slr-1b.html float-in-rtl-slr-1-ref.html
+== float-in-rtl-slr-1c.html float-in-rtl-slr-1-ref.html
+== float-in-rtl-slr-1d.html float-in-rtl-slr-1-ref.html
+== float-in-rtl-slr-2a.html float-in-rtl-slr-2-ref.html
+== float-in-rtl-slr-2b.html float-in-rtl-slr-2-ref.html
+== float-in-rtl-slr-2c.html float-in-rtl-slr-2-ref.html
+== float-in-rtl-slr-2d.html float-in-rtl-slr-2-ref.html
+== float-in-rtl-slr-3a.html float-in-rtl-slr-3-ref.html
+== float-in-rtl-slr-3b.html float-in-rtl-slr-3-ref.html
+== float-in-rtl-slr-3c.html float-in-rtl-slr-3-ref.html
+== float-in-rtl-slr-3d.html float-in-rtl-slr-3-ref.html
+== float-in-rtl-slr-4a.html float-in-rtl-slr-4-ref.html
+== float-in-rtl-slr-4b.html float-in-rtl-slr-4-ref.html
+== float-in-rtl-slr-4c.html float-in-rtl-slr-4-ref.html
+== float-in-rtl-slr-4d.html float-in-rtl-slr-4-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { padding: 5px; display: inline-block; }
+span { display: block; width: 2em; }
+span:nth-child(1) { background: red;    height: 1em; }
+span:nth-child(2) { background: orange; height: 1.1em; }
+span:nth-child(3) { background: yellow; height: 1.3em; }
+span:nth-child(4) { background: green;  height: 1.5em; }
+span:nth-child(5) { background: blue;   height: 1.7em; }
+span:nth-child(6) { background: indigo; height: 1.9em; }
+span:nth-child(7) { background: violet; height: 2em; }
+</style>
+</head>
+
+<body>
+
+<p>All four columns should look the same:
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { padding: 5px; display: inline-block; }
+.vlr { writing-mode: vertical-lr; text-orientation: sideways; }
+.vrl { writing-mode: vertical-rl; text-orientation: sideways; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+span { display: inline-block; block-size: 2em; vertical-align: middle; }
+span:nth-child(1) { background: red;    inline-size: 1em; }
+span:nth-child(2) { background: orange; inline-size: 1.1em; }
+span:nth-child(3) { background: yellow; inline-size: 1.3em; }
+span:nth-child(4) { background: green;  inline-size: 1.5em; }
+span:nth-child(5) { background: blue;   inline-size: 1.7em; }
+span:nth-child(6) { background: indigo; inline-size: 1.9em; }
+span:nth-child(7) { background: violet; inline-size: 2em; }
+</style>
+</head>
+
+<body>
+
+<p>All four columns should look the same:
+<div dir=ltr class="vrl"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=ltr class="vlr"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=ltr class="srl"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=rtl class="slr"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-2-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { padding: 5px; display: inline-block; }
+span { display: block; width: 2em; }
+span:nth-child(7) { background: red;    height: 1em; }
+span:nth-child(6) { background: orange; height: 1.1em; }
+span:nth-child(5) { background: yellow; height: 1.3em; }
+span:nth-child(4) { background: green;  height: 1.5em; }
+span:nth-child(3) { background: blue;   height: 1.7em; }
+span:nth-child(2) { background: indigo; height: 1.9em; }
+span:nth-child(1) { background: violet; height: 2em; }
+</style>
+</head>
+
+<body>
+
+<p>All four columns should look the same:
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { padding: 5px; display: inline-block; }
+.vlr { writing-mode: vertical-lr; text-orientation: sideways; }
+.vrl { writing-mode: vertical-rl; text-orientation: sideways; }
+.slr { writing-mode: sideways-lr; }
+.srl { writing-mode: sideways-rl; }
+span { display: inline-block; block-size: 2em; vertical-align: middle; }
+span:nth-child(1) { background: red;    inline-size: 1em; }
+span:nth-child(2) { background: orange; inline-size: 1.1em; }
+span:nth-child(3) { background: yellow; inline-size: 1.3em; }
+span:nth-child(4) { background: green;  inline-size: 1.5em; }
+span:nth-child(5) { background: blue;   inline-size: 1.7em; }
+span:nth-child(6) { background: indigo; inline-size: 1.9em; }
+span:nth-child(7) { background: violet; inline-size: 2em; }
+</style>
+</head>
+
+<body>
+
+<p>All four columns should look the same:
+<div dir=rtl class="vrl"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=rtl class="vlr"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=rtl class="srl"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
+<div dir=ltr class="slr"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-3-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { border: 1px solid silver; padding: 5px; writing-mode: sideways-rl; transform: rotate(180deg); }
+span { padding: 5px; color: transparent; }
+span:nth-child(1) { background: red;    }
+span:nth-child(2) { background: orange; }
+span:nth-child(3) { background: yellow; }
+span:nth-child(4) { background: green;  }
+span:nth-child(5) { background: blue;   }
+span:nth-child(6) { background: indigo; }
+span:nth-child(7) { background: violet; }
+</style>
+</head>
+
+<body>
+
+<div><span>one</span> <span>two</span> <span>three</span> <span>four</span>
+ <span>five</span> <span>six</span> <span>seven</span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-3.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { border: 1px solid silver; padding: 5px; writing-mode: sideways-lr; }
+span { padding: 5px; color: transparent; }
+span:nth-child(1) { background: red;    }
+span:nth-child(2) { background: orange; }
+span:nth-child(3) { background: yellow; }
+span:nth-child(4) { background: green;  }
+span:nth-child(5) { background: blue;   }
+span:nth-child(6) { background: indigo; }
+span:nth-child(7) { background: violet; }
+</style>
+</head>
+
+<body>
+
+<div><span>one</span> <span>two</span> <span>three</span> <span>four</span>
+ <span>five</span> <span>six</span> <span>seven</span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-4-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { display: inline-block; border: 1px solid silver; padding: 5px;
+      line-height: 2em; writing-mode: sideways-rl; transform: rotate(180deg); }
+span { padding: 5px; color: transparent; }
+span:nth-child(1) { background: red;    }
+span:nth-child(2) { background: orange; }
+span:nth-child(3) { background: yellow; }
+span:nth-child(4) { background: green;  }
+span:nth-child(5) { background: blue;   }
+span:nth-child(6) { background: indigo; }
+span:nth-child(7) { background: violet; }
+</style>
+</head>
+
+<body>
+
+<div><span>"One</span> <span>two</span>
+ <span>three</span> <span>four</span> <span>five</span>
+ <span>six</span> <span>seven."</span></div>
+
+<div dir=rtl><span>"One</span> <span>two</span>
+ <span>three</span> <span>four</span> <span>five</span>
+ <span>six</span> <span>seven."</span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-4.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<style>
+div { display: inline-block; border: 1px solid silver; padding: 5px;
+      line-height: 2em; writing-mode: sideways-lr; }
+span { padding: 5px; color: transparent; }
+span:nth-child(1) { background: red;    }
+span:nth-child(2) { background: orange; }
+span:nth-child(3) { background: yellow; }
+span:nth-child(4) { background: green;  }
+span:nth-child(5) { background: blue;   }
+span:nth-child(6) { background: indigo; }
+span:nth-child(7) { background: violet; }
+</style>
+</head>
+
+<body>
+
+<div><span>"One</span> <span>two</span>
+ <span>three</span> <span>four</span> <span>five</span>
+ <span>six</span> <span>seven."</span></div>
+
+<div dir=rtl><span>"One</span> <span>two</span>
+ <span>three</span> <span>four</span> <span>five</span>
+ <span>six</span> <span>seven."</span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-decoration-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<div style="width:300px;
+            height:300px;
+            text-align:center;
+            line-height:150px;
+            transform:rotate(-90deg);">
+  <span style="text-decoration:underline;">under</span>
+  <span style="text-decoration:overline;">over</span>
+  <span style="text-decoration:line-through;">through</span>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1193519-sideways-lr-decoration-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<div style="width:300px;
+            height:300px;
+            text-align:center;
+            line-height:150px;
+            writing-mode:sideways-lr;">
+  <span style="text-decoration:underline;">under</span>
+  <span style="text-decoration:overline;">over</span>
+  <span style="text-decoration:line-through;">through</span>
+</div>
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -147,16 +147,24 @@ test-pref(dom.meta-viewport.enabled,true
 == 1172774-percent-padding-1.html 1172774-percent-horizontal-ref.html
 == 1172774-percent-padding-2.html 1172774-percent-horizontal-ref.html
 == 1172774-percent-padding-3.html 1172774-percent-vertical-ref.html
 == 1172774-percent-padding-4.html 1172774-percent-vertical-ref.html
 == 1174450-intrinsic-sizing.html 1174450-intrinsic-sizing-ref.html
 == 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html
 == 1188061-1-nsChangeHint_ClearAncestorIntrinsics.html 1188061-1-nsChangeHint_ClearAncestorIntrinsics-ref.html
 == 1188061-2-nsChangeHint_UpdateComputedBSize.html 1188061-2-nsChangeHint_UpdateComputedBSize-ref.html
+
+# tests involving sideways-lr mode
+== 1193519-sideways-lr-1.html 1193519-sideways-lr-1-ref.html
+== 1193519-sideways-lr-2.html 1193519-sideways-lr-2-ref.html
+== 1193519-sideways-lr-3.html 1193519-sideways-lr-3-ref.html
+== 1193519-sideways-lr-4.html 1193519-sideways-lr-4-ref.html
+fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(cocoaWidget,65,69) == 1193519-sideways-lr-decoration-1.html 1193519-sideways-lr-decoration-1-ref.html
+
 == 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html
 == 1205787-legacy-svg-values-1.html 1205787-legacy-svg-values-1-ref.html
 
 # Suite of tests from GĂ©rard Talbot in bug 1079151
 include abspos/reftest.list
 
 # Tests for tables with vertical writing modes
 include tables/reftest.list
--- a/layout/reftests/writing-mode/tables/reftest.list
+++ b/layout/reftests/writing-mode/tables/reftest.list
@@ -82,8 +82,12 @@ fuzzy-if(Android,255,38) == table-captio
 == border-collapse-bevels-1a.html border-collapse-bevels-1-ref.html
 fuzzy-if(cocoaWidget,23,162) == border-collapse-bevels-1b.html border-collapse-bevels-1-ref.html
 fuzzy-if(cocoaWidget,23,162) == border-collapse-bevels-1c.html border-collapse-bevels-1-ref.html
 fuzzy-if(cocoaWidget,23,162) == border-collapse-bevels-1d.html border-collapse-bevels-1-ref.html
 fuzzy-if(cocoaWidget,23,162) == border-collapse-bevels-1e.html border-collapse-bevels-1-ref.html
 
 == vertical-rl-row-progression-1a.html vertical-rl-row-progression-1-ref.html
 == vertical-rl-row-progression-1b.html vertical-rl-row-progression-1-ref.html
+== sideways-lr-row-progression-1a.html sideways-lr-row-progression-1-ref.html
+== sideways-lr-row-progression-1b.html sideways-lr-row-progression-1-ref.html
+== sideways-rl-row-progression-1a.html sideways-rl-row-progression-1-ref.html
+== sideways-rl-row-progression-1b.html sideways-rl-row-progression-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/tables/sideways-lr-row-progression-1-ref.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>CSS Reference: sideways-lr Table Row/Rowgroup/Cell Ordering</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jfkthame@gmail.com">
+<link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on vertical-rl Table Row/Rowgroup/Cell Ordering tests by Elika J. Etemad -->
+
+<style>
+  table {
+    border-spacing: 0;
+    margin: 1em;
+  }
+  td {
+    width: 1em;
+    height: 1em;
+    border: solid gray;
+  }
+
+  .a { background: navy}
+  .b { background: blue }
+  .c { background: aqua }
+  .d { background: teal }
+  .e { background: purple }
+  .f { background: fuchsia }
+  .g { background: yellow }
+  .h { background: orange }
+</style>
+
+<table>
+  <tr>
+    <td rowspan=2>
+    <td class="e">
+    <td rowspan=2 colspan=2>
+    <td rowspan=3>
+    <td class="d">
+  </tr>
+  <tr>
+    <td rowspan=2>
+    <td class="c">
+  </tr>
+  <tr>
+    <td class="b">
+    <td class="f">
+    <td class="g">
+    <td rowspan=2>
+  </tr>
+  <tr>
+    <td class="a">
+    <td colspan=3>
+    <td class="h">
+  </tr>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/tables/sideways-lr-row-progression-1a.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>CSS Test: sideways-lr Table Row/Rowgroup/Cell Ordering</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jfkthame@gmail.com">
+<meta name="assert" content="This test checks that sideways-lr tables order rows/rowgroups left to right and cells bottom-to-top (LTR) per 'direction'.">
+<link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on vertical-rl Table Row/Rowgroup/Cell Ordering tests by Elika J. Etemad -->
+
+<style>
+  .test {
+    writing-mode: sideways-lr;
+  }
+
+  table {
+    border-spacing: 0;
+    margin: 1em;
+  }
+  td {
+    width: 1em;
+    height: 1em;
+    border: solid gray;
+  }
+
+  .a { background: navy}
+  .b { background: blue }
+  .c { background: aqua }
+  .d { background: teal }
+  .e { background: purple }
+  .f { background: fuchsia }
+  .g { background: yellow }
+  .h { background: orange }
+</style>
+
+<table class="test">
+  <thead>
+    <tr>
+      <td class="a">
+      <td class="b">
+      <td colspan=2>
+    </tr>
+  </thead>
+  <tfoot>
+    <tr>
+      <td colspan=2>
+      <td class="c">
+      <td class="d">
+    </tr>
+  </tfoot>
+  <tbody>
+    <tr>
+      <td rowspan=3>
+      <td colspan=2>
+      <td class="e">
+    </tr>
+    <tr>
+      <td class="f">
+      <td rowspan=2 colspan=2>
+    </tr>
+    <tr>
+      <td class="g">
+    </tr>
+  </tbody>
+  <tbody>
+    <tr>
+      <td class="h">
+      <td colspan=3>
+    </tr>
+  </tbody>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/tables/sideways-lr-row-progression-1b.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>CSS Test: sideways-lr Table Row/Rowgroup/Cell Ordering</title>
+<link rel="author" title="Jonathan Kew" href="mailto:jfkthame@gmail.com">
+<meta name="assert" content="This test checks that sideways-lr tables order rows/rowgroups left to right and cells top-to-bottom (RTL) per 'direction'.">
+<link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on vertical-rl Table Row/Rowgroup/Cell Ordering tests by Elika J. Etemad -->
+
+<style>
+  .test {
+    writing-mode: sideways-lr;
+    direction: rtl;
+  }
+
+  table {
+    border-spacing: 0;
+    margin: 1em;
+  }
+  td {
+    width: 1em;
+    height: 1em;
+    border: solid gray;
+  }
+
+  .a { background: navy}
+  .b { background: blue }
+  .c { background: aqua }
+  .d { background: teal }
+  .e { background: purple }
+  .f { background: fuchsia }
+  .g { background: yellow }
+  .h { background: orange }
+</style>
+
+<table class="test">
+  <thead>
+    <tr>
+      <td colspan=2>
+      <td class="b">
+      <td class="a">
+    </tr>
+  </thead>
+  <tfoot>
+    <tr>
+      <td class="d">
+      <td class="c">
+      <td colspan=2>
+    </tr>
+  </tfoot>
+  <tbody>
+    <tr>
+      <td class="e">
+      <td colspan=2>
+      <td rowspan=3>
+    </tr>
+    <tr>
+      <td rowspan=2 colspan=2>
+      <td class="f">
+    </tr>
+    <tr>
+      <td class="g">
+    </tr>
+  </tbody>
+  <tbody>
+    <tr>
+      <td colspan=3>
+      <td class="h">
+    </tr>
+  </tbody>
+</table>
copy from layout/reftests/writing-mode/tables/vertical-rl-row-progression-1-ref.html
copy to layout/reftests/writing-mode/tables/sideways-rl-row-progression-1-ref.html
--- a/layout/reftests/writing-mode/tables/vertical-rl-row-progression-1-ref.html
+++ b/layout/reftests/writing-mode/tables/sideways-rl-row-progression-1-ref.html
@@ -1,16 +1,16 @@
 <!DOCTYPE html>
-<title>CSS Reference: vertical-rl Table Row/Rowgroup/Cell Ordering</title>
-<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
+<title>CSS Reference: sideways-rl Table Row/Rowgroup/Cell Ordering</title>
 <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on equivalent testcase for vertical-rl mode by Elika J. Etemad -->
 
 <style>
   .test {
-    writing-mode: vertical-rl;
+    writing-mode: sideways-rl;
   }
 
   table {
     border-spacing: 0;
     margin: 1em;
   }
   td {
     width: 1em;
@@ -29,22 +29,26 @@
 </style>
 
 <table class="reference">
   <tr>
     <td rowspan=2>
     <td class="h">
     <td colspan=3">
     <td class="a">
+  </tr>
   <tr>
     <td rowspan=3">
     <td class="g">
     <td class="f">
     <td rowspan=2">
     <td class="b">
+  </tr>
   <tr>
     <td class="c">
     <td rowspan=2 colspan=2>
     <td rowspan=2>
+  </tr>
   <tr>
     <td class="d">
     <td class="e">
+  </tr>
 </table>
copy from layout/reftests/writing-mode/tables/vertical-rl-row-progression-1a.html
copy to layout/reftests/writing-mode/tables/sideways-rl-row-progression-1a.html
--- a/layout/reftests/writing-mode/tables/vertical-rl-row-progression-1a.html
+++ b/layout/reftests/writing-mode/tables/sideways-rl-row-progression-1a.html
@@ -1,17 +1,18 @@
 <!DOCTYPE html>
-<title>CSS Test: vertical-rl Table Row/Rowgroup/Cell Ordering</title>
-<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
-<meta name="assert" content="This test checks that vertical-rl tables order rows/rowgroups right to left and cells top-to-bottom (LTR) per 'direction'.">
+<title>CSS Test: sideways-rl Table Row/Rowgroup/Cell Ordering</title>
+<meta name="assert" content="This test checks that sideways-rl tables order rows/rowgroups right to left and cells top-to-bottom (LTR) per 'direction'.">
 <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on equivalent testcase for vertical-rl mode by Elika J. Etemad -->
 
 <style>
   .test {
-    writing-mode: vertical-rl;
+    writing-mode: sideways-rl;
+    direction: ltr;
   }
 
   table {
     border-spacing: 0;
     margin: 1em;
   }
   td {
     width: 1em;
@@ -30,28 +31,38 @@
 </style>
 
 <table class="test">
   <thead>
     <tr>
       <td class="a">
       <td class="b">
       <td colspan=2>
+    </tr>
+  </thead>
   <tfoot>
     <tr>
-      <td colspan=2> 
+      <td colspan=2>
       <td class="c">
       <td class="d">
+    </tr>
+  </tfoot>
   <tbody>
     <tr>
       <td rowspan=3>
       <td colspan=2>
       <td class="e">
+    </tr>
     <tr>
       <td class="f">
       <td rowspan=2 colspan=2>
+    </tr>
     <tr>
       <td class="g">
+    </tr>
+  </tbody>
   <tbody>
     <tr>
       <td class="h">
       <td colspan=3>
+    </tr>
+  </tbody>
 </table>
copy from layout/reftests/writing-mode/tables/vertical-rl-row-progression-1b.html
copy to layout/reftests/writing-mode/tables/sideways-rl-row-progression-1b.html
--- a/layout/reftests/writing-mode/tables/vertical-rl-row-progression-1b.html
+++ b/layout/reftests/writing-mode/tables/sideways-rl-row-progression-1b.html
@@ -1,17 +1,18 @@
 <!DOCTYPE html>
-<title>CSS Test: vertical-rl Table Row/Rowgroup/Cell Ordering</title>
-<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
-<meta name="assert" content="This test checks that vertical-rl tables order rows/rowgroups right to left and cells bottom-to-top (RTL) per 'direction'.">
+<title>CSS Test: sideways-rl Table Row/Rowgroup/Cell Ordering</title>
+<meta name="assert" content="This test checks that sideways-rl tables order rows/rowgroups right to left and cells bottom-to-top (RTL) per 'direction'.">
 <link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#writing-mode">
+<!-- based on equivalent testcase for vertical-rl mode by Elika J. Etemad -->
 
 <style>
   .test {
-    writing-mode: vertical-rl;
+    writing-mode: sideways-rl;
+    direction: rtl;
   }
 
   table {
     border-spacing: 0;
     margin: 1em;
   }
   td {
     width: 1em;
@@ -24,34 +25,44 @@
   .c { background: aqua }
   .d { background: teal }
   .e { background: purple }
   .f { background: fuchsia }
   .g { background: yellow }
   .h { background: orange }
 </style>
 
-<table dir=rtl class="test">
+<table class="test">
   <thead>
     <tr>
       <td colspan=2>
       <td class="b">
       <td class="a">
+    </tr>
+  </thead>
   <tfoot>
     <tr>
       <td class="d">
       <td class="c">
-      <td colspan=2> 
+      <td colspan=2>
+    </tr>
+  </tfoot>
   <tbody>
     <tr>
       <td class="e">
       <td colspan=2>
       <td rowspan=3>
+    </tr>
     <tr>
       <td rowspan=2 colspan=2>
       <td class="f">
+    </tr>
     <tr>
       <td class="g">
+    </tr>
+  </tbody>
   <tbody>
     <tr>
       <td colspan=3>
       <td class="h">
+    </tr>
+  </tbody>
 </table>
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -480,17 +480,17 @@ CSS_KEY(select-menu, select_menu)
 CSS_KEY(select-same, select_same)
 CSS_KEY(semi-condensed, semi_condensed)
 CSS_KEY(semi-expanded, semi_expanded)
 CSS_KEY(separate, separate)
 CSS_KEY(sepia, sepia)
 CSS_KEY(serif, serif)
 CSS_KEY(show, show)
 CSS_KEY(sideways, sideways)
-/*CSS_KEY(sideways-lr, sideways_lr)*/
+CSS_KEY(sideways-lr, sideways_lr)
 CSS_KEY(sideways-right, sideways_right) /* alias for 'sideways' */
 CSS_KEY(sideways-rl, sideways_rl)
 CSS_KEY(simp-chinese-formal, simp_chinese_formal)
 CSS_KEY(simp-chinese-informal, simp_chinese_informal)
 CSS_KEY(simplified, simplified)
 CSS_KEY(skew, skew)
 CSS_KEY(skewx, skewx)
 CSS_KEY(skewy, skewy)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1939,17 +1939,17 @@ const KTableValue nsCSSProps::kWordWrapK
   eCSSKeyword_break_word, NS_STYLE_WORDWRAP_BREAK_WORD,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const KTableValue nsCSSProps::kWritingModeKTable[] = {
   eCSSKeyword_horizontal_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB,
   eCSSKeyword_vertical_lr, NS_STYLE_WRITING_MODE_VERTICAL_LR,
   eCSSKeyword_vertical_rl, NS_STYLE_WRITING_MODE_VERTICAL_RL,
-/*  eCSSKeyword_sideways_lr, NS_STYLE_WRITING_MODE_SIDEWAYS_LR, */
+  eCSSKeyword_sideways_lr, NS_STYLE_WRITING_MODE_SIDEWAYS_LR,
   eCSSKeyword_sideways_rl, NS_STYLE_WRITING_MODE_SIDEWAYS_RL,
   eCSSKeyword_lr, NS_STYLE_WRITING_MODE_HORIZONTAL_TB,
   eCSSKeyword_lr_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB,
   eCSSKeyword_rl, NS_STYLE_WRITING_MODE_HORIZONTAL_TB,
   eCSSKeyword_rl_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB,
   eCSSKeyword_tb, NS_STYLE_WRITING_MODE_VERTICAL_RL,
   eCSSKeyword_tb_rl, NS_STYLE_WRITING_MODE_VERTICAL_RL,
   eCSSKeyword_UNKNOWN, -1
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4726,18 +4726,18 @@ if (SpecialPowers.getBoolPref("layout.cs
 
 if (SpecialPowers.getBoolPref("layout.css.vertical-text.enabled")) {
   var verticalTextProperties = {
     "writing-mode": {
       domProp: "writingMode",
       inherited: true,
       type: CSS_TYPE_LONGHAND,
       initial_values: [ "horizontal-tb", "lr", "lr-tb", "rl", "rl-tb" ],
-      other_values: [ "vertical-lr", "vertical-rl", "sideways-rl", "tb", "tb-rl" ],
-      invalid_values: [ "10px", "30%", "justify", "auto", "1em", "sideways-lr" ] /* sideways-lr not yet supported */
+      other_values: [ "vertical-lr", "vertical-rl", "sideways-rl", "sideways-lr", "tb", "tb-rl" ],
+      invalid_values: [ "10px", "30%", "justify", "auto", "1em" ]
     },
     "text-orientation": {
       domProp: "textOrientation",
       inherited: true,
       type: CSS_TYPE_LONGHAND,
       initial_values: [ "mixed" ],
       other_values: [ "upright", "sideways", "sideways-right" ], /* sideways-right alias for backward compatibility */
       invalid_values: [ "none", "3em", "sideways-left" ] /* sideways-left removed from CSS Writing Modes */
--- a/layout/style/test/test_logical_properties.html
+++ b/layout/style/test/test_logical_properties.html
@@ -67,23 +67,23 @@ var gWritingModes = [
   { style: [
       "writing-mode: vertical-rl; direction: ltr; ",
       "writing-mode: sideways-rl; direction: ltr; ",
     ],
     blockStart: "right", blockEnd: "left", inlineStart: "top", inlineEnd: "bottom",
     block: "horizontal", inline: "vertical" },
   { style: [
       "writing-mode: vertical-lr; direction: rtl; ",
-      // "writing-mode: sideways-lr; direction: ltr; ",
+      "writing-mode: sideways-lr; direction: ltr; ",
     ],
     blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top",
     block: "horizontal", inline: "vertical" },
   { style: [
       "writing-mode: vertical-lr; direction: ltr; ",
-      // "writing-mode: sideways-lr; direction: rtl; ",
+      "writing-mode: sideways-lr; direction: rtl; ",
     ],
     blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom",
     block: "horizontal", inline: "vertical" },
 ];
 
 function init() {
   gBoxPropertyGroups = [];
 
@@ -138,30 +138,16 @@ function init() {
     gAxisPropertyGroups.push({
       horizontal: `${aPrefix}width`, vertical: `${aPrefix}height`,
       inline: `${aPrefix}inline-size`, block: `${aPrefix}block-size`,
       type: "length",
       prerequisites:
         make_declaration(gCSSProperties[`${aPrefix}height`].prerequisites)
     });
   });
-
-  // Assume that the sideways-lr keyword is not parsed yet for writing-mode.
-  // When we start supporting this keyword, the entries in the .style
-  // properties of the gWritingModes objects above should be uncommented.
-  var s = document.createElement("style");
-  document.body.appendChild(s);
-
-  s.textContent = "div { }";
-  s.sheet.cssRules[0].style.writingMode = "sideways-lr";
-  todo(s.sheet.cssRules[0].style.writingMode, "sideways-lr",
-       "uncomment sideways-lr cases from gWritingModes and " +
-       "remove this todo()!");
-
-  s.remove();
 }
 
 function test_computed_values(aTestName, aRules, aExpectedValues) {
   gSheet.textContent = aRules;
   var cs = getComputedStyle(gTest);
   aExpectedValues.forEach(function(aPair) {
     is(cs.getPropertyValue(aPair[0]), aPair[1], `${aTestName}, ${aPair[0]}`);
   });
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -644,17 +644,19 @@ Mvhd::Mvhd(Box& aBox)
     mTimescale = reader->ReadU32();
     mDuration = reader->ReadU64();
   } else {
     reader->DiscardRemaining();
     return;
   }
   // More stuff that we don't care about
   reader->DiscardRemaining();
-  mValid = true;
+  if (mTimescale) {
+    mValid = true;
+  }
 }
 
 Mdhd::Mdhd(Box& aBox)
   : Mvhd(aBox)
 {
 }
 
 Trex::Trex(Box& aBox)
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -30,17 +30,19 @@ public:
     , mTimescale(0)
     , mDuration(0)
   {
   }
   explicit Mvhd(Box& aBox);
 
   Microseconds ToMicroseconds(int64_t aTimescaleUnits)
   {
-    return aTimescaleUnits * 1000000ll / mTimescale;
+    int64_t major = aTimescaleUnits / mTimescale;
+    int64_t remainder = aTimescaleUnits % mTimescale;
+    return major * 1000000ll + remainder * 1000000ll / mTimescale;
   }
 
   uint64_t mCreationTime;
   uint64_t mModificationTime;
   uint32_t mTimescale;
   uint64_t mDuration;
 };
 
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -502,22 +502,28 @@ status_t MPEG4Extractor::readMetaData() 
         mInitCheck = OK;
     } else {
         mInitCheck = err;
     }
 
     CHECK_NE(err, (status_t)NO_INIT);
 
     // copy pssh data into file metadata
-    int psshsize = 0;
+    uint64_t psshsize = 0;
     for (size_t i = 0; i < mPssh.Length(); i++) {
         psshsize += 20 + mPssh[i].datalen;
     }
+    if (psshsize > kMAX_ALLOCATION) {
+        return ERROR_MALFORMED;
+    }
     if (psshsize) {
         char *buf = (char*)malloc(psshsize);
+        if (!buf) {
+            return ERROR_MALFORMED;
+        }
         char *ptr = buf;
         for (size_t i = 0; i < mPssh.Length(); i++) {
             memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
             memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
             ptr += (20 + mPssh[i].datalen);
         }
         mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
         free(buf);
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -2001,23 +2001,37 @@ JsepSessionImpl::CreateSsrc(uint32_t* ss
 
   return NS_OK;
 }
 
 void
 JsepSessionImpl::SetupDefaultCodecs()
 {
   // Supported audio codecs.
+  // Per jmspeex on IRC:
+  // For 32KHz sampling, 28 is ok, 32 is good, 40 should be really good
+  // quality.  Note that 1-2Kbps will be wasted on a stereo Opus channel
+  // with mono input compared to configuring it for mono.
+  // If we reduce bitrate enough Opus will low-pass us; 16000 will kill a
+  // 9KHz tone.  This should be adaptive when we're at the low-end of video
+  // bandwidth (say <100Kbps), and if we're audio-only, down to 8 or
+  // 12Kbps.
   mSupportedCodecs.values.push_back(new JsepAudioCodecDescription(
       "109",
       "opus",
       48000,
       2,
       960,
-      16000));
+#ifdef WEBRTC_GONK
+      // TODO Move this elsewhere to be adaptive to rate - Bug 1207925
+      16000 // B2G uses lower capture sampling rate
+#else
+      40000
+#endif
+      ));
 
   mSupportedCodecs.values.push_back(new JsepAudioCodecDescription(
       "9",
       "G722",
       8000,
       1,
       320,
       64000));
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -43,16 +43,19 @@
 #include "mozilla/PeerIdentity.h"
 #endif
 #include "mozilla/gfx/Point.h"
 #include "mozilla/gfx/Types.h"
 #include "mozilla/UniquePtr.h"
 
 #include "logging.h"
 
+// Should come from MediaEngineWebRTC.h, but that's a pain to include here
+#define DEFAULT_SAMPLE_RATE 32000
+
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 // Logging context
 MOZ_MTLOG_MODULE("mediapipeline")
 
 namespace mozilla {
@@ -1327,17 +1330,17 @@ void GenericReceiveListener::AddSelf(Med
   RefPtr<TrackAddedCallback> callback = new GenericReceiveCallback(this);
   AddTrackAndListener(source_, track_id_, track_rate_, this, segment, callback,
                       queue_track_);
 }
 
 MediaPipelineReceiveAudio::PipelineListener::PipelineListener(
     SourceMediaStream * source, TrackID track_id,
     const RefPtr<MediaSessionConduit>& conduit, bool queue_track)
-  : GenericReceiveListener(source, track_id, 16000, queue_track), // XXX rate assumption
+  : GenericReceiveListener(source, track_id, DEFAULT_SAMPLE_RATE, queue_track), // XXX rate assumption
     conduit_(conduit)
 {
   MOZ_ASSERT(track_rate_%100 == 0);
 }
 
 void MediaPipelineReceiveAudio::PipelineListener::
 NotifyPull(MediaStreamGraph* graph, StreamTime desired_time) {
   MOZ_ASSERT(source_);
--- a/memory/volatile/VolatileBuffer.h
+++ b/memory/volatile/VolatileBuffer.h
@@ -4,16 +4,17 @@
 
 #ifndef mozalloc_VolatileBuffer_h
 #define mozalloc_VolatileBuffer_h
 
 #include "mozilla/mozalloc.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/RefCounted.h"
 
 /* VolatileBuffer
  *
  * This class represents a piece of memory that can potentially be reclaimed
  * by the OS when not in use. As long as there are one or more
  * VolatileBufferPtrs holding on to a VolatileBuffer, the memory will remain
  * available. However, when there are no VolatileBufferPtrs holding a
  * VolatileBuffer, the OS can purge the pages if it wants to. The OS can make
new file mode 100644
--- /dev/null
+++ b/mfbt/RefCounted.h
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* CRTP refcounting templates.  Do not use unless you are an Expert. */
+
+#ifndef mozilla_RefCounted_h
+#define mozilla_RefCounted_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "mozilla/RefCountType.h"
+#include "mozilla/TypeTraits.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+#include "nsXPCOM.h"
+#endif
+
+#if defined(MOZILLA_INTERNAL_API) && \
+    !defined(MOZILLA_XPCOMRT_API) && \
+    (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
+#define MOZ_REFCOUNTED_LEAK_CHECKING
+#endif
+
+namespace mozilla {
+
+template<typename T> class RefPtr;
+
+/**
+ * RefCounted<T> is a sort of a "mixin" for a class T.  RefCounted
+ * manages, well, refcounting for T, and because RefCounted is
+ * parameterized on T, RefCounted<T> can call T's destructor directly.
+ * This means T doesn't need to have a virtual dtor and so doesn't
+ * need a vtable.
+ *
+ * RefCounted<T> is created with refcount == 0.  Newly-allocated
+ * RefCounted<T> must immediately be assigned to a RefPtr to make the
+ * refcount > 0.  It's an error to allocate and free a bare
+ * RefCounted<T>, i.e. outside of the RefPtr machinery.  Attempts to
+ * do so will abort DEBUG builds.
+ *
+ * Live RefCounted<T> have refcount > 0.  The lifetime (refcounts) of
+ * live RefCounted<T> are controlled by nsRefPtr<T> and
+ * nsRefPtr<super/subclass of T>.  Upon a transition from refcounted==1
+ * to 0, the RefCounted<T> "dies" and is destroyed.  The "destroyed"
+ * state is represented in DEBUG builds by refcount==0xffffdead.  This
+ * state distinguishes use-before-ref (refcount==0) from
+ * use-after-destroy (refcount==0xffffdead).
+ *
+ * Note that when deriving from RefCounted or AtomicRefCounted, you
+ * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
+ * section of your class, where ClassName is the name of your class.
+ */
+namespace detail {
+const MozRefCountType DEAD = 0xffffdead;
+
+// When building code that gets compiled into Gecko, try to use the
+// trace-refcount leak logging facilities.
+#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
+class RefCountLogger
+{
+public:
+  static void logAddRef(const void* aPointer, MozRefCountType aRefCount,
+                        const char* aTypeName, uint32_t aInstanceSize)
+  {
+    MOZ_ASSERT(aRefCount != DEAD);
+    NS_LogAddRef(const_cast<void*>(aPointer), aRefCount, aTypeName,
+                 aInstanceSize);
+  }
+
+  static void logRelease(const void* aPointer, MozRefCountType aRefCount,
+                         const char* aTypeName)
+  {
+    MOZ_ASSERT(aRefCount != DEAD);
+    NS_LogRelease(const_cast<void*>(aPointer), aRefCount, aTypeName);
+  }
+};
+#endif
+
+// This is used WeakPtr.h as well as this file.
+enum RefCountAtomicity
+{
+  AtomicRefCount,
+  NonAtomicRefCount
+};
+
+template<typename T, RefCountAtomicity Atomicity>
+class RefCounted
+{
+  friend class RefPtr<T>;
+
+protected:
+  RefCounted() : mRefCnt(0) {}
+  ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
+
+public:
+  // Compatibility with nsRefPtr.
+  void AddRef() const
+  {
+    // Note: this method must be thread safe for AtomicRefCounted.
+    MOZ_ASSERT(int32_t(mRefCnt) >= 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+    ++mRefCnt;
+#else
+    const char* type = static_cast<const T*>(this)->typeName();
+    uint32_t size = static_cast<const T*>(this)->typeSize();
+    const void* ptr = static_cast<const T*>(this);
+    MozRefCountType cnt = ++mRefCnt;
+    detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
+#endif
+  }
+
+  void Release() const
+  {
+    // Note: this method must be thread safe for AtomicRefCounted.
+    MOZ_ASSERT(int32_t(mRefCnt) > 0);
+#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
+    MozRefCountType cnt = --mRefCnt;
+#else
+    const char* type = static_cast<const T*>(this)->typeName();
+    const void* ptr = static_cast<const T*>(this);
+    MozRefCountType cnt = --mRefCnt;
+    // Note: it's not safe to touch |this| after decrementing the refcount,
+    // except for below.
+    detail::RefCountLogger::logRelease(ptr, cnt, type);
+#endif
+    if (0 == cnt) {
+      // Because we have atomically decremented the refcount above, only
+      // one thread can get a 0 count here, so as long as we can assume that
+      // everything else in the system is accessing this object through
+      // RefPtrs, it's safe to access |this| here.
+#ifdef DEBUG
+      mRefCnt = detail::DEAD;
+#endif
+      delete static_cast<const T*>(this);
+    }
+  }
+
+  // Compatibility with wtf::RefPtr.
+  void ref() { AddRef(); }
+  void deref() { Release(); }
+  MozRefCountType refCount() const { return mRefCnt; }
+  bool hasOneRef() const
+  {
+    MOZ_ASSERT(mRefCnt > 0);
+    return mRefCnt == 1;
+  }
+
+private:
+  mutable typename Conditional<Atomicity == AtomicRefCount,
+                               Atomic<MozRefCountType>,
+                               MozRefCountType>::Type mRefCnt;
+};
+
+#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
+// Passing override for the optional argument marks the typeName and
+// typeSize functions defined by this macro as overrides.
+#define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...) \
+  virtual const char* typeName() const __VA_ARGS__ { return #T; } \
+  virtual size_t typeSize() const __VA_ARGS__ { return sizeof(*this); }
+#else
+#define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...)
+#endif
+
+// Note that this macro is expanded unconditionally because it declares only
+// two small inline functions which will hopefully get eliminated by the linker
+// in non-leak-checking builds.
+#define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \
+  const char* typeName() const { return #T; } \
+  size_t typeSize() const { return sizeof(*this); }
+
+} // namespace detail
+
+template<typename T>
+class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
+{
+public:
+  ~RefCounted()
+  {
+    static_assert(IsBaseOf<RefCounted, T>::value,
+                  "T must derive from RefCounted<T>");
+  }
+};
+
+namespace external {
+
+/**
+ * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
+ * reference counter.
+ *
+ * NOTE: Please do not use this class, use NS_INLINE_DECL_THREADSAFE_REFCOUNTING
+ * instead.
+ */
+template<typename T>
+class AtomicRefCounted :
+  public mozilla::detail::RefCounted<T, mozilla::detail::AtomicRefCount>
+{
+public:
+  ~AtomicRefCounted()
+  {
+    static_assert(IsBaseOf<AtomicRefCounted, T>::value,
+                  "T must derive from AtomicRefCounted<T>");
+  }
+};
+
+} // namespace external
+
+} // namespace mozilla
+
+#endif // mozilla_RefCounted_h
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -9,216 +9,36 @@
 #ifndef mozilla_RefPtr_h
 #define mozilla_RefPtr_h
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
+#include "mozilla/RefCounted.h"
 #include "mozilla/RefCountType.h"
 #include "mozilla/nsRefPtr.h"
 #include "mozilla/TypeTraits.h"
 #if defined(MOZILLA_INTERNAL_API)
 #include "nsXPCOM.h"
 #endif
 
 #if defined(MOZILLA_INTERNAL_API) && \
     !defined(MOZILLA_XPCOMRT_API) && \
     (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
 #define MOZ_REFCOUNTED_LEAK_CHECKING
 #endif
 
 namespace mozilla {
 
-template<typename T> class RefCounted;
-template<typename T> class RefPtr;
 template<typename T> class OutParamRef;
 template<typename T> OutParamRef<T> byRef(RefPtr<T>&);
 
 /**
- * RefCounted<T> is a sort of a "mixin" for a class T.  RefCounted
- * manages, well, refcounting for T, and because RefCounted is
- * parameterized on T, RefCounted<T> can call T's destructor directly.
- * This means T doesn't need to have a virtual dtor and so doesn't
- * need a vtable.
- *
- * RefCounted<T> is created with refcount == 0.  Newly-allocated
- * RefCounted<T> must immediately be assigned to a RefPtr to make the
- * refcount > 0.  It's an error to allocate and free a bare
- * RefCounted<T>, i.e. outside of the RefPtr machinery.  Attempts to
- * do so will abort DEBUG builds.
- *
- * Live RefCounted<T> have refcount > 0.  The lifetime (refcounts) of
- * live RefCounted<T> are controlled by RefPtr<T> and
- * RefPtr<super/subclass of T>.  Upon a transition from refcounted==1
- * to 0, the RefCounted<T> "dies" and is destroyed.  The "destroyed"
- * state is represented in DEBUG builds by refcount==0xffffdead.  This
- * state distinguishes use-before-ref (refcount==0) from
- * use-after-destroy (refcount==0xffffdead).
- *
- * Note that when deriving from RefCounted or AtomicRefCounted, you
- * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
- * section of your class, where ClassName is the name of your class.
- */
-namespace detail {
-const MozRefCountType DEAD = 0xffffdead;
-
-// When building code that gets compiled into Gecko, try to use the
-// trace-refcount leak logging facilities.
-#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
-class RefCountLogger
-{
-public:
-  static void logAddRef(const void* aPointer, MozRefCountType aRefCount,
-                        const char* aTypeName, uint32_t aInstanceSize)
-  {
-    MOZ_ASSERT(aRefCount != DEAD);
-    NS_LogAddRef(const_cast<void*>(aPointer), aRefCount, aTypeName,
-                 aInstanceSize);
-  }
-
-  static void logRelease(const void* aPointer, MozRefCountType aRefCount,
-                         const char* aTypeName)
-  {
-    MOZ_ASSERT(aRefCount != DEAD);
-    NS_LogRelease(const_cast<void*>(aPointer), aRefCount, aTypeName);
-  }
-};
-#endif
-
-// This is used WeakPtr.h as well as this file.
-enum RefCountAtomicity
-{
-  AtomicRefCount,
-  NonAtomicRefCount
-};
-
-template<typename T, RefCountAtomicity Atomicity>
-class RefCounted
-{
-  friend class RefPtr<T>;
-
-protected:
-  RefCounted() : mRefCnt(0) {}
-  ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD); }
-
-public:
-  // Compatibility with nsRefPtr.
-  void AddRef() const
-  {
-    // Note: this method must be thread safe for AtomicRefCounted.
-    MOZ_ASSERT(int32_t(mRefCnt) >= 0);
-#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
-    ++mRefCnt;
-#else
-    const char* type = static_cast<const T*>(this)->typeName();
-    uint32_t size = static_cast<const T*>(this)->typeSize();
-    const void* ptr = static_cast<const T*>(this);
-    MozRefCountType cnt = ++mRefCnt;
-    detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
-#endif
-  }
-
-  void Release() const
-  {
-    // Note: this method must be thread safe for AtomicRefCounted.
-    MOZ_ASSERT(int32_t(mRefCnt) > 0);
-#ifndef MOZ_REFCOUNTED_LEAK_CHECKING
-    MozRefCountType cnt = --mRefCnt;
-#else
-    const char* type = static_cast<const T*>(this)->typeName();
-    const void* ptr = static_cast<const T*>(this);
-    MozRefCountType cnt = --mRefCnt;
-    // Note: it's not safe to touch |this| after decrementing the refcount,
-    // except for below.
-    detail::RefCountLogger::logRelease(ptr, cnt, type);
-#endif
-    if (0 == cnt) {
-      // Because we have atomically decremented the refcount above, only
-      // one thread can get a 0 count here, so as long as we can assume that
-      // everything else in the system is accessing this object through
-      // RefPtrs, it's safe to access |this| here.
-#ifdef DEBUG
-      mRefCnt = detail::DEAD;
-#endif
-      delete static_cast<const T*>(this);
-    }
-  }
-
-  // Compatibility with wtf::RefPtr.
-  void ref() { AddRef(); }
-  void deref() { Release(); }
-  MozRefCountType refCount() const { return mRefCnt; }
-  bool hasOneRef() const
-  {
-    MOZ_ASSERT(mRefCnt > 0);
-    return mRefCnt == 1;
-  }
-
-private:
-  mutable typename Conditional<Atomicity == AtomicRefCount,
-                               Atomic<MozRefCountType>,
-                               MozRefCountType>::Type mRefCnt;
-};
-
-#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
-// Passing override for the optional argument marks the typeName and
-// typeSize functions defined by this macro as overrides.
-#define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...) \
-  virtual const char* typeName() const __VA_ARGS__ { return #T; } \
-  virtual size_t typeSize() const __VA_ARGS__ { return sizeof(*this); }
-#else
-#define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...)
-#endif
-
-// Note that this macro is expanded unconditionally because it declares only
-// two small inline functions which will hopefully get eliminated by the linker
-// in non-leak-checking builds.
-#define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \
-  const char* typeName() const { return #T; } \
-  size_t typeSize() const { return sizeof(*this); }
-
-} // namespace detail
-
-template<typename T>
-class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
-{
-public:
-  ~RefCounted()
-  {
-    static_assert(IsBaseOf<RefCounted, T>::value,
-                  "T must derive from RefCounted<T>");
-  }
-};
-
-namespace external {
-
-/**
- * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
- * reference counter.
- *
- * NOTE: Please do not use this class, use NS_INLINE_DECL_THREADSAFE_REFCOUNTING
- * instead.
- */
-template<typename T>
-class AtomicRefCounted :
-  public mozilla::detail::RefCounted<T, mozilla::detail::AtomicRefCount>
-{
-public:
-  ~AtomicRefCounted()
-  {
-    static_assert(IsBaseOf<AtomicRefCounted, T>::value,
-                  "T must derive from AtomicRefCounted<T>");
-  }
-};
-
-} // namespace external
-
-/**
  * RefPtr points to a refcounted thing that has AddRef and Release
  * methods to increase/decrease the refcount, respectively.  After a
  * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr
  * as if it were a T*.
  *
  * A RefPtr can forget its underlying T*, which results in the T*
  * being wrapped in a temporary object until the T* is either
  * re-adopted from or released by the temporary.
@@ -365,33 +185,16 @@ private:
  */
 template<typename T>
 OutParamRef<T>
 byRef(RefPtr<T>& aPtr)
 {
   return OutParamRef<T>(aPtr);
 }
 
-/**
- * Helper function to be able to conveniently write things like:
- *
- *   already_AddRefed<T>
- *   f(...)
- *   {
- *     return MakeAndAddRef<T>(...);
- *   }
- */
-template<typename T, typename... Args>
-already_AddRefed<T>
-MakeAndAddRef(Args&&... aArgs)
-{
-  RefPtr<T> p(new T(Forward<Args>(aArgs)...));
-  return p.forget();
-}
-
 } // namespace mozilla
 
 // Declared in nsRefPtr.h
 template<class T> template<class U>
 nsRefPtr<T>::nsRefPtr(mozilla::RefPtr<U>&& aOther)
   : nsRefPtr(aOther.forget())
 {
 }
--- a/mfbt/WeakPtr.h
+++ b/mfbt/WeakPtr.h
@@ -65,16 +65,17 @@
  */
 
 #ifndef mozilla_WeakPtr_h
 #define mozilla_WeakPtr_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/RefCounted.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TypeTraits.h"
 
 #include <string.h>
 
 namespace mozilla {
 
 template <typename T> class WeakPtr;
new file mode 100644
--- /dev/null
+++ b/mfbt/XorShift128PlusRNG.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* The xorshift128+ pseudo-random number generator. */
+
+#ifndef mozilla_XorShift128Plus_h
+#define mozilla_XorShift128Plus_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FloatingPoint.h"
+
+#include <math.h>
+#include <memory.h>
+#include <stdint.h>
+
+namespace mozilla {
+namespace non_crypto {
+
+/*
+ * A stream of pseudo-random numbers generated using the xorshift+ technique
+ * described here:
+ *
+ * Vigna, Sebastiano (2014). "Further scramblings of Marsaglia's xorshift
+ * generators". arXiv:1404.0390 (http://arxiv.org/abs/1404.0390)
+ *
+ * That paper says:
+ *
+ *     In particular, we propose a tightly coded xorshift128+ generator that
+ *     does not fail systematically any test from the BigCrush suite of TestU01
+ *     (even reversed) and generates 64 pseudorandom bits in 1.10 ns on an
+ *     Intel(R) Core(TM) i7-4770 CPU @3.40GHz (Haswell). It is the fastest
+ *     generator we are aware of with such empirical statistical properties.
+ *
+ * This generator is not suitable as a cryptographically secure random number
+ * generator.
+ */
+class XorShift128PlusRNG {
+  uint64_t mState[2];
+
+ public:
+  /*
+   * Construct a xorshift128+ pseudo-random number stream using |aInitial0| and
+   * |aInitial1| as the initial state. These may not both be zero; ideally, they
+   * should have an almost even mix of zero and one bits.
+   */
+  XorShift128PlusRNG(uint64_t aInitial0, uint64_t aInitial1) {
+    setState(aInitial0, aInitial1);
+  }
+
+  /* Return a pseudo-random 64-bit number. */
+  uint64_t next() {
+    uint64_t s1 = mState[0];
+    const uint64_t s0 = mState[1];
+    mState[0] = s0;
+    s1 ^= s1 << 23;
+    mState[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26);
+    return mState[1] + s0;
+  }
+
+  /*
+   * Return a pseudo-random floating-point value in the range [0, 1).
+   * More precisely, choose an integer in the range [0, 2**53) and
+   * divide it by 2**53.
+   */
+  double nextDouble() {
+    /*
+     * Because the IEEE 64-bit floating point format stores the leading '1' bit
+     * of the mantissa implicitly, it effectively represents a mantissa in the
+     * range [0, 2**53) in only 52 bits. FloatingPoint<double>::kExponentShift
+     * is the width of the bitfield in the in-memory format, so we must add one
+     * to get the mantissa's range.
+     */
+    static const int kMantissaBits =
+      mozilla::FloatingPoint<double>::kExponentShift + 1;
+    uint64_t mantissa = next() & ((1ULL << kMantissaBits) - 1);
+    return ldexp(static_cast<double>(mantissa), -kMantissaBits);
+  }
+
+  /*
+   * Set the stream's current state to |aState0| and |aState1|. These must not
+   * both be zero; ideally, they should have an almost even mix of zero and one
+   * bits.
+   */
+  void setState(uint64_t aState0, uint64_t aState1) {
+    MOZ_ASSERT(aState0 || aState1);
+    mState[0] = aState0;
+    mState[1] = aState1;
+  }
+};
+
+} // namespace non_crypto
+} // namespace mozilla
+
+#endif // mozilla_XorShift128Plus_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -62,16 +62,17 @@ EXPORTS.mozilla = [
     'NullPtr.h',
     'NumericLimits.h',
     'Pair.h',
     'PodOperations.h',
     'Poison.h',
     'Range.h',
     'RangedArray.h',
     'RangedPtr.h',
+    'RefCounted.h',
     'RefCountType.h',
     'ReentrancyGuard.h',
     'RefPtr.h',
     'ReverseIterator.h',
     'RollingMean.h',
     'Scoped.h',
     'ScopeExit.h',
     'SegmentedVector.h',
@@ -86,16 +87,17 @@ EXPORTS.mozilla = [
     'Tuple.h',
     'TypedEnumBits.h',
     'Types.h',
     'TypeTraits.h',
     'UniquePtr.h',
     'Variant.h',
     'Vector.h',
     'WeakPtr.h',
+    'XorShift128PlusRNG.h',
     'unused.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla += [
         'WindowsVersion.h',
     ]
 elif CONFIG['OS_ARCH'] == 'Linux':
--- a/mfbt/nsRefPtr.h
+++ b/mfbt/nsRefPtr.h
@@ -586,9 +586,30 @@ operator!=(decltype(nullptr), const nsRe
 template <class T>
 inline already_AddRefed<T>
 do_AddRef(T*&& aObj)
 {
   nsRefPtr<T> ref(aObj);
   return ref.forget();
 }
 
+namespace mozilla {
+
+/**
+ * Helper function to be able to conveniently write things like:
+ *
+ *   already_AddRefed<T>
+ *   f(...)
+ *   {
+ *     return MakeAndAddRef<T>(...);
+ *   }
+ */
+template<typename T, typename... Args>
+already_AddRefed<T>
+MakeAndAddRef(Args&&... aArgs)
+{
+  nsRefPtr<T> p(new T(Forward<Args>(aArgs)...));
+  return p.forget();
+}
+
+} // namespace mozilla
+
 #endif /* mozilla_nsRefPtr_h */
--- a/mfbt/tests/TestRefPtr.cpp
+++ b/mfbt/tests/TestRefPtr.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
 
 using mozilla::RefCounted;
 using mozilla::RefPtr;
 
 class Foo : public RefCounted<Foo>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo)
@@ -50,32 +51,21 @@ void
 GetNewFoo(Foo** aFoo)
 {
   *aFoo = new Bar();
   // Kids, don't try this at home
   (*aFoo)->AddRef();
 }
 
 void
-GetPassedFoo(Foo** aFoo)
-{
-  // Kids, don't try this at home
-  (*aFoo)->AddRef();
-}
-
-void
 GetNewFoo(RefPtr<Foo>* aFoo)
 {
   *aFoo = new Bar();
 }
 
-void
-GetPassedFoo(RefPtr<Foo>* aFoo)
-{}
-
 already_AddRefed<Foo>
 GetNullFoo()
 {
   return 0;
 }
 
 int
 main()
@@ -124,41 +114,27 @@ main()
     RefPtr<Foo> f = new Foo();
     GetNewFoo(byRef(f));
     MOZ_RELEASE_ASSERT(7 == Foo::sNumDestroyed);
   }
   MOZ_RELEASE_ASSERT(8 == Foo::sNumDestroyed);
 
   {
     RefPtr<Foo> f = new Foo();
-    GetPassedFoo(byRef(f));
-    MOZ_RELEASE_ASSERT(8 == Foo::sNumDestroyed);
+    GetNewFoo(&f);
+    MOZ_RELEASE_ASSERT(9 == Foo::sNumDestroyed);
   }
-  MOZ_RELEASE_ASSERT(9 == Foo::sNumDestroyed);
+  MOZ_RELEASE_ASSERT(10 == Foo::sNumDestroyed);
 
   {
-    RefPtr<Foo> f = new Foo();
-    GetNewFoo(&f);
-    MOZ_RELEASE_ASSERT(10 == Foo::sNumDestroyed);
+    RefPtr<Foo> f1 = new Bar();
   }
   MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
 
   {
-    RefPtr<Foo> f = new Foo();
-    GetPassedFoo(&f);
+    RefPtr<Foo> f = GetNullFoo();
     MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
   }
-  MOZ_RELEASE_ASSERT(12 == Foo::sNumDestroyed);
-
-  {
-    RefPtr<Foo> f1 = new Bar();
-  }
-  MOZ_RELEASE_ASSERT(13 == Foo::sNumDestroyed);
-
-  {
-    RefPtr<Foo> f = GetNullFoo();
-    MOZ_RELEASE_ASSERT(13 == Foo::sNumDestroyed);
-  }
-  MOZ_RELEASE_ASSERT(13 == Foo::sNumDestroyed);
+  MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed);
 
   return 0;
 }
 
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestXorShift128PlusRNG.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/XorShift128PlusRNG.h"
+
+using mozilla::non_crypto::XorShift128PlusRNG;
+
+static void
+TestDumbSequence()
+{
+  XorShift128PlusRNG rng(1, 4);
+
+  // Calculated by hand following the algorithm given in the paper. The upper
+  // bits are mostly zero because we started with a poor seed; once it has run
+  // for a while, we'll get an even mix of ones and zeros in all 64 bits.
+  MOZ_RELEASE_ASSERT(rng.next() == 0x800049);
+  MOZ_RELEASE_ASSERT(rng.next() == 0x3000186);
+  MOZ_RELEASE_ASSERT(rng.next() == 0x400003001145);
+
+  // Using ldexp here lets us write out the mantissa in hex, so we can compare
+  // them with the results generated by hand.
+  MOZ_RELEASE_ASSERT(rng.nextDouble()
+                     == ldexp(static_cast<double>(0x1400003105049), -53));
+  MOZ_RELEASE_ASSERT(rng.nextDouble()
+                     == ldexp(static_cast<double>(0x2000802e49146), -53));
+  MOZ_RELEASE_ASSERT(rng.nextDouble()
+                     == ldexp(static_cast<double>(0x248300468544d), -53));
+}
+
+static size_t
+Population(uint64_t n)
+{
+  size_t pop = 0;
+
+  while (n > 0) {
+    n &= n-1; // Clear the rightmost 1-bit in n.
+    pop++;
+  }
+
+  return pop;
+}
+
+static void
+TestPopulation()
+{
+  XorShift128PlusRNG rng(698079309544035222ULL, 6012389156611637584ULL);
+
+  // Give it some time to warm up; it should tend towards more
+  // even distributions of zeros and ones.
+  for (size_t i = 0; i < 40; i++)
+    rng.next();
+
+  for (size_t i = 0; i < 40; i++) {
+    size_t pop = Population(rng.next());
+    MOZ_RELEASE_ASSERT(24 <= pop && pop <= 40);
+  }
+}
+
+static void
+TestSetState()
+{
+  static const uint64_t seed[2] = { 1795644156779822404ULL, 14162896116325912595ULL };
+  XorShift128PlusRNG rng(seed[0], seed[1]);
+
+  const size_t n = 10;
+  uint64_t log[n];
+
+  for (size_t i = 0; i < n; i++)
+    log[i] = rng.next();
+
+  rng.setState(seed[0], seed[1]);
+
+  for (size_t i = 0; i < n; i++)
+    MOZ_RELEASE_ASSERT(log[i] == rng.next());
+}
+
+static void
+TestDoubleDistribution()
+{
+  XorShift128PlusRNG rng(0xa207aaede6859736, 0xaca6ca5060804791);
+
+  const size_t n = 100;
+  size_t bins[n];
+  mozilla::PodArrayZero(bins);
+
+  // This entire file runs in 0.006s on my laptop. Generating
+  // more numbers lets us put tighter bounds on the bins.
+  for (size_t i = 0; i < 100000; i++) {
+    double d = rng.nextDouble();
+    MOZ_RELEASE_ASSERT(0.0 <= d && d < 1.0);
+    bins[(int) (d * n)]++;
+  }
+
+  for (size_t i = 0; i < n; i++) {
+    MOZ_RELEASE_ASSERT(900 <= bins[i] && bins[i] <= 1100);
+  }
+}
+
+int
+main()
+{
+  TestDumbSequence();
+  TestPopulation();
+  TestSetState();
+  TestDoubleDistribution();
+
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -35,16 +35,17 @@ CppUnitTests([
     'TestTemplateLib',
     'TestTuple',
     'TestTypedEnum',
     'TestTypeTraits',
     'TestUniquePtr',
     'TestVariant',
     'TestVector',
     'TestWeakPtr',
+    'TestXorShift128PlusRNG',
 ])
 
 if not CONFIG['MOZ_ASAN']:
     CppUnitTests([
         'TestPoisonArea',
     ])
 
 # Since we link directly with MFBT object files, define IMPL_MFBT
--- a/mobile/android/b2gdroid/installer/package-manifest.in
+++ b/mobile/android/b2gdroid/installer/package-manifest.in
@@ -303,16 +303,18 @@
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/SettingsService.js
 @BINPATH@/components/SettingsService.manifest
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
+@BINPATH@/components/PackagedAppUtils.manifest
+@BINPATH@/components/PackagedAppUtils.js
 @BINPATH@/components/BrowserFeeds.manifest
 @BINPATH@/components/FeedConverter.js
 @BINPATH@/components/FeedWriter.js
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/PermissionPromptService.js
 @BINPATH@/components/PermissionPromptService.manifest
 @BINPATH@/components/fuelApplication.manifest
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3597,17 +3597,21 @@ Tab.prototype = {
     }
 
     // Only reload the page for http/https schemes
     let currentURI = this.browser.currentURI;
     if (!currentURI.schemeIs("http") && !currentURI.schemeIs("https"))
       return;
 
     let url = currentURI.spec;
-    let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
+    // We need LOAD_FLAGS_BYPASS_CACHE here since we're changing the User-Agent
+    // string, and servers typically don't use the Vary: User-Agent header, so
+    // not doing this means that we'd get some of the previously cached content.
+    let flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE |
+                Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
     if (this.originalURI && !this.originalURI.equals(currentURI)) {
       // We were redirected; reload the original URL
       url = this.originalURI.spec;
     }
 
     this.browser.docShell.loadURI(url, flags, null, null, null);
   },
 
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -306,16 +306,18 @@
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/BrowserElementParent.manifest
 @BINPATH@/components/BrowserElementParent.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
+@BINPATH@/components/PackagedAppUtils.manifest
+@BINPATH@/components/PackagedAppUtils.js
 @BINPATH@/components/BrowserFeeds.manifest
 @BINPATH@/components/FeedConverter.js
 @BINPATH@/components/FeedWriter.js
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/PermissionPromptService.js
 @BINPATH@/components/PermissionPromptService.manifest
 @BINPATH@/components/fuelApplication.manifest
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -360,18 +360,18 @@ pref("media.navigator.video.default_minf
 
 pref("media.webrtc.debug.trace_mask", 0);
 pref("media.webrtc.debug.multi_log", false);
 pref("media.webrtc.debug.aec_log_dir", "");
 pref("media.webrtc.debug.log_file", "");
 pref("media.webrtc.debug.aec_dump_max_size", 4194304); // 4MB
 
 #ifdef MOZ_WIDGET_GONK
-pref("media.navigator.video.default_width",320);
-pref("media.navigator.video.default_height",240);
+pref("media.navigator.video.default_width", 320);
+pref("media.navigator.video.default_height", 240);
 pref("media.peerconnection.enabled", true);
 pref("media.peerconnection.video.enabled", true);
 pref("media.navigator.video.max_fs", 1200); // 640x480 == 1200mb
 pref("media.navigator.video.max_fr", 30);
 pref("media.navigator.video.h264.level", 12); // 0x42E00C - level 1.2
 pref("media.navigator.video.h264.max_br", 700); // 8x10
 pref("media.navigator.video.h264.max_mbps", 11880); // CIF@30fps
 pref("media.peerconnection.video.h264_enabled", false);
@@ -395,16 +395,17 @@ pref("media.peerconnection.video.h264_en
 pref("media.getusermedia.aec", 1);
 pref("media.getusermedia.browser.enabled", true);
 // Desktop is typically VGA capture or more; and qm_select will not drop resolution
 // below 1/2 in each dimension (or so), so QVGA (320x200) is the lowest here usually.
 pref("media.peerconnection.video.min_bitrate", 200);
 pref("media.peerconnection.video.start_bitrate", 300);
 pref("media.peerconnection.video.max_bitrate", 2000);
 #endif
+pref("media.navigator.audio.fake_frequency", 1000);
 pref("media.navigator.permission.disabled", false);
 pref("media.peerconnection.default_iceservers", "[]");
 pref("media.peerconnection.ice.loopback", false); // Set only for testing in offline environments.
 pref("media.peerconnection.ice.tcp", false);
 pref("media.peerconnection.ice.tcp_so_sock_count", 0); // Disable SO gathering
 pref("media.peerconnection.ice.link_local", false); // Set only for testing IPV6 in networks that don't assign IPV6 addresses
 pref("media.peerconnection.ice.force_interface", ""); // Limit to only a single interface
 pref("media.peerconnection.ice.relay_only", false); // Limit candidates to TURN
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -33,16 +33,17 @@ XPIDL_SOURCES += [
     'nsIChildChannel.idl',
     'nsIClassOfService.idl',
     'nsIContentSniffer.idl',
     'nsICryptoFIPSInfo.idl',
     'nsICryptoHash.idl',
     'nsICryptoHMAC.idl',
     'nsIDashboard.idl',
     'nsIDashboardEventNotifier.idl',
+    'nsIDeprecationWarner.idl',
     'nsIDivertableChannel.idl',
     'nsIDownloader.idl',
     'nsIEncodedChannel.idl',
     'nsIExternalProtocolHandler.idl',
     'nsIFileStreams.idl',
     'nsIFileURL.idl',
     'nsIForcePendingChannel.idl',
     'nsIHttpPushListener.idl',
@@ -64,16 +65,17 @@ XPIDL_SOURCES += [
     'nsINetworkLinkService.idl',
     'nsINetworkPredictor.idl',
     'nsINetworkPredictorVerifier.idl',
     'nsINetworkProperties.idl',
     'nsINSSErrorsService.idl',
     'nsINullChannel.idl',
     'nsIPACGenerator.idl',
     'nsIPackagedAppService.idl',
+    'nsIPackagedAppUtils.idl',
     'nsIPackagedAppVerifier.idl',
     'nsIParentChannel.idl',
     'nsIParentRedirectingChannel.idl',
     'nsIPermission.idl',
     'nsIPermissionManager.idl',
     'nsIPrivateBrowsingChannel.idl',
     'nsIProgressEventSink.idl',
     'nsIPrompt.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIDeprecationWarner.idl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Interface for warning about deprecated operations.  Consumers should
+ * attach this interface to the channel's notification callbacks/loadgroup.
+ */
+[uuid(665c5124-2c52-41ba-ae72-2393f8e76c25)]
+interface nsIDeprecationWarner : nsISupports
+{
+    /**
+     * Issue a deprecation warning.
+     *
+     * @param aWarning a warning code as declared in nsDeprecatedOperationList.h.
+     * @param aAsError optional boolean flag indicating whether the warning
+     *                 should be treated as an error.
+     */
+    void issueWarning(in uint32_t aWarning, [optional] in bool aAsError);
+};
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIPackagedAppUtils.idl
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIVerificationCallback;
+
+%{C++
+#define NS_PACKAGEDAPPUTILS_CONTRACTID "@mozilla.org/network/packaged-app-utils;1"
+%}
+
+/**
+ * A package using privileged APIs should be signed by marketplace or trust-
+ * worthy developers. When Necko receives such a package, it has to
+ * extract the manifest and the signature and calls verifyManifest(...) to verify
+ * the manifest. nsIPackagedAppUtils will parse the manifest and
+ * store the hash values of each resource. When a resource is ready, Necko
+ * will calculate its hash value (including the header like Content-Location: xxx),
+ * and calls checkIntegrity(...) to verify the integrity.
+ *
+ * For more detail:
+ *   https://wiki.mozilla.org/FirefoxOS/New_security_model/Packaging
+ */
+
+[scriptable, uuid(d0a98a69-a215-4cf9-abb3-7a0b9237cd27)]
+interface nsIPackagedAppUtils : nsISupports
+{
+  /**
+   * @aHeader is the package's header including
+   *   - "manifest-signature: xxxxxx" (base64 encoding)
+   * @aManifest is the manifest of the package
+   *   - the multipart header is included
+   *   - manifest must be the first resource of the package
+   * @aCallback is the callback, see comments of nsIVerificationCallback below
+   */
+  void verifyManifest(in ACString aHeader,
+                      in ACString aManifest,
+                      in nsIVerificationCallback aVerifier);
+
+  /**
+   * @aFileName is the name of a resource in the package
+   * @aHashValue is the hash value of this resource named aFileName
+   *   - aHashValue should be computed by the caller of this method
+   * @aCallback is the callback, see comments of nsIVerificationCallback below
+   */
+  void checkIntegrity(in ACString aFileName,
+                      in ACString aHashValue,
+                      in nsIVerificationCallback aVerifier);
+};
+
+/**
+  * The callback passed to verifyManifest and checkIntegrity
+  */
+[scriptable, uuid(e1912028-93e5-4378-aa3f-a58702937169)]
+interface nsIVerificationCallback : nsISupports
+{
+  /**
+   * @aForManifest
+   *   - true if it's called by verifyManifest
+   *   - false if it's called by checkIntegrity
+   * @aSuccess
+   *   - true if the verification succeeds, false otherwise
+   */
+  void fireVerifiedEvent(in boolean aForManifest,
+                         in boolean aSuccess);
+};
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/DNS.h"
 #include "SerializedLoadContext.h"
 #include "nsInputStreamPump.h"
 #include "InterceptedChannel.h"
 #include "nsPerformance.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsContentSecurityManager.h"
+#include "nsIDeprecationWarner.h"
 
 #ifdef OS_POSIX
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
@@ -2314,10 +2315,22 @@ HttpChannelChild::OverrideWithSynthesize
 
 NS_IMETHODIMP
 HttpChannelChild::ForceIntercepted()
 {
   mShouldParentIntercept = true;
   return NS_OK;
 }
 
+bool
+HttpChannelChild::RecvIssueDeprecationWarning(const uint32_t& warning,
+                                              const bool& asError)
+{
+  nsCOMPtr<nsIDeprecationWarner> warner;
+  GetCallback(warner);
+  if (warner) {
+    warner->IssueWarning(warning, asError);
+  }
+  return true;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -143,16 +143,19 @@ protected:
                                      const nsCString& clientID) override;
   bool RecvFlushedForDiversion() override;
   bool RecvDivertMessages() override;
   bool RecvDeleteSelf() override;
 
   bool RecvReportSecurityMessage(const nsString& messageTag,
                                  const nsString& messageCategory) override;
 
+  bool RecvIssueDeprecationWarning(const uint32_t& warning,
+                                   const bool& asError) override;
+
   bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
   virtual void DoNotifyListenerCleanup() override;
 
   NS_IMETHOD GetResponseSynthesized(bool* aSynthesized) override;
 
 private:
   nsresult ContinueAsyncOpen();
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -148,17 +148,18 @@ HttpChannelParent::Init(const HttpChanne
 NS_IMPL_ISUPPORTS(HttpChannelParent,
                   nsIInterfaceRequestor,
                   nsIProgressEventSink,
                   nsIRequestObserver,
                   nsIStreamListener,
                   nsIParentChannel,
                   nsIAuthPromptProvider,
                   nsIParentRedirectingChannel,
-                  nsINetworkInterceptController)
+                  nsINetworkInterceptController,
+                  nsIDeprecationWarner)
 
 NS_IMETHODIMP
 HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
                                              nsContentPolicyType aType,
                                              bool* aShouldIntercept)
 {
   *aShouldIntercept = mShouldIntercept;
   return NS_OK;
@@ -1574,10 +1575,17 @@ HttpChannelParent::ReportSecurityMessage
   if (mIPCClosed ||
       NS_WARN_IF(!SendReportSecurityMessage(nsString(aMessageTag),
                                             nsString(aMessageCategory)))) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpChannelParent::IssueWarning(uint32_t aWarning, bool aAsError)
+{
+  unused << SendIssueDeprecationWarning(aWarning, aAsError);
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -16,16 +16,17 @@
 #include "OfflineObserver.h"
 #include "nsIObserver.h"
 #include "nsIParentRedirectingChannel.h"
 #include "nsIProgressEventSink.h"
 #include "nsHttpChannel.h"
 #include "nsIAuthPromptProvider.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "nsINetworkInterceptController.h"
+#include "nsIDeprecationWarner.h"
 
 class nsICacheEntry;
 class nsIAssociatedContentSecurity;
 
 namespace mozilla {
 
 namespace dom{
 class TabParent;
@@ -38,31 +39,33 @@ class HttpChannelParentListener;
 
 class HttpChannelParent final : public PHttpChannelParent
                               , public nsIParentRedirectingChannel
                               , public nsIProgressEventSink
                               , public nsIInterfaceRequestor
                               , public ADivertableParentChannel
                               , public nsIAuthPromptProvider
                               , public nsINetworkInterceptController
+                              , public nsIDeprecationWarner
                               , public DisconnectableParent
                               , public HttpChannelSecurityWarningReporter
 {
   virtual ~HttpChannelParent();
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIPARENTCHANNEL
   NS_DECL_NSIPARENTREDIRECTINGCHANNEL
   NS_DECL_NSIPROGRESSEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSINETWORKINTERCEPTCONTROLLER
+  NS_DECL_NSIDEPRECATIONWARNER
 
   HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
                     nsILoadContext* aLoadContext,
                     PBOverrideStatus aStatus);
 
   bool Init(const HttpChannelCreationArgs& aOpenArgs);
 
   // ADivertableParentChannel functions.
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -149,14 +149,17 @@ child:
 
   // Report a security message to the console associated with this
   // channel.
   ReportSecurityMessage(nsString messageTag, nsString messageCategory);
 
   // Tell child to delete channel (all IPDL deletes must be done from child to
   // avoid races: see bug 591708).
   DeleteSelf();
+
+  // Tell the child to issue a deprecation warning.
+  IssueDeprecationWarning(uint32_t warning, bool asError);
 };
 
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/PackagedAppService.cpp
+++ b/netwerk/protocol/http/PackagedAppService.cpp
@@ -220,25 +220,22 @@ CreateSharedStringStream(const char* aDa
 
   return stream.forget();
 }
 
 // Get the original HTTP response header from the request.
 static bool
 GetOriginalResponseHeader(nsIRequest* aRequest, nsACString& aHeader)
 {
-  // TODO: The flattened http header might be different from the original.
-  //       See Bug 1198669 for further information.
+  nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
+  if (!multiPartChannel) {
+    return false;
+  }
 
-  nsCOMPtr<nsIResponseHeadProvider> headerProvider(do_QueryInterface(aRequest));
-  nsHttpResponseHead *responseHead = headerProvider->GetResponseHead();
-  NS_ENSURE_TRUE(responseHead, false);
-
-  responseHead->Flatten(aHeader, true);
-  aHeader.Append("\r\n");
+  multiPartChannel->GetOriginalResponseHeader(aHeader);
 
   return true;
 }
 
 } // anon
 
 /* static */ nsresult
 PackagedAppService::CacheEntryWriter::CopyHeadersFromChannel(nsIChannel *aChannel,
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppUtils.js
@@ -0,0 +1,109 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PACKAGEDAPPUTILS_CONTRACTID = "@mozilla.org/network/packaged-app-utils;1";
+const PACKAGEDAPPUTILS_CID = Components.ID("{fe8f1c2e-3c13-11e5-9a3f-bbf47d1e6697}");
+
+function PackagedAppUtils() {
+
+}
+
+let DEBUG = 0
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- PackagedAppUtils: " + s + "\n");
+  }
+}
+
+PackagedAppUtils.prototype = {
+  classID: PACKAGEDAPPUTILS_CID,
+  contractID: PACKAGEDAPPUTILS_CONTRACTID,
+  classDescription: "Packaged App Utils",
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPackagedAppUtils]),
+
+  verifyManifest: function(aHeader, aManifest, aCallback) {
+    debug("Manifest: " + aManifest);
+
+    // parse signature from header
+    let signature;
+    const signatureField = "manifest-signature: ";
+    for (let item of aHeader.split('\r\n')) {
+      if (item.substr(0, signatureField.length) == signatureField) {
+        signature = item.substr(signatureField.length);
+        break;
+      }
+    }
+    if (!signature) {
+      debug("No signature in header");
+      aCallback.fireVerifiedEvent(true, false);
+      return;
+    }
+    debug("Signature: " + signature);
+
+    try {
+      // Base64 decode
+      signature = atob(signature);
+
+      // Remove header
+      let manifestBody = aManifest.substr(aManifest.indexOf('\r\n\r\n') + 4);
+      debug("manifestBody: " + manifestBody);
+
+      // Parse manifest, store resource hashes
+      this.resources = JSON.parse(manifestBody)["moz-resources"];
+    } catch (e) {
+      debug("JSON parsing failure");
+      aCallback.fireVerifiedEvent(true, false);
+      return;
+    }
+
+    let manifestStream = Cc["@mozilla.org/io/string-input-stream;1"]
+                           .createInstance(Ci.nsIStringInputStream);
+    let signatureStream = Cc["@mozilla.org/io/string-input-stream;1"]
+                            .createInstance(Ci.nsIStringInputStream);
+    manifestStream.setData(aManifest, aManifest.length);
+    signatureStream.setData(signature, signature.length);
+
+    let certDb;
+    try {
+      certDb = Cc["@mozilla.org/security/x509certdb;1"]
+                 .getService(Ci.nsIX509CertDB);
+    } catch (e) {
+      debug("nsIX509CertDB error: " + e);
+      // unrecoverable error, don't bug the user
+      throw "CERTDB_ERROR";
+    }
+
+    certDb.verifySignedManifestAsync(
+      Ci.nsIX509CertDB.PrivilegedPackageRoot, manifestStream, signatureStream,
+      function(aRv, aCert) {
+        aCallback.fireVerifiedEvent(true, Components.isSuccessCode(aRv));
+      });
+  },
+
+  checkIntegrity: function(aFileName, aHashValue, aCallback) {
+    debug("checkIntegrity() " + aFileName + ": " + aHashValue + "\n");
+    if (!this.resources) {
+      debug("resource hashes not found");
+      aCallback.fireVerifiedEvent(false, false);
+      return;
+    }
+    for (let r of this.resources) {
+      if (r.src === aFileName) {
+        debug("found integrity = " + r.integrity);
+        aCallback.fireVerifiedEvent(false, r.integrity === aHashValue);
+        return;
+      }
+    }
+    aCallback.fireVerifiedEvent(false, false);
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PackagedAppUtils]);
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppUtils.manifest
@@ -0,0 +1,3 @@
+# PackagedAppUtils.js
+component {fe8f1c2e-3c13-11e5-9a3f-bbf47d1e6697} PackagedAppUtils.js
+contract @mozilla.org/network/packaged-app-utils;1 {fe8f1c2e-3c13-11e5-9a3f-bbf47d1e6697}
--- a/netwerk/protocol/http/PackagedAppVerifier.cpp
+++ b/netwerk/protocol/http/PackagedAppVerifier.cpp
@@ -9,29 +9,31 @@
 #include "../../cache2/CacheFileUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/DebugOnly.h"
 #include "nsThreadUtils.h"
 #include "PackagedAppVerifier.h"
 #include "nsITimer.h"
 #include "nsIPackagedAppVerifier.h"
 #include "mozilla/Preferences.h"
+#include "nsIPackagedAppUtils.h"
+#include "nsIInputStream.h"
 
 static const short kResourceHashType = nsICryptoHash::SHA256;
 
 // If it's true, all the verification will be skipped and the package will
 // be treated signed.
 static bool gDeveloperMode = false;
 
 namespace mozilla {
 namespace net {
 
 ///////////////////////////////////////////////////////////////////////////////
 
-NS_IMPL_ISUPPORTS(PackagedAppVerifier, nsIPackagedAppVerifier)
+NS_IMPL_ISUPPORTS(PackagedAppVerifier, nsIPackagedAppVerifier, nsIVerificationCallback)
 
 NS_IMPL_ISUPPORTS(PackagedAppVerifier::ResourceCacheInfo, nsISupports)
 
 const char* PackagedAppVerifier::kSignedPakOriginMetadataKey = "signed-pak-origin";
 
 PackagedAppVerifier::PackagedAppVerifier()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(),
@@ -71,81 +73,125 @@ NS_IMETHODIMP PackagedAppVerifier::Init(
   }
 
   mListener = aListener;
   mState = STATE_UNKNOWN;
   mPackageOrigin = aPackageOrigin;
   mSignature = aSignature;
   mIsPackageSigned = false;
   mPackageCacheEntry = aPackageCacheEntry;
+  mIsFirstResource = true;
+
+  nsresult rv;
+  mPackagedAppUtils = do_CreateInstance(NS_PACKAGEDAPPUTILS_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    LOG(("create packaged app utils failed"));
+    return rv;
+  }
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsIStreamListener
 //----------------------------------------------------------------------
 
 // @param aRequest nullptr.
 // @param aContext The URI of the resource. (nsIURI)
 NS_IMETHODIMP
 PackagedAppVerifier::OnStartRequest(nsIRequest *aRequest,
                                     nsISupports *aContext)
 {
+  if (mIsFirstResource) {
+    // The first resource must be the manifest, hashes not needed
+    return NS_OK;
+  }
+
   if (!mHasher) {
     mHasher = do_CreateInstance("@mozilla.org/security/hash;1");
   }
 
   NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIURI> uri = do_QueryInterface(aContext);
   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
   uri->GetAsciiSpec(mHashingResourceURI);
 
   return mHasher->Init(kResourceHashType);
 }
 
+NS_METHOD
+PackagedAppVerifier::WriteManifest(nsIInputStream* aStream,
+                                   void* aManifest,
+                                   const char* aFromRawSegment,
+                                   uint32_t aToOffset,
+                                   uint32_t aCount,
+                                   uint32_t* aWriteCount)
+{
+  LOG(("WriteManifest: length %u", aCount));
+  LOG(("%s", aFromRawSegment));
+  nsCString* manifest = static_cast<nsCString*>(aManifest);
+  manifest->AppendASCII(aFromRawSegment, aCount);
+  *aWriteCount = aCount;
+  return NS_OK;
+}
+
 // @param aRequest nullptr.
 // @param aContext nullptr.
 // @param aInputStream as-is.
 // @param aOffset as-is.
 // @param aCount as-is.
 NS_IMETHODIMP
 PackagedAppVerifier::OnDataAvailable(nsIRequest *aRequest,
                                      nsISupports *aContext,
                                      nsIInputStream *aInputStream,
                                      uint64_t aOffset,
                                      uint32_t aCount)
 {
+  if (mIsFirstResource) {
+    // The first resource must be the manifest, hash value not needed.
+    // Instead, we read from the input stream and append to mManifest.
+    uint32_t count;
+    LOG(("ReadSegments: size = %u", aCount));
+    nsresult rv = aInputStream->ReadSegments(WriteManifest, &mManifest, aCount, &count);
+    MOZ_ASSERT(count == aCount, "Bytes read by ReadSegments don't match");
+    return rv;
+  }
+
   MOZ_ASSERT(!mHashingResourceURI.IsEmpty(), "MUST call BeginResourceHash first.");
   NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE);
   return mHasher->UpdateFromStream(aInputStream, aCount);
 }
 
 // @param aRequest nullptr.
 // @param aContext The resource cache info.
 // @param aStatusCode as-is,
 NS_IMETHODIMP
 PackagedAppVerifier::OnStopRequest(nsIRequest* aRequest,
                                     nsISupports* aContext,
                                     nsresult aStatusCode)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mHashingResourceURI is not thread safe.");
 
-  NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE);
-
-  nsAutoCString hash;
-  nsresult rv = mHasher->Finish(true, hash);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (mIsFirstResource) {
+    // The first resource must be the manifest, hash value not needed
+    mIsFirstResource = false;
+  } else {
+    NS_ENSURE_TRUE(mHasher, NS_ERROR_FAILURE);
 
-  LOG(("Hash of %s is %s", mHashingResourceURI.get(), hash.get()));
+    nsAutoCString hash;
+    nsresult rv = mHasher->Finish(true, hash);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  // Store the computated hash associated with the resource URI.
-  mResourceHashStore.Put(mHashingResourceURI, new nsCString(hash));
-  mHashingResourceURI = EmptyCString();
+    LOG(("Hash of %s is %s", mHashingResourceURI.get(), hash.get()));
+
+    // Store the computated hash associated with the resource URI.
+    mResourceHashStore.Put(mHashingResourceURI, new nsCString(hash));
+    mHashingResourceURI = EmptyCString();
+  }
 
   // Get a internal copy and take over the life cycle handling
   // since the linked list we use only supports pointer-based element.
   ResourceCacheInfo* info
     = new ResourceCacheInfo(*(static_cast<ResourceCacheInfo*>(aContext)));
 
   ProcessResourceCache(info);
 
@@ -181,32 +227,35 @@ PackagedAppVerifier::ProcessResourceCach
     break;
 
   default:
     MOZ_CRASH("Unexpected PackagedAppVerifier state."); // Shouldn't get here.
     break;
   }
 }
 
-void
+NS_IMETHODIMP
 PackagedAppVerifier::FireVerifiedEvent(bool aForManifest, bool aSuccess)
 {
+  LOG(("FireVerifiedEvent aForManifest=%d aSuccess=%d", aForManifest, aSuccess));
   nsCOMPtr<nsIRunnable> r;
 
   if (aForManifest) {
     r = NS_NewRunnableMethodWithArgs<bool>(this,
                                            &PackagedAppVerifier::OnManifestVerified,
                                            aSuccess);
   } else {
     r = NS_NewRunnableMethodWithArgs<bool>(this,
                                            &PackagedAppVerifier::OnResourceVerified,
                                            aSuccess);
   }
 
   NS_DispatchToMainThread(r);
+
+  return NS_OK;
 }
 
 void
 PackagedAppVerifier::VerifyManifest(const ResourceCacheInfo* aInfo)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Manifest verification must be on main thread");
 
   LOG(("Ready to verify manifest."));
@@ -226,19 +275,22 @@ PackagedAppVerifier::VerifyManifest(cons
   }
 
   if (mSignature.IsEmpty()) {
     LOG(("No signature. No need to do verification."));
     FireVerifiedEvent(true, true);
     return;
   }
 
-  // TODO: Implement manifest verification.
-  LOG(("Manifest verification not implemented yet. See Bug 1178518."));
-  FireVerifiedEvent(true, false);
+  LOG(("Signature: length = %u\n%s", mSignature.Length(), mSignature.get()));
+  LOG(("Manifest: length = %u\n%s", mManifest.Length(), mManifest.get()));
+  nsresult rv = mPackagedAppUtils->VerifyManifest(mSignature, mManifest, this);
+  if (NS_FAILED(rv)) {
+    LOG(("VerifyManifest FAILED rv = %u", (unsigned)rv));
+  }
 }
 
 void
 PackagedAppVerifier::VerifyResource(const ResourceCacheInfo* aInfo)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Resource verification must be on main thread");
 
   if (!aInfo->mURI) { // Broken last part.
@@ -264,19 +316,30 @@ PackagedAppVerifier::VerifyResource(cons
   }
 
   if (mSignature.IsEmpty()) {
     LOG(("No signature. No need to do resource integrity check."));
     FireVerifiedEvent(false, true);
     return;
   }
 
-  // TODO: Implement resource integrity check.
-  LOG(("Resource integrity check not implemented yet. See Bug 1178518."));
-  FireVerifiedEvent(false, false);
+  nsAutoCString path;
+  nsCOMPtr<nsIURL> url(do_QueryInterface(aInfo->mURI));
+  if (url) {
+    url->GetFilePath(path);
+  }
+  int32_t pos = path.Find("!//");
+  if (pos == kNotFound) {
+    FireVerifiedEvent(false, false);
+    return;
+  }
+  // Only keep the part after "!//"
+  path.Cut(0, pos + 3);
+
+  mPackagedAppUtils->CheckIntegrity(path, *resourceHash, this);
 }
 
 void
 PackagedAppVerifier::OnManifestVerified(bool aSuccess)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "OnManifestVerified must be on main thread.");
 
   LOG(("PackagedAppVerifier::OnManifestVerified: %d", aSuccess));
--- a/netwerk/protocol/http/PackagedAppVerifier.h
+++ b/netwerk/protocol/http/PackagedAppVerifier.h
@@ -9,28 +9,31 @@
 
 #include "nsICacheEntry.h"
 #include "nsIURI.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsICryptoHash.h"
 #include "nsIPackagedAppVerifier.h"
 #include "mozilla/LinkedList.h"
+#include "nsIPackagedAppUtils.h"
 
 namespace mozilla {
 namespace net {
 
 class PackagedAppVerifier final
   : public nsIPackagedAppVerifier
+  , public nsIVerificationCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIPACKAGEDAPPVERIFIER
+  NS_DECL_NSIVERIFICATIONCALLBACK
 
 public:
   enum EState {
     // The initial state.
     STATE_UNKNOWN,
 
     // When we are notified to process the first resource, we will start to
     // verify the manifest and go to this state no matter the package has
@@ -115,38 +118,49 @@ private:
   //    back in OnManifestVerified no matter this package has a signature or not.
   //
   // 2) PackagedAppVerifierListener::OnResourceVerified.
   //    ------------------------------------------------------------------------
   //    Otherwise, the resource will be called back here.
   //
   void ProcessResourceCache(const ResourceCacheInfo* aInfo);
 
+  // Callback for nsIInputStream::ReadSegment() to read manifest
+  static NS_METHOD WriteManifest(nsIInputStream* aStream,
+                                void* aManifest,
+                                const char* aFromRawSegment,
+                                uint32_t aToOffset,
+                                uint32_t aCount,
+                                uint32_t* aWriteCount);
+
   // This two functions would call the actual verifier.
   void VerifyManifest(const ResourceCacheInfo* aInfo);
   void VerifyResource(const ResourceCacheInfo* aInfo);
 
   void OnManifestVerified(bool aSuccess);
   void OnResourceVerified(bool aSuccess);
 
-  // Fire a async event to notify the verification result.
-  void FireVerifiedEvent(bool aForManifest, bool aSuccess);
-
   // To notify that either manifest or resource check is done.
   nsCOMPtr<nsIPackagedAppVerifierListener> mListener;
 
   // The internal verification state.
   EState mState;
 
   // Initialized as a normal origin. Will be updated once we verified the manifest.
   nsCString mPackageOrigin;
 
   // The signature of the package.
   nsCString mSignature;
 
+  // The app manfiest of the package
+  nsCString mManifest;
+
+  // Whether we're processing the first resource, which is the manfiest
+  bool mIsFirstResource;
+
   // Whether this package app is signed.
   bool mIsPackageSigned;
 
   // The package cache entry (e.g. http://foo.com/app.pak) used to store
   // any necessarry signed package information.
   nsCOMPtr<nsICacheEntry> mPackageCacheEntry;
 
   // The resource URI that we are computing its hash.
@@ -154,16 +168,19 @@ private:
 
   // Used to compute resource's hash value.
   nsCOMPtr<nsICryptoHash> mHasher;
 
   // The last computed hash value for a resource. It will be set on every
   // |EndResourceHash| call.
   nsCString mLastComputedResourceHash;
 
+  // This will help to verify manifests and resource integrity
+  nsCOMPtr<nsIPackagedAppUtils> mPackagedAppUtils;
+
   // A list of pending resource that is downloaded but not verified yet.
   mozilla::LinkedList<ResourceCacheInfo> mPendingResourceCacheInfoList;
 
   // A place to store the computed hashes of each resource.
   nsClassHashtable<nsCStringHashKey, nsCString> mResourceHashStore;
 }; // class PackagedAppVerifier
 
 } // namespace net
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -108,8 +108,13 @@ EXTRA_JS_MODULES += [
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/netwerk/base',
 ]
+
+EXTRA_COMPONENTS += [
+    'PackagedAppUtils.js',
+    'PackagedAppUtils.manifest',
+]
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -82,16 +82,18 @@
 #include "mozilla/Telemetry.h"
 #include "AlternateServices.h"
 #include "InterceptedChannel.h"
 #include "nsIHttpPushListener.h"
 #include "nsIX509Cert.h"
 #include "ScopedNSSTypes.h"
 #include "nsNullPrincipal.h"
 #include "nsIPackagedAppService.h"
+#include "nsIDeprecationWarner.h"
+#include "nsIDocument.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
@@ -2817,18 +2819,17 @@ nsHttpChannel::ContinueProcessFallback(n
         rv = mRedirectChannel->AsyncOpen2(mListener);
     }
     else {
         rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
-        Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
-                              true);
+        MaybeWarnAboutAppCache();
     }
 
     // close down this channel
     Cancel(NS_BINDING_REDIRECTED);
 
     notifier.RedirectSucceeded();
 
     ReleaseListeners();
@@ -3618,18 +3619,17 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
         // We successfully opened an offline cache session and the entry,
         // so indicate we will load from the offline cache.
         mLoadedFromApplicationCache = true;
         mCacheEntryIsReadOnly = true;
         mCacheEntry = aEntry;
         mCacheEntryIsWriteOnly = false;
 
         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) {
-            Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
-                                  true);
+            MaybeWarnAboutAppCache();
         }
 
         return NS_OK;
     }
 
     if (!mApplicationCacheForWrite && !mFallbackChannel) {
         if (!mApplicationCache) {
             mApplicationCache = aAppCache;
@@ -7021,10 +7021,28 @@ nsHttpChannel::OnPreflightFailed(nsresul
     mIsCorsPreflightDone = 1;
     mPreflightChannel = nullptr;
 
     CloseCacheEntry(true);
     AsyncAbort(aError);
     return NS_OK;
 }
 
+void
+nsHttpChannel::MaybeWarnAboutAppCache()
+{
+    // First, accumulate a telemetry ping about appcache usage.
+    Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
+                          true);
+
+    // Then, issue a deprecation warning if service worker interception is
+    // enabled.
+    if (nsContentUtils::ServiceWorkerInterceptionEnabled()) {
+        nsCOMPtr<nsIDeprecationWarner> warner;
+        GetCallback(warner);
+        if (warner) {
+            warner->IssueWarning(nsIDocument::eAppCache, false);
+        }
+    }
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -393,16 +393,18 @@ private:
     nsresult SetupByteRangeRequest(int64_t partialLen);
     void UntieByteRangeRequest();
     void UntieValidationRequest();
     nsresult OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering,
                                   bool checkingAppCacheEntry);
 
     void SetPushedStream(Http2PushedStream *stream);
 
+    void MaybeWarnAboutAppCache();
+
 private:
     nsCOMPtr<nsICancelable>           mProxyRequest;
 
     nsRefPtr<nsInputStreamPump>       mTransactionPump;
     nsRefPtr<nsHttpTransaction>       mTransaction;
 
     uint64_t                          mLogicalOffset;
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_utils.js
@@ -0,0 +1,135 @@
+const header_missing_signature = "header1: content1";
+const header_invalid_signature = "header1: content1\r\nmanifest-signature: invalid-signature\r\n";
+const header = "manifest-signature: MIIF1AYJKoZIhvcNAQcCoIIFxTCCBcECAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCA54wggOaMIICgqADAgECAgECMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEkMCIGA1UEChMbRXhhbXBsZSBUcnVzdGVkIENvcnBvcmF0aW9uMRkwFwYDVQQDExBUcnVzdGVkIFZhbGlkIENBMB4XDTE1MDkxMDA4MDQzNVoXDTM1MDkxMDA4MDQzNVowdDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGjAYBgNVBAMTEVRydXN0ZWQgQ29ycCBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAts8whjOzEbn/w1xkFJ67af7F/JPujBK91oyJekh2schIMzFau9pY8S1AiJQoJCulOJCJfUc8hBLKBZiGAkii+4Gpx6cVqMLe6C22MdD806Soxn8Dg4dQqbIvPuI4eeVKu5CEk80PW/BaFMmRvRHO62C7PILuH6yZeGHC4P7dTKpsk4CLxh/jRGXLC8jV2BCW0X+3BMbHBg53NoI9s1Gs7KGYnfOHbBP5wEFAa00RjHnubUaCdEBlC8Kl4X7p0S4RGb3rsB08wgFe9EmSZHIgcIm+SuVo7N4qqbI85qo2ulU6J8NN7ZtgMPHzrMhzgAgf/KnqPqwDIxnNmRNJmHTUYwIDAQABozgwNjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAukH6cJUUj5faa8CuPCqrEa0PoLY4SYNnff9NI+TTAHkB9l+kOcFl5eo2EQOcWmZKYi7QLlWC4jy/KQYattO9FMaxiOQL4FAc6ZIbNyfwWBzZWyr5syYJTTTnkLq8A9pCKarN49+FqhJseycU+8EhJEJyP5pv5hLvDNTTHOQ6SXhASsiX8cjo3AY4bxA5pWeXuTZ459qDxOnQd+GrOe4dIeqflk0hA2xYKe3SfF+QlK8EO370B8Dj8RX230OATM1E3OtYyALe34KW3wM9Qm9rb0eViDnVyDiCWkhhQnw5yPg/XQfloug2itRYuCnfUoRt8xfeHgwz2Ymz8cUADn3KpTGCAf4wggH6AgEBMHgwczELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFRydXN0ZWQgVmFsaWQgQ0ECAQIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MDkxMDA4MDQ0M1owIwYJKoZIhvcNAQkEMRYEFNg6lGtV9bJbL2hA0c5DdOeuCQ6lMA0GCSqGSIb3DQEBAQUABIIBAKGziwzA5Q38rIvNUDHCjYVTR1FhALGZv677Tc2+pwd82W6O9q5GG9IfkF3ajb1fquUIpGPkf7r0oiO4udC8cSehA+lfhR94A8aCM9UhzvTtRI3tFB+TPSk1UcXlX8tB7dNkx4zC06ujlSaRKkmaZODVXQFEcsF6CKMApsBuUJrwzvbQqVi2KHXUO6oGlMEyt4tY+g2OY/vyxGajfAL49dAYOTtrV0arvJvoTYh+E0iSrsbuiuAxKAVjK/QnLJoV/dTaCqW4t3lzHrpE3+avqMXiewxu84VJSURxoryY89uAZS9+4MKrSOGlGCJy/8xDIAm9pi6lPJBP2pIRjaRt9r0=\r\n";
+
+const manifest = "Content-Location: manifest.webapp\r\n" +
+  "Content-Type: application/x-web-app-manifest+json\r\n\r\n" +
+`{
+  "name": "My App",
+  "moz-resources": [
+    {
+      "src": "page2.html",
+      "integrity": "JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II="
+    },
+    {
+      "src": "index.html",
+      "integrity": "B5Phw8L1tpyRBkI0gwg/evy1fgtMlMq3BIY3Q8X0rYU="
+    },
+    {
+      "src": "scripts/script.js",
+      "integrity": "6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q="
+    },
+    {
+      "src": "scripts/library.js",
+      "integrity": "TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8="
+    }
+  ],
+  "moz-permissions": [
+    {
+      "systemXHR": {
+        "description": "Needed to download stuff"
+      },
+      "devicestorage:pictures": {
+        "description": "Need to load pictures"
+      }
+    }
+  ],
+  "moz-uuid": "some-uuid",
+  "moz-package-location": "https://example.com/myapp/app.pak",
+  "description": "A great app!"
+}`;
+
+const manifest_missing_moz_resources = "Content-Location: manifest.webapp\r\n" +
+  "Content-Type: application/x-web-app-manifest+json\r\n\r\n" +
+`{
+  "name": "My App",
+  "moz-permissions": [
+    {
+      "systemXHR": {
+        "description": "Needed to download stuff"
+      },
+      "devicestorage:pictures": {
+        "description": "Need to load pictures"
+      }
+    }
+  ],
+  "moz-uuid": "some-uuid",
+  "moz-package-location": "https://example.com/myapp/app.pak",
+  "description": "A great app!"
+}`;
+
+const manifest_malformed_json = "}";
+
+let packagedAppUtils;
+
+function run_test() {
+  add_test(test_verify_manifest(header_missing_signature, manifest, false));
+  add_test(test_verify_manifest(header_invalid_signature, manifest, false));
+  add_test(test_verify_manifest(header, manifest_malformed_json, false));
+  add_test(test_verify_manifest(header, manifest_missing_moz_resources, false));
+  add_test(test_verify_manifest(header, manifest, true));
+
+  // The last verification must succeed, because check_integrity use that object;
+  add_test(test_check_integrity_success);
+  add_test(test_check_integrity_filename_not_matched);
+  add_test(test_check_integrity_hashvalue_not_matched);
+
+  run_next_test();
+}
+
+function test_verify_manifest(aHeader, aManifest, aShouldSucceed) {
+  return function() {
+    do_test_pending();
+    packagedAppUtils = Cc["@mozilla.org/network/packaged-app-utils;1"].
+                       createInstance(Ci.nsIPackagedAppUtils);
+    let fakeVerifier = {
+      fireVerifiedEvent: function(aForManifest, aSuccess) {
+        ok(aForManifest, "aForManifest should be true");
+        equal(aSuccess, aShouldSucceed, "Expected verification result: " + aShouldSucceed);
+        do_test_finished();
+        run_next_test();
+      }
+    };
+    packagedAppUtils.verifyManifest(aHeader, aManifest, fakeVerifier);
+  }
+}
+
+function test_check_integrity_success() {
+  let manifestBody = manifest.substr(manifest.indexOf('\r\n\r\n') + 4);
+  fakeVerifier = {
+    fireVerifiedEvent: function(aForManifest, aSuccess) {
+      ok(!aForManifest && aSuccess, "checkIntegrity should succeed");
+      do_test_finished();
+      run_next_test();
+    }
+  };
+  for (let resource of JSON.parse(manifestBody)["moz-resources"]) {
+    do_test_pending();
+    packagedAppUtils.checkIntegrity(resource.src, resource.integrity, fakeVerifier);
+  }
+}
+
+function test_check_integrity_filename_not_matched() {
+  fakeVerifier = {
+    fireVerifiedEvent: function(aForManifest, aSuccess) {
+      ok(!aForManifest && !aSuccess, "checkIntegrity should fail");
+      do_test_finished();
+      run_next_test();
+    }
+  };
+  do_test_pending();
+  packagedAppUtils.checkIntegrity("/nosuchfile.html", "sha256-kass...eoirW-e", fakeVerifier);
+  run_next_test();
+}
+
+function test_check_integrity_hashvalue_not_matched() {
+  fakeVerifier = {
+    fireVerifiedEvent: function(aForManifest, aSuccess) {
+      ok(!aForManifest && !aSuccess, "checkIntegrity should fail");
+      do_test_finished();
+      run_next_test();
+    }
+  };
+  do_test_pending();
+  packagedAppUtils.checkIntegrity("/index.html", "kass...eoirW-e", fakeVerifier);
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -318,15 +318,16 @@ skip-if = os != "win"
 [test_tls_server.js]
 # The local cert service used by this test is not currently shipped on Android
 skip-if = os == "android"
 [test_1073747.js]
 [test_multipart_streamconv_application_package.js]
 [test_safeoutputstream_append.js]
 [test_packaged_app_service.js]
 [test_packaged_app_verifier.js]
+[test_packaged_app_utils.js]
 [test_suspend_channel_before_connect.js]
 [test_inhibit_caching.js]
 [test_dns_disable_ipv4.js]
 [test_dns_disable_ipv6.js]
 [test_packaged_app_service_paths.js]
 [test_bug1195415.js]
 [test_cookie_blacklist.js]
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -21,16 +21,18 @@
 #include "marketplace-stage.inc"
 #include "xpcshell.inc"
 // Trusted Hosted Apps Certificates
 #include "manifest-signing-root.inc"
 #include "manifest-signing-test-root.inc"
 // Add-on signing Certificates
 #include "addons-public.inc"
 #include "addons-stage.inc"
+// Privileged Package Certificates
+#include "privileged-package-root.inc"
 
 using namespace mozilla::pkix;
 
 extern PRLogModuleInfo* gPIPNSSLog;
 
 static const unsigned int DEFAULT_MIN_RSA_BITS = 2048;
 
 namespace mozilla { namespace psm {
@@ -89,16 +91,21 @@ AppTrustDomain::SetTrustedRoot(AppTruste
       trustedDER.len = mozilla::ArrayLength(addonsPublicRoot);
       break;
 
     case nsIX509CertDB::AddonsStageRoot:
       trustedDER.data = const_cast<uint8_t*>(addonsStageRoot);
       trustedDER.len = mozilla::ArrayLength(addonsStageRoot);
       break;
 
+    case nsIX509CertDB::PrivilegedPackageRoot:
+      trustedDER.data = const_cast<uint8_t*>(privilegedPackageRoot);
+      trustedDER.len = mozilla::ArrayLength(privilegedPackageRoot);
+      break;
+
     default:
       PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
       return SECFailure;
   }
 
   mTrustedRoot = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                          &trustedDER, nullptr, false, true);
   if (!mTrustedRoot) {
--- a/security/apps/gen_cert_header.py
+++ b/security/apps/gen_cert_header.py
@@ -32,13 +32,14 @@ array_names = [
   'marketplaceDevPublicRoot',
   'marketplaceDevReviewersRoot',
   'marketplaceStageRoot',
   'trustedAppPublicRoot',
   'trustedAppTestRoot',
   'xpcshellRoot',
   'addonsPublicRoot',
   'addonsStageRoot',
+  'privilegedPackageRoot',
 ]
 
 for n in array_names:
   # Make sure the lambda captures the right string.
   globals()[n] = lambda header, cert_filename, name=n: header.write(_create_header(name, _file_byte_generator(cert_filename)))
--- a/security/apps/moz.build
+++ b/security/apps/moz.build
@@ -29,15 +29,16 @@ headers_arrays_certs = [
     ('marketplace-dev-public.inc', 'marketplaceDevPublicRoot', 'marketplace-dev-public.crt'),
     ('marketplace-dev-reviewers.inc', 'marketplaceDevReviewersRoot', 'marketplace-dev-reviewers.crt'),
     ('marketplace-stage.inc', 'marketplaceStageRoot', 'marketplace-stage.crt'),
     ('manifest-signing-root.inc', 'trustedAppPublicRoot', 'trusted-app-public.der'),
     ('manifest-signing-test-root.inc', 'trustedAppTestRoot', test_ssl_path + '/test_signed_manifest/trusted_ca1.der'),
     ('xpcshell.inc', 'xpcshellRoot', test_ssl_path + '/test_signed_apps/trusted_ca1.der'),
     ('addons-public.inc', 'addonsPublicRoot', 'addons-public.crt'),
     ('addons-stage.inc', 'addonsStageRoot', 'addons-stage.crt'),
+    ('privileged-package-root.inc', 'privilegedPackageRoot', 'privileged-package-root.der'),
 ]
 
 for header, array_name, cert in headers_arrays_certs:
     GENERATED_FILES += [header]
     h = GENERATED_FILES[header]
     h.script = 'gen_cert_header.py:' + array_name
     h.inputs = [cert]
new file mode 100644
index 0000000000000000000000000000000000000000..9f15847bfccb5bfc22cbefd770b543b23ea01df3
GIT binary patch
literal 930
zc$_n6VxDKv#MHKcnTe5!iILHOmyJ`a&7<u*FC!y2D}zC?A-4f18*?ZNn=n&oFpR??
z%;fB7C}to6;;;+z`sSDBl_X~7DTHOFmK&-VD1pSegr!|85_1c3QWZjqN{dTUQxu%@
ziwg3K5=%1k^9&^o#6hZ<g#}=;VTn1JKsAmA3gWzmrUsUV1_l-eCdMXF68uI6rpN*V
z<E$n|CFI~?WMyD(V&rE4igPhFF)}ioyV{?(Z|9V~D+Ai=mnAXHyd!e%WW%<Oite6u
zuL^SS%>K`*`(bDG$|C6?ro*oN>k7X9HvRN}<I$Hn+n*X1?rRXc*LzCuWNk%*)(`)k
ztd}Ni=w=D;I>Y_)$HH|tc7A^ur+WHNVPWeMcK+)fyO)?ZpE&1~@s;(ujm7=N(RL1{
z1+V39TsR`?bfM7afuYYWHw%#kj;Z|%^9$2EQm@~dU9x2(r<>nVhSXWCvz||xuXOuz
zuF>a-MkT4G+J1a)jiJ+G%ck$WrKoQ|y^~X;%Ho)$s;Y+Yf>l#YL;CKN3(u&YdNttn
z3HcfNuU<y0y~~W*lk0q1ZN*A;@wCNvt9hy;{r85{e`I22WMEuuZD47@4-7(CVMfOP
zEUX61K*~T2B%lfs;4$E0<IrYfWMyS%W;WmhiSvWRS(uraSkO`*GbmKcm3}9vhR4lZ
z=)iW~c*pNj+1`E*t`66AH4_hZ?K|^gTl*Bdeb0TjH@x>$?>_7KVNyt2d4Pj#mkYmE
za)N&?*N5_F7ZrTsS_}<lSN(LZIJuTX;B|iIiF~yQHI0oLt}gNJu21vK9tH24yRPkt
z!}9qTX4}PQwsvoL)*LtSRB4$-@Nb)~EX8eBoBvF8Oify5R6Eh(bE#at?W8=0NxO2A
zy&fCaziSJQIVjA&*~9o3_uE^h9@TusRfSq}4f!9Y-^*vLIG6BLtg7lc$GN<X(vlPR
zdOhClx?zQ>-+3E_8;!A^Q_t+)>YqCM^~(4ME4DkI6usMNQ28lod2bWDp3Hgk#hJ4J
Dha6Q2
--- a/security/manager/ssl/nsIX509CertDB.idl
+++ b/security/manager/ssl/nsIX509CertDB.idl
@@ -41,17 +41,17 @@ interface nsIVerifySignedManifestCallbac
   void verifySignedManifestFinished(in nsresult rv,
                                     in nsIX509Cert aSignerCert);
 };
 
 /**
  * This represents a service to access and manipulate
  * X.509 certificates stored in a database.
  */
-[scriptable, uuid(3fe3702b-766b-47dd-8f77-c08c3a339a74)]
+[scriptable, uuid(0a47571d-602c-4b21-9f52-c3d0e681d83a)]
 interface nsIX509CertDB : nsISupports {
 
   /**
    *  Constants that define which usages a certificate
    *  is trusted for.
    */
   const unsigned long UNTRUSTED       =      0;
   const unsigned long TRUSTED_SSL     = 1 << 0;
@@ -313,16 +313,17 @@ interface nsIX509CertDB : nsISupports {
   const AppTrustedRoot AppMarketplaceProdPublicRoot = 1;
   const AppTrustedRoot AppMarketplaceProdReviewersRoot = 2;
   const AppTrustedRoot AppMarketplaceDevPublicRoot = 3;
   const AppTrustedRoot AppMarketplaceDevReviewersRoot = 4;
   const AppTrustedRoot AppMarketplaceStageRoot = 5;
   const AppTrustedRoot AppXPCShellRoot = 6;
   const AppTrustedRoot AddonsPublicRoot = 7;
   const AppTrustedRoot AddonsStageRoot = 8;
+  const AppTrustedRoot PrivilegedPackageRoot = 9;
   void openSignedAppFileAsync(in AppTrustedRoot trustedRoot,
                               in nsIFile aJarFile,
                               in nsIOpenSignedAppFileCallback callback);
 
   /**
    *  Verifies the signature on a directory representing an unpacked signed
    *  JAR file. To be considered valid, there must be exactly one signature
    *  on the directory structure and that signature must have signed every
--- a/testing/talos/talos/output.py
+++ b/testing/talos/talos/output.py
@@ -166,17 +166,17 @@ class GraphserverOutput(Output):
             # HACK: when running xperf, we upload xperf counters to the graph
             # server but we do not want to
             # upload the test results as they will confuse the graph server
             if not (test.format == 'tpformat' and test.using_xperf):
                 vals = []
                 for result in test.results:
                     filtered_val = result.values(testname,
                                                  test.test_config['filters'])
-                    vals.extend([[i['filtered'], j] for i, j in filtered_val])
+                    vals.extend([[i['value'], j] for i, j in filtered_val])
                 result_strings.append(self.construct_results(vals,
                                                              testname=testname,
                                                              **info_dict))
 
             # counter results
             for cd in test.all_counter_results:
                 for counter_type, values in cd.items():
                     # get the counter name
@@ -510,17 +510,17 @@ class PerfherderOutput(Output):
                             results.setdefault(page, []).extend(val)
 
                 tresults = [tsresult] if tsresult else test.results
 
                 for result in tresults:
                     filtered_results = \
                         result.values(test_result['testrun']['suite'],
                                       test.test_config['filters'])
-                    vals.extend([[i['filtered'], j] for i, j in filtered_results])
+                    vals.extend([[i['value'], j] for i, j in filtered_results])
                     for val, page in filtered_results:
                         if page == 'NULL':
                             summary['subtests'][test.name()] = val
                         else:
                             summary['subtests'][page] = val
 
 
                 suite_summary = self.construct_results(vals,
--- a/testing/talos/talos/results.py
+++ b/testing/talos/talos/results.py
@@ -153,39 +153,31 @@ class Results(object):
 
             # ignore* functions return a filtered set of data
             for f in filters:
                 if f.func.__name__.startswith('ignore'):
                     data = f.apply(data)
                 else:
                     remaining_filters.append(f)
 
-            # calculate common numbers with the raw data
-            data_summary = {
-                'min': min(data),
-                'max': max(data),
-                'mean': filter.mean(data),
-                'median': filter.median(data),
-                'std': filter.stddev(data)
-            }
-
             # apply the summarization filters
             for f in remaining_filters:
                 if f.func.__name__ == "v8_subtest":
                     # for v8_subtest we need to page for reference data
                     data = filter.v8_subtest(data, page)
                 else:
                     data = f.apply(data)
-            data_summary['filtered'] = data
 
-            # special case for dromaeo_dom and v8_7
-            if testname == 'dromaeo_dom' or testname.startswith('v8_7'):
-                data_summary['value'] = data
+            summary = {
+                'filtered': data, # for backwards compatibility with perfherder
+                'value': data
+            }
 
-            retval.append([data_summary, page])
+            retval.append([summary, page])
+
         return retval
 
     def raw_values(self):
         return [(result['page'], result['runs']) for result in self.results]
 
     def values(self, testname, filters):
         """return filtered (value, page) for each value"""
         return [[val, page] for val, page in self.filter(testname, filters)
--- a/testing/taskcluster/README.md
+++ b/testing/taskcluster/README.md
@@ -52,17 +52,18 @@ templates variables:
 
   - `now`: Current time as a json formatted date.
 
 
 ### Build tasks
 
 By convention build tasks are stored in `tasks/builds/` the location of
 each particular type of build is specified in `job_flags.yml` (and more
-locations in the future)
+locations in the future), which is located in the appropriate subdirectory
+of `branches/`.
 
 #### Task format
 
 To facilitate better reuse of tasks there are some expectations of the
 build tasks. These are required for the test tasks to interact with the
 builds correctly but may not effect the builds or indexing services.
 
 ```yaml
--- a/testing/taskcluster/tasks/branches/alder/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/alder/job_flags.yml
@@ -1,11 +1,11 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_job_flags.yml
 
 builds:
   linux64_gecko:
     platforms:
       - b2g
--- a/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/b2g-inbound/job_flags.yml
@@ -1,7 +1,7 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_jobs.yml
 
--- a/testing/taskcluster/tasks/branches/base_job_flags.yml
+++ b/testing/taskcluster/tasks/branches/base_job_flags.yml
@@ -29,16 +29,17 @@ flags:
     - dolphin-512
     - dolphin-512-eng
     - aries
     - aries-ota
     - aries-eng
     - aries-dogfood
     - android-api-11
     - linux64
+    - linux64-st-an
     - macosx64
 
   tests:
     - cppunit
     - crashtest
     - crashtest-ipc
     - gaia-build
     - gaia-build-unit
--- a/testing/taskcluster/tasks/branches/base_jobs.yml
+++ b/testing/taskcluster/tasks/branches/base_jobs.yml
@@ -1,11 +1,11 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_job_flags.yml
 
 builds:
   linux64_gecko:
     platforms:
       - b2g
--- a/testing/taskcluster/tasks/branches/cedar/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/cedar/job_flags.yml
@@ -1,11 +1,11 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_job_flags.yml
 
 builds:
   linux64_gecko:
     platforms:
       - b2g
--- a/testing/taskcluster/tasks/branches/fx-team/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/fx-team/job_flags.yml
@@ -1,7 +1,7 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_jobs.yml
 
--- a/testing/taskcluster/tasks/branches/mozilla-central/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/mozilla-central/job_flags.yml
@@ -1,11 +1,11 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_jobs.yml
 
 builds:
   android-api-11:
     platforms:
       - Android
--- a/testing/taskcluster/tasks/branches/mozilla-inbound/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/mozilla-inbound/job_flags.yml
@@ -1,7 +1,7 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_jobs.yml
 
--- a/testing/taskcluster/tasks/branches/try/job_flags.yml
+++ b/testing/taskcluster/tasks/branches/try/job_flags.yml
@@ -1,11 +1,11 @@
 ---
 # For complete sample of all build and test jobs,
-# see <gecko>/testing/taskcluster/tasks/job_flags.yml
+# see <gecko>/testing/taskcluster/tasks/branches/base_job_flags.yml
 
 $inherits:
   from: tasks/branches/base_job_flags.yml
 
 # Flags specific to this branch
 flags:
   post-build:
     - upload-symbols
deleted file mode 100644
--- a/testing/taskcluster/tasks/job_flags.yml
+++ /dev/null
@@ -1,259 +0,0 @@
-# This file contains the list of "job flags"/"try flags" for tests and builds.
----
-
-# List of all possible flags for each category of tests used in the case where
-# "all" is specified.
-flags:
-  builds:
-    - emulator
-    - emulator-jb
-    - emulator-kk
-    - emulator-x86-kk
-    - linux32_gecko   # b2g desktop linux 32 bit
-    - linux64_gecko   # b2g desktop linux 64 bit
-    - linux64-mulet   # Firefox desktop - b2g gecko linux 64 bit
-    - macosx64_gecko  # b2g desktop osx 64 bit
-    - win32_gecko     # b2g desktop win 32 bit
-    - sm-plain        # spidermonkey plain
-    - sm-arm-sim      # spidermonkey arm-sim
-    - sm-arm-sim-osx  # spidermonkey arm-sim-osx
-    - sm-compacting   # spidermonkey compacting
-    - sm-generational # spidermonkey generational
-    - sm-rootanalysis # spidermonkey rootanalysis
-    - sm-warnaserr    # spidermonkey warnings-as-errors
-
-  tests:
-    - cppunit
-    - crashtest
-    - crashtest-ipc
-    - gaia-build
-    - gaia-build-unit
-    - gaia-js-integration
-    - gaia-linter
-    - gaia-unit
-    - gaia-unit-oop
-    - gaia-ui-test-oop
-    - gaia-ui-test-accessibility
-    - gaia-ui-test-functional
-    - gaia-ui-test-unit
-    - jetpack
-    - jittests
-    - jsreftest
-    - marionette
-    - marionette-webapi
-    - mochitest
-    - mochitest-media
-    - mochitest-oop
-    - mozmill
-    - reftest
-    - reftest-ipc
-    - reftest-no-accel
-    - reftest-sanity-oop
-    - web-platform-tests
-    - xpcshell
-
-# Build section covers the -b[uild] and -p[latform] options that try provides.
-builds:
-  # The format for registering a new build flag -> task looks like this:
-  #
-  # <flag name>
-  #   # Platforms are primarily used to restrict test runs to only X platform
-  #   # but the information is stored on the build to indicate which platform(s)
-  #   # the build belongs to. Note that `platforms` here is the term used by the
-  #   # try chooser meaning "some group of tests" examples of platforms are
-  #   # things like "b2g", "win32"
-  #   platforms:
-  #     - <platform name>
-  #   # note that for sanity o -> means opt and d -> means debug if additional
-  #   # flags are passed we will attempt to match them up to an option here if
-  #   # available
-  #   types:
-  #     opt: <path to opt task>
-  #     debug: <path to debug task>
-  #
-  linux64_gecko:
-    platforms:
-      - b2g
-    types:
-      opt:
-        task: tasks/builds/b2g_desktop_opt.yml
-      debug:
-        task: tasks/builds/b2g_desktop_debug.yml
-  linux64-mulet:
-    platforms:
-      - Mulet Linux
-    types:
-      opt:
-        task: tasks/builds/mulet_linux.yml
-  emulator-kk:
-    platforms:
-      - b2g
-    types:
-      opt:
-        task: tasks/builds/b2g_emulator_kk_opt.yml
-      debug:
-        task: tasks/builds/b2g_emulator_kk_debug.yml
-  emulator-jb:
-    platforms:
-      - b2g
-    types:
-      opt:
-        task: tasks/builds/b2g_emulator_jb_opt.yml
-      debug:
-        task: tasks/builds/b2g_emulator_jb_debug.yml
-  emulator:
-    platforms:
-      - b2g
-    types:
-      opt:
-        task: tasks/builds/b2g_emulator_ics_opt.yml
-      debug:
-        task: tasks/builds/b2g_emulator_ics_debug.yml
-
-# Test section covers the -u options in the try flags
-tests:
-  # The format for registering a new test flag -> task looks like this:
-  #
-  # <flag name>
-  #   task: <path to test task>
-  #   # Note that total number of chunks effects more then just performance we
-  #   # need to schedule specific chunks in some cases!
-  #   chunks: <total number of chunks>
-  #   # Not all tests can run on all builds and we may not want to run some
-  #   # tests on all build variants so we use "allowed tasks" instead of
-  #   # "allowed platforms" here.
-  #   allowed_build_tasks:
-  #     - builds/b2g_desktop_opt.yml
-  cppunit:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_cpp_unit.yml
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_cpp_unit.yml
-  crashtest:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_crashtest.yml
-        chunks: 3
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_crashtest.yml
-        chunks: 3
-  gaia-build:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_build_test.yml
-  gaia-build-unit:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_build_unit.yml
-  gaia-js-integration:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_js_integration_tests.yml
-        chunks: 10
-      tasks/builds/b2g_desktop_debug.yml:
-        task: tasks/tests/b2g_gaia_js_integration_tests.yml
-        chunks: 10
-  gaia-linter:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_linter.yml
-  gaia-ui-test-accessibility:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_ui_test_accessibility.yml
-      tasks/builds/b2g_emulator.yml:
-        task: tasks/tests/b2g_emulator_gaia_ui_test_accessibility.yml
-  gaia-ui-test-functional:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_ui_test_functional.yml
-        chunks: 3
-  gaia-ui-test-oop:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gip_oop.yml
-  gaia-ui-test-unit:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_ui_test_unit.yml
-  gaia-unit:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_unit.yml
-  gaia-unit-oop:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_gaia_unit_oop.yml
-  jsreftest:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_js_reftest.yml
-        chunks: 3
-  marionette:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_marionette.yml
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_marionette.yml
-  marionette-webapi:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_marionette_webapi.yml
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_marionette_webapi.yml
-  mochitest:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_mochitest.yml
-        chunks: 5
-      tasks/builds/b2g_desktop_debug.yml:
-        task: tasks/tests/b2g_mochitest.yml
-        chunks: 5
-      tasks/builds/mulet_linux.yml:
-        task: tasks/tests/mulet_mochitests.yml
-        chunks: 5
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_mochitest.yml
-        chunks: 9
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_mochitest.yml
-        chunks: 20
-      tasks/builds/b2g_emulator_jb_opt.yml:
-        task: tasks/tests/b2g_emulator_mochitest.yml
-        chunks: 1
-  mochitest-media:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_mochitest_media.yml
-  mochitest-oop:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_mochitest_oop.yml
-        chunks: 1
-  reftest:
-    allowed_build_tasks:
-      tasks/builds/mulet_linux.yml:
-        task: tasks/tests/mulet_reftests.yml
-        chunks: 6
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_reftests.yml
-        chunks: 10
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_reftest.yml
-        chunks: 20
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_reftest.yml
-        chunks: 10
-  reftest-sanity-oop:
-    allowed_build_tasks:
-      tasks/builds/b2g_desktop_opt.yml:
-        task: tasks/tests/b2g_reftests_sanity_oop.yml
-        chunks: 1
-  xpcshell:
-    allowed_build_tasks:
-      tasks/builds/b2g_emulator_ics_opt.yml:
-        task: tasks/tests/b2g_emulator_xpcshell.yml
-      tasks/builds/b2g_emulator_ics_debug.yml:
-        task: tasks/tests/b2g_emulator_xpcshell_chunked.yml
-        chunks: 2
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZ_PROFILE_BUFFER_H
 #define MOZ_PROFILE_BUFFER_H
 
 #include "ProfileEntry.h"
 #include "platform.h"
 #include "ProfileJSONWriter.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
 
 class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer> {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
 
   explicit ProfileBuffer(int aEntrySize);
 
   virtual ~ProfileBuffer();
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -5884,17 +5884,19 @@ PanGestureTypeForEvent(NSEvent* aEvent)
   nsCOMPtr<nsIDragService> dragService = mDragService;
   if (!dragService) {
     dragService = do_GetService(kDragServiceContractID);
   }
 
   if (dragService) {
     NSPoint pnt = [NSEvent mouseLocation];
     FlipCocoaScreenCoordinate(pnt);
-    dragService->DragMoved(NSToIntRound(pnt.x), NSToIntRound(pnt.y));
+
+    nsIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt);
+    dragService->DragMoved(devPoint.x, devPoint.y);
   }
 }
 
 // NSDraggingSource
 - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -427,18 +427,18 @@ nsBaseDragService::FireDragEventAtSource
  */
 NS_IMETHODIMP
 nsBaseDragService::DragMoved(int32_t aX, int32_t aY)
 {
   if (mDragPopup) {
     nsIFrame* frame = mDragPopup->GetPrimaryFrame();
     if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
       nsPresContext* presContext = frame->PresContext();
-      int32_t x = presContext->DevPixelsToIntCSSPixels(aX - mImageX);
-      int32_t y = presContext->DevPixelsToIntCSSPixels(aY - mImageY);
+      int32_t x = presContext->DevPixelsToIntCSSPixels(aX) - mImageX;
+      int32_t y = presContext->DevPixelsToIntCSSPixels(aY) - mImageY;
       (static_cast<nsMenuPopupFrame *>(frame))->MoveTo(x, y, true);
     }
   }
 
   return NS_OK;
 }
 
 static nsIPresShell*