Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 12 Jun 2015 17:17:43 -0700
changeset 279435 b7ee8e13145aef2b4766bcba4294107e751bb055
parent 279399 2408569864e1b0b22e4c8ee9949e849a5130ee7e (current diff)
parent 279434 c117126d24e7abc064a0f42428c39b40f2c5f629 (diff)
child 279450 aa109a7d3c9afda101d6411e439fb2ca154029c5
child 279481 bfd3cda6d87bb2fe2897774342c4a67fe4e8cb67
child 279506 84566e9bc014183d3f5f9d5a3b38d9654dff8dad
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.0a1
first release with
nightly linux32
b7ee8e13145a / 41.0a1 / 20150613030206 / files
nightly linux64
b7ee8e13145a / 41.0a1 / 20150613030206 / files
nightly mac
b7ee8e13145a / 41.0a1 / 20150613030206 / files
nightly win32
b7ee8e13145a / 41.0a1 / 20150613030206 / files
nightly win64
b7ee8e13145a / 41.0a1 / 20150613030206 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -800,17 +800,21 @@ getParentCB(AtkObject *aAtkObj)
     Accessible* parent = wrapper->Parent();
     atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr;
   } else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
     ProxyAccessible* parent = proxy->Parent();
     if (parent) {
       atkParent = GetWrapperFor(parent);
     } else {
       // Otherwise this should be the proxy for the tab's top level document.
-      atkParent = AccessibleWrap::GetAtkObject(proxy->OuterDocOfRemoteBrowser());
+      Accessible* outerDocParent = proxy->OuterDocOfRemoteBrowser();
+      NS_ASSERTION(outerDocParent, "this document should have an outerDoc as a parent");
+      if (outerDocParent) {
+        atkParent = AccessibleWrap::GetAtkObject(outerDocParent);
+      }
     }
   }
 
   if (atkParent)
     atk_object_set_parent(aAtkObj, atkParent);
 
   return aAtkObj->accessible_parent;
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -147,17 +147,21 @@
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
 
     <!-- for select dropdowns. The menupopup is what shows the list of options,
          and the popuponly menulist makes things like the menuactive attributes
          work correctly on the menupopup. ContentSelectDropdown expects the
          popuponly menulist to be its immediate parent. -->
     <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
-      <menupopup rolluponmousewheel="true"/>
+      <menupopup rolluponmousewheel="true"
+#ifdef XP_WIN
+                 consumeoutsideclicks="false"
+#endif
+        />
     </menulist>
 
     <!-- for invalid form error message -->
     <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
       <description/>
     </panel>
 
     <panel id="editBookmarkPanel"
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -68,20 +68,16 @@
 
 #include "nsIMemoryReporter.h"
 #include <winternl.h>
 #include "d3dkmtQueryStatistics.h"
 
 #include "SurfaceCache.h"
 #include "gfxPrefs.h"
 
-#if defined(MOZ_CRASHREPORTER)
-#include "nsExceptionHandler.h"
-#endif
-
 #include "VsyncSource.h"
 #include "DriverInitCrashDetection.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
@@ -1716,26 +1712,22 @@ bool DoesD3D11DeviceWork(ID3D11Device *d
     return true;
   }
 
   if (GetModuleHandleW(L"dlumd32.dll") && GetModuleHandleW(L"igd10umd32.dll")) {
     nsString displayLinkModuleVersionString;
     gfxWindowsPlatform::GetDLLVersion(L"dlumd32.dll", displayLinkModuleVersionString);
     uint64_t displayLinkModuleVersion;
     if (!ParseDriverVersion(displayLinkModuleVersionString, &displayLinkModuleVersion)) {
-#if defined(MOZ_CRASHREPORTER)
-      CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DisplayLink: could not parse version\n"));
-#endif
+      gfxCriticalError() << "DisplayLink: could not parse version";
       gANGLESupportsD3D11 = false;
       return false;
     }
     if (displayLinkModuleVersion <= V(8,6,1,36484)) {
-#if defined(MOZ_CRASHREPORTER)
-      CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DisplayLink: too old version\n"));
-#endif
+      gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DisplayLink: too old version";
       gANGLESupportsD3D11 = false;
       return false;
     }
   }
   result = true;
   return true;
 }
 
@@ -1757,19 +1749,17 @@ bool DoesD3D11TextureSharingWorkInternal
 
   if (GetModuleHandleW(L"atidxx32.dll")) {
     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     if (gfxInfo) {
       nsString vendorID, vendorID2;
       gfxInfo->GetAdapterVendorID(vendorID);
       gfxInfo->GetAdapterVendorID2(vendorID2);
       if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
-#if defined(MOZ_CRASHREPORTER)
-        CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("Unexpected Intel/AMD dual-GPU setup\n"));
-#endif
+        gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Unexpected Intel/AMD dual-GPU setup";
         return false;
       }
     }
   }
 
   RefPtr<ID3D11Texture2D> texture;
   D3D11_TEXTURE2D_DESC desc;
   desc.Width = 32;
@@ -1799,32 +1789,31 @@ bool DoesD3D11TextureSharingWorkInternal
     return false;
   }
 
   nsRefPtr<ID3D11Resource> sharedResource;
   nsRefPtr<ID3D11Texture2D> sharedTexture;
   if (FAILED(device->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource),
                                         getter_AddRefs(sharedResource))))
   {
+    gfxCriticalError(CriticalLog::DefaultOptions(false)) << "OpenSharedResource failed for format " << format;
     return false;
   }
 
   if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D),
                                             getter_AddRefs(sharedTexture))))
   {
     return false;
   }
 
   RefPtr<ID3D11ShaderResourceView> sharedView;
 
   // This if(FAILED()) is the one that actually fails on systems affected by bug 1083071.
   if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL, byRef(sharedView)))) {
-#if defined(MOZ_CRASHREPORTER)
-    CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("CreateShaderResourceView failed\n"));
-#endif
+    gfxCriticalError(CriticalLog::DefaultOptions(false)) << "CreateShaderResourceView failed for format" << format;
     return false;
   }
 
   return true;
 }
 
 bool DoesD3D11TextureSharingWork(ID3D11Device *device)
 {
@@ -1838,23 +1827,27 @@ bool DoesD3D11TextureSharingWork(ID3D11D
   result = DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
   return result;
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
   if (gfxInfo) {
-    // Disable texture sharing if we're blocking d2d since that's the only other time we use it
-    // and it might be broken.
-    int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        return false;
-      }
+    // A8 texture sharing crashes on this intel driver version (and no others)
+    // so just avoid using it in that case.
+    nsString adapterVendor;
+    nsString driverVersion;
+    gfxInfo->GetAdapterVendorID(adapterVendor);
+    gfxInfo->GetAdapterDriverVersion(driverVersion);
+
+    nsAString &intelVendorID = (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorIntel);
+    if (adapterVendor.Equals(intelVendorID, nsCaseInsensitiveStringComparator()) &&
+        driverVersion.Equals(NS_LITERAL_STRING("8.15.10.2086"))) {
+      return false;
     }
   }
 
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_A8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
 void
 gfxWindowsPlatform::InitD3D11Devices()
@@ -1946,25 +1939,28 @@ gfxWindowsPlatform::InitD3D11Devices()
       hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                              // Use
                              // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                              // to prevent bug 1092260. IE 11 also uses this flag.
                              D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                              featureLevels.Elements(), featureLevels.Length(),
                              D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
     } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+      gfxCriticalError() << "Crash during D3D11 device creation";
+
       if (gfxPrefs::LayersD3D11DisableWARP()) {
         return;
       }
 
       useWARP = true;
       adapter = nullptr;
     }
 
     if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) {
+      gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
       if (gfxPrefs::LayersD3D11DisableWARP()) {
         return;
       }
 
       useWARP = true;
       adapter = nullptr;
     }
   }
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -23,26 +23,28 @@ MacroAssembler::framePushed() const
 
 void
 MacroAssembler::setFramePushed(uint32_t framePushed)
 {
     framePushed_ = framePushed;
 }
 
 void
-MacroAssembler::adjustFrame(int value)
+MacroAssembler::adjustFrame(int32_t value)
 {
+    MOZ_ASSERT_IF(value < 0, framePushed_ >= uint32_t(-value));
     setFramePushed(framePushed_ + value);
 }
 
 void
 MacroAssembler::implicitPop(uint32_t bytes)
 {
     MOZ_ASSERT(bytes % sizeof(intptr_t) == 0);
-    adjustFrame(-bytes);
+    MOZ_ASSERT(bytes <= INT32_MAX);
+    adjustFrame(-int32_t(bytes));
 }
 
 // ===============================================================
 // Stack manipulation functions.
 
 CodeOffsetLabel
 MacroAssembler::PushWithPatch(ImmWord word)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -275,17 +275,17 @@ class MacroAssembler : public MacroAssem
     }
 
   public:
     // ===============================================================
     // Frame manipulation functions.
 
     inline uint32_t framePushed() const;
     inline void setFramePushed(uint32_t framePushed);
-    inline void adjustFrame(int value);
+    inline void adjustFrame(int32_t value);
 
     // Adjust the frame, to account for implicit modification of the stack
     // pointer, such that callee can remove arguments on the behalf of the
     // caller.
     inline void implicitPop(uint32_t bytes);
 
   private:
     // This field is used to statically (at compilation time) emulate a frame
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -3335,17 +3335,17 @@ RangeAnalysis::prepareForUCE(bool* shoul
     return tryRemovingGuards();
 }
 
 bool RangeAnalysis::tryRemovingGuards()
 {
     MDefinitionVector guards(alloc());
 
     for (ReversePostorderIterator block = graph_.rpoBegin(); block != graph_.rpoEnd(); block++) {
-        for (MInstructionReverseIterator iter = block->rbegin(); iter != block->rend(); iter++) {
+        for (MDefinitionIterator iter(*block); iter; iter++) {
             if (!iter->isGuardRangeBailouts())
                 continue;
 
             iter->setInWorklist();
             if (!guards.append(*iter))
                 return false;
         }
     }
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -7,16 +7,17 @@
 /*
  * Portable safe sprintf code.
  *
  * Author: Kipp E.B. Hickman
  */
 
 #include "jsprf.h"
 
+#include "mozilla/Snprintf.h"
 #include "mozilla/Vector.h"
 
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "jsalloc.h"
@@ -281,49 +282,42 @@ static bool cvt_ll(SprintfState* ss, int
     // Now that we have the number converted without its sign, deal with
     // the sign and zero padding.
     return fill_n(ss, cvt, digits, width, prec, type, flags);
 }
 
 /*
  * Convert a double precision floating point number into its printable
  * form.
- *
- * XXX stop using sprintf to convert floating point
  */
 static bool cvt_f(SprintfState* ss, double d, const char* fmt0, const char* fmt1)
 {
     char fin[20];
     char fout[300];
     int amount = fmt1 - fmt0;
 
     MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
     if (amount >= (int)sizeof(fin)) {
         // Totally bogus % command to sprintf. Just ignore it
         return true;
     }
     js_memcpy(fin, fmt0, (size_t)amount);
     fin[amount] = 0;
 
-    // Convert floating point using the native sprintf code
+    // Convert floating point using the native snprintf code
 #ifdef DEBUG
     {
         const char* p = fin;
         while (*p) {
             MOZ_ASSERT(*p != 'L');
             p++;
         }
     }
 #endif
-    sprintf(fout, fin, d);
-
-    // This assert will catch overflow's of fout, when building with
-    // debugging on. At least this way we can track down the evil piece
-    // of calling code and fix it!
-    MOZ_ASSERT(strlen(fout) < sizeof(fout));
+    snprintf_literal(fout, fin, d);
 
     return (*ss->stuff)(ss, fout, strlen(fout));
 }
 
 static inline const char* generic_null_str(const char*) { return "(null)"; }
 static inline const char16_t* generic_null_str(const char16_t*) { return MOZ_UTF16("(null)"); }
 
 static inline size_t generic_strlen(const char* s) { return strlen(s); }
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -528,29 +528,28 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) c
   if (!mids.empty()) {
     UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
     groupAttr->PushEntry(SdpGroupAttributeList::kBundle, mids);
     sdp->GetAttributeList().SetAttribute(groupAttr.release());
   }
 }
 
 nsresult
-JsepSessionImpl::FinalizeTransportAttributes(Sdp* offer)
+JsepSessionImpl::SetupTransportAttributes(const Sdp& newOffer, Sdp* local)
 {
   const Sdp* oldAnswer = GetAnswer();
 
   if (oldAnswer) {
     // Renegotiation, we might have transport attributes to copy over
     for (size_t i = 0; i < oldAnswer->GetMediaSectionCount(); ++i) {
-      if (!MsectionIsDisabled(offer->GetMediaSection(i)) &&
-          !MsectionIsDisabled(oldAnswer->GetMediaSection(i)) &&
-          !IsBundleSlave(*oldAnswer, i)) {
+      if (!MsectionIsDisabled(local->GetMediaSection(i)) &&
+          AreOldTransportParamsValid(*oldAnswer, newOffer, i)) {
         nsresult rv = CopyTransportParams(
             mCurrentLocalDescription->GetMediaSection(i),
-            &offer->GetMediaSection(i));
+            &local->GetMediaSection(i));
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -764,17 +763,17 @@ JsepSessionImpl::CreateOffer(const JsepO
   }
 
   // Now add all the m-lines that we are attempting to negotiate.
   rv = AddOfferMSections(options, sdp.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetupBundle(sdp.get());
 
-  rv = FinalizeTransportAttributes(sdp.get());
+  rv = SetupTransportAttributes(*sdp, sdp.get());
   NS_ENSURE_SUCCESS(rv,rv);
 
   *offer = sdp->ToString();
   mGeneratedLocalDescription = Move(sdp);
 
   return NS_OK;
 }
 
@@ -1017,16 +1016,19 @@ JsepSessionImpl::CreateAnswer(const Jsep
   size_t numMsections = offer.GetMediaSectionCount();
 
   for (size_t i = 0; i < numMsections; ++i) {
     const SdpMediaSection& remoteMsection = offer.GetMediaSection(i);
     rv = CreateAnswerMSection(options, i, remoteMsection, sdp.get());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  rv = SetupTransportAttributes(offer, sdp.get());
+  NS_ENSURE_SUCCESS(rv,rv);
+
   *answer = sdp->ToString();
   mGeneratedLocalDescription = Move(sdp);
 
   return NS_OK;
 }
 
 static bool
 HasRtcp(SdpMediaSection::Protocol proto)
@@ -1411,16 +1413,17 @@ JsepSessionImpl::SetLocalDescriptionAnsw
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = HandleNegotiatedSession(mPendingLocalDescription,
                                mPendingRemoteDescription);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mCurrentRemoteDescription = Move(mPendingRemoteDescription);
   mCurrentLocalDescription = Move(mPendingLocalDescription);
+  mWasOffererLastTime = mIsOfferer;
 
   SetState(kJsepStateStable);
   return NS_OK;
 }
 
 nsresult
 JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
 {
@@ -1860,16 +1863,47 @@ JsepSessionImpl::FinalizeTransport(const
 
   if (answer.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
     transport->mComponents = 1;
   }
 
   return NS_OK;
 }
 
+bool
+JsepSessionImpl::AreOldTransportParamsValid(const Sdp& oldAnswer,
+                                            const Sdp& newOffer,
+                                            size_t level)
+{
+  if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) ||
+      MsectionIsDisabled(newOffer.GetMediaSection(level))) {
+    // Obvious
+    return false;
+  }
+
+  if (IsBundleSlave(oldAnswer, level)) {
+    // The transport attributes on this m-section were thrown away, because it
+    // was bundled.
+    return false;
+  }
+
+  if (newOffer.GetMediaSection(level).GetAttributeList().HasAttribute(
+        SdpAttribute::kBundleOnlyAttribute) &&
+      IsBundleSlave(newOffer, level)) {
+    // It never makes sense to put transport attributes in a bundle-only
+    // m-section
+    return false;
+  }
+
+  // TODO(bug 906986): Check for ICE restart (will need to pass the offerer's
+  // old SDP to compare it against |newOffer|)
+
+  return true;
+}
+
 nsresult
 JsepSessionImpl::AddTransportAttributes(SdpMediaSection* msection,
                                         SdpSetupAttribute::Role dtlsRole)
 {
   if (mIceUfrag.empty() || mIcePwd.empty()) {
     JSEP_SET_ERROR("Missing ICE ufrag or password");
     return NS_ERROR_FAILURE;
   }
@@ -1881,43 +1915,53 @@ JsepSessionImpl::AddTransportAttributes(
       new SdpStringAttribute(SdpAttribute::kIcePwdAttribute, mIcePwd));
 
   msection->GetAttributeList().SetAttribute(new SdpSetupAttribute(dtlsRole));
 
   return NS_OK;
 }
 
 nsresult
-JsepSessionImpl::CopyTransportParams(const SdpMediaSection& source,
-                                     SdpMediaSection* dest)
+JsepSessionImpl::CopyTransportParams(const SdpMediaSection& oldLocal,
+                                     SdpMediaSection* newLocal)
 {
   // Copy over m-section details
-  dest->SetPort(source.GetPort());
-  dest->GetConnection() = source.GetConnection();
-
-  auto& sourceAttrs = source.GetAttributeList();
-  auto& destAttrs = dest->GetAttributeList();
+  newLocal->SetPort(oldLocal.GetPort());
+  newLocal->GetConnection() = oldLocal.GetConnection();
+
+  const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList();
+  SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList();
+
+  // If newLocal is an offer, this will be the number of components we used
+  // last time, and if it is an answer, this will be the number of components
+  // we know we're using now.
+  size_t components = mTransports[oldLocal.GetLevel()]->mComponents;
 
   // Now we copy over attributes that won't be added by the usual logic
-  if (sourceAttrs.HasAttribute(SdpAttribute::kCandidateAttribute)) {
-    auto* candidateAttrs =
-      new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute);
-    candidateAttrs->mValues = sourceAttrs.GetCandidate();
-    destAttrs.SetAttribute(candidateAttrs);
+  if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) &&
+      components) {
+    UniquePtr<SdpMultiStringAttribute> candidateAttrs(
+      new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
+    for (const std::string& candidate : oldLocalAttrs.GetCandidate()) {
+      size_t component;
+      nsresult rv = GetComponent(candidate, &component);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (components >= component) {
+        candidateAttrs->mValues.push_back(candidate);
+      }
+    }
+    if (candidateAttrs->mValues.size()) {
+      newLocalAttrs.SetAttribute(candidateAttrs.release());
+    }
   }
 
-  if (sourceAttrs.HasAttribute(SdpAttribute::kEndOfCandidatesAttribute)) {
-    destAttrs.SetAttribute(
-        new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
-  }
-
-  if (!destAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute) &&
-      sourceAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
-    // copy rtcp attribute
-    destAttrs.SetAttribute(new SdpRtcpAttribute(sourceAttrs.GetRtcp()));
+  if (components == 2 &&
+      oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
+    // copy rtcp attribute if we had one that we are using
+    newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
   }
 
   return NS_OK;
 }
 
 nsresult
 JsepSessionImpl::CopyStickyParams(const SdpMediaSection& source,
                                   SdpMediaSection* dest)
@@ -2070,16 +2114,17 @@ JsepSessionImpl::SetRemoteDescriptionAns
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = HandleNegotiatedSession(mPendingLocalDescription,
                                mPendingRemoteDescription);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mCurrentRemoteDescription = Move(mPendingRemoteDescription);
   mCurrentLocalDescription = Move(mPendingLocalDescription);
+  mWasOffererLastTime = mIsOfferer;
 
   SetState(kJsepStateStable);
   return NS_OK;
 }
 
 nsresult
 JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp* remoteDescription)
 {
@@ -2562,16 +2607,31 @@ JsepSessionImpl::AddCandidateToSdp(Sdp* 
             attrList.GetAttribute(SdpAttribute::kCandidateAttribute))));
   }
   candidates->PushEntry(candidate);
   attrList.SetAttribute(candidates.release());
 
   return NS_OK;
 }
 
+// TODO(bug 1142105): Move this into an SDP helper class. Ideally, we'd like
+// to have real parse code and a useful representation of candidate attrs.
+nsresult
+JsepSessionImpl::GetComponent(const std::string& candidate, size_t* component)
+{
+  unsigned int temp;
+  int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp);
+  if (result == 1) {
+    *component = temp;
+    return NS_OK;
+  }
+  JSEP_SET_ERROR("Malformed local ICE candidate: " << candidate);
+  return NS_ERROR_INVALID_ARG;
+}
+
 nsresult
 JsepSessionImpl::AddRemoteIceCandidate(const std::string& candidate,
                                        const std::string& mid,
                                        uint16_t level)
 {
   mLastError.clear();
 
   mozilla::Sdp* sdp = 0;
@@ -2631,16 +2691,17 @@ JsepSessionImpl::AddLocalIceCandidate(co
 static void SetDefaultAddresses(const std::string& defaultCandidateAddr,
                                 uint16_t defaultCandidatePort,
                                 const std::string& defaultRtcpCandidateAddr,
                                 uint16_t defaultRtcpCandidatePort,
                                 SdpMediaSection* msection)
 {
   msection->GetConnection().SetAddress(defaultCandidateAddr);
   msection->SetPort(defaultCandidatePort);
+
   if (!defaultRtcpCandidateAddr.empty()) {
     sdp::AddrType ipVersion = sdp::kIPv4;
     if (defaultRtcpCandidateAddr.find(':') != std::string::npos) {
       ipVersion = sdp::kIPv6;
     }
     msection->GetAttributeList().SetAttribute(new SdpRtcpAttribute(
           defaultRtcpCandidatePort,
           sdp::kInternet,
@@ -2664,27 +2725,40 @@ JsepSessionImpl::EndOfLocalCandidates(co
     sdp = mPendingLocalDescription.get();
   } else if (mCurrentLocalDescription) {
     sdp = mCurrentLocalDescription.get();
   } else {
     JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
-  std::set<std::string> bundleMids;
-  const SdpMediaSection* bundleMsection;
-  nsresult rv = GetNegotiatedBundleInfo(&bundleMids, &bundleMsection);
-  if (NS_FAILED(rv)) {
-    MOZ_ASSERT(false);
-    mLastError += " (This should have been caught sooner!)";
-    return NS_ERROR_FAILURE;
+  if (level >= sdp->GetMediaSectionCount()) {
+    return NS_OK;
+  }
+
+  std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr);
+  if (mState == kJsepStateStable && mTransports[level]->mComponents == 1) {
+    // We know we're doing rtcp-mux by now. Don't create an rtcp attr.
+    defaultRtcpCandidateAddrCopy = "";
+    defaultRtcpCandidatePort = 0;
   }
 
-  if (level < sdp->GetMediaSectionCount()) {
-    SdpMediaSection& msection = sdp->GetMediaSection(level);
+  SdpMediaSection& msection = sdp->GetMediaSection(level);
+
+  if (mState == kJsepStateStable) {
+    // offer/answer is done. Do we actually incorporate these defaults?
+    const Sdp* answer(GetAnswer());
+    std::set<std::string> bundleMids;
+    const SdpMediaSection* bundleMsection;
+    nsresult rv = GetBundleInfo(*answer, &bundleMids, &bundleMsection);
+    if (NS_FAILED(rv)) {
+      MOZ_ASSERT(false);
+      mLastError += " (This should have been caught sooner!)";
+      return NS_ERROR_FAILURE;
+    }
 
     if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute) &&
         bundleMids.count(msection.GetAttributeList().GetMid())) {
       if (msection.GetLevel() != bundleMsection->GetLevel()) {
         // Slave bundle m-section. Skip.
         return NS_OK;
       }
 
@@ -2693,36 +2767,36 @@ JsepSessionImpl::EndOfLocalCandidates(co
       for (auto i = bundleMids.begin(); i != bundleMids.end(); ++i) {
         SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, *i);
         if (!bundledMsection) {
           MOZ_ASSERT(false);
           continue;
         }
         SetDefaultAddresses(defaultCandidateAddr,
                             defaultCandidatePort,
-                            defaultRtcpCandidateAddr,
+                            defaultRtcpCandidateAddrCopy,
                             defaultRtcpCandidatePort,
                             bundledMsection);
       }
     }
-
-    SetDefaultAddresses(defaultCandidateAddr,
-                        defaultCandidatePort,
-                        defaultRtcpCandidateAddr,
-                        defaultRtcpCandidatePort,
-                        &msection);
-
-    // TODO(bug 1095793): Will this have an mid someday?
-
-    SdpAttributeList& attrs = msection.GetAttributeList();
-    attrs.SetAttribute(
-        new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
-    if (!mIsOfferer) {
-      attrs.RemoveAttribute(SdpAttribute::kIceOptionsAttribute);
-    }
+  }
+
+  SetDefaultAddresses(defaultCandidateAddr,
+                      defaultCandidatePort,
+                      defaultRtcpCandidateAddrCopy,
+                      defaultRtcpCandidatePort,
+                      &msection);
+
+  // TODO(bug 1095793): Will this have an mid someday?
+
+  SdpAttributeList& attrs = msection.GetAttributeList();
+  attrs.SetAttribute(
+      new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+  if (!mIsOfferer) {
+    attrs.RemoveAttribute(SdpAttribute::kIceOptionsAttribute);
   }
 
   return NS_OK;
 }
 
 SdpMediaSection*
 JsepSessionImpl::FindMsectionByMid(Sdp& sdp,
                                    const std::string& mid) const
@@ -3034,20 +3108,18 @@ JsepSessionImpl::MsectionIsDisabled(cons
   return !msection.GetPort() &&
          !msection.GetAttributeList().HasAttribute(
              SdpAttribute::kBundleOnlyAttribute);
 }
 
 const Sdp*
 JsepSessionImpl::GetAnswer() const
 {
-  MOZ_ASSERT(mState == kJsepStateStable);
-
-  return mIsOfferer ? mCurrentRemoteDescription.get()
-                    : mCurrentLocalDescription.get();
+  return mWasOffererLastTime ? mCurrentRemoteDescription.get()
+                             : mCurrentLocalDescription.get();
 }
 
 nsresult
 JsepSessionImpl::Close()
 {
   mLastError.clear();
   SetState(kJsepStateClosed);
   return NS_OK;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -27,16 +27,17 @@ public:
 };
 
 class JsepSessionImpl : public JsepSession
 {
 public:
   JsepSessionImpl(const std::string& name, UniquePtr<JsepUuidGenerator> uuidgen)
       : JsepSession(name),
         mIsOfferer(false),
+        mWasOffererLastTime(false),
         mIceControlling(false),
         mRemoteIsIceLite(false),
         mSessionId(0),
         mSessionVersion(0),
         mUuidGen(Move(uuidgen))
   {
   }
 
@@ -235,17 +236,17 @@ private:
                                     size_t* offerToRecv);
   nsresult AddRecvonlyMsections(SdpMediaSection::MediaType mediatype,
                                 size_t count,
                                 Sdp* sdp);
   nsresult CreateReoffer(const Sdp& oldLocalSdp,
                          const Sdp& oldAnswer,
                          Sdp* newSdp);
   void SetupBundle(Sdp* sdp) const;
-  nsresult FinalizeTransportAttributes(Sdp* sdp);
+  nsresult SetupTransportAttributes(const Sdp& newOffer, Sdp* local);
   void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
   nsresult GetIdsFromMsid(const Sdp& sdp,
                           const SdpMediaSection& msection,
                           std::string* streamId,
                           std::string* trackId);
   nsresult GetRemoteIds(const Sdp& sdp,
                         const SdpMediaSection& msection,
                         std::string* streamId,
@@ -281,21 +282,25 @@ private:
                           RefPtr<JsepTrack>* track);
 
   void UpdateTransport(const SdpMediaSection& msection,
                        JsepTransport* transport);
 
   nsresult FinalizeTransport(const SdpAttributeList& remote,
                              const SdpAttributeList& answer,
                              const RefPtr<JsepTransport>& transport);
+  bool AreOldTransportParamsValid(const Sdp& oldAnswer,
+                                  const Sdp& newOffer,
+                                  size_t level);
 
   nsresult AddCandidateToSdp(Sdp* sdp,
                              const std::string& candidate,
                              const std::string& mid,
                              uint16_t level);
+  nsresult GetComponent(const std::string& candidate, size_t* component);
 
   SdpMediaSection* FindMsectionByMid(Sdp& sdp,
                                      const std::string& mid) const;
 
   const SdpMediaSection* FindMsectionByMid(const Sdp& sdp,
                                            const std::string& mid) const;
 
   const SdpGroupAttributeList::Group* FindBundleGroup(const Sdp& sdp) const;
@@ -325,16 +330,17 @@ private:
   std::vector<JsepReceivingTrack> mRemoteTracksAdded;
   std::vector<JsepReceivingTrack> mRemoteTracksRemoved;
   std::vector<RefPtr<JsepTransport> > mTransports;
   // So we can rollback
   std::vector<RefPtr<JsepTransport> > mOldTransports;
   std::vector<JsepTrackPair> mNegotiatedTrackPairs;
 
   bool mIsOfferer;
+  bool mWasOffererLastTime;
   bool mIceControlling;
   std::string mIceUfrag;
   std::string mIcePwd;
   bool mRemoteIsIceLite;
   std::vector<std::string> mIceOptions;
   std::vector<JsepDtlsFingerprint> mDtlsFingerprints;
   uint64_t mSessionId;
   uint64_t mSessionVersion;
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -26,37 +26,18 @@
 #include "signaling/src/jsep/JsepTrack.h"
 #include "signaling/src/jsep/JsepSession.h"
 #include "signaling/src/jsep/JsepSessionImpl.h"
 #include "signaling/src/jsep/JsepTrack.h"
 
 #include "mtransport_test_utils.h"
 
 namespace mozilla {
-static const char* kCandidates[] = {
-  "0 1 UDP 9999 192.168.0.1 2000 typ host",
-  "0 1 UDP 9999 192.168.0.1 2001 typ host",
-  "0 1 UDP 9999 192.168.0.2 2002 typ srflx raddr 10.252.34.97 rport 53594",
-  // Mix up order
-  "0 1 UDP 9999 192.168.1.2 2012 typ srflx raddr 10.252.34.97 rport 53595",
-  "0 1 UDP 9999 192.168.1.1 2010 typ host",
-  "0 1 UDP 9999 192.168.1.1 2011 typ host"
-};
-
-static const char* kRtcpCandidates[] = {
-  "0 2 UDP 9999 192.168.0.1 3000 typ host",
-  "0 2 UDP 9999 192.168.0.1 3001 typ host",
-  "0 2 UDP 9999 192.168.0.2 3002 typ srflx raddr 10.252.34.97 rport 53596",
-  // Mix up order
-  "0 2 UDP 9999 192.168.1.2 3012 typ srflx raddr 10.252.34.97 rport 53597",
-  "0 2 UDP 9999 192.168.1.1 3010 typ host",
-  "0 2 UDP 9999 192.168.1.1 3011 typ host"
-};
-
 static std::string kAEqualsCandidate("a=candidate:");
+const static size_t kNumCandidatesPerComponent = 3;
 
 class JsepSessionTestBase : public ::testing::Test
 {
 };
 
 class FakeUuidGenerator : public mozilla::JsepUuidGenerator
 {
 public:
@@ -367,18 +348,17 @@ protected:
       if ((*i)->GetMediaType() == type) {
         ++result;
       }
     }
     return result;
   }
 
   UniquePtr<Sdp> GetParsedLocalDescription(const JsepSessionImpl& side) const {
-    SipccSdpParser parser;
-    return mozilla::Move(parser.Parse(side.GetLocalDescription()));
+    return Parse(side.GetLocalDescription());
   }
 
   SdpMediaSection* GetMsection(Sdp& sdp,
                                SdpMediaSection::MediaType type,
                                size_t index) const {
     for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
       auto& msection = sdp.GetMediaSection(i);
       if (msection.GetMediaType() != type) {
@@ -586,165 +566,240 @@ protected:
           ASSERT_NE(std::string::npos, answer.find(msidAttr))
             << "Did not find " << msidAttr << " in offer";
         }
       }
     }
     DumpTrackPairs(mSessionAns);
   }
 
-  void
-  GatherCandidates(JsepSession& session)
-  {
-    bool skipped;
-    for (size_t i = 0; i < 3; ++i) {
-      session.AddLocalIceCandidate(
-          kAEqualsCandidate + kCandidates[i], "", 0, &skipped);
-      session.AddLocalIceCandidate(
-          kAEqualsCandidate + kRtcpCandidates[i], "", 0, &skipped);
-    }
-    session.EndOfLocalCandidates("192.168.0.2", 2002, "192.168.0.2", 3002, 0);
-
-    for (size_t i = 3; i < 6; ++i) {
-      session.AddLocalIceCandidate(
-          kAEqualsCandidate + kCandidates[i], "", 1, &skipped);
-      session.AddLocalIceCandidate(
-          kAEqualsCandidate + kRtcpCandidates[i], "", 1, &skipped);
-    }
-    session.EndOfLocalCandidates("192.168.1.2", 2012, "192.168.1.2", 3012, 1);
-
-    std::cerr << "local SDP after candidates: "
-              << session.GetLocalDescription();
-  }
-
-  void
-  TrickleCandidates(JsepSession& session)
-  {
-    for (size_t i = 0; i < 3; ++i) {
-      session.AddRemoteIceCandidate(
-          kAEqualsCandidate + kCandidates[i], "", 0);
-      session.AddRemoteIceCandidate(
-          kAEqualsCandidate + kRtcpCandidates[i], "", 0);
-    }
-
-    for (size_t i = 3; i < 6; ++i) {
-      session.AddRemoteIceCandidate(
-          kAEqualsCandidate + kCandidates[i], "", 1);
-      session.AddRemoteIceCandidate(
-          kAEqualsCandidate + kRtcpCandidates[i], "", 1);
-    }
-
-    std::cerr << "remote SDP after candidates: "
-              << session.GetRemoteDescription();
-  }
-
-  void
-  GatherOffererCandidates()
-  {
-    GatherCandidates(mSessionOff);
-  }
-
-  void
-  TrickleOffererCandidates()
-  {
-    TrickleCandidates(mSessionAns);
-  }
+  typedef enum {
+    RTP = 1,
+    RTCP = 2
+  } ComponentType;
+
+  class CandidateSet {
+    public:
+      CandidateSet() {}
+
+      void Gather(JsepSession& session,
+                  const std::vector<SdpMediaSection::MediaType>& types,
+                  ComponentType maxComponent = RTCP)
+      {
+        for (size_t level = 0; level < types.size(); ++level) {
+          Gather(session, level, RTP);
+          if (types[level] != SdpMediaSection::kApplication &&
+              maxComponent == RTCP) {
+            Gather(session, level, RTCP);
+          }
+        }
+        FinishGathering(session);
+      }
+
+      void Gather(JsepSession& session, size_t level, ComponentType component)
+      {
+        static uint16_t port = 1000;
+        std::vector<std::string> candidates;
+        for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
+          ++port;
+          std::ostringstream candidate;
+          candidate << "0 " << static_cast<uint16_t>(component)
+                    << " UDP 9999 192.168.0.1 " << port << " typ host";
+          bool skipped;
+          session.AddLocalIceCandidate(kAEqualsCandidate + candidate.str(),
+                                       "", level, &skipped);
+          if (!skipped) {
+            mCandidatesToTrickle.push_back(
+                std::pair<uint16_t, std::string>(
+                  level, kAEqualsCandidate + candidate.str()));
+            candidates.push_back(candidate.str());
+          }
+        }
+
+        // Stomp existing candidates
+        mCandidates[level][component] = candidates;
+
+        // Stomp existing defaults
+        mDefaultCandidates[level][component] =
+          std::make_pair("192.168.0.1", port);
+      }
+
+      void FinishGathering(JsepSession& session) const
+      {
+        // Copy so we can be terse and use []
+        for (auto levelAndCandidates : mDefaultCandidates) {
+          ASSERT_EQ(1U, levelAndCandidates.second.count(RTP));
+          session.EndOfLocalCandidates(
+              levelAndCandidates.second[RTP].first,
+              levelAndCandidates.second[RTP].second,
+              // Will be empty string if not present, which is how we indicate
+              // that there is no default for RTCP
+              levelAndCandidates.second[RTCP].first,
+              levelAndCandidates.second[RTCP].second,
+              levelAndCandidates.first);
+        }
+      }
+
+      void Trickle(JsepSession& session)
+      {
+        for (const auto& levelAndCandidate : mCandidatesToTrickle) {
+          session.AddRemoteIceCandidate(levelAndCandidate.second,
+                                        "",
+                                        levelAndCandidate.first);
+        }
+        mCandidatesToTrickle.clear();
+      }
+
+      void CheckRtpCandidates(bool expectRtpCandidates,
+                              const SdpMediaSection& msection,
+                              size_t transportLevel,
+                              const std::string& context) const
+      {
+        auto& attrs = msection.GetAttributeList();
+
+        ASSERT_EQ(expectRtpCandidates,
+                  attrs.HasAttribute(SdpAttribute::kCandidateAttribute))
+          << context << " (level " << msection.GetLevel() << ")";
+
+        if (expectRtpCandidates) {
+          // Copy so we can be terse and use []
+          auto expectedCandidates = mCandidates;
+          ASSERT_LE(kNumCandidatesPerComponent,
+                    expectedCandidates[transportLevel][RTP].size());
+
+          auto& candidates = attrs.GetCandidate();
+          ASSERT_LE(kNumCandidatesPerComponent, candidates.size())
+            << context << " (level " << msection.GetLevel() << ")";
+          for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
+            ASSERT_EQ(expectedCandidates[transportLevel][RTP][i], candidates[i])
+              << context << " (level " << msection.GetLevel() << ")";
+          }
+        }
+      }
+
+      void CheckRtcpCandidates(bool expectRtcpCandidates,
+                               const SdpMediaSection& msection,
+                               size_t transportLevel,
+                               const std::string& context) const
+      {
+        auto& attrs = msection.GetAttributeList();
+
+        if (expectRtcpCandidates) {
+          // Copy so we can be terse and use []
+          auto expectedCandidates = mCandidates;
+          ASSERT_LE(kNumCandidatesPerComponent,
+                    expectedCandidates[transportLevel][RTCP].size());
+
+          ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kCandidateAttribute))
+            << context << " (level " << msection.GetLevel() << ")";
+          auto& candidates = attrs.GetCandidate();
+          ASSERT_EQ(kNumCandidatesPerComponent * 2, candidates.size())
+            << context << " (level " << msection.GetLevel() << ")";
+          for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) {
+            ASSERT_EQ(expectedCandidates[transportLevel][RTCP][i],
+                      candidates[i + kNumCandidatesPerComponent])
+              << context << " (level " << msection.GetLevel() << ")";
+          }
+        }
+      }
+
+      void CheckDefaultRtpCandidate(bool expectDefault,
+                                    const SdpMediaSection& msection,
+                                    size_t transportLevel,
+                                    const std::string& context) const
+      {
+        if (expectDefault) {
+          // Copy so we can be terse and use []
+          auto defaultCandidates = mDefaultCandidates;
+          ASSERT_EQ(defaultCandidates[transportLevel][RTP].first,
+                    msection.GetConnection().GetAddress())
+            << context << " (level " << msection.GetLevel() << ")";
+          ASSERT_EQ(defaultCandidates[transportLevel][RTP].second,
+                    msection.GetPort())
+            << context << " (level " << msection.GetLevel() << ")";
+        } else {
+          ASSERT_EQ("0.0.0.0", msection.GetConnection().GetAddress())
+            << context << " (level " << msection.GetLevel() << ")";
+          ASSERT_EQ(9U, msection.GetPort())
+            << context << " (level " << msection.GetLevel() << ")";
+        }
+      }
+
+      void CheckDefaultRtcpCandidate(bool expectDefault,
+                                     const SdpMediaSection& msection,
+                                     size_t transportLevel,
+                                     const std::string& context) const
+      {
+        if (expectDefault) {
+          // Copy so we can be terse and use []
+          auto defaultCandidates = mDefaultCandidates;
+          ASSERT_TRUE(msection.GetAttributeList().HasAttribute(
+                SdpAttribute::kRtcpAttribute))
+            << context << " (level " << msection.GetLevel() << ")";
+          auto& rtcpAttr = msection.GetAttributeList().GetRtcp();
+          ASSERT_EQ(defaultCandidates[transportLevel][RTCP].second,
+                    rtcpAttr.mPort)
+            << context << " (level " << msection.GetLevel() << ")";
+          ASSERT_EQ(sdp::kInternet, rtcpAttr.mNetType)
+            << context << " (level " << msection.GetLevel() << ")";
+          ASSERT_EQ(sdp::kIPv4, rtcpAttr.mAddrType)
+            << context << " (level " << msection.GetLevel() << ")";
+          ASSERT_EQ(defaultCandidates[transportLevel][RTCP].first,
+                    rtcpAttr.mAddress)
+            << context << " (level " << msection.GetLevel() << ")";
+        } else {
+          ASSERT_FALSE(msection.GetAttributeList().HasAttribute(
+                SdpAttribute::kRtcpAttribute))
+            << context << " (level " << msection.GetLevel() << ")";
+        }
+      }
+
+    private:
+      typedef size_t Level;
+      typedef std::string Candidate;
+      typedef std::string Address;
+      typedef uint16_t Port;
+      // Default candidates are put into the m-line, c-line, and rtcp
+      // attribute for endpoints that don't support ICE.
+      std::map<Level,
+               std::map<ComponentType,
+                        std::pair<Address, Port>>> mDefaultCandidates;
+      std::map<Level,
+               std::map<ComponentType,
+                        std::vector<Candidate>>> mCandidates;
+      // Level/candidate pairs that need to be trickled
+      std::vector<std::pair<Level, Candidate>> mCandidatesToTrickle;
+  };
 
   // For streaming parse errors
   std::string
   GetParseErrors(const SipccSdpParser& parser) const
   {
     std::stringstream output;
     for (auto e = parser.GetParseErrors().begin();
          e != parser.GetParseErrors().end();
          ++e) {
       output << e->first << ": " << e->second << std::endl;
     }
     return output.str();
   }
 
-  void
-  ValidateCandidates(JsepSession& session, bool local)
+  void CheckEndOfCandidates(bool expectEoc,
+                            const SdpMediaSection& msection,
+                            const std::string& context)
   {
-    std::string sdp =
-        local ? session.GetLocalDescription() : session.GetRemoteDescription();
-    SipccSdpParser parser;
-    UniquePtr<Sdp> parsed = parser.Parse(sdp);
-    ASSERT_TRUE(!!parsed) << "Parse failed on " << std::endl << sdp << std::endl
-                          << "Errors were: " << GetParseErrors(parser);
-    ASSERT_LT(0U, parsed->GetMediaSectionCount());
-
-    auto& msection_0 = parsed->GetMediaSection(0);
-
-    // We should not be doing things like setting the c-line on remote SDP
-    if (local) {
-      ASSERT_EQ("192.168.0.2", msection_0.GetConnection().GetAddress());
-      ASSERT_EQ(2002U, msection_0.GetPort());
-      ASSERT_TRUE(msection_0.GetAttributeList().HasAttribute(
-            SdpAttribute::kRtcpAttribute));
-      auto& rtcpAttr = msection_0.GetAttributeList().GetRtcp();
-      ASSERT_EQ(3002U, rtcpAttr.mPort);
-      ASSERT_EQ(sdp::kInternet, rtcpAttr.mNetType);
-      ASSERT_EQ(sdp::kIPv4, rtcpAttr.mAddrType);
-      ASSERT_EQ("192.168.0.2", rtcpAttr.mAddress);
-      ASSERT_TRUE(msection_0.GetAttributeList().HasAttribute(
-            SdpAttribute::kEndOfCandidatesAttribute));
+    if (expectEoc) {
+      ASSERT_TRUE(msection.GetAttributeList().HasAttribute(
+            SdpAttribute::kEndOfCandidatesAttribute))
+        << context << " (level " << msection.GetLevel() << ")";
+    } else {
+      ASSERT_FALSE(msection.GetAttributeList().HasAttribute(
+            SdpAttribute::kEndOfCandidatesAttribute))
+        << context << " (level " << msection.GetLevel() << ")";
     }
-
-    auto& attrs_0 = msection_0.GetAttributeList();
-    ASSERT_TRUE(attrs_0.HasAttribute(SdpAttribute::kCandidateAttribute));
-
-    auto& candidates_0 = attrs_0.GetCandidate();
-    ASSERT_EQ(6U, candidates_0.size());
-    ASSERT_EQ(kCandidates[0], candidates_0[0]);
-    ASSERT_EQ(kRtcpCandidates[0], candidates_0[1]);
-    ASSERT_EQ(kCandidates[1], candidates_0[2]);
-    ASSERT_EQ(kRtcpCandidates[1], candidates_0[3]);
-    ASSERT_EQ(kCandidates[2], candidates_0[4]);
-    ASSERT_EQ(kRtcpCandidates[2], candidates_0[5]);
-
-    if (parsed->GetMediaSectionCount() > 1) {
-      auto& msection_1 = parsed->GetMediaSection(1);
-
-      if (local) {
-        ASSERT_EQ("192.168.1.2", msection_1.GetConnection().GetAddress());
-        ASSERT_EQ(2012U, msection_1.GetPort());
-        auto& rtcpAttr = msection_1.GetAttributeList().GetRtcp();
-        ASSERT_EQ(3012U, rtcpAttr.mPort);
-        ASSERT_EQ(sdp::kInternet, rtcpAttr.mNetType);
-        ASSERT_EQ(sdp::kIPv4, rtcpAttr.mAddrType);
-        ASSERT_EQ("192.168.1.2", rtcpAttr.mAddress);
-        ASSERT_TRUE(msection_0.GetAttributeList().HasAttribute(
-              SdpAttribute::kEndOfCandidatesAttribute));
-      }
-
-      auto& attrs_1 = msection_1.GetAttributeList();
-      ASSERT_TRUE(attrs_1.HasAttribute(SdpAttribute::kCandidateAttribute));
-
-      auto& candidates_1 = attrs_1.GetCandidate();
-      ASSERT_EQ(6U, candidates_1.size());
-      ASSERT_EQ(kCandidates[3], candidates_1[0]);
-      ASSERT_EQ(kRtcpCandidates[3], candidates_1[1]);
-      ASSERT_EQ(kCandidates[4], candidates_1[2]);
-      ASSERT_EQ(kRtcpCandidates[4], candidates_1[3]);
-      ASSERT_EQ(kCandidates[5], candidates_1[4]);
-      ASSERT_EQ(kRtcpCandidates[5], candidates_1[5]);
-    }
-  }
-
-  void
-  ValidateOffererCandidates()
-  {
-    ValidateCandidates(mSessionOff, true);
-  }
-
-  void
-  ValidateAnswererCandidates()
-  {
-    ValidateCandidates(mSessionAns, false);
   }
 
   void
   DisableMsid(std::string* sdp) const {
     size_t pos = sdp->find("a=msid-semantic");
     ASSERT_NE(std::string::npos, pos);
     (*sdp)[pos + 2] = 'X'; // garble, a=Xsid-semantic
   }
@@ -753,18 +808,17 @@ protected:
   DisableBundle(std::string* sdp) const {
     size_t pos = sdp->find("a=group:BUNDLE");
     ASSERT_NE(std::string::npos, pos);
     (*sdp)[pos + 11] = 'G'; // garble, a=group:BUNGLE
   }
 
   void
   DisableMsection(std::string* sdp, size_t level) const {
-    SipccSdpParser parser;
-    UniquePtr<Sdp> parsed = parser.Parse(*sdp);
+    UniquePtr<Sdp> parsed(Parse(*sdp));
     ASSERT_TRUE(parsed.get());
     ASSERT_LT(level, parsed->GetMediaSectionCount());
     parsed->GetMediaSection(level).SetPort(0);
 
     auto& attrs = parsed->GetMediaSection(level).GetAttributeList();
 
     ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kMidAttribute));
     std::string mid = attrs.GetMid();
@@ -808,28 +862,39 @@ protected:
       }
       if (i->mReceiving) {
         std::cerr << "Receiving-->" << std::endl;
         DumpTrack(*i->mReceiving);
       }
     }
   }
 
+  UniquePtr<Sdp>
+  Parse(const std::string& sdp) const
+  {
+    SipccSdpParser parser;
+    UniquePtr<Sdp> parsed = parser.Parse(sdp);
+    EXPECT_TRUE(parsed.get()) << "Should have valid SDP" << std::endl
+                              << "Errors were: " << GetParseErrors(parser);
+    return parsed;
+  }
+
   JsepSessionImpl mSessionOff;
+  CandidateSet mOffCandidates;
   JsepSessionImpl mSessionAns;
+  CandidateSet mAnsCandidates;
   std::vector<SdpMediaSection::MediaType> types;
+  std::vector<std::pair<std::string, uint16_t>> mGatheredCandidates;
 
 private:
   void
   ValidateTransport(TransportData& source, const std::string& sdp_str)
   {
-    SipccSdpParser parser;
-    auto sdp = parser.Parse(sdp_str);
-    ASSERT_TRUE(!!sdp) << "Should have valid SDP" << std::endl
-                       << "Errors were: " << GetParseErrors(parser);
+    UniquePtr<Sdp> sdp(Parse(sdp_str));
+    ASSERT_TRUE(!!sdp);
     size_t num_m_sections = sdp->GetMediaSectionCount();
     for (size_t i = 0; i < num_m_sections; ++i) {
       auto& msection = sdp->GetMediaSection(i);
 
       if (msection.GetMediaType() == SdpMediaSection::kApplication) {
         ASSERT_EQ(SdpMediaSection::kDtlsSctp, msection.GetProtocol());
       } else {
         ASSERT_EQ(SdpMediaSection::kRtpSavpf, msection.GetProtocol());
@@ -1988,27 +2053,299 @@ TEST_P(JsepSessionTest, RenegotiationAns
             answererPairs[0].mRtpTransport.get());
 }
 
 TEST_P(JsepSessionTest, FullCallWithCandidates)
 {
   AddTracks(mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
-  GatherOffererCandidates();
-  ValidateOffererCandidates();
+  mOffCandidates.Gather(mSessionOff, types);
+
+  UniquePtr<Sdp> localOffer(Parse(mSessionOff.GetLocalDescription()));
+  for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
+    mOffCandidates.CheckRtpCandidates(
+        true, localOffer->GetMediaSection(i), i,
+        "Local offer after gathering should have RTP candidates.");
+    mOffCandidates.CheckDefaultRtpCandidate(
+        true, localOffer->GetMediaSection(i), i,
+        "Local offer after gathering should have a default RTP candidate.");
+    mOffCandidates.CheckRtcpCandidates(
+        types[i] != SdpMediaSection::kApplication,
+        localOffer->GetMediaSection(i), i,
+        "Local offer after gathering should have RTCP candidates "
+        "(unless m=application)");
+    mOffCandidates.CheckDefaultRtcpCandidate(
+        types[i] != SdpMediaSection::kApplication,
+        localOffer->GetMediaSection(i), i,
+        "Local offer after gathering should have a default RTCP candidate "
+        "(unless m=application)");
+    CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
+        "Local offer after gathering should have an end-of-candidates.");
+  }
+
   SetRemoteOffer(offer);
-  TrickleOffererCandidates();
-  ValidateAnswererCandidates();
+  mOffCandidates.Trickle(mSessionAns);
+
+  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns.GetRemoteDescription()));
+  for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
+    mOffCandidates.CheckRtpCandidates(
+        true, remoteOffer->GetMediaSection(i), i,
+        "Remote offer after trickle should have RTP candidates.");
+    mOffCandidates.CheckDefaultRtpCandidate(
+        false, remoteOffer->GetMediaSection(i), i,
+        "Initial remote offer should not have a default RTP candidate.");
+    mOffCandidates.CheckRtcpCandidates(
+        types[i] != SdpMediaSection::kApplication,
+        remoteOffer->GetMediaSection(i), i,
+        "Remote offer after trickle should have RTCP candidates "
+        "(unless m=application)");
+    mOffCandidates.CheckDefaultRtcpCandidate(
+        false, remoteOffer->GetMediaSection(i), i,
+        "Initial remote offer should not have a default RTCP candidate.");
+    CheckEndOfCandidates(false, remoteOffer->GetMediaSection(i),
+        "Initial remote offer should not have an end-of-candidates.");
+  }
+
+  AddTracks(mSessionAns);
+  std::string answer = CreateAnswer();
+  SetLocalAnswer(answer);
+  // This will gather candidates that mSessionAns knows it doesn't need.
+  // They should not be present in the SDP.
+  mAnsCandidates.Gather(mSessionAns, types);
+
+  UniquePtr<Sdp> localAnswer(Parse(mSessionAns.GetLocalDescription()));
+  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
+    mAnsCandidates.CheckRtpCandidates(
+        i == 0, localAnswer->GetMediaSection(i), i,
+        "Local answer after gathering should have RTP candidates on level 0.");
+    mAnsCandidates.CheckDefaultRtpCandidate(
+        true, localAnswer->GetMediaSection(i), 0,
+        "Local answer after gathering should have a default RTP candidate "
+        "on all levels that matches transport level 0.");
+    mAnsCandidates.CheckRtcpCandidates(
+        false, localAnswer->GetMediaSection(i), i,
+        "Local answer after gathering should not have RTCP candidates "
+        "(because we're answering with rtcp-mux)");
+    mAnsCandidates.CheckDefaultRtcpCandidate(
+        false, localAnswer->GetMediaSection(i), i,
+        "Local answer after gathering should not have a default RTCP candidate "
+        "(because we're answering with rtcp-mux)");
+    CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
+        "Local answer after gathering should have an end-of-candidates only for"
+        " level 0.");
+  }
+
+  SetRemoteAnswer(answer);
+  mAnsCandidates.Trickle(mSessionOff);
+
+  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff.GetRemoteDescription()));
+  for (size_t i = 0; i < remoteAnswer->GetMediaSectionCount(); ++i) {
+    mAnsCandidates.CheckRtpCandidates(
+        i == 0, remoteAnswer->GetMediaSection(i), i,
+        "Remote answer after trickle should have RTP candidates on level 0.");
+    mAnsCandidates.CheckDefaultRtpCandidate(
+        false, remoteAnswer->GetMediaSection(i), i,
+        "Remote answer after trickle should not have a default RTP candidate.");
+    mAnsCandidates.CheckRtcpCandidates(
+        false, remoteAnswer->GetMediaSection(i), i,
+        "Remote answer after trickle should not have RTCP candidates "
+        "(because we're answering with rtcp-mux)");
+    mAnsCandidates.CheckDefaultRtcpCandidate(
+        false, remoteAnswer->GetMediaSection(i), i,
+        "Remote answer after trickle should not have a default RTCP "
+        "candidate.");
+    CheckEndOfCandidates(false, remoteAnswer->GetMediaSection(i),
+        "Remote answer after trickle should not have an end-of-candidates.");
+  }
+}
+
+TEST_P(JsepSessionTest, RenegotiationWithCandidates)
+{
+  AddTracks(mSessionOff);
+  std::string offer = CreateOffer();
+  SetLocalOffer(offer);
+  mOffCandidates.Gather(mSessionOff, types);
+  SetRemoteOffer(offer);
+  mOffCandidates.Trickle(mSessionAns);
   AddTracks(mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
+  mAnsCandidates.Gather(mSessionAns, types);
   SetRemoteAnswer(answer);
+  mAnsCandidates.Trickle(mSessionOff);
+
+  offer = CreateOffer();
+  SetLocalOffer(offer);
+
+  UniquePtr<Sdp> parsedOffer(Parse(offer));
+  for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
+    mOffCandidates.CheckRtpCandidates(
+        i == 0, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should have RTP candidates on level 0"
+        " only.");
+    mOffCandidates.CheckDefaultRtpCandidate(
+        i == 0, parsedOffer->GetMediaSection(i), 0,
+        "Local reoffer before gathering should have a default RTP candidate "
+        "on level 0 only.");
+    mOffCandidates.CheckRtcpCandidates(
+        false, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should not have RTCP candidates.");
+    mOffCandidates.CheckDefaultRtcpCandidate(
+        false, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should not have a default RTCP "
+        "candidate.");
+    CheckEndOfCandidates(false, parsedOffer->GetMediaSection(i),
+        "Local reoffer before gathering should not have an end-of-candidates.");
+  }
+
+  // mSessionAns should generate a reoffer that is similar
+  std::string otherOffer;
+  JsepOfferOptions defaultOptions;
+  nsresult rv = mSessionAns.CreateOffer(defaultOptions, &otherOffer);
+  ASSERT_EQ(NS_OK, rv);
+  parsedOffer = Parse(otherOffer);
+  for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
+    mAnsCandidates.CheckRtpCandidates(
+        i == 0, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should have RTP candidates on level 0"
+        " only. (previous answerer)");
+    mAnsCandidates.CheckDefaultRtpCandidate(
+        i == 0, parsedOffer->GetMediaSection(i), 0,
+        "Local reoffer before gathering should have a default RTP candidate "
+        "on level 0 only. (previous answerer)");
+    mAnsCandidates.CheckRtcpCandidates(
+        false, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should not have RTCP candidates."
+        " (previous answerer)");
+    mAnsCandidates.CheckDefaultRtcpCandidate(
+        false, parsedOffer->GetMediaSection(i), i,
+        "Local reoffer before gathering should not have a default RTCP "
+        "candidate. (previous answerer)");
+    CheckEndOfCandidates(false, parsedOffer->GetMediaSection(i),
+        "Local reoffer before gathering should not have an end-of-candidates. "
+        "(previous answerer)");
+  }
+
+  // Ok, let's continue with the renegotiation
+  SetRemoteOffer(offer);
+
+  // PeerConnection will not re-gather for RTP, but it will for RTCP in case
+  // the answerer decides to turn off rtcp-mux.
+  if (types[0] != SdpMediaSection::kApplication) {
+    mOffCandidates.Gather(mSessionOff, 0, RTCP);
+  }
+
+  // Since the remaining levels were bundled, PeerConnection will re-gather for
+  // both RTP and RTCP, in case the answerer rejects bundle.
+  for (size_t level = 1; level < types.size(); ++level) {
+    mOffCandidates.Gather(mSessionOff, level, RTP);
+    if (types[level] != SdpMediaSection::kApplication) {
+      mOffCandidates.Gather(mSessionOff, level, RTCP);
+    }
+  }
+  mOffCandidates.FinishGathering(mSessionOff);
+
+  mOffCandidates.Trickle(mSessionAns);
+
+  UniquePtr<Sdp> localOffer(Parse(mSessionOff.GetLocalDescription()));
+  for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
+    mOffCandidates.CheckRtpCandidates(
+        true, localOffer->GetMediaSection(i), i,
+        "Local reoffer after gathering should have RTP candidates.");
+    mOffCandidates.CheckDefaultRtpCandidate(
+        true, localOffer->GetMediaSection(i), i,
+        "Local reoffer after gathering should have a default RTP candidate.");
+    mOffCandidates.CheckRtcpCandidates(
+        types[i] != SdpMediaSection::kApplication,
+        localOffer->GetMediaSection(i), i,
+        "Local reoffer after gathering should have RTCP candidates "
+        "(unless m=application)");
+    mOffCandidates.CheckDefaultRtcpCandidate(
+        types[i] != SdpMediaSection::kApplication,
+        localOffer->GetMediaSection(i), i,
+        "Local reoffer after gathering should have a default RTCP candidate "
+        "(unless m=application)");
+    CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
+        "Local reoffer after gathering should have an end-of-candidates.");
+  }
+
+  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns.GetRemoteDescription()));
+  for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
+    mOffCandidates.CheckRtpCandidates(
+        true, remoteOffer->GetMediaSection(i), i,
+        "Remote reoffer after trickle should have RTP candidates.");
+    mOffCandidates.CheckDefaultRtpCandidate(
+        i == 0, remoteOffer->GetMediaSection(i), i,
+        "Remote reoffer should have a default RTP candidate on level 0 "
+        "(because it was gathered last offer/answer).");
+    mOffCandidates.CheckRtcpCandidates(
+        types[i] != SdpMediaSection::kApplication,
+        remoteOffer->GetMediaSection(i), i,
+        "Remote reoffer after trickle should have RTCP candidates.");
+    mOffCandidates.CheckDefaultRtcpCandidate(
+        false, remoteOffer->GetMediaSection(i), i,
+        "Remote reoffer should not have a default RTCP candidate.");
+    CheckEndOfCandidates(false, remoteOffer->GetMediaSection(i),
+        "Remote reoffer should not have an end-of-candidates.");
+  }
+
+  answer = CreateAnswer();
+  SetLocalAnswer(answer);
+  SetRemoteAnswer(answer);
+  // No candidates should be gathered at the answerer, but default candidates
+  // should be set.
+  mAnsCandidates.FinishGathering(mSessionAns);
+
+  UniquePtr<Sdp> localAnswer(Parse(mSessionAns.GetLocalDescription()));
+  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
+    mAnsCandidates.CheckRtpCandidates(
+        i == 0, localAnswer->GetMediaSection(i), i,
+        "Local reanswer after gathering should have RTP candidates on level "
+        "0.");
+    mAnsCandidates.CheckDefaultRtpCandidate(
+        true, localAnswer->GetMediaSection(i), 0,
+        "Local reanswer after gathering should have a default RTP candidate "
+        "on all levels that matches transport level 0.");
+    mAnsCandidates.CheckRtcpCandidates(
+        false, localAnswer->GetMediaSection(i), i,
+        "Local reanswer after gathering should not have RTCP candidates "
+        "(because we're reanswering with rtcp-mux)");
+    mAnsCandidates.CheckDefaultRtcpCandidate(
+        false, localAnswer->GetMediaSection(i), i,
+        "Local reanswer after gathering should not have a default RTCP "
+        "candidate (because we're reanswering with rtcp-mux)");
+    CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
+        "Local reanswer after gathering should have an end-of-candidates only "
+        "for level 0.");
+  }
+
+  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff.GetRemoteDescription()));
+  for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
+    mAnsCandidates.CheckRtpCandidates(
+        i == 0, remoteAnswer->GetMediaSection(i), i,
+        "Remote reanswer after trickle should have RTP candidates on level 0.");
+    mAnsCandidates.CheckDefaultRtpCandidate(
+        i == 0, remoteAnswer->GetMediaSection(i), i,
+        "Remote reanswer should have a default RTP candidate on level 0 "
+        "(because it was gathered last offer/answer).");
+    mAnsCandidates.CheckRtcpCandidates(
+        false, remoteAnswer->GetMediaSection(i), i,
+        "Remote reanswer after trickle should not have RTCP candidates "
+        "(because we're reanswering with rtcp-mux)");
+    mAnsCandidates.CheckDefaultRtcpCandidate(
+        false, remoteAnswer->GetMediaSection(i), i,
+        "Remote reanswer after trickle should not have a default RTCP "
+        "candidate.");
+    CheckEndOfCandidates(false, remoteAnswer->GetMediaSection(i),
+        "Remote reanswer after trickle should not have an end-of-candidates.");
+  }
 }
 
+
 INSTANTIATE_TEST_CASE_P(
     Variants,
     JsepSessionTest,
     ::testing::Values("audio",
                       "video",
                       "datachannel",
                       "audio,video",
                       "video,audio",
@@ -2034,20 +2371,18 @@ INSTANTIATE_TEST_CASE_P(
 TEST_F(JsepSessionTest, OfferAnswerRecvOnlyLines)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(2U));
   options.mDontOfferDataChannel = Some(true);
   std::string offer = CreateOffer(Some(options));
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
-  ASSERT_TRUE(!!parsedOffer) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> parsedOffer(Parse(offer));
+  ASSERT_TRUE(!!parsedOffer);
 
   ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             parsedOffer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             parsedOffer->GetMediaSection(0).GetAttributeList().GetDirection());
   ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
         SdpAttribute::kSsrcAttribute));
@@ -2074,17 +2409,17 @@ TEST_F(JsepSessionTest, OfferAnswerRecvO
       SdpAttribute::kRtcpMuxAttribute));
 
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   AddTracks(mSessionAns, "audio,video");
   SetRemoteOffer(offer, CHECK_SUCCESS);
 
   std::string answer = CreateAnswer();
-  UniquePtr<Sdp> parsedAnswer = parser.Parse(answer);
+  UniquePtr<Sdp> parsedAnswer(Parse(answer));
 
   ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             parsedAnswer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kSendonly,
             parsedAnswer->GetMediaSection(0).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpMediaSection::kVideo,
             parsedAnswer->GetMediaSection(1).GetMediaType());
@@ -2113,20 +2448,18 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
   AddTracks(mSessionOff, "audio,video,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   options.mDontOfferDataChannel = Some(true);
   std::string offer = CreateOffer(Some(options));
 
-  SipccSdpParser parser;
-  auto outputSdp = parser.Parse(offer);
-  ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> outputSdp(Parse(offer));
+  ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             outputSdp->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kSendonly,
             outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpMediaSection::kVideo,
             outputSdp->GetMediaSection(1).GetMediaType());
@@ -2145,17 +2478,17 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
       SdpAttribute::kRtcpMuxAttribute));
 
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   AddTracks(mSessionAns, "audio,video");
   SetRemoteOffer(offer, CHECK_SUCCESS);
 
   std::string answer = CreateAnswer();
-  outputSdp = parser.Parse(answer);
+  outputSdp = Parse(answer);
 
   ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             outputSdp->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
   ASSERT_EQ(SdpMediaSection::kVideo,
             outputSdp->GetMediaSection(1).GetMediaType());
@@ -2169,51 +2502,49 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
 
 TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> offer(parser.Parse(mSessionOff.GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(mSessionOff.GetLocalDescription()));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(parser.Parse(mSessionAns.GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(mSessionAns.GetLocalDescription()));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
 TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveVideo = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> offer(parser.Parse(mSessionOff.GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(mSessionOff.GetLocalDescription()));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(parser.Parse(mSessionAns.GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(mSessionAns.GetLocalDescription()));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
@@ -2224,20 +2555,18 @@ TEST_F(JsepSessionTest, CreateOfferNoDat
   mSessionOff.AddTrack(msta);
 
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v1"));
   mSessionOff.AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
-  SipccSdpParser parser;
-  auto outputSdp = parser.Parse(offer);
-  ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> outputSdp(Parse(offer));
+  ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             outputSdp->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpMediaSection::kVideo,
             outputSdp->GetMediaSection(1).GetMediaType());
 }
 
@@ -2250,20 +2579,18 @@ TEST_F(JsepSessionTest, ValidateOfferedC
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
   mSessionOff.AddTrack(msta);
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v2"));
   mSessionOff.AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
-  SipccSdpParser parser;
-  auto outputSdp = parser.Parse(offer);
-  ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> outputSdp(Parse(offer));
+  ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& video_section = outputSdp->GetMediaSection(1);
   ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   auto& video_attrs = video_section.GetAttributeList();
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
 
   ASSERT_EQ(4U, video_section.GetFormats().size());
@@ -2384,20 +2711,18 @@ TEST_F(JsepSessionTest, ValidateAnswered
       new JsepTrack(SdpMediaSection::kAudio, "answerer_stream", "a1"));
   mSessionAns.AddTrack(msta);
   RefPtr<JsepTrack> mstv1_ans(
       new JsepTrack(SdpMediaSection::kVideo, "answerer_stream", "v1"));
   mSessionAns.AddTrack(mstv1);
 
   std::string answer = CreateAnswer();
 
-  SipccSdpParser parser;
-  auto outputSdp = parser.Parse(answer);
-  ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> outputSdp(Parse(answer));
+  ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& video_section = outputSdp->GetMediaSection(1);
   ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   auto& video_attrs = video_section.GetAttributeList();
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
 
   // TODO(bug 1099351): Once fixed, this stuff will need to be updated.
@@ -2884,20 +3209,18 @@ TEST_P(JsepSessionTest, TestRejectMline)
   }
 
   std::string offer = CreateOffer();
   mSessionOff.SetLocalDescription(kJsepSdpOffer, offer);
   mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
 
   std::string answer = CreateAnswer();
 
-  SipccSdpParser parser;
-  auto outputSdp = parser.Parse(answer);
-  ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
-                           << "Errors were: " << GetParseErrors(parser);
+  UniquePtr<Sdp> outputSdp(Parse(answer));
+  ASSERT_TRUE(!!outputSdp);
 
   ASSERT_NE(0U, outputSdp->GetMediaSectionCount());
   SdpMediaSection* failed_section = nullptr;
 
   for (size_t i = 0; i < outputSdp->GetMediaSectionCount(); ++i) {
     if (outputSdp->GetMediaSection(i).GetMediaType() == types.front()) {
       failed_section = &outputSdp->GetMediaSection(i);
     }
@@ -2937,18 +3260,17 @@ TEST_F(JsepSessionTest, CreateOfferNoMli
 
 TEST_F(JsepSessionTest, TestIceLite)
 {
   AddTracks(mSessionOff, "audio");
   AddTracks(mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
+  UniquePtr<Sdp> parsedOffer(Parse(offer));
   parsedOffer->GetAttributeList().SetAttribute(
       new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
 
   std::ostringstream os;
   parsedOffer->Serialize(os);
   SetRemoteOffer(os.str(), CHECK_SUCCESS);
 
   ASSERT_TRUE(mSessionAns.RemoteIsIceLite());
@@ -2983,33 +3305,32 @@ TEST_F(JsepSessionTest, TestExtmap)
   mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 2
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
+  UniquePtr<Sdp> parsedOffer(Parse(offer));
   ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount());
 
   auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList();
   ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps;
   ASSERT_EQ(3U, offerExtmap.size());
   ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
       offerExtmap[0].extensionname);
   ASSERT_EQ(1U, offerExtmap[0].entry);
   ASSERT_EQ("foo", offerExtmap[1].extensionname);
   ASSERT_EQ(2U, offerExtmap[1].entry);
   ASSERT_EQ("bar", offerExtmap[2].extensionname);
   ASSERT_EQ(3U, offerExtmap[2].entry);
 
-  UniquePtr<Sdp> parsedAnswer = parser.Parse(answer);
+  UniquePtr<Sdp> parsedAnswer(Parse(answer));
   ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount());
 
   auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList();
   ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute));
   auto& answerExtmap = answerMediaAttrs.GetExtmap().mExtmaps;
   ASSERT_EQ(2U, answerExtmap.size());
   ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
       answerExtmap[0].extensionname);
@@ -3021,18 +3342,17 @@ TEST_F(JsepSessionTest, TestExtmap)
 
 TEST_F(JsepSessionTest, TestRtcpFbStar)
 {
   AddTracks(mSessionOff, "video");
   AddTracks(mSessionAns, "video");
 
   std::string offer = CreateOffer();
 
-  SipccSdpParser parser;
-  UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
+  UniquePtr<Sdp> parsedOffer(Parse(offer));
   auto* rtcpfbs = new SdpRtcpFbAttributeList;
   rtcpfbs->PushEntry("*", SdpRtcpFbAttributeList::kNack);
   parsedOffer->GetMediaSection(0).GetAttributeList().SetAttribute(rtcpfbs);
   offer = parsedOffer->ToString();
 
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -13,17 +13,20 @@ It also defines the set of variables and
 If you are looking for the absolute authority on what moz.build files can
 contain, you've come to the right place.
 """
 
 from __future__ import unicode_literals
 
 import os
 
-from collections import OrderedDict
+from collections import (
+    Counter,
+    OrderedDict,
+)
 from mozbuild.util import (
     HierarchicalStringList,
     HierarchicalStringListWithFlagsFactory,
     KeyedDefaultDict,
     List,
     memoize,
     memoized_property,
     ReadOnlyKeyedDefaultDict,
@@ -571,16 +574,59 @@ class Files(SubContext):
         """
         d = {}
         if 'BUG_COMPONENT' in self:
             bc = self['BUG_COMPONENT']
             d['bug_component'] = (bc.product, bc.component)
 
         return d
 
+    @staticmethod
+    def aggregate(files):
+        """Given a mapping of path to Files, obtain aggregate results.
+
+        Consumers may want to extract useful information from a collection of
+        Files describing paths. e.g. given the files info data for N paths,
+        recommend a single bug component based on the most frequent one. This
+        function provides logic for deriving aggregate knowledge from a
+        collection of path File metadata.
+
+        Note: the intent of this function is to operate on the result of
+        :py:func:`mozbuild.frontend.reader.BuildReader.files_info`. The
+        :py:func:`mozbuild.frontend.context.Files` instances passed in are
+        thus the "collapsed" (``__iadd__``ed) results of all ``Files`` from all
+        moz.build files relevant to a specific path, not individual ``Files``
+        instances from a single moz.build file.
+        """
+        d = {}
+
+        bug_components = Counter()
+
+        for f in files.values():
+            bug_component = f.get('BUG_COMPONENT')
+            if bug_component:
+                bug_components[bug_component] += 1
+
+        d['bug_component_counts'] = []
+        for c, count in bug_components.most_common():
+            component = (c.product, c.component)
+            d['bug_component_counts'].append((c, count))
+
+            if 'recommended_bug_component' not in d:
+                d['recommended_bug_component'] = component
+                recommended_count = count
+            elif count == recommended_count:
+                # Don't recommend a component if it doesn't have a clear lead.
+                d['recommended_bug_component'] = None
+
+        # In case no bug components.
+        d.setdefault('recommended_bug_component', None)
+
+        return d
+
 
 # This defines functions that create sub-contexts.
 #
 # Values are classes that are SubContexts. The class name will be turned into
 # a function that when called emits an instance of that class.
 #
 # Arbitrary arguments can be passed to the class constructor. The first
 # argument is always the parent context. It is up to each class to perform
--- a/python/mozbuild/mozbuild/test/frontend/test_context.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_context.py
@@ -6,16 +6,17 @@ import os
 import unittest
 
 from mozunit import main
 
 from mozbuild.frontend.context import (
     Context,
     ContextDerivedTypedList,
     ContextDerivedTypedListWithItems,
+    Files,
     FUNCTIONS,
     ObjDirPath,
     Path,
     SourcePath,
     SPECIAL_VARIABLES,
     SUBCONTEXTS,
     VARIABLES,
 )
@@ -560,10 +561,69 @@ class TestPaths(unittest.TestCase):
             l[p].foo = True
 
         for p_str, p_path in zip(paths, l):
             self.assertEqual(p_str, p_path)
             self.assertEqual(p_path, Path(ctxt1, p_str))
             self.assertEqual(l[p_str].foo, True)
             self.assertEqual(l[p_path].foo, True)
 
+
+class TestFiles(unittest.TestCase):
+    def test_aggregate_empty(self):
+        c = Context({})
+
+        files = {'moz.build': Files(c, pattern='**')}
+
+        self.assertEqual(Files.aggregate(files), {
+            'bug_component_counts': [],
+            'recommended_bug_component': None,
+        })
+
+    def test_single_bug_component(self):
+        c = Context({})
+        f = Files(c, pattern='**')
+        f['BUG_COMPONENT'] = (u'Product1', u'Component1')
+
+        files = {'moz.build': f}
+        self.assertEqual(Files.aggregate(files), {
+            'bug_component_counts': [((u'Product1', u'Component1'), 1)],
+            'recommended_bug_component': (u'Product1', u'Component1'),
+        })
+
+    def test_multiple_bug_components(self):
+        c = Context({})
+        f1 = Files(c, pattern='**')
+        f1['BUG_COMPONENT'] = (u'Product1', u'Component1')
+
+        f2 = Files(c, pattern='**')
+        f2['BUG_COMPONENT'] = (u'Product2', u'Component2')
+
+        files = {'a': f1, 'b': f2, 'c': f1}
+        self.assertEqual(Files.aggregate(files), {
+            'bug_component_counts': [
+                ((u'Product1', u'Component1'), 2),
+                ((u'Product2', u'Component2'), 1),
+            ],
+            'recommended_bug_component': (u'Product1', u'Component1'),
+        })
+
+    def test_no_recommended_bug_component(self):
+        """If there is no clear count winner, we don't recommend a bug component."""
+        c = Context({})
+        f1 = Files(c, pattern='**')
+        f1['BUG_COMPONENT'] = (u'Product1', u'Component1')
+
+        f2 = Files(c, pattern='**')
+        f2['BUG_COMPONENT'] = (u'Product2', u'Component2')
+
+        files = {'a': f1, 'b': f2}
+        self.assertEqual(Files.aggregate(files), {
+            'bug_component_counts': [
+                ((u'Product1', u'Component1'), 1),
+                ((u'Product2', u'Component2'), 1),
+            ],
+            'recommended_bug_component': None,
+        })
+
+
 if __name__ == '__main__':
     main()
--- a/python/mozbuild/setup.py
+++ b/python/mozbuild/setup.py
@@ -1,15 +1,29 @@
 # 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/.
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from setuptools import setup
+from setuptools import setup, find_packages
 
-VERSION = '0.1'
+VERSION = '0.2'
 
 setup(
+    author='Mozilla Foundation',
+    author_email='dev-builds@lists.mozilla.org',
     name='mozbuild',
     description='Mozilla build system functionality.',
     license='MPL 2.0',
-    packages=['mach', 'mozbuild', 'mozpack'],
-    version=VERSION
+    packages=find_packages(),
+    version=VERSION,
+    install_requires=[
+        'jsmin',
+        'mozfile',
+    ],
+    classifiers=[
+        'Development Status :: 3 - Alpha',
+        'Topic :: Software Development :: Build Tools',
+        'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: Implementation :: CPython',
+    ],
+    keywords='mozilla build',
 )
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -457,26 +457,16 @@ CertErrorRunnable::CheckCertOverrides()
   unused << mFdForLogging;
 
   if (!NS_IsMainThread()) {
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
-  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
-    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
-  if (sslSocketControl &&
-      sslSocketControl->GetBypassAuthentication()) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-           ("[%p][%p] Bypass Auth in CheckCertOverrides\n",
-            mFdForLogging, this));
-    return new SSLServerCertVerificationResult(mInfoObject, 0);
-  }
-
   int32_t port;
   mInfoObject->GetPort(&port);
 
   nsAutoCString hostWithPortString(mInfoObject->GetHostName());
   hostWithPortString.Append(':');
   hostWithPortString.AppendInt(port);
 
   uint32_t remaining_display_errors = mCollectedErrors;
@@ -575,16 +565,18 @@ CertErrorRunnable::CheckCertOverrides()
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
          ("[%p][%p] Certificate error was not overridden\n",
          mFdForLogging, this));
 
   // Ok, this is a full stop.
   // First, deliver the technical details of the broken SSL status.
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
+    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
   if (sslSocketControl) {
     nsCOMPtr<nsIInterfaceRequestor> cb;
     sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
     if (cb) {
       nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
       if (bcl) {
         nsIInterfaceRequestor* csi
           = static_cast<nsIInterfaceRequestor*>(mInfoObject);
@@ -1457,16 +1449,24 @@ AuthCertificateHook(void* arg, PRFileDes
   socketInfo->SetFullHandshake();
 
   Time now(Now());
   PRTime prnow(PR_Now());
 
   if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
     return SECFailure;
 
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
+    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, socketInfo));
+  if (sslSocketControl && sslSocketControl->GetBypassAuthentication()) {
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("[%p] Bypass Auth in AuthCertificateHook\n", fd));
+    return SECSuccess;
+  }
+
   bool onSTSThread;
   nsresult nrv;
   nsCOMPtr<nsIEventTarget> sts
     = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
   if (NS_SUCCEEDED(nrv)) {
     nrv = sts->IsOnCurrentThread(&onSTSThread);
   }
 
--- a/services/common/tests/mach_commands.py
+++ b/services/common/tests/mach_commands.py
@@ -81,19 +81,19 @@ class SyncTestCommands(MachCommandBase):
         args = [
             '%s/run-mozilla.sh' % self.bindir,
             '%s/xpcshell' % self.bindir,
             '-g', self.bindir,
             '-a', self.bindir,
             '-r', '%s/components/httpd.manifest' % self.bindir,
             '-m',
             '-s',
+            '-e', 'const _TESTING_MODULES_DIR = "%s/_tests/modules";' % topobjdir,
             '-f', '%s/testing/xpcshell/head.js' % topsrcdir,
             '-e', 'const _SERVER_ADDR = "%s";' % hostname,
-            '-e', 'const _TESTING_MODULES_DIR = "%s/_tests/modules";' % topobjdir,
             '-e', 'const SERVER_PORT = "%s";' % port,
             '-e', 'const INCLUDE_FILES = [%s];' % ', '.join(head_paths),
             '-e', '_register_protocol_handlers();',
             '-e', 'for each (let name in INCLUDE_FILES) load(name);',
             '-e', '_fakeIdleService.activate();',
             '-f', js_file
             ]
 
--- a/testing/tools/screenshot/gdk-screenshot.cpp
+++ b/testing/tools/screenshot/gdk-screenshot.cpp
@@ -58,27 +58,29 @@ gboolean save_to_stdout(const gchar *buf
 
 int main(int argc, char** argv)
 {
   gdk_init(&argc, &argv);
 
 #if defined(HAVE_LIBXSS) && defined(MOZ_WIDGET_GTK)
   int event_base, error_base;
   Bool have_xscreensaver =
-    XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, &error_base);
+    XScreenSaverQueryExtension(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+                               &event_base, &error_base);
 
   if (!have_xscreensaver) {
     fprintf(stderr, "No XScreenSaver extension on display\n");
   } else {
     XScreenSaverInfo* info = XScreenSaverAllocInfo();
     if (!info) {
       fprintf(stderr, "%s: Out of memory\n", argv[0]);
       return 1;
     }
-    XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), info);
+    XScreenSaverQueryInfo(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+                          GDK_ROOT_WINDOW(), info);
 
     const char* state;
     const char* til_or_since = nullptr;
     switch (info->state) {
     case ScreenSaverOff:
       state = "Off";
       til_or_since = "XScreenSaver will activate after another %lu seconds idle time\n";
       break;
--- a/toolkit/components/jsdownloads/test/unit/head.js
+++ b/toolkit/components/jsdownloads/test/unit/head.js
@@ -132,18 +132,29 @@ function getTempFile(aLeafName)
   let leafName = base + "-" + gFileCounter + ext;
   gFileCounter++;
 
   // Get a file reference under the temporary directory for this test file.
   let file = FileUtils.getFile("TmpD", [leafName]);
   do_check_false(file.exists());
 
   do_register_cleanup(function () {
-    if (file.exists()) {
-      file.remove(false);
+    try {
+      file.remove(false)
+    } catch (e) {
+      if (!(e instanceof Components.Exception &&
+            (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED ||
+             e.result == Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST ||
+             e.result == Cr.NS_ERROR_FILE_NOT_FOUND))) {
+        throw e;
+      }
+      // On Windows, we may get an access denied error if the file existed before,
+      // and was recently deleted.
+      // Don't bother checking file.exists() as that may also cause an access
+      // denied error.
     }
   });
 
   return file;
 }
 
 /**
  * Waits for pending events to be processed.
--- a/toolkit/themes/osx/global/menu.css
+++ b/toolkit/themes/osx/global/menu.css
@@ -156,17 +156,17 @@ menulist > menupopup > menu {
 
 /* ::::: menuitems in editable menulist popups ::::: */
 
 menulist[editable="true"] > menupopup > menuitem,
 menulist[editable="true"] > menupopup > menucaption {
   -moz-appearance: none;
 }
 
-menulist[editable="true"] > menupopup > :moz-any(menuitem, menucaption) > .menu-iconic-left
+menulist[editable="true"] > menupopup > :-moz-any(menuitem, menucaption) > .menu-iconic-left {
   display: none;
 }
 
 /* ::::: checked menuitems ::::: */
 
 :not(menulist) > menupopup > menuitem[checked="true"],
 :not(menulist) > menupopup > menuitem[selected="true"] {
   -moz-appearance: checkmenuitem;
--- a/toolkit/themes/windows/global/menu.css
+++ b/toolkit/themes/windows/global/menu.css
@@ -234,17 +234,17 @@ menulist > menupopup > menuitem[_moz-men
   color: GrayText;
 }
 
 menulist > menupopup > menuitem[disabled="true"]:not([_moz-menuactive="true"]):-moz-system-metric(windows-classic) {
   color: GrayText;
   text-shadow: none;
 }
 
-menulist > menupopup > :moz-any(menuitem, menucaption):not(.menuitem-iconic) > .menu-iconic-text,
+menulist > menupopup > :-moz-any(menuitem, menucaption):not(.menuitem-iconic) > .menu-iconic-text {
   margin: 0 !important;
 }
 
 /* ::::: checkbox and radio menuitems ::::: */
 
 menuitem[type="checkbox"],
 menuitem[checked="true"] {
   -moz-appearance: checkmenuitem;
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -817,23 +817,23 @@ DrawThemeWithCairo(gfxContext* aContext,
   if (aScaleFactor != 1)
     transform.PreScale(aScaleFactor, aScaleFactor);
 
   cairo_matrix_t mat;
   GfxMatrixToCairoMatrix(transform, mat);
 
 #ifndef MOZ_TREE_CAIRO
   // Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
-  BorrowedCairoContext borrow(aDrawTarget);
-  if (borrow.mCairo) {
-    cairo_set_matrix(borrow.mCairo, &mat);
+  BorrowedCairoContext borrowCairo(aDrawTarget);
+  if (borrowCairo.mCairo) {
+    cairo_set_matrix(borrowCairo.mCairo, &mat);
 
-    moz_gtk_widget_paint(aGTKWidgetType, borrow.mCairo, &aGDKRect, &aState, aFlags, aDirection);
+    moz_gtk_widget_paint(aGTKWidgetType, borrowCairo.mCairo, &aGDKRect, &aState, aFlags, aDirection);
 
-    borrow.Finish();
+    borrowCairo.Finish();
     return;
   }
 #endif
 
   // A direct Cairo draw target is not available, so we need to create a temporary one.
 #if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
   // If using a Cairo xlib surface, then try to reuse it.
   BorrowedXlibDrawable borrow(aDrawTarget);