Merge m-c to fx-team a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Thu, 19 Mar 2015 21:03:38 -0700
changeset 263443 cdec6c4e2995b280a7975754f877e83983d2af87
parent 263442 0e6aa605af74fc735eac425dbd58561994342d82 (current diff)
parent 263424 4d2d97b3ba34d95b0d29ce099f8f5dc0a2ae722d (diff)
child 263444 687b6a735b3420cfa16cc30b55d7fd447e528fa5
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team a=merge CLOSED TREE
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -744,44 +744,60 @@ nsScriptSecurityManager::CheckLoadURIWit
 
     // Check for chrome target URI
     rv = NS_URIChainHasFlags(targetBaseURI,
                              nsIProtocolHandler::URI_IS_UI_RESOURCE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (hasFlags) {
         if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
+
+            // For now, don't change behavior for resource:// or moz-icon:// and
+            // just allow them.
             if (!targetScheme.EqualsLiteral("chrome")) {
-                // for now don't change behavior for resource: or moz-icon:
                 return NS_OK;
             }
 
-            // allow load only if chrome package is whitelisted
+            // Allow a URI_IS_UI_RESOURCE source to link to a URI_IS_UI_RESOURCE
+            // target if ALLOW_CHROME is set.
+            //
+            // ALLOW_CHROME is a flag that we pass on all loads _except_ docshell
+            // loads (since docshell loads run the loaded content with its origin
+            // principal). So we're effectively allowing resource://, chrome://,
+            // and moz-icon:// source URIs to load resource://, chrome://, and
+            // moz-icon:// files, so long as they're not loading it as a document.
+            bool sourceIsUIResource;
+            rv = NS_URIChainHasFlags(sourceBaseURI,
+                                     nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                                     &sourceIsUIResource);
+            NS_ENSURE_SUCCESS(rv, rv);
+            if (sourceIsUIResource) {
+                return NS_OK;
+            }
+
+            // Allow the load only if the chrome package is whitelisted.
             nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
                                                  NS_CHROMEREGISTRY_CONTRACTID));
             if (reg) {
                 bool accessAllowed = false;
                 reg->AllowContentToAccess(targetBaseURI, &accessAllowed);
                 if (accessAllowed) {
                     return NS_OK;
                 }
             }
         }
 
-        // resource: and chrome: are equivalent, securitywise
-        // That's bogus!!  Fix this.  But watch out for
-        // the view-source stylesheet?
-        bool sourceIsChrome;
-        rv = NS_URIChainHasFlags(sourceBaseURI,
-                                 nsIProtocolHandler::URI_IS_UI_RESOURCE,
-                                 &sourceIsChrome);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (sourceIsChrome) {
+        // Special-case the hidden window: it's allowed to load
+        // URI_IS_UI_RESOURCE no matter what.  Bug 1145470 tracks removing this.
+        nsAutoCString sourceSpec;
+        if (NS_SUCCEEDED(sourceBaseURI->GetSpec(sourceSpec)) &&
+            sourceSpec.EqualsLiteral("resource://gre-resources/hiddenWindow.html")) {
             return NS_OK;
         }
+
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
         return NS_ERROR_DOM_BAD_URI;
     }
 
     // Check for target URI pointing to a file
     rv = NS_URIChainHasFlags(targetBaseURI,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4630,17 +4630,18 @@ nsDocShell::IsPrintingOrPP(bool aDisplay
   return mIsPrintingOrPP;
 }
 
 bool
 nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
                                 bool aCheckIfUnloadFired)
 {
   bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
-                   (!aCheckIfUnloadFired || !mFiredUnloadEvent);
+                   (!aCheckIfUnloadFired || !mFiredUnloadEvent) &&
+                   !mBlockNavigation;
   if (!isAllowed) {
     return false;
   }
   if (!mContentViewer) {
     return true;
   }
   bool firingBeforeUnload;
   mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
@@ -9994,23 +9995,28 @@ nsDocShell::InternalLoad(nsIURI* aURI,
        sameExceptHashes && !newHash.IsEmpty());
 
     if (doShortCircuitedLoad) {
       // Save the position of the scrollers.
       nscoord cx = 0, cy = 0;
       GetCurScrollPos(ScrollOrientation_X, &cx);
       GetCurScrollPos(ScrollOrientation_Y, &cy);
 
-      // ScrollToAnchor doesn't necessarily cause us to scroll the window;
-      // the function decides whether a scroll is appropriate based on the
-      // arguments it receives.  But even if we don't end up scrolling,
-      // ScrollToAnchor performs other important tasks, such as informing
-      // the presShell that we have a new hash.  See bug 680257.
-      rv = ScrollToAnchor(curHash, newHash, aLoadType);
-      NS_ENSURE_SUCCESS(rv, rv);
+      {
+        AutoRestore<bool> scrollingToAnchor(mBlockNavigation);
+        mBlockNavigation = true;
+
+        // ScrollToAnchor doesn't necessarily cause us to scroll the window;
+        // the function decides whether a scroll is appropriate based on the
+        // arguments it receives.  But even if we don't end up scrolling,
+        // ScrollToAnchor performs other important tasks, such as informing
+        // the presShell that we have a new hash.  See bug 680257.
+        rv = ScrollToAnchor(curHash, newHash, aLoadType);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
 
       // Reset mLoadType to its original value once we exit this block,
       // because this short-circuited load might have started after a
       // normal, network load, and we don't want to clobber its load type.
       // See bug 737307.
       AutoRestore<uint32_t> loadTypeResetter(mLoadType);
 
       // If a non-short-circuit load (i.e., a network load) is pending,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -892,16 +892,17 @@ protected:
   bool mIsActive;
   bool mIsPrerendered;
   bool mIsAppTab;
   bool mUseGlobalHistory;
   bool mInPrivateBrowsing;
   bool mUseRemoteTabs;
   bool mDeviceSizeIsPageSize;
   bool mWindowDraggingAllowed;
+  bool mBlockNavigation;
 
   // Because scriptability depends on the mAllowJavascript values of our
   // ancestors, we cache the effective scriptability and recompute it when
   // it might have changed;
   bool mCanExecuteScripts;
   void RecomputeCanExecuteScripts();
 
   // This boolean is set to true right before we fire pagehide and generally
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -73,17 +73,27 @@ namespace cache {
 
 using mozilla::ErrorResult;
 using mozilla::unused;
 using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
 using mozilla::dom::workers::WorkerPrivate;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::Cache);
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache);
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Cache, mGlobal, mRequestPromises)
+NS_IMPL_CYCLE_COLLECTION_CLASS(mozilla::dom::cache::Cache)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::Cache)
+  tmp->DisconnectFromActor();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::Cache)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::Cache)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 NS_INTERFACE_MAP_END
 
 Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
   : mGlobal(aGlobal)
   , mActor(aActor)
@@ -529,16 +539,22 @@ Cache::RejectedCallback(JSContext* aCx, 
 {
   // Do nothing.  The Promise will automatically drop the ref to us after
   // calling the callback.  This is what we want as we only registered in order
   // to be held alive via the Promise handle.
 }
 
 Cache::~Cache()
 {
+  DisconnectFromActor();
+}
+
+void
+Cache::DisconnectFromActor()
+{
   if (mActor) {
     mActor->StartDestroy();
     // DestroyInternal() is called synchronously by StartDestroy().  So we
     // should have already cleared the mActor.
     MOZ_ASSERT(!mActor);
   }
 }
 
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -102,16 +102,19 @@ public:
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
   virtual void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
 private:
   ~Cache();
 
+  // Called when we're destroyed or CCed.
+  void DisconnectFromActor();
+
   // TODO: Replace with actor-per-request model during refactor (bug 1110485)
   RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
   already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
   CacheChild* mActor;
   nsTArray<nsRefPtr<Promise>> mRequestPromises;
 
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -36,18 +36,27 @@ using mozilla::dom::workers::WorkerPriva
 using mozilla::ipc::BackgroundChild;
 using mozilla::ipc::PBackgroundChild;
 using mozilla::ipc::IProtocol;
 using mozilla::ipc::PrincipalInfo;
 using mozilla::ipc::PrincipalToPrincipalInfo;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::CacheStorage);
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::CacheStorage);
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CacheStorage, mGlobal,
-                                                    mRequestPromises)
+NS_IMPL_CYCLE_COLLECTION_CLASS(mozilla::dom::cache::CacheStorage)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::CacheStorage)
+  tmp->DisconnectFromActor();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::CacheStorage)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::CacheStorage)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
 NS_INTERFACE_MAP_END
 
 // static
 already_AddRefed<CacheStorage>
@@ -520,16 +529,22 @@ CacheStorage::RejectedCallback(JSContext
 {
   // Do nothing.  The Promise will automatically drop the ref to us after
   // calling the callback.  This is what we want as we only registered in order
   // to be held alive via the Promise handle.
 }
 
 CacheStorage::~CacheStorage()
 {
+  DisconnectFromActor();
+}
+
+void
+CacheStorage::DisconnectFromActor()
+{
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
   if (mActor) {
     mActor->StartDestroy();
     // DestroyInternal() is called synchronously by StartDestroy().  So we
     // should have already cleared the mActor.
     MOZ_ASSERT(!mActor);
   }
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -104,16 +104,19 @@ public:
   virtual void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
 
 private:
   CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
                const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
   ~CacheStorage();
 
+  // Called when we're destroyed or CCed.
+  void DisconnectFromActor();
+
   void MaybeRunPendingRequests();
 
   RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
   already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
 
   // Would like to use CacheInitData here, but we cannot because
   // its an IPC struct which breaks webidl by including windows.h.
   const Namespace mNamespace;
--- a/dom/cache/test/mochitest/driver.js
+++ b/dom/cache/test/mochitest/driver.js
@@ -76,17 +76,18 @@ function runTests(testFile, order) {
       };
       document.body.appendChild(iframe);
     });
   }
 
   SimpleTest.waitForExplicitFinish();
 
   if (typeof order == "undefined") {
-    order = "both"; // both by default
+    order = "sequential"; // sequential by default, see bug 1143222.
+    // TODO: Make this "both" again.
   }
 
   ok(order == "parallel" || order == "sequential" || order == "both",
      "order argument should be valid");
 
   if (order == "both") {
     info("Running tests in both modes; first: sequential");
     return runTests(testFile, "sequential")
--- a/ipc/chromium/src/base/message_pump_libevent.cc
+++ b/ipc/chromium/src/base/message_pump_libevent.cc
@@ -170,16 +170,33 @@ bool MessagePumpLibevent::WatchFileDescr
   // If we're modifying an existing event and there's an error then we need to
   // tell libevent to clean it up via event_delete() before returning.
   bool should_delete_event = true;
   mozilla::UniquePtr<event> evt(controller->ReleaseEvent());
   if (evt.get() == NULL) {
     should_delete_event = false;
     // Ownership is transferred to the controller.
     evt = mozilla::MakeUnique<event>();
+  } else {
+    // It's illegal to use this function to listen on 2 separate fds with the
+    // same |controller|.
+    if (EVENT_FD(evt.get()) != fd) {
+      NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd;
+      return false;
+    }
+
+    // Make sure we don't pick up any funky internal libevent masks.
+    int old_interest_mask = evt.get()->ev_events &
+      (EV_READ | EV_WRITE | EV_PERSIST);
+
+    // Combine old/new event masks.
+    event_mask |= old_interest_mask;
+
+    // Must disarm the event before we can reuse it.
+    event_del(evt.get());
   }
 
   // Set current interest mask and message pump for this event.
   event_set(evt.get(), fd, event_mask, OnLibeventNotification,
             delegate);
 
   // Tell libevent which message pump this socket will belong to when we add it.
   if (event_base_set(event_base_, evt.get()) != 0) {
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -119,21 +119,31 @@ Pickle::Pickle(int header_size)
   if (!header_) {
     NS_ABORT_OOM(kPayloadUnit);
   }
   header_->payload_size = 0;
 }
 
 Pickle::Pickle(const char* data, int data_len)
     : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
-      header_size_(data_len - header_->payload_size),
+      header_size_(0),
       capacity_(kCapacityReadOnly),
       variable_buffer_offset_(0) {
-  DCHECK(header_size_ >= sizeof(Header));
-  DCHECK(header_size_ == AlignInt(header_size_));
+  if (data_len >= static_cast<int>(sizeof(Header)))
+    header_size_ = data_len - header_->payload_size;
+
+  if (header_size_ > static_cast<unsigned int>(data_len))
+    header_size_ = 0;
+
+  if (header_size_ != AlignInt(header_size_))
+    header_size_ = 0;
+
+  // If there is anything wrong with the data, we're not going to use it.
+  if (!header_size_)
+    header_ = nullptr;
 }
 
 Pickle::Pickle(const Pickle& other)
     : header_(NULL),
       header_size_(other.header_size_),
       capacity_(0),
       variable_buffer_offset_(other.variable_buffer_offset_) {
   uint32_t payload_size = header_size_ + other.header_->payload_size;
@@ -643,16 +653,20 @@ bool Pickle::Resize(uint32_t new_capacit
 
 // static
 const char* Pickle::FindNext(uint32_t header_size,
                              const char* start,
                              const char* end) {
   DCHECK(header_size == AlignInt(header_size));
   DCHECK(header_size <= static_cast<memberAlignmentType>(kPayloadUnit));
 
+  if (end < start)
+    return nullptr;
+  size_t length = static_cast<size_t>(end - start);
+  if (length < sizeof(Header))
+    return nullptr;
+
   const Header* hdr = reinterpret_cast<const Header*>(start);
-  const char* payload_base = start + header_size;
-  const char* payload_end = payload_base + hdr->payload_size;
-  if (payload_end < payload_base)
-    return NULL;
+  if (length < header_size || length - header_size < hdr->payload_size)
+    return nullptr;
 
-  return (payload_end > end) ? NULL : payload_end;
+  return start + header_size + hdr->payload_size;
 }
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -221,20 +221,22 @@ class Pickle {
   }
   const char* payload() const {
     return reinterpret_cast<const char*>(header_) + header_size_;
   }
 
   // Returns the address of the byte immediately following the currently valid
   // header + payload.
   char* end_of_payload() {
+    // We must have a valid header_.
     return payload() + payload_size();
   }
   const char* end_of_payload() const {
-    return payload() + payload_size();
+    // This object may be invalid.
+    return header_ ? payload() + payload_size() : nullptr;
   }
 
   uint32_t capacity() const {
     return capacity_;
   }
 
   // Resizes the buffer for use when writing the specified amount of data. The
   // location that the data should be written at is returned, or NULL if there
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -839,16 +839,18 @@ void Channel::ChannelImpl::OnFileCanRead
     waiting_connect_ = false;
     send_server_hello_msg = true;
   }
 
   if (!waiting_connect_ && fd == pipe_) {
     if (!ProcessIncomingMessages()) {
       Close();
       listener_->OnChannelError();
+      // The OnChannelError() call may delete this, so we need to exit now.
+      return;
     }
   }
 
   // If we're a server and handshaking, then we want to make sure that we
   // only send our handshake message after we've processed the client's.
   // This gives us a chance to kill the client if the incoming handshake
   // is invalid.
   if (send_server_hello_msg) {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/UniquePtr.h"
 
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
 #include "jsmath.h"
 #include "jsprf.h"
+#include "jsutil.h"
 #include "prmjtime.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSModule.h"
 #include "asmjs/AsmJSSignalHandlers.h"
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "jit/CodeGenerator.h"
@@ -4439,19 +4440,27 @@ FoldMaskedArrayIndex(FunctionCompiler &f
 
     ParseNode *indexNode = BitwiseLeft(*indexExpr);
     ParseNode *maskNode = BitwiseRight(*indexExpr);
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an 'out of
         // bounds' access can not occur based on the current heap length constraint.
-        if (mask2 == 0 ||
-            CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
+        if (mask2 == 0) {
             *needsBoundsCheck = NO_BOUNDS_CHECK;
+        } else {
+            uint32_t minHeap = f.m().minHeapLength();
+            uint32_t minHeapZeroes = CountLeadingZeroes32(minHeap - 1);
+            uint32_t maskZeroes = CountLeadingZeroes32(mask2);
+            if ((minHeapZeroes < maskZeroes) ||
+                (IsPowerOfTwo(minHeap) && minHeapZeroes == maskZeroes))
+            {
+                *needsBoundsCheck = NO_BOUNDS_CHECK;
+            }
         }
         *mask &= mask2;
         *indexExpr = indexNode;
         return true;
     }
 
     return false;
 }
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9891,33 +9891,33 @@ nsCSSFrameConstructor::CreateNeededPseud
   nsIFrame* aParentFrame)
 {
   const ParentType ourParentType = GetParentType(aParentFrame);
   if (!IsRubyParentType(ourParentType) ||
       aItems.AllWantParentType(ourParentType)) {
     return;
   }
 
-  nsStyleContext* parentStyle = aParentFrame->StyleContext();
-  if (!parentStyle->GetPseudo()) {
-    // Normally, pseudo frames start from and end at some elements,
+  if (!IsRubyPseudo(aParentFrame)) {
+    // Normally, ruby pseudo frames start from and end at some elements,
     // which means they don't have leading and trailing whitespaces at
     // all.  But there are two cases where they do actually have leading
     // or trailing whitespaces:
     // 1. It is an inter-segment whitespace which in an individual ruby
     //    base container.
     // 2. The pseudo frame starts from or ends at consecutive inline
     //    content, which is not pure whitespace, but includes some.
     // In either case, the whitespaces are not the leading or trailing
     // whitespaces defined in the spec, and thus should not be trimmed.
     TrimLeadingAndTrailingWhitespaces(aState, aItems);
   }
 
   FCItemIterator iter(aItems);
   nsIContent* parentContent = aParentFrame->GetContent();
+  nsStyleContext* parentStyle = aParentFrame->StyleContext();
   while (!iter.IsDone()) {
     if (!iter.SkipItemsWantingParentType(ourParentType)) {
       if (ourParentType == eTypeRuby) {
         WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle,
                                             parentContent);
       } else {
         WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent);
       }
--- a/security/pkix/lib/pkixnames.cpp
+++ b/security/pkix/lib/pkixnames.cpp
@@ -343,18 +343,20 @@ SearchNames(/*optional*/ const Input* su
   if (subjectAltName) {
     Reader altNames;
     rv = der::ExpectTagAndGetValueAtEnd(*subjectAltName, der::SEQUENCE,
                                         altNames);
     if (rv != Success) {
       return rv;
     }
 
-    // do { ... } while(...) because subjectAltName isn't allowed to be empty.
-    do {
+    // According to RFC 5280, "If the subjectAltName extension is present, the
+    // sequence MUST contain at least one entry." For compatibility reasons, we
+    // do not enforce this. See bug 1143085.
+    while (!altNames.AtEnd()) {
       GeneralNameType presentedIDType;
       Input presentedID;
       rv = ReadGeneralName(altNames, presentedIDType, presentedID);
       if (rv != Success) {
         return rv;
       }
 
       rv = MatchPresentedIDWithReferenceID(presentedIDType, presentedID,
@@ -366,17 +368,17 @@ SearchNames(/*optional*/ const Input* su
       if (referenceIDType != GeneralNameType::nameConstraints &&
           match == MatchResult::Match) {
         return Success;
       }
       if (presentedIDType == GeneralNameType::dNSName ||
           presentedIDType == GeneralNameType::iPAddress) {
         fallBackToCommonName = FallBackToSearchWithinSubject::No;
       }
-    } while (!altNames.AtEnd());
+    }
   }
 
   if (referenceIDType == GeneralNameType::nameConstraints) {
     rv = CheckPresentedIDConformsToConstraints(GeneralNameType::directoryName,
                                                subject, referenceID);
     if (rv != Success) {
       return rv;
     }
--- a/security/pkix/test/gtest/pkixnames_tests.cpp
+++ b/security/pkix/test/gtest/pkixnames_tests.cpp
@@ -1349,17 +1349,21 @@ static const CheckCertHostnameParams CHE
   // Duplicate DNSName.
   WITH_SAN("a", RDN(CN("foo")), DNSName("a") + DNSName("a"), Success),
   // After an invalid DNSName.
   WITH_SAN("b", RDN(CN("foo")), DNSName("!") + DNSName("b"),
            Result::ERROR_BAD_DER),
 
   // http://tools.ietf.org/html/rfc5280#section-4.2.1.6: "If the subjectAltName
   // extension is present, the sequence MUST contain at least one entry."
-  WITH_SAN("a", RDN(CN("a")), ByteString(), Result::ERROR_BAD_DER),
+  // However, for compatibility reasons, this is not enforced. See bug 1143085.
+  // This case is treated as if the extension is not present (i.e. name
+  // matching falls back to the subject CN).
+  WITH_SAN("a", RDN(CN("a")), ByteString(), Success),
+  WITH_SAN("a", RDN(CN("b")), ByteString(), Result::ERROR_BAD_CERT_DOMAIN),
 
   // http://tools.ietf.org/html/rfc5280#section-4.1.2.6 says "If subject naming
   // information is present only in the subjectAltName extension (e.g., a key
   // bound only to an email address or URI), then the subject name MUST be an
   // empty sequence and the subjectAltName extension MUST be critical." So, we
   // have to support an empty subject. We don't enforce that the SAN must be
   // critical or even that there is a SAN when the subject is empty, though.
   WITH_SAN("a", ByteString(), DNSName("a"), Success),
@@ -2212,21 +2216,16 @@ static const NameConstraintParams NAME_C
     Success, Success
   },
   { RDN(CN("a.example.com")), NO_SAN, GeneralSubtree(DNSName("a.example.com")),
     Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
   },
   { RDN(CN("b.example.com")), NO_SAN, GeneralSubtree(DNSName("a.example.com")),
     Result::ERROR_CERT_NOT_IN_NAME_SPACE, Success
   },
-  { // Empty SAN is rejected
-    RDN(CN("a.example.com")), ByteString(),
-    GeneralSubtree(DNSName("a.example.com")),
-    Result::ERROR_BAD_DER, Result::ERROR_BAD_DER
-  },
   { // DNSName CN-ID match is detected when there is a SAN w/o any DNSName or
     // IPAddress
     RDN(CN("a.example.com")), RFC822Name("foo@example.com"),
     GeneralSubtree(DNSName("a.example.com")),
     Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
   },
   { // DNSName CN-ID mismatch is detected when there is a SAN w/o any DNSName
     // or IPAddress
@@ -2387,16 +2386,44 @@ static const NameConstraintParams NAME_C
   { ByteString(), RFC822Name("uses_underscore@example.com"),
     GeneralSubtree(RFC822Name("example.com")),
     Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
   },
   { ByteString(), RFC822Name("a@a.uses_underscore.example.com"),
     GeneralSubtree(RFC822Name(".uses_underscore.example.com")),
     Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
   },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Name constraint tests that relate to having an empty SAN. According to RFC
+  // 5280 this isn't valid, but we allow it for compatibility reasons (see bug
+  // 1143085).
+  { // For DNSNames, we fall back to the subject CN.
+    RDN(CN("a.example.com")), ByteString(),
+    GeneralSubtree(DNSName("a.example.com")),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
+  { // For RFC822Names, we do not fall back to the subject emailAddress.
+    // This new implementation seems to conform better to the standards for
+    // RFC822 name constraints, by only applying the name constraints to
+    // emailAddress names in the certificate subject if there is no
+    // subjectAltName extension in the cert.
+    // In this case, the presence of the (empty) SAN extension means that RFC822
+    // name constraints are not enforced on the emailAddress attributes of the
+    // subject.
+    RDN(emailAddress("a@example.com")), ByteString(),
+    GeneralSubtree(RFC822Name("a@example.com")),
+    Success, Success
+  },
+  { // Compare this to the case where there is no SAN (i.e. the name
+    // constraints are enforced, because the extension is not present at all).
+    RDN(emailAddress("a@example.com")), NO_SAN,
+    GeneralSubtree(RFC822Name("a@example.com")),
+    Success, Result::ERROR_CERT_NOT_IN_NAME_SPACE
+  },
 };
 
 class pkixnames_CheckNameConstraints
   : public ::testing::Test
   , public ::testing::WithParamInterface<NameConstraintParams>
 {
 };
 
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -680,16 +680,28 @@ OU(const ByteString& value)
   // python DottedOIDToCode.py --tlv id-at-organizationalUnitName 2.5.4.11
   static const uint8_t tlv_id_at_organizationalUnitName[] = {
     0x06, 0x03, 0x55, 0x04, 0x0b
   };
 
   return AVA(tlv_id_at_organizationalUnitName, der::UTF8String, value);
 }
 
+ByteString
+emailAddress(const ByteString& value)
+{
+  // id-emailAddress AttributeType ::= { pkcs-9 1 }
+  // python DottedOIDToCode.py --tlv id-emailAddress 1.2.840.113549.1.9.1
+  static const uint8_t tlv_id_emailAddress[] = {
+    0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01
+  };
+
+  return AVA(tlv_id_emailAddress, der::IA5String, value);
+}
+
 // RelativeDistinguishedName ::=
 //   SET SIZE (1..MAX) OF AttributeTypeAndValue
 //
 ByteString
 RDN(const ByteString& avas)
 {
   return TLV(der::SET, avas);
 }
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -141,16 +141,25 @@ ByteString OU(const ByteString&);
 
 inline ByteString
 OU(const char* value)
 {
   return OU(ByteString(reinterpret_cast<const uint8_t*>(value),
                        std::strlen(value)));
 }
 
+ByteString emailAddress(const ByteString&);
+
+inline ByteString
+emailAddress(const char* value)
+{
+  return emailAddress(ByteString(reinterpret_cast<const uint8_t*>(value),
+                                 std::strlen(value)));
+}
+
 // RelativeDistinguishedName ::=
 //   SET SIZE (1..MAX) OF AttributeTypeAndValue
 //
 ByteString RDN(const ByteString& avas);
 
 // Name ::= CHOICE { -- only one possibility for now --
 //   rdnSequence  RDNSequence }
 //
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3985,17 +3985,17 @@ bool nsWindow::DispatchMouseEvent(uint32
         }
       }
     } else if (aEventType == NS_MOUSE_EXIT) {
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
       }
     }
 
-    result = DispatchInputEvent(&event);
+    result = ConvertStatus(DispatchInputEvent(&event));
 
     if (nsToolkit::gMouseTrailer)
       nsToolkit::gMouseTrailer->Enable();
 
     // Release the widget with NS_IF_RELEASE() just in case
     // the context menu key code in EventListenerManager::HandleEvent()
     // released it already.
     return result;