Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Mon, 07 Jan 2013 16:09:32 +0000
changeset 127023 c4966e14e726f492f394732b749cc2c38b20bf00
parent 126980 683f2f7633164d35502f9582e953c0c6a6c07367 (current diff)
parent 127022 2889e68bf6ca305dcc9887e438077aa346f7f39c (diff)
child 127024 8f8ceea47a265e7787230997bef25e0a5716915f
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone20.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 last PGO-green changeset of mozilla-inbound to mozilla-central
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -614,14 +614,17 @@ pref("font.size.inflation.disabledInMast
 // Enable freeing dirty pages when minimizing memory; this reduces memory
 // consumption when applications are sent to the background.
 pref("memory.free_dirty_pages", true);
 
 // UAProfile settings
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
 
+// Enable native identity (persona/browserid)
+pref("dom.identity.enabled", true);
+
 // Wait up to this much milliseconds when orientation changed
 pref("layers.orientation.sync.timeout", 1000);
 
 // Don't discard WebGL contexts for foreground apps on memory
 // pressure.
 pref("webgl.can-lose-context-in-foreground", false);
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -178,16 +178,18 @@ let FormVisibility = {
   }
 };
 
 let FormAssistant = {
   init: function fa_init() {
     addEventListener("focus", this, true, false);
     addEventListener("blur", this, true, false);
     addEventListener("resize", this, true, false);
+    addEventListener("submit", this, true, false);
+    addEventListener("pagehide", this, true, false);
     addMessageListener("Forms:Select:Choice", this);
     addMessageListener("Forms:Input:Value", this);
     addMessageListener("Forms:Select:Blur", this);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   ignoredInputTypes: new Set([
     'button', 'file', 'checkbox', 'radio', 'reset', 'submit', 'image'
@@ -241,16 +243,18 @@ let FormAssistant = {
           break;
         }
 
         if (target && this.isFocusableElement(target))
           this.showKeyboard(target);
         break;
 
       case "blur":
+      case "submit":
+      case "pagehide":
         if (this.focusedElement)
           this.hideKeyboard();
         break;
 
       case 'mousedown':
         // We only listen for this event on the currently focused element.
         // When the mouse goes down, note the cursor/selection position
         this.selectionStart = this.focusedElement.selectionStart;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2528,16 +2528,31 @@
           if (!aEvent.isTrusted) {
             // Don't let untrusted events mess with tabs.
             return;
           }
 
           if (aEvent.altKey)
             return;
 
+          if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) {
+            switch (aEvent.keyCode) {
+              case aEvent.DOM_VK_PAGE_UP:
+                this.moveTabBackward();
+                aEvent.stopPropagation();
+                aEvent.preventDefault();
+                return;
+              case aEvent.DOM_VK_PAGE_DOWN:
+                this.moveTabForward();
+                aEvent.stopPropagation();
+                aEvent.preventDefault();
+                return;
+            }
+          }
+
           // We need to take care of FAYT-watching as long as the findbar
           // isn't initialized.  The checks on aEvent are copied from
           // _shouldFastFind (see findbar.xml).
           if (!gFindBarInitialized &&
               !(aEvent.ctrlKey || aEvent.metaKey) &&
               !aEvent.defaultPrevented) {
             let charCode = aEvent.charCode;
             if (charCode) {
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -77,18 +77,16 @@ SessionStartup.prototype = {
       return;
 
 #ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     let pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
     if (pbs.lastChangedByCommandLine)
       return;
 #endif
-    // Session state is unknown until we read the file.
-    this._sessionType = null;
     _SessionFile.read().then(
       this._onSessionFileRead.bind(this)
     );
     debug("init launched");
   },
 
   // Wrap a string as a nsISupports
   _createSupportsString: function ssfi_createSupportsString(aData) {
--- a/configure.in
+++ b/configure.in
@@ -5230,16 +5230,24 @@ if test "$ACCESSIBILITY"; then
                 AC_MSG_ERROR([You have accessibility enabled, but widl could not be found. Add --disable-accessibility to your mozconfig or install widl. See https://developer.mozilla.org/en-US/docs/Cross_Compile_Mozilla_for_Mingw32 for details.])
             fi
         fi
     esac
     AC_DEFINE(ACCESSIBILITY)
 fi
 
 dnl ========================================================
+dnl Accessibility is required for the linuxgl widget
+dnl backend
+dnl ========================================================
+if test "${MOZ_WIDGET_TOOLKIT}" = "linuxgl" -a "$ACCESSIBILITY" != "1"; then
+    AC_MSG_ERROR(["Accessibility is required for the linuxgl widget backend"])
+fi
+
+dnl ========================================================
 dnl Disable printing
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(printing,
 [  --disable-printing      Disable printing support],
     NS_PRINTING=,
     NS_PRINTING=1)
 
 if test "$NS_PRINTING"; then
@@ -5248,18 +5256,22 @@ if test "$NS_PRINTING"; then
 fi
 
 dnl Turn off webrtc for OS's we don't handle yet, but allow 
 dnl --enable-webrtc to override.  Can disable for everything in
 dnl the master list above.
 if test -n "$MOZ_WEBRTC"; then
     case "$target" in
     *-android*|*-linuxandroid*)
-        dnl Make sure doesn't get matched by *-linux*
-        MOZ_WEBRTC=
+        if test -n "$MOZ_B2G"; then
+            MOZ_WEBRTC=1
+        else
+            dnl Make sure doesn't get matched by *-linux*
+            MOZ_WEBRTC=
+        fi
         ;;
     *-linux*|*-mingw*|*-darwin*)
         dnl Leave enabled
         ;;
     *)
         dnl default to disabled for all others
         MOZ_WEBRTC=
         ;;
@@ -5277,28 +5289,30 @@ MOZ_ARG_DISABLE_BOOL(webrtc,
 if test -n "$MOZ_WEBRTC"; then
     AC_DEFINE(MOZ_WEBRTC)
     MOZ_MEDIA=1
     MOZ_RAW=1
     MOZ_VP8=1
     MOZ_VP8_ENCODER=1
     MOZ_VP8_ERROR_CONCEALMENT=1
 
-    dnl OpenSLES is only available in Android 2.3 and later; we'll change this
-    dnl hard dependency to a dynamic load with graceful runtime failure before
-    dnl we make --enable-webrtc on by default in Android (bug 815905)
-    dnl
-    if test "$OS_TARGET" = "Android"; then
-       LDFLAGS="$LDFLAGS -lOpenSLES"
-    fi
-    case "$target" in
-    *-android*|*-linuxandroid*)
-       LDFLAGS="$LDFLAGS -lOpenSLES"
-       ;;
-    esac
+    if test "$MOZ_WIDGET_TOOLKIT" != "gonk"; then
+       dnl OpenSLES is only available in Android 2.3 and later; we'll change this
+       dnl hard dependency to a dynamic load with graceful runtime failure before
+       dnl we make --enable-webrtc on by default in Android (bug 815905)
+       dnl
+       if test "$OS_TARGET" = "Android"; then
+          LDFLAGS="$LDFLAGS -lOpenSLES"
+       fi
+       case "$target" in
+          *-android*|*-linuxandroid*)
+          LDFLAGS="$LDFLAGS -lOpenSLES"
+          ;;
+       esac
+    fi
 
 dnl enable once Signaling lands
     MOZ_WEBRTC_SIGNALING=1
     AC_DEFINE(MOZ_WEBRTC_SIGNALING)
     if test "${OS_TARGET}" = "WINNT"; then
         MOZ_WEBRTC_IN_LIBXUL=1
     fi
 dnl enable once PeerConnection lands
@@ -9040,31 +9054,45 @@ if test "${OS_TARGET}" = "WINNT"; then
    if test "$HAVE_64BIT_OS"; then
       OS_BITS=64
    else
       OS_BITS=32
    fi
    EXTRA_GYP_DEFINES="-D MSVS_VERSION=${_MSVS_VERSION} -D MSVS_OS_BITS=${OS_BITS}"
 
 elif test "${OS_TARGET}" = "Android"; then
-   EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
+   if test "${MOZ_WIDGET_TOOLKIT}" != "gonk"; then
+      EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
+   fi
    if test -n "$ARM_ARCH" && test "$ARM_ARCH" -lt 7; then
-      EXTRA_GYP_DEFINES+=" -D armv7=0 "
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
    else
-      EXTRA_GYP_DEFINES+=" -D armv7=1 "
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 "
    fi
 fi
 
 if test -n "$MOZ_WEBRTC"; then
    AC_MSG_RESULT("generating WebRTC Makefiles...")
 
+   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D moz_widget_toolkit_gonk=1"
+   else
+      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D moz_widget_toolkit_gonk=0"
+   fi
+
 dnl Any --include files must also appear in -D FORCED_INCLUDE_FILE= entries
 dnl so that regeneration via dependencies works correctly
    WEBRTC_CONFIG="-D build_with_mozilla=1 --include ${srcdir}/media/webrtc/webrtc_config.gypi -D FORCED_INCLUDE_FILE=${srcdir}/media/webrtc/webrtc_config.gypi"
 
+   if test -n HAVE_CLOCK_MONOTONIC; then
+      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D moz_have_clock_monotonic=1"
+   else
+      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D moz_have_clock_monotonic=0"
+   fi
+
    GYP_WEBRTC_OPTIONS="--format=mozmake ${WEBRTC_CONFIG} -D target_arch=${WEBRTC_TARGET_ARCH} ${EXTRA_GYP_DEFINES} --depth=${srcdir}/media/webrtc/trunk --toplevel-dir=${srcdir} -G OBJDIR=${_objdir}"
 
    $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
      $GYP_WEBRTC_OPTIONS \
      --generator-output=${_objdir}/media/webrtc/trunk \
      ${srcdir}/media/webrtc/trunk/peerconnection.gyp
    if test "$?" != 0; then
       AC_MSG_ERROR([failed to generate WebRTC Makefiles])
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -454,16 +454,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream);
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLMediaElement, Crossorigin, crossorigin)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Loop, loop)
@@ -3593,11 +3594,13 @@ void nsHTMLMediaElement::UpdateAudioChan
     }
   }
 #endif
 }
 
 /* void canPlayChanged (in boolean canPlay); */
 NS_IMETHODIMP nsHTMLMediaElement::CanPlayChanged(bool canPlay)
 {
+  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
+
   UpdateChannelMuteState(canPlay);
   return NS_OK;
 }
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -403,23 +403,28 @@ nsMathMLElement::ParseNumericValue(const
     }
     return false;
   }
 
   nsCSSUnit cssUnit;
   if (unit.IsEmpty()) {
     if (aFlags & PARSE_ALLOW_UNITLESS) {
       // no explicit unit, this is a number that will act as a multiplier
-      cssUnit = eCSSUnit_Number;
       if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
         nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                         "MathML", aDocument,
                                         nsContentUtils::eMATHML_PROPERTIES,
                                         "UnitlessValuesAreDeprecated");
       }
+      if (aFlags & CONVERT_UNITLESS_TO_PERCENT) {
+        aCSSValue.SetPercentValue(floatValue);
+        return true;
+      }
+      else
+        cssUnit = eCSSUnit_Number;
     } else {
       // We are supposed to have a unit, but there isn't one.
       // If the value is 0 we can just call it "pixels" otherwise
       // this is illegal.
       if (floatValue != 0.0) {
         if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
           ReportLengthParseError(aString, aDocument);
         }
@@ -491,25 +496,30 @@ nsMathMLElement::MapMathMLAttributesInto
     //
     // "Specifies the minimum font size allowed due to changes in scriptlevel.
     // Note that this does not limit the font size due to changes to mathsize."
     //
     // values: length
     // default: 8pt
     //
     // We don't allow negative values.
-    // XXXfredw Should we allow unitless values? (bug 411227)
-    // XXXfredw Does a relative unit give a multiple of the default value?
+    // Unitless and percent values give a multiple of the default value.
     //
     value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
     nsCSSValue* scriptMinSize = aData->ValueForScriptMinSize();
     if (value && value->Type() == nsAttrValue::eString &&
         scriptMinSize->GetUnit() == eCSSUnit_Null) {
-      ParseNumericValue(value->GetStringValue(), *scriptMinSize, 0,
+      ParseNumericValue(value->GetStringValue(), *scriptMinSize,
+                        PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
                         aData->mPresContext->Document());
+
+      if (scriptMinSize->GetUnit() == eCSSUnit_Percent) {
+        scriptMinSize->SetFloatValue(8.0 * scriptMinSize->GetPercentValue(),
+                                     eCSSUnit_Point);
+      }
     }
 
     // scriptlevel
     // 
     // "Changes the scriptlevel in effect for the children. When the value is
     // given without a sign, it sets scriptlevel to the specified value; when a
     // sign is given, it increments ("+") or decrements ("-") the current
     // value. (Note that large decrements can result in negative values of
@@ -560,35 +570,36 @@ nsMathMLElement::MapMathMLAttributesInto
     // fontsize
     //
     // "Specified the size for the token. Deprecated in favor of mathsize."
     //
     // values: length
     // default: inherited
     //
     // In both cases, we don't allow negative values.
-    // XXXfredw Should we allow unitless values? (bug 411227)
-    // XXXfredw Does a relative unit give a multiple of the default value?
+    // Unitless values give a multiple of the default value.
     //  
     bool parseSizeKeywords = true;
     value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
     if (!value) {
       parseSizeKeywords = false;
       value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
       if (value) {
         WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(),
                        nsGkAtoms::mathsize_->GetUTF16String(),
                        aData->mPresContext->Document());
       }
     }
     nsCSSValue* fontSize = aData->ValueForFontSize();
     if (value && value->Type() == nsAttrValue::eString &&
         fontSize->GetUnit() == eCSSUnit_Null) {
       nsAutoString str(value->GetStringValue());
-      if (!ParseNumericValue(str, *fontSize, PARSE_SUPPRESS_WARNINGS, nullptr)
+      if (!ParseNumericValue(str, *fontSize, PARSE_SUPPRESS_WARNINGS |
+                             PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
+                             nullptr)
           && parseSizeKeywords) {
         static const char sizes[3][7] = { "small", "normal", "big" };
         static const int32_t values[NS_ARRAY_LENGTH(sizes)] = {
           NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM,
           NS_STYLE_FONT_SIZE_LARGE
         };
         str.CompressWhitespace();
         for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
--- a/content/mathml/content/src/nsMathMLElement.h
+++ b/content/mathml/content/src/nsMathMLElement.h
@@ -49,17 +49,18 @@ public:
                                 nsAttrValue& aResult);
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
   enum {
     PARSE_ALLOW_UNITLESS = 0x01, // unitless 0 will be turned into 0px
     PARSE_ALLOW_NEGATIVE = 0x02,
-    PARSE_SUPPRESS_WARNINGS = 0x04
+    PARSE_SUPPRESS_WARNINGS = 0x04,
+    CONVERT_UNITLESS_TO_PERCENT = 0x08
   };
   static bool ParseNamedSpaceValue(const nsString& aString,
                                    nsCSSValue&     aCSSValue,
                                    uint32_t        aFlags);
 
   static bool ParseNumericValue(const nsString& aString,
                                 nsCSSValue&     aCSSValue,
                                 uint32_t        aFlags,
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1772,18 +1772,30 @@ MediaStream::GraphTimeToStreamTime(Graph
 
 void
 MediaStream::FinishOnGraphThread()
 {
   GraphImpl()->FinishStream(this);
 }
 
 void
+MediaStream::RemoveAllListenersImpl()
+{
+  for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
+    nsRefPtr<MediaStreamListener> listener = mListeners[i].forget();
+    listener->NotifyRemoved(GraphImpl());
+  }
+  mListeners.Clear();
+}
+
+void
 MediaStream::DestroyImpl()
 {
+  RemoveAllListenersImpl();
+
   for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
     mConsumers[i]->Disconnect();
   }
   for (uint32_t i = 0; i < mAudioOutputStreams.Length(); ++i) {
     mAudioOutputStreams[i].mStream->Shutdown();
   }
   mAudioOutputStreams.Clear();
 }
@@ -1957,16 +1969,25 @@ MediaStream::AddListener(MediaStreamList
       mStream->AddListenerImpl(mListener.forget());
     }
     nsRefPtr<MediaStreamListener> mListener;
   };
   GraphImpl()->AppendMessage(new Message(this, aListener));
 }
 
 void
+MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
+{ 
+  // wouldn't need this if we could do it in the opposite order
+  nsRefPtr<MediaStreamListener> listener(aListener);
+  mListeners.RemoveElement(aListener);
+  listener->NotifyRemoved(GraphImpl());
+}
+
+void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
       ControlMessage(aStream), mListener(aListener) {}
     virtual void Run()
     {
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -138,16 +138,22 @@ public:
    */
   virtual void NotifyOutput(MediaStreamGraph* aGraph) {}
 
   /**
    * Notify that the stream finished.
    */
   virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
 
+  /**
+   * Notify that your listener has been removed, either due to RemoveListener(),
+   * or due to the stream being destroyed.  You will get no further notifications.
+   */
+  virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
+
   enum {
     TRACK_EVENT_CREATED = 0x01,
     TRACK_EVENT_ENDED = 0x02
   };
   /**
    * Notify that changes to one of the stream tracks have been queued.
    * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
    * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
@@ -358,20 +364,19 @@ public:
   {
     mVideoOutputs.RemoveElement(aContainer);
   }
   void ChangeExplicitBlockerCountImpl(StreamTime aTime, int32_t aDelta)
   {
     mExplicitBlockerCount.SetAtAndAfter(aTime, mExplicitBlockerCount.GetAt(aTime) + aDelta);
   }
   void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
-  void RemoveListenerImpl(MediaStreamListener* aListener)
-  {
-    mListeners.RemoveElement(aListener);
-  }
+  void RemoveListenerImpl(MediaStreamListener* aListener);
+  void RemoveAllListenersImpl();
+
   void AddConsumer(MediaInputPort* aPort)
   {
     mConsumers.AppendElement(aPort);
   }
   void RemoveConsumer(MediaInputPort* aPort)
   {
     mConsumers.RemoveElement(aPort);
   }
--- a/content/media/nsDOMMediaStream.cpp
+++ b/content/media/nsDOMMediaStream.cpp
@@ -60,16 +60,24 @@ nsDOMMediaStream::~nsDOMMediaStream()
 
 NS_IMETHODIMP
 nsDOMMediaStream::GetCurrentTime(double *aCurrentTime)
 {
   *aCurrentTime = mStream ? MediaTimeToSeconds(mStream->GetCurrentTime()) : 0.0;
   return NS_OK;
 }
 
+nsDOMLocalMediaStream::~nsDOMLocalMediaStream()
+{
+  if (mStream) {
+    // Make sure Listeners of this stream know it's going away
+    Stop();
+  }
+}
+
 NS_IMETHODIMP
 nsDOMLocalMediaStream::Stop()
 {
   if (mStream && mStream->AsSourceStream()) {
     mStream->AsSourceStream()->EndAllTrackAndFinish();
   }
   return NS_OK;
 }
--- a/content/media/nsDOMMediaStream.h
+++ b/content/media/nsDOMMediaStream.h
@@ -100,17 +100,17 @@ protected:
   uint32_t mHintContents;
 };
 
 class nsDOMLocalMediaStream : public nsDOMMediaStream,
                               public nsIDOMLocalMediaStream
 {
 public:
   nsDOMLocalMediaStream() {}
-  virtual ~nsDOMLocalMediaStream() {}
+  virtual ~nsDOMLocalMediaStream();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMLocalMediaStream, nsDOMMediaStream)
   NS_DECL_NSIDOMLOCALMEDIASTREAM
 
   NS_FORWARD_NSIDOMMEDIASTREAM(nsDOMMediaStream::)
 
   /**
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -611,16 +611,33 @@ BluetoothHfpManager::ReceiveSocketData(U
   } else if (msg.Find("AT+CIND?") != -1) {
     // Asking for CIND value
     SendCommand("+CIND: ", 1);
   } else if (msg.Find("AT+CMER=") != -1) {
     /**
      * SLC establishment is done when AT+CMER has been received.
      * Do nothing but respond with "OK".
      */
+  } else if (msg.Find("AT+VGM=") != -1) {
+    ParseAtCommand(msg, 7, atCommandValues);
+
+    if (atCommandValues.IsEmpty()) {
+      NS_WARNING("Couldn't get the value of command [AT+VGM]");
+      goto respond_with_ok;
+    }
+
+    nsresult rv;
+    int vgm = atCommandValues[0].ToInteger(&rv);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to extract microphone volume from bluetooth headset!");
+      goto respond_with_ok;
+    }
+
+    NS_ASSERTION(vgm >= 0 && vgm <= 15, "Received invalid VGM value");
+    mCurrentVgm = vgm;
   } else if (msg.Find("AT+CHLD=?") != -1) {
     SendLine("+CHLD: (1,2)");
   } else if (msg.Find("AT+CHLD=") != -1) {
     ParseAtCommand(msg, 8, atCommandValues);
 
     if (atCommandValues.IsEmpty()) {
       NS_WARNING("Could't get the value of command [AT+VGS=]");
       goto respond_with_ok;
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -51,16 +51,17 @@ private:
   void Cleanup();
   void NotifyDialer(const nsAString& aCommand);
   void NotifySettings();
   virtual void OnConnectSuccess() MOZ_OVERRIDE;
   virtual void OnConnectError() MOZ_OVERRIDE;
   virtual void OnDisconnect() MOZ_OVERRIDE;
 
   int mCurrentVgs;
+  int mCurrentVgm;
   int mCurrentCallIndex;
   bool mCLIP;
   bool mReceiveVgsFlag;
   nsString mDevicePath;
   nsString mMsisdn;
   enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
   nsTArray<int> mCurrentCallStateArray;
   nsAutoPtr<BluetoothRilListener> mListener;
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -425,14 +425,20 @@ InitProcessPriorityManager()
   sManager = new ProcessPriorityManager();
   sManager->Init();
   ClearOnShutdown(&sManager);
 }
 
 bool
 CurrentProcessIsForeground()
 {
+  // The process priority manager is the only thing which changes our priority,
+  // so if the manager does not exist, then we must be in the foreground.
+  if (!sManager) {
+    return true;
+  }
+
   return sManager->GetPriority() >= PROCESS_PRIORITY_FOREGROUND;
 }
 
 } // namespace ipc
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -187,16 +187,39 @@ public:
   }
 
 private:
   already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices;
 };
 
+// Handle removing GetUserMediaCallbackMediaStreamListener from main thread
+class GetUserMediaListenerRemove: public nsRunnable
+{
+public:
+  GetUserMediaListenerRemove(uint64_t aWindowID,
+    GetUserMediaCallbackMediaStreamListener *aListener)
+    : mWindowID(aWindowID)
+    , mListener(aListener) {}
+
+  NS_IMETHOD
+  Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+    nsRefPtr<MediaManager> manager(MediaManager::GetInstance());
+    manager->RemoveFromWindowList(mWindowID, mListener);
+    return NS_OK;
+  }
+
+protected:
+  uint64_t mWindowID;
+  nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
+};
+
 /**
  * nsIMediaDevice implementation.
  */
 NS_IMPL_THREADSAFE_ISUPPORTS1(MediaDevice, nsIMediaDevice)
 
 NS_IMETHODIMP
 MediaDevice::GetName(nsAString& aName)
 {
@@ -236,24 +259,34 @@ public:
   {
     nsRefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream();
     stream->InitTrackUnionStream(aHintContents);
     return stream.forget();
   }
 
   virtual ~nsDOMUserMediaStream()
   {
+    Stop();
+
     if (mPort) {
       mPort->Destroy();
     }
     if (mSourceStream) {
       mSourceStream->Destroy();
     }
   }
 
+  NS_IMETHODIMP Stop()
+  {
+    if (mSourceStream) {
+      mSourceStream->EndAllTrackAndFinish();
+    }
+    return NS_OK;
+  }
+
   // The actual MediaStream is a TrackUnionStream. But these resources need to be
   // explicitly destroyed too.
   nsRefPtr<SourceMediaStream> mSourceStream;
   nsRefPtr<MediaInputPort> mPort;
 };
 
 /**
  * Creates a MediaStream, attaches a listener and fires off a success callback
@@ -271,37 +304,39 @@ public:
  */
 class GetUserMediaStreamRunnable : public nsRunnable
 {
 public:
   GetUserMediaStreamRunnable(
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID,
+    GetUserMediaCallbackMediaStreamListener* aListener,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource)
     : mSuccess(aSuccess)
     , mError(aError)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mWindowID(aWindowID)
+    , mListener(aListener)
     , mManager(MediaManager::GetInstance()) {}
 
   ~GetUserMediaStreamRunnable() {}
 
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
     // We're on main-thread, and the windowlist can only
     // be invalidated from the main-thread (see OnNavigation)
     StreamListeners* listeners = mManager->GetWindowListeners(mWindowID);
     if (!listeners) {
-      // This window is no longer live.
+      // This window is no longer live.  mListener has already been removed
       return NS_OK;
     }
 
     // Create a media stream.
     uint32_t hints = (mAudioSource ? nsDOMMediaStream::HINT_CONTENTS_AUDIO : 0);
     hints |= (mVideoSource ? nsDOMMediaStream::HINT_CONTENTS_VIDEO : 0);
 
     nsRefPtr<nsDOMUserMediaStream> trackunion =
@@ -324,37 +359,29 @@ public:
     trackunion->mPort = port;
 
     nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
       (nsGlobalWindow::GetInnerWindowWithId(mWindowID));
     if (window && window->GetExtantDoc()) {
       trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
     }
 
-    // Ensure there's a thread for gum to proxy to off main thread
-    nsIThread *mediaThread = MediaManager::GetThread();
-
-    // Add our listener. We'll call Start() on the source when get a callback
+    // The listener was added at the begining in an inactive state.
+    // Activate our listener. We'll call Start() on the source when get a callback
     // that the MediaStream has started consuming. The listener is freed
     // when the page is invalidated (on navigation or close).
-    GetUserMediaCallbackMediaStreamListener* listener =
-      new GetUserMediaCallbackMediaStreamListener(mediaThread, stream.forget(),
-                                                  port.forget(),
-                                                  mAudioSource,
-                                                  mVideoSource);
-    listener->Stream()->AddListener(listener);
-
-    // No need for locking because we always do this in the main thread.
-    listeners->AppendElement(listener);
+    mListener->Activate(stream.forget(), port.forget(),
+                        mAudioSource, mVideoSource);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while
+    nsIThread *mediaThread = MediaManager::GetThread();
     nsRefPtr<MediaOperationRunnable> runnable(
-      new MediaOperationRunnable(MEDIA_START, listener,
-                                 mAudioSource, mVideoSource));
+      new MediaOperationRunnable(MEDIA_START, mListener,
+                                 mAudioSource, mVideoSource, false));
     mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
     // We're in the main thread, so no worries here either.
     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
     nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
 
     if (!(mManager->IsWindowStillActive(mWindowID))) {
       return NS_OK;
@@ -368,16 +395,17 @@ public:
   }
 
 private:
   already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaEngineSource> mAudioSource;
   nsRefPtr<MediaEngineSource> mVideoSource;
   uint64_t mWindowID;
+  nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia or ProcessGetUserMediaSnapshot is called, and the results
  * are sent back to the DOM.
@@ -390,63 +418,68 @@ class GetUserMediaRunnable : public nsRu
 public:
   /**
    * The caller can choose to provide a MediaDevice as the last argument,
    * if one is not provided, a default device is automatically chosen.
    */
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    uint64_t aWindowID, MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
+    uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
+    MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
+    , mListener(aListener)
     , mDeviceChosen(true)
     , mBackendChosen(false)
     , mManager(MediaManager::GetInstance())
     {
       if (mAudio) {
         mAudioDevice = aAudioDevice;
       }
       if (mVideo) {
         mVideoDevice = aVideoDevice;
       }
     }
 
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    uint64_t aWindowID)
+    uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
+    , mListener(aListener)
     , mDeviceChosen(false)
     , mBackendChosen(false)
     , mManager(MediaManager::GetInstance()) {}
 
   /**
    * The caller can also choose to provide their own backend instead of
    * using the one provided by MediaManager::GetBackend.
    */
   GetUserMediaRunnable(bool aAudio, bool aVideo,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    uint64_t aWindowID, MediaEngine* aBackend)
+    uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
+    MediaEngine* aBackend)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(false)
     , mSuccess(aSuccess)
     , mError(aError)
     , mWindowID(aWindowID)
+    , mListener(aListener)
     , mDeviceChosen(false)
     , mBackendChosen(true)
     , mBackend(aBackend)
     , mManager(MediaManager::GetInstance()) {}
 
   ~GetUserMediaRunnable() {
     if (mBackendChosen) {
       delete mBackend;
@@ -488,26 +521,36 @@ public:
     ProcessGetUserMedia((mAudio && mAudioDevice) ? mAudioDevice->GetSource() : nullptr,
                         (mVideo && mVideoDevice) ? mVideoDevice->GetSource() : nullptr);
     return NS_OK;
   }
 
   nsresult
   Denied()
   {
+      // We add a disabled listener to the StreamListeners array until accepted
+      // If this was the only active MediaStream, remove the window from the list.
     if (NS_IsMainThread()) {
       // This is safe since we're on main-thread, and the window can only
       // be invalidated from the main-thread (see OnNavigation)
       nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
       error->OnError(NS_LITERAL_STRING("PERMISSION_DENIED"));
+
+      // Should happen *after* error runs for consistency, but may not matter
+      nsRefPtr<MediaManager> manager(MediaManager::GetInstance());
+      manager->RemoveFromWindowList(mWindowID, mListener);
     } else {
       // This will re-check the window being alive on main-thread
+      // Note: we must remove the listener on MainThread as well
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mSuccess, mError, NS_LITERAL_STRING("PERMISSION_DENIED"), mWindowID
       ));
+
+      // MUST happen after ErrorCallbackRunnable Run()s, as it checks the active window list
+      NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
     }
 
     return NS_OK;
   }
 
   nsresult
   SetAudioDevice(MediaDevice* aAudioDevice)
   {
@@ -622,17 +665,17 @@ public:
         NS_DispatchToMainThread(new ErrorCallbackRunnable(
           mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
                                                           ));
         return;
       }
     }
 
     NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
-      mSuccess, mError, mWindowID, aAudioSource, aVideoSource
+      mSuccess, mError, mWindowID, mListener, aAudioSource, aVideoSource
     ));
     return;
   }
 
   /**
    * Allocates a video device, takes a snapshot and returns a DOMFile via
    * a SuccessRunnable or an error via the ErrorRunnable. Off the main thread.
    */
@@ -663,16 +706,17 @@ public:
 private:
   bool mAudio;
   bool mVideo;
   bool mPicture;
 
   already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   uint64_t mWindowID;
+  nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaDevice> mAudioDevice;
   nsRefPtr<MediaDevice> mVideoDevice;
 
   bool mDeviceChosen;
   bool mBackendChosen;
 
   MediaEngine* mBackend;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
@@ -861,16 +905,25 @@ MediaManager::GetUserMedia(bool aPrivile
   nsRefPtr<GetUserMediaRunnable> gUMRunnable;
   // This is safe since we're on main-thread, and the windowlist can only
   // be invalidated from the main-thread (see OnNavigation)
   StreamListeners* listeners = GetActiveWindows()->Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     GetActiveWindows()->Put(windowID, listeners);
   }
+  // Ensure there's a thread for gum to proxy to off main thread
+  nsIThread *mediaThread = MediaManager::GetThread();
+
+  // Create a disabled listener to act as a placeholder
+  GetUserMediaCallbackMediaStreamListener* listener =
+    new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID);
+
+  // No need for locking because we always do this in the main thread.
+  listeners->AppendElement(listener);
 
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     aPrivileged = true;
   }
 
   /**
    * Pass runnables along to GetUserMediaRunnable so it can add the
@@ -879,30 +932,30 @@ MediaManager::GetUserMedia(bool aPrivile
    * selected by the user via the UI, or was provided by privileged code
    * via the device: attribute via nsIMediaStreamOptions.
    *
    * If a fake stream was requested, we force the use of the default backend.
    */
   if (fake) {
     // Fake stream from default backend.
     gUMRunnable = new GetUserMediaRunnable(
-      audio, video, onSuccess.forget(), onError.forget(), windowID,
+      audio, video, onSuccess.forget(), onError.forget(), windowID, listener,
       new MediaEngineDefault()
                                            );
   } else if (audiodevice || videodevice) {
     // Stream from provided device.
     gUMRunnable = new GetUserMediaRunnable(
-      audio, video, picture, onSuccess.forget(), onError.forget(), windowID,
+      audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener,
       static_cast<MediaDevice*>(audiodevice.get()),
       static_cast<MediaDevice*>(videodevice.get())
                                            );
   } else {
     // Stream from default device from WebRTC backend.
     gUMRunnable = new GetUserMediaRunnable(
-      audio, video, picture, onSuccess.forget(), onError.forget(), windowID
+      audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener
                                            );
   }
 
 #ifdef ANDROID
   if (picture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
     NS_DispatchToMainThread(gUMRunnable);
   }
@@ -1011,22 +1064,43 @@ MediaManager::OnNavigation(uint64_t aWin
   if (!listeners) {
     return;
   }
 
   uint32_t length = listeners->Length();
   for (uint32_t i = 0; i < length; i++) {
     nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
       listeners->ElementAt(i);
-    listener->Invalidate();
+    listener->Invalidate(true);
     listener->Remove();
   }
   listeners->Clear();
 
-  GetActiveWindows()->Remove(aWindowID);
+  RemoveWindowID(aWindowID);
+  // listeners has been deleted
+}
+
+void
+MediaManager::RemoveFromWindowList(uint64_t aWindowID,
+  GetUserMediaCallbackMediaStreamListener *aListener)
+{
+  NS_ASSERTION(NS_IsMainThread(), "RemoveFromWindowList called off main thread");
+
+  // This is defined as safe on an inactive GUMCMSListener
+  aListener->Remove(); // really queues the remove
+
+  StreamListeners* listeners = GetWindowListeners(aWindowID);
+  if (!listeners) {
+    return;
+  }
+  listeners->RemoveElement(aListener);
+  if (listeners->Length() == 0) {
+    RemoveWindowID(aWindowID);
+    // listeners has been deleted here
+  }
 }
 
 nsresult
 MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   const PRUnichar* aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread");
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -1154,21 +1228,29 @@ MediaManager::GetActiveMediaCaptureWindo
 
   mActiveWindows.EnumerateRead(WindowsHashToArrayFunc, array);
 
   *aArray = array;
   return NS_OK;
 }
 
 void
-GetUserMediaCallbackMediaStreamListener::Invalidate()
+GetUserMediaCallbackMediaStreamListener::Invalidate(bool aNeedsFinish)
 {
   nsRefPtr<MediaOperationRunnable> runnable;
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   runnable = new MediaOperationRunnable(MEDIA_STOP,
-                                        this, mAudioSource, mVideoSource);
+                                        this, mAudioSource, mVideoSource,
+                                        aNeedsFinish);
   mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
+void
+GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
+{
+  Invalidate(false);
+  NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
+}
+
 } // namespace mozilla
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -64,53 +64,63 @@ class GetUserMediaNotificationEvent: pub
 /**
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
 {
 public:
+  // Create in an inactive state
   GetUserMediaCallbackMediaStreamListener(nsIThread *aThread,
-    already_AddRefed<SourceMediaStream> aStream,
-    already_AddRefed<MediaInputPort> aPort,
-    MediaEngineSource* aAudioSource,
-    MediaEngineSource* aVideoSource)
+    uint64_t aWindowID)
     : mMediaThread(aThread)
-    , mAudioSource(aAudioSource)
-    , mVideoSource(aVideoSource)
-    , mStream(aStream)
-    , mPort(aPort)
-    , mLastEndTimeAudio(0)
-    , mLastEndTimeVideo(0) {}
+    , mWindowID(aWindowID) {}
 
   ~GetUserMediaCallbackMediaStreamListener()
   {
     // It's OK to release mStream and mPort on any thread; they have thread-safe
     // refcounts.
   }
 
+  void Activate(already_AddRefed<SourceMediaStream> aStream,
+    already_AddRefed<MediaInputPort> aPort,
+    MediaEngineSource* aAudioSource,
+    MediaEngineSource* aVideoSource)
+  {
+    mStream = aStream; // also serves as IsActive();
+    mPort = aPort;
+    mAudioSource = aAudioSource;
+    mVideoSource = aVideoSource;
+    mLastEndTimeAudio = 0;
+    mLastEndTimeVideo = 0;
+
+    mStream->AddListener(this);
+  }
+
   MediaStream *Stream()
   {
     return mStream;
   }
   SourceMediaStream *GetSourceStream()
   {
+    MOZ_ASSERT(mStream);
     return mStream->AsSourceStream();
   }
 
-  void
-  Invalidate(); // implement in .cpp to avoid circular dependency with MediaOperationRunnable
+  // implement in .cpp to avoid circular dependency with MediaOperationRunnable
+  void Invalidate(bool aNeedsFinish);
 
   void
   Remove()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     // Caller holds strong reference to us, so no death grip required
-    mStream->RemoveListener(this);
+    if (mStream) // allow even if inactive for easier cleanup
+      mStream->RemoveListener(this);
   }
 
   // Proxy NotifyPull() to sources
   void
   NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime)
   {
     // Currently audio sources ignore NotifyPull, but they could
     // watch it especially for fake audio.
@@ -118,24 +128,21 @@ public:
       mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
     }
     if (mVideoSource) {
       mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
     }
   }
 
   void
-  NotifyFinished(MediaStreamGraph* aGraph)
-  {
-    Invalidate();
-    // XXX right now this calls Finish, which isn't ideal but doesn't hurt
-  }
+  NotifyFinished(MediaStreamGraph* aGraph);
 
 private:
   nsCOMPtr<nsIThread> mMediaThread;
+  uint64_t mWindowID;
   nsRefPtr<MediaEngineSource> mAudioSource;
   nsRefPtr<MediaEngineSource> mVideoSource;
   nsRefPtr<SourceMediaStream> mStream;
   nsRefPtr<MediaInputPort> mPort;
   TrackTicks mLastEndTimeAudio;
   TrackTicks mLastEndTimeVideo;
 };
 
@@ -149,21 +156,23 @@ typedef enum {
 // ProxyReleases mStream since it's cycle collected.
 class MediaOperationRunnable : public nsRunnable
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     MediaEngineSource* aAudioSource,
-    MediaEngineSource* aVideoSource)
+    MediaEngineSource* aVideoSource,
+    bool aNeedsFinish)
     : mType(aType)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
+    , mFinish(aNeedsFinish)
     {}
 
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
 
   NS_IMETHOD
@@ -213,17 +222,19 @@ public:
             mAudioSource->Stop(source, kAudioTrack);
             mAudioSource->Deallocate();
           }
           if (mVideoSource) {
             mVideoSource->Stop(source, kVideoTrack);
             mVideoSource->Deallocate();
           }
           // Do this after stopping all tracks with EndTrack()
-          source->Finish();
+          if (mFinish) {
+            source->Finish();
+          }
           // the TrackUnion destination of the port will autofinish
 
           nsRefPtr<GetUserMediaNotificationEvent> event =
             new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STOPPING);
 
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
@@ -235,16 +246,17 @@ public:
     return NS_OK;
   }
 
 private:
   MediaOperation mType;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
+  bool mFinish;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
@@ -304,19 +316,25 @@ public:
   NS_DECL_NSIMEDIAMANAGERSERVICE
 
   MediaEngine* GetBackend();
   StreamListeners *GetWindowListeners(uint64_t aWindowId) {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
 
     return mActiveWindows.Get(aWindowId);
   }
+  void RemoveWindowID(uint64_t aWindowId) {
+    mActiveWindows.Remove(aWindowId);
+  }
   bool IsWindowStillActive(uint64_t aWindowId) {
     return !!GetWindowListeners(aWindowId);
   }
+  // Note: also calls aListener->Remove(), even if inactive
+  void RemoveFromWindowList(uint64_t aWindowID,
+    GetUserMediaCallbackMediaStreamListener *aListener);
 
   nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
     nsIMediaStreamOptions* aParams,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1606,16 +1606,32 @@ RadioInterfaceLayer.prototype = {
                          oldIcc.isDisplaySpnRequired != message.isDisplaySpnRequired ||
                          oldIcc.msisdn != message.msisdn;
     if (!iccInfoChanged) {
       return;
     }
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when the MCC or MNC codes have changed.
     this._sendTargetMessage("mobileconnection", "RIL:IccInfoChanged", message);
+
+    // If spn becomes available, we should check roaming again.
+    if (!oldIcc.spn && message.spn) {
+      let voice = this.rilContext.voice;
+      let data = this.rilContext.data;
+      let voiceRoaming = voice.roaming;
+      let dataRoaming = data.roaming;
+      this.checkRoamingBetweenOperators(voice);
+      this.checkRoamingBetweenOperators(data);
+      if (voiceRoaming != voice.roaming) {
+        this._sendTargetMessage("mobileconnection", "RIL:VoiceInfoChanged", voice);
+      }
+      if (dataRoaming != data.roaming) {
+        this._sendTargetMessage("mobileconnection", "RIL:DataInfoChanged", data);
+      }
+    }
   },
 
   handleICCCardLockResult: function handleICCCardLockResult(message) {
     this._sendRequestResults("RIL:CardLockResult", message);
   },
 
   handleUSSDReceived: function handleUSSDReceived(ussd) {
     debug("handleUSSDReceived " + JSON.stringify(ussd));
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -460,19 +460,25 @@ this.EF_TYPE_CYCLIC = 3;
 
 // Status code of EFsms
 // see 3GPP TS 51.011 clause 10.5.3
 this.EFSMS_STATUS_FREE       = 0x00;
 this.EFSMS_STATUS_READ       = 0x01;
 this.EFSMS_STATUS_TO_BE_READ = 0x03;
 this.EFSMS_STATUS_TO_BE_SENT = 0x07;
 
-// For retrieving MSISDN, TS 151.011 clause 10.5.5
-this.MSISDN_FOOTER_SIZE_BYTES = 14;
-this.MSISDN_MAX_NUMBER_SIZE_BYTES = 10;
+// Total size of ADN footer(the size of Alpha identifier excluded).
+// See TS 151.011 clause 10.5.1 EF_ADN.
+this.ADN_FOOTER_SIZE_BYTES = 14;
+// Maximum size of BCD numbers in ADN.
+// See TS 151.011 clause 10.5.1 EF_ADN, 'Length of BCD number/SSC contents'.
+this.ADN_MAX_BCD_NUMBER_BYTES = 11;
+// Maximum digits of the Dialling Number in ADN.
+// See TS 151.011 clause 10.5.1 EF_ADN, 'Dialling Number'.
+this.ADN_MAX_NUMBER_DIGITS = 20;
 
 // READ_RECORD mode,  TS 102.221
 this.READ_RECORD_ABSOLUTE_MODE = 4;
 
 // GET_RESPONSE mandatory response size for EF, see TS 51.011 clause 9, 
 // 'Response data in case of an EF.'
 this.GET_RESPONSE_EF_SIZE_BYTES = 15;
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -5526,16 +5526,59 @@ let GsmPDUHelper = {
       }
     }
 
     Buf.seekIncoming((numOctets - i) * PDU_HEX_OCTET_SIZE);
     return ret;
   },
 
   /**
+   * Write GSM 8-bit unpacked octets.
+   *
+   * @param numOctets   Number of total octets to be writen, including trailing
+   *                    0xff.
+   * @param str         String to be written. Could be null.
+   */
+  writeStringTo8BitUnpacked: function writeStringTo8BitUnpacked(numOctets, str) {
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+
+    // If the character is GSM extended alphabet, two octets will be written.
+    // So we need to keep track of number of octets to be written.
+    let i, j;
+    let len = str ? str.length : 0;
+    for (i = 0, j = 0; i < len && j < numOctets; i++) {
+      let c = str.charAt(i);
+      let octet = langTable.indexOf(c);
+
+      if (octet == -1) {
+        // Make sure we still have enough space to write two octets.
+        if (j + 2 > numOctets) {
+          break;
+        }
+
+        octet = langShiftTable.indexOf(c);
+        if (octet == -1) {
+          // Fallback to ASCII space.
+          octet = langTable.indexOf(' ');
+        }
+        this.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
+        j++;
+      }
+      this.writeHexOctet(octet);
+      j++;
+    }
+
+    // trailing 0xff
+    while (j++ < numOctets) {
+      this.writeHexOctet(0xff);
+    }
+  },
+
+  /**
    * Read user data and decode as a UCS2 string.
    *
    * @param numOctets
    *        Number of octets to be read as UCS2 string.
    *
    * @return a string.
    */
   readUCS2String: function readUCS2String(numOctets) {
@@ -5908,49 +5951,95 @@ let GsmPDUHelper = {
     if ((toa & 0xF0) == (PDU_TOA_INTERNATIONAL)) {
       addr = '+' + addr;
     }
 
     return addr;
   },
 
   /**
-   *  Read Alpha Id and Dialling number from TS 131.102
-   *
-   *  @param options
-   *         The 'options' object passed from RIL.iccIO
-   */
-  readAlphaIdDiallingNumber: function readAlphaIdDiallingNumber(options) {
-    let contact = null;
-    let ffLen; // The length of trailing 0xff to be read.
+   * Read Alpha Id and Dialling number from TS TS 151.011 clause 10.5.1
+   *
+   * @param recordSize  The size of linear fixed record.
+   */
+  readAlphaIdDiallingNumber: function readAlphaIdDiallingNumber(recordSize) {
     let length = Buf.readUint32();
 
-    let alphaLen = options.recordSize - MSISDN_FOOTER_SIZE_BYTES;
+    let alphaLen = recordSize - ADN_FOOTER_SIZE_BYTES;
     let alphaId = this.readAlphaIdentifier(alphaLen);
 
+    let number;
     let numLen = this.readHexOctet();
     if (numLen != 0xff) {
-      // +1 for TON/NPI
-      if (numLen > MSISDN_MAX_NUMBER_SIZE_BYTES + 1) {
+      if (numLen > ADN_MAX_BCD_NUMBER_BYTES) {
         throw new Error("invalid length of BCD number/SSC contents - " + numLen);
       }
 
+      number = this.readDiallingNumber(numLen);
+      Buf.seekIncoming((ADN_MAX_BCD_NUMBER_BYTES - numLen) * PDU_HEX_OCTET_SIZE);
+    } else {
+      Buf.seekIncoming(ADN_MAX_BCD_NUMBER_BYTES * PDU_HEX_OCTET_SIZE);
+    }
+
+    // Skip 2 unused octets, CCP and EXT1.
+    Buf.seekIncoming(2 * PDU_HEX_OCTET_SIZE);
+    Buf.readStringDelimiter(length);
+
+    let contact = null;
+    if (alphaId || number) {
       contact = {alphaId: alphaId,
-                 number: this.readDiallingNumber(numLen)};
-
-      ffLen = length / 2 - alphaLen - numLen - 1; // Minus 1 for the numLen field.
+                 number: number};
+    }
+    return contact;
+  },
+
+  /**
+   * Write Alpha Identifier and Dialling number from TS 151.011 clause 10.5.1
+   *
+   * @param recordSize  The size of linear fixed record.
+   * @param alphaId     Alpha Identifier to be written.
+   * @param number      Dialling Number to be written.
+   */
+  writeAlphaIdDiallingNumber: function writeAlphaIdDiallingNumber(recordSize,
+                                                                  alphaId,
+                                                                  number) {
+    // Write String length
+    let length = recordSize * 2;
+    Buf.writeUint32(length);
+
+    let alphaLen = recordSize - ADN_FOOTER_SIZE_BYTES;
+    this.writeAlphaIdentifier(alphaLen, alphaId);
+
+    if (number) {
+      let numStart = number[0] == "+" ? 1 : 0;
+      let numDigits = number.length - numStart;
+      if (numDigits > ADN_MAX_NUMBER_DIGITS) {
+        number = number.substring(0, ADN_MAX_NUMBER_DIGITS + numStart);
+        numDigits = number.length - numStart;
+      }
+
+      // +1 for TON/NPI
+      let numLen = Math.ceil(numDigits / 2) + 1;
+      this.writeHexOctet(numLen);
+      this.writeDiallingNumber(number);
+      // Write trailing 0xff of Dialling Number.
+      for (let i = 0; i < ADN_MAX_BCD_NUMBER_BYTES - numLen; i++) {
+        this.writeHexOctet(0xff);
+      }
     } else {
-      ffLen = MSISDN_FOOTER_SIZE_BYTES - 1; // Minus 1 for the numLen field.
-    }
-
-    // Consumes the remaining 0xff
-    Buf.seekIncoming(ffLen * PDU_HEX_OCTET_SIZE);
-    Buf.readStringDelimiter(length);
-
-    return contact;
+      // +1 for numLen
+      for (let i = 0; i < ADN_MAX_BCD_NUMBER_BYTES + 1; i++) {
+        this.writeHexOctet(0xff);
+      }
+    }
+
+    // Write unused octets 0xff, CCP and EXT1.
+    this.writeHexOctet(0xff);
+    this.writeHexOctet(0xff);
+    Buf.writeStringDelimiter(length);
   },
 
   /**
    * Read Alpha Identifier.
    *
    * @see TS 131.102
    *
    * @param numOctets
@@ -5958,31 +6047,68 @@ let GsmPDUHelper = {
    *
    * It uses either
    *  1. SMS default 7-bit alphabet with bit 8 set to 0.
    *  2. UCS2 string.
    *
    * Unused bytes should be set to 0xff.
    */
   readAlphaIdentifier: function readAlphaIdentifier(numOctets) {
+    if (numOctets === 0) {
+      return "";
+    }
+
     let temp;
-
     // Read the 1st octet to determine the encoding.
     if ((temp = GsmPDUHelper.readHexOctet()) == 0x80 ||
          temp == 0x81 ||
          temp == 0x82) {
       numOctets--;
       return this.readICCUCS2String(temp, numOctets);
     } else {
       Buf.seekIncoming(-1 * PDU_HEX_OCTET_SIZE);
       return this.read8BitUnpackedToString(numOctets);
     }
   },
 
   /**
+   * Write Alpha Identifier.
+   *
+   * @param numOctets
+   *        Total number of octets to be written. This includes the length of
+   *        alphaId and the length of trailing unused octets(0xff).
+   * @param alphaId
+   *        Alpha Identifier to be written.
+   *
+   * Unused octets will be written as 0xff.
+   */
+  writeAlphaIdentifier: function writeAlphaIdentifier(numOctets, alphaId) {
+    if (numOctets === 0) {
+      return;
+    }
+
+    // If alphaId is empty or it's of GSM 8 bit.
+    if (!alphaId || ICCUtilsHelper.isGsm8BitAlphabet(alphaId)) {
+      this.writeStringTo8BitUnpacked(numOctets, alphaId);
+    } else {
+      // Currently only support UCS2 coding scheme 0x80.
+      this.writeHexOctet(0x80);
+      numOctets--;
+      // Now the alphaId is UCS2 string, each character will take 2 octets.
+      if (alphaId.length * 2 > numOctets) {
+        alphaId = alphaId.substring(0, Math.floor(numOctets / 2));
+      }
+      this.writeUCS2String(alphaId);
+      for (let i = alphaId.length * 2; i < numOctets; i++) {
+        this.writeHexOctet(0xff);
+      }
+    }
+  },
+
+  /**
    * Read Dialling number.
    *
    * @see TS 131.102
    *
    * @param len
    *        The Length of BCD number.
    *
    * From TS 131.102, in EF_ADN, EF_FDN, the field 'Length of BCD number'
@@ -8460,17 +8586,17 @@ let ICCRecordHelper = {
                                    callback: callback.bind(this)});
   },
 
   /**
    * Read the MSISDN from the ICC.
    */
   getMSISDN: function getMSISDN() {
     function callback(options) {
-      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options);
+      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
       if (!contact || RIL.iccInfo.msisdn === contact.number) {
         return;
       }
       RIL.iccInfo.msisdn = contact.number;
       if (DEBUG) debug("MSISDN: " + RIL.iccInfo.msisdn);
       ICCUtilsHelper.handleICCInfoChange();
     }
 
@@ -8611,17 +8737,17 @@ let ICCRecordHelper = {
   /**
    *  Get ICC FDN.
    *
    *  @param requestId
    *         Request id from RadioInterfaceLayer.
    */
   getFDN: function getFDN(options) {
     function callback(options) {
-      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options);
+      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
       if (contact) {
         RIL.iccInfo.fdn.push(contact);
       }
 
       if (options.p1 < options.totalRecords) {
         ICCIOHelper.loadNextRecord(options);
       } else {
         if (DEBUG) {
@@ -8652,17 +8778,17 @@ let ICCRecordHelper = {
    *
    *  @param fileId
    *         EF id of the ADN.
    *  @param requestId
    *         Request id from RadioInterfaceLayer.
    */
   getADN: function getADN(options) {
     function callback(options) {
-      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options);
+      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
       if (contact) {
         RIL.iccInfo.adn.push(contact);
       }
 
       if (options.p1 < options.totalRecords) {
         ICCIOHelper.loadNextRecord(options);
       } else {
         if (DEBUG) {
@@ -8696,17 +8822,17 @@ let ICCRecordHelper = {
 
   /**
    * Get ICC MBDN. (Mailbox Dialling Number)
    *
    * @see TS 131.102, clause 4.2.60
    */
   getMBDN: function getMBDN() {
     function callback(options) {
-      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options);
+      let contact = GsmPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
       if (!contact || RIL.iccInfo.mbdn === contact.number){
         return;
       }
       RIL.iccInfo.mbdn = contact.number;
       if (DEBUG) {
         debug("MBDN, alphaId="+contact.alphaId+" number="+contact.number);
       }
       contact.rilMessageType = "iccmbdn";
@@ -9282,16 +9408,44 @@ let ICCUtilsHelper = {
       index = Math.floor(usimService / 8);
       bitmask = 1 << ((usimService % 8) << 0);
     }
 
     return (serviceTable &&
            (index < serviceTable.length) &&
            (serviceTable[index] & bitmask)) != 0;
   },
+
+  /**
+   * Check if the string is of GSM default 7-bit coded alphabets with bit 8
+   * set to 0.
+   *
+   * @param str  String to be checked.
+   */
+  isGsm8BitAlphabet: function isGsm8BitAlphabet(str) {
+    if (!str) {
+      return false;
+    }
+
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+
+    for (let i = 0; i < str.length; i++) {
+      let c = str.charAt(i);
+      let octet = langTable.indexOf(c);
+      if (octet == -1) {
+        octet = langShiftTable.indexOf(c);
+        if (octet == -1) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  },
 };
 
 /**
  * Global stuff.
  */
 
 if (!this.debug) {
   // Debugging stub that goes nowhere.
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -70,32 +70,261 @@ add_test(function test_read_icc_ucs2_str
 });
 
 /**
  * Verify GsmPDUHelper#read8BitUnpackedToString
  */
 add_test(function test_read_8bit_unpacked_to_string() {
   let worker = newUint8Worker();
   let helper = worker.GsmPDUHelper;
-  let buf = worker.Buf;
   const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+  const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
 
-  // Only write characters before PDU_NL_EXTENDED_ESCAPE to simplify test.
+  // Test 1: Read GSM alphabets.
+  // Write alphabets before ESCAPE.
   for (let i = 0; i < PDU_NL_EXTENDED_ESCAPE; i++) {
     helper.writeHexOctet(i);
   }
 
+  // Write two ESCAPEs to make it become ' '.
+  helper.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
+  helper.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
+
+  for (let i = PDU_NL_EXTENDED_ESCAPE + 1; i < langTable.length; i++) {
+    helper.writeHexOctet(i);
+  }
+
   // Also write two unused fields.
   let ffLen = 2;
   for (let i = 0; i < ffLen; i++) {
     helper.writeHexOctet(0xff);
   }
 
-  do_check_eq(helper.read8BitUnpackedToString(PDU_NL_EXTENDED_ESCAPE + ffLen),
+  do_check_eq(helper.read8BitUnpackedToString(PDU_NL_EXTENDED_ESCAPE),
               langTable.substring(0, PDU_NL_EXTENDED_ESCAPE));
+  do_check_eq(helper.read8BitUnpackedToString(2), " ");
+  do_check_eq(helper.read8BitUnpackedToString(langTable.length -
+                                              PDU_NL_EXTENDED_ESCAPE - 1 + ffLen),
+              langTable.substring(PDU_NL_EXTENDED_ESCAPE + 1));
+
+  // Test 2: Read GSM extended alphabets.
+  for (let i = 0; i < langShiftTable.length; i++) {
+    helper.writeHexOctet(PDU_NL_EXTENDED_ESCAPE);
+    helper.writeHexOctet(i);
+  }
+
+  // Read string before RESERVED_CONTROL.
+  do_check_eq(helper.read8BitUnpackedToString(PDU_NL_RESERVED_CONTROL  * 2),
+              langShiftTable.substring(0, PDU_NL_RESERVED_CONTROL));
+  // ESCAPE + RESERVED_CONTROL will become ' '.
+  do_check_eq(helper.read8BitUnpackedToString(2), " ");
+  // Read string between RESERVED_CONTROL and EXTENDED_ESCAPE.
+  do_check_eq(helper.read8BitUnpackedToString(
+                (PDU_NL_EXTENDED_ESCAPE - PDU_NL_RESERVED_CONTROL - 1)  * 2),
+              langShiftTable.substring(PDU_NL_RESERVED_CONTROL + 1,
+                                       PDU_NL_EXTENDED_ESCAPE));
+  // ESCAPE + ESCAPE will become ' '.
+  do_check_eq(helper.read8BitUnpackedToString(2), " ");
+  // Read remaining string.
+  do_check_eq(helper.read8BitUnpackedToString(
+                (langShiftTable.length - PDU_NL_EXTENDED_ESCAPE - 1)  * 2),
+              langShiftTable.substring(PDU_NL_EXTENDED_ESCAPE + 1));
+
+  run_next_test();
+});
+
+/**
+ * Verify GsmPDUHelper#writeStringTo8BitUnpacked.
+ *
+ * Test writing GSM 8 bit alphabets.
+ */
+add_test(function test_write_string_to_8bit_unpacked() {
+  let worker = newUint8Worker();
+  let helper = worker.GsmPDUHelper;
+  const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+  const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+  // Length of trailing 0xff.
+  let ffLen = 2;
+  let str;
+
+  // Test 1, write GSM alphabets.
+  helper.writeStringTo8BitUnpacked(langTable.length + ffLen, langTable);
+
+  for (let i = 0; i < langTable.length; i++) {
+    do_check_eq(helper.readHexOctet(), i);
+  }
+
+  for (let i = 0; i < ffLen; i++) {
+    do_check_eq(helper.readHexOctet(), 0xff);
+  }
+
+  // Test 2, write GSM extended alphabets.
+  str = "\u000c\u20ac";
+  helper.writeStringTo8BitUnpacked(4, str);
+
+  do_check_eq(helper.read8BitUnpackedToString(4), str);
+
+  // Test 3, write GSM and GSM extended alphabets.
+  // \u000c, \u20ac are from gsm extended alphabets.
+  // \u00a3 is from gsm alphabet.
+  str = "\u000c\u20ac\u00a3";
+
+  // 2 octets * 2 = 4 octets for 2 gsm extended alphabets,
+  // 1 octet for 1 gsm alphabet,
+  // 2 octes for trailing 0xff.
+  // "Totally 7 octets are to be written."
+  helper.writeStringTo8BitUnpacked(7, str);
+
+  do_check_eq(helper.read8BitUnpackedToString(7), str);
+
+  run_next_test();
+});
+
+/**
+ * Verify GsmPDUHelper#writeStringTo8BitUnpacked with maximum octets written.
+ */
+add_test(function test_write_string_to_8bit_unpacked_with_max_octets_written() {
+  let worker = newUint8Worker();
+  let helper = worker.GsmPDUHelper;
+  const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+  const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+
+  // The maximum of the number of octets that can be written is 3.
+  // Only 3 characters shall be written even the length of the string is 4.
+  helper.writeStringTo8BitUnpacked(3, langTable.substring(0, 4));
+  helper.writeHexOctet(0xff); // dummy octet.
+  for (let i = 0; i < 3; i++) {
+    do_check_eq(helper.readHexOctet(), i);
+  }
+  do_check_false(helper.readHexOctet() == 4);
+
+  // \u000c is GSM extended alphabet, 2 octets.
+  // \u00a3 is GSM alphabet, 1 octet.
+  let str = "\u000c\u00a3";
+  helper.writeStringTo8BitUnpacked(3, str);
+  do_check_eq(helper.read8BitUnpackedToString(3), str);
+
+  str = "\u00a3\u000c";
+  helper.writeStringTo8BitUnpacked(3, str);
+  do_check_eq(helper.read8BitUnpackedToString(3), str);
+
+  // 2 GSM extended alphabets cost 4 octets, but maximum is 3, so only the 1st
+  // alphabet can be written.
+  str = "\u000c\u000c";
+  helper.writeStringTo8BitUnpacked(3, str);
+  helper.writeHexOctet(0xff); // dummy octet.
+  do_check_eq(helper.read8BitUnpackedToString(4), str.substring(0, 1));
+
+  run_next_test();
+});
+
+/**
+ * Verify GsmPDUHelper.writeAlphaIdentifier
+ */
+add_test(function test_write_alpha_identifier() {
+  let worker = newUint8Worker();
+  let helper = worker.GsmPDUHelper;
+  // Length of trailing 0xff.
+  let ffLen = 2;
+
+  // Removal
+  helper.writeAlphaIdentifier(10, null);
+  do_check_eq(helper.readAlphaIdentifier(10), "");
+
+  // GSM 8 bit
+  let str = "Mozilla";
+  helper.writeAlphaIdentifier(str.length + ffLen, str);
+  do_check_eq(helper.readAlphaIdentifier(str.length + ffLen), str);
+
+  // UCS2
+  str = "Mozilla\u694a";
+  helper.writeAlphaIdentifier(str.length * 2 + ffLen, str);
+  // * 2 for each character will be encoded to UCS2 alphabets.
+  do_check_eq(helper.readAlphaIdentifier(str.length * 2 + ffLen), str);
+
+  // Test with maximum octets written.
+  // 1 coding scheme (0x80) and 1 UCS2 character, total 3 octets.
+  str = "\u694a";
+  helper.writeAlphaIdentifier(3, str);
+  do_check_eq(helper.readAlphaIdentifier(3), str);
+
+  // 1 coding scheme (0x80) and 2 UCS2 characters, total 5 octets.
+  // numOctets is limited to 4, so only 1 UCS2 character can be written.
+  str = "\u694a\u694a";
+  helper.writeAlphaIdentifier(4, str);
+  helper.writeHexOctet(0xff); // dummy octet.
+  do_check_eq(helper.readAlphaIdentifier(5), str.substring(0, 1));
+
+  // Write 0 octet.
+  helper.writeAlphaIdentifier(0, "1");
+  helper.writeHexOctet(0xff); // dummy octet.
+  do_check_eq(helper.readAlphaIdentifier(1), "");
+
+  run_next_test();
+});
+
+/**
+ * Verify GsmPDUHelper.writeAlphaIdDiallingNumber
+ */
+add_test(function test_write_alpha_id_dialling_number() {
+  let worker = newUint8Worker();
+  let helper = worker.GsmPDUHelper;
+  const recordSize = 32;
+
+  // Write a normal contact.
+  let contactW = {
+    alphaId: "Mozilla",
+    number: "1234567890"
+  };
+  helper.writeAlphaIdDiallingNumber(recordSize, contactW.alphaId,
+                                    contactW.number);
+
+  let contactR = helper.readAlphaIdDiallingNumber(recordSize);
+  do_check_eq(contactW.alphaId, contactR.alphaId);
+  do_check_eq(contactW.number, contactR.number);
+
+  // Write a contact with alphaId encoded in UCS2 and number has '+'.
+  let contactUCS2 = {
+    alphaId: "火狐",
+    number: "+1234567890"
+  };
+  helper.writeAlphaIdDiallingNumber(recordSize, contactUCS2.alphaId,
+                                    contactUCS2.number);
+  contactR = helper.readAlphaIdDiallingNumber(recordSize);
+  do_check_eq(contactUCS2.alphaId, contactR.alphaId);
+  do_check_eq(contactUCS2.number, contactR.number);
+
+  // Write a null contact (Removal).
+  helper.writeAlphaIdDiallingNumber(recordSize);
+  contactR = helper.readAlphaIdDiallingNumber(recordSize);
+  do_check_eq(contactR, null);
+
+  // Write a longer alphaId/dialling number
+  // Dialling Number : Maximum 20 digits(10 octets).
+  // Alpha Identifier: 32(recordSize) - 14 (10 octets for Dialling Number, 1
+  //                   octet for TON/NPI, 1 for number length octet, and 2 for
+  //                   Ext) = Maximum 18 octets.
+  let longContact = {
+    alphaId: "AAAAAAAAABBBBBBBBBCCCCCCCCC",
+    number: "123456789012345678901234567890",
+  };
+  helper.writeAlphaIdDiallingNumber(recordSize, longContact.alphaId,
+                                    longContact.number);
+  contactR = helper.readAlphaIdDiallingNumber(recordSize);
+  do_check_eq(contactR.alphaId, "AAAAAAAAABBBBBBBBB");
+  do_check_eq(contactR.number, "12345678901234567890");
+
+  // Add '+' to number and test again.
+  longContact.number = "+123456789012345678901234567890";
+  helper.writeAlphaIdDiallingNumber(recordSize, longContact.alphaId,
+                                    longContact.number);
+  contactR = helper.readAlphaIdDiallingNumber(recordSize);
+  do_check_eq(contactR.alphaId, "AAAAAAAAABBBBBBBBB");
+  do_check_eq(contactR.number, "+12345678901234567890");
+
   run_next_test();
 });
 
 /**
  * Verify GsmPDUHelper.writeDiallingNumber
  */
 add_test(function test_write_dialling_number() {
   let worker = newUint8Worker();
@@ -208,16 +437,32 @@ add_test(function test_is_icc_service_av
   test_table([0x08], "ADN", true, false);
   test_table([0x08], "FDN", false, false);
   test_table([0x08], "SDN", false, true);
 
   run_next_test();
 });
 
 /**
+ * Verify ICCUtilsHelper.isGsm8BitAlphabet
+ */
+add_test(function test_is_gsm_8bit_alphabet() {
+  let worker = newUint8Worker();
+  let ICCUtilsHelper = worker.ICCUtilsHelper;
+  const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+  const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT];
+
+  do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet(langTable), true);
+  do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet(langShiftTable), true);
+  do_check_eq(ICCUtilsHelper.isGsm8BitAlphabet("\uaaaa"), false);
+
+  run_next_test();
+});
+
+/**
  * Verify RIL.sendStkTerminalProfile
  */
 add_test(function test_send_stk_terminal_profile() {
   let worker = newUint8Worker();
   let ril = worker.RIL;
   let buf = worker.Buf;
 
   ril.sendStkTerminalProfile(STK_SUPPORTED_TERMINAL_PROFILE);
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -14,16 +14,18 @@
 #include <string.h>
 
 #include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "js/Utility.h"
 #include "js/Vector.h"
 
+class nsISupports;      // This is needed for ObjectPrivateVisitor.
+
 namespace js {
 
 // In memory reporting, we have concept of "sundries", line items which are too
 // small to be worth reporting individually.  Under some circumstances, a memory
 // reporter gets tossed into the sundries bucket if it's smaller than
 // MemoryReportingSundriesThreshold() bytes.
 //
 // We need to define this value here, rather than in the code which actually
@@ -386,18 +388,27 @@ struct RuntimeStats
 };
 
 #ifdef JS_THREADSAFE
 
 class ObjectPrivateVisitor
 {
 public:
     // Within CollectRuntimeStats, this method is called for each JS object
-    // that has a private slot containing an nsISupports pointer.
-    virtual size_t sizeOfIncludingThis(void *aSupports) = 0;
+    // that has an nsISupports pointer.
+    virtual size_t sizeOfIncludingThis(nsISupports *aSupports) = 0;
+
+    // A callback that gets a JSObject's nsISupports pointer, if it has one.
+    // Note: this function does *not* addref |iface|.
+    typedef JSBool(*GetISupportsFun)(JSObject *obj, nsISupports **iface);
+    GetISupportsFun getISupports;
+
+    ObjectPrivateVisitor(GetISupportsFun getISupports)
+      : getISupports(getISupports)
+    {}
 };
 
 extern JS_PUBLIC_API(bool)
 CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv);
 
 extern JS_PUBLIC_API(int64_t)
 GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf);
 
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -983,12 +983,42 @@ Cell::compartment() const
 #ifdef DEBUG
 bool
 Cell::isAligned() const
 {
     return Arena::isAligned(address(), arenaHeader()->getThingSize());
 }
 #endif
 
+inline bool
+InFreeList(ArenaHeader *aheader, void *thing)
+{
+    if (!aheader->hasFreeThings())
+        return false;
+
+    FreeSpan firstSpan(aheader->getFirstFreeSpan());
+    uintptr_t addr = reinterpret_cast<uintptr_t>(thing);
+
+    for (const FreeSpan *span = &firstSpan;;) {
+        /* If the thing comes before the current span, it's not free. */
+        if (addr < span->first)
+            return false;
+
+        /*
+         * If we find it inside the span, it's dead. We use here "<=" and not
+         * "<" even for the last span as we know that thing is inside the
+         * arena. Thus, for the last span thing < span->end.
+         */
+        if (addr <= span->last)
+            return true;
+
+        /*
+         * The last possible empty span is an the end of the arena. Here
+         * span->end < thing < thingsEnd and so we must have more spans.
+         */
+        span = span->nextSpan();
+    }
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_heap_h___ */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -139,17 +139,24 @@ CheckMarkedThing(JSTracer *trc, T *thing
     JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
                  thing->compartment()->isCollecting() ||
                  thing->compartment() == rt->atomsCompartment);
 
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && ((GCMarker *)trc)->getMarkColor() == GRAY,
                  thing->compartment()->isGCMarkingGray() ||
                  thing->compartment() == rt->atomsCompartment);
 
-    JS_ASSERT(!IsThingPoisoned(thing));
+    /*
+     * Try to assert that the thing is allocated.  This is complicated by the
+     * fact that allocated things may still contain the poison pattern if that
+     * part has not been overwritten, and that the free span list head in the
+     * ArenaHeader may not be synced with the real one in ArenaLists.
+     */
+    JS_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy(),
+                 !InFreeList(thing->arenaHeader(), thing));
 }
 
 static GCMarker *
 AsGCMarker(JSTracer *trc)
 {
     JS_ASSERT(IS_GC_MARKING_TRACER(trc));
     return static_cast<GCMarker *>(trc);
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -82,45 +82,16 @@ MarkExactStackRoots(JSTracer *trc)
         for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
             MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
         }
         MarkExactStackRooters(trc, rt->mainThread.thingGCRooters[i], ThingRootKind(i));
     }
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
-static inline bool
-InFreeList(ArenaHeader *aheader, uintptr_t addr)
-{
-    if (!aheader->hasFreeThings())
-        return false;
-
-    FreeSpan firstSpan(aheader->getFirstFreeSpan());
-
-    for (const FreeSpan *span = &firstSpan;;) {
-        /* If the thing comes fore the current span, it's not free. */
-        if (addr < span->first)
-            return false;
-
-        /*
-         * If we find it inside the span, it's dead. We use here "<=" and not
-         * "<" even for the last span as we know that thing is inside the
-         * arena. Thus for the last span thing < span->end.
-         */
-        if (addr <= span->last)
-            return true;
-
-        /*
-         * The last possible empty span is an the end of the arena. Here
-         * span->end < thing < thingsEnd and so we must have more spans.
-         */
-        span = span->nextSpan();
-    }
-}
-
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
     CGCT_NOTARENA,  /* not within arena range in a chunk */
     CGCT_OTHERCOMPARTMENT,  /* in another compartment */
     CGCT_NOTCHUNK,  /* not within a valid chunk */
     CGCT_FREEARENA, /* within arena containing only free things */
@@ -235,17 +206,17 @@ MarkIfGCThingWord(JSTracer *trc, uintptr
     if (status != CGCT_VALID)
         return status;
 
     /*
      * Check if the thing is free. We must use the list of free spans as at
      * this point we no longer have the mark bits from the previous GC run and
      * we must account for newly allocated things.
      */
-    if (InFreeList(aheader, uintptr_t(thing)))
+    if (InFreeList(aheader, thing))
         return CGCT_NOTLIVE;
 
     JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
 #ifdef DEBUG
     const char pattern[] = "machine_stack %p";
     char nameBuf[sizeof(pattern) - 2 + sizeof(thing) * 2];
     JS_snprintf(nameBuf, sizeof(nameBuf), pattern, thing);
     JS_SET_TRACING_NAME(trc, nameBuf);
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -438,17 +438,17 @@ def main(argv):
     op.add_option('--jitflags', dest='jitflags', default='',
                   help='Example: --jitflags=m,mn to run each test with "-m" and "-m -n" [default="%default"]. ' +
                        'Long flags, such as "--no-jm", should be set using --args.')
     op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true',
                   help='Use js-shell file indirection instead of piping stdio.')
     op.add_option('--write-failure-output', dest='write_failure_output', action='store_true',
                   help='With --write-failures=FILE, additionally write the output of failed tests to [FILE]')
     op.add_option('--ion', dest='ion', action='store_true',
-                  help='Run tests with --ion flag (ignores --jitflags)')
+                  help='Run tests once with --ion-eager and once with --no-jm (ignores --jitflags)')
     op.add_option('--tbpl', dest='tbpl', action='store_true',
                   help='Run tests with all IonMonkey option combinations (ignores --jitflags)')
     (OPTIONS, args) = op.parse_args(argv)
     if len(args) < 1:
         op.error('missing JS_SHELL argument')
     # We need to make sure we are using backslashes on Windows.
     JS, test_args = os.path.normpath(args[0]), args[1:]
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-820186.js
@@ -0,0 +1,295 @@
+function randomRecursion() {
+    var y = ""
+    if (rnd(2)) {
+        var x = 2;
+        "{" + x + "}";
+        randomRecursion();
+        randomRecursion();
+        return [""];
+    }
+    return [""];
+}
+
+function thisFunctionIsNeverCalled() {
+}
+
+function testOne() {
+    ox = newGlobal();
+    var code = randomRecursion()[rnd(3)];
+}
+
+initRnd();
+gczeal(10, 3);
+
+for (var count = 0; count < 20; count++) {
+    print(count);
+    testOne()
+}
+
+// ==========================================================================================
+
+// this program is a JavaScript version of Mersenne Twister, with concealment and encapsulation in class,
+// an almost straight conversion from the original program, mt19937ar.c,
+// translated by y. okada on July 17, 2006.
+// Changes by Jesse Ruderman: added "var" keyword in a few spots; added export_mta etc; pasted into fuzz.js.
+// in this program, procedure descriptions and comments of original source code were not removed.
+// lines commented with //c// were originally descriptions of c procedure. and a few following lines are appropriate JavaScript descriptions.
+// lines commented with /* and */ are original comments.
+// lines commented with // are additional comments in this JavaScript version.
+// before using this version, create at least one instance of MersenneTwister19937 class, and initialize the each state, given below in c comments, of all the instances.
+/*
+   A C-program for MT19937, with initialization improved 2002/1/26.
+   Coded by Takuji Nishimura and Makoto Matsumoto.
+
+   Before using, initialize the state by using init_genrand(seed)
+   or init_by_array(init_key, key_length).
+
+   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+     1. Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+     2. Redistributions in binary form must reproduce the above copyright
+        notice, this list of conditions and the following disclaimer in the
+        documentation and/or other materials provided with the distribution.
+
+     3. The names of its contributors may not be used to endorse or promote
+        products derived from this software without specific prior written
+        permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+   Any feedback is very welcome.
+   http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+   email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+function MersenneTwister19937()
+{
+	/* Period parameters */
+	//c//#define N 624
+	//c//#define M 397
+	//c//#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
+	//c//#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+	//c//#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+	var N = 624;
+	var M = 397;
+	var MATRIX_A = 0x9908b0df;   /* constant vector a */
+	var UPPER_MASK = 0x80000000; /* most significant w-r bits */
+	var LOWER_MASK = 0x7fffffff; /* least significant r bits */
+	//c//static unsigned long mt[N]; /* the array for the state vector  */
+	//c//static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+	var mt = new Array(N);   /* the array for the state vector  */
+	var mti = N+1;           /* mti==N+1 means mt[N] is not initialized */
+
+	function unsigned32 (n1) // returns a 32-bits unsiged integer from an operand to which applied a bit operator.
+	{
+		return n1 < 0 ? (n1 ^ UPPER_MASK) + UPPER_MASK : n1;
+	}
+
+	function subtraction32 (n1, n2) // emulates lowerflow of a c 32-bits unsiged integer variable, instead of the operator -. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+	{
+		return n1 < n2 ? unsigned32((0x100000000 - (n2 - n1)) & 0xffffffff) : n1 - n2;
+	}
+
+	function addition32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator +. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+	{
+		return unsigned32((n1 + n2) & 0xffffffff)
+	}
+
+	function multiplication32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator *. these both arguments must be non-negative integers expressible using unsigned 32 bits.
+	{
+		var sum = 0;
+		for (var i = 0; i < 32; ++i){
+			if ((n1 >>> i) & 0x1){
+				sum = addition32(sum, unsigned32(n2 << i));
+			}
+		}
+		return sum;
+	}
+
+	/* initializes mt[N] with a seed */
+	//c//void init_genrand(unsigned long s)
+	this.init_genrand = function (s)
+	{
+		//c//mt[0]= s & 0xffffffff;
+		mt[0]= unsigned32(s & 0xffffffff);
+		for (mti=1; mti<N; mti++) {
+			mt[mti] =
+			//c//(1812433253 * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+			addition32(multiplication32(1812433253, unsigned32(mt[mti-1] ^ (mt[mti-1] >>> 30))), mti);
+			/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+			/* In the previous versions, MSBs of the seed affect   */
+			/* only MSBs of the array mt[].                        */
+			/* 2002/01/09 modified by Makoto Matsumoto             */
+			//c//mt[mti] &= 0xffffffff;
+			mt[mti] = unsigned32(mt[mti] & 0xffffffff);
+			/* for >32 bit machines */
+		}
+	}
+
+	/* initialize by an array with array-length */
+	/* init_key is the array for initializing keys */
+	/* key_length is its length */
+	/* slight change for C++, 2004/2/26 */
+	//c//void init_by_array(unsigned long init_key[], int key_length)
+	this.init_by_array = function (init_key, key_length)
+	{
+		//c//int i, j, k;
+		var i, j, k;
+		//c//init_genrand(19650218);
+		this.init_genrand(19650218);
+		i=1; j=0;
+		k = (N>key_length ? N : key_length);
+		for (; k; k--) {
+			//c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525))
+			//c//	+ init_key[j] + j; /* non linear */
+			mt[i] = addition32(addition32(unsigned32(mt[i] ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1664525)), init_key[j]), j);
+			mt[i] =
+			//c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
+			unsigned32(mt[i] & 0xffffffff);
+			i++; j++;
+			if (i>=N) { mt[0] = mt[N-1]; i=1; }
+			if (j>=key_length) j=0;
+		}
+		for (k=N-1; k; k--) {
+			//c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941))
+			//c//- i; /* non linear */
+			mt[i] = subtraction32(unsigned32((dbg=mt[i]) ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1566083941)), i);
+			//c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */
+			mt[i] = unsigned32(mt[i] & 0xffffffff);
+			i++;
+			if (i>=N) { mt[0] = mt[N-1]; i=1; }
+		}
+		mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */
+	}
+
+  this.export_state = function() { return [mt, mti]; };
+  this.import_state = function(s) { mt = s[0]; mti = s[1]; };
+  this.export_mta = function() { return mt; };
+  this.import_mta = function(_mta) { mt = _mta };
+  this.export_mti = function() { return mti; };
+  this.import_mti = function(_mti) { mti = _mti; }
+
+	/* generates a random number on [0,0xffffffff]-interval */
+	//c//unsigned long genrand_int32(void)
+	this.genrand_int32 = function ()
+	{
+		//c//unsigned long y;
+		//c//static unsigned long mag01[2]={0x0UL, MATRIX_A};
+		var y;
+		var mag01 = new Array(0x0, MATRIX_A);
+		/* mag01[x] = x * MATRIX_A  for x=0,1 */
+
+		if (mti >= N) { /* generate N words at one time */
+			//c//int kk;
+			var kk;
+
+			if (mti == N+1)   /* if init_genrand() has not been called, */
+				//c//init_genrand(5489); /* a default initial seed is used */
+				this.init_genrand(5489); /* a default initial seed is used */
+
+			for (kk=0;kk<N-M;kk++) {
+				//c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+				//c//mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
+				y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK));
+				mt[kk] = unsigned32(mt[kk+M] ^ (y >>> 1) ^ mag01[y & 0x1]);
+			}
+			for (;kk<N-1;kk++) {
+				//c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+				//c//mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
+				y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK));
+				mt[kk] = unsigned32(mt[kk+(M-N)] ^ (y >>> 1) ^ mag01[y & 0x1]);
+			}
+			//c//y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+			//c//mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+			y = unsigned32((mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK));
+			mt[N-1] = unsigned32(mt[M-1] ^ (y >>> 1) ^ mag01[y & 0x1]);
+			mti = 0;
+		}
+
+		y = mt[mti++];
+
+		/* Tempering */
+		//c//y ^= (y >> 11);
+		//c//y ^= (y << 7) & 0x9d2c5680;
+		//c//y ^= (y << 15) & 0xefc60000;
+		//c//y ^= (y >> 18);
+		y = unsigned32(y ^ (y >>> 11));
+		y = unsigned32(y ^ ((y << 7) & 0x9d2c5680));
+		y = unsigned32(y ^ ((y << 15) & 0xefc60000));
+		y = unsigned32(y ^ (y >>> 18));
+
+		return y;
+	}
+
+	/* generates a random number on [0,0x7fffffff]-interval */
+	//c//long genrand_int31(void)
+	this.genrand_int31 = function ()
+	{
+		//c//return (genrand_int32()>>1);
+		return (this.genrand_int32()>>>1);
+	}
+
+	/* generates a random number on [0,1]-real-interval */
+	//c//double genrand_real1(void)
+	this.genrand_real1 = function ()
+	{
+		//c//return genrand_int32()*(1.0/4294967295.0);
+		return this.genrand_int32()*(1.0/4294967295.0);
+		/* divided by 2^32-1 */
+	}
+
+	/* generates a random number on [0,1)-real-interval */
+	//c//double genrand_real2(void)
+	this.genrand_real2 = function ()
+	{
+		//c//return genrand_int32()*(1.0/4294967296.0);
+		return this.genrand_int32()*(1.0/4294967296.0);
+		/* divided by 2^32 */
+	}
+
+	/* generates a random number on (0,1)-real-interval */
+	//c//double genrand_real3(void)
+	this.genrand_real3 = function ()
+	{
+		//c//return ((genrand_int32()) + 0.5)*(1.0/4294967296.0);
+		return ((this.genrand_int32()) + 0.5)*(1.0/4294967296.0);
+		/* divided by 2^32 */
+	}
+
+	/* generates a random number on [0,1) with 53-bit resolution*/
+	//c//double genrand_res53(void)
+	this.genrand_res53 = function ()
+	{
+		//c//unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+		var a=this.genrand_int32()>>>5, b=this.genrand_int32()>>>6;
+		return(a*67108864.0+b)*(1.0/9007199254740992.0);
+	}
+	/* These real versions are due to Isaku Wada, 2002/01/09 added */
+}
+
+function initRnd() {
+  var fuzzMT = new MersenneTwister19937;
+  var fuzzSeed = 53;
+  fuzzMT.init_genrand(fuzzSeed);
+  rnd = function (n) { var v = Math.floor(fuzzMT.genrand_real2() * n); return v; };
+  rnd.rndReal = function() { return fuzzMT.genrand_real2(); };
+  rnd.fuzzMT = fuzzMT;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-824321.js
@@ -0,0 +1,3 @@
+x = "\udada\udada";
+gc();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-825326.js
@@ -0,0 +1,18 @@
+// |jit-test| mjitalways;
+
+try {
+    a = []
+    r = /x/
+    gczeal(10, 2)()
+} catch (e) {}
+try {
+    (function() {
+        r(function() {
+            eval()
+        })
+    })()
+} catch (e) {}
+try {
+    s
+} catch (e) {}
+a.every(function() {})
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1573,9 +1573,9 @@ typedef void
  * Sets a callback that is run whenever js-ctypes is about to be used when
  * calling into C.
  */
 JS_FRIEND_API(void)
 SetCTypesActivityCallback(JSRuntime *rt, CTypesActivityCallback cb);
 
 } /* namespace js */
 
-#endif /* jsfriendapi_h___ */
\ No newline at end of file
+#endif /* jsfriendapi_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -262,17 +262,17 @@ ArenaHeader::checkSynchronizedWithFreeLi
      */
     JS_ASSERT(allocated());
 
     /*
      * We can be called from the background finalization thread when the free
      * list in the compartment can mutate at any moment. We cannot do any
      * checks in this case.
      */
-    if (!compartment->rt->isHeapBusy())
+    if (IsBackgroundFinalized(getAllocKind()) && !compartment->rt->isHeapBusy())
         return;
 
     FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
     if (firstSpan.isEmpty())
         return;
     const FreeSpan *list = compartment->arenas.getFreeList(getAllocKind());
     if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress())
         return;
@@ -3819,17 +3819,17 @@ ResetIncrementalGC(JSRuntime *rt, const 
 
         break;
       }
 
       case SWEEP:
         for (CompartmentsIter c(rt); !c.done(); c.next()) {
             c->scheduledForDestruction = false;
 
-            if (c->activeAnalysis && !c->gcTypesMarked) {
+            if (c->isGCMarking() && c->activeAnalysis && !c->gcTypesMarked) {
                 AutoCopyFreeListToArenas copy(rt);
                 gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_SWEEP);
                 gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_MARK);
                 gcstats::AutoPhase ap3(rt->gcStats, gcstats::PHASE_SWEEP_MARK_TYPES);
                 rt->gcIncrementalState = MARK_ROOTS;
                 c->markTypes(&rt->gcMarker);
                 rt->gcIncrementalState = SWEEP;
             }
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -480,29 +480,16 @@ class GCCompartmentGroupIter {
 
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
 
-template<typename T>
-static inline void
-UnpoisonThing(T *thing)
-{
-#ifdef DEBUG
-    /* Change the contents of memory slightly so that IsThingPoisoned returns false. */
-    JS_STATIC_ASSERT(sizeof(T) >= sizeof(FreeSpan) + sizeof(uint8_t));
-    uint8_t *p =
-        reinterpret_cast<uint8_t *>(reinterpret_cast<FreeSpan *>(thing) + 1);
-    *p = 0;
-#endif
-}
-
 template <typename T>
 inline T *
 NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
     AssertCanGC();
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
     JS_ASSERT_IF(cx->compartment == cx->runtime->atomsCompartment,
                  kind == FINALIZE_STRING ||
@@ -529,18 +516,16 @@ NewGCThing(JSContext *cx, js::gc::AllocK
     JS_ASSERT_IF(t && comp->wasGCStarted() && (comp->isGCMarking() || comp->isGCSweeping()),
                  t->arenaHeader()->allocatedDuringIncremental);
 
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
         comp->gcNursery.insertPointer(t);
 #endif
 
-    if (t)
-        UnpoisonThing(t);
     return t;
 }
 
 /* Alternate form which allocates a GC thing if doing so cannot trigger a GC. */
 template <typename T>
 inline T *
 TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
 {
@@ -562,18 +547,16 @@ TryNewGCThing(JSContext *cx, js::gc::All
                  t->arenaHeader()->allocatedDuringIncremental);
 
 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
     JSCompartment *comp = cx->compartment;
     if (cx->runtime->gcVerifyPostData && IsNurseryAllocable(kind) && !IsAtomsCompartment(comp))
         comp->gcNursery.insertPointer(t);
 #endif
 
-    if (t)
-        UnpoisonThing(t);
     return t;
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -154,21 +154,19 @@ StatsCellCallback(JSRuntime *rt, void *d
 
         ObjectsExtraSizes objectsExtra;
         obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &objectsExtra);
         cStats->objectsExtra.add(objectsExtra);
 
         // JSObject::sizeOfExcludingThis() doesn't measure objectsExtraPrivate,
         // so we do it here.
         if (ObjectPrivateVisitor *opv = closure->opv) {
-            js::Class *clazz = js::GetObjectClass(obj);
-            if (clazz->flags & JSCLASS_HAS_PRIVATE &&
-                clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
-            {
-                cStats->objectsExtra.private_ += opv->sizeOfIncludingThis(GetObjectPrivate(obj));
+            nsISupports *iface;
+            if (opv->getISupports(obj, &iface) && iface) {
+                cStats->objectsExtra.private_ += opv->sizeOfIncludingThis(iface);
             }
         }
         break;
     }
     case JSTRACE_STRING:
     {
         JSString *str = static_cast<JSString *>(thing);
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1990,25 +1990,26 @@ SizeOfTreeIncludingThis(nsINode *tree)
         n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
     }   
     return n;
 }
 
 class OrphanReporter : public JS::ObjectPrivateVisitor
 {
 public:
-    OrphanReporter()
+    OrphanReporter(GetISupportsFun aGetISupports)
+      : JS::ObjectPrivateVisitor(aGetISupports)
     {
         mAlreadyMeasuredOrphanTrees.Init();
     }
 
-    virtual size_t sizeOfIncludingThis(void *aSupports)
+    virtual size_t sizeOfIncludingThis(nsISupports *aSupports)
     {
         size_t n = 0;
-        nsCOMPtr<nsINode> node = do_QueryInterface(static_cast<nsISupports*>(aSupports));
+        nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
         // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
         // that we have to skip XBL elements because they violate certain
         // assumptions.  Yuk.
         if (node && !node->IsInDoc() &&
             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
         {
             // This is an orphan node.  If we haven't already handled the
             // sub-tree that this node belongs to, measure the sub-tree's size
@@ -2060,25 +2061,25 @@ class XPCJSRuntimeStats : public JS::Run
                 // The global is a |window| object.  Use the path prefix that
                 // we should have already created for it.
                 if (mWindowPaths->Get(piwindow->WindowID(), &cJSPathPrefix)) {
                     cDOMPathPrefix.Assign(cJSPathPrefix);
                     cDOMPathPrefix.AppendLiteral("/dom/");
                     cJSPathPrefix.AppendLiteral("/js/");
                 } else {
                     cJSPathPrefix.AssignLiteral("explicit/js-non-window/compartments/unknown-window-global/");
-                    cDOMPathPrefix.AssignLiteral("explicit/dom/?!/");
+                    cDOMPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
                 }
             } else {
                 cJSPathPrefix.AssignLiteral("explicit/js-non-window/compartments/non-window-global/");
-                cDOMPathPrefix.AssignLiteral("explicit/dom/?!/");
+                cDOMPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
             }
         } else {
             cJSPathPrefix.AssignLiteral("explicit/js-non-window/compartments/no-global/");
-            cDOMPathPrefix.AssignLiteral("explicit/dom/?!/");
+            cDOMPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
         }
 
         cJSPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
 
         // cJSPathPrefix is used for almost all the compartment-specific
         // reports.  At this point it has the form
         // "<something>/compartment/(<cname>)/".
         //
@@ -2103,17 +2104,17 @@ JSMemoryMultiReporter::CollectReports(Wi
 
     // In the first step we get all the stats and stash them in a local
     // data structure.  In the second step we pass all the stashed stats to
     // the callback.  Separating these steps is important because the
     // callback may be a JS function, and executing JS while getting these
     // stats seems like a bad idea.
 
     XPCJSRuntimeStats rtStats(windowPaths);
-    OrphanReporter orphanReporter;
+    OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
     if (!JS::CollectRuntimeStats(xpcrt->GetJSRuntime(), &rtStats, &orphanReporter))
         return NS_ERROR_FAILURE;
 
     size_t xpconnect =
         xpcrt->SizeOfIncludingThis(JsMallocSizeOf) +
         XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
 
     // This is the second step (see above).  First we report stuff in the
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3819,25 +3819,30 @@ ComputeSnappedImageDrawingParameters(gfx
 
   gfxRect devPixelDest =
     nsLayoutUtils::RectToGfxRect(aDest, aAppUnitsPerDevPixel);
   gfxRect devPixelFill =
     nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel);
   gfxRect devPixelDirty =
     nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
 
-  bool ignoreScale = false;
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  ignoreScale = true;
-#endif
+  gfxMatrix currentMatrix = aCtx->CurrentMatrix();
   gfxRect fill = devPixelFill;
-  bool didSnap = aCtx->UserToDevicePixelSnapped(fill, ignoreScale);
-  gfxMatrix currentMatrix = aCtx->CurrentMatrix();
-  if (didSnap && currentMatrix.HasNonAxisAlignedTransform()) {
-    // currentMatrix must have some rotation by a multiple of 90 degrees.
+  bool didSnap;
+  // Snap even if we have a scale in the context. But don't snap if
+  // we have something that's not translation+scale, or if the scale flips in
+  // the X or Y direction, because snapped image drawing can't handle that yet.
+  if (!currentMatrix.HasNonAxisAlignedTransform() &&
+      currentMatrix.xx > 0.0 && currentMatrix.yy > 0.0 &&
+      aCtx->UserToDevicePixelSnapped(fill, true)) {
+    didSnap = true;
+    if (fill.IsEmpty()) {
+      return SnappedImageDrawingParameters();
+    }
+  } else {
     didSnap = false;
     fill = devPixelFill;
   }
 
   gfxSize imageSize(aImageSize.width, aImageSize.height);
 
   // Compute the set of pixels that would be sampled by an ideal rendering
   gfxPoint subimageTopLeft =
--- a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
+++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
@@ -89,43 +89,41 @@ nsMathMLmmultiscriptsFrame::ProcessAttri
   //
   // "Specifies the minimum amount to shift the baseline of subscript down; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   nsAutoString value;
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::subscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSubScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &mSubScriptShift, 0, PresContext(),
+                      mStyleContext);
   }
   // superscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of superscript up; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::superscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mSupScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &mSupScriptShift, 0, PresContext(),
+                      mStyleContext);
   }
 }
 
 /* virtual */ nsresult
 nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext,
                                   bool                 aPlaceOrigin,
                                   nsHTMLReflowMetrics& aDesiredSize)
 {
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -391,20 +391,20 @@ nsMathMLmoFrame::ProcessOperatorData()
 
   // lspace
   //
   // "Specifies the leading space appearing before the operator"
   //
   // values: length
   // default: set by dictionary (thickmathspace) 
   //
-  // XXXfredw Should we allow negative values? (bug 411227) They will be made
-  // positive by the rounding below.
-  // XXXfredw Should we allow relative values? They will give a multiple of the
-  // current leading space, which is not necessarily the default one.
+  // XXXfredw Support for negative and relative values is not implemented
+  // (bug 805926).
+  // Relative values will give a multiple of the current leading space,
+  // which is not necessarily the default one.
   //
   nscoord leadingSpace = mEmbellishData.leadingSpace;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::lspace_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
     if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
                                            mContent->OwnerDoc())) {
@@ -418,20 +418,20 @@ nsMathMLmoFrame::ProcessOperatorData()
 
   // rspace
   //
   // "Specifies the trailing space appearing after the operator"
   //
   // values: length
   // default: set by dictionary (thickmathspace) 
   //
-  // XXXfredw Should we allow negative values? (bug 411227) They will be made
-  // positive by the rounding below.
-  // XXXfredw Should we allow relative values? They will give a multiple of the
-  // current trailing space, which is not necessarily the default one.
+  // XXXfredw Support for negative and relative values is not implemented
+  // (bug 805926).
+  // Relative values will give a multiple of the current leading space,
+  // which is not necessarily the default one.
   //
   nscoord trailingSpace = mEmbellishData.trailingSpace;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::rspace_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
     if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
                                            mContent->OwnerDoc())) {
--- a/layout/mathml/nsMathMLmsubFrame.cpp
+++ b/layout/mathml/nsMathMLmsubFrame.cpp
@@ -58,26 +58,24 @@ nsMathMLmsubFrame::Place (nsRenderingCon
   //
   // "Specifies the minimum amount to shift the baseline of subscript down; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   nscoord subScriptShift = 0;
   nsAutoString value;
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::subscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &subScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &subScriptShift, 0, PresContext(), mStyleContext);
   }
 
   return nsMathMLmsubFrame::PlaceSubScript(PresContext(), 
                                            aRenderingContext,
                                            aPlaceOrigin,
                                            aDesiredSize,
                                            this,
                                            subScriptShift,
--- a/layout/mathml/nsMathMLmsubsupFrame.cpp
+++ b/layout/mathml/nsMathMLmsubsupFrame.cpp
@@ -64,45 +64,41 @@ nsMathMLmsubsupFrame::Place(nsRenderingC
   //
   // "Specifies the minimum amount to shift the baseline of subscript down; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   nsAutoString value;
   nscoord subScriptShift = 0;
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::subscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &subScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &subScriptShift, 0, PresContext(), mStyleContext);
   }
   // superscriptshift
   //
   // "Specifies the minimum amount to shift the baseline of superscript up; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   nscoord supScriptShift = 0;
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::superscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &supScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &supScriptShift, 0, PresContext(), mStyleContext);
   }
 
   return nsMathMLmsubsupFrame::PlaceSubSupScript(PresContext(),
                                                  aRenderingContext,
                                                  aPlaceOrigin,
                                                  aDesiredSize,
                                                  this,
                                                  subScriptShift,
--- a/layout/mathml/nsMathMLmsupFrame.cpp
+++ b/layout/mathml/nsMathMLmsupFrame.cpp
@@ -58,26 +58,24 @@ nsMathMLmsupFrame::Place(nsRenderingCont
   //
   // "Specifies the minimum amount to shift the baseline of superscript up; the
   // default is for the rendering agent to use its own positioning rules."
   //
   // values: length
   // default: automatic
   //
   // We use 0 as the default value so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bug 411227)
+  // As a minimum, negative values can be ignored.
   //
   nsAutoString value;
   nscoord supScriptShift = 0;
   GetAttribute(mContent, mPresentationData.mstyle,
                nsGkAtoms::superscriptshift_, value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &supScriptShift,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
-                      PresContext(), mStyleContext);
+    ParseNumericValue(value, &supScriptShift, 0, PresContext(), mStyleContext);
   }
 
   return nsMathMLmsupFrame::PlaceSuperScript(PresContext(), 
                                              aRenderingContext,
                                              aPlaceOrigin,
                                              aDesiredSize,
                                              this,
                                              supScriptShift,
--- a/layout/printing/nsPrintData.cpp
+++ b/layout/printing/nsPrintData.cpp
@@ -111,16 +111,76 @@ AssertPresShellsAndContextsSane(nsPrintO
     ASSERT_AND_NOTE("print object has mismatching pres shell and pres context");
   }
 
   for (uint32_t i = 0; i < aPO->mKids.Length(); i++) {
     AssertPresShellsAndContextsSane(aPO->mKids[i], aPresShells, aPresContexts);
   }
 }
 
+#ifdef MOZ_CRASHREPORTER
+static void
+AppendBoolean(nsCString& aString, bool aValue)
+{
+  if (aValue) {
+    aString.AppendLiteral("true");
+  } else {
+    aString.AppendLiteral("false");
+  }
+}
+
+static void
+NotePrintObjectTree(nsPrintObject* aPO, int32_t aDepth)
+{
+  nsCString note;
+  for (int32_t i = 0; i < aDepth; i++) {
+    note.AppendLiteral("  ");
+  }
+  note.AppendInt(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(aPO)), 16);
+  note.AppendLiteral(" = { mFrameType = ");
+  note.AppendInt(aPO->mFrameType);
+  note.AppendLiteral(", mHasBeenPrinted = ");
+  AppendBoolean(note, aPO->mHasBeenPrinted);
+  note.AppendLiteral(", mDontPrint = ");
+  AppendBoolean(note, aPO->mDontPrint);
+  note.AppendLiteral(", mPrintAsIs = ");
+  AppendBoolean(note, aPO->mPrintAsIs);
+  note.AppendLiteral(", mInvisible = ");
+  AppendBoolean(note, aPO->mInvisible);
+  note.AppendLiteral(", mPrintPreview = ");
+  AppendBoolean(note, aPO->mPrintPreview);
+  note.AppendLiteral(", mDidCreateDocShell = ");
+  AppendBoolean(note, aPO->mDidCreateDocShell);
+  note.AppendLiteral(", mShrinkRatio = ");
+  note.AppendFloat(aPO->mShrinkRatio);
+  note.AppendLiteral(", mZoomRatio = ");
+  note.AppendFloat(aPO->mZoomRatio);
+  note.AppendLiteral(", mContent = ");
+  if (aPO->mContent) {
+    nsString tag;
+    aPO->mContent->Tag()->ToString(tag);
+    LossyAppendUTF16toASCII(tag, note);
+  } else {
+    note.AppendLiteral("null");
+  }
+  note.AppendLiteral(" }\n");
+  CrashReporter::AppendAppNotesToCrashReport(note);
+  for (uint32_t i = 0; i < aPO->mKids.Length(); i++) {
+    NotePrintObjectTree(aPO->mKids[i], aDepth + 1);
+  }
+}
+
+static void
+NotePrintObjectTree(nsPrintObject* aPO)
+{
+  CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("Print object tree:\n"));
+  NotePrintObjectTree(aPO, 1);
+}
+#endif
+
 #undef ASSERT_AND_NOTE
 
 static void
 AssertPresShellsAndContextsSane(nsPrintObject* aPO)
 {
   nsTArray<nsIPresShell*> presShells;
   nsTArray<nsPresContext*> presContexts;
   AssertPresShellsAndContextsSane(aPO, presShells, presContexts);
@@ -155,16 +215,19 @@ nsPrintData::~nsPrintData()
       }
       if (NS_FAILED(rv)) {
         // XXX nsPrintData::ShowPrintErrorDialog(rv);
       }
     }
   }
 
   AssertPresShellsAndContextsSane(mPrintObject);
+#ifdef MOZ_CRASHREPORTER
+  NotePrintObjectTree(mPrintObject);
+#endif
   delete mPrintObject;
 
   if (mBrandName) {
     NS_Free(mBrandName);
   }
 }
 
 void nsPrintData::OnStartPrinting()
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -119,16 +119,20 @@ static const char kPrintingPromptService
 
 #include "nsFocusManager.h"
 #include "nsRange.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "mozilla/dom/Element.h"
 #include "nsContentList.h"
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //-----------------------------------------------------
 // PR LOGGING
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif
@@ -1525,16 +1529,19 @@ nsresult nsPrintEngine::CleanupOnFailure
    * 
    * When rv == NS_ERROR_ABORT, it means we want out of the 
    * print job without displaying any error messages
    */
   if (aResult != NS_ERROR_ABORT) {
     ShowPrintErrorDialog(aResult, aIsPrinting);
   }
 
+#ifdef MOZ_CRASHREPORTER
+  CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("Unsuccessful print.\n"));
+#endif
   FirePrintCompletionEvent();
 
   return aResult;
 
 }
 
 //---------------------------------------------------------------------
 void
@@ -3165,16 +3172,19 @@ nsPrintEngine::DonePrintingPages(nsPrint
     bool didPrint = PrintDocContent(mPrt->mPrintObject, rv);
     if (NS_SUCCEEDED(rv) && didPrint) {
       PR_PL(("****** In DV::DonePrintingPages PO: %p (%s) didPrint:%s (Not Done Printing)\n", aPO, gFrameTypesStr[aPO->mFrameType], PRT_YESNO(didPrint)));
       return false;
     }
   }
 
   if (NS_SUCCEEDED(aResult)) {
+#ifdef MOZ_CRASHREPORTER
+    CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("Successful print.\n"));
+#endif
     FirePrintCompletionEvent();
   }
 
   TurnScriptingOn(true);
   SetIsPrinting(false);
 
   // Release reference to mPagePrintTimer; the timer object destroys itself
   // after this returns true
--- a/layout/printing/nsPrintObject.cpp
+++ b/layout/printing/nsPrintObject.cpp
@@ -9,16 +9,20 @@
 #include "nsContentUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsGkAtoms.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIBaseWindow.h"
                                                    
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
 //---------------------------------------------------
 //-- nsPrintObject Class Impl
 //---------------------------------------------------
 nsPrintObject::nsPrintObject() :
   mContent(nullptr), mFrameType(eFrame), mParent(nullptr),
   mHasBeenPrinted(false), mDontPrint(true), mPrintAsIs(false),
   mInvisible(false), mDidCreateDocShell(false),
   mShrinkRatio(1.0), mZoomRatio(1.0)
@@ -96,16 +100,22 @@ nsPrintObject::Init(nsIDocShell* aDocShe
 }
 
 //------------------------------------------------------------------
 // Resets PO by destroying the presentation
 void 
 nsPrintObject::DestroyPresentation()
 {
   if (mPresShell) {
+#ifdef MOZ_CRASHREPORTER
+    if (mPresShell->GetPresContext() && !mPresShell->GetPresContext()->GetPresShell()) {
+      NS_ASSERTION(false, "about to destroy print object's PresShell when its pres context no longer has it");
+      CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("about to destroy print object's PresShell when its pres context no longer has it\n"));
+    }
+#endif
     mPresShell->EndObservingDocument();
     nsAutoScriptBlocker scriptBlocker;
     mPresShell->Destroy();
   }
   mPresContext = nullptr;
   mPresShell   = nullptr;
   mViewManager = nullptr;
 }
--- a/layout/reftests/bugs/355548-3-ref.xml
+++ b/layout/reftests/bugs/355548-3-ref.xml
@@ -45,23 +45,23 @@
 </m:math></p>
 
 <p><m:math>
   <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
-  <m:mstyle><m:mi style="font-size:48px;">Id</m:mi></m:mstyle>
 </m:math></p>
 
 <p><m:math>
   <m:mstyle><m:mi style="font-size:24px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:24px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:24px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:24px;">Id</m:mi></m:mstyle>
+  <m:mstyle><m:mi style="font-size:24px;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:0;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:0;">Id</m:mi></m:mstyle>
   <m:mstyle><m:mi style="font-size:0;">Id</m:mi></m:mstyle>
 </m:math></p>
 
 </body>
 </html>
--- a/layout/reftests/bugs/355548-3.xml
+++ b/layout/reftests/bugs/355548-3.xml
@@ -52,27 +52,27 @@
   <!-- CSS takes priority over mathsize and fontsize -->
   <m:mstyle mathcolor="red" color="yellow" style="color:green;"><m:mi>Id</m:mi></m:mstyle>
 </m:mstyle></m:math></p>
 
 <p><m:math><m:mstyle scriptlevel="0" scriptsizemultiplier="0.5" scriptminsize="18px" style="font-size:48px;">
   <!-- test invalid values for MathML length attributes -->
   <m:mstyle mathsize="20 px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="20PX"><m:mi>Id</m:mi></m:mstyle>
-  <m:mstyle mathsize="20"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize=".px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="..px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="+20px"><m:mi>Id</m:mi></m:mstyle>
 </m:mstyle></m:math></p>
 
 <p><m:math><m:mstyle scriptlevel="0" scriptsizemultiplier="0.5" scriptminsize="18px" style="font-size:48px;">
   <!-- test valid values for MathML length attributes -->
   <m:mstyle mathsize=" 24px "><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="24.0px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="24.px"><m:mi>Id</m:mi></m:mstyle>
+  <m:mstyle mathsize="0.5"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="50%"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize=".0px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="-0px"><m:mi>Id</m:mi></m:mstyle>
   <m:mstyle mathsize="0"><m:mi>Id</m:mi></m:mstyle>
 </m:mstyle></m:math></p>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-seam-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<style>
+body {
+  background-color:black;
+}
+div {
+  transform: scale(0.3);
+  transform-origin: 0 0;
+  width: 512px;
+  height: 256px;
+  background: white;
+}
+</style>
+<div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-seam-1a.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<style>
+body {
+  background-color:black;
+}
+div {
+  transform: scale(0.3);
+  transform-origin: 0 0;
+}
+</style>
+<div>
+<!-- Test that drawing of (scaled) single pixel images is snapped -->
+<img style="width:256px; height:256px"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+P//PwAF/gL+3MxZ5wAAAABJRU5ErkJggg=="
+><img style="width:256px; height:256px"
+  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+P//PwAF/gL+3MxZ5wAAAABJRU5ErkJggg==">
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-seam-1b.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<style>
+body {
+  background-color:black;
+}
+div {
+  transform: scale(0.3);
+  transform-origin: 0 0;
+}
+</style>
+<div>
+<!-- Test that drawing of a 20x20 image with all pixels the same color is snapped -->
+<img style="width:256px; height:256px"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAG0lEQVQ4jWP4T2XAMGrgqIGjBo4aOGrgSDUQAOdROhuRCSeMAAAAAElFTkSuQmCC"
+><img style="width:256px; height:256px"
+  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAG0lEQVQ4jWP4T2XAMGrgqIGjBo4aOGrgSDUQAOdROhuRCSeMAAAAAElFTkSuQmCC">
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-seam-2-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<style>
+body {
+  background-color:black;
+}
+div {
+  transform: scale(0.3);
+  transform-origin: 0 0;
+}
+</style>
+<div>
+<img style="width:412px; height:256px"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAADklEQVQImWP4jwYY0AEAlpkP8c9bnjkAAAAASUVORK5CYII="
+><img style="width:100px; height:256px"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAADklEQVQImWP4jwYY0AEAlpkP8c9bnjkAAAAASUVORK5CYII="
+>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-seam-2.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<style>
+body {
+  background-color:black;
+}
+div {
+  transform: scale(0.3);
+  transform-origin: 0 0;
+}
+</style>
+<div>
+<!-- Test that drawing of nonuniform images is snapped -->
+<!-- This is a 2x2 image where the top row is white and the bottom row is transpraent -->
+<img style="width:256px; height:256px"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAADklEQVQImWP4DwUMMAAAa6YH+b3nj3MAAAAASUVORK5CYII="
+><img style="width:256px; height:256px"
+  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAADklEQVQImWP4DwUMMAAAa6YH+b3nj3MAAAAASUVORK5CYII=">
+</div>
--- a/layout/reftests/image/reftest.list
+++ b/layout/reftests/image/reftest.list
@@ -1,9 +1,12 @@
 random-if(bug685516) fuzzy-if(Android,4,15) == background-image-zoom-1.html background-image-zoom-1-ref.html
 == background-image-zoom-2.html about:blank
+== image-seam-1a.html image-seam-1-ref.html
+== image-seam-1b.html image-seam-1-ref.html
+fails-if(cocoaWidget) == image-seam-2.html image-seam-2-ref.html # Quartz doesn't support EXTEND_PAD (bug 567370)
 == image-zoom-1.html image-zoom-1-ref.html
 == image-zoom-2.html image-zoom-1-ref.html
 == invalid-url-image-1.html invalid-url-image-1-ref.html
 == sync-image-switch-1a.html sync-image-switch-1-ref.html
 == sync-image-switch-1b.html sync-image-switch-1-ref.html
 == sync-image-switch-1c.html sync-image-switch-1-ref.html
 == sync-image-switch-1d.html sync-image-switch-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfrac-linethickness-2-ref.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mfrac linethickness="200%">
+      <mi>x</mi>
+      <mn>2</mn>
+    </mfrac>
+  </math>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfrac-linethickness-2.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mfrac linethickness="2">
+      <mi>x</mi>
+      <mn>2</mn>
+    </mfrac>
+  </math>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfrac-linethickness-3-ref.xhtml
@@ -0,0 +1,25 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mfrac>
+      <mi>x</mi>
+      <mn>2</mn>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac>
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac>
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac>
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+  </math>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfrac-linethickness-3.xhtml
@@ -0,0 +1,25 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mfrac linethickness="2km">
+      <mi>x</mi>
+      <mn>2</mn>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac linethickness = "cat">
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac linethickness = "-2px">
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+    <mo>=</mo>
+    <mfrac linethickness="1">
+      <mi>a</mi>
+      <mi>b</mi>
+    </mfrac>
+  </math>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/number-size-1-ref.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mn mathsize="200%">2</mn>
+  </math>  
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/number-size-1.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mn mathsize="2">2</mn>
+  </math> 
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/operator-1-ref.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mn>3</mn>
+    <mo>+</mo>
+    <mn>2</mn>
+  </math> 
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/operator-1.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <mn>3</mn>
+    <mo lspace="-5pt" rspace="-4pt">+</mo>
+    <mn>2</mn>
+  </math> 
+</body>
+</html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -89,16 +89,18 @@ fails == mstyle-5.xhtml mstyle-5-ref.xht
 == mpadded-6.html mpadded-6-ref.html
 == math-display.html math-display-ref.html
 == scriptlevel-movablelimits-1.html scriptlevel-movablelimits-1-ref.html
 == munderover-align-accent-false.html munderover-align-accent-false-ref.html
 == munderover-align-accent-true.html munderover-align-accent-true-ref.html
 == munder-mover-align-accent-true.html munder-mover-align-accent-true-ref.html
 == munder-mover-align-accent-false.html munder-mover-align-accent-false-ref.html
 == mfrac-linethickness-1.xhtml mfrac-linethickness-1-ref.xhtml
+== mfrac-linethickness-2.xhtml mfrac-linethickness-2-ref.xhtml
+== mfrac-linethickness-3.xhtml mfrac-linethickness-3-ref.xhtml
 == mathml-negativespace.html mathml-negativespace-ref.html
 != link-1.xhtml link-ref.xhtml
 == munderover-empty-scripts.html munderover-empty-scripts-ref.html
 == positive-namedspace.html positive-namedspace-ref.html
 == mtable-align-whitespace.html mtable-align-whitespace-ref.html
 == mtable-width.html mtable-width-ref.html
 == maction-selection.html maction-selection-ref.html
 == maction-dynamic-embellished-op.html maction-dynamic-embellished-op-ref.html
@@ -106,8 +108,11 @@ fails == mstyle-5.xhtml mstyle-5-ref.xht
 == maction-dynamic-2.html maction-dynamic-2-ref.html
 == mo-lspace-rspace.html mo-lspace-rspace-ref.html
 == maction-dynamic-3.html maction-dynamic-3-ref.html
 == whitespace-trim-1.html whitespace-trim-1-ref.html
 == whitespace-trim-2.html whitespace-trim-2-ref.html
 == whitespace-trim-3.html whitespace-trim-3-ref.html
 fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215
 == whitespace-trim-5.html whitespace-trim-5-ref.html
+== operator-1.xhtml operator-1-ref.xhtml
+== scriptshift-1.xhtml scriptshift-1-ref.xhtml
+== number-size-1.xhtml number-size-1-ref.xhtml
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/scriptshift-1-ref.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <msub>
+      <mi>x</mi>
+      <mn>2</mn>
+    </msub>
+    <mo>+</mo>
+    <msup>
+      <mi>x</mi>
+      <mn>2</mn>
+    </msup>
+    <mo>+</mo>
+    <msubsup>
+      <mi>x</mi>
+      <mn>2</mn>
+      <mn>3</mn>
+    </msubsup>
+    <mo>+</mo>
+    <mmultiscripts>
+      <mi> x </mi>
+      <mn>2</mn>
+      <mn>3</mn>
+      <mprescripts/>
+      <mn>4</mn>
+      <mn>5</mn>
+    </mmultiscripts>
+  </math> 
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/scriptshift-1.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+  <math xmlns="http://www.w3.org/1998/Math/MathML">
+    <msub subscriptshift="-5px">
+      <mi>x</mi>
+      <mn>2</mn>
+    </msub>
+    <mo>+</mo>
+    <msup superscriptshift="-5px">
+      <mi>x</mi>
+      <mn>2</mn>
+    </msup>
+    <mo>+</mo>
+    <msubsup subscriptshift="-5px" superscriptshift="-5px">
+      <mi>x</mi>
+      <mn>2</mn>
+      <mn>3</mn>
+    </msubsup>
+    <mo>+</mo>
+    <mmultiscripts subscriptshift="-5px" superscriptshift="-5px">
+      <mi> x </mi>
+      <mn>2</mn>
+      <mn>3</mn>
+      <mprescripts/>
+      <mn>4</mn>
+      <mn>5</mn>
+    </mmultiscripts>
+  </math> 
+</body>
+</html>
--- a/media/mtransport/test/Makefile.in
+++ b/media/mtransport/test/Makefile.in
@@ -94,22 +94,24 @@ CPPFLAGS += \
   $(STLPORT_CPPFLAGS) \
   $(NULL)
 DEFINES += \
   -DGTEST_USE_OWN_TR1_TUPLE=1 \
   $(NULL)
 endif
 
 ifneq ($(OS_TARGET),WINNT)
+ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPP_UNIT_TESTS = \
   ice_unittest.cpp \
   nrappkit_unittest.cpp \
   sockettransportservice_unittest.cpp \
   transport_unittests.cpp \
   runnable_utils_unittest.cpp \
   $(NULL)
 ifdef MOZ_SCTP
 CPP_UNIT_TESTS += sctp_unittest.cpp
 endif
 
 endif
+endif
 
 include $(topsrcdir)/config/rules.mk
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -199,13 +199,19 @@
                  ],
 
 		 'include_dirs': [
 		     '../nrappkit/src/port/linux/include'
 		 ],
 		 
 		 'sources': [
 		 ],
-              }]
-          ]
+              }],
+              ['moz_widget_toolkit_gonk==1', {
+                'defines' : [
+                  'WEBRTC_GONK',
+                  'NO_REG_RPC',
+                ],
+             }],
+          ],
       }]
 }
 
--- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -97,19 +97,22 @@ int nr_ice_candidate_pair_create(nr_ice_
     if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
       rcand->foundation,NULL))
       ABORT(r);
 
 
     /* OK, now the STUN data */
     lufrag=lcand->stream->ufrag?lcand->stream->ufrag:pctx->ctx->ufrag;
     lpwd=lcand->stream->pwd?lcand->stream->pwd:pctx->ctx->pwd;
+    assert(lufrag);
+    assert(lpwd);
     rufrag=rcand->stream->ufrag?rcand->stream->ufrag:pctx->peer_ufrag;
     rpwd=rcand->stream->pwd?rcand->stream->pwd:pctx->peer_pwd;
-
+    if (!rufrag || !rpwd)
+      ABORT(R_BAD_DATA);
 
     /* Compute the RTO per S 16 */
     RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs));
 
     /* Make a bogus candidate to compute a theoretical peer reflexive
      * priority per S 7.1.1.1 */
     memcpy(&tmpcand, lcand, sizeof(tmpcand));
     tmpcand.type = PEER_REFLEXIVE;
--- a/media/webrtc/Makefile.in
+++ b/media/webrtc/Makefile.in
@@ -6,16 +6,21 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir          = @srcdir@
 VPATH           = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = \
   trunk \
+  signaling \
+  $(NULL)
+
+ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
+DIRS += \
+  signalingtest \
   trunk/testing \
-  signaling \
-  signalingtest \
-  $(NULL)
+	$(NULL)
+endif
 
 # These Makefiles don't have corresponding Makefile.ins
 NO_SUBMAKEFILES_RULE = 1
 include $(topsrcdir)/config/rules.mk
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -8,16 +8,25 @@
 # sip.gyp - a library for SIP
 #
 
 {
   'variables': {
     'chromium_code': 1,
   },
 
+  'target_defaults': {
+    'conditions': [
+      ['moz_widget_toolkit_gonk==1', {
+        'defines' : [
+          'WEBRTC_GONK',
+       ],
+      }],
+    ],
+  }, 
   'targets': [
 
     #
     # ECC
     #
     {
       'target_name': 'ecc',
       'type': 'static_library',
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -552,22 +552,22 @@ static void NotifyDataChannel_m(nsRefPtr
   MOZ_ASSERT(NS_IsMainThread());
 
   aObserver->NotifyDataChannel(aChannel);
   NS_DataChannelAppReady(aChannel);
 }
 #endif
 
 void
-PeerConnectionImpl::NotifyDataChannel(mozilla::DataChannel *aChannel)
+PeerConnectionImpl::NotifyDataChannel(already_AddRefed<mozilla::DataChannel> aChannel)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
-  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aChannel.get());
 
-  CSFLogDebugS(logTag, __FUNCTION__ << ": channel: " << static_cast<void*>(aChannel));
+  CSFLogDebugS(logTag, __FUNCTION__ << ": channel: " << static_cast<void*>(aChannel.get()));
 
 #ifdef MOZILLA_INTERNAL_API
    nsCOMPtr<nsIDOMDataChannel> domchannel;
    nsresult rv = NS_NewDOMDataChannel(aChannel, mWindow,
                                       getter_AddRefs(domchannel));
   NS_ENSURE_SUCCESS_VOID(rv);
 
   RUN_ON_THREAD(mThread,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -132,17 +132,17 @@ public:
     ccapi_call_event_e aCallEvent,
     CSF::CC_CallPtr aCall,
     CSF::CC_CallInfoPtr aInfo
   );
 
   // DataConnection observers
   void NotifyConnection();
   void NotifyClosedConnection();
-  void NotifyDataChannel(mozilla::DataChannel *aChannel);
+  void NotifyDataChannel(already_AddRefed<mozilla::DataChannel> aChannel);
 
   // Get the media object
   const nsRefPtr<PeerConnectionMedia>& media() const {
     PC_AUTO_ENTER_API_CALL_NO_CHECK();
     return mMedia;
   }
 
   // Handle system to allow weak references to be passed through C code
--- a/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_init.c
+++ b/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_init.c
@@ -112,18 +112,23 @@
 #include "cpr.h"
 #include "cpr_stdlib.h"
 #include "cpr_stdio.h"
 #include "cpr_timers.h"
 #include "cpr_linux_locks.h"
 #include "cpr_linux_timers.h"
 #include "plat_api.h"
 #include <errno.h>
+#if defined(WEBRTC_GONK)
+#include <linux/msg.h>
+#include <linux/ipc.h>
+#else
 #include <sys/msg.h>
 #include <sys/ipc.h>
+#endif
 #include "plat_debug.h"
 
 /**
   * Mutex to manage message queue list.
   */
 extern pthread_mutex_t msgQueueListMutex;
 
 /**
--- a/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_ipc.c
+++ b/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_ipc.c
@@ -31,25 +31,54 @@
  * @{
  *
  *
  */
 #include "cpr.h"
 #include "cpr_stdlib.h"
 #include <cpr_stdio.h>
 #include <errno.h>
+#if defined(WEBRTC_GONK)
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <linux/msg.h>
+#include <linux/ipc.h>
+#else
 #include <sys/msg.h>
 #include <sys/ipc.h>
+#endif
 #include "plat_api.h"
 #include "CSFLog.h"
 
 static const char *logTag = "cpr_linux_ipc";
 
 #define STATIC static
 
+#if defined(WEBRTC_GONK)
+int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
+{
+  return syscall(__NR_msgsnd, msqid, msgp, msgsz, msgflg);
+}
+
+ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
+{
+  return syscall(__NR_msgrcv, msqid, msgp, msgsz, msgtyp, msgflg);
+}
+
+int msgctl(int msqid, int cmd, struct msqid_ds *buf)
+{
+  return syscall(__NR_msgctl, msqid, cmd, buf);
+}
+
+int msgget(key_t key, int msgflg)
+{
+  return syscall(__NR_msgget, key, msgflg);
+}
+#endif
+
 /* @def The Message Queue depth */
 #define OS_MSGTQL 31
 
 /*
  * Internal CPR API
  */
 extern pthread_t cprGetThreadId(cprThread_t thread);
 
--- a/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_socket.c
+++ b/media/webrtc/signaling/src/sipcc/cpr/linux/cpr_linux_socket.c
@@ -7,18 +7,23 @@
 #include "cpr_assert.h"
 #include "cpr_socket.h"
 #include "cpr_debug.h"
 #include "cpr_rand.h"
 #include "cpr_timers.h"
 #include "cpr_errno.h"
 #include "cpr_stdlib.h"
 #include "cpr_string.h"
+#if defined(WEBRTC_GONK)
+#include <syslog.h>
+#include <linux/fcntl.h>
+#else
 #include <sys/syslog.h>
 #include <sys/fcntl.h>
+#endif
 #include <ctype.h>
 
 
 //const cpr_in6_addr_t in6addr_any = IN6ADDR_ANY_INIT;
 const cpr_ip_addr_t ip_addr_invalid = {0};
 
 #define IN6ADDRSZ   16
 #define INT16SZ     2
--- a/media/webrtc/signaling/test/Makefile.in
+++ b/media/webrtc/signaling/test/Makefile.in
@@ -131,23 +131,25 @@ LOCAL_INCLUDES += \
  -I$(topsrcdir)/media/webrtc/signaling/src/mediapipeline \
  -I$(topsrcdir)/media/webrtc/signaling/src/sipcc/include \
  -I$(topsrcdir)/media/webrtc/signaling/src/peerconnection \
  -I$(topsrcdir)/media/webrtc/signaling/media-conduit\
  -I$(topsrcdir)/media/webrtc/trunk/third_party/libjingle/source/ \
  $(NULL)
 
 ifneq ($(OS_TARGET),WINNT)
+ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
 ifdef JS_SHARED_LIBRARY
 LIBS += $(MOZ_ZLIB_LIBS)
 endif
 
 CPP_UNIT_TESTS = \
   signaling_unittests.cpp \
   mediapipeline_unittest.cpp \
   mediaconduit_unittests.cpp \
   $(NULL)
 endif
+endif
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/media/webrtc/webrtc-config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
--- a/media/webrtc/trunk/build/common.gypi
+++ b/media/webrtc/trunk/build/common.gypi
@@ -870,16 +870,23 @@
 
     'sas_dll_exists': '<!(python <(DEPTH)/build/dir_exists.py <(sas_dll_path))',
     'wix_exists': '<!(python <(DEPTH)/build/dir_exists.py <(wix_path))',
 
     'windows_sdk_default_path': '<(DEPTH)/third_party/platformsdk_win8/files',
     'directx_sdk_default_path': '<(DEPTH)/third_party/directxsdk/files',
 
     'conditions': [
+      ['moz_widget_toolkit_gonk==1', {
+        'variables': {
+          'disable_sse2': 1,
+        },
+      }],
+    ],
+    'conditions': [
       ['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(windows_sdk_default_path))"=="True"', {
         'windows_sdk_path%': '<(windows_sdk_default_path)',
       }, {
         'windows_sdk_path%': 'C:/Program Files (x86)/Windows Kits/8.0',
       }],
       ['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(directx_sdk_default_path))"=="True"', {
         'directx_sdk_path%': '<(directx_sdk_default_path)',
       }, {
--- a/media/webrtc/trunk/src/build/arm_neon.gypi
+++ b/media/webrtc/trunk/src/build/arm_neon.gypi
@@ -16,17 +16,17 @@
 #     'foo.c',
 #     'bar.cc',
 #   ],
 #   'includes': ['path/to/this/gypi/file'],
 # }
 
 {
   'conditions': [
-    ['OS=="android"', {
+    ['OS=="android" or moz_widget_toolkit_gonk==1', {
       'cflags!': [
         '-mfpu=vfpv3-d16',
       ],
       'cflags_mozilla!': [
         '-mfpu=vfpv3-d16',
       ],
       'cflags': [
         '-mfpu=neon',
--- a/media/webrtc/trunk/src/build/common.gypi
+++ b/media/webrtc/trunk/src/build/common.gypi
@@ -115,16 +115,22 @@
     ],
     'defines': [
       # TODO(leozwang): Run this as a gclient hook rather than at build-time:
       # http://code.google.com/p/webrtc/issues/detail?id=687
       'WEBRTC_SVNREVISION="\\\"Unavailable(issue687)\\\""',
       #'WEBRTC_SVNREVISION="<!(python <(webrtc_root)/build/version.py)"',
     ],
     'conditions': [
+      ['moz_widget_toolkit_gonk==1', {
+        'defines' : [
+          'WEBRTC_GONK',
+        ],
+      }],
+  
       ['build_with_chromium==1', {
         'defines': [
           # Changes settings for Chromium build.
           'WEBRTC_CHROMIUM_BUILD',
         ],
       }, {
         'conditions': [
           ['os_posix==1', {
@@ -168,16 +174,23 @@
                   'WEBRTC_DETECT_ARM_NEON',
                 ],
               }],
             ],
           }],
         ],
       }],
       ['OS=="linux"', {
+        'conditions': [
+          ['moz_have_clock_monotonic==1', {
+            'defines': [
+              'WEBRTC_CLOCK_TYPE_REALTIME',
+            ],
+          }],
+        ],
         'defines': [
           'WEBRTC_LINUX',
           'WEBRTC_THREAD_RR',
           # TODO(andrew): can we select this automatically?
           # Define this if the Linux system does not support CLOCK_MONOTONIC.
           #'WEBRTC_CLOCK_TYPE_REALTIME',
         ],
       }],
--- a/media/webrtc/trunk/src/modules/audio_device/main/source/linux/audio_device_alsa_linux.h
+++ b/media/webrtc/trunk/src/modules/audio_device/main/source/linux/audio_device_alsa_linux.h
@@ -10,20 +10,25 @@
 
 #ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ALSA_LINUX_H
 #define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ALSA_LINUX_H
 
 #include "audio_device_generic.h"
 #include "critical_section_wrapper.h"
 #include "audio_mixer_manager_alsa_linux.h"
 
-#include <sys/soundcard.h>
 #include <sys/ioctl.h>
 
+#if defined (WEBRTC_GONK)
+#include <linux/soundcard.h>
+#include <tinyalsa/asoundlib.h>
+#else
+#include <sys/soundcard.h>
 #include <alsa/asoundlib.h>
+#endif
 
 namespace webrtc
 {
 class EventWrapper;
 class ThreadWrapper;
 
 class AudioDeviceLinuxALSA : public AudioDeviceGeneric
 {
--- a/media/webrtc/trunk/src/modules/audio_device/main/source/linux/audio_mixer_manager_alsa_linux.h
+++ b/media/webrtc/trunk/src/modules/audio_device/main/source/linux/audio_mixer_manager_alsa_linux.h
@@ -11,17 +11,21 @@
 #ifndef WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_ALSA_LINUX_H
 #define WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_ALSA_LINUX_H
 
 #include "typedefs.h"
 #include "audio_device.h"
 #include "critical_section_wrapper.h"
 #include "alsasymboltable_linux.h"
 
+#if defined (WEBRTC_GONK)
+#include <tinyalsa/asoundlib.h>
+#else
 #include <alsa/asoundlib.h>
+#endif
 
 namespace webrtc
 {
 
 class AudioMixerManagerLinuxALSA
 {
 public:
     WebRtc_Word32 OpenSpeaker(char* deviceName);
--- a/media/webrtc/trunk/src/system_wrappers/source/aligned_malloc.cc
+++ b/media/webrtc/trunk/src/system_wrappers/source/aligned_malloc.cc
@@ -24,16 +24,20 @@
 #endif
 
 #if _WIN32
     #include <windows.h>
 #else
     #include <stdint.h>
 #endif
 
+#ifdef WEBRTC_GONK
+#include <string.h>
+#endif
+
 #include "typedefs.h"
 
 // Ok reference on memory alignment:
 // http://stackoverflow.com/questions/227897/solve-the-memory-alignment-in-c-interview-question-that-stumped-me
 
 namespace webrtc
 {
 // TODO (hellner) better to create just one memory block and
--- a/media/webrtc/trunk/src/system_wrappers/source/cpu_info.cc
+++ b/media/webrtc/trunk/src/system_wrappers/source/cpu_info.cc
@@ -35,17 +35,17 @@ WebRtc_UWord32 CpuInfo::DetectNumberOfCo
     {
 #if defined(_WIN32)
         SYSTEM_INFO si;
         GetSystemInfo(&si);
         _numberOfCores = static_cast<WebRtc_UWord32>(si.dwNumberOfProcessors);
         WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
                      "Available number of cores:%d", _numberOfCores);
 
-#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && !defined(WEBRTC_GONK)
         _numberOfCores = get_nprocs();
         WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
                      "Available number of cores:%d", _numberOfCores);
 
 #elif (defined(WEBRTC_MAC) || defined(WEBRTC_MAC_INTEL))
         int name[] = {CTL_HW, HW_AVAILCPU};
         int ncpu;
         size_t size = sizeof(ncpu);
--- a/media/webrtc/trunk/src/system_wrappers/source/thread_posix.cc
+++ b/media/webrtc/trunk/src/system_wrappers/source/thread_posix.cc
@@ -124,17 +124,17 @@ uint32_t ThreadWrapper::GetThreadId() {
 #else
   return reinterpret_cast<uint32_t>(pthread_self());
 #endif
 }
 
 int ThreadPosix::Construct()
 {
     int result = 0;
-#if !defined(WEBRTC_ANDROID)
+#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_GONK)
     // Enable immediate cancellation if requested, see Shutdown()
     result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
     if (result != 0)
     {
         return -1;
     }
     result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
     if (result != 0)
@@ -279,17 +279,17 @@ bool ThreadPosix::SetAffinity(const int*
 void ThreadPosix::SetNotAlive()
 {
     CriticalSectionScoped cs(_crit_state);
     _alive = false;
 }
 
 bool ThreadPosix::Shutdown()
 {
-#if !defined(WEBRTC_ANDROID)
+#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_GONK)
     if (_thread && (0 != pthread_cancel(_thread)))
     {
         return false;
     }
 
     return true;
 #else
     return false;
--- a/media/webrtc/trunk/supplement/supplement.gypi
+++ b/media/webrtc/trunk/supplement/supplement.gypi
@@ -3,10 +3,15 @@
   'variables': {
     'build_with_chromium': 0,
     'enable_protobuf': 0,
     'enabled_libjingle_device_manager': 1,
     'include_internal_audio_device': 1,
     'include_internal_video_capture': 1,
     'include_internal_video_render': 0,
     'include_pulse_audio': 0,
-  }
+    'conditions': [
+      ['moz_widget_toolkit_gonk==1', {
+        'include_internal_audio_device': 0,
+      }],
+    ],
+  },
 }
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -702,20 +702,20 @@ public:
 };
 
 //---------------------------------------------------------------------------
 // Stack traces
 //---------------------------------------------------------------------------
 
 class StackTrace
 {
-  static const uint32_t MaxDepth = 24;
+  static const uint32_t MaxFrames = 24;
 
   uint32_t mLength;             // The number of PCs.
-  void* mPcs[MaxDepth];         // The PCs themselves.
+  void* mPcs[MaxFrames];        // The PCs themselves.
 
 public:
   StackTrace() : mLength(0) {}
 
   uint32_t Length() const { return mLength; }
   void* Pc(uint32_t i) const { MOZ_ASSERT(i < mLength); return mPcs[i]; }
 
   uint32_t Size() const { return mLength * sizeof(mPcs[0]); }
@@ -746,23 +746,19 @@ public:
     return aA->mLength == aB->mLength &&
            memcmp(aA->mPcs, aB->mPcs, aA->Size()) == 0;
   }
 
 private:
   static void StackWalkCallback(void* aPc, void* aSp, void* aClosure)
   {
     StackTrace* st = (StackTrace*) aClosure;
-
-    // Only fill to MaxDepth.
-    // XXX: bug 818793 will allow early bailouts.
-    if (st->mLength < MaxDepth) {
-      st->mPcs[st->mLength] = aPc;
-      st->mLength++;
-    }
+    MOZ_ASSERT(st->mLength < MaxFrames);
+    st->mPcs[st->mLength] = aPc;
+    st->mLength++;
   }
 
   static int QsortCmp(const void* aA, const void* aB)
   {
     const void* const a = *static_cast<const void* const*>(aA);
     const void* const b = *static_cast<const void* const*>(aB);
     if (a < b) return -1;
     if (a > b) return  1;
@@ -773,23 +769,17 @@ private:
 typedef js::HashSet<StackTrace*, StackTrace, InfallibleAllocPolicy>
         StackTraceTable;
 static StackTraceTable* gStackTraceTable = nullptr;
 
 void
 StackTrace::Print(const Writer& aWriter, LocationService* aLocService) const
 {
   if (mLength == 0) {
-    W("   (empty)\n");
-    return;
-  }
-
-  if (gMode == Test) {
-    // Don't print anything because there's too much variation.
-    W("   (stack omitted due to test mode)\n");
+    W("   (empty)\n");  // StackTrace::Get() must have failed
     return;
   }
 
   for (uint32_t i = 0; i < mLength; i++) {
     aLocService->WriteLocation(aWriter, Pc(i));
   }
 }
 
@@ -801,27 +791,43 @@ StackTrace::Get(Thread* aT)
 
   // On Windows, NS_StackWalk can acquire a lock from the shared library
   // loader.  Another thread might call malloc while holding that lock (when
   // loading a shared library).  So we can't be in gStateLock during the call
   // to NS_StackWalk.  For details, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=374829#c8
   // On Linux, something similar can happen;  see bug 824340.
   // So let's just release it on all platforms.
+  nsresult rv;
   StackTrace tmp;
   {
     AutoUnlockState unlock;
-    // In normal operation, skip=3 gets us past various malloc wrappers into
-    // more interesting stuff.  But in test mode we need to skip a bit less to
-    // sufficiently differentiate some similar stacks.
-    uint32_t skip = (gMode == Test) ? 2 : 3;
-    nsresult rv = NS_StackWalk(StackWalkCallback, skip, &tmp, 0, nullptr);
-    if (NS_FAILED(rv) || tmp.mLength == 0) {
-      tmp.mLength = 0;
-    }
+    uint32_t skipFrames = 2;
+    rv = NS_StackWalk(StackWalkCallback, skipFrames, MaxFrames, &tmp, 0,
+                      nullptr);
+  }
+
+  if (rv == NS_OK) {
+    // Handle the common case first.  All is ok.  Nothing to do.
+  } else if (rv == NS_ERROR_NOT_IMPLEMENTED || rv == NS_ERROR_FAILURE) {
+    tmp.mLength = 0;
+  } else if (rv == NS_ERROR_UNEXPECTED) {
+    // XXX: This |rv| only happens on Mac, and it indicates that we're handling
+    // a call to malloc that happened inside a mutex-handling function.  Any
+    // attempt to create a semaphore (which can happen in printf) could
+    // deadlock.
+    //
+    // However, the most complex thing DMD does after Get() returns is to put
+    // something in a hash table, which might call
+    // InfallibleAllocPolicy::malloc_.  I'm not yet sure if this needs special
+    // handling, hence the forced abort.  Sorry.  If you hit this, please file
+    // a bug and CC nnethercote.
+    MOZ_CRASH();
+  } else {
+    MOZ_CRASH();  // should be impossible
   }
 
   StackTraceTable::AddPtr p = gStackTraceTable->lookupForAdd(&tmp);
   if (!p) {
     StackTrace* stnew = InfallibleAllocPolicy::new_<StackTrace>(tmp);
     (void)gStackTraceTable->add(p, stnew);
   }
   return *p;
@@ -1574,17 +1580,17 @@ BadArg(const char* aArg)
 static void
 NopStackWalkCallback(void* aPc, void* aSp, void* aClosure)
 {
 }
 #endif
 
 // Note that fopen() can allocate.
 static FILE*
-OpenTestOrStressFile(const char* aFilename)
+OpenOutputFile(const char* aFilename)
 {
   FILE* fp = fopen(aFilename, "w");
   if (!fp) {
     StatusMsg("can't create %s file: %s\n", aFilename, strerror(errno));
     exit(1);
   }
   return fp;
 }
@@ -1671,47 +1677,48 @@ Init(const malloc_table_t* aMallocTable)
 
 #ifdef XP_MACOSX
   // On Mac OS X we need to call StackWalkInitCriticalAddress() very early
   // (prior to the creation of any mutexes, apparently) otherwise we can get
   // hangs when getting stack traces (bug 821577).  But
   // StackWalkInitCriticalAddress() isn't exported from xpcom/, so instead we
   // just call NS_StackWalk, because that calls StackWalkInitCriticalAddress().
   // See the comment above StackWalkInitCriticalAddress() for more details.
-  (void)NS_StackWalk(NopStackWalkCallback, 0, nullptr, 0, nullptr);
+  (void)NS_StackWalk(NopStackWalkCallback, /* skipFrames */ 0,
+                     /* maxFrames */ 1, nullptr, 0, nullptr);
 #endif
 
   gStateLock = InfallibleAllocPolicy::new_<Mutex>();
 
   gSmallBlockActualSizeCounter = 0;
 
   DMD_CREATE_TLS_INDEX(gTlsIndex);
 
   gStackTraceTable = InfallibleAllocPolicy::new_<StackTraceTable>();
   gStackTraceTable->init(8192);
 
   gBlockTable = InfallibleAllocPolicy::new_<BlockTable>();
   gBlockTable->init(8192);
 
   if (gMode == Test) {
-    // OpenTestOrStressFile() can allocate.  So do this before setting
+    // OpenOutputFile() can allocate.  So do this before setting
     // gIsDMDRunning so those allocations don't show up in our results.  Once
     // gIsDMDRunning is set we are intercepting malloc et al. in earnest.
-    FILE* fp = OpenTestOrStressFile("test.dmd");
+    FILE* fp = OpenOutputFile("test.dmd");
     gIsDMDRunning = true;
 
     StatusMsg("running test mode...\n");
     RunTestMode(fp);
     StatusMsg("finished test mode\n");
     fclose(fp);
     exit(0);
   }
 
   if (gMode == Stress) {
-    FILE* fp = OpenTestOrStressFile("stress.dmd");
+    FILE* fp = OpenOutputFile("stress.dmd");
     gIsDMDRunning = true;
 
     StatusMsg("running stress mode...\n");
     RunStressMode(fp);
     StatusMsg("finished stress mode\n");
     fclose(fp);
     exit(0);
   }
@@ -1822,22 +1829,16 @@ PrintSortedTraceAndFrameRecords(const Wr
                                 const char* aStr, const char* astr,
                                 const TraceRecordTable& aTraceRecordTable,
                                 size_t aCategoryUsableSize,
                                 size_t aTotalUsableSize)
 {
   PrintSortedRecords(aWriter, aLocService, aStr, astr, aTraceRecordTable,
                      aCategoryUsableSize, aTotalUsableSize);
 
-  // Frame records are totally dependent on vagaries of stack traces, so we
-  // can't show them in test mode.
-  if (gMode == Test) {
-    return;
-  }
-
   FrameRecordTable frameRecordTable;
   (void)frameRecordTable.init(2048);
   for (TraceRecordTable::Range r = aTraceRecordTable.all();
        !r.empty();
        r.popFront()) {
     const TraceRecord& tr = r.front();
     const StackTrace* st = tr.mAllocStackTrace;
 
new file mode 100755
--- /dev/null
+++ b/memory/replace/dmd/check_test_output.py
@@ -0,0 +1,139 @@
+#! /usr/bin/python
+
+"""This script takes the file produced by DMD's test mode and checks its
+correctness.
+
+It produces the following output files: $TMP/test-{fixed,filtered,diff}.dmd.
+
+It runs the appropriate fix* script to get nice stack traces.  It also
+filters out platform-specific details from the test output file.
+
+Note: you must run this from the same directory that you invoked DMD's test
+mode, otherwise the fix* script will not work properly, because some of the
+paths in the test output are relative.
+
+"""
+
+from __future__ import print_function
+
+import os
+import platform
+import re
+import subprocess
+import sys
+import tempfile
+
+
+def main():
+
+    # Arguments
+
+    if (len(sys.argv) != 3):
+        print("usage:", sys.argv[0], "<srcdir> <test-output>")
+        sys.exit(1)
+
+    srcdir = sys.argv[1]
+
+    # Filenames
+
+    tempdir = tempfile.gettempdir()
+    in_name       = sys.argv[2]
+    fixed_name    = tempdir + os.sep + "test-fixed.dmd"
+    filtered_name = tempdir + os.sep + "test-filtered.dmd"
+    diff_name     = tempdir + os.sep + "test-diff.dmd"
+    expected_name = srcdir + os.sep + \
+                    "memory/replace/dmd/test-expected.dmd"
+
+    # Fix stack traces
+
+    print("fixing output to", fixed_name)
+
+    sysname = platform.system()
+    if sysname == "Linux":
+        fix = srcdir + os.sep + "tools/rb/fix-linux-stack.pl"
+    elif sysname == "Darwin":
+        fix = srcdir + os.sep + "tools/rb/fix_macosx_stack.py"
+    else:
+        print("unhandled platform: " + sysname, file=sys.stderr)
+        sys.exit(1)
+
+    subprocess.call(fix, stdin=open(in_name, "r"),
+                         stdout=open(fixed_name, "w"))
+
+    # Filter output
+
+    # In stack trace records we filter out all stack frames that contain a
+    # function whose name doesn't begin with "RunTestMode".  And the remaining
+    # ones have their line numbers omitted, unfortunately, because they are
+    # often off by one or two and this can vary between builds (e.g. debug vs
+    # non-debug).
+    #
+    # As for stack frame records, we complete eliminate all those that contain
+    # a function whose name doesn't begin with "RunTestMode", because such
+    # stack frame records are highly dependent on the exact forms of stack
+    # traces.
+
+    print("filtering output to", filtered_name)
+
+    with open(fixed_name, "r") as fin, \
+         open(filtered_name, "w") as fout:
+
+        test_frame_re = re.compile(r".*(RunTestMode\w*).*(DMD.cpp)")
+
+        for line in fin:
+            if re.match(r" (Allocated at|Reported( again)? at)", line):
+                # It's a stack trace record.
+                print(line, end='', file=fout)
+
+                # Filter the stack trace -- only show RunTestMode* frames.
+                for frame in fin:
+                    if re.match(r"   ", frame):
+                        m = test_frame_re.match(frame)
+                        if m:
+                            print("   ...", m.group(1), "...", m.group(2),
+                                  file=fout)
+                    else:
+                        # We're past the stack trace.
+                        print(frame, end='', file=fout)
+                        break
+
+            elif re.search("in stack frame record", line):
+                # Stack frame record.  Get the whole thing (we know how many
+                # lines it has).
+                line2 = fin.next()
+                line3 = fin.next()
+                line4 = fin.next()
+                frame = fin.next()
+                line6 = fin.next()
+                m = test_frame_re.match(frame)
+                if m:
+                    # This is a stack frame record from RunTestMode* -- print
+                    # it, obscuring record numbers (which vary unpredictably).
+                    print(re.sub(r"record \d+ of \d+", "record M of N", line),
+                          end='', file=fout)
+                    print(line2, end='', file=fout)
+                    print(line3, end='', file=fout)
+                    print(line4, end='', file=fout)
+                    print("   ...", m.group(1), "...", m.group(2), file=fout)
+                    print(line6, end='', file=fout)
+
+            else:
+                # Some line that needs no special handling.  Copy it through.
+                print(line, end='', file=fout)
+
+    # Compare with expected output
+
+    print("diffing output to", diff_name)
+
+    ret = subprocess.call(["diff", "-u", filtered_name, expected_name],
+                          stdout=open(diff_name, "w"))
+
+    if ret == 0:
+        print("test PASSED")
+    else:
+        print("test FAILED (did you remember to run this script and Firefox "
+              "in the same directory?)")
+
+
+if __name__ == "__main__":
+    main()
--- a/memory/replace/dmd/test-expected.dmd
+++ b/memory/replace/dmd/test-expected.dmd
@@ -13,22 +13,34 @@ Twice-reported stack trace records
 
 ------------------------------------------------------------------
 Unreported stack trace records
 ------------------------------------------------------------------
 
 (none)
 
 ------------------------------------------------------------------
+Unreported stack frame records
+------------------------------------------------------------------
+
+(none)
+
+------------------------------------------------------------------
 Once-reported stack trace records
 ------------------------------------------------------------------
 
 (none)
 
 ------------------------------------------------------------------
+Once-reported stack frame records
+------------------------------------------------------------------
+
+(none)
+
+------------------------------------------------------------------
 Summary
 ------------------------------------------------------------------
 
 Total:                     0 bytes (100.00%) in       0 blocks (100.00%)
 Unreported:                0 bytes (  0.00%) in       0 blocks (  0.00%)
 Once-reported:             0 bytes (  0.00%) in       0 blocks (  0.00%)
 Twice-reported:            0 bytes (  0.00%) in       0 blocks (  0.00%)
 
@@ -42,190 +54,264 @@ Sample-below size = 1
 ------------------------------------------------------------------
 Twice-reported stack trace records
 ------------------------------------------------------------------
 
 Twice-reported: 1 block in stack trace record 1 of 4
  80 bytes (79 requested / 1 slop)
  0.53% of the heap (0.53% cumulative);  29.41% of twice-reported (29.41% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Twice-reported: 1 block in stack trace record 2 of 4
  80 bytes (78 requested / 2 slop)
  0.53% of the heap (1.05% cumulative);  29.41% of twice-reported (58.82% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Twice-reported: 1 block in stack trace record 3 of 4
  80 bytes (77 requested / 3 slop)
  0.53% of the heap (1.58% cumulative);  29.41% of twice-reported (88.24% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Twice-reported: 1 block in stack trace record 4 of 4
  32 bytes (30 requested / 2 slop)
  0.21% of the heap (1.79% cumulative);  11.76% of twice-reported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Unreported stack trace records
 ------------------------------------------------------------------
 
 Unreported: 1 block in stack trace record 1 of 4
  4,096 bytes (1 requested / 4,095 slop)
  27.00% of the heap (27.00% cumulative);  76.88% of unreported (76.88% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 9 blocks in stack trace record 2 of 4
  1,008 bytes (900 requested / 108 slop)
  6.65% of the heap (33.65% cumulative);  18.92% of unreported (95.80% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 2 blocks in stack trace record 3 of 4
  112 bytes (112 requested / 0 slop)
  0.74% of the heap (34.39% cumulative);  2.10% of unreported (97.90% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 2 blocks in stack trace record 4 of 4
  112 bytes (112 requested / 0 slop)
  0.74% of the heap (35.13% cumulative);  2.10% of unreported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
+
+------------------------------------------------------------------
+Unreported stack frame records
+------------------------------------------------------------------
+
+Unreported: 1 block from 1 stack trace record in stack frame record M of N
+ 4,096 bytes (1 requested / 4,095 slop)
+ 27.00% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 9 blocks from 1 stack trace record in stack frame record M of N
+ 1,008 bytes (900 requested / 108 slop)
+ 6.65% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 2 blocks from 1 stack trace record in stack frame record M of N
+ 112 bytes (112 requested / 0 slop)
+ 0.74% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 2 blocks from 1 stack trace record in stack frame record M of N
+ 112 bytes (112 requested / 0 slop)
+ 0.74% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Once-reported stack trace records
 ------------------------------------------------------------------
 
 Once-reported: 1 block in stack trace record 1 of 11
  8,192 bytes (4,097 requested / 4,095 slop)
  54.01% of the heap (54.01% cumulative);  85.62% of once-reported (85.62% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 2 of 11
  512 bytes (512 requested / 0 slop)
  3.38% of the heap (57.38% cumulative);  5.35% of once-reported (90.97% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 2 blocks in stack trace record 3 of 11
  240 bytes (240 requested / 0 slop)
  1.58% of the heap (58.97% cumulative);  2.51% of once-reported (93.48% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 2 blocks in stack trace record 4 of 11
  240 bytes (240 requested / 0 slop)
  1.58% of the heap (60.55% cumulative);  2.51% of once-reported (95.99% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 5 of 11
  96 bytes (96 requested / 0 slop)
  0.63% of the heap (61.18% cumulative);  1.00% of once-reported (96.99% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 6 of 11
  96 bytes (96 requested / 0 slop)
  0.63% of the heap (61.81% cumulative);  1.00% of once-reported (97.99% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 7 of 11
  80 bytes (80 requested / 0 slop)
  0.53% of the heap (62.34% cumulative);  0.84% of once-reported (98.83% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 8 of 11
  80 bytes (80 requested / 0 slop)
  0.53% of the heap (62.87% cumulative);  0.84% of once-reported (99.67% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 9 of 11
  16 bytes (10 requested / 6 slop)
  0.11% of the heap (62.97% cumulative);  0.17% of once-reported (99.83% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 10 of 11
  8 bytes (0 requested / 8 slop)
  0.05% of the heap (63.03% cumulative);  0.08% of once-reported (99.92% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 11 of 11
  8 bytes (0 requested / 8 slop)
  0.05% of the heap (63.08% cumulative);  0.08% of once-reported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
+
+------------------------------------------------------------------
+Once-reported stack frame records
+------------------------------------------------------------------
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 8,192 bytes (4,097 requested / 4,095 slop)
+ 54.01% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 512 bytes (512 requested / 0 slop)
+ 3.38% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 4 blocks from 3 stack trace records in stack frame record M of N
+ 416 bytes (416 requested / 0 slop)
+ 2.74% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 4 blocks from 3 stack trace records in stack frame record M of N
+ 416 bytes (416 requested / 0 slop)
+ 2.74% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 16 bytes (10 requested / 6 slop)
+ 0.11% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 8 bytes (0 requested / 8 slop)
+ 0.05% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 8 bytes (0 requested / 8 slop)
+ 0.05% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Summary
 ------------------------------------------------------------------
 
 Total:                15,168 bytes (100.00%) in      31 blocks (100.00%)
 Unreported:            5,328 bytes ( 35.13%) in      14 blocks ( 45.16%)
 Once-reported:         9,568 bytes ( 63.08%) in      13 blocks ( 41.94%)
@@ -241,97 +327,147 @@ Sample-below size = 1
 ------------------------------------------------------------------
 Twice-reported stack trace records
 ------------------------------------------------------------------
 
 Twice-reported: 1 block in stack trace record 1 of 2
  80 bytes (77 requested / 3 slop)
  2.82% of the heap (2.82% cumulative);  90.91% of twice-reported (90.91% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Twice-reported: 1 block in stack trace record 2 of 2
  8 bytes (0 requested / 8 slop)
  0.28% of the heap (3.10% cumulative);  9.09% of twice-reported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported again at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Unreported stack trace records
 ------------------------------------------------------------------
 
 Unreported: 9 blocks in stack trace record 1 of 3
  1,008 bytes (900 requested / 108 slop)
  35.49% of the heap (35.49% cumulative);  48.84% of unreported (48.84% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 6 blocks in stack trace record 2 of 3
  528 bytes (528 requested / 0 slop)
  18.59% of the heap (54.08% cumulative);  25.58% of unreported (74.42% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 6 blocks in stack trace record 3 of 3
  528 bytes (528 requested / 0 slop)
  18.59% of the heap (72.68% cumulative);  25.58% of unreported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
+
+------------------------------------------------------------------
+Unreported stack frame records
+------------------------------------------------------------------
+
+Unreported: 9 blocks from 1 stack trace record in stack frame record M of N
+ 1,008 bytes (900 requested / 108 slop)
+ 35.49% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 6 blocks from 1 stack trace record in stack frame record M of N
+ 528 bytes (528 requested / 0 slop)
+ 18.59% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 6 blocks from 1 stack trace record in stack frame record M of N
+ 528 bytes (528 requested / 0 slop)
+ 18.59% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Once-reported stack trace records
 ------------------------------------------------------------------
 
 Once-reported: 1 block in stack trace record 1 of 4
  512 bytes (512 requested / 0 slop)
  18.03% of the heap (18.03% cumulative);  74.42% of once-reported (74.42% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 2 of 4
  80 bytes (79 requested / 1 slop)
  2.82% of the heap (20.85% cumulative);  11.63% of once-reported (86.05% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 3 of 4
  80 bytes (78 requested / 2 slop)
  2.82% of the heap (23.66% cumulative);  11.63% of once-reported (97.67% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Once-reported: 1 block in stack trace record 4 of 4
  16 bytes (10 requested / 6 slop)
  0.56% of the heap (24.23% cumulative);  2.33% of once-reported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
  Reported at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
+
+------------------------------------------------------------------
+Once-reported stack frame records
+------------------------------------------------------------------
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 512 bytes (512 requested / 0 slop)
+ 18.03% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 80 bytes (79 requested / 1 slop)
+ 2.82% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 80 bytes (78 requested / 2 slop)
+ 2.82% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Once-reported: 1 block from 1 stack trace record in stack frame record M of N
+ 16 bytes (10 requested / 6 slop)
+ 0.56% of the heap;  0.00% of once-reported
+ PC is
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Summary
 ------------------------------------------------------------------
 
 Total:                 2,840 bytes (100.00%) in      27 blocks (100.00%)
 Unreported:            2,064 bytes ( 72.68%) in      21 blocks ( 77.78%)
 Once-reported:           688 bytes ( 24.23%) in       4 blocks ( 14.81%)
@@ -353,61 +489,113 @@ Twice-reported stack trace records
 ------------------------------------------------------------------
 Unreported stack trace records
 ------------------------------------------------------------------
 
 Unreported: ~4 blocks in stack trace record 1 of 7
  ~512 bytes (~512 requested / ~0 slop)
  35.96% of the heap (35.96% cumulative);  35.96% of unreported (35.96% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 1 block in stack trace record 2 of 7
  256 bytes (256 requested / 0 slop)
  17.98% of the heap (53.93% cumulative);  17.98% of unreported (53.93% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 1 block in stack trace record 3 of 7
  144 bytes (144 requested / 0 slop)
  10.11% of the heap (64.04% cumulative);  10.11% of unreported (64.04% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: 1 block in stack trace record 4 of 7
  128 bytes (128 requested / 0 slop)
  8.99% of the heap (73.03% cumulative);  8.99% of unreported (73.03% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: ~1 block in stack trace record 5 of 7
  ~128 bytes (~128 requested / ~0 slop)
  8.99% of the heap (82.02% cumulative);  8.99% of unreported (82.02% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: ~1 block in stack trace record 6 of 7
  ~128 bytes (~128 requested / ~0 slop)
  8.99% of the heap (91.01% cumulative);  8.99% of unreported (91.01% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
 
 Unreported: ~1 block in stack trace record 7 of 7
  ~128 bytes (~128 requested / ~0 slop)
  8.99% of the heap (100.00% cumulative);  8.99% of unreported (100.00% cumulative)
  Allocated at
-   (stack omitted due to test mode)
+   ... RunTestMode ... DMD.cpp
+
+------------------------------------------------------------------
+Unreported stack frame records
+------------------------------------------------------------------
+
+Unreported: ~4 blocks from ~1 stack trace record in stack frame record M of N
+ ~512 bytes (~512 requested / ~0 slop)
+ 35.96% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 1 block from 1 stack trace record in stack frame record M of N
+ 256 bytes (256 requested / 0 slop)
+ 17.98% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 1 block from 1 stack trace record in stack frame record M of N
+ 144 bytes (144 requested / 0 slop)
+ 10.11% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: 1 block from 1 stack trace record in stack frame record M of N
+ 128 bytes (128 requested / 0 slop)
+ 8.99% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: ~1 block from ~1 stack trace record in stack frame record M of N
+ ~128 bytes (~128 requested / ~0 slop)
+ 8.99% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: ~1 block from ~1 stack trace record in stack frame record M of N
+ ~128 bytes (~128 requested / ~0 slop)
+ 8.99% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
+
+Unreported: ~1 block from ~1 stack trace record in stack frame record M of N
+ ~128 bytes (~128 requested / ~0 slop)
+ 8.99% of the heap;  0.00% of unreported
+ PC is
+   ... RunTestMode ... DMD.cpp
 
 ------------------------------------------------------------------
 Once-reported stack trace records
 ------------------------------------------------------------------
 
 (none)
 
 ------------------------------------------------------------------
+Once-reported stack frame records
+------------------------------------------------------------------
+
+(none)
+
+------------------------------------------------------------------
 Summary
 ------------------------------------------------------------------
 
 Total:                ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
 Unreported:           ~1,424 bytes (100.00%) in     ~10 blocks (100.00%)
 Once-reported:            ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
 Twice-reported:           ~0 bytes (  0.00%) in      ~0 blocks (  0.00%)
 
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -22,16 +22,18 @@
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
 #ifdef MOZ_PEERCONNECTION
 #include "mtransport/runnable_utils.h"
 #endif
+
+#define DATACHANNEL_LOG(args) LOG(args)
 #include "DataChannel.h"
 #include "DataChannelProtocol.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo*
 GetDataChannelLog()
 {
   static PRLogModuleInfo* sLog;
@@ -147,19 +149,22 @@ DataChannelConnection::DataChannelConnec
   mRemotePort = 0;
   mDeferTimeout = 10;
   mTimerRunning = false;
   LOG(("Constructor DataChannelConnection=%p, listener=%p", this, mListener));
 }
 
 DataChannelConnection::~DataChannelConnection()
 {
+  LOG(("Deleting DataChannelConnection %p", (void *) this));
   // This may die on the MainThread, or on the STS thread
   MOZ_ASSERT(mState == CLOSED);
   MOZ_ASSERT(!mMasterSocket);
+  MOZ_ASSERT(mPending.GetSize() == 0);
+  // Already disconnected from sigslot/mTransportFlow
 }
 
 void
 DataChannelConnection::Destroy()
 {
   // Though it's probably ok to do this and close the sockets;
   // if we really want it to do true clean shutdowns it can
   // create a dependant Internal object that would remain around
@@ -172,16 +177,28 @@ DataChannelConnection::Destroy()
   if (mMasterSocket)
     usrsctp_close(mMasterSocket);
 
   mSocket = nullptr;
   mMasterSocket = nullptr;
 
   // We can't get any more new callbacks from the SCTP library
   // All existing callbacks have refs to DataChannelConnection
+
+  // nsDOMDataChannel objects have refs to DataChannels that have refs to us
+
+  if (mTransportFlow) {
+    MOZ_ASSERT(mSTS);
+    MOZ_ASSERT(NS_IsMainThread());
+    RUN_ON_THREAD(mSTS, WrapRunnable(nsRefPtr<DataChannelConnection>(this),
+                                     &DataChannelConnection::disconnect_all),
+                  NS_DISPATCH_NORMAL);
+    // safe to do now from Mainthread per ekr
+    mTransportFlow = nullptr;
+  }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(DataChannelConnection,
                               nsITimerCallback)
 
 bool
 DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls)
 {
@@ -1502,18 +1519,21 @@ DataChannelConnection::HandleStreamReset
           // 2. We sent our own reset (CLOSING); either they crossed on the
           //    wire, or this is a response to our Reset.
           //    Go to CLOSED
           // 3. We've sent a open but haven't gotten a response yet (OPENING)
           //    I believe this is impossible, as we don't have an input stream yet.
 
           LOG(("Incoming: Channel %d outgoing/%d incoming closed, state %d",
                channel->mStreamOut, channel->mStreamIn, channel->mState));
-          MOZ_ASSERT(channel->mState == OPEN || channel->mState == CLOSING);
-          if (channel->mState == OPEN) {
+          MOZ_ASSERT(channel->mState == DataChannel::OPEN ||
+                     channel->mState == DataChannel::CLOSING ||
+                     channel->mState == DataChannel::WAITING_TO_OPEN);
+          if (channel->mState == DataChannel::OPEN ||
+              channel->mState == DataChannel::WAITING_TO_OPEN) {
             ResetOutgoingStream(channel->mStreamOut);
             NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                       DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
                                       channel));
             mStreamsOut[channel->mStreamOut] = nullptr;
           }
           mStreamsIn[channel->mStreamIn] = nullptr;
 
@@ -2005,16 +2025,20 @@ DataChannelConnection::Close(uint16_t st
 {
   nsRefPtr<DataChannel> channel; // make sure it doesn't go away on us
 
   MutexAutoLock lock(mLock);
   channel = FindChannelByStreamOut(streamOut);
   if (channel) {
     LOG(("Connection %p/Channel %p: Closing stream %d",
          (void *) channel->mConnection.get(), (void *) channel.get(), streamOut));
+    if (channel->mState == CLOSED || channel->mState == CLOSING) {
+      LOG(("Channel already closing/closed (%d)", channel->mState));
+      return;
+    }
     channel->mBufferedData.Clear();
     if (channel->mStreamOut != INVALID_STREAM)
       ResetOutgoingStream(channel->mStreamOut);
     SendOutgoingStreamReset();
     channel->mState = CLOSING;
   } else {
     LOG(("!!!? no channel when closing stream %d?",streamOut));
   }
@@ -2034,30 +2058,34 @@ void DataChannelConnection::CloseAll()
   for (uint32_t i = 0; i < mStreamsOut.Length(); ++i) {
     if (mStreamsOut[i]) {
       mStreamsOut[i]->Close();
     }
   }
 
   // Clean up any pending opens for channels
   nsRefPtr<DataChannel> channel;
-  while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(mPending.PopFront()))))
+  while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(mPending.PopFront())))) {
+    LOG(("closing pending channel %p, stream %d", channel.get(), channel->mStreamOut));
     channel->Close(); // also releases the ref on each iteration
+  }
 }
 
 DataChannel::~DataChannel()
 {
   if (mConnection)
     Close();
 }
 
 // Used when disconnecting from the DataChannelConnection
 void
 DataChannel::Destroy()
 {
+  ENSURE_DATACONNECTION;
+
   LOG(("Destroying Data channel %d/%d", mStreamOut, mStreamIn));
   MOZ_ASSERT_IF(mStreamOut != INVALID_STREAM,
                 !mConnection->FindChannelByStreamOut(mStreamOut));
   MOZ_ASSERT_IF(mStreamIn != INVALID_STREAM,
                 !mConnection->FindChannelByStreamIn(mStreamIn));
   mStreamIn  = INVALID_STREAM;
   mStreamOut = INVALID_STREAM;
   mState = CLOSED;
@@ -2067,31 +2095,34 @@ DataChannel::Destroy()
 void
 DataChannel::Close()
 {
   if (mState == CLOSING || mState == CLOSED ||
       mStreamOut == INVALID_STREAM) {
     return;
   }
   mState = CLOSING;
+  ENSURE_DATACONNECTION;
   mConnection->Close(mStreamOut);
 }
 
 void
 DataChannel::SetListener(DataChannelListener *aListener, nsISupports *aContext)
 {
   MOZ_ASSERT(!mListener); // only should be set once, avoids races w/o locking
   mContext = aContext;
   mListener = aListener;
 }
 
 // May be called from another (i.e. Main) thread!
 void
 DataChannel::AppReady()
 {
+  ENSURE_DATACONNECTION;
+
   MutexAutoLock lock(mConnection->mLock);
 
   mReady = true;
   if (mState == WAITING_TO_OPEN) {
     mState = OPEN;
     NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                               DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, mConnection,
                               this));
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -25,16 +25,20 @@
 #include "DataChannelProtocol.h"
 #ifdef SCTP_DTLS_SUPPORTED
 #include "mtransport/sigslot.h"
 #include "mtransport/transportflow.h"
 #include "mtransport/transportlayer.h"
 #include "mtransport/transportlayerprsock.h"
 #endif
 
+#ifndef DATACHANNEL_LOG
+#define DATACHANNEL_LOG(args)
+#endif
+
 #ifndef EALREADY
 #define EALREADY  WSAEALREADY
 #endif
 
 extern "C" {
   struct socket;
   struct sctp_rcvinfo;
 }
@@ -97,17 +101,17 @@ public:
 
     // Called when a the connection is open
     virtual void NotifyConnection() = 0;
 
     // Called when a the connection is lost/closed
     virtual void NotifyClosedConnection() = 0;
 
     // Called when a new DataChannel has been opened by the other side.
-    virtual void NotifyDataChannel(DataChannel *channel) = 0;
+    virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0;
   };
 
   DataChannelConnection(DataConnectionListener *listener);
   virtual ~DataChannelConnection();
 
   bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls);
   void Destroy(); // So we can spawn refs tied to runnables in shutdown
 
@@ -122,17 +126,17 @@ public:
   bool ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
 #endif
 
   typedef enum {
     RELIABLE=0,
     PARTIAL_RELIABLE_REXMIT = 1,
     PARTIAL_RELIABLE_TIMED = 2
   } Type;
-    
+
   already_AddRefed<DataChannel> Open(const nsACString& label,
                                      Type type, bool inOrder,
                                      uint32_t prValue,
                                      DataChannelListener *aListener,
                                      nsISupports *aContext);
 
   void Close(uint16_t stream);
   void CloseAll();
@@ -143,18 +147,18 @@ public:
     }
   int32_t SendBinaryMsg(uint16_t stream, const nsACString &aMsg)
     {
       return SendMsgCommon(stream, aMsg, true);
     }
   int32_t SendBlob(uint16_t stream, nsIInputStream *aBlob);
 
   // Called on data reception from the SCTP library
-  // must(?) be public so my c->c++ tramploine can call it
-  int ReceiveCallback(struct socket* sock, void *data, size_t datalen, 
+  // must(?) be public so my c->c++ trampoline can call it
+  int ReceiveCallback(struct socket* sock, void *data, size_t datalen,
                       struct sctp_rcvinfo rcv, int32_t flags);
 
   // Find out state
   enum {
     CONNECTING = 0U,
     OPEN = 1U,
     CLOSING = 2U,
     CLOSED = 3U
@@ -253,28 +257,34 @@ private:
   nsCOMPtr<nsITimer> mDeferredTimer;
   uint32_t mDeferTimeout; // in ms
   bool mTimerRunning;
 
   // Thread used for connections
   nsCOMPtr<nsIThread> mConnectThread;
 };
 
+#define ENSURE_DATACONNECTION \
+  do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return; } } while (0)
+
+#define ENSURE_DATACONNECTION_RET(x) \
+  do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return (x); } } while (0)
+
 class DataChannel {
 public:
   enum {
     CONNECTING = 0U,
     OPEN = 1U,
     CLOSING = 2U,
     CLOSED = 3U,
     WAITING_TO_OPEN = 4U
   };
 
   DataChannel(DataChannelConnection *connection,
-              uint16_t streamOut, uint16_t streamIn, 
+              uint16_t streamOut, uint16_t streamIn,
               uint16_t state,
               const nsACString& label,
               uint16_t policy, uint32_t value,
               uint32_t flags,
               DataChannelListener *aListener,
               nsISupports *aContext)
     : mListener(aListener)
     , mConnection(connection)
@@ -301,34 +311,40 @@ public:
 
   // Set the listener (especially for channels created from the other side)
   // Note: The Listener and Context should only be set once
   void SetListener(DataChannelListener *aListener, nsISupports *aContext);
 
   // Send a string
   bool SendMsg(const nsACString &aMsg)
     {
+      ENSURE_DATACONNECTION_RET(false);
+
       if (mStreamOut != INVALID_STREAM)
         return (mConnection->SendMsg(mStreamOut, aMsg) > 0);
       else
         return false;
     }
 
   // Send a binary message (TypedArray)
   bool SendBinaryMsg(const nsACString &aMsg)
     {
+      ENSURE_DATACONNECTION_RET(false);
+
       if (mStreamOut != INVALID_STREAM)
         return (mConnection->SendBinaryMsg(mStreamOut, aMsg) > 0);
       else
         return false;
     }
 
   // Send a binary blob
   bool SendBinaryStream(nsIInputStream *aBlob, uint32_t msgLen)
     {
+      ENSURE_DATACONNECTION_RET(false);
+
       if (mStreamOut != INVALID_STREAM)
         return (mConnection->SendBlob(mStreamOut, aBlob) > 0);
       else
         return false;
     }
 
   uint16_t GetType() { return mPrPolicy; }
 
@@ -396,17 +412,17 @@ public:
 
   DataChannelOnMessageAvailable(int32_t     aType,
                                 DataChannelConnection *aConnection,
                                 DataChannel *aChannel,
                                 nsCString   &aData,  // XXX this causes inefficiency
                                 int32_t     aLen)
     : mType(aType),
       mChannel(aChannel),
-      mConnection(aConnection), 
+      mConnection(aConnection),
       mData(aData),
       mLen(aLen) {}
 
   DataChannelOnMessageAvailable(int32_t     aType,
                                 DataChannel *aChannel)
     : mType(aType),
       mChannel(aChannel) {}
   // XXX is it safe to leave mData/mLen uninitialized?  This should only be
@@ -456,17 +472,18 @@ public:
         break;
       case ON_CHANNEL_OPEN:
         mChannel->mListener->OnChannelConnected(mChannel->mContext);
         break;
       case ON_CHANNEL_CLOSED:
         mChannel->mListener->OnChannelClosed(mChannel->mContext);
         break;
       case ON_CHANNEL_CREATED:
-        mConnection->mListener->NotifyDataChannel(mChannel);
+        // important to give it an already_AddRefed pointer!
+        mConnection->mListener->NotifyDataChannel(mChannel.forget());
         break;
       case ON_CONNECTION:
         if (mResult) {
           mConnection->mListener->NotifyConnection();
         }
         mConnection->mConnectThread = nullptr; // kill the connection thread
         break;
       case ON_DISCONNECTED:
--- a/testing/marionette/client/marionette/runtests.py
+++ b/testing/marionette/client/marionette/runtests.py
@@ -207,22 +207,22 @@ class MarionetteTestRunner(object):
         self.marionette = None
         self.logcat_dir = logcat_dir
         self.perfrequest = None
         self.xml_output = xml_output
         self.repeat = repeat
         self.perf = perf
         self.perfserv = perfserv
         self.gecko_path = gecko_path
-        self.testvars = None
+        self.testvars = {}
         self.tree = tree
         self.load_early = load_early
         self.device = device
 
-        if testvars is not None:
+        if testvars:
             if not os.path.exists(testvars):
                 raise Exception('--testvars file does not exist')
 
             import json
             with open(testvars) as f:
                 self.testvars = json.loads(f.read())
 
         # set up test handlers
@@ -236,16 +236,17 @@ class MarionetteTestRunner(object):
             self.logger.setLevel(logging.INFO)
             self.logger.addHandler(logging.StreamHandler())
 
         if self.logcat_dir:
             if not os.access(self.logcat_dir, os.F_OK):
                 os.mkdir(self.logcat_dir)
 
         # for XML output
+        self.testvars['xml_output'] = self.xml_output
         self.results = []
 
     def reset_test_stats(self):
         self.passed = 0
         self.failed = 0
         self.todo = 0
         self.failures = []
         self.perfrequest = None
@@ -362,16 +363,18 @@ class MarionetteTestRunner(object):
         if self.perfrequest and options.perf:
             try:
                 self.perfrequest.submit()
             except Exception, e:
                 print "Could not submit to datazilla"
                 print e
 
         if self.xml_output:
+            if not os.path.exists(os.path.dirname(self.xml_output)):
+                os.makedirs(os.path.dirname(self.xml_output))
             with open(self.xml_output, 'w') as f:
                 f.write(self.generate_xml(self.results))
 
         if self.marionette.instance:
             self.marionette.instance.close()
             self.marionette.instance = None
         del self.marionette
 
--- a/toolkit/components/url-classifier/Makefile.in
+++ b/toolkit/components/url-classifier/Makefile.in
@@ -11,16 +11,17 @@ VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = url-classifier
 LIBRARY_NAME = urlclassifier_s
 XPIDL_MODULE = url-classifier
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
+FAIL_ON_WARNINGS = 1
 
 # Normally the "client ID" sent in updates is appinfo.name, but for
 # official Firefox releases from Mozilla we use a special identifier.
 ifdef MOZILLA_OFFICIAL
 ifdef MOZ_PHOENIX
 DEFINES += -DUSE_HISTORIC_SAFEBROWSING_ID=1
 endif
 endif
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -25,16 +25,17 @@
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 #include "nsProxyRelease.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "prlog.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "Entries.h"
 #include "mozilla/Attributes.h"
@@ -1501,17 +1502,17 @@ nsUrlClassifierDBService::Shutdown()
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
     prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
     prefs->RemoveObserver(GETHASH_TABLES_PREF, this);
     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
   }
 
-  nsresult rv;
+  DebugOnly<nsresult> rv;
   // First close the db connection.
   if (mWorker) {
     rv = mWorkerProxy->CancelUpdate();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
 
     rv = mWorkerProxy->CloseDb();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
   }
--- a/toolkit/crashreporter/tools/symbolstore.py
+++ b/toolkit/crashreporter/tools/symbolstore.py
@@ -24,16 +24,18 @@ import sys
 import platform
 import os
 import re
 import shutil
 import textwrap
 import fnmatch
 import subprocess
 import urlparse
+import multiprocessing
+import collections
 from optparse import OptionParser
 from xml.dom.minidom import parse
 
 # Utility classes
 
 class VCSFileInfo:
     """ A base class for version-controlled file information. Ensures that the
         following attributes are generated only once (successfully):
@@ -298,31 +300,48 @@ def SourceIndex(fileStream, outputPath, 
     pdbStreamFile.write('''SRCSRV: ini ------------------------------------------------\r\nVERSION=2\r\nINDEXVERSION=2\r\nVERCTRL=http\r\nSRCSRV: variables ------------------------------------------\r\nHGSERVER=''')
     pdbStreamFile.write(vcs_root)
     pdbStreamFile.write('''\r\nSRCSRVVERCTRL=http\r\nHTTP_EXTRACT_TARGET=%hgserver%/raw-file/%var3%/%var2%\r\nSRCSRVTRG=%http_extract_target%\r\nSRCSRV: source files ---------------------------------------\r\n''')
     pdbStreamFile.write(fileStream) # can't do string interpolation because the source server also uses this and so there are % in the above
     pdbStreamFile.write("SRCSRV: end ------------------------------------------------\r\n\n")
     pdbStreamFile.close()
     return result
 
+def WorkerInitializer(cls, lock):
+    """Windows worker processes won't have run GlobalInit, and due to a lack of fork(),
+    won't inherit the class variables from the parent. The only one they need is the lock,
+    so we run an initializer to set it. Redundant but harmless on other platforms."""
+    cls.lock = lock
+
+def StartProcessFilesWork(dumper, files, arch_num, arch, vcs_root, after, after_arg):
+    """multiprocessing can't handle methods as Process targets, so we define
+    a simple wrapper function around the work method."""
+    return dumper.ProcessFilesWork(files, arch_num, arch, vcs_root, after, after_arg)
+
 class Dumper:
     """This class can dump symbols from a file with debug info, and
     store the output in a directory structure that is valid for use as
     a Breakpad symbol server.  Requires a path to a dump_syms binary--
     |dump_syms| and a directory to store symbols in--|symbol_path|.
     Optionally takes a list of processor architectures to process from
     each debug file--|archs|, the full path to the top source
     directory--|srcdir|, for generating relative source file names,
     and an option to copy debug info files alongside the dumped
     symbol files--|copy_debug|, mostly useful for creating a
     Microsoft Symbol Server from the resulting output.
 
     You don't want to use this directly if you intend to call
     ProcessDir.  Instead, call GetPlatformSpecificDumper to
-    get an instance of a subclass."""
+    get an instance of a subclass.
+ 
+    Processing is performed asynchronously via worker processes; in
+    order to wait for processing to finish and cleanup correctly, you
+    must call Finish after all Process/ProcessDir calls have been made.
+    You must also call Dumper.GlobalInit before creating or using any
+    instances."""
     def __init__(self, dump_syms, symbol_path,
                  archs=None,
                  srcdirs=[],
                  copy_debug=False,
                  vcsinfo=False,
                  srcsrv=False,
                  exclude=[],
                  repo_manifest=None):
@@ -337,16 +356,69 @@ class Dumper:
         self.srcdirs = [os.path.normpath(a) for a in srcdirs]
         self.copy_debug = copy_debug
         self.vcsinfo = vcsinfo
         self.srcsrv = srcsrv
         self.exclude = exclude[:]
         if repo_manifest:
             self.parse_repo_manifest(repo_manifest)
 
+        # book-keeping to keep track of our jobs and the cleanup work per file tuple
+        self.files_record = {}
+        self.jobs_record = collections.defaultdict(int)
+
+    @classmethod
+    def GlobalInit(cls, module=multiprocessing):
+        """Initialize the class globals for the multiprocessing setup; must
+        be called before any Dumper instances are created and used. Test cases
+        may pass in a different module to supply Manager and Pool objects,
+        usually multiprocessing.dummy."""
+        num_cpus = module.cpu_count()
+        if num_cpus is None:
+            # assume a dual core machine if we can't find out for some reason
+            # probably better on single core anyway due to I/O constraints
+            num_cpus = 2
+
+        # have to create any locks etc before the pool
+        cls.manager = module.Manager()
+        cls.jobs_condition = Dumper.manager.Condition()
+        cls.lock = Dumper.manager.RLock()
+        cls.pool = module.Pool(num_cpus, WorkerInitializer, (cls, cls.lock))
+
+    def JobStarted(self, file_key):
+        """Increments the number of submitted jobs for the specified key file,
+        defined as the original file we processed; note that a single key file
+        can generate up to 1 + len(self.archs) jobs in the Mac case."""
+        with Dumper.jobs_condition:
+            self.jobs_record[file_key] += 1
+            Dumper.jobs_condition.notify_all()
+
+    def JobFinished(self, file_key):
+        """Decrements the number of submitted jobs for the specified key file,
+        defined as the original file we processed; once the count is back to 0,
+        remove the entry from our record."""
+        with Dumper.jobs_condition:
+            self.jobs_record[file_key] -= 1
+
+            if self.jobs_record[file_key] == 0:
+                del self.jobs_record[file_key]
+
+            Dumper.jobs_condition.notify_all()
+
+    def output(self, dest, output_str):
+        """Writes |output_str| to |dest|, holding |lock|;
+        terminates with a newline."""
+        with Dumper.lock:
+            dest.write(output_str + "\n")
+            dest.flush()
+
+    def output_pid(self, dest, output_str):
+        """Debugging output; prepends the pid to the string."""
+        self.output(dest, "%d: %s" % (os.getpid(), output_str))
+
     def parse_repo_manifest(self, repo_manifest):
         """
         Parse an XML manifest of repository info as produced
         by the `repo manifest -r` command.
         """
         doc = parse(repo_manifest)
         if doc.firstChild.tagName != "manifest":
             return
@@ -412,51 +484,89 @@ class Dumper:
     # This is a no-op except on Win32
     def SourceServerIndexing(self, debug_file, guid, sourceFileStream, vcs_root):
         return ""
 
     # subclasses override this if they want to support this
     def CopyDebug(self, file, debug_file, guid):
         pass
 
+    def Finish(self, stop_pool=True):
+        """Wait for the expected number of jobs to be submitted, and then
+        wait for the pool to finish processing them. By default, will close
+        and clear the pool, but for testcases that need multiple runs, pass
+        stop_pool = False."""
+        with Dumper.jobs_condition:
+            while len(self.jobs_record) != 0:
+                Dumper.jobs_condition.wait()
+        if stop_pool:
+            Dumper.pool.close()
+            Dumper.pool.join()
+
     def Process(self, file_or_dir):
-        "Process a file or all the (valid) files in a directory."
+        """Process a file or all the (valid) files in a directory; processing is performed
+        asynchronously, and Finish must be called to wait for it complete and cleanup."""
         if os.path.isdir(file_or_dir) and not self.ShouldSkipDir(file_or_dir):
-            return self.ProcessDir(file_or_dir)
+            self.ProcessDir(file_or_dir)
         elif os.path.isfile(file_or_dir):
-            return self.ProcessFile(file_or_dir)
-        # maybe it doesn't exist?
-        return False
+            self.ProcessFiles((file_or_dir,))
 
     def ProcessDir(self, dir):
         """Process all the valid files in this directory.  Valid files
-        are determined by calling ShouldProcess."""
-        result = True
+        are determined by calling ShouldProcess; processing is performed
+        asynchronously, and Finish must be called to wait for it complete and cleanup."""
         for root, dirs, files in os.walk(dir):
             for d in dirs[:]:
                 if self.ShouldSkipDir(d):
                     dirs.remove(d)
             for f in files:
                 fullpath = os.path.join(root, f)
                 if self.ShouldProcess(fullpath):
-                    if not self.ProcessFile(fullpath):
-                        result = False
-        return result
+                    self.ProcessFiles((fullpath,))
+
+    def SubmitJob(self, file_key, func, args, callback):
+        """Submits a job to the pool of workers; increments the number of submitted jobs."""
+        self.JobStarted(file_key)
+        res = Dumper.pool.apply_async(func, args=args, callback=callback)
 
-    def ProcessFile(self, file):
-        """Dump symbols from this file into a symbol file, stored
-        in the proper directory structure in  |symbol_path|."""
-        print >> sys.stderr, "Processing file: %s" % file
-        sys.stderr.flush()
-        result = False
-        sourceFileStream = ''
+    def ProcessFilesFinished(self, res):
+        """Callback from multiprocesing when ProcessFilesWork finishes;
+        run the cleanup work, if any"""
+        self.JobFinished(res['files'][-1])
+        # only run the cleanup function once per tuple of files
+        self.files_record[res['files']] += 1
+        if self.files_record[res['files']] == len(self.archs):
+            del self.files_record[res['files']]
+            if res['after']:
+                res['after'](res['status'], res['after_arg'])
+
+    def ProcessFiles(self, files, after=None, after_arg=None):
+        """Dump symbols from these files into a symbol file, stored
+        in the proper directory structure in  |symbol_path|; processing is performed
+        asynchronously, and Finish must be called to wait for it complete and cleanup.
+        All files after the first are fallbacks in case the first file does not process
+        successfully; if it does, no other files will be touched."""
+        self.output_pid(sys.stderr, "Submitting jobs for files: %s" % str(files))
+
         # tries to get the vcs root from the .mozconfig first - if it's not set
         # the tinderbox vcs path will be assigned further down
         vcs_root = os.environ.get("SRCSRV_ROOT")
         for arch_num, arch in enumerate(self.archs):
+            self.files_record[files] = 0 # record that we submitted jobs for this tuple of files
+            self.SubmitJob(files[-1], StartProcessFilesWork, args=(self, files, arch_num, arch, vcs_root, after, after_arg), callback=self.ProcessFilesFinished)
+
+    def ProcessFilesWork(self, files, arch_num, arch, vcs_root, after, after_arg):
+        self.output_pid(sys.stderr, "Worker processing files: %s" % (files,))
+
+        # our result is a status, a cleanup function, an argument to that function, and the tuple of files we were called on
+        result = { 'status' : False, 'after' : after, 'after_arg' : after_arg, 'files' : files }
+
+        sourceFileStream = ''
+        for file in files:
+            # files is a tuple of files, containing fallbacks in case the first file doesn't process successfully
             try:
                 proc = subprocess.Popen([self.dump_syms] + arch.split() + [file],
                                         stdout=subprocess.PIPE)
                 module_line = proc.stdout.next()
                 if module_line.startswith("MODULE"):
                     # MODULE os cpu guid debug_file
                     (guid, debug_file) = (module_line.split())[3:5]
                     # strip off .pdb extensions, and append .sym
@@ -496,33 +606,36 @@ class Dumper:
                             if filename.startswith("hg"):
                                 (ver, checkout, source_file, revision) = filename.split(":", 3)
                                 sourceFileStream += sourcepath + "*" + source_file + '*' + revision + "\r\n"
                             f.write("FILE %s %s\n" % (index, filename))
                         else:
                             # pass through all other lines unchanged
                             f.write(line)
                             # we want to return true only if at least one line is not a MODULE or FILE line
-                            result = True
+                            result['status'] = True
                     f.close()
                     proc.wait()
                     # we output relative paths so callers can get a list of what
                     # was generated
-                    print rel_path
+                    self.output(sys.stdout, rel_path)
                     if self.srcsrv and vcs_root:
                         # add source server indexing to the pdb file
                         self.SourceServerIndexing(file, guid, sourceFileStream, vcs_root)
                     # only copy debug the first time if we have multiple architectures
                     if self.copy_debug and arch_num == 0:
                         self.CopyDebug(file, debug_file, guid)
             except StopIteration:
                 pass
-            except:
-                print >> sys.stderr, "Unexpected error: ", sys.exc_info()[0]
+            except e:
+                self.output(sys.stderr, "Unexpected error: %s" % (str(e),))
                 raise
+            if result['status']:
+                # we only need 1 file to work
+                break
         return result
 
 # Platform-specific subclasses.  For the most part, these just have
 # logic to determine what files to extract symbols from.
 
 class Dumper_Win32(Dumper):
     fixedFilenameCaseCache = {}
 
@@ -571,19 +684,19 @@ class Dumper_Win32(Dumper):
         compressed_file = os.path.splitext(full_path)[0] + ".pd_"
         # ignore makecab's output
         success = subprocess.call(["makecab.exe", "/D", "CompressionType=LZX", "/D",
                                    "CompressionMemory=21",
                                    full_path, compressed_file],
                                   stdout=open("NUL:","w"), stderr=subprocess.STDOUT)
         if success == 0 and os.path.exists(compressed_file):
             os.unlink(full_path)
-            print os.path.splitext(rel_path)[0] + ".pd_"
+            self.output(sys.stdout, os.path.splitext(rel_path)[0] + ".pd_")
         else:
-            print rel_path
+            self.output(sys.stdout, rel_path)
         
     def SourceServerIndexing(self, debug_file, guid, sourceFileStream, vcs_root):
         # Creates a .pdb.stream file in the mozilla\objdir to be used for source indexing
         debug_file = os.path.abspath(debug_file)
         streamFilename = debug_file + ".stream"
         stream_output_path = os.path.abspath(streamFilename)
         # Call SourceIndex to create the .stream file
         result = SourceIndex(sourceFileStream, stream_output_path, vcs_root)
@@ -620,17 +733,17 @@ class Dumper_Linux(Dumper):
             rel_path = os.path.join(debug_file,
                                     guid,
                                     debug_file + ".dbg")
             full_path = os.path.normpath(os.path.join(self.symbol_path,
                                                       rel_path))
             shutil.move(file_dbg, full_path)
             # gzip the shipped debug files
             os.system("gzip %s" % full_path)
-            print rel_path + ".gz"
+            self.output(sys.stdout, rel_path + ".gz")
         else:
             if os.path.isfile(file_dbg):
                 os.unlink(file_dbg)
 
 class Dumper_Solaris(Dumper):
     def RunFileCommand(self, file):
         """Utility function, returns the output of file(1)"""
         try:
@@ -645,16 +758,26 @@ class Dumper_Solaris(Dumper):
         file(1) reports as being ELF files.  It expects to find the file
         command in PATH."""
         if not Dumper.ShouldProcess(self, file):
             return False
         if file.endswith(".so") or os.access(file, os.X_OK):
             return self.RunFileCommand(file).startswith("ELF")
         return False
 
+def StartProcessFilesWorkMac(dumper, file):
+    """multiprocessing can't handle methods as Process targets, so we define
+    a simple wrapper function around the work method."""
+    return dumper.ProcessFilesWorkMac(file)
+
+def AfterMac(status, dsymbundle):
+    """Cleanup function to run on Macs after we process the file(s)."""
+    # CopyDebug will already have been run from Dumper.ProcessFiles
+    shutil.rmtree(dsymbundle)
+
 class Dumper_Mac(Dumper):
     def ShouldProcess(self, file):
         """This function will allow processing of files that are
         executable, or end with the .dylib extension, and additionally
         file(1) reports as being Mach-O files.  It expects to find the file
         command in PATH."""
         if not Dumper.ShouldProcess(self, file):
             return False
@@ -666,57 +789,70 @@ class Dumper_Mac(Dumper):
         """We create .dSYM bundles on the fly, but if someone runs
         buildsymbols twice, we should skip any bundles we created
         previously, otherwise we'll recurse into them and try to 
         dump the inner bits again."""
         if dir.endswith(".dSYM"):
             return True
         return False
 
-    def ProcessFile(self, file):
+    def ProcessFiles(self, files, after=None, after_arg=None):
+        # also note, files must be len 1 here, since we're the only ones
+        # that ever add more than one file to the list
+        self.output_pid(sys.stderr, "Submitting job for Mac pre-processing on file: %s" % (files[0]))
+        self.SubmitJob(files[0], StartProcessFilesWorkMac, args=(self, files[0]), callback=self.ProcessFilesMacFinished)
+
+    def ProcessFilesMacFinished(self, result):
+        if result['status']:
+            # kick off new jobs per-arch with our new list of files
+            Dumper.ProcessFiles(self, result['files'], after=AfterMac, after_arg=result['files'][0])
+        # only decrement jobs *after* that, since otherwise we'll remove the record for this file
+        self.JobFinished(result['files'][-1])
+
+    def ProcessFilesWorkMac(self, file):
         """dump_syms on Mac needs to be run on a dSYM bundle produced
         by dsymutil(1), so run dsymutil here and pass the bundle name
         down to the superclass method instead."""
+        self.output_pid(sys.stderr, "Worker running Mac pre-processing on file: %s" % (file,))
+
+        # our return is a status and a tuple of files to dump symbols for
+        # the extra files are fallbacks; as soon as one is dumped successfully, we stop
+        result = { 'status' : False, 'files' : None, 'file_key' : file }
         dsymbundle = file + ".dSYM"
         if os.path.exists(dsymbundle):
             shutil.rmtree(dsymbundle)
         # dsymutil takes --arch=foo instead of -a foo like everything else
         subprocess.call(["dsymutil"] + [a.replace('-a ', '--arch=') for a in self.archs if a]
                         + [file],
                         stdout=open("/dev/null","w"))
         if not os.path.exists(dsymbundle):
             # dsymutil won't produce a .dSYM for files without symbols
-            return False
-        res = Dumper.ProcessFile(self, dsymbundle)
-        # CopyDebug will already have been run from Dumper.ProcessFile
-        shutil.rmtree(dsymbundle)
+            result['status'] = False
+            return result
 
-        # fallback for DWARF-less binaries
-        if not res:
-            print >> sys.stderr, "Couldn't read DWARF symbols in: %s" % dsymbundle
-            res = Dumper.ProcessFile(self, file)
-
-        return res
+        result['status'] = True
+        result['files'] = (dsymbundle, file)
+        return result
 
     def CopyDebug(self, file, debug_file, guid):
-        """ProcessFile has already produced a dSYM bundle, so we should just
+        """ProcessFiles has already produced a dSYM bundle, so we should just
         copy that to the destination directory. However, we'll package it
         into a .tar.bz2 because the debug symbols are pretty huge, and
         also because it's a bundle, so it's a directory. |file| here is the
         dSYM bundle, and |debug_file| is the original filename."""
         rel_path = os.path.join(debug_file,
                                 guid,
                                 os.path.basename(file) + ".tar.bz2")
         full_path = os.path.abspath(os.path.join(self.symbol_path,
                                                   rel_path))
         success = subprocess.call(["tar", "cjf", full_path, os.path.basename(file)],
                                   cwd=os.path.dirname(file),
                                   stdout=open("/dev/null","w"), stderr=subprocess.STDOUT)
         if success == 0 and os.path.exists(full_path):
-            print rel_path
+            self.output(sys.stdout, rel_path)
 
 # Entry point if called as a standalone program
 def main():
     parser = OptionParser(usage="usage: %prog [options] <dump_syms binary> <symbol store path> <debug info files>")
     parser.add_option("-c", "--copy",
                       action="store_true", dest="copy_debug", default=False,
                       help="Copy debug info files into the same directory structure as symbol files")
     parser.add_option("-a", "--archs",
@@ -758,12 +894,17 @@ produced by the `repo manifest -r` comma
                                        archs=options.archs,
                                        srcdirs=options.srcdir,
                                        vcsinfo=options.vcsinfo,
                                        srcsrv=options.srcsrv,
                                        exclude=options.exclude,
                                        repo_manifest=options.repo_manifest)
     for arg in args[2:]:
         dumper.Process(arg)
+    dumper.Finish()
 
 # run main if run directly
 if __name__ == "__main__":
+    # set up the multiprocessing infrastructure before we start;
+    # note that this needs to be in the __main__ guard, or else Windows will choke
+    Dumper.GlobalInit()
+
     main()
--- a/toolkit/crashreporter/tools/unit-symbolstore.py
+++ b/toolkit/crashreporter/tools/unit-symbolstore.py
@@ -1,14 +1,14 @@
 #!/usr/bin/env python
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import os, tempfile, unittest, shutil, struct, platform, subprocess
+import os, tempfile, unittest, shutil, struct, platform, subprocess, multiprocessing.dummy
 import mock
 from mock import patch
 import symbolstore
 
 # Some simple functions to mock out files that the platform-specific dumpers will accept.
 # dump_syms itself will not be run (we mock that call out), but we can't override
 # the ShouldProcessFile method since we actually want to test that.
 def write_elf(filename):
@@ -61,44 +61,48 @@ class HelperMixin(object):
             writer(f)
 
 class TestExclude(HelperMixin, unittest.TestCase):
     def test_exclude_wildcard(self):
         """
         Test that using an exclude list with a wildcard pattern works.
         """
         processed = []
-        def mock_process_file(filename):
-            processed.append((filename[len(self.test_dir):] if filename.startswith(self.test_dir) else filename).replace('\\', '/'))
+        def mock_process_file(filenames):
+            for filename in filenames:
+                processed.append((filename[len(self.test_dir):] if filename.startswith(self.test_dir) else filename).replace('\\', '/'))
             return True
         self.add_test_files(add_extension(["foo", "bar", "abc/xyz", "abc/fooxyz", "def/asdf", "def/xyzfoo"]))
         d = symbolstore.GetPlatformSpecificDumper(dump_syms="dump_syms",
                                                   symbol_path="symbol_path",
                                                   exclude=["*foo*"])
-        d.ProcessFile = mock_process_file
-        self.assertTrue(d.Process(self.test_dir))
+        d.ProcessFiles = mock_process_file
+        d.Process(self.test_dir)
+        d.Finish(stop_pool=False)
         processed.sort()
         expected = add_extension(["bar", "abc/xyz", "def/asdf"])
         expected.sort()
         self.assertEqual(processed, expected)
 
     def test_exclude_filenames(self):
         """
         Test that excluding a filename without a wildcard works.
         """
         processed = []
-        def mock_process_file(filename):
-            processed.append((filename[len(self.test_dir):] if filename.startswith(self.test_dir) else filename).replace('\\', '/'))
+        def mock_process_file(filenames):
+            for filename in filenames:
+                processed.append((filename[len(self.test_dir):] if filename.startswith(self.test_dir) else filename).replace('\\', '/'))
             return True
         self.add_test_files(add_extension(["foo", "bar", "abc/foo", "abc/bar", "def/foo", "def/bar"]))
         d = symbolstore.GetPlatformSpecificDumper(dump_syms="dump_syms",
                                                   symbol_path="symbol_path",
                                                   exclude=add_extension(["foo"]))
-        d.ProcessFile = mock_process_file
-        self.assertTrue(d.Process(self.test_dir))
+        d.ProcessFiles = mock_process_file
+        d.Process(self.test_dir)
+        d.Finish(stop_pool=False)
         processed.sort()
         expected = add_extension(["bar", "abc/bar", "def/bar"])
         expected.sort()
         self.assertEqual(processed, expected)
 
 def popen_factory(stdouts):
     """
     Generate a class that can mock subprocess.Popen. |stdouts| is an iterable that
@@ -124,23 +128,29 @@ class TestCopyDebugUniversal(HelperMixin
     def setUp(self):
         HelperMixin.setUp(self)
         self.symbol_dir = tempfile.mkdtemp()
         self._subprocess_call = subprocess.call
         subprocess.call = self.mock_call
         self._subprocess_popen = subprocess.Popen
         subprocess.Popen = popen_factory(self.next_mock_stdout())
         self.stdouts = []
+        self._shutil_rmtree = shutil.rmtree
+        shutil.rmtree = self.mock_rmtree
 
     def tearDown(self):
         HelperMixin.tearDown(self)
+        shutil.rmtree = self._shutil_rmtree
         shutil.rmtree(self.symbol_dir)
         subprocess.call = self._subprocess_call
         subprocess.Popen = self._subprocess_popen
 
+    def mock_rmtree(self, path):
+        pass
+
     def mock_call(self, args, **kwargs):
         if args[0].endswith("dsymutil"):
             filename = args[-1]
             os.makedirs(filename + ".dSYM")
         return 0
 
     def next_mock_stdout(self):
         if not self.stdouts:
@@ -159,17 +169,18 @@ class TestCopyDebugUniversal(HelperMixin
         self.add_test_files(add_extension(["foo"]))
         self.stdouts.append(mock_dump_syms("X" * 33, add_extension(["foo"])[0]))
         self.stdouts.append(mock_dump_syms("Y" * 33, add_extension(["foo"])[0]))
         d = symbolstore.GetPlatformSpecificDumper(dump_syms="dump_syms",
                                                   symbol_path=self.symbol_dir,
                                                   copy_debug=True,
                                                   archs="abc xyz")
         d.CopyDebug = mock_copy_debug
-        self.assertTrue(d.Process(self.test_dir))
+        d.Process(self.test_dir)
+        d.Finish(stop_pool=False)
         self.assertEqual(1, len(copied))
 
 class TestGetVCSFilename(HelperMixin, unittest.TestCase):
     def setUp(self):
         HelperMixin.setUp(self)
 
     def tearDown(self):
         HelperMixin.tearDown(self)
@@ -226,9 +237,16 @@ class TestRepoManifest(HelperMixin, unit
         self.assertEqual("git:example.com/bar/projects/one:src1.c:abcd1234",
                          symbolstore.GetVCSFilename(file1, d.srcdirs)[0])
         self.assertEqual("git:example.com/foo/projects/two:src2.c:ffffffff",
                          symbolstore.GetVCSFilename(file2, d.srcdirs)[0])
         self.assertEqual("git:example.com/bar/something_else:src3.c:00000000",
                          symbolstore.GetVCSFilename(file3, d.srcdirs)[0])
 
 if __name__ == '__main__':
-  unittest.main()
+    # use the multiprocessing.dummy module to use threading wrappers so
+    # that our mocking/module-patching works
+    symbolstore.Dumper.GlobalInit(module=multiprocessing.dummy)
+
+    unittest.main()
+
+    symbolstore.Dumper.pool.close()
+    symbolstore.Dumper.pool.join()
--- a/toolkit/xre/nsSigHandlers.cpp
+++ b/toolkit/xre/nsSigHandlers.cpp
@@ -72,17 +72,18 @@ void
 ah_crap_handler(int signum)
 {
   printf("\nProgram %s (pid = %d) received signal %d.\n",
          _progname,
          getpid(),
          signum);
 
   printf("Stack:\n");
-  NS_StackWalk(PrintStackFrame, 2, nullptr, 0, nullptr);
+  NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0,
+               nullptr, 0, nullptr);
 
   printf("Sleeping for %d seconds.\n",_gdb_sleep_duration);
   printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
          _progname,
          getpid());
 
   sleep(_gdb_sleep_duration);
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -791,20 +791,17 @@ typedef struct {
   size_t size;
   size_t count;
 } PCArray;
 
 static
 void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
 {
   PCArray* array = static_cast<PCArray*>(aClosure);
-  if (array->count >= array->size) {
-    // too many frames, ignore
-    return;
-  }
+  MOZ_ASSERT(array->count < array->size);
   array->sp_array[array->count] = aSP;
   array->array[array->count] = aPC;
   array->count++;
 }
 
 void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
 {
 #ifndef XP_MACOSX
@@ -823,26 +820,30 @@ void TableTicker::doBacktrace(ThreadProf
   // Start with the current function.
   StackWalkCallback(aSample->pc, aSample->sp, &array);
 
   void *platformData = nullptr;
 #ifdef XP_WIN
   platformData = aSample->context;
 #endif
 
+  uint32_t maxFrames = array.size - array.count;
 #ifdef XP_MACOSX
   pthread_t pt = GetProfiledThread(platform_data());
   void *stackEnd = reinterpret_cast<void*>(-1);
   if (pt)
     stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
   nsresult rv = NS_OK;
   if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
-    rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
+    rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
+                               maxFrames, &array,
+                               reinterpret_cast<void**>(aSample->fp), stackEnd);
 #else
-  nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread, platformData);
+  nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
+                             &array, thread, platformData);
 #endif
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)"));
 
     ProfileStack* stack = aProfile.GetStack();
     int pseudoStackPos = 0;
 
     /* We have two stacks, the native C stack we extracted from unwinding,
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -909,32 +909,33 @@ stack_callback(void *pc, void *sp, void 
 /*
  * The caller MUST NOT be holding tmlock when calling backtrace.
  * On return, if *immediate_abort is set, then the return value is NULL
  * and the thread is in a very dangerous situation (e.g. holding
  * sem_pool_lock in Mac OS X pthreads); the caller should bail out
  * without doing anything (such as acquiring locks).
  */
 static callsite *
-backtrace(tm_thread *t, int skip, int *immediate_abort)
+backtrace(tm_thread *t, int skipFrames, int *immediate_abort)
 {
     callsite *site;
     stack_buffer_info *info = &t->backtrace_buf;
     void ** new_stack_buffer;
     size_t new_stack_buffer_size;
     nsresult rv;
 
     t->suppress_tracing++;
 
     if (!stacks_enabled) {
 #if defined(XP_MACOSX)
         /* Walk the stack, even if stacks_enabled is false. We do this to
            check if we must set immediate_abort. */
         info->entries = 0;
-        rv = NS_StackWalk(stack_callback, skip, info, 0, NULL);
+        rv = NS_StackWalk(stack_callback, skipFrames, /* maxFrames */ 0, info,
+                          0, NULL);
         *immediate_abort = rv == NS_ERROR_UNEXPECTED;
         if (rv == NS_ERROR_UNEXPECTED || info->entries == 0) {
             t->suppress_tracing--;
             return NULL;
         }
 #endif
 
         /*
@@ -956,20 +957,24 @@ backtrace(tm_thread *t, int skip, int *i
         /*
          * NS_StackWalk can (on Windows) acquire a lock the shared library
          * loader.  Another thread might call malloc while holding that lock
          * (when loading a shared library).  So we can't be in tmlock during
          * this call.  For details, see
          * https://bugzilla.mozilla.org/show_bug.cgi?id=374829#c8
          */
 
-        /* skip == 0 means |backtrace| should show up, so don't use skip + 1 */
-        /* NB: this call is repeated below if the buffer is too small */
+        /*
+         * skipFrames == 0 means |backtrace| should show up, so don't use
+         * skipFrames + 1.
+         * NB: this call is repeated below if the buffer is too small.
+         */
         info->entries = 0;
-        rv = NS_StackWalk(stack_callback, skip, info, 0, NULL);
+        rv = NS_StackWalk(stack_callback, skipFrames, /* maxFrames */ 0, info,
+                          0, NULL);
         *immediate_abort = rv == NS_ERROR_UNEXPECTED;
         if (rv == NS_ERROR_UNEXPECTED || info->entries == 0) {
             t->suppress_tracing--;
             return NULL;
         }
 
         /*
          * To avoid allocating in stack_callback (which, on Windows, is
@@ -983,17 +988,18 @@ backtrace(tm_thread *t, int skip, int *i
                                    new_stack_buffer_size * sizeof(void*));
             if (!new_stack_buffer)
                 return NULL;
             info->buffer = new_stack_buffer;
             info->size = new_stack_buffer_size;
 
             /* and call NS_StackWalk again */
             info->entries = 0;
-            NS_StackWalk(stack_callback, skip, info, 0, NULL);
+            NS_StackWalk(stack_callback, skipFrames, /* maxFrames */ 0, info,
+                         0, NULL);
 
             /* same stack */
             PR_ASSERT(info->entries * 2 == new_stack_buffer_size);
         }
     }
 
     TM_ENTER_LOCK(t);
 
--- a/xpcom/base/StackWalk.h
+++ b/xpcom/base/StackWalk.h
@@ -10,13 +10,14 @@
 
 // XXX: it would be nice to eventually remove this header dependency on nsStackWalk.h
 #include "nsStackWalk.h"
 
 namespace mozilla {
 
 nsresult
 FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-                      void *aClosure, void **bp, void *stackEnd);
+                      uint32_t aMaxFrames, void *aClosure, void **bp,
+                      void *stackEnd);
 
 }
 
 #endif /* !defined(StackWalk_h_) */
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -97,17 +97,18 @@ my_malloc_logger(uint32_t type, uintptr_
   if (once)
     return;
   once = true;
 
   // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
   // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
   const char *name = OnSnowLeopardOrLater() ? "new_sem_from_pool" :
     "pthread_cond_wait$UNIX2003";
-  NS_StackWalk(stack_callback, 0, const_cast<char*>(name), 0, nullptr);
+  NS_StackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
+               const_cast<char*>(name), 0, nullptr);
 }
 
 // This is called from NS_LogInit() and from the stack walking functions, but
 // only the first call has any effect.  We need to call this function from both
 // places because it must run before any mutexes are created, and also before
 // any objects whose refcounts we're logging are created.  Running this
 // function during NS_LogInit() ensures that we meet the first criterion, and
 // running this function during the stack walking functions ensures we meet the
@@ -211,16 +212,17 @@ struct WalkStackData {
   HANDLE thread;
   bool walkCallingThread;
   HANDLE process;
   HANDLE eventStart;
   HANDLE eventEnd;
   void **pcs;
   uint32_t pc_size;
   uint32_t pc_count;
+  uint32_t pc_max;
   void **sps;
   uint32_t sp_size;
   uint32_t sp_count;
   void *platformData;
 };
 
 void PrintError(char *prefix, WalkStackData* data);
 unsigned int WINAPI WalkStackThread(void* data);
@@ -388,16 +390,19 @@ WalkStackMain64(struct WalkStackData* da
         if (data->pc_count < data->pc_size)
             data->pcs[data->pc_count] = (void*)addr;
         ++data->pc_count;
 
         if (data->sp_count < data->sp_size)
             data->sps[data->sp_count] = (void*)spaddr;
         ++data->sp_count;
 
+        if (data->pc_max != 0 && data->pc_count == data->pc_max)
+            break;
+
         if (frame64.AddrReturn.Offset == 0)
             break;
     }
     return;
 }
 
 
 unsigned int WINAPI
@@ -457,26 +462,27 @@ WalkStackThread(void* aData)
  * chain in aBuffer. For this to work properly, the DLLs must be rebased
  * so that the address in the file agrees with the address in memory.
  * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
  * whose in memory address doesn't match its in-file address.
  */
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData)
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData)
 {
     StackWalkInitCriticalAddress();
     static HANDLE myProcess = NULL;
     HANDLE myThread;
     DWORD walkerReturn;
     struct WalkStackData data;
 
     if (!EnsureImageHlpInitialized())
-        return NS_OK;
+        return NS_ERROR_FAILURE;
 
     HANDLE targetThread = ::GetCurrentThread();
     data.walkCallingThread = true;
     if (aThread) {
         HANDLE threadToWalk = reinterpret_cast<HANDLE> (aThread);
         // walkCallingThread indicates whether we are walking the caller's stack
         data.walkCallingThread = (threadToWalk == targetThread);
         targetThread = threadToWalk;
@@ -512,20 +518,21 @@ NS_StackWalk(NS_WalkStackCallback aCallb
 
     data.skipFrames = aSkipFrames;
     data.thread = myThread;
     data.process = myProcess;
     void *local_pcs[1024];
     data.pcs = local_pcs;
     data.pc_count = 0;
     data.pc_size = ArrayLength(local_pcs);
+    data.pc_max = aMaxFrames;
     void *local_sps[1024];
     data.sps = local_sps;
     data.sp_count = 0;
-    data.sp_size = ArrayLength(local_pcs);
+    data.sp_size = ArrayLength(local_sps);
     data.platformData = aPlatformData;
 
     if (aThread) {
         // If we're walking the stack of another thread, we don't need to
         // use a separate walker thread.
         WalkStackMain64(&data);
 
         if (data.pc_count > data.pc_size) {
@@ -567,17 +574,17 @@ NS_StackWalk(NS_WalkStackCallback aCallb
         ::CloseHandle(data.eventEnd);
     }
 
     ::CloseHandle(myThread);
 
     for (uint32_t i = 0; i < data.pc_count; ++i)
         (*aCallback)(data.pcs[i], data.sps[i], aClosure);
 
-    return NS_OK;
+    return data.pc_count == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 
 static BOOL CALLBACK callbackEspecial64(
   PCSTR aModuleName,
   DWORD64 aModuleBase,
   ULONG aModuleSize,
   PVOID aUserContext)
@@ -893,16 +900,18 @@ struct bucket {
     void * pc;
     int index;
     struct bucket * next;
 };
 
 struct my_user_args {
     NS_WalkStackCallback callback;
     uint32_t skipFrames;
+    uint32_t maxFrames;
+    uint32_t numFrames;
     void *closure;
 };
 
 
 static void myinit();
 
 #pragma init (myinit)
 
@@ -926,17 +935,17 @@ myinit()
         }
 #endif /*__GNUC__*/
     }    
     initialized = 1;
 }
 
 
 static int
-load_address(void * pc, void * arg )
+load_address(void * pc, void * arg)
 {
     static struct bucket table[2048];
     static mutex_t lock;
     struct bucket * ptr;
     struct my_user_args * args = (struct my_user_args *) arg;
 
     unsigned int val = NS_PTR_TO_INT32(pc);
 
@@ -944,25 +953,29 @@ load_address(void * pc, void * arg )
 
     mutex_lock(&lock);
     while (ptr->next) {
         if (ptr->next->pc == pc)
             break;
         ptr = ptr->next;
     }
 
+    int stop = 0;
     if (ptr->next) {
         mutex_unlock(&lock);
     } else {
         (args->callback)(pc, args->closure);
+        args->numFrames++;
+        if (args->maxFrames != 0 && args->numFrames == args->maxFrames)
+          stop = 1;   // causes us to stop getting frames
 
         ptr->next = newbucket(pc);
         mutex_unlock(&lock);
     }
-    return 0;
+    return stop;
 }
 
 
 static struct bucket *
 newbucket(void * pc)
 {
     struct bucket * ptr = (struct bucket *) malloc(sizeof (*ptr));
     static int index; /* protected by lock in caller */
@@ -1015,32 +1028,35 @@ cswalkstack(struct frame *fp, int (*oper
 static void
 cs_operate(int (*operate_func)(void *, void *, void *), void * usrarg)
 {
     cswalkstack(csgetframeptr(), operate_func, usrarg);
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData)
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData)
 {
     MOZ_ASSERT(!aThread);
     MOZ_ASSERT(!aPlatformData);
     struct my_user_args args;
 
     StackWalkInitCriticalAddress();
 
     if (!initialized)
         myinit();
 
     args.callback = aCallback;
     args.skipFrames = aSkipFrames; /* XXX Not handled! */
+    args.maxFrames = aMaxFrames;
+    args.numFrames = 0;
     args.closure = aClosure;
     cs_operate(load_address, &args);
-    return NS_OK;
+    return args.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
 {
     aDetails->library[0] = '\0';
     aDetails->loffset = 0;
     aDetails->filename[0] = '\0';
@@ -1095,21 +1111,23 @@ NS_FormatCodeAddressDetails(void *aPC, c
 #endif
 
 #if HAVE___LIBC_STACK_END
 extern void *__libc_stack_end; // from ld-linux.so
 #endif
 namespace mozilla {
 nsresult
 FramePointerStackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-                      void *aClosure, void **bp, void *aStackEnd)
+                      uint32_t aMaxFrames, void *aClosure, void **bp,
+                      void *aStackEnd)
 {
   // Stack walking code courtesy Kipp's "leaky".
 
-  int skip = aSkipFrames;
+  int32_t skip = aSkipFrames;
+  uint32_t numFrames = 0;
   while (1) {
     void **next = (void**)*bp;
     // bp may not be a frame pointer on i386 if code was compiled with
     // -fomit-frame-pointer, so do some sanity checks.
     // (bp should be a frame pointer on ppc(64) but checking anyway may help
     // a little if the stack has been corrupted.)
     // We don't need to check against the begining of the stack because
     // we can assume that bp > sp
@@ -1131,30 +1149,34 @@ FramePointerStackWalk(NS_WalkStackCallba
       return NS_ERROR_UNEXPECTED;
     }
     if (--skip < 0) {
       // Assume that the SP points to the BP of the function
       // it called. We can't know the exact location of the SP
       // but this should be sufficient for our use the SP
       // to order elements on the stack.
       (*aCallback)(pc, bp, aClosure);
+      numFrames++;
+      if (aMaxFrames != 0 && numFrames == aMaxFrames)
+        break;
     }
     bp = next;
   }
-  return NS_OK;
+  return numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 }
 
 #define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__))
 #if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData)
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData)
 {
   MOZ_ASSERT(!aThread);
   MOZ_ASSERT(!aPlatformData);
   StackWalkInitCriticalAddress();
 
   // Get the frame pointer
   void **bp;
 #if defined(__i386) 
@@ -1167,78 +1189,92 @@ NS_StackWalk(NS_WalkStackCallback aCallb
 #endif
 
   void *stackEnd;
 #if HAVE___LIBC_STACK_END
   stackEnd = __libc_stack_end;
 #else
   stackEnd = reinterpret_cast<void*>(-1);
 #endif
-  return FramePointerStackWalk(aCallback, aSkipFrames,
+  return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
                                aClosure, bp, stackEnd);
 
 }
 
 #elif defined(HAVE__UNWIND_BACKTRACE)
 
 // libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
 #include <unwind.h>
 
 struct unwind_info {
     NS_WalkStackCallback callback;
     int skip;
+    int maxFrames;
+    int numFrames;
+    bool isCriticalAbort;
     void *closure;
 };
 
 static _Unwind_Reason_Code
 unwind_callback (struct _Unwind_Context *context, void *closure)
 {
     unwind_info *info = static_cast<unwind_info *>(closure);
     void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
     // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
     if (IsCriticalAddress(pc)) {
         printf("Aborting stack trace, PC is critical\n");
-        /* We just want to stop the walk, so any error code will do.
-           Using _URC_NORMAL_STOP would probably be the most accurate,
-           but it is not defined on Android for ARM. */
+        info->isCriticalAbort = true;
+        // We just want to stop the walk, so any error code will do.  Using
+        // _URC_NORMAL_STOP would probably be the most accurate, but it is not
+        // defined on Android for ARM.
         return _URC_FOREIGN_EXCEPTION_CAUGHT;
     }
-    if (--info->skip < 0)
+    if (--info->skip < 0) {
         (*info->callback)(pc, NULL, info->closure);
+        info->numFrames++;
+        if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
+            // Again, any error code that stops the walk will do.
+            return _URC_FOREIGN_EXCEPTION_CAUGHT;
+        }
+    }
     return _URC_NO_REASON;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData)
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData)
 {
     MOZ_ASSERT(!aThread);
     MOZ_ASSERT(!aPlatformData);
     StackWalkInitCriticalAddress();
     unwind_info info;
     info.callback = aCallback;
     info.skip = aSkipFrames + 1;
+    info.maxFrames = aMaxFrames;
+    info.numFrames = 0;
+    info.isCriticalAbort = false;
     info.closure = aClosure;
 
-    _Unwind_Reason_Code t = _Unwind_Backtrace(unwind_callback, &info);
-#if defined(ANDROID) && defined(__arm__)
-    // Ignore the _Unwind_Reason_Code on Android + ARM, because bionic's
-    // _Unwind_Backtrace usually (always?) returns _URC_FAILURE.  See
-    // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
-    //
-    // (Ideally, the #if above would be specifically for bionic, not for
-    // Android + ARM, but we don't have a define specifically for bionic.)
-#else
-    if (t != _URC_END_OF_STACK)
+    (void)_Unwind_Backtrace(unwind_callback, &info);
+
+    // We ignore the return value from _Unwind_Backtrace and instead determine
+    // the outcome from |info|.  There are two main reasons for this:
+    // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
+    //   _URC_FAILURE.  See
+    //   https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
+    // - If aMaxFrames != 0, we want to stop early, and the only way to do that
+    //   is to make unwind_callback return something other than _URC_NO_REASON,
+    //   which causes _Unwind_Backtrace to return a non-success code.
+    if (info.isCriticalAbort)
         return NS_ERROR_UNEXPECTED;
-#endif
-    return NS_OK;
+    return info.numFrames == 0 ? NS_ERROR_FAILURE : NS_OK;
 }
 
 #endif
 
 EXPORT_XPCOM_API(nsresult)
 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
 {
   aDetails->library[0] = '\0';
@@ -1295,17 +1331,18 @@ NS_FormatCodeAddressDetails(void *aPC, c
 }
 
 #endif
 
 #else // unsupported platform.
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData)
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData)
 {
     MOZ_ASSERT(!aThread);
     MOZ_ASSERT(!aPlatformData);
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 namespace mozilla {
 nsresult
--- a/xpcom/base/nsStackWalk.h
+++ b/xpcom/base/nsStackWalk.h
@@ -26,39 +26,48 @@ typedef void
 /**
  * Call aCallback for the C/C++ stack frames on the current thread, from
  * the caller of NS_StackWalk to main (or above).
  *
  * @param aCallback    Callback function, called once per frame.
  * @param aSkipFrames  Number of initial frames to skip.  0 means that
  *                     the first callback will be for the caller of
  *                     NS_StackWalk.
+ * @param aMaxFrames   Maximum number of frames to trace.  0 means no limit.
  * @param aClosure     Caller-supplied data passed through to aCallback.
  * @param aThread      The thread for which the stack is to be retrieved.
  *                     Passing null causes us to walk the stack of the
  *                     current thread. On Windows, this is a thread HANDLE.
  *                     It is currently not supported on any other platform.
  * @param aPlatformData Platform specific data that can help in walking the
  *                      stack, this should be NULL unless you really know
  *                      what you're doing! This needs to be a pointer to a
  *                      CONTEXT on Windows and should not be passed on other
  *                      platforms.
  *
- * Returns NS_ERROR_NOT_IMPLEMENTED on platforms where it is
- * unimplemented.
- * Returns NS_ERROR_UNEXPECTED when the stack indicates that the thread
- * is in a very dangerous situation (e.g., holding sem_pool_lock in 
- * Mac OS X pthreads code). Callers should then bail out immediately.
+ * Return values:
+ * - NS_ERROR_NOT_IMPLEMENTED.  Occurs on platforms where it is unimplemented.
+ *
+ * - NS_ERROR_UNEXPECTED.  Occurs when the stack indicates that the thread
+ *   is in a very dangerous situation (e.g., holding sem_pool_lock in Mac OS X
+ *   pthreads code).  Callers should then bail out immediately.
+ *
+ * - NS_ERROR_FAILURE.  Occurs when stack walking completely failed, i.e.
+ *   aCallback was never called.
+ *
+ * - NS_OK.  Occurs when stack walking succeeded, i.e. aCallback was called at
+ *   least once (and there was no need to exit with NS_ERROR_UNEXPECTED).
  *
  * May skip some stack frames due to compiler optimizations or code
  * generation.
  */
 XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, uint32_t aSkipFrames,
-             void *aClosure, uintptr_t aThread, void *aPlatformData);
+             uint32_t aMaxFrames, void *aClosure, uintptr_t aThread,
+             void *aPlatformData);
 
 typedef struct {
     /*
      * The name of the shared library or executable containing an
      * address and the address's offset within that library, or empty
      * string and zero if unknown.
      */
     char library[256];
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -849,17 +849,18 @@ static void PrintStackFrame(void *aPC, v
   fputs(buf, stream);
 }
 
 }
 
 void
 nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 {
-  NS_StackWalk(PrintStackFrame, 2, aStream, 0, nullptr);
+  NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
+               0, nullptr);
 }
 
 //----------------------------------------------------------------------
 
 // This thing is exported by libstdc++
 // Yes, this is a gcc only hack
 #if defined(MOZ_DEMANGLE_SYMBOLS)
 #include <cxxabi.h>
--- a/xpcom/build/mozPoisonWriteMac.cpp
+++ b/xpcom/build/mozPoisonWriteMac.cpp
@@ -91,17 +91,18 @@ bool ValidWriteAssert(bool ok)
 
     if (ok || !sProfileDirectory || !Telemetry::CanRecord())
         return ok;
 
     // Write the stack and loaded libraries to a file. We can get here
     // concurrently from many writes, so we use multiple temporary files.
     std::vector<uintptr_t> rawStack;
 
-    NS_StackWalk(RecordStackWalker, 0, reinterpret_cast<void*>(&rawStack), 0, nullptr);
+    NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+                 reinterpret_cast<void*>(&rawStack), 0, nullptr);
     Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
 
     nsPrintfCString nameAux("%s%s", sProfileDirectory,
                             "/Telemetry.LateWriteTmpXXXXXX");
     char *name;
     nameAux.GetMutableData(&name);
 
     // We want the sha1 of the entire file, so please don't write to fd
--- a/xpcom/threads/HangMonitor.cpp
+++ b/xpcom/threads/HangMonitor.cpp
@@ -132,17 +132,18 @@ GetChromeHangReport(Telemetry::Processed
 
   // The thread we're about to suspend might have the alloc lock
   // so allocate ahead of time
   std::vector<uintptr_t> rawStack;
   rawStack.reserve(MAX_CALL_STACK_PCS);
   DWORD ret = ::SuspendThread(winMainThreadHandle);
   if (ret == -1)
     return;
-  NS_StackWalk(ChromeStackWalker, 0, reinterpret_cast<void*>(&rawStack),
+  NS_StackWalk(ChromeStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+               reinterpret_cast<void*>(&rawStack),
                reinterpret_cast<uintptr_t>(winMainThreadHandle), nullptr);
   ret = ::ResumeThread(winMainThreadHandle);
   if (ret == -1)
     return;
   aStack = Telemetry::GetStackAndModules(rawStack);
 }
 #endif