Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 27 Aug 2014 13:52:07 -0400
changeset 223400 8eaaedaf5d3d0a622a78cb277642b085ed48d0d7
parent 223371 85135c5c6ba842c2b051a84e14376dc67e6a3712 (current diff)
parent 223399 fff4d503ad889519d0e97359fa2536a747a463a8 (diff)
child 223437 d3f9fd13aefc8740529679585caab60905b9a546
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
layout/ipc/jar.mn
layout/ipc/test-ipcbrowser-chrome.js
layout/ipc/test-ipcbrowser-content.js
layout/ipc/test-ipcbrowser.xul
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -739,16 +739,20 @@
 #endif
 #endif
 @BINPATH@/chrome/pippki@JAREXT@
 @BINPATH@/chrome/pippki.manifest
 
 ; For process sandboxing
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
+#elif defined(XP_LINUX)
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.chk
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.chk
 bin/libfreebl_32int_3.so
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1647,17 +1647,17 @@
                   class="text-link plain" href=""
                   value="&trackingContentBlocked.learnMore;"/>
               </xul:vbox>
               <xul:button
                 type="menu" label="&trackingContentBlocked.options;"
                 sizetopopup="none">
                 <xul:menupopup>
                   <xul:menuitem anonid="trackingContentAction.unblock"
-                    hidden="true" label="&trackingContentBlocked.unblock.label;"
+                    hidden="true" label="&trackingContentBlocked.unblock.label2;"
                     oncommand="document.getBindingParent(this).disableTrackingContentProtection();"/>
                   <xul:menuitem anonid="trackingContentAction.block"
                     hidden="true" label="&trackingContentBlocked.block.label;"
                     oncommand="document.getBindingParent(this).enableTrackingContentProtection();"/>
                 </xul:menupopup>
               </xul:button>
             </xul:hbox>
             <xul:hbox anonid="trackingContentProtectionDisabled" hidden="true"
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -999,17 +999,17 @@ let SessionStoreInternal = {
     if (this._loadState == STATE_RUNNING) {
       // Flush all data queued in the content script before the window is gone.
       TabState.flushWindow(aWindow);
 
       // update all window data for a last time
       this._collectWindowData(aWindow);
 
       if (isFullyLoaded) {
-        winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
+        winData.title = tabbrowser.selectedBrowser.contentTitle || tabbrowser.selectedTab.label;
         winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
                                                   tabbrowser.selectedTab);
         SessionCookies.update([winData]);
       }
 
 #ifndef XP_MACOSX
       // Until we decide otherwise elsewhere, this window is part of a series
       // of closing windows to quit.
@@ -2651,18 +2651,18 @@ let SessionStoreInternal = {
         aWindow.gURLBar.readOnly = false;
         aWindow.gURLBar.setAttribute("enablehistory", "true");
       }
     }
 
     var _this = this;
     aWindow.setTimeout(function() {
       _this.restoreDimensions.apply(_this, [aWindow,
-        +aWinData.width || 0,
-        +aWinData.height || 0,
+        +(aWinData.width || 0),
+        +(aWinData.height || 0),
         "screenX" in aWinData ? +aWinData.screenX : NaN,
         "screenY" in aWinData ? +aWinData.screenY : NaN,
         aWinData.sizemode || "", aWinData.sidebar || ""]);
     }, 0);
   },
 
   /**
    * Restore a window's dimensions
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -749,16 +749,20 @@
 @BINPATH@/chrome/pippki.manifest
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(XP_WIN)
 @BINPATH@/@DLL_PREFIX@sandboxbroker@DLL_SUFFIX@
+#elif defined(XP_LINUX)
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.so
 #endif
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -736,17 +736,17 @@ just addresses the organization to follo
 <!ENTITY mixedContentBlocked2.block.label "Enable protection">
 <!ENTITY mixedContentBlocked2.block.accesskey "B">
 <!ENTITY mixedContentBlocked2.disabled.message "Protection is disabled">
 
 <!ENTITY trackingContentBlocked.message "Tracking">
 <!ENTITY trackingContentBlocked.moreinfo "Parts of the page that track your online activity have been blocked.">
 <!ENTITY trackingContentBlocked.learnMore "Learn More">
 <!ENTITY trackingContentBlocked.options "Options">
-<!ENTITY trackingContentBlocked.unblock.label "Disable protection">
+<!ENTITY trackingContentBlocked.unblock.label2 "Disable protection for this site">
 <!ENTITY trackingContentBlocked.block.label "Enable protection">
 <!ENTITY trackingContentBlocked.disabled.message "Tracking protection is disabled">
 
 <!ENTITY pointerLock.notification.message "Press ESC at any time to show it again.">
 
 <!ENTITY pluginNotification.showAll.label "Show All">
 <!ENTITY pluginNotification.showAll.accesskey "S">
 
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -16,18 +16,20 @@
   <binding id="local-browser" extends="chrome://global/content/bindings/browser.xml#browser">
     <implementation type="application/javascript"
       implements="nsIObserver, nsIDOMEventListener, nsIMessageListener">
 
       <constructor>
         <![CDATA[
           this._frameLoader =
             this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
-          this._contentViewManager =
-            this._frameLoader.QueryInterface(Components.interfaces.nsIContentViewManager);
+          this._contentViewManager = null;
+            // TODO: rip out all the code dealing with _contentViewManager because it is
+            // obsolete and dead code. The nsIContentViewManager interface that it used
+            // to point to is being removed in bug 936690.
 
           let prefService =
             Components.classes["@mozilla.org/preferences-service;1"]
                       .getService(Components.interfaces.nsIPrefBranch);
           this._cacheRatioWidth =
             Math.max(1, prefService.getIntPref("toolkit.browser.cacheRatioWidth") / 1000);
           this._cacheRatioHeight =
             Math.max(1, prefService.getIntPref("toolkit.browser.cacheRatioHeight") / 1000);
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -209,17 +209,18 @@ public:
    * or ShadowRoot as an ancestor of things in the corresponding DocumentFragment.
    * See the concept of "host-including inclusive ancestor" in the DOM
    * specification.
    */
   static bool ContentIsHostIncludingDescendantOf(
     const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
 
   /**
-   * Similar to ContentIsDescendantOf except it crosses document boundaries.
+   * Similar to ContentIsDescendantOf except it crosses document boundaries,
+   * also crosses ShadowRoot boundaries from ShadowRoot to its host.
    */
   static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
                                               nsINode* aPossibleAncestor);
 
   /*
    * This method fills the |aArray| with all ancestor nodes of |aNode|
    * including |aNode| at the zero index.
    */
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -11,112 +11,16 @@ interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
 interface nsITabParent;
 interface nsILoadContext;
 
-typedef unsigned long long nsContentViewId;
-
-/**
- * These interfaces do *not* scroll or scale the content document;
- * instead they set a "goal" scroll/scale wrt the current content
- * view.  When the content document is painted, the scroll*
- * attributes are used to set a compensating transform.  If the
- * metrics of the content document's current pixels don't match the
- * view config, the transform matrix may need to translate
- * content pixels and/or perform a "fuzzy-scale" that doesn't
- * re-rasterize fonts or intelligently resample images.
- *
- * The attrs are allowed to transform content pixels in
- * such a way that the <browser>'s visible rect encloses pixels that
- * the content document does not (yet) define.
- *
- * The view scroll values are in units of chrome-document CSS
- * pixels.
- *
- * These APIs are designed to be used with nsIDOMWindowUtils
- * setDisplayPort() and setResolution().
- */
-[scriptable, uuid(c04c5c40-fa2a-4e9c-94f5-b362a10a86cb)]
-interface nsIContentView : nsISupports
-{
-  /**
-   * Scroll view to or by the given chrome-document CSS pixels.
-   * Fails if the view is no longer valid.
-   */
-  void scrollTo(in float xPx, in float yPx);
-  void scrollBy(in float dxPx, in float dyPx);
-
-  void setScale(in float xScale, in float yScale);
-
-  /**
-   * Scroll offset in chrome-document CSS pixels.
-   *
-   * When this view is active (i.e. it is being painted because it's in the
-   * visible region of the screen), this value is at first lined up with the
-   * content's scroll offset.
-   *
-   * Note that when this view becomes inactive, the new content view will have
-   * scroll values that are reset to the default!
-   */
-  readonly attribute float scrollX;
-  readonly attribute float scrollY;
-
-  /**
-   * Dimensions of the viewport in chrome-document CSS pixels.
-   */
-  readonly attribute float viewportWidth;
-  readonly attribute float viewportHeight;
-
-  /**
-   * Dimensions of scrolled content in chrome-document CSS pixels.
-   */
-  readonly attribute float contentWidth;
-  readonly attribute float contentHeight;
-
-  /**
-   * ID that can be used in conjunction with nsIDOMWindowUtils to change
-   * the actual document, instead of just how it is transformed.
-   */
-  readonly attribute nsContentViewId id;
-};
-
-[scriptable, uuid(ba5af90d-ece5-40b2-9a1d-a0154128db1c)]
-interface nsIContentViewManager : nsISupports
-{
-  /**
-   * Retrieve view scrolling/scaling interfaces in a given area,
-   * used to support asynchronous re-paints of content pixels.
-   * These interfaces are only meaningful for <browser>.
-   *
-   * Pixels are in chrome device pixels and are relative to the browser
-   * element.
-   *
-   * @param aX x coordinate that will be in target rectangle
-   * @param aY y coordinate that will be in target rectangle
-   * @param aTopSize How much to expand up the rectangle
-   * @param aRightSize How much to expand right the rectangle
-   * @param aBottomSize How much to expand down the rectangle
-   * @param aLeftSize How much to expand left the rectangle
-   */
-  void getContentViewsIn(in float aXPx, in float aYPx,
-                         in float aTopSize, in float aRightSize,
-                         in float aBottomSize, in float aLeftSize,
-                         [optional] out unsigned long aLength,
-                         [retval, array, size_is(aLength)] out nsIContentView aResult);
-
-  /**
-   * The root content view.
-   */
-  readonly attribute nsIContentView rootContentView;
-};
-
 [scriptable, builtinclass, uuid(55a772b8-855a-4c5f-b4a1-284b6b3bec28)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
 
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "mozilla/dom/NodeInfo.h"
+#include "mozilla/dom/Event.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIDOMNodeList.h"
 #include "nsIContentIterator.h"
 #include "nsFocusManager.h"
 #include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIURL.h"
@@ -693,24 +694,37 @@ nsIContent::PreHandleEvent(EventChainPre
   // inside chrome access only content.
   bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
   if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
        aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
        aVisitor.mEvent->message == NS_POINTER_OVER ||
        aVisitor.mEvent->message == NS_POINTER_OUT) &&
       // Check if we should stop event propagation when event has just been
       // dispatched or when we're about to propagate from
-      // chrome access only subtree.
+      // chrome access only subtree or if we are about to propagate out of
+      // a shadow root to a shadow root host.
       ((this == aVisitor.mEvent->originalTarget &&
-        !ChromeOnlyAccess()) || isAnonForEvents)) {
+        !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
      nsCOMPtr<nsIContent> relatedTarget =
        do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
     if (relatedTarget &&
         relatedTarget->OwnerDoc() == OwnerDoc()) {
 
+      // In the web components case, we may need to stop propagation of events
+      // at shadow root host.
+      if (GetShadowRoot()) {
+        nsIContent* adjustedTarget =
+          Event::GetShadowRelatedTarget(this, relatedTarget);
+        if (this == adjustedTarget) {
+          aVisitor.mParentTarget = nullptr;
+          aVisitor.mCanHandle = false;
+          return NS_OK;
+        }
+      }
+
       // If current target is anonymous for events or we know that related
       // target is descendant of an element which is anonymous for events,
       // we may want to stop event propagation.
       // If this is the original target, aVisitor.mRelatedTargetIsInAnon
       // must be updated.
       if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
           (aVisitor.mEvent->originalTarget == this &&
            (aVisitor.mRelatedTargetIsInAnon =
@@ -764,16 +778,109 @@ nsIContent::PreHandleEvent(EventChainPre
             }
           }
         }
       }
     }
   }
 
   nsIContent* parent = GetParent();
+
+  // Web components have a special event chain that need to account
+  // for destination insertion points where nodes have been distributed.
+  nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints();
+  if (destPoints && !destPoints->IsEmpty()) {
+    // Push destination insertion points to aVisitor.mDestInsertionPoints
+    // excluding shadow insertion points.
+    bool didPushNonShadowInsertionPoint = false;
+    for (uint32_t i = 0; i < destPoints->Length(); i++) {
+      nsIContent* point = destPoints->ElementAt(i);
+      if (!ShadowRoot::IsShadowInsertionPoint(point)) {
+        aVisitor.mDestInsertionPoints.AppendElement(point);
+        didPushNonShadowInsertionPoint = true;
+      }
+    }
+
+    // Next node in the event path is the final destination
+    // (non-shadow) insertion point that was pushed.
+    if (didPushNonShadowInsertionPoint) {
+      parent = aVisitor.mDestInsertionPoints.LastElement();
+      aVisitor.mDestInsertionPoints.SetLength(
+        aVisitor.mDestInsertionPoints.Length() - 1);
+    }
+  }
+
+  ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
+  if (thisShadowRoot) {
+    // The following events must always be stopped at the root node of the node tree:
+    //   abort
+    //   error
+    //   select
+    //   change
+    //   load
+    //   reset
+    //   resize
+    //   scroll
+    //   selectstart
+    bool stopEvent = false;
+    switch (aVisitor.mEvent->message) {
+      case NS_IMAGE_ABORT:
+      case NS_LOAD_ERROR:
+      case NS_FORM_SELECTED:
+      case NS_FORM_CHANGE:
+      case NS_LOAD:
+      case NS_FORM_RESET:
+      case NS_RESIZE_EVENT:
+      case NS_SCROLL_EVENT:
+        stopEvent = true;
+        break;
+      case NS_USER_DEFINED_EVENT:
+        if (aVisitor.mDOMEvent) {
+          nsAutoString eventType;
+          aVisitor.mDOMEvent->GetType(eventType);
+          if (eventType.EqualsLiteral("abort") ||
+              eventType.EqualsLiteral("error") ||
+              eventType.EqualsLiteral("select") ||
+              eventType.EqualsLiteral("change") ||
+              eventType.EqualsLiteral("load") ||
+              eventType.EqualsLiteral("reset") ||
+              eventType.EqualsLiteral("resize") ||
+              eventType.EqualsLiteral("scroll") ||
+              eventType.EqualsLiteral("selectstart")) {
+            stopEvent = true;
+          }
+        }
+        break;
+    }
+
+    if (stopEvent) {
+      // If we do stop propagation, we still want to propagate
+      // the event to chrome (nsPIDOMWindow::GetParentTarget()).
+      // The load event is special in that we don't ever propagate it
+      // to chrome.
+      nsCOMPtr<nsPIDOMWindow> win = OwnerDoc()->GetWindow();
+      EventTarget* parentTarget = win && aVisitor.mEvent->message != NS_LOAD
+        ? win->GetParentTarget() : nullptr;
+
+      aVisitor.mParentTarget = parentTarget;
+      return NS_OK;
+    }
+
+    if (!aVisitor.mDestInsertionPoints.IsEmpty()) {
+      parent = aVisitor.mDestInsertionPoints.LastElement();
+      aVisitor.mDestInsertionPoints.SetLength(
+        aVisitor.mDestInsertionPoints.Length() - 1);
+    } else {
+      // The pool host for the youngest shadow root is shadow DOM host,
+      // for older shadow roots, it is the shadow insertion point
+      // where the shadow root is projected, nullptr if none exists.
+      parent = thisShadowRoot->GetPoolHost();
+    }
+  }
+
   // Event may need to be retargeted if this is the root of a native
   // anonymous content subtree or event is dispatched somewhere inside XBL.
   if (isAnonForEvents) {
 #ifdef DEBUG
     // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
     // all the events are allowed even in the native anonymous content..
     nsCOMPtr<nsIContent> t = do_QueryInterface(aVisitor.mEvent->originalTarget);
     NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
@@ -799,17 +906,17 @@ nsIContent::PreHandleEvent(EventChainPre
     if (insertionParent) {
       parent = insertionParent;
     }
   }
 
   if (parent) {
     aVisitor.mParentTarget = parent;
   } else {
-    aVisitor.mParentTarget = GetCurrentDoc();
+    aVisitor.mParentTarget = GetComposedDoc();
   }
   return NS_OK;
 }
 
 bool
 nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                     nsAString& aResult) const
 {
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -988,17 +988,17 @@ nsContentList::RemoveFromHashtable()
 
   if (!gContentListHashTable.ops)
     return;
 
   PL_DHashTableOperate(&gContentListHashTable,
                        &key,
                        PL_DHASH_REMOVE);
 
-  if (gContentListHashTable.entryCount == 0) {
+  if (gContentListHashTable.EntryCount() == 0) {
     PL_DHashTableFinish(&gContentListHashTable);
     gContentListHashTable.ops = nullptr;
   }
 }
 
 void
 nsContentList::BringSelfUpToDate(bool aDoFlush)
 {
@@ -1031,17 +1031,17 @@ nsCacheableFuncStringContentList::Remove
     return;
   }
 
   nsFuncStringCacheKey key(mRootNode, mFunc, mString);
   PL_DHashTableOperate(&gFuncStringContentListHashTable,
                        &key,
                        PL_DHASH_REMOVE);
 
-  if (gFuncStringContentListHashTable.entryCount == 0) {
+  if (gFuncStringContentListHashTable.EntryCount() == 0) {
     PL_DHashTableFinish(&gFuncStringContentListHashTable);
     gFuncStringContentListHashTable.ops = nullptr;
   }
 }
 
 #ifdef DEBUG_CONTENT_LIST
 void
 nsContentList::AssertInSync()
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1776,29 +1776,29 @@ nsContentUtils::Shutdown()
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
   delete sStringEventTable;
   sStringEventTable = nullptr;
   delete sUserDefinedEvents;
   sUserDefinedEvents = nullptr;
 
   if (sEventListenerManagersHash.ops) {
-    NS_ASSERTION(sEventListenerManagersHash.entryCount == 0,
+    NS_ASSERTION(sEventListenerManagersHash.EntryCount() == 0,
                  "Event listener manager hash not empty at shutdown!");
 
     // See comment above.
 
     // However, we have to handle this table differently.  If it still
     // has entries, we want to leak it too, so that we can keep it alive
     // in case any elements are destroyed.  Because if they are, we need
     // their event listener managers to be destroyed too, or otherwise
     // it could leave dangling references in DOMClassInfo's preserved
     // wrapper table.
 
-    if (sEventListenerManagersHash.entryCount == 0) {
+    if (sEventListenerManagersHash.EntryCount() == 0) {
       PL_DHashTableFinish(&sEventListenerManagersHash);
       sEventListenerManagersHash.ops = nullptr;
     }
   }
 
   NS_ASSERTION(!sBlockedScriptRunners ||
                sBlockedScriptRunners->Length() == 0,
                "How'd this happen?");
@@ -2077,16 +2077,23 @@ nsContentUtils::ContentIsCrossDocDescend
                                               nsINode* aPossibleAncestor)
 {
   NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
   NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
 
   do {
     if (aPossibleDescendant == aPossibleAncestor)
       return true;
+
+    // Step over shadow root to the host node.
+    ShadowRoot* shadowRoot = ShadowRoot::FromNode(aPossibleDescendant);
+    if (shadowRoot) {
+      aPossibleDescendant = shadowRoot->GetHost();
+    }
+
     aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
   } while (aPossibleDescendant);
 
   return false;
 }
 
 
 // static
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -117,123 +117,16 @@ public:
     if (base_win) {
       base_win->Destroy();
     }
     return NS_OK;
   }
   nsRefPtr<nsIDocShell> mDocShell;
 };
 
-NS_IMPL_ISUPPORTS(nsContentView, nsIContentView)
-
-nsresult
-nsContentView::Update(const ViewConfig& aConfig)
-{
-  if (aConfig == mConfig) {
-    return NS_OK;
-  }
-  mConfig = aConfig;
-
-  // View changed.  Try to locate our subdoc frame and invalidate
-  // it if found.
-  if (!mFrameLoader) {
-    if (IsRoot()) {
-      // Oops, don't have a frame right now.  That's OK; the view
-      // config persists and will apply to the next frame we get, if we
-      // ever get one.
-      return NS_OK;
-    } else {
-      // This view is no longer valid.
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::ScrollTo(float aXpx, float aYpx)
-{
-  ViewConfig config(mConfig);
-  config.mScrollOffset = nsPoint(nsPresContext::CSSPixelsToAppUnits(aXpx),
-                                 nsPresContext::CSSPixelsToAppUnits(aYpx));
-  return Update(config);
-}
-
-NS_IMETHODIMP
-nsContentView::ScrollBy(float aDXpx, float aDYpx)
-{
-  ViewConfig config(mConfig);
-  config.mScrollOffset.MoveBy(nsPresContext::CSSPixelsToAppUnits(aDXpx),
-                              nsPresContext::CSSPixelsToAppUnits(aDYpx));
-  return Update(config);
-}
-
-NS_IMETHODIMP
-nsContentView::SetScale(float aXScale, float aYScale)
-{
-  ViewConfig config(mConfig);
-  config.mXScale = aXScale;
-  config.mYScale = aYScale;
-  return Update(config);
-}
-
-NS_IMETHODIMP
-nsContentView::GetScrollX(float* aViewScrollX)
-{
-  *aViewScrollX = nsPresContext::AppUnitsToFloatCSSPixels(
-    mConfig.mScrollOffset.x);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetScrollY(float* aViewScrollY)
-{
-  *aViewScrollY = nsPresContext::AppUnitsToFloatCSSPixels(
-    mConfig.mScrollOffset.y);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetViewportWidth(float* aWidth)
-{
-  *aWidth = nsPresContext::AppUnitsToFloatCSSPixels(mViewportSize.width);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetViewportHeight(float* aHeight)
-{
-  *aHeight = nsPresContext::AppUnitsToFloatCSSPixels(mViewportSize.height);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetContentWidth(float* aWidth)
-{
-  *aWidth = nsPresContext::AppUnitsToFloatCSSPixels(mContentSize.width);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetContentHeight(float* aHeight)
-{
-  *aHeight = nsPresContext::AppUnitsToFloatCSSPixels(mContentSize.height);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContentView::GetId(nsContentViewId* aId)
-{
-  NS_ASSERTION(sizeof(nsContentViewId) == sizeof(ViewID),
-               "ID size for XPCOM ID and internal ID type are not the same!");
-  *aId = mScrollId;
-  return NS_OK;
-}
-
 // Bug 136580: Limit to the number of nested content frames that can have the
 //             same URL. This is to stop content that is recursively loading
 //             itself.  Note that "#foo" on the end of URL doesn't affect
 //             whether it's considered identical, but "?foo" or ";foo" are
 //             considered and compared.
 // Bug 228829: Limit this to 1, like IE does.
 #define MAX_SAME_URL_CONTENT_FRAMES 1
 
@@ -247,17 +140,16 @@ nsContentView::GetId(nsContentViewId* aI
 #define MAX_DEPTH_CONTENT_FRAMES 10
 
 NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
-  NS_INTERFACE_MAP_ENTRY(nsIContentViewManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
 NS_INTERFACE_MAP_END
 
 nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
   : mOwnerContent(aOwner)
   , mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
   , mDetachedSubdocViews(nullptr)
   , mDepthTooGreat(false)
@@ -2374,44 +2266,16 @@ nsFrameLoader::GetMessageManager(nsIMess
 {
   EnsureMessageManager();
   if (mMessageManager) {
     CallQueryInterface(mMessageManager, aManager);
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsFrameLoader::GetContentViewsIn(float aXPx, float aYPx,
-                                 float aTopSize, float aRightSize,
-                                 float aBottomSize, float aLeftSize,
-                                 uint32_t* aLength,
-                                 nsIContentView*** aResult)
-{
-  *aResult = nullptr;
-  *aLength = 0;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameLoader::GetRootContentView(nsIContentView** aContentView)
-{
-  RenderFrameParent* rfp = GetCurrentRemoteFrame();
-  if (!rfp) {
-    *aContentView = nullptr;
-    return NS_OK;
-  }
-
-  nsContentView* view = rfp->GetRootContentView();
-  NS_ABORT_IF_FALSE(view, "Should always be able to create root scrollable!");
-  nsRefPtr<nsIContentView>(view).forget(aContentView);
-
-  return NS_OK;
-}
-
 nsresult
 nsFrameLoader::EnsureMessageManager()
 {
   NS_ENSURE_STATE(mOwnerContent);
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -49,111 +49,17 @@ class RenderFrameParent;
 
 #if defined(MOZ_WIDGET_GTK)
 typedef struct _GtkWidget GtkWidget;
 #endif
 #ifdef MOZ_WIDGET_QT
 class QX11EmbedContainer;
 #endif
 
-/**
- * Defines a target configuration for this <browser>'s content
- * document's view.  If the content document's actual view
- * doesn't match this nsIContentView, then on paints its pixels
- * are transformed to compensate for the difference.
- *
- * Used to support asynchronous re-paints of content pixels; see
- * nsIContentView.
- */
-class nsContentView MOZ_FINAL : public nsIContentView
-{
-public:
-  typedef mozilla::layers::FrameMetrics::ViewID ViewID;
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICONTENTVIEW
- 
-  struct ViewConfig {
-    ViewConfig()
-      : mScrollOffset(0, 0)
-      , mXScale(1.0)
-      , mYScale(1.0)
-    {}
-
-    // Default copy ctor and operator= are fine
-
-    bool operator==(const ViewConfig& aOther) const
-    {
-      return (mScrollOffset == aOther.mScrollOffset &&
-              mXScale == aOther.mXScale &&
-              mYScale == aOther.mYScale);
-    }
-
-    // This is the scroll offset the <browser> user wishes or expects
-    // its enclosed content document to have.  "Scroll offset" here
-    // means the document pixel at pixel (0,0) within the CSS
-    // viewport.  If the content document's actual scroll offset
-    // doesn't match |mScrollOffset|, the difference is used to define
-    // a translation transform when painting the content document.
-    nsPoint mScrollOffset;
-    // The scale at which the <browser> user wishes to paint its
-    // enclosed content document.  If content-document layers have a
-    // lower or higher resolution than the desired scale, then the
-    // ratio is used to define a scale transform when painting the
-    // content document.
-    float mXScale;
-    float mYScale;
-  };
-
-  nsContentView(nsFrameLoader* aFrameLoader, ViewID aScrollId, bool aIsRoot,
-                ViewConfig aConfig = ViewConfig())
-    : mViewportSize(0, 0)
-    , mContentSize(0, 0)
-    , mParentScaleX(1.0)
-    , mParentScaleY(1.0)
-    , mFrameLoader(aFrameLoader)
-    , mScrollId(aScrollId)
-    , mIsRoot(aIsRoot)
-    , mConfig(aConfig)
-  {}
-
-  bool IsRoot() const
-  {
-    return mIsRoot;
-  }
-
-  ViewID GetId() const
-  {
-    return mScrollId;
-  }
-
-  ViewConfig GetViewConfig() const
-  {
-    return mConfig;
-  }
-
-  nsSize mViewportSize;
-  nsSize mContentSize;
-  float mParentScaleX;
-  float mParentScaleY;
-
-  nsFrameLoader* mFrameLoader;  // WEAK
-
-private:
-  ~nsContentView() {}
-
-  nsresult Update(const ViewConfig& aConfig);
-
-  ViewID mScrollId;
-  bool mIsRoot;
-  ViewConfig mConfig;
-};
-
-
 class nsFrameLoader MOZ_FINAL : public nsIFrameLoader,
-                                public nsIContentViewManager,
                                 public nsStubMutationObserver,
                                 public mozilla::dom::ipc::MessageManagerCallback
 {
   friend class AutoResetInShow;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
 
@@ -169,17 +75,16 @@ public:
   }
 
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
                                bool aNetworkCreated);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
   NS_DECL_NSIFRAMELOADER
-  NS_DECL_NSICONTENTVIEWMANAGER
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void Finalize();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
   mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
   nsresult CreateStaticClone(nsIFrameLoader* aDest);
 
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2597,17 +2597,17 @@ void MediaDecoderStateMachine::AdvanceFr
 #ifdef PR_LOGGING
     int32_t droppedFrames = 0;
 #endif
     while (mScheduler->IsRealTime() || clock_time >= frame->mTime) {
       mVideoFrameEndTime = frame->GetEndTime();
 #ifdef PR_LOGGING
       if (currentFrame) {
         VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld (%d so far)",
-                    currentFrame->mTime, ++droppedFrames);
+                    currentFrame->mTime, clock_time, ++droppedFrames);
       }
 #endif
       currentFrame = frame;
       VideoQueue().PopFront();
       // Notify the decode thread that the video queue's buffers may have
       // free'd up space for more frames.
       mDecoder->GetReentrantMonitor().NotifyAll();
       OnPlaybackOffsetUpdate(frame->mOffset);
--- a/content/media/fmp4/apple/AppleCMLinker.cpp
+++ b/content/media/fmp4/apple/AppleCMLinker.cpp
@@ -68,17 +68,17 @@ fail:
   sLinkStatus = LinkStatus_FAILED;
   return false;
 }
 
 /* static */ void
 AppleCMLinker::Unlink()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(sLink && sRefCount > 0, "Unbalanced Unlink()");
+  MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
   --sRefCount;
   if (sLink && sRefCount < 1) {
     LOG("Unlinking CoreMedia framework.");
     dlclose(sLink);
     sLink = nullptr;
   }
 }
 
--- a/content/media/fmp4/apple/AppleVTDecoder.cpp
+++ b/content/media/fmp4/apple/AppleVTDecoder.cpp
@@ -114,17 +114,18 @@ AppleVTDecoder::Input(mp4_demuxer::MP4Sa
 nsresult
 AppleVTDecoder::Flush()
 {
   mTaskQueue->Flush();
   nsresult rv = WaitForAsynchronousFrames();
   if (NS_FAILED(rv)) {
     LOG("AppleVTDecoder::Drain failed waiting for platform decoder.");
   }
-  mReorderQueue.Clear();
+  ClearReorderedFrames();
+
   return rv;
 }
 
 nsresult
 AppleVTDecoder::Drain()
 {
   mTaskQueue->AwaitIdle();
   nsresult rv = WaitForAsynchronousFrames();
@@ -216,16 +217,24 @@ AppleVTDecoder::WaitForAsynchronousFrame
 void
 AppleVTDecoder::DrainReorderedFrames()
 {
   while (!mReorderQueue.IsEmpty()) {
     mCallback->Output(mReorderQueue.Pop());
   }
 }
 
+void
+AppleVTDecoder::ClearReorderedFrames()
+{
+  while (!mReorderQueue.IsEmpty()) {
+    delete mReorderQueue.Pop();
+  }
+}
+
 // Copy and return a decoded frame.
 nsresult
 AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
                             nsAutoPtr<FrameRef> aFrameRef)
 {
   size_t width = CVPixelBufferGetWidth(aImage);
   size_t height = CVPixelBufferGetHeight(aImage);
   LOG("  got decoded frame data... %ux%u %s", width, height,
--- a/content/media/fmp4/apple/AppleVTDecoder.h
+++ b/content/media/fmp4/apple/AppleVTDecoder.h
@@ -49,13 +49,14 @@ private:
   ReorderQueue mReorderQueue;
 
   // Method to pass a frame to VideoToolbox for decoding.
   nsresult SubmitFrame(mp4_demuxer::MP4Sample* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
   nsresult WaitForAsynchronousFrames();
   void DrainReorderedFrames();
+  void ClearReorderedFrames();
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AppleVTDecoder_h
--- a/content/media/fmp4/apple/AppleVTLinker.cpp
+++ b/content/media/fmp4/apple/AppleVTLinker.cpp
@@ -72,17 +72,17 @@ fail:
 /* static */ void
 AppleVTLinker::Unlink()
 {
   // We'll be called by multiple Decoders, one intantiated for
   // each media element. Therefore we receive must maintain a
   // reference count to avoidunloading our symbols when other
   // instances still need them.
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(sLink && sRefCount > 0, "Unbalanced Unlink()");
+  MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
   --sRefCount;
   if (sLink && sRefCount < 1) {
     LOG("Unlinking VideoToolbox framework.");
     dlclose(sLink);
     sLink = nullptr;
   }
 }
 
--- a/content/media/mediasource/MediaSourceReader.cpp
+++ b/content/media/mediasource/MediaSourceReader.cpp
@@ -393,22 +393,28 @@ public:
 private:
   nsRefPtr<AbstractMediaDecoder> mDecoder;
 };
 }
 
 bool
 MediaSourceReader::DecodersContainTime(double aTime)
 {
+  bool found = false;
+
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    if (!mDecoders[i]->IsDiscarded() && mDecoders[i]->ContainsTime(aTime)) {
-      return true;
+    if (!mDecoders[i]->IsDiscarded()) {
+      if (!mDecoders[i]->ContainsTime(aTime)) {
+        // No use to continue searching, one source buffer isn't ready yet
+        return false;
+      }
+      found = true;
     }
   }
-  return false;
+  return found;
 }
 
 nsresult
 MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
                         int64_t aCurrentTime)
 {
   MSE_DEBUG("MediaSourceReader(%p)::Seek(aTime=%lld, aStart=%lld, aEnd=%lld, aCurrent=%lld)",
             this, aTime, aStartTime, aEndTime, aCurrentTime);
--- a/content/media/mediasource/SourceBufferDecoder.cpp
+++ b/content/media/mediasource/SourceBufferDecoder.cpp
@@ -61,18 +61,17 @@ void
 SourceBufferDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
 {
   MSE_DEBUG("SourceBufferDecoder(%p)::NotifyBytesConsumed UNIMPLEMENTED", this);
 }
 
 int64_t
 SourceBufferDecoder::GetEndMediaTime() const
 {
-  MSE_DEBUG("SourceBufferDecoder(%p)::GetEndMediaTime UNIMPLEMENTED", this);
-  return -1;
+  return mMediaDuration;
 }
 
 int64_t
 SourceBufferDecoder::GetMediaDuration()
 {
   return mMediaDuration;
 }
 
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -135,27 +135,28 @@ nsDefaultURIFixup::CreateFixupURI(const 
 
 NS_IMETHODIMP
 nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, uint32_t aFixupFlags,
                                    nsIInputStream **aPostData, nsIURIFixupInfo **aInfo)
 {
     NS_ENSURE_ARG(!aStringURI.IsEmpty());
 
     nsresult rv;
-    nsRefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aStringURI);
-    NS_ADDREF(*aInfo = info);
 
     nsAutoCString uriString(aStringURI);
     uriString.Trim(" ");  // Cleanup the empty spaces that might be on each end.
 
     // Eliminate embedded newlines, which single-line text fields now allow:
     uriString.StripChars("\r\n");
 
     NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
 
+    nsRefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
+    NS_ADDREF(*aInfo = info);
+
     nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     nsAutoCString scheme;
     ioService->ExtractScheme(aStringURI, scheme);
     
     // View-source is a pseudo scheme. We're interested in fixing up the stuff
     // after it. The easiest way to do that is to call this method again with the
     // "view-source:" lopped off and then prepend it again afterwards.
@@ -979,17 +980,17 @@ void nsDefaultURIFixup::KeywordURIFixup(
             }
             if (Preferences::GetBool(pref.get(), false))
             {
                 return;
             }
         }
         // If we get here, we don't have a valid URI, or we did but the
         // host is not whitelisted, so we do a keyword search *anyway*:
-        rv = KeywordToURI(aURIString, aPostData,
+        rv = KeywordToURI(aFixupInfo->mOriginalInput, aPostData,
                           getter_AddRefs(aFixupInfo->mPreferredURI));
         if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
         {
             aFixupInfo->mFixupUsedKeyword = true;
         }
     }
 }
 
--- a/docshell/test/unit/test_nsDefaultURIFixup_info.js
+++ b/docshell/test/unit/test_nsDefaultURIFixup_info.js
@@ -54,31 +54,35 @@ let testcases = [
   ["http://test./", "http://test./", "http://www.test./", false, false],
   ["127.0.0.1", "http://127.0.0.1/", null, false, true],
   ["1234", "http://1234/", "http://www.1234.com/", true, true],
   ["host/foo.txt", "http://host/foo.txt", "http://www.host.com/foo.txt", false, true],
   ["mozilla", "http://mozilla/", "http://www.mozilla.com/", true, true],
   ["test.", "http://test./", "http://www.test./", true, true],
   [".test", "http://.test/", "http://www..test/", true, true],
   ["mozilla is amazing", null, null, true, true],
+  ["mozilla ", "http://mozilla/", "http://www.mozilla.com/", true, true],
   ["", null, null, true, true],
   ["[]", null, null, true, true]
 ];
 
 if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
   testcases.push(["C:\\some\\file.txt", "file:///C:/some/file.txt", null, false, true]);
   testcases.push(["//mozilla", "http://mozilla/", "http://www.mozilla.com/", false, true]);
+  testcases.push(["mozilla\\", "http://mozilla/", "http://www.mozilla.com/", true, true]);
 } else {
   testcases.push(["/some/file.txt", "file:///some/file.txt", null, false, true]);
   testcases.push(["//mozilla", "file:////mozilla", null, false, true]);
+  testcases.push(["mozilla\\", "http://mozilla\\/", "http://www.mozilla/", true, true]);
 }
 
 function run_test() {
   for (let [testInput, expectedFixedURI, alternativeURI,
             expectKeywordLookup, expectProtocolChange] of testcases) {
+    testInput = testInput.trim();
     for (let flags of flagInputs) {
       let info;
       let fixupURIOnly = null;
       try {
         fixupURIOnly = urifixup.createFixupURI(testInput, flags);
       } catch (ex) {
         do_print("Caught exception: " + ex);
         do_check_eq(expectedFixedURI, null);
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccessCheck.h"
 #include "base/basictypes.h"
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
@@ -1114,16 +1115,54 @@ Event::SetOwner(mozilla::dom::EventTarge
   }
 
 #ifdef DEBUG
   nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(aOwner);
   MOZ_ASSERT(root, "Unexpected EventTarget!");
 #endif
 }
 
+// static
+nsIContent*
+Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
+                              nsIContent* aRelatedTarget)
+{
+  if (!aCurrentTarget || !aRelatedTarget) {
+    return nullptr;
+  }
+
+  // Walk up the ancestor node trees of the related target until
+  // we encounter the node tree of the current target in order
+  // to find the adjusted related target. Walking up the tree may
+  // not find a common ancestor node tree if the related target is in
+  // an ancestor tree, but in that case it does not need to be adjusted.
+  ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
+  if (!currentTargetShadow) {
+    return nullptr;
+  }
+
+  nsIContent* relatedTarget = aCurrentTarget;
+  while (relatedTarget) {
+    ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
+    if (currentTargetShadow == ancestorShadow) {
+      return relatedTarget;
+    }
+
+    // Didn't find the ancestor tree, thus related target does not have to
+    // adjusted.
+    if (!ancestorShadow) {
+      return nullptr;
+    }
+
+    relatedTarget = ancestorShadow->GetHost();
+  }
+
+  return nullptr;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult
 NS_NewDOMEvent(nsIDOMEvent** aInstancePtrResult,
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -216,16 +216,24 @@ public:
    */
   void PreventDefaultInternal(bool aCalledByDefaultHandler);
 
   bool IsMainThreadEvent()
   {
     return mIsMainThreadEvent;
   }
 
+  /**
+   * For a given current target, returns the related target adjusted with
+   * shadow DOM retargeting rules. Returns nullptr if related target
+   * is not adjusted.
+   */
+  static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
+                                            nsIContent* aRelatedTarget);
+
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
   /**
    * IsChrome() returns true if aCx is chrome context or the event is created
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -516,17 +516,18 @@ EventDispatcher::Dispatch(nsISupports* a
       aEvent->originalTarget->GetTargetForEventTargetChain();
     NS_ENSURE_STATE(aEvent->originalTarget);
   }
   else {
     aEvent->originalTarget = aEvent->target;
   }
 
   nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget);
-  bool isInAnon = (content && content->IsInAnonymousSubtree());
+  bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
+                               content->IsInShadowTree()));
 
   aEvent->mFlags.mIsBeingDispatched = true;
 
   // Create visitor object and start event dispatching.
   // PreHandleEvent for the original target.
   nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
   EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
                                   isInAnon);
--- a/dom/events/EventDispatcher.h
+++ b/dom/events/EventDispatcher.h
@@ -4,20 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZILLA_INTERNAL_API
 #ifndef mozilla_EventDispatcher_h_
 #define mozilla_EventDispatcher_h_
 
 #include "mozilla/EventForwards.h"
 #include "nsCOMPtr.h"
+#include "nsTArray.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
+class nsIContent;
 class nsIDOMEvent;
 class nsIScriptGlobalObject;
 class nsPresContext;
 
 template<class E> class nsCOMArray;
 
 namespace mozilla {
 namespace dom {
@@ -188,16 +190,23 @@ public:
    */
   dom::EventTarget* mParentTarget;
 
   /**
    * If the event needs to be retargeted, this is the event target,
    * which should be used when the event is handled at mParentTarget.
    */
   dom::EventTarget* mEventTargetAtParent;
+
+  /**
+   * An array of destination insertion points that need to be inserted
+   * into the event path of nodes that are distributed by the
+   * web components distribution algorithm.
+   */
+  nsTArray<nsIContent*> mDestInsertionPoints;
 };
 
 class EventChainPostVisitor : public mozilla::EventChainVisitor
 {
 public:
   explicit EventChainPostVisitor(EventChainVisitor& aOther)
     : EventChainVisitor(aOther.mPresContext, aOther.mEvent,
                         aOther.mDOMEvent, aOther.mEventStatus)
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -274,16 +274,23 @@ MouseEvent::GetRelatedTarget()
         do_QueryInterface(mEvent->AsMouseEventBase()->relatedTarget);
       break;
     default:
       break;
   }
 
   if (relatedTarget) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
+    nsCOMPtr<nsIContent> currentTarget = do_QueryInterface(mEvent->currentTarget);
+
+    nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content);
+    if (shadowRelatedTarget) {
+      relatedTarget = shadowRelatedTarget;
+    }
+
     if (content && content->ChromeOnlyAccess() &&
         !nsContentUtils::CanAccessNativeAnon()) {
       relatedTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
     }
 
     if (relatedTarget) {
       relatedTarget = relatedTarget->GetTargetForDOMEvent();
     }
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -269,17 +269,17 @@ OnWrapperDestroyed()
 
       // No more wrappers, and our hash was initialized. Finish the
       // hash to prevent leaking it.
       sJSObjWrappers.finish();
       sJSObjWrappersAccessible = false;
     }
 
     if (sNPObjWrappers.ops) {
-      MOZ_ASSERT(sNPObjWrappers.entryCount == 0);
+      MOZ_ASSERT(sNPObjWrappers.EntryCount() == 0);
 
       // No more wrappers, and our hash was initialized. Finish the
       // hash to prevent leaking it.
       PL_DHashTableFinish(&sNPObjWrappers);
 
       sNPObjWrappers.ops = nullptr;
     }
 
@@ -1758,24 +1758,24 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
       return nullptr;
     }
     return obj;
   }
 
   entry->mNPObj = npobj;
   entry->mNpp = npp;
 
-  uint32_t generation = sNPObjWrappers.generation;
+  uint32_t generation = sNPObjWrappers.Generation();
 
   // No existing JSObject, create one.
 
   JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, &sNPObjectJSWrapperClass, JS::NullPtr(),
                                                JS::NullPtr()));
 
-  if (generation != sNPObjWrappers.generation) {
+  if (generation != sNPObjWrappers.Generation()) {
       // Reload entry if the JS_NewObject call caused a GC and reallocated
       // the table (see bug 445229). This is guaranteed to succeed.
 
       entry = static_cast<NPObjWrapperHashEntry *>
         (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
       NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
                    "Hashtable didn't find what we just added?");
   }
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -11,16 +11,19 @@ support-files =
 [test_fallback_dest_insertion_points.html]
 [test_dynamic_content_element_matching.html]
 [test_document_register.html]
 [test_document_register_base_queue.html]
 [test_document_register_lifecycle.html]
 [test_document_register_parser.html]
 [test_document_register_stack.html]
 [test_document_shared_registry.html]
+[test_event_dispatch.html]
+[test_event_retarget.html]
+[test_event_stopping.html]
 [test_template.html]
 [test_template_xhtml.html]
 [test_shadowroot.html]
 [test_shadowroot_inert_element.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_multiple_shadow.html]
 [test_shadowroot_style_order.html]
 [test_style_fallback_content.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_event_dispatch.html
@@ -0,0 +1,458 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887541
+-->
+<head>
+  <title>Test for event model in web components</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+<script>
+
+var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
+            .getService(SpecialPowers.Ci.nsIEventListenerService);
+
+function eventListener(e) {
+  eventChain.push(this);
+}
+
+function isEventChain(actual, expected, msg) {
+  is(actual.length, expected.length, msg);
+  for (var i = 0; i < expected.length; i++) {
+    is(actual[i], expected[i], msg + " at " + i);
+  }
+
+  // Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor
+  if (0 < actual.length) {
+    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    for (var i = 0; i < expected.length; i++) {
+      ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService");
+    }
+  }
+}
+
+/*
+ * Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point.
+ *
+ * <div elemOne> ------ <shadow-root shadowOne>
+ *        |                        |
+ * <div elemTwo>            <span elemThree>
+ *                                 |
+ *                         <content elemFour>
+ */
+
+var elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+var elemTwo = document.createElement("div");
+elemTwo.addEventListener("custom", eventListener);
+
+var elemThree = document.createElement("span");
+elemThree.addEventListener("custom", eventListener);
+
+var elemFour = document.createElement("content");
+elemFour.addEventListener("custom", eventListener);
+
+var shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+elemThree.appendChild(elemFour);
+shadowOne.appendChild(elemThree);
+elemOne.appendChild(elemTwo);
+
+var eventChain = [];
+var customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo.");
+
+/*
+ * Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points.
+ *
+ * <div elemFive> --- <shadow-root shadowTwo>
+ *       |                       |
+ * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+ *                               |                        |
+ *                       <content elemTwo>           <p elemSix>
+ *                                                        |
+ *                                               <content elemThree>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("content");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("content");
+elemThree.addEventListener("custom", eventListener);
+
+var elemFour = document.createElement("div");
+elemFour.addEventListener("custom", eventListener);
+
+var elemFive = document.createElement("div");
+elemFive.addEventListener("custom", eventListener);
+
+var elemSix = document.createElement("p");
+elemSix.addEventListener("custom", eventListener);
+
+var shadowOne = elemFour.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+var shadowTwo = elemFive.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+elemFive.appendChild(elemOne);
+shadowTwo.appendChild(elemFour);
+elemFour.appendChild(elemTwo);
+shadowOne.appendChild(elemSix);
+elemSix.appendChild(elemThree);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemOne.dispatchEvent(customEvent);
+is(elemOne.getDestinationInsertionPoints().length, 2, "yes");
+isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne.");
+
+/*
+ * Test 3: Test of event dispatch through nested ShadowRoot with content insertion points.
+ *
+ * <div elemOne> ------- <shadow-root shadowOne>
+ *        |                        |
+ * <span elemTwo>          <span elemThree> ------------ <shadow-root shadowTwo>
+ *                                 |                               |
+ *                          <span elemFour>                <content elemSix>
+ *                                 |
+ *                         <content elemFive>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("span");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("span");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("span");
+elemFour.addEventListener("custom", eventListener);
+
+elemFive = document.createElement("content");
+elemFive.addEventListener("custom", eventListener);
+
+elemSix = document.createElement("content");
+elemSix.addEventListener("custom", eventListener);
+
+shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+shadowTwo = elemThree.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+elemOne.appendChild(elemTwo);
+shadowOne.appendChild(elemThree);
+elemThree.appendChild(elemFour);
+elemFour.appendChild(elemFive);
+shadowTwo.appendChild(elemSix);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo.");
+
+/*
+ * Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
+ *
+ * <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot)
+ *       |         |             |
+ * <div elemOne>   |      <div elemSix> -------- <shadow-root shadowOne>
+ *                 |             |                         |
+ *                 |     <shadow elemFour>         <content elemFive>
+ *                 |             |
+ *                 |     <content elemTwo>
+ *                 |
+ *                 --- <shadow-root shadowThree> (older ShadowRoot)
+ *                         |                |
+ *                         |       <content elemThree>
+ *                         |
+ *                  <div elemEight>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("content");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("content");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("shadow");
+elemFour.addEventListener("custom", eventListener);
+
+elemFive = document.createElement("content");
+elemFive.addEventListener("custom", eventListener);
+
+elemSix = document.createElement("div");
+elemSix.addEventListener("custom", eventListener);
+
+var elemSeven = document.createElement("div");
+elemSeven.addEventListener("custom", eventListener);
+
+var elemEight = document.createElement("div");
+elemEight.addEventListener("custom", eventListener);
+
+var shadowThree = elemSeven.createShadowRoot();
+shadowThree.addEventListener("custom", eventListener);
+
+shadowTwo = elemSeven.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+shadowOne = elemSix.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+elemSeven.appendChild(elemOne);
+shadowTwo.appendChild(elemSix);
+elemSix.appendChild(elemFour);
+elemFour.appendChild(elemTwo);
+shadowThree.appendChild(elemEight);
+shadowThree.appendChild(elemThree);
+shadowOne.appendChild(elemFive);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemOne.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne.");
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemEight.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight.");
+
+/*
+ * Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
+ *
+ * <div elemOne> --------- <shadow-root shadowOne>
+ *    |      |                        |
+ *    |  <p elemThree>        <span elemFour> ------------------------ <shadow-root shadowTwo>
+ *    |                          |       |                                        |
+ * <span elemTwo>                |   <content select="p" elemFive>       <content elemSeven>
+ *                               |
+ *                       <content select="span" elemSix>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("span");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("p");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("span");
+elemFour.addEventListener("custom", eventListener);
+
+elemFive = document.createElement("content");
+elemFive.select = "p";
+elemFive.addEventListener("custom", eventListener);
+
+elemSix = document.createElement("content");
+elemSix.select = "span";
+elemSix.addEventListener("custom", eventListener);
+
+elemSeven = document.createElement("content");
+elemSeven.addEventListener("custom", eventListener);
+
+shadowTwo = elemFour.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+elemOne.appendChild(elemTwo);
+elemOne.appendChild(elemThree);
+shadowOne.appendChild(elemFour);
+elemFour.appendChild(elemSix);
+elemFour.appendChild(elemFive);
+shadowTwo.appendChild(elemSeven);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo.");
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree.");
+
+/*
+ * Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
+ *
+ * <div elemOne> --------- <shadow-root shadowOne>;
+ *    |      |                        |
+ *    |  <p elemThree>         <span elemFour> ------ <shadow-root shadowTwo>
+ *    |                               |                   |            |
+ * <span elemTwo>            <content elemFive>           |  <content select="p" elemSeven>
+ *                                                        |
+ *                                                 <content select="span" elemSix>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("span");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("p");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("span");
+elemFour.addEventListener("custom", eventListener);
+
+elemFive = document.createElement("content");
+elemFive.addEventListener("custom", eventListener);
+
+elemSix = document.createElement("content");
+elemSix.select = "span";
+elemSix.addEventListener("custom", eventListener);
+
+elemSeven = document.createElement("content");
+elemSeven.select = "p";
+elemSeven.addEventListener("custom", eventListener);
+
+shadowTwo = elemFour.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+elemOne.appendChild(elemTwo);
+elemOne.appendChild(elemThree);
+shadowOne.appendChild(elemFour);
+elemFour.appendChild(elemFive);
+shadowTwo.appendChild(elemSix);
+shadowTwo.appendChild(elemSeven);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo.");
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree.");
+
+/*
+ * Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
+ *
+ * <div elemOne> --------- <shadow-root shadowOne>
+ *    |      |                        |
+ *    |  <p elemThree>         <span elemFour> ------ <shadow-root shadowTwo>
+ *    |                               |                         |
+ * <span elemTwo>            <content elemFive>           <span elemEight>
+ *                                                           |        |
+ *                                                           |   <content select="p" elemSeven>
+ *                                                           |
+ *                                              <content select="span" elemSix>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("span");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("p");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("span");
+elemFour.addEventListener("custom", eventListener);
+
+elemFive = document.createElement("content");
+elemFive.addEventListener("custom", eventListener);
+
+elemSix = document.createElement("content");
+elemSix.select = "span";
+elemSix.addEventListener("custom", eventListener);
+
+elemSeven = document.createElement("content");
+elemSeven.select = "p";
+elemSeven.addEventListener("custom", eventListener);
+
+elemEight = document.createElement("span");
+elemEight.addEventListener("custom", eventListener);
+
+shadowTwo = elemFour.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+elemOne.appendChild(elemTwo);
+elemOne.appendChild(elemThree);
+shadowOne.appendChild(elemFour);
+elemFour.appendChild(elemFive);
+shadowTwo.appendChild(elemEight);
+elemEight.appendChild(elemSix);
+elemEight.appendChild(elemSeven);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo.");
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree.");
+
+/*
+ * Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
+ *
+ * <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot)
+ *               |             |
+ *               |      <div elemFour>
+ *               |             |
+ *               |     <shadow elemTwo>
+ *               |
+ *               --- <shadow-root shadowTwo> (older ShadowRoot)
+ *                             |
+ *                      <div elemThree>
+ */
+
+elemOne = document.createElement("div");
+elemOne.addEventListener("custom", eventListener);
+
+elemTwo = document.createElement("shadow");
+elemTwo.addEventListener("custom", eventListener);
+
+elemThree = document.createElement("div");
+elemThree.addEventListener("custom", eventListener);
+
+elemFour = document.createElement("div");
+elemFour.addEventListener("custom", eventListener);
+
+shadowTwo = elemOne.createShadowRoot();
+shadowTwo.addEventListener("custom", eventListener);
+
+shadowOne = elemOne.createShadowRoot();
+shadowOne.addEventListener("custom", eventListener);
+
+shadowOne.appendChild(elemFour);
+elemFour.appendChild(elemTwo);
+shadowTwo.appendChild(elemThree);
+
+eventChain = [];
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree.");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_event_retarget.html
@@ -0,0 +1,147 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887541
+-->
+<head>
+  <title>Test for event retargeting in web components</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+<script>
+
+/*
+ * Creates an event listener with an expected event target.
+ */
+function createEventListener(expectedTarget, msg) {
+  return function(e) {
+    is(e.target, expectedTarget, msg);
+  };
+}
+
+/*
+ * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+ *
+ * <div elemThree> ---- <shadow-root shadowOne>
+ *        |                        |
+ * <div elemOne>            <content elemTwo>
+ *
+ * Dispatch event on elemOne
+ */
+
+var elemOne = document.createElement("div");
+var elemTwo = document.createElement("content");
+var elemThree = document.createElement("div");
+var shadowOne = elemThree.createShadowRoot();
+
+elemThree.appendChild(elemOne);
+shadowOne.appendChild(elemTwo);
+
+elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+
+var customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemOne.dispatchEvent(customEvent);
+
+/*
+ * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+ *
+ * <div elemThree> ---- <shadow-root shadowOne>
+ *        |                        |
+ * <div elemOne>            <content elemTwo>
+ *
+ * Dispatch event on elemTwo
+ */
+
+elemOne = document.createElement("div");
+elemTwo = document.createElement("content");
+elemThree = document.createElement("div");
+shadowOne = elemThree.createShadowRoot();
+
+elemThree.appendChild(elemOne);
+shadowOne.appendChild(elemTwo);
+
+elemTwo.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of elemTwo."));
+elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+shadowOne.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of shadowOne."));
+
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemTwo.dispatchEvent(customEvent);
+
+/*
+ * Test of event retargeting through a nested ShadowRoots with content insertion points.
+ *
+ * <div elemFive> --- <shadow-root shadowTwo>
+ *       |                       |
+ * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+ *                               |                        |
+ *                       <content elemTwo>       <content elemThree>
+ *
+ * Dispatch custom event on elemOne.
+ */
+
+elemOne = document.createElement("div");
+elemTwo = document.createElement("content");
+elemThree = document.createElement("content");
+var elemFour = document.createElement("div");
+var elemFive = document.createElement("div");
+var shadowTwo = elemFive.createShadowRoot();
+shadowOne = elemFour.createShadowRoot();
+
+elemFive.appendChild(elemOne);
+shadowTwo.appendChild(elemFour);
+elemFour.appendChild(elemTwo);
+shadowOne.appendChild(elemThree);
+
+elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+elemFour.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFour."));
+elemFive.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFive."));
+shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+shadowTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowTwo."));
+
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemOne.dispatchEvent(customEvent);
+
+/*
+ * Test of event retargeting through a nested ShadowRoots with content insertion points.
+ *
+ * <div elemFive> --- <shadow-root shadowTwo>
+ *       |                       |
+ * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+ *                               |                        |
+ *                       <content elemTwo>       <content elemThree>
+ *
+ * Dispatch custom event on elemThree.
+ */
+
+elemOne = document.createElement("div");
+elemTwo = document.createElement("content");
+elemThree = document.createElement("content");
+elemFour = document.createElement("div");
+elemFive = document.createElement("div");
+shadowTwo = elemFive.createShadowRoot();
+shadowOne = elemFour.createShadowRoot();
+
+elemFive.appendChild(elemOne);
+shadowTwo.appendChild(elemFour);
+elemFour.appendChild(elemTwo);
+shadowOne.appendChild(elemThree);
+
+elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+elemFour.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of elemFour."));
+elemFive.addEventListener("custom", createEventListener(elemFive, "elemFive is in common ancestor tree of elemFive."));
+shadowOne.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of shadowOne."));
+shadowTwo.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of shadowTwo."));
+
+customEvent = new CustomEvent("custom", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887541
+-->
+<head>
+  <title>Test for event model in web components</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+<script>
+
+var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
+            .getService(SpecialPowers.Ci.nsIEventListenerService);
+
+function eventListener(e) {
+  eventChain.push(this);
+}
+
+function isEventChain(actual, expected, msg) {
+  is(actual.length, expected.length, msg);
+  for (var i = 0; i < expected.length; i++) {
+    is(actual[i], expected[i], msg + " at " + i);
+  }
+
+  if (0 < actual.length) {
+    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    ok(expected.length < chain.length, "There should be additional chrome event targets.");
+  }
+}
+
+/*
+ * <div elemOne> ------ <shadow-root shadowOne>
+ *                                 |
+ *                          <span elemTwo>
+ *                                 |
+ *                         <span elemThree>
+ */
+
+var elemOne = document.createElement("div");
+var elemTwo = document.createElement("span");
+var elemThree = document.createElement("span");
+var shadowOne = elemOne.createShadowRoot();
+
+shadowOne.appendChild(elemTwo);
+elemTwo.appendChild(elemThree);
+
+// Test stopping "abort" event.
+
+elemOne.addEventListener("abort", eventListener);
+elemTwo.addEventListener("abort", eventListener);
+elemThree.addEventListener("abort", eventListener);
+shadowOne.addEventListener("abort", eventListener);
+
+var eventChain = [];
+
+var customEvent = new CustomEvent("abort", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that abort event is stopped at shadow root.");
+
+// Test stopping "error" event.
+
+elemOne.addEventListener("error", eventListener);
+elemTwo.addEventListener("error", eventListener);
+elemThree.addEventListener("error", eventListener);
+shadowOne.addEventListener("error", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("error", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that error event is stopped at shadow root.");
+
+// Test stopping "select" event.
+
+elemOne.addEventListener("select", eventListener);
+elemTwo.addEventListener("select", eventListener);
+elemThree.addEventListener("select", eventListener);
+shadowOne.addEventListener("select", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("select", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that select event is stopped at shadow root.");
+
+// Test stopping "change" event.
+
+elemOne.addEventListener("change", eventListener);
+elemTwo.addEventListener("change", eventListener);
+elemThree.addEventListener("change", eventListener);
+shadowOne.addEventListener("change", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("change", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+
+// Test stopping "reset" event.
+
+elemOne.addEventListener("reset", eventListener);
+elemTwo.addEventListener("reset", eventListener);
+elemThree.addEventListener("reset", eventListener);
+shadowOne.addEventListener("reset", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("reset", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that reset event is stopped at shadow root.");
+
+// Test stopping "load" event.
+
+elemOne.addEventListener("load", eventListener);
+elemTwo.addEventListener("load", eventListener);
+elemThree.addEventListener("load", eventListener);
+shadowOne.addEventListener("load", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("load", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that load event is stopped at shadow root.");
+
+// Test stopping "resize" event.
+
+elemOne.addEventListener("resize", eventListener);
+elemTwo.addEventListener("resize", eventListener);
+elemThree.addEventListener("resize", eventListener);
+shadowOne.addEventListener("resize", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("resize", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that resize event is stopped at shadow root.");
+
+// Test stopping "scroll" event.
+
+elemOne.addEventListener("scroll", eventListener);
+elemTwo.addEventListener("scroll", eventListener);
+elemThree.addEventListener("scroll", eventListener);
+shadowOne.addEventListener("scroll", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("scroll", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that scroll event is stopped at shadow root.");
+
+// Test stopping "selectstart" event.
+
+elemOne.addEventListener("selectstart", eventListener);
+elemTwo.addEventListener("selectstart", eventListener);
+elemThree.addEventListener("selectstart", eventListener);
+shadowOne.addEventListener("selectstart", eventListener);
+
+eventChain = [];
+
+customEvent = new CustomEvent("selectstart", { "bubbles" : true });
+elemThree.dispatchEvent(customEvent);
+isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that selectstart event is stopped at shadow root.");
+
+</script>
+</body>
+</html>
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -210,16 +210,21 @@ DrawTargetCG::CreateSourceSurfaceFromDat
 
   if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
     return nullptr;
   }
 
   return newSurf.forget();
 }
 
+static void releaseDataSurface(void* info, const void *data, size_t size)
+{
+  static_cast<DataSourceSurface*>(info)->Release();
+}
+
 // This function returns a retained CGImage that needs to be released after
 // use. The reason for this is that we want to either reuse an existing CGImage
 // or create a new one.
 static CGImageRef
 GetRetainedImageFromSourceSurface(SourceSurface *aSurface)
 {
   switch(aSurface->GetType()) {
     case SurfaceType::COREGRAPHICS_IMAGE:
@@ -229,17 +234,19 @@ GetRetainedImageFromSourceSurface(Source
       return CGImageRetain(static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage());
 
     default:
     {
       RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
       if (!data) {
         MOZ_CRASH("unsupported source surface");
       }
-      return CreateCGImage(nullptr, data->GetData(), data->GetSize(),
+      data->AddRef();
+      return CreateCGImage(releaseDataSurface, data.get(),
+                           data->GetData(), data->GetSize(),
                            data->Stride(), data->GetFormat());
     }
   }
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
--- a/gfx/2d/SourceSurfaceCG.cpp
+++ b/gfx/2d/SourceSurfaceCG.cpp
@@ -54,16 +54,32 @@ static void releaseCallback(void *info, 
 
 CGImageRef
 CreateCGImage(void *aInfo,
               const void *aData,
               const IntSize &aSize,
               int32_t aStride,
               SurfaceFormat aFormat)
 {
+  return CreateCGImage(releaseCallback,
+                       aInfo,
+                       aData,
+                       aSize,
+                       aStride,
+                       aFormat);
+}
+
+CGImageRef
+CreateCGImage(CGDataProviderReleaseDataCallback aCallback,
+              void *aInfo,
+              const void *aData,
+              const IntSize &aSize,
+              int32_t aStride,
+              SurfaceFormat aFormat)
+{
   //XXX: we should avoid creating this colorspace everytime
   CGColorSpaceRef colorSpace = nullptr;
   CGBitmapInfo bitinfo = 0;
   int bitsPerComponent = 0;
   int bitsPerPixel = 0;
 
   switch (aFormat) {
     case SurfaceFormat::B8G8R8A8:
@@ -92,17 +108,17 @@ CreateCGImage(void *aInfo,
 
   size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
   if (bufLen == 0) {
     return nullptr;
   }
   CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo,
                                                                 aData,
                                                                 bufLen,
-                                                                releaseCallback);
+                                                                aCallback);
 
   CGImageRef image;
   if (aFormat == SurfaceFormat::A8) {
     CGFloat decode[] = {1.0, 0.0};
     image = CGImageMaskCreate (aSize.width, aSize.height,
                                bitsPerComponent,
                                bitsPerPixel,
                                aStride,
--- a/gfx/2d/SourceSurfaceCG.h
+++ b/gfx/2d/SourceSurfaceCG.h
@@ -11,16 +11,24 @@
 #include "2D.h"
 
 class MacIOSurface;
 
 namespace mozilla {
 namespace gfx {
 
 CGImageRef
+CreateCGImage(CGDataProviderReleaseDataCallback aCallback,
+              void *aInfo,
+              const void *aData,
+              const IntSize &aSize,
+              int32_t aStride,
+              SurfaceFormat aFormat);
+
+CGImageRef
 CreateCGImage(void *aInfo,
               const void *aData,
               const IntSize &aSize,
               int32_t aStride,
               SurfaceFormat aFormat);
 
 class DrawTargetCG;
 
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/cairo-region-clip.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Matt Woodrow <mwoodrow@mozilla.com>
+# Date 1408674084 -43200
+#      Fri Aug 22 14:21:24 2014 +1200
+# Node ID 2b819b882c3b26c02d821e8d713591a9b56f1728
+# Parent  ffd1fc7e7d5a85e4823b5f2067b4a24d358a0e41
+Bug 1050788 - Fix cairo clip path region construction when the first path generates no traps. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c
+--- a/gfx/cairo/cairo/src/cairo-clip.c
++++ b/gfx/cairo/cairo/src/cairo-clip.c
+@@ -590,16 +590,22 @@ static cairo_int_status_t
+     status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
+ 							  clip_path->fill_rule,
+ 							  &traps);
+     if (unlikely (_cairo_status_is_error (status)))
+ 	return status;
+     if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ 	goto UNSUPPORTED;
+ 
++    if (unlikely (traps.num_traps == 0)) {
++	clip_path->region = cairo_region_create ();
++	clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
++	return CAIRO_STATUS_SUCCESS;
++    }
++
+     if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
+ 	boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+ 	if (unlikely (boxes == NULL))
+ 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+     }
+ 
+     for (n = 0; n < traps.num_traps; n++) {
+ 	boxes[n].p1.x = traps.traps[n].left.p1.x;
--- a/gfx/cairo/cairo/src/cairo-clip.c
+++ b/gfx/cairo/cairo/src/cairo-clip.c
@@ -590,16 +590,22 @@ static cairo_int_status_t
     status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
 							  clip_path->fill_rule,
 							  &traps);
     if (unlikely (_cairo_status_is_error (status)))
 	return status;
     if (status == CAIRO_INT_STATUS_UNSUPPORTED)
 	goto UNSUPPORTED;
 
+    if (unlikely (traps.num_traps == 0)) {
+	clip_path->region = cairo_region_create ();
+	clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
 	boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
 	if (unlikely (boxes == NULL))
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
     for (n = 0; n < traps.num_traps; n++) {
 	boxes[n].p1.x = traps.traps[n].left.p1.x;
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -25,16 +25,17 @@
 #include "nsISupportsImpl.h"            // for gfxContext::AddRef, etc
 #include "nsSize.h"                     // for nsIntSize
 #include "gfxReusableSharedImageSurfaceWrapper.h"
 #include "nsExpirationTracker.h"        // for nsExpirationTracker
 #include "nsMathUtils.h"               // for NS_roundf
 #include "gfx2DGlue.h"
 #include "LayersLogging.h"
 #include "UnitTransforms.h"             // for TransformTo
+#include "mozilla/UniquePtr.h"
 
 // This is the minimum area that we deem reasonable to copy from the front buffer to the
 // back buffer on tile updates. If the valid region is smaller than this, we just
 // redraw it and save on the copy (and requisite surface-locking involved).
 #define MINIMUM_TILE_COPY_AREA (1.f/16.f)
 
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
 #include "cairo.h"
@@ -429,38 +430,59 @@ gfxShmSharedReadLock::GetReadCount() {
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   return info->readCount;
 }
 
 class TileExpiry MOZ_FINAL : public nsExpirationTracker<TileClient, 3>
 {
   public:
     TileExpiry() : nsExpirationTracker<TileClient, 3>(1000) {}
+
+    static void AddTile(TileClient* aTile)
+    {
+      if (!sTileExpiry) {
+        sTileExpiry = MakeUnique<TileExpiry>();
+      }
+
+      sTileExpiry->AddObject(aTile);
+    }
+
+    static void RemoveTile(TileClient* aTile)
+    {
+      MOZ_ASSERT(sTileExpiry);
+      sTileExpiry->RemoveObject(aTile);
+    }
+
+    static void Shutdown() {
+      sTileExpiry = nullptr;
+    }
   private:
-    virtual void NotifyExpired(TileClient* aTile)
+    virtual void NotifyExpired(TileClient* aTile) MOZ_OVERRIDE
     {
       aTile->DiscardBackBuffer();
     }
-};
 
-TileExpiry *TileExpirer()
+    static UniquePtr<TileExpiry> sTileExpiry;
+};
+UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
+
+void ShutdownTileCache()
 {
-  static TileExpiry * sTileExpiry = new TileExpiry();
-  return sTileExpiry;
+  TileExpiry::Shutdown();
 }
 
 void
 TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
 {
   if (mBuffer) {
-    TileExpirer()->RemoveObject(aContainer);
+    TileExpiry::RemoveTile(aContainer);
   }
   mBuffer = aNewValue;
   if (mBuffer) {
-    TileExpirer()->AddObject(aContainer);
+    TileExpiry::AddTile(aContainer);
   }
 }
 
 void
 TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
 {
   Set(aContainer, RefPtr<TextureClient>(aNewValue));
 }
@@ -470,17 +492,17 @@ TileClient::TileClient()
   : mCompositableClient(nullptr)
 {
 }
 
 TileClient::~TileClient()
 {
   if (mExpirationState.IsTracked()) {
     MOZ_ASSERT(mBackBuffer);
-    TileExpirer()->RemoveObject(this);
+    TileExpiry::RemoveTile(this);
   }
 }
 
 TileClient::TileClient(const TileClient& o)
 {
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
@@ -1117,32 +1139,31 @@ ClientTiledLayerBuffer::ValidateTile(Til
     if (backBuffer->HasInternalBuffer()) {
       // If our new buffer has an internal buffer, we don't want to keep another
       // TextureClient around unnecessarily, so discard the back-buffer.
       aTile.DiscardBackBuffer();
     }
 
     // prepare an array of Moz2D tiles that will be painted into in PostValidate
     gfx::Tile moz2DTile;
-    gfx::Tile moz2DTileOnWhite;
-    moz2DTile.mDrawTarget = backBuffer->BorrowDrawTarget();
+    RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
+    RefPtr<DrawTarget> dtOnWhite;
     if (backBufferOnWhite) {
-      moz2DTileOnWhite.mDrawTarget = backBufferOnWhite->BorrowDrawTarget();
+      dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+      moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+    } else {
+      moz2DTile.mDrawTarget = dt;
     }
-    moz2DTile.mTileOrigin = moz2DTileOnWhite.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
-    if (!moz2DTile.mDrawTarget ||
-        (backBufferOnWhite && !moz2DTileOnWhite.mDrawTarget)) {
+    moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+    if (!dt || (backBufferOnWhite && !dtOnWhite)) {
       aTile.DiscardFrontBuffer();
       return aTile;
     }
 
     mMoz2DTiles.push_back(moz2DTile);
-    if (backBufferOnWhite) {
-      mMoz2DTiles.push_back(moz2DTileOnWhite);
-    }
 
     nsIntRegionRectIterator it(aDirtyRegion);
     for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) {
       gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x,
                          dirtyRect->y - aTileOrigin.y,
                          dirtyRect->width,
                          dirtyRect->height);
       drawRect.Scale(mResolution);
@@ -1151,20 +1172,20 @@ ClientTiledLayerBuffer::ValidateTile(Til
                             NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution),
                             drawRect.width,
                             drawRect.height);
       gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y));
       // Mark the newly updated area as invalid in the front buffer
       aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
 
       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-        moz2DTile.mDrawTarget->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
-        moz2DTileOnWhite.mDrawTarget->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+        dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+        dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
       } else if (content == gfxContentType::COLOR_ALPHA) {
-        moz2DTile.mDrawTarget->ClearRect(drawRect);
+        dt->ClearRect(drawRect);
       }
     }
 
     return aTile;
   } else {
     MOZ_ASSERT(!backBufferOnWhite, "Component alpha only supported with TiledDrawTarget");
   }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -88,23 +88,24 @@ class mozilla::gl::SkiaGLGlue : public G
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 
 #include "nsIGfxInfo.h"
 #include "nsIXULRuntime.h"
 
-#ifdef MOZ_WIDGET_GONK
 namespace mozilla {
 namespace layers {
+#ifdef MOZ_WIDGET_GONK
 void InitGralloc();
+#endif
+void ShutdownTileCache();
 }
 }
-#endif
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 gfxPlatform *gPlatform = nullptr;
 static bool gEverInitialized = false;
 
 static Mutex* gGfxPlatformPrefsLock = nullptr;
@@ -437,16 +438,17 @@ gfxPlatform::Shutdown()
     // These may be called before the corresponding subsystems have actually
     // started up. That's OK, they can handle it.
     gfxFontCache::Shutdown();
     gfxFontGroup::Shutdown();
     gfxGradientCache::Shutdown();
     gfxAlphaBoxBlur::ShutdownBlurCache();
     gfxGraphiteShaper::Shutdown();
     gfxPlatformFontList::Shutdown();
+    ShutdownTileCache();
 
     // Free the various non-null transforms and loaded profiles
     ShutdownCMS();
 
     // In some cases, gPlatform may not be created but Shutdown() called,
     // e.g., during xpcshell tests.
     if (gPlatform) {
         /* Unregister our CMS Override callback. */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3514,16 +3514,18 @@ JSObject::CopyElementsForWrite(ThreadSaf
     uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
     uint32_t newAllocated = goodAllocated(allocated);
 
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
 
     if (newCapacity >= NELEMENTS_LIMIT)
         return false;
 
+    JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
+
     ObjectElements *newheader = AllocateElements(cx, obj, newAllocated);
     if (!newheader)
         return false;
     js_memcpy(newheader, obj->getElementsHeader(),
               (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
 
     newheader->capacity = newCapacity;
     newheader->clearCopyOnWrite();
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1593,17 +1593,17 @@ PerThreadData::runtimeFromMainThread()
 {
     JS_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     return runtime_;
 }
 
 inline JSRuntime *
 PerThreadData::runtimeIfOnOwnerThread()
 {
-    return CurrentThreadCanAccessRuntime(runtime_) ? runtime_ : nullptr;
+    return (runtime_ && CurrentThreadCanAccessRuntime(runtime_)) ? runtime_ : nullptr;
 }
 
 inline bool
 PerThreadData::exclusiveThreadsPresent()
 {
     return runtime_->exclusiveThreadsPresent();
 }
 
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -480,17 +480,17 @@ IID2ThisTranslatorMap::Entry::Match(PLDH
 {
     return ((const nsID*)key)->Equals(((Entry*)entry)->key);
 }
 
 void
 IID2ThisTranslatorMap::Entry::Clear(PLDHashTable *table, PLDHashEntryHdr *entry)
 {
     static_cast<Entry*>(entry)->value = nullptr;
-    memset(entry, 0, table->entrySize);
+    memset(entry, 0, table->EntrySize());
 }
 
 const struct PLDHashTableOps IID2ThisTranslatorMap::Entry::sOps =
 {
     PL_DHashAllocTable,
     PL_DHashFreeTable,
     HashIIDPtrKey,
     Match,
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -142,17 +142,17 @@ public:
         MOZ_ASSERT(!wrapperInMap || wrapperInMap == wrapper,
                    "About to remove a different wrapper with the same "
                    "nsISupports identity! This will most likely cause serious "
                    "problems!");
 #endif
         PL_DHashTableOperate(mTable, wrapper->GetIdentityObject(), PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~Native2WrappedNativeMap();
 private:
     Native2WrappedNativeMap();    // no implementation
@@ -204,17 +204,17 @@ public:
     }
 
     inline void Remove(nsXPCWrappedJSClass* clazz)
     {
         NS_PRECONDITION(clazz,"bad param");
         PL_DHashTableOperate(mTable, &clazz->GetIID(), PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2WrappedJSClassMap();
 private:
     IID2WrappedJSClassMap();    // no implementation
     IID2WrappedJSClassMap(int size);
 private:
@@ -261,17 +261,17 @@ public:
     }
 
     inline void Remove(XPCNativeInterface* iface)
     {
         NS_PRECONDITION(iface,"bad param");
         PL_DHashTableOperate(mTable, iface->GetIID(), PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~IID2NativeInterfaceMap();
 private:
     IID2NativeInterfaceMap();    // no implementation
@@ -320,17 +320,17 @@ public:
     }
 
     inline void Remove(nsIClassInfo* info)
     {
         NS_PRECONDITION(info,"bad param");
         PL_DHashTableOperate(mTable, info, PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     // ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets.
     // So we don't want to count those XPCNativeSets, because they are better
     // counted elsewhere (i.e. in XPCJSRuntime::mNativeSetMap, which holds
     // pointers to *all* XPCNativeSets).  Hence the "Shallow".
     size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
@@ -380,17 +380,17 @@ public:
     }
 
     inline void Remove(nsIClassInfo* info)
     {
         NS_PRECONDITION(info,"bad param");
         PL_DHashTableOperate(mTable, info, PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~ClassInfo2WrappedNativeProtoMap();
 private:
     ClassInfo2WrappedNativeProtoMap();    // no implementation
@@ -453,17 +453,17 @@ public:
     inline void Remove(XPCNativeSet* set)
     {
         NS_PRECONDITION(set,"bad param");
 
         XPCNativeSetKey key(set, nullptr, 0);
         PL_DHashTableOperate(mTable, &key, PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~NativeSetMap();
 private:
     NativeSetMap();    // no implementation
@@ -520,17 +520,17 @@ public:
         return obj;
     }
 
     inline void Remove(REFNSIID iid)
     {
         PL_DHashTableOperate(mTable, &iid, PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2ThisTranslatorMap();
 private:
     IID2ThisTranslatorMap();    // no implementation
     IID2ThisTranslatorMap(int size);
 private:
@@ -557,17 +557,17 @@ public:
         static const struct PLDHashTableOps sOps;
     };
 
     static XPCNativeScriptableSharedMap* newMap(int length);
 
     bool GetNewOrUsed(uint32_t flags, char* name, uint32_t interfacesBitmap,
                       XPCNativeScriptableInfo* si);
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCNativeScriptableSharedMap();
 private:
     XPCNativeScriptableSharedMap();    // no implementation
     XPCNativeScriptableSharedMap(int size);
 private:
@@ -595,17 +595,17 @@ public:
     }
 
     inline void Remove(XPCWrappedNativeProto* proto)
     {
         NS_PRECONDITION(proto,"bad param");
         PL_DHashTableOperate(mTable, proto, PL_DHASH_REMOVE);
     }
 
-    inline uint32_t Count() {return mTable->entryCount;}
+    inline uint32_t Count() { return mTable->EntryCount(); }
     inline uint32_t Enumerate(PLDHashEnumerator f, void *arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCWrappedNativeProtoMap();
 private:
     XPCWrappedNativeProtoMap();    // no implementation
     XPCWrappedNativeProtoMap(int size);
 private:
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1689,17 +1689,17 @@ nsCSSRendering::GetBackgroundClip(const 
   if (isSolidBorder && backgroundClip == NS_STYLE_BG_CLIP_BORDER) {
     // If we have rounded corners, we need to inflate the background
     // drawing area a bit to avoid seams between the border and
     // background.
     backgroundClip = haveRoundedCorners ?
       NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
   }
 
-  aClipState->mBGClipArea = aBorderArea;
+  aClipState->mBGClipArea = clipBorderArea;
   aClipState->mHasAdditionalBGClipArea = false;
   aClipState->mCustomClip = false;
 
   if (aForFrame->GetType() == nsGkAtoms::scrollFrame &&
       NS_STYLE_BG_ATTACHMENT_LOCAL == aLayer.mAttachment) {
     // As of this writing, this is still in discussion in the CSS Working Group
     // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -24,17 +24,16 @@
 #include "nsView.h"
 #include "nsViewportFrame.h"
 #include "RenderFrameParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "ClientLayerManager.h"
 #include "FrameLayerBuilder.h"
 
-typedef nsContentView::ViewConfig ViewConfig;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace layout {
 
 typedef FrameMetrics::ViewID ViewID;
@@ -299,23 +298,16 @@ RenderFrameParent::RenderFrameParent(nsF
   // Perhaps the document containing this frame currently has no presentation?
   if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
     *aTextureFactoryIdentifier =
       static_cast<ClientLayerManager*>(lm.get())->GetTextureFactoryIdentifier();
   } else {
     *aTextureFactoryIdentifier = TextureFactoryIdentifier();
   }
 
-  if (lm && lm->GetRoot() && lm->GetRoot()->AsContainerLayer()) {
-    ViewID rootScrollId = lm->GetRoot()->AsContainerLayer()->GetFrameMetrics().GetScrollId();
-    if (rootScrollId != FrameMetrics::NULL_SCROLL_ID) {
-      mRootContentView = new nsContentView(aFrameLoader, rootScrollId, true);
-    }
-  }
-
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // Our remote frame will push layers updates to the compositor,
     // and we'll keep an indirect reference to that tree.
     *aId = mLayersId = CompositorParent::AllocateLayerTreeId();
     if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
       ClientLayerManager *clientManager =
         static_cast<ClientLayerManager*>(lm.get());
       clientManager->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
@@ -351,22 +343,16 @@ RenderFrameParent::~RenderFrameParent()
 {}
 
 void
 RenderFrameParent::Destroy()
 {
   mFrameLoaderDestroyed = true;
 }
 
-nsContentView*
-RenderFrameParent::GetRootContentView()
-{
-  return mRootContentView;
-}
-
 already_AddRefed<Layer>
 RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
                               nsIFrame* aFrame,
                               LayerManager* aManager,
                               const nsIntRect& aVisibleRect,
                               nsDisplayItem* aItem,
                               const ContainerLayerParameters& aContainerParameters)
 {
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -10,17 +10,16 @@
 
 #include "mozilla/Attributes.h"
 #include <map>
 
 #include "mozilla/layout/PRenderFrameParent.h"
 #include "nsDisplayList.h"
 #include "RenderFrameUtils.h"
 
-class nsContentView;
 class nsFrameLoader;
 class nsSubDocumentFrame;
 
 namespace mozilla {
 
 class InputEvent;
 
 namespace layers {
@@ -61,22 +60,16 @@ public:
   RenderFrameParent(nsFrameLoader* aFrameLoader,
                     ScrollingBehavior aScrollingBehavior,
                     TextureFactoryIdentifier* aTextureFactoryIdentifier,
                     uint64_t* aId, bool* aSuccess);
   virtual ~RenderFrameParent();
 
   void Destroy();
 
-  /**
-   * Helper functions for getting a non-owning reference to a scrollable.
-   * @param aId The ID of the frame.
-   */
-  nsContentView* GetRootContentView();
-
   void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                         nsSubDocumentFrame* aFrame,
                         const nsRect& aDirtyRect,
                         const nsDisplayListSet& aLists);
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame,
                                      LayerManager* aManager,
@@ -135,20 +128,16 @@ private:
   // When our scrolling behavior is ASYNC_PAN_ZOOM, we have a nonnull
   // APZCTreeManager. It's used to manipulate the shadow layer tree
   // on the compositor thread.
   nsRefPtr<layers::APZCTreeManager> mApzcTreeManager;
   nsRefPtr<RemoteContentController> mContentController;
 
   layers::APZCTreeManager* GetApzcTreeManager();
 
-  // This contains the views for all the scrollable frames currently in the
-  // painted region of our remote content.
-  nsRefPtr<nsContentView> mRootContentView;
-
   // True after Destroy() has been called, which is triggered
   // originally by nsFrameLoader::Destroy().  After this point, we can
   // no longer safely ask the frame loader to find its nearest layer
   // manager, because it may have been disconnected from the DOM.
   // It's still OK to *tell* the frame loader that we've painted after
   // it's destroyed; it'll just ignore us, and we won't be able to
   // find an nsIFrame to invalidate.  See ShadowLayersUpdated().
   //
deleted file mode 100644
--- a/layout/ipc/jar.mn
+++ /dev/null
@@ -1,8 +0,0 @@
-# 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/.
-
-toolkit.jar:
-        content/global/test-ipcbrowser.xul (test-ipcbrowser.xul)
-        content/global/test-ipcbrowser-chrome.js (test-ipcbrowser-chrome.js)
-        content/global/test-ipcbrowser-content.js (test-ipcbrowser-content.js)
--- a/layout/ipc/moz.build
+++ b/layout/ipc/moz.build
@@ -26,10 +26,8 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/content/base/src',
     '/layout/base',
     '/layout/generic',
     '/layout/xul',
 ]
-
-JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
deleted file mode 100644
--- a/layout/ipc/test-ipcbrowser-chrome.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/* 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/. */
-
-function init() {
-    enableAsyncScrolling();
-    messageManager.loadFrameScript(
-        "chrome://global/content/test-ipcbrowser-content.js", true
-    );
-}
-
-function browser() {
-    return document.getElementById("content");
-}
-
-function frameLoader() {
-    return browser().QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
-}
-
-function viewManager() {
-    return frameLoader().QueryInterface(Components.interfaces.nsIContentViewManager);
-}
-
-function rootView() {
-    return viewManager().rootContentView;
-}
-
-function enableAsyncScrolling() {
-    frameLoader().renderMode = Components.interfaces.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL;
-}
-
-// Functions affecting the content window.
-
-function loadURL(url) {
-    browser().setAttribute('src', url);
-}
-
-function scrollContentBy(dx, dy) {
-    messageManager.broadcastAsyncMessage("scrollBy",
-                                         { dx: dx, dy: dy });
-
-}
-
-function scrollContentTo(x, y) {
-    messageManager.broadcastAsyncMessage("scrollTo",
-                                         { x: x, y: y });
-}
-
-function setContentViewport(w, h) {
-    messageManager.broadcastAsyncMessage("setViewport",
-                                         { w: w, h: h });
-}
-
-function setContentDisplayPort(x, y, w, h) {
-    messageManager.broadcastAsyncMessage("setDisplayPort",
-                                         { x: x, y: y, w: w, h: h });
-}
-
-function setContentResolution(xres, yres) {
-    messageManager.broadcastAsyncMessage("setResolution",
-                                         { xres: xres, yres: yres });
-}
-
-// Functions affecting <browser>.
-
-function scrollViewportBy(dx, dy) {
-    rootView().scrollBy(dx, dy);
-}
-
-function scrollViewportTo(x, y) {
-    rootView().scrollTo(x, y);
-}
-
-function setViewportScale(xs, ys) {
-    rootView().setScale(xs, ys);
-}
-
-var kDelayMs = 100;
-var kDurationMs = 250;
-var scrolling = false;
-function startAnimatedScrollBy(dx, dy) {
-    if (scrolling)
-        throw "don't interrupt me!";
-
-    scrolling = true;
-
-    var start = mozAnimationStartTime;
-    var end = start + kDurationMs;
-    // |- k| so that we do something in first invocation of nudge()
-    var prevNow = start - 20;
-    var accumDx = 0, accumDy = 0;
-
-    var sentScrollBy = false;
-    function nudgeScroll(now) {
-	if (!scrolling) {
-	    // we've been canceled
-	    return;
-	}
-        var ddx = dx * (now - prevNow) / kDurationMs;
-        var ddy = dy * (now - prevNow) / kDurationMs;
-
-        ddx = Math.min(dx - accumDx, ddx);
-        ddy = Math.min(dy - accumDy, ddy);
-        accumDx += ddx;
-        accumDy += ddy;
-
-        rootView().scrollBy(ddx, ddy);
-
-        if (!sentScrollBy && 100 <= (now - start)) {
-            messageManager.broadcastAsyncMessage("scrollBy",
-                                                 { dx: dx, dy: dy });
-            sentScrollBy = true;
-        }
-
-        if (now >= end || (accumDx >= dx && accumDy >= dy)) {
-            var fixupDx = Math.max(dx - accumDx, 0);
-            var fixupDy = Math.max(dy - accumDy, 0);
-            rootView().scrollBy(fixupDx, fixupDy);
-
-            scrolling = false;
-        }
-        else {
-            mozRequestAnimationFrame(nudgeScroll);
-        }
-
-        prevNow = now;
-    }
-
-    nudgeScroll(start);
-    mozRequestAnimationFrame(nudgeScroll);
-}
deleted file mode 100644
--- a/layout/ipc/test-ipcbrowser-content.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* 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/. */
-
-function windowUtils() {
-    return content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-               .getInterface(Components.interfaces.nsIDOMWindowUtils);
-}
-
-function recvSetViewport(w, h) {
-
-    dump("setting viewport to "+ w +"x"+ h +"\n");
-
-    windowUtils().setCSSViewport(w, h);
-}
-
-function recvSetDisplayPort(x, y, w, h) {
-
-    dump("setting displayPort to <"+ x +", "+ y +", "+ w +", "+ h +">\n");
-
-    windowUtils().setDisplayPortForElement(x, y, w, h, content.document.documentElement, 0);
-}
-
-function recvSetResolution(xres, yres) {
-
-    dump("setting xres="+ xres +" yres="+ yres +"\n");
-
-    windowUtils().setResolution(xres, yres);
-}
-
-function recvScrollBy(dx, dy) {
-    content.scrollBy(dx, dy);
-}
-
-function recvScrollTo(x, y) {
-    content.scrollTo(x, y);
-}
-
-addMessageListener(
-    "setViewport",
-    function (m) { recvSetViewport(m.json.w, m.json.h); }
-);
-
-addMessageListener(
-    "setDisplayPort",
-    function (m) { recvSetDisplayPort(m.json.x, m.json.y,
-                                      m.json.w, m.json.h); }
-);
-
-addMessageListener(
-    "setResolution",
-    function (m) { recvSetResolution(m.json.xres, m.json.yres); }
-);
-
-addMessageListener(
-    "scrollBy",
-    function(m) { recvScrollBy(m.json.dx, m.json.dy); }
-);
-
-addMessageListener(
-    "scrollTo",
-    function(m) { recvScrollTo(m.json.x, m.json.y); }
-);
deleted file mode 100644
--- a/layout/ipc/test-ipcbrowser.xul
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0"?>
-<!-- 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/. -->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        width="800" height="800" orient="vertical"
-        onload="init()">
-  <script src="chrome://global/content/test-ipcbrowser-chrome.js"/>
-
-  <toolbar id="controls">
-    <toolbarbutton label="Back"/>
-    <toolbarbutton label="Forward"/>
-    <textbox onchange="loadURL(this.value)" flex="1"/>
-  </toolbar>
-  <toolbar>
-    <textbox id="scbDx" flex="1" value="0"/>
-    <textbox id="scbDy" flex="1" value="100"/>
-    <toolbarbutton
-       onclick="scrollContentBy(document.getElementById('scbDx').value,
-                                document.getElementById('scbDy').value);"
-       label="scrollBy"/>
-    <textbox id="sctX" flex="1" value="200"/>
-    <textbox id="sctY" flex="1" value="300"/>
-    <toolbarbutton
-       onclick="scrollContentTo(document.getElementById('sctX').value,
-                                document.getElementById('sctY').value);"
-       label="scrollTo"/>
-  </toolbar>
-  <toolbar>
-    <textbox id="vW" flex="1" value="600"/>
-    <textbox id="vH" flex="1" value="400"/>
-    <toolbarbutton
-       onclick="setContentViewport(document.getElementById('vW').value,
-                                   document.getElementById('vH').value);"
-       label="setViewport"/>
-    <textbox id="dX" flex="1" value="0"/>
-    <textbox id="dY" flex="1" value="0"/>
-    <textbox id="dW" flex="1" value="600"/>
-    <textbox id="dH" flex="1" value="600"/>
-    <toolbarbutton
-       onclick="setContentDisplayPort(document.getElementById('dX').value,
-                                      document.getElementById('dY').value,
-                                      document.getElementById('dW').value,
-                                      document.getElementById('dH').value);"
-       label="setDisplayPort"/>
-    <textbox id="xR" flex="1" value="2.0"/>
-    <textbox id="yR" flex="1" value="2.0"/>
-    <toolbarbutton
-       onclick="setContentResolution(document.getElementById('xR').value,
-                                     document.getElementById('yR').value);"
-       label="setResolution"/>
-  </toolbar>
-  <toolbar>
-    <textbox id="vsbX" flex="1" value="0"/>
-    <textbox id="vsbY" flex="1" value="100"/>
-    <toolbarbutton
-       onclick="scrollViewportBy(document.getElementById('vsbX').value,
-                                 document.getElementById('vsbY').value);"
-       label="scrollViewportBy"/>
-    <textbox id="vstX" flex="1" value="200"/>
-    <textbox id="vstY" flex="1" value="300"/>
-    <toolbarbutton
-       onclick="scrollViewportTo(document.getElementById('vstX').value,
-                                 document.getElementById('vstY').value);"
-       label="scrollViewportTo"/>
-    <textbox id="vsX" flex="1" value="2.0"/>
-    <textbox id="vsY" flex="1" value="2.0"/>
-    <toolbarbutton
-       onclick="setViewportScale(document.getElementById('vsX').value,
-                                 document.getElementById('vsY').value);"
-       label="setViewportScale"/>
-  </toolbar>
-
-  <browser type="content" src="http://www.google.com/" flex="1" id="content"
-           remote="true"/>
-</window>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1050788-1.html
@@ -0,0 +1,34 @@
+<html class="reftest-wait">
+<head>
+  <script>
+    function doTest() {
+      var c = document.getElementById('c');
+      var ctx = c.getContext('2d');
+
+      ctx.transform(1, 0, 0, 1, 0.5, 0.5);
+
+      ctx.moveTo(0,0);
+      ctx.lineTo(100,0);
+      ctx.lineTo(100, 100);
+      ctx.lineTo(0,100);
+      ctx.closePath();
+
+      ctx.moveTo(0,0);
+      ctx.lineTo(100,0);
+      ctx.lineTo(100, 100);
+      ctx.lineTo(0,100);
+      ctx.closePath();
+
+      ctx.clip('evenodd');
+
+      ctx.fillStyle = 'red';
+      ctx.fillRect(0,0,400,400);
+
+      document.documentElement.removeAttribute("class");
+  }
+  </script>
+</head>
+<body onload="doTest()">
+  <canvas id="c" width="400" height="400"></canvas>
+</body>
+</body></html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1816,10 +1816,11 @@ pref(layout.css.sticky.enabled,true) == 
 fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),255,1) == 1013054-1.html 1013054-1-ref.html
 pref(layout.css.will-change.enabled,true) == 1018522-1.html 1018522-1-ref.html
 pref(browser.display.use_document_fonts,0) == 1022481-1.html 1022481-1-ref.html
 == 1022612-1.html 1022612-1-ref.html
 == 1024473-1.html 1024473-1-ref.html
 == 1042104-1.html 1042104-1-ref.html
 == 1044198-1.html 1044198-1-ref.html
 == 1049499-1.html 1049499-1-ref.html
+== 1050788-1.html about:blank
 == 1053035-1-flex.html 1053035-1-ref.html
 test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2545,17 +2545,17 @@ nsCSSRuleProcessor::RulesMatching(Pseudo
   }
 }
 
 /* virtual */ void
 nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
 {
   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
 
-  if (cascade && cascade->mAnonBoxRules.entryCount) {
+  if (cascade && cascade->mAnonBoxRules.EntryCount()) {
     RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
       (PL_DHashTableOperate(&cascade->mAnonBoxRules, aData->mPseudoTag,
                             PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       nsTArray<RuleValue>& rules = entry->mRules;
       for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
            value != end; ++value) {
         value->mRule->RuleMatched();
@@ -2566,17 +2566,17 @@ nsCSSRuleProcessor::RulesMatching(AnonBo
 }
 
 #ifdef MOZ_XUL
 /* virtual */ void
 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
 {
   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
 
-  if (cascade && cascade->mXULTreeRules.entryCount) {
+  if (cascade && cascade->mXULTreeRules.EntryCount()) {
     RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
       (PL_DHashTableOperate(&cascade->mXULTreeRules, aData->mPseudoTag,
                             PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       NodeMatchContext nodeContext(EventStates(),
                                    nsCSSRuleProcessor::IsLink(aData->mElement));
       nsTArray<RuleValue>& rules = entry->mRules;
       for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
@@ -3504,17 +3504,17 @@ nsCSSRuleProcessor::RefreshRuleCascade(n
         return; /* out of memory */
 
       for (uint32_t i = 0; i < mSheets.Length(); ++i) {
         if (!CascadeSheet(mSheets.ElementAt(i), &data))
           return; /* out of memory */
       }
 
       // Sort the hash table of per-weight linked lists by weight.
-      uint32_t weightCount = data.mRulesByWeight.entryCount;
+      uint32_t weightCount = data.mRulesByWeight.EntryCount();
       nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
       FillWeightArrayData fwData(weightArray);
       PL_DHashTableEnumerate(&data.mRulesByWeight, FillWeightArray, &fwData);
       NS_QuickSort(weightArray, weightCount, sizeof(PerWeightData),
                    CompareWeightData, nullptr);
 
       // Put things into the rule hash.
       // The primary sort is by weight...
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -495,22 +495,22 @@ nsHTMLStyleSheet::UniqueMappedAttributes
 
 void
 nsHTMLStyleSheet::DropMappedAttributes(nsMappedAttributes* aMapped)
 {
   NS_ENSURE_TRUE_VOID(aMapped);
 
   NS_ASSERTION(mMappedAttrTable.ops, "table uninitialized");
 #ifdef DEBUG
-  uint32_t entryCount = mMappedAttrTable.entryCount - 1;
+  uint32_t entryCount = mMappedAttrTable.EntryCount() - 1;
 #endif
 
   PL_DHashTableOperate(&mMappedAttrTable, aMapped, PL_DHASH_REMOVE);
 
-  NS_ASSERTION(entryCount == mMappedAttrTable.entryCount, "not removed");
+  NS_ASSERTION(entryCount == mMappedAttrTable.EntryCount(), "not removed");
 }
 
 nsIStyleRule*
 nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage)
 {
   if (!mLangRuleTable.ops) {
     PL_DHashTableInit(&mLangRuleTable, &LangRuleTable_Ops,
                       nullptr, sizeof(LangRuleTableEntry));
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -9000,19 +9000,19 @@ nsRuleNode::SweepChildren(nsTArray<nsRul
   NS_ASSERTION(!(mDependentBits & NS_RULE_NODE_GC_MARK),
                "missing DestroyIfNotMarked() call");
   NS_ASSERTION(HaveChildren(),
                "why call SweepChildren with no children?");
   uint32_t childrenDestroyed = 0;
   nsRuleNode* survivorsWithChildren = nullptr;
   if (ChildrenAreHashed()) {
     PLDHashTable* children = ChildrenHash();
-    uint32_t oldChildCount = children->entryCount;
+    uint32_t oldChildCount = children->EntryCount();
     PL_DHashTableEnumerate(children, SweepHashEntry, &survivorsWithChildren);
-    childrenDestroyed = oldChildCount - children->entryCount;
+    childrenDestroyed = oldChildCount - children->EntryCount();
     if (childrenDestroyed == oldChildCount) {
       PL_DHashTableDestroy(children);
       mChildren.asVoid = nullptr;
     }
   } else {
     for (nsRuleNode** children = ChildrenListPtr(); *children; ) {
       nsRuleNode* next = (*children)->mNextSibling;
       if ((*children)->DestroyIfNotMarked()) {
--- a/layout/tables/SpanningCellSorter.cpp
+++ b/layout/tables/SpanningCellSorter.cpp
@@ -148,30 +148,30 @@ SpanningCellSorter::GetNext(int32_t *aCo
                 ++mEnumerationIndex;
                 return result;
             }
             /* prepare to enumerate the hash */
             mState = ENUMERATING_HASH;
             mEnumerationIndex = 0;
             if (mHashTable.ops) {
                 HashTableEntry **sh =
-                    new HashTableEntry*[mHashTable.entryCount];
+                    new HashTableEntry*[mHashTable.EntryCount()];
                 if (!sh) {
                     // give up
                     mState = DONE;
                     return nullptr;
                 }
                 PL_DHashTableEnumerate(&mHashTable, FillSortedArray, sh);
-                NS_QuickSort(sh, mHashTable.entryCount, sizeof(sh[0]),
+                NS_QuickSort(sh, mHashTable.EntryCount(), sizeof(sh[0]),
                              SortArray, nullptr);
                 mSortedHashTable = sh;
             }
             /* fall through */
         case ENUMERATING_HASH:
-            if (mHashTable.ops && mEnumerationIndex < mHashTable.entryCount) {
+            if (mHashTable.ops && mEnumerationIndex < mHashTable.EntryCount()) {
                 Item *result = mSortedHashTable[mEnumerationIndex]->mItems;
                 *aColSpan = mSortedHashTable[mEnumerationIndex]->mColSpan;
                 NS_ASSERTION(result, "holes in hash table");
 #ifdef DEBUG_SPANNING_CELL_SORTER
                 printf("SpanningCellSorter[%p]:"
                        " returning list for colspan=%d from hash\n",
                        static_cast<void*>(this), *aColSpan);
 #endif
--- a/mfbt/tests/TestMaybe.cpp
+++ b/mfbt/tests/TestMaybe.cpp
@@ -18,29 +18,29 @@ using mozilla::IsSame;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::Nothing;
 using mozilla::Some;
 using mozilla::Swap;
 using mozilla::ToMaybe;
 using mozilla::UniquePtr;
 
-// Work around a bug in Visual Studio 2010 that prevents expressions of the form
-// |decltype(foo)::type| from working. See here:
+// Work around a bug in Visual Studio 2010 and 2012 that prevents expressions of
+// the form |decltype(foo)::type| from working. See here:
 // http://stackoverflow.com/questions/14330768/c11-compiler-error-when-using-decltypevar-followed-by-internal-type-of-var
-// GCC 4.4 also appears to have a similar bug.
+// GCC < 4.7 also has a similar bug.
 #if MOZ_IS_MSVC
-#  if MOZ_MSVC_VERSION_AT_LEAST(11)
+#  if MOZ_MSVC_VERSION_AT_LEAST(12)
 #    define DECLTYPE(EXPR) decltype(EXPR)
 #  else
      template<typename T> struct Identity { typedef T type; };
 #    define DECLTYPE(EXPR) Identity<decltype(EXPR)>::type
 #  endif
 #elif MOZ_IS_GCC
-#  if MOZ_GCC_VERSION_AT_LEAST(4,5,0)
+#  if MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
 #    define DECLTYPE(EXPR) decltype(EXPR)
 #  else
      template<typename T> struct Identity { typedef T type; };
 #    define DECLTYPE(EXPR) Identity<decltype(EXPR)>::type
 #  endif
 #else
 #  define DECLTYPE(EXPR) decltype(EXPR)
 #endif
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -539,16 +539,21 @@
 ; [Personal Security Manager]
 ;
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 @BINPATH@/chrome/pippki@JAREXT@
 @BINPATH@/chrome/pippki.manifest
 
+; For process sandboxing
+#if defined(MOZ_CONTENT_SANDBOX) || defined(MOZ_GMP_SANDBOX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
+
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.chk
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.chk
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.chk
 bin/libfreebl_32int64_3.so
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -740,17 +740,17 @@ Preferences::GetPreference(PrefSetting* 
     return;
 
   pref_GetPrefFromEntry(entry, aPref);
 }
 
 void
 Preferences::GetPreferences(InfallibleTArray<PrefSetting>* aPrefs)
 {
-  aPrefs->SetCapacity(PL_DHASH_TABLE_CAPACITY(&gHashTable));
+  aPrefs->SetCapacity(gHashTable.Capacity());
   PL_DHashTableEnumerate(&gHashTable, pref_GetPrefs, aPrefs);
 }
 
 NS_IMETHODIMP
 Preferences::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
 {
   nsresult rv;
 
@@ -966,33 +966,33 @@ Preferences::WritePrefFile(nsIFile* aFil
                                        -1,
                                        0600);
   if (NS_FAILED(rv)) 
       return rv;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink, 4096);
   if (NS_FAILED(rv)) 
       return rv;  
 
-  nsAutoArrayPtr<char*> valueArray(new char*[gHashTable.entryCount]);
-  memset(valueArray, 0, gHashTable.entryCount * sizeof(char*));
+  nsAutoArrayPtr<char*> valueArray(new char*[gHashTable.EntryCount()]);
+  memset(valueArray, 0, gHashTable.EntryCount() * sizeof(char*));
   pref_saveArgs saveArgs;
   saveArgs.prefArray = valueArray;
   saveArgs.saveTypes = SAVE_ALL;
   
   // get the lines that we're supposed to be writing to the file
   PL_DHashTableEnumerate(&gHashTable, pref_savePref, &saveArgs);
     
   /* Sort the preferences to make a readable file on disk */
-  NS_QuickSort(valueArray, gHashTable.entryCount, sizeof(char *), pref_CompareStrings, nullptr);
+  NS_QuickSort(valueArray, gHashTable.EntryCount(), sizeof(char *), pref_CompareStrings, nullptr);
   
   // write out the file header
   outStream->Write(outHeader, sizeof(outHeader) - 1, &writeAmount);
 
   char** walker = valueArray;
-  for (uint32_t valueIdx = 0; valueIdx < gHashTable.entryCount; valueIdx++, walker++) {
+  for (uint32_t valueIdx = 0; valueIdx < gHashTable.EntryCount(); valueIdx++, walker++) {
     if (*walker) {
       outStream->Write(*walker, strlen(*walker), &writeAmount);
       outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
       NS_Free(*walker);
     }
   }
 
   // tell the safe output stream to overwrite the real prefs file
--- a/modules/libpref/nsPrefBranch.cpp
+++ b/modules/libpref/nsPrefBranch.cpp
@@ -890,9 +890,9 @@ NS_IMETHODIMP nsRelativeFilePref::GetRel
 }
 
 NS_IMETHODIMP nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey)
 {
   mRelativeToKey.Assign(aRelativeToKey);
   return NS_OK;
 }
 
-#undef ENSURE_MAIN_PROCESS
\ No newline at end of file
+#undef ENSURE_MAIN_PROCESS
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -42,17 +42,17 @@ clearPrefEntry(PLDHashTable *table, PLDH
         if (pref->defaultPref.stringVal)
             PL_strfree(pref->defaultPref.stringVal);
         if (pref->userPref.stringVal)
             PL_strfree(pref->userPref.stringVal);
     }
     // don't need to free this as it's allocated in memory owned by
     // gPrefNameArena
     pref->key = nullptr;
-    memset(entry, 0, table->entrySize);
+    memset(entry, 0, table->EntrySize());
 }
 
 static bool
 matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
                const void* key)
 {
     const PrefHashEntry *prefEntry =
         static_cast<const PrefHashEntry*>(entry);
@@ -60,17 +60,17 @@ matchPrefEntry(PLDHashTable*, const PLDH
     if (prefEntry->key == key) return true;
 
     if (!prefEntry->key || !key) return false;
 
     const char *otherKey = reinterpret_cast<const char*>(key);
     return (strcmp(prefEntry->key, otherKey) == 0);
 }
 
-PLDHashTable        gHashTable = { nullptr };
+PLDHashTable        gHashTable;
 static PLArenaPool  gPrefNameArena;
 bool                gDirty = false;
 
 static struct CallbackNode* gCallbacks = nullptr;
 static bool         gIsAnyPrefLocked = false;
 // These are only used during the call to pref_DoCallback
 static bool         gCallbacksInProgress = false;
 static bool         gShouldCleanupDeadNodes = false;
--- a/netwerk/base/src/nsLoadGroup.cpp
+++ b/netwerk/base/src/nsLoadGroup.cpp
@@ -225,17 +225,17 @@ AppendRequestsToArray(PLDHashTable *tabl
 
 NS_IMETHODIMP
 nsLoadGroup::Cancel(nsresult status)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
     nsresult rv;
-    uint32_t count = mRequests.entryCount;
+    uint32_t count = mRequests.EntryCount();
 
     nsAutoTArray<nsIRequest*, 8> requests;
 
     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
                            static_cast<nsTArray<nsIRequest*> *>(&requests));
 
     if (requests.Length() != count) {
         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
@@ -296,32 +296,32 @@ nsLoadGroup::Cancel(nsresult status)
         // Remember the first failure and return it...
         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
             firstError = rv;
 
         NS_RELEASE(request);
     }
 
 #if defined(DEBUG)
-    NS_ASSERTION(mRequests.entryCount == 0, "Request list is not empty.");
+    NS_ASSERTION(mRequests.EntryCount() == 0, "Request list is not empty.");
     NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
 #endif
 
     mStatus = NS_OK;
     mIsCanceling = false;
 
     return firstError;
 }
 
 
 NS_IMETHODIMP
 nsLoadGroup::Suspend()
 {
     nsresult rv, firstError;
-    uint32_t count = mRequests.entryCount;
+    uint32_t count = mRequests.EntryCount();
 
     nsAutoTArray<nsIRequest*, 8> requests;
 
     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
                            static_cast<nsTArray<nsIRequest*> *>(&requests));
 
     if (requests.Length() != count) {
         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
@@ -363,17 +363,17 @@ nsLoadGroup::Suspend()
     return firstError;
 }
 
 
 NS_IMETHODIMP
 nsLoadGroup::Resume()
 {
     nsresult rv, firstError;
-    uint32_t count = mRequests.entryCount;
+    uint32_t count = mRequests.EntryCount();
 
     nsAutoTArray<nsIRequest*, 8> requests;
 
     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
                            static_cast<nsTArray<nsIRequest*> *>(&requests));
 
     if (requests.Length() != count) {
         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
@@ -484,17 +484,17 @@ nsLoadGroup::AddRequest(nsIRequest *requ
 {
     nsresult rv;
 
 #if defined(PR_LOGGING)
     {
         nsAutoCString nameStr;
         request->GetName(nameStr);
         LOG(("LOADGROUP [%x]: Adding request %x %s (count=%d).\n",
-             this, request, nameStr.get(), mRequests.entryCount));
+             this, request, nameStr.get(), mRequests.EntryCount()));
     }
 #endif /* PR_LOGGING */
 
 #ifdef DEBUG
     {
       RequestMapEntry *entry =
           static_cast<RequestMapEntry *>
                      (PL_DHashTableOperate(&mRequests, request,
@@ -597,17 +597,17 @@ nsLoadGroup::RemoveRequest(nsIRequest *r
     NS_ENSURE_ARG_POINTER(request);
     nsresult rv;
 
 #if defined(PR_LOGGING)
     {
         nsAutoCString nameStr;
         request->GetName(nameStr);
         LOG(("LOADGROUP [%x]: Removing request %x %s status %x (count=%d).\n",
-            this, request, nameStr.get(), aStatus, mRequests.entryCount-1));
+            this, request, nameStr.get(), aStatus, mRequests.EntryCount() - 1));
     }
 #endif
 
     // Make sure we have a owning reference to the request we're about
     // to remove.
 
     nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
 
@@ -659,17 +659,17 @@ nsLoadGroup::RemoveRequest(nsIRequest *r
                     Telemetry::HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME,
                     mDefaultRequestCreationTime, timeStamp);
             }
 
             TelemetryReportChannel(timedChannel, false);
         }
     }
 
-    if (mRequests.entryCount == 0) {
+    if (mRequests.EntryCount() == 0) {
         TelemetryReport();
     }
 
     // Undo any group priority delta...
     if (mPriority != 0)
         RescheduleRequest(request, -mPriority);
 
     nsLoadFlags flags;
@@ -715,17 +715,17 @@ AppendRequestsToCOMArray(PLDHashTable *t
     static_cast<nsCOMArray<nsIRequest>*>(arg)->AppendObject(e->mKey);
     return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsLoadGroup::GetRequests(nsISimpleEnumerator * *aRequests)
 {
     nsCOMArray<nsIRequest> requests;
-    requests.SetCapacity(mRequests.entryCount);
+    requests.SetCapacity(mRequests.EntryCount());
 
     PL_DHashTableEnumerate(&mRequests, AppendRequestsToCOMArray, &requests);
 
     return NS_NewArrayEnumerator(aRequests, requests);
 }
 
 NS_IMETHODIMP
 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver)
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -37,17 +37,17 @@ enum {
 // the atom table is destroyed.  The structure and value string are allocated
 // as one contiguous block.
 
 struct HttpHeapAtom {
     struct HttpHeapAtom *next;
     char                 value[1];
 };
 
-static struct PLDHashTable  sAtomTable = {0};
+static struct PLDHashTable  sAtomTable;
 static struct HttpHeapAtom *sHeapAtoms = nullptr;
 static Mutex               *sLock = nullptr;
 
 HttpHeapAtom *
 NewHeapAtom(const char *value) {
     int len = strlen(value);
 
     HttpHeapAtom *a =
--- a/parser/htmlparser/nsHTMLEntities.cpp
+++ b/parser/htmlparser/nsHTMLEntities.cpp
@@ -68,18 +68,18 @@ static const PLDHashTableOps UnicodeToEn
   hashUnicodeValue,
   matchNodeUnicode,
   PL_DHashMoveEntryStub,
   PL_DHashClearEntryStub,
   PL_DHashFinalizeStub,
   nullptr,
 };
 
-static PLDHashTable gEntityToUnicode = { 0 };
-static PLDHashTable gUnicodeToEntity = { 0 };
+static PLDHashTable gEntityToUnicode;
+static PLDHashTable gUnicodeToEntity;
 static nsrefcnt gTableRefCnt = 0;
 
 #define HTML_ENTITY(_name, _value) { #_name, _value },
 static const EntityNode gEntityArray[] = {
 #include "nsHTMLEntityList.h"
 };
 #undef HTML_ENTITY
 
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -396,17 +396,18 @@ class Build(MachCommandBase):
         ccache_end = monitor.ccache_stats()
 
         if ccache_start and ccache_end:
             ccache_diff = ccache_end - ccache_start
             if ccache_diff:
                 self.log(logging.INFO, 'ccache',
                          {'msg': ccache_diff.hit_rate_message()}, "{msg}")
 
-        if monitor.elapsed > 300:
+        moz_nospam = os.environ.get('MOZ_NOSPAM')
+        if monitor.elapsed > 300 and not moz_nospam:
             # Display a notification when the build completes.
             # This could probably be uplifted into the mach core or at least
             # into a helper API. It is here as an experimentation to see how it
             # is received.
             try:
                 if sys.platform.startswith('darwin'):
                     try:
                         notifier = which.which('terminal-notifier')
--- a/rdf/base/nsInMemoryDataSource.cpp
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -1311,17 +1311,17 @@ InMemoryDataSource::LockedUnassert(nsIRD
                 if (hdr) {
                     Entry* entry = reinterpret_cast<Entry*>(hdr);
                     entry->mNode = aProperty;
                     entry->mAssertions = next->mNext;
                 }
             }
             else {
                 // If this second-level hash empties out, clean it up.
-                if (!root->u.hash.mPropertyHash->entryCount) {
+                if (!root->u.hash.mPropertyHash->EntryCount()) {
                     root->Release();
                     SetForwardArcs(aSource, nullptr);
                 }
             }
         }
         else {
             prev->mNext = next->mNext;
         }
@@ -1656,17 +1656,17 @@ InMemoryDataSource::ResourceEnumerator(P
     return PL_DHASH_NEXT;
 }
 
 
 NS_IMETHODIMP
 InMemoryDataSource::GetAllResources(nsISimpleEnumerator** aResult)
 {
     nsCOMArray<nsIRDFNode> nodes;
-    nodes.SetCapacity(mForwardArcs.entryCount);
+    nodes.SetCapacity(mForwardArcs.EntryCount());
 
     // Enumerate all of our entries into an nsCOMArray
     PL_DHashTableEnumerate(&mForwardArcs, ResourceEnumerator, &nodes);
 
     return NS_NewArrayEnumerator(aResult, nodes);
 }
 
 NS_IMETHODIMP
@@ -1915,17 +1915,17 @@ InMemoryDataSource::SweepForwardArcsEntr
     Assertion* as = entry->mAssertions;
     if (as && (as->mHashEntry))
     {
         // Stuff in sub-hashes must be swept recursively (max depth: 1)
         PL_DHashTableEnumerate(as->u.hash.mPropertyHash,
                                SweepForwardArcsEntries, info);
 
         // If the sub-hash is now empty, clean it up.
-        if (!as->u.hash.mPropertyHash->entryCount) {
+        if (!as->u.hash.mPropertyHash->EntryCount()) {
             as->Release();
             result = PL_DHASH_REMOVE;
         }
 
         return result;
     }
 
     Assertion* prev = nullptr;
--- a/security/sandbox/chromium/base/shim/base/logging.cpp
+++ b/security/sandbox/chromium/base/shim/base/logging.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "base/logging.h"
+#ifdef OS_WIN
 #include <windows.h>
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
+#endif
 
 namespace {
 int min_log_level = 0;
 }
 
 namespace logging
 {
 
@@ -46,36 +46,46 @@ LogMessage::LogMessage(const char* file,
   line_(line)
 {
 }
 
 LogMessage::~LogMessage()
 {
 }
 
+int GetMinLogLevel()
+{
+  return min_log_level;
+}
+
+int GetVlogLevelHelper(const char* file, size_t N)
+{
+  return 0;
+}
+
+void RawLog(int level, const char* message)
+{
+}
+
+#ifdef OS_WIN
 LogMessage::SaveLastError::SaveLastError() :
   last_error_(::GetLastError())
 {
 }
 
 LogMessage::SaveLastError::~SaveLastError()
 {
   ::SetLastError(last_error_);
 }
 
 SystemErrorCode GetLastSystemErrorCode()
 {
   return ::GetLastError();
 }
 
-int GetMinLogLevel()
-{
-  return min_log_level;
-}
-
 Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, int line,
                                            LogSeverity severity,
                                            SystemErrorCode err,
                                            const char* module) :
   err_(err),
   module_(module),
   log_message_(file, line, severity)
 {
@@ -89,15 +99,11 @@ Win32ErrorLogMessage::Win32ErrorLogMessa
   module_(NULL),
   log_message_(file, line, severity)
 {
 }
 
 Win32ErrorLogMessage::~Win32ErrorLogMessage()
 {
 }
-
-int GetVlogLevelHelper(const char* file, size_t N)
-{
-  return 0;
-}
+#endif // OS_WIN
 
 } // namespace logging
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/Sandbox.h"
+#include "Sandbox.h"
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
 
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/ptrace.h>
 #include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <signal.h>
 #include <string.h>
@@ -19,41 +21,30 @@
 #include <stdlib.h>
 #include <pthread.h>
 #include <errno.h>
 #include <fcntl.h>
 
 #include "mozilla/Atomics.h"
 #include "mozilla/NullPtr.h"
 #include "mozilla/unused.h"
-#include "mozilla/dom/Exceptions.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
-
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"
-#endif
 
 #if defined(ANDROID)
 #include "android_ucontext.h"
-#include <android/log.h>
 #endif
 
 #include "linux_seccomp.h"
 #include "SandboxFilter.h"
 
 // See definition of SandboxDie, below.
 #include "sandbox/linux/seccomp-bpf/die.h"
 
 namespace mozilla {
-#if defined(ANDROID)
-#define LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args)
-#else
-#define LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt "\n", ## args)
-#endif
+
+SandboxCrashFunc gSandboxCrashFunc;
 
 #ifdef MOZ_GMP_SANDBOX
 // For media plugins, we can start the sandbox before we dlopen the
 // module, so we have to pre-open the file and simulate the sandboxed
 // open().
 static int gMediaPluginFileDesc = -1;
 static const char *gMediaPluginFilePath;
 #endif
@@ -72,89 +63,47 @@ struct SandboxFlags {
     if (getenv("MOZ_FAKE_NO_SANDBOX")) {
       isSupported = false;
     } else {
       // Determine whether seccomp-bpf is supported by trying to
       // enable it with an invalid pointer for the filter.  This will
       // fail with EFAULT if supported and EINVAL if not, without
       // changing the process's state.
       if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr) != -1) {
-        MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr) didn't fail");
+        MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr)"
+                  " didn't fail");
       }
       isSupported = errno == EFAULT;
     }
 #ifdef MOZ_CONTENT_SANDBOX
     isDisabledForContent = getenv("MOZ_DISABLE_CONTENT_SANDBOX");
 #endif
 #ifdef MOZ_GMP_SANDBOX
     isDisabledForGMP = getenv("MOZ_DISABLE_GMP_SANDBOX");
 #endif
   }
 };
 
 static const SandboxFlags gSandboxFlags;
 
 /**
- * Log JS stack info in the same place as the sandbox violation
- * message.  Useful in case the responsible code is JS and all we have
- * are logs and a minidump with the C++ stacks (e.g., on TBPL).
- */
-static void
-SandboxLogJSStack(void)
-{
-  if (!NS_IsMainThread()) {
-    // This might be a worker thread... or it might be a non-JS
-    // thread, or a non-NSPR thread.  There's isn't a good API for
-    // dealing with this, yet.
-    return;
-  }
-  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
-  for (int i = 0; frame != nullptr; ++i) {
-    nsAutoString fileName, funName;
-    int32_t lineNumber;
-
-    // Don't stop unwinding if an attribute can't be read.
-    fileName.SetIsVoid(true);
-    unused << frame->GetFilename(fileName);
-    lineNumber = 0;
-    unused << frame->GetLineNumber(&lineNumber);
-    funName.SetIsVoid(true);
-    unused << frame->GetName(funName);
-
-    if (!funName.IsVoid() || !fileName.IsVoid()) {
-      LOG_ERROR("JS frame %d: %s %s line %d", i,
-                funName.IsVoid() ?
-                  "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(),
-                fileName.IsVoid() ?
-                  "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(),
-                lineNumber);
-    }
-
-    nsCOMPtr<nsIStackFrame> nextFrame;
-    nsresult rv = frame->GetCaller(getter_AddRefs(nextFrame));
-    NS_ENSURE_SUCCESS_VOID(rv);
-    frame = nextFrame;
-  }
-}
-
-/**
  * This is the SIGSYS handler function. It is used to report to the user
  * which system call has been denied by Seccomp.
  * This function also makes the process exit as denying the system call
  * will otherwise generally lead to unexpected behavior from the process,
  * since we don't know if all functions will handle such denials gracefully.
  *
  * @see InstallSyscallReporter() function.
  */
 static void
 Reporter(int nr, siginfo_t *info, void *void_context)
 {
   ucontext_t *ctx = static_cast<ucontext_t*>(void_context);
   unsigned long syscall_nr, args[6];
-  pid_t pid = getpid(), tid = syscall(__NR_gettid);
+  pid_t pid = getpid();
 
   if (nr != SIGSYS) {
     return;
   }
   if (info->si_code != SYS_SECCOMP) {
     return;
   }
   if (!ctx) {
@@ -170,52 +119,40 @@ Reporter(int nr, siginfo_t *info, void *
   args[5] = SECCOMP_PARM6(ctx);
 
 #ifdef MOZ_GMP_SANDBOX
   if (syscall_nr == __NR_open && gMediaPluginFilePath) {
     const char *path = reinterpret_cast<const char*>(args[0]);
     int flags = int(args[1]);
 
     if ((flags & O_ACCMODE) != O_RDONLY) {
-      LOG_ERROR("non-read-only open of file %s attempted (flags=0%o)",
-                path, flags);
+      SANDBOX_LOG_ERROR("non-read-only open of file %s attempted (flags=0%o)",
+                        path, flags);
     } else if (strcmp(path, gMediaPluginFilePath) != 0) {
-      LOG_ERROR("attempt to open file %s which is not the media plugin %s",
-                path, gMediaPluginFilePath);
+      SANDBOX_LOG_ERROR("attempt to open file %s which is not the media plugin"
+                        " %s", path, gMediaPluginFilePath);
     } else if (gMediaPluginFileDesc == -1) {
-      LOG_ERROR("multiple opens of media plugin file unimplemented");
+      SANDBOX_LOG_ERROR("multiple opens of media plugin file unimplemented");
     } else {
       SECCOMP_RESULT(ctx) = gMediaPluginFileDesc;
       gMediaPluginFileDesc = -1;
       return;
     }
   }
 #endif
 
-  LOG_ERROR("seccomp sandbox violation: pid %d, syscall %lu, args %lu %lu %lu"
-            " %lu %lu %lu.  Killing process.", pid, syscall_nr,
-            args[0], args[1], args[2], args[3], args[4], args[5]);
+  SANDBOX_LOG_ERROR("seccomp sandbox violation: pid %d, syscall %lu,"
+                    " args %lu %lu %lu %lu %lu %lu.  Killing process.",
+                    pid, syscall_nr,
+                    args[0], args[1], args[2], args[3], args[4], args[5]);
 
-#ifdef MOZ_CRASHREPORTER
   // Bug 1017393: record syscall number somewhere useful.
   info->si_addr = reinterpret_cast<void*>(syscall_nr);
-  bool dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context);
-  if (!dumped) {
-    LOG_ERROR("Failed to write minidump");
-  }
-#endif
 
-  // Do this last, in case it crashes or deadlocks.
-  SandboxLogJSStack();
-
-  // Try to reraise, so the parent sees that this process crashed.
-  // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
-  // also accomplishes that goal.)
-  signal(SIGSYS, SIG_DFL);
-  syscall(__NR_tgkill, pid, tid, nr);
+  gSandboxCrashFunc(nr, info, void_context);
   _exit(127);
 }
 
 /**
  * The reporter is called when the process receives a SIGSYS signal.
  * The signal is sent by the kernel when Seccomp encounter a system call
  * that has not been allowed.
  * We register an action for that signal (calling the Reporter function).
@@ -261,23 +198,23 @@ InstallSyscallReporter(void)
  * Reports failure by crashing.
  *
  * @see sock_fprog (the seccomp_prog).
  */
 static void
 InstallSyscallFilter(const sock_fprog *prog)
 {
   if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
-    LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
+    SANDBOX_LOG_ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %s", strerror(errno));
     MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
   }
 
   if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)prog, 0, 0)) {
-    LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
-              strerror(errno));
+    SANDBOX_LOG_ERROR("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed: %s",
+                      strerror(errno));
     MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
   }
 }
 
 // Use signals for permissions that need to be set per-thread.
 // The communication channel from the signal handler back to the main thread.
 static mozilla::Atomic<int> sSetSandboxDone;
 // Pass the filter itself through a global.
@@ -336,37 +273,37 @@ SetThreadSandboxHandler(int signum)
 static void
 BroadcastSetThreadSandbox(SandboxType aType)
 {
   int signum;
   pid_t pid, tid, myTid;
   DIR *taskdp;
   struct dirent *de;
   SandboxFilter filter(&sSetSandboxFilter, aType,
-                       PR_GetEnv("MOZ_SANDBOX_VERBOSE"));
+                       getenv("MOZ_SANDBOX_VERBOSE"));
 
   static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
                 "mozilla::Atomic<int> isn't represented by an int");
   pid = getpid();
   myTid = syscall(__NR_gettid);
   taskdp = opendir("/proc/self/task");
   if (taskdp == nullptr) {
-    LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
+    SANDBOX_LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno));
     MOZ_CRASH();
   }
   signum = FindFreeSignalNumber();
   if (signum == 0) {
-    LOG_ERROR("No available signal numbers!");
+    SANDBOX_LOG_ERROR("No available signal numbers!");
     MOZ_CRASH();
   }
   void (*oldHandler)(int);
   oldHandler = signal(signum, SetThreadSandboxHandler);
   if (oldHandler != SIG_DFL) {
     // See the comment on FindFreeSignalNumber about race conditions.
-    LOG_ERROR("signal %d in use by handler %p!\n", signum, oldHandler);
+    SANDBOX_LOG_ERROR("signal %d in use by handler %p!\n", signum, oldHandler);
     MOZ_CRASH();
   }
 
   // In case this races with a not-yet-deprivileged thread cloning
   // itself, repeat iterating over all threads until we find none
   // that are still privileged.
   bool sandboxProgress;
   do {
@@ -383,22 +320,22 @@ BroadcastSetThreadSandbox(SandboxType aT
         // Drop this thread's privileges last, below, so we can
         // continue to signal other threads.
         continue;
       }
       // Reset the futex cell and signal.
       sSetSandboxDone = 0;
       if (syscall(__NR_tgkill, pid, tid, signum) != 0) {
         if (errno == ESRCH) {
-          LOG_ERROR("Thread %d unexpectedly exited.", tid);
+          SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
           // Rescan threads, in case it forked before exiting.
           sandboxProgress = true;
           continue;
         }
-        LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
+        SANDBOX_LOG_ERROR("tgkill(%d,%d): %s\n", pid, tid, strerror(errno));
         MOZ_CRASH();
       }
       // It's unlikely, but if the thread somehow manages to exit
       // after receiving the signal but before entering the signal
       // handler, we need to avoid blocking forever.
       //
       // Using futex directly lets the signal handler send the wakeup
       // from an async signal handler (pthread mutex/condvar calls
@@ -415,68 +352,72 @@ BroadcastSetThreadSandbox(SandboxType aT
       clock_gettime(CLOCK_MONOTONIC, &timeLimit);
       timeLimit.tv_sec += crashDelay;
       while (true) {
         static const struct timespec futexTimeout = { 0, 10*1000*1000 }; // 10ms
         // Atomically: if sSetSandboxDone == 0, then sleep.
         if (syscall(__NR_futex, reinterpret_cast<int*>(&sSetSandboxDone),
                   FUTEX_WAIT, 0, &futexTimeout) != 0) {
           if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
-            LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
+            SANDBOX_LOG_ERROR("FUTEX_WAIT: %s\n", strerror(errno));
             MOZ_CRASH();
           }
         }
         // Did the handler finish?
         if (sSetSandboxDone > 0) {
           if (sSetSandboxDone == 2) {
             sandboxProgress = true;
           }
           break;
         }
         // Has the thread ceased to exist?
         if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
           if (errno == ESRCH) {
-            LOG_ERROR("Thread %d unexpectedly exited.", tid);
+            SANDBOX_LOG_ERROR("Thread %d unexpectedly exited.", tid);
           }
           // Rescan threads, in case it forked before exiting.
           // Also, if it somehow failed in a way that wasn't ESRCH,
           // and still exists, that will be handled on the next pass.
           sandboxProgress = true;
           break;
         }
         struct timespec now;
         clock_gettime(CLOCK_MONOTONIC, &now);
         if (now.tv_sec > timeLimit.tv_nsec ||
             (now.tv_sec == timeLimit.tv_nsec &&
              now.tv_nsec > timeLimit.tv_nsec)) {
-          LOG_ERROR("Thread %d unresponsive for %d seconds.  Killing process.",
-                    tid, crashDelay);
+          SANDBOX_LOG_ERROR("Thread %d unresponsive for %d seconds."
+                            "  Killing process.",
+                            tid, crashDelay);
           MOZ_CRASH();
         }
       }
     }
     rewinddir(taskdp);
   } while (sandboxProgress);
   oldHandler = signal(signum, SIG_DFL);
   if (oldHandler != SetThreadSandboxHandler) {
     // See the comment on FindFreeSignalNumber about race conditions.
-    LOG_ERROR("handler for signal %d was changed to %p!", signum, oldHandler);
+    SANDBOX_LOG_ERROR("handler for signal %d was changed to %p!",
+                      signum, oldHandler);
     MOZ_CRASH();
   }
   unused << closedir(taskdp);
   // And now, deprivilege the main thread:
   SetThreadSandbox();
 }
 
 // Common code for sandbox startup.
 static void
 SetCurrentProcessSandbox(SandboxType aType)
 {
+  MOZ_ASSERT(gSandboxCrashFunc);
+
   if (InstallSyscallReporter()) {
-    LOG_ERROR("install_syscall_reporter() failed\n");
+    SANDBOX_LOG_ERROR("install_syscall_reporter() failed\n");
   }
 
   BroadcastSetThreadSandbox(aType);
 }
 
 #ifdef MOZ_CONTENT_SANDBOX
 /**
  * Starts the seccomp sandbox for a content process.  Should be called
@@ -519,55 +460,25 @@ SetMediaPluginSandbox(const char *aFileP
   if (gSandboxFlags.isDisabledForGMP) {
     return;
   }
 
   if (aFilePath) {
     gMediaPluginFilePath = strdup(aFilePath);
     gMediaPluginFileDesc = open(aFilePath, O_RDONLY | O_CLOEXEC);
     if (gMediaPluginFileDesc == -1) {
-      LOG_ERROR("failed to open plugin file %s: %s", aFilePath, strerror(errno));
+      SANDBOX_LOG_ERROR("failed to open plugin file %s: %s",
+                        aFilePath, strerror(errno));
       MOZ_CRASH();
     }
   }
   // Finally, start the sandbox.
   SetCurrentProcessSandbox(kSandboxMediaPlugin);
 }
 
 bool
 CanSandboxMediaPlugin()
 {
   return gSandboxFlags.isSupported || gSandboxFlags.isDisabledForGMP;
 }
 #endif // MOZ_GMP_SANDBOX
 
 } // namespace mozilla
-
-
-// "Polyfill" for sandbox::Die, the real version of which requires
-// Chromium's logging code.
-namespace sandbox {
-
-void
-Die::SandboxDie(const char* msg, const char* file, int line)
-{
-  LOG_ERROR("%s:%d: %s\n", file, line, msg);
-  _exit(127);
-}
-
-} // namespace sandbox
-
-
-// Stubs for unreached logging calls from Chromium CHECK() macro.
-#include "base/logging.h"
-namespace logging {
-
-LogMessage::LogMessage(const char *file, int line, int)
-  : file_(file), line_(line)
-{
-  MOZ_CRASH("Unexpected call to logging::LogMessage::LogMessage");
-}
-
-LogMessage::~LogMessage() {
-  MOZ_CRASH("Unexpected call to logging::LogMessage::~LogMessage");
-}
-
-} // namespace logging
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -2,32 +2,34 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_Sandbox_h
 #define mozilla_Sandbox_h
 
+#include "mozilla/Types.h"
+
 namespace mozilla {
 
 // The Set*Sandbox() functions must not be called if the corresponding
 // CanSandbox*() function has returned false; if sandboxing is
 // attempted, any failure to enable it is fatal.
 //
 // If sandboxing is disabled for a process type with the corresponding
 // environment variable, Set*Sandbox() does nothing and CanSandbox*()
 // returns true.
 
 #ifdef MOZ_CONTENT_SANDBOX
 // Disabled by setting env var MOZ_DISABLE_CONTENT_SANDBOX.
-bool CanSandboxContentProcess();
-void SetContentProcessSandbox();
+MFBT_API bool CanSandboxContentProcess();
+MFBT_API void SetContentProcessSandbox();
 #endif
 #ifdef MOZ_GMP_SANDBOX
 // Disabled by setting env var MOZ_DISABLE_GMP_SANDBOX.
-bool CanSandboxMediaPlugin();
-void SetMediaPluginSandbox(const char *aFilePath);
+MFBT_API bool CanSandboxMediaPlugin();
+MFBT_API void SetMediaPluginSandbox(const char *aFilePath);
 #endif
 
 } // namespace mozilla
 
 #endif // mozilla_Sandbox_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxInternal.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxInternal_h
+#define mozilla_SandboxInternal_h
+
+// The code in Sandbox.cpp can't link against libxul, where
+// SandboxCrash.cpp lives, so it has to use a callback, defined here.
+
+#include <signal.h>
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+typedef void (*SandboxCrashFunc)(int, siginfo_t*, void*);
+extern MFBT_API SandboxCrashFunc gSandboxCrashFunc;
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxInternal_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxLogging.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxLogging_h
+#define mozilla_SandboxLogging_h
+
+#if defined(ANDROID)
+#include <android/log.h>
+#else
+#include <stdio.h>
+#endif
+
+#if defined(ANDROID)
+#define SANDBOX_LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args)
+#else
+#define SANDBOX_LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt "\n", ## args)
+#endif
+
+#endif // mozilla_SandboxLogging_h
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/glue/SandboxCrash.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file needs to be linked into libxul, so it can access the JS
+// stack and the crash reporter.  Everything else in this directory
+// should be able to be linked into its own shared library, in order
+// to be able to isolate sandbox/chromium from ipc/chromium.
+
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "mozilla/NullPtr.h"
+#include "mozilla/unused.h"
+#include "mozilla/dom/Exceptions.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+namespace mozilla {
+
+// Log JS stack info in the same place as the sandbox violation
+// message.  Useful in case the responsible code is JS and all we have
+// are logs and a minidump with the C++ stacks (e.g., on TBPL).
+static void
+SandboxLogJSStack(void)
+{
+  if (!NS_IsMainThread()) {
+    // This might be a worker thread... or it might be a non-JS
+    // thread, or a non-NSPR thread.  There's isn't a good API for
+    // dealing with this, yet.
+    return;
+  }
+  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
+  for (int i = 0; frame != nullptr; ++i) {
+    nsAutoString fileName, funName;
+    int32_t lineNumber;
+
+    // Don't stop unwinding if an attribute can't be read.
+    fileName.SetIsVoid(true);
+    unused << frame->GetFilename(fileName);
+    lineNumber = 0;
+    unused << frame->GetLineNumber(&lineNumber);
+    funName.SetIsVoid(true);
+    unused << frame->GetName(funName);
+
+    if (!funName.IsVoid() || !fileName.IsVoid()) {
+      SANDBOX_LOG_ERROR("JS frame %d: %s %s line %d", i,
+                        funName.IsVoid() ?
+                        "(anonymous)" : NS_ConvertUTF16toUTF8(funName).get(),
+                        fileName.IsVoid() ?
+                        "(no file)" : NS_ConvertUTF16toUTF8(fileName).get(),
+                        lineNumber);
+    }
+
+    nsCOMPtr<nsIStackFrame> nextFrame;
+    nsresult rv = frame->GetCaller(getter_AddRefs(nextFrame));
+    NS_ENSURE_SUCCESS_VOID(rv);
+    frame = nextFrame;
+  }
+}
+
+static void
+SandboxCrash(int nr, siginfo_t *info, void *void_context)
+{
+  pid_t pid = getpid(), tid = syscall(__NR_gettid);
+
+#ifdef MOZ_CRASHREPORTER
+  bool dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context);
+  if (!dumped) {
+    SANDBOX_LOG_ERROR("Failed to write minidump");
+  }
+#endif
+
+  // Do this last, in case it crashes or deadlocks.
+  SandboxLogJSStack();
+
+  // Try to reraise, so the parent sees that this process crashed.
+  // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
+  // also accomplishes that goal.)
+  signal(SIGSYS, SIG_DFL);
+  syscall(__NR_tgkill, pid, tid, nr);
+}
+
+static void __attribute__((constructor))
+SandboxSetCrashFunc()
+{
+  gSandboxCrashFunc = SandboxCrash;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/glue/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FAIL_ON_WARNINGS = True
+
+SOURCES += [
+    'SandboxCrash.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/security/sandbox/linux',
+]
+
+USE_LIBS += [
+    'mozsandbox',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -1,25 +1,44 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 FAIL_ON_WARNINGS = True
 
+LIBRARY_NAME = 'mozsandbox'
+FORCE_SHARED_LIB = True
+
 EXPORTS.mozilla += [
     'Sandbox.h',
 ]
 
 SOURCES += [
+    # Bug 1059038 workaround:
+    '../../../xpcom/glue/unused.cpp',
+    '../chromium/base/shim/base/logging.cpp',
     '../chromium/sandbox/linux/seccomp-bpf/basicblock.cc',
     '../chromium/sandbox/linux/seccomp-bpf/codegen.cc',
+    '../chromium/sandbox/linux/seccomp-bpf/die.cc',
+    '../chromium/sandbox/linux/seccomp-bpf/syscall.cc',
     'Sandbox.cpp',
     'SandboxAssembler.cpp',
     'SandboxFilter.cpp',
 ]
 
+DEFINES['NS_NO_XPCOM'] = True
+DISABLE_STL_WRAPPING = True
+
+LOCAL_INCLUDES += ['/security/sandbox/chromium/base/shim']
 LOCAL_INCLUDES += ['/security/sandbox/chromium']
+LOCAL_INCLUDES += ['/security/sandbox']
 
-include('/ipc/chromium/chromium-config.mozbuild')
+if CONFIG['OS_TARGET'] != 'Android':
+    # Needed for clock_gettime with glibc < 2.17:
+    OS_LIBS += [
+        'rt',
+    ]
 
-FINAL_LIBRARY = 'xul'
+DIRS += [
+    'glue',
+]
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1380,17 +1380,17 @@ static PLDHashOperator
 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
                    void *arg)
 {
   return PL_DHASH_REMOVE;
 }
 
 void nsDocLoader::ClearRequestInfoHash(void)
 {
-  if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
+  if (!mRequestInfoHash.ops || !mRequestInfoHash.EntryCount()) {
     // No hash, or the hash is empty, nothing to do here then...
 
     return;
   }
 
   PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nullptr);
 }
 
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -873,17 +873,17 @@ public:
 #endif
 
   PtrInfo* FindNode(void* aPtr);
   PtrToNodeEntry* AddNodeToMap(void* aPtr);
   void RemoveNodeFromMap(void* aPtr);
 
   uint32_t MapCount() const
   {
-    return mPtrToNodeMap.entryCount;
+    return mPtrToNodeMap.EntryCount();
   }
 
   void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                            size_t* aNodesSize, size_t* aEdgesSize,
                            size_t* aWeakMapsSize) const
   {
     *aNodesSize = mNodes.SizeOfExcludingThis(aMallocSizeOf);
     *aEdgesSize = mEdges.SizeOfExcludingThis(aMallocSizeOf);
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -338,23 +338,22 @@ NS_PurgeAtomTable()
   delete gStaticAtomTable;
 
   if (gAtomTable.ops) {
 #ifdef DEBUG
     const char* dumpAtomLeaks = PR_GetEnv("MOZ_DUMP_ATOM_LEAKS");
     if (dumpAtomLeaks && *dumpAtomLeaks) {
       uint32_t leaked = 0;
       printf("*** %d atoms still exist (including permanent):\n",
-             gAtomTable.entryCount);
+             gAtomTable.EntryCount());
       PL_DHashTableEnumerate(&gAtomTable, DumpAtomLeaks, &leaked);
       printf("*** %u non-permanent atoms leaked\n", leaked);
     }
 #endif
     PL_DHashTableFinish(&gAtomTable);
-    gAtomTable.entryCount = 0;
     gAtomTable.ops = nullptr;
   }
 }
 
 AtomImpl::AtomImpl(const nsAString& aString, uint32_t aHash)
 {
   mLength = aString.Length();
   nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
@@ -401,19 +400,19 @@ AtomImpl::~AtomImpl()
 {
   NS_PRECONDITION(gAtomTable.ops, "uninitialized atom hashtable");
   // Permanent atoms are removed from the hashtable at shutdown, and we
   // don't want to remove them twice.  See comment above in
   // |AtomTableClearEntry|.
   if (!IsPermanentInDestructor()) {
     AtomTableKey key(mString, mLength, mHash);
     PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_REMOVE);
-    if (gAtomTable.entryCount == 0) {
+    if (gAtomTable.ops && gAtomTable.EntryCount() == 0) {
       PL_DHashTableFinish(&gAtomTable);
-      NS_ASSERTION(gAtomTable.entryCount == 0,
+      NS_ASSERTION(gAtomTable.EntryCount() == 0,
                    "PL_DHashTableFinish changed the entry count");
     }
   }
 
   nsStringBuffer::FromData(mString)->Release();
 }
 
 NS_IMPL_ISUPPORTS(AtomImpl, nsIAtom)
@@ -553,31 +552,31 @@ static inline AtomTableEntry*
 GetAtomHashEntry(const char* aString, uint32_t aLength, uint32_t* aHashOut)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHashOut);
   AtomTableEntry* e = static_cast<AtomTableEntry*>(
     PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   if (!e) {
-    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
+    NS_ABORT_OOM(gAtomTable.EntryCount() * gAtomTable.EntrySize());
   }
   return e;
 }
 
 static inline AtomTableEntry*
 GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t* aHashOut)
 {
   MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
   EnsureTableExists();
   AtomTableKey key(aString, aLength, aHashOut);
   AtomTableEntry* e = static_cast<AtomTableEntry*>(
     PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
   if (!e) {
-    NS_ABORT_OOM(gAtomTable.entryCount * gAtomTable.entrySize);
+    NS_ABORT_OOM(gAtomTable.EntryCount() * gAtomTable.EntrySize());
   }
   return e;
 }
 
 class CheckStaticAtomSizes
 {
   CheckStaticAtomSizes()
   {
@@ -714,17 +713,17 @@ NS_NewPermanentAtom(const nsAString& aUT
 
   // No need to addref since permanent atoms aren't refcounted anyway
   return atom;
 }
 
 nsrefcnt
 NS_GetNumberOfAtoms(void)
 {
-  return gAtomTable.entryCount;
+  return gAtomTable.EntryCount();
 }
 
 nsIAtom*
 NS_GetStaticAtom(const nsAString& aUTF16String)
 {
   NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
   NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
   StaticAtomEntry* entry = gStaticAtomTable->GetEntry(aUTF16String);
--- a/xpcom/ds/nsPersistentProperties.cpp
+++ b/xpcom/ds/nsPersistentProperties.cpp
@@ -602,21 +602,21 @@ AddElemToArray(PLDHashTable* aTable, PLD
 
 
 NS_IMETHODIMP
 nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
 {
   nsCOMArray<nsIPropertyElement> props;
 
   // We know the necessary size; we can avoid growing it while adding elements
-  props.SetCapacity(mTable.entryCount);
+  props.SetCapacity(mTable.EntryCount());
 
   // Step through hash entries populating a transient array
   uint32_t n = PL_DHashTableEnumerate(&mTable, AddElemToArray, (void*)&props);
-  if (n < mTable.entryCount) {
+  if (n < mTable.EntryCount()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_NewArrayEnumerator(aResult, props);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XXX Some day we'll unify the nsIPersistentProperties interface with
--- a/xpcom/ds/nsStaticNameTable.cpp
+++ b/xpcom/ds/nsStaticNameTable.cpp
@@ -111,17 +111,17 @@ nsStaticCaseInsensitiveNameTable::nsStat
   MOZ_COUNT_CTOR(nsStaticCaseInsensitiveNameTable);
   mNameTable.ops = nullptr;
 }
 
 nsStaticCaseInsensitiveNameTable::~nsStaticCaseInsensitiveNameTable()
 {
   if (mNameArray) {
     // manually call the destructor on placement-new'ed objects
-    for (uint32_t index = 0; index < mNameTable.entryCount; index++) {
+    for (uint32_t index = 0; index < mNameTable.EntryCount(); index++) {
       mNameArray[index].~nsDependentCString();
     }
     nsMemory::Free((void*)mNameArray);
   }
   if (mNameTable.ops) {
     PL_DHashTableFinish(&mNameTable);
   }
   MOZ_COUNT_DTOR(nsStaticCaseInsensitiveNameTable);
@@ -226,13 +226,13 @@ nsStaticCaseInsensitiveNameTable::Lookup
 }
 
 const nsAFlatCString&
 nsStaticCaseInsensitiveNameTable::GetStringValue(int32_t aIndex)
 {
   NS_ASSERTION(mNameArray, "not inited");
   NS_ASSERTION(mNameTable.ops, "not inited");
 
-  if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.entryCount)) {
+  if ((NOT_FOUND < aIndex) && ((uint32_t)aIndex < mNameTable.EntryCount())) {
     return mNameArray[aIndex];
   }
   return mNullStr;
 }
--- a/xpcom/glue/nsBaseHashtable.h
+++ b/xpcom/glue/nsBaseHashtable.h
@@ -119,17 +119,17 @@ public:
    * put a new value for the associated key
    * @param aKey the key to put
    * @param aData the new data
    * @return always true, unless memory allocation failed
    */
   void Put(KeyType aKey, const UserDataType& aData)
   {
     if (!Put(aKey, aData, fallible_t())) {
-      NS_ABORT_OOM(this->mTable.entrySize * this->mTable.entryCount);
+      NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
     }
   }
 
   NS_WARN_UNUSED_RESULT bool Put(KeyType aKey, const UserDataType& aData,
                                  const fallible_t&)
   {
     EntryType* ent = this->PutEntry(aKey);
     if (!ent) {
--- a/xpcom/glue/nsRefPtrHashtable.h
+++ b/xpcom/glue/nsRefPtrHashtable.h
@@ -143,17 +143,17 @@ nsRefPtrHashtable<KeyClass, RefPtr>::Get
 }
 
 template<class KeyClass, class RefPtr>
 void
 nsRefPtrHashtable<KeyClass, RefPtr>::Put(KeyType aKey,
                                          already_AddRefed<RefPtr> aData)
 {
   if (!Put(aKey, mozilla::Move(aData), mozilla::fallible_t())) {
-    NS_ABORT_OOM(this->mTable.entrySize * this->mTable.entryCount);
+    NS_ABORT_OOM(this->mTable.EntrySize() * this->mTable.EntryCount());
   }
 }
 
 template<class KeyClass, class RefPtr>
 bool
 nsRefPtrHashtable<KeyClass, RefPtr>::Put(KeyType aKey,
                                          already_AddRefed<RefPtr> aData,
                                          const mozilla::fallible_t&)
--- a/xpcom/glue/nsTHashtable.h
+++ b/xpcom/glue/nsTHashtable.h
@@ -94,33 +94,33 @@ public:
   ~nsTHashtable();
 
   nsTHashtable(nsTHashtable<EntryType>&& aOther);
 
   /**
    * Return the generation number for the table. This increments whenever
    * the table data items are moved.
    */
-  uint32_t GetGeneration() const { return mTable.generation; }
+  uint32_t GetGeneration() const { return mTable.Generation(); }
 
   /**
    * KeyType is typedef'ed for ease of use.
    */
   typedef typename EntryType::KeyType KeyType;
 
   /**
    * KeyTypePointer is typedef'ed for ease of use.
    */
   typedef typename EntryType::KeyTypePointer KeyTypePointer;
 
   /**
    * Return the number of entries in the table.
    * @return    number of entries
    */
-  uint32_t Count() const { return mTable.entryCount; }
+  uint32_t Count() const { return mTable.EntryCount(); }
 
   /**
    * Get the entry associated with a key.
    * @param     aKey the key to retrieve
    * @return    pointer to the entry class, if the key exists; nullptr if the
    *            key doesn't exist
    */
   EntryType* GetEntry(KeyType aKey) const
@@ -145,17 +145,17 @@ public:
    * @param     aKey the key to retrieve
    * @return    pointer to the entry class retreived; nullptr only if memory
                 can't be allocated
    */
   EntryType* PutEntry(KeyType aKey)
   {
     EntryType* e = PutEntry(aKey, fallible_t());
     if (!e) {
-      NS_ABORT_OOM(mTable.entrySize * mTable.entryCount);
+      NS_ABORT_OOM(mTable.EntrySize() * mTable.EntryCount());
     }
     return e;
   }
 
   EntryType* PutEntry(KeyType aKey, const fallible_t&) NS_WARN_UNUSED_RESULT {
     NS_ASSERTION(mTable.ops, "nsTHashtable was not initialized properly.");
 
     return static_cast<EntryType*>(PL_DHashTableOperate(
--- a/xpcom/glue/pldhash.cpp
+++ b/xpcom/glue/pldhash.cpp
@@ -110,37 +110,56 @@ PL_DHashMatchStringKey(PLDHashTable* aTa
   const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
 
   /* XXX tolerate null keys on account of sloppy Mozilla callers. */
   return stub->key == aKey ||
          (stub->key && aKey &&
           strcmp((const char*)stub->key, (const char*)aKey) == 0);
 }
 
+MOZ_ALWAYS_INLINE void
+PLDHashTable::MoveEntryStub(const PLDHashEntryHdr* aFrom,
+                            PLDHashEntryHdr* aTo)
+{
+  memcpy(aTo, aFrom, entrySize);
+}
+
 void
 PL_DHashMoveEntryStub(PLDHashTable* aTable,
                       const PLDHashEntryHdr* aFrom,
                       PLDHashEntryHdr* aTo)
 {
-  memcpy(aTo, aFrom, aTable->entrySize);
+  aTable->MoveEntryStub(aFrom, aTo);
+}
+
+MOZ_ALWAYS_INLINE void
+PLDHashTable::ClearEntryStub(PLDHashEntryHdr* aEntry)
+{
+  memset(aEntry, 0, entrySize);
 }
 
 void
 PL_DHashClearEntryStub(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
-  memset(aEntry, 0, aTable->entrySize);
+  aTable->ClearEntryStub(aEntry);
+}
+
+MOZ_ALWAYS_INLINE void
+PLDHashTable::FreeStringKey(PLDHashEntryHdr* aEntry)
+{
+  const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
+
+  free((void*)stub->key);
+  memset(aEntry, 0, entrySize);
 }
 
 void
 PL_DHashFreeStringKey(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
-  const PLDHashEntryStub* stub = (const PLDHashEntryStub*)aEntry;
-
-  free((void*)stub->key);
-  memset(aEntry, 0, aTable->entrySize);
+  aTable->FreeStringKey(aEntry);
 }
 
 void
 PL_DHashFinalizeStub(PLDHashTable* aTable)
 {
 }
 
 static const PLDHashTableOps stub_ops = {
@@ -215,72 +234,79 @@ MinLoad(uint32_t aCapacity)
 }
 
 static inline uint32_t
 MinCapacity(uint32_t aLength)
 {
   return (aLength * 4 + (3 - 1)) / 3;   // == ceil(aLength * 4 / 3)
 }
 
-bool
-PL_DHashTableInit(PLDHashTable* aTable, const PLDHashTableOps* aOps,
-                  void* aData, uint32_t aEntrySize, const fallible_t&,
-                  uint32_t aLength)
+MOZ_ALWAYS_INLINE bool
+PLDHashTable::Init(const PLDHashTableOps* aOps, void* aData,
+                   uint32_t aEntrySize, const fallible_t&, uint32_t aLength)
 {
 #ifdef DEBUG
   if (aEntrySize > 16 * sizeof(void*)) {
     printf_stderr(
-      "pldhash: for the aTable at address %p, the given aEntrySize"
+      "pldhash: for the table at address %p, the given aEntrySize"
       " of %lu definitely favors chaining over double hashing.\n",
-      (void*)aTable,
+      (void*)this,
       (unsigned long) aEntrySize);
   }
 #endif
 
   if (aLength > PL_DHASH_MAX_INITIAL_LENGTH) {
     return false;
   }
 
-  aTable->ops = aOps;
-  aTable->data = aData;
+  ops = aOps;
+  data = aData;
 
   // Compute the smallest capacity allowing |aLength| elements to be inserted
   // without rehashing.
   uint32_t capacity = MinCapacity(aLength);
   if (capacity < PL_DHASH_MIN_CAPACITY) {
     capacity = PL_DHASH_MIN_CAPACITY;
   }
 
   int log2 = CeilingLog2(capacity);
 
   capacity = 1u << log2;
   MOZ_ASSERT(capacity <= PL_DHASH_MAX_CAPACITY);
-  aTable->hashShift = PL_DHASH_BITS - log2;
-  aTable->entrySize = aEntrySize;
-  aTable->entryCount = aTable->removedCount = 0;
-  aTable->generation = 0;
+  hashShift = PL_DHASH_BITS - log2;
+  entrySize = aEntrySize;
+  entryCount = removedCount = 0;
+  generation = 0;
   uint32_t nbytes;
   if (!SizeOfEntryStore(capacity, aEntrySize, &nbytes)) {
     return false;  // overflowed
   }
 
-  aTable->entryStore = (char*)aOps->allocTable(aTable, nbytes);
-  if (!aTable->entryStore) {
+  entryStore = (char*)aOps->allocTable(this, nbytes);
+  if (!entryStore) {
     return false;
   }
-  memset(aTable->entryStore, 0, nbytes);
-  METER(memset(&aTable->stats, 0, sizeof(aTable->stats)));
+  memset(entryStore, 0, nbytes);
+  METER(memset(&stats, 0, sizeof(stats)));
 
 #ifdef DEBUG
-  aTable->recursionLevel = 0;
+  recursionLevel = 0;
 #endif
 
   return true;
 }
 
+bool
+PL_DHashTableInit(PLDHashTable* aTable, const PLDHashTableOps* aOps,
+                  void* aData, uint32_t aEntrySize,
+                  const fallible_t& aFallible, uint32_t aLength)
+{
+  return aTable->Init(aOps, aData, aEntrySize, aFallible, aLength);
+}
+
 void
 PL_DHashTableInit(PLDHashTable* aTable, const PLDHashTableOps* aOps,
                   void* aData, uint32_t aEntrySize, uint32_t aLength)
 {
   if (!PL_DHashTableInit(aTable, aOps, aData, aEntrySize, fallible_t(),
                          aLength)) {
     if (aLength > PL_DHASH_MAX_INITIAL_LENGTH) {
       MOZ_CRASH();          // the asked-for length was too big
@@ -302,93 +328,93 @@ PL_DHashTableInit(PLDHashTable* aTable, 
 
 /*
  * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels.  Note
  * that a removed-entry sentinel need be stored only if the removed entry had
  * a colliding entry added after it.  Therefore we can use 1 as the collision
  * flag in addition to the removed-entry sentinel value.  Multiplicative hash
  * uses the high order bits of keyHash, so this least-significant reservation
  * should not hurt the hash function's effectiveness much.
- *
- * If you change any of these magic numbers, also update PL_DHASH_ENTRY_IS_LIVE
- * in pldhash.h.  It used to be private to pldhash.c, but then became public to
- * assist iterator writers who inspect table->entryStore directly.
  */
 #define COLLISION_FLAG              ((PLDHashNumber) 1)
 #define MARK_ENTRY_FREE(entry)      ((entry)->keyHash = 0)
 #define MARK_ENTRY_REMOVED(entry)   ((entry)->keyHash = 1)
 #define ENTRY_IS_REMOVED(entry)     ((entry)->keyHash == 1)
-#define ENTRY_IS_LIVE(entry)        PL_DHASH_ENTRY_IS_LIVE(entry)
+#define ENTRY_IS_LIVE(entry)        ((entry)->keyHash >= 2)
 #define ENSURE_LIVE_KEYHASH(hash0)  if (hash0 < 2) hash0 -= 2; else (void)0
 
 /* Match an entry's keyHash against an unstored one computed from a key. */
 #define MATCH_ENTRY_KEYHASH(entry,hash0) \
     (((entry)->keyHash & ~COLLISION_FLAG) == (hash0))
 
 /* Compute the address of the indexed entry in table. */
 #define ADDRESS_ENTRY(table, index) \
     ((PLDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))
 
-void
-PL_DHashTableFinish(PLDHashTable* aTable)
+MOZ_ALWAYS_INLINE void
+PLDHashTable::Finish()
 {
-  INCREMENT_RECURSION_LEVEL(aTable);
+  INCREMENT_RECURSION_LEVEL(this);
 
   /* Call finalize before clearing entries, so it can enumerate them. */
-  aTable->ops->finalize(aTable);
+  ops->finalize(this);
 
   /* Clear any remaining live entries. */
-  char* entryAddr = aTable->entryStore;
-  uint32_t entrySize = aTable->entrySize;
-  char* entryLimit = entryAddr + PL_DHASH_TABLE_CAPACITY(aTable) * entrySize;
+  char* entryAddr = entryStore;
+  char* entryLimit = entryAddr + Capacity() * entrySize;
   while (entryAddr < entryLimit) {
     PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
     if (ENTRY_IS_LIVE(entry)) {
-      METER(aTable->stats.removeEnums++);
-      aTable->ops->clearEntry(aTable, entry);
+      METER(stats.removeEnums++);
+      ops->clearEntry(this, entry);
     }
     entryAddr += entrySize;
   }
 
-  DECREMENT_RECURSION_LEVEL(aTable);
-  MOZ_ASSERT(RECURSION_LEVEL_SAFE_TO_FINISH(aTable));
+  DECREMENT_RECURSION_LEVEL(this);
+  MOZ_ASSERT(RECURSION_LEVEL_SAFE_TO_FINISH(this));
 
   /* Free entry storage last. */
-  aTable->ops->freeTable(aTable, aTable->entryStore);
+  ops->freeTable(this, entryStore);
 }
 
-static PLDHashEntryHdr* PL_DHASH_FASTCALL
-SearchTable(PLDHashTable* aTable, const void* aKey, PLDHashNumber aKeyHash,
-            PLDHashOperator aOp)
+void
+PL_DHashTableFinish(PLDHashTable* aTable)
 {
-  METER(aTable->stats.searches++);
+  aTable->Finish();
+}
+
+PLDHashEntryHdr* PL_DHASH_FASTCALL
+PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash,
+                          PLDHashOperator aOp)
+{
+  METER(stats.searches++);
   NS_ASSERTION(!(aKeyHash & COLLISION_FLAG),
                "!(aKeyHash & COLLISION_FLAG)");
 
   /* Compute the primary hash address. */
-  int hashShift = aTable->hashShift;
   PLDHashNumber hash1 = HASH1(aKeyHash, hashShift);
-  PLDHashEntryHdr* entry = ADDRESS_ENTRY(aTable, hash1);
+  PLDHashEntryHdr* entry = ADDRESS_ENTRY(this, hash1);
 
   /* Miss: return space for a new entry. */
   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
-    METER(aTable->stats.misses++);
+    METER(stats.misses++);
     return entry;
   }
 
   /* Hit: return entry. */
-  PLDHashMatchEntry matchEntry = aTable->ops->matchEntry;
+  PLDHashMatchEntry matchEntry = ops->matchEntry;
   if (MATCH_ENTRY_KEYHASH(entry, aKeyHash) &&
-      matchEntry(aTable, entry, aKey)) {
-    METER(aTable->stats.hits++);
+      matchEntry(this, entry, aKey)) {
+    METER(stats.hits++);
     return entry;
   }
 
   /* Collision: double hash. */
-  int sizeLog2 = PL_DHASH_BITS - aTable->hashShift;
+  int sizeLog2 = PL_DHASH_BITS - hashShift;
   PLDHashNumber hash2 = HASH2(aKeyHash, sizeLog2, hashShift);
   uint32_t sizeMask = (1u << sizeLog2) - 1;
 
   /* Save the first removed entry pointer so PL_DHASH_ADD can recycle it. */
   PLDHashEntryHdr* firstRemoved = nullptr;
 
   for (;;) {
     if (MOZ_UNLIKELY(ENTRY_IS_REMOVED(entry))) {
@@ -396,29 +422,29 @@ SearchTable(PLDHashTable* aTable, const 
         firstRemoved = entry;
       }
     } else {
       if (aOp == PL_DHASH_ADD) {
         entry->keyHash |= COLLISION_FLAG;
       }
     }
 
-    METER(aTable->stats.steps++);
+    METER(stats.steps++);
     hash1 -= hash2;
     hash1 &= sizeMask;
 
-    entry = ADDRESS_ENTRY(aTable, hash1);
+    entry = ADDRESS_ENTRY(this, hash1);
     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
-      METER(aTable->stats.misses++);
+      METER(stats.misses++);
       return (firstRemoved && aOp == PL_DHASH_ADD) ? firstRemoved : entry;
     }
 
     if (MATCH_ENTRY_KEYHASH(entry, aKeyHash) &&
-        matchEntry(aTable, entry, aKey)) {
-      METER(aTable->stats.hits++);
+        matchEntry(this, entry, aKey)) {
+      METER(stats.hits++);
       return entry;
     }
   }
 
   /* NOTREACHED */
   return nullptr;
 }
 
@@ -427,261 +453,268 @@ SearchTable(PLDHashTable* aTable, const 
  *   1. assume |aOp == PL_DHASH_ADD|,
  *   2. assume that |aKey| will never match an existing entry, and
  *   3. assume that no entries have been removed from the current table
  *      structure.
  * Avoiding the need for |aKey| means we can avoid needing a way to map
  * entries to keys, which means callers can use complex key types more
  * easily.
  */
-static PLDHashEntryHdr* PL_DHASH_FASTCALL
-FindFreeEntry(PLDHashTable* aTable, PLDHashNumber aKeyHash)
+PLDHashEntryHdr* PL_DHASH_FASTCALL
+PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
 {
-  METER(aTable->stats.searches++);
+  METER(stats.searches++);
   NS_ASSERTION(!(aKeyHash & COLLISION_FLAG),
                "!(aKeyHash & COLLISION_FLAG)");
 
   /* Compute the primary hash address. */
-  int hashShift = aTable->hashShift;
   PLDHashNumber hash1 = HASH1(aKeyHash, hashShift);
-  PLDHashEntryHdr* entry = ADDRESS_ENTRY(aTable, hash1);
+  PLDHashEntryHdr* entry = ADDRESS_ENTRY(this, hash1);
 
   /* Miss: return space for a new entry. */
   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
-    METER(aTable->stats.misses++);
+    METER(stats.misses++);
     return entry;
   }
 
   /* Collision: double hash. */
-  int sizeLog2 = PL_DHASH_BITS - aTable->hashShift;
+  int sizeLog2 = PL_DHASH_BITS - hashShift;
   PLDHashNumber hash2 = HASH2(aKeyHash, sizeLog2, hashShift);
   uint32_t sizeMask = (1u << sizeLog2) - 1;
 
   for (;;) {
     NS_ASSERTION(!ENTRY_IS_REMOVED(entry),
                  "!ENTRY_IS_REMOVED(entry)");
     entry->keyHash |= COLLISION_FLAG;
 
-    METER(aTable->stats.steps++);
+    METER(stats.steps++);
     hash1 -= hash2;
     hash1 &= sizeMask;
 
-    entry = ADDRESS_ENTRY(aTable, hash1);
+    entry = ADDRESS_ENTRY(this, hash1);
     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
-      METER(aTable->stats.misses++);
+      METER(stats.misses++);
       return entry;
     }
   }
 
   /* NOTREACHED */
   return nullptr;
 }
 
-static bool
-ChangeTable(PLDHashTable* aTable, int aDeltaLog2)
+bool
+PLDHashTable::ChangeTable(int aDeltaLog2)
 {
   /* Look, but don't touch, until we succeed in getting new entry store. */
-  int oldLog2 = PL_DHASH_BITS - aTable->hashShift;
+  int oldLog2 = PL_DHASH_BITS - hashShift;
   int newLog2 = oldLog2 + aDeltaLog2;
   uint32_t newCapacity = 1u << newLog2;
   if (newCapacity > PL_DHASH_MAX_CAPACITY) {
     return false;
   }
 
-  uint32_t entrySize = aTable->entrySize;
   uint32_t nbytes;
   if (!SizeOfEntryStore(newCapacity, entrySize, &nbytes)) {
     return false;   // overflowed
   }
 
-  char* newEntryStore = (char*)aTable->ops->allocTable(aTable, nbytes);
+  char* newEntryStore = (char*)ops->allocTable(this, nbytes);
   if (!newEntryStore) {
     return false;
   }
 
   /* We can't fail from here on, so update table parameters. */
 #ifdef DEBUG
-  uint32_t recursionLevel = aTable->recursionLevel;
+  uint32_t recursionLevelTmp = recursionLevel;
 #endif
-  aTable->hashShift = PL_DHASH_BITS - newLog2;
-  aTable->removedCount = 0;
-  aTable->generation++;
+  hashShift = PL_DHASH_BITS - newLog2;
+  removedCount = 0;
+  generation++;
 
   /* Assign the new entry store to table. */
   memset(newEntryStore, 0, nbytes);
   char* oldEntryStore;
   char* oldEntryAddr;
-  oldEntryAddr = oldEntryStore = aTable->entryStore;
-  aTable->entryStore = newEntryStore;
-  PLDHashMoveEntry moveEntry = aTable->ops->moveEntry;
+  oldEntryAddr = oldEntryStore = entryStore;
+  entryStore = newEntryStore;
+  PLDHashMoveEntry moveEntry = ops->moveEntry;
 #ifdef DEBUG
-  aTable->recursionLevel = recursionLevel;
+  recursionLevel = recursionLevelTmp;
 #endif
 
   /* Copy only live entries, leaving removed ones behind. */
   uint32_t oldCapacity = 1u << oldLog2;
   for (uint32_t i = 0; i < oldCapacity; ++i) {
     PLDHashEntryHdr* oldEntry = (PLDHashEntryHdr*)oldEntryAddr;
     if (ENTRY_IS_LIVE(oldEntry)) {
       oldEntry->keyHash &= ~COLLISION_FLAG;
-      PLDHashEntryHdr* newEntry = FindFreeEntry(aTable, oldEntry->keyHash);
+      PLDHashEntryHdr* newEntry = FindFreeEntry(oldEntry->keyHash);
       NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(newEntry),
                    "PL_DHASH_ENTRY_IS_FREE(newEntry)");
-      moveEntry(aTable, oldEntry, newEntry);
+      moveEntry(this, oldEntry, newEntry);
       newEntry->keyHash = oldEntry->keyHash;
     }
     oldEntryAddr += entrySize;
   }
 
-  aTable->ops->freeTable(aTable, oldEntryStore);
+  ops->freeTable(this, oldEntryStore);
   return true;
 }
 
-PLDHashEntryHdr* PL_DHASH_FASTCALL
-PL_DHashTableOperate(PLDHashTable* aTable, const void* aKey, PLDHashOperator aOp)
+MOZ_ALWAYS_INLINE PLDHashEntryHdr*
+PLDHashTable::Operate(const void* aKey, PLDHashOperator aOp)
 {
   PLDHashEntryHdr* entry;
 
-  MOZ_ASSERT(aOp == PL_DHASH_LOOKUP || aTable->recursionLevel == 0);
-  INCREMENT_RECURSION_LEVEL(aTable);
+  MOZ_ASSERT(aOp == PL_DHASH_LOOKUP || recursionLevel == 0);
+  INCREMENT_RECURSION_LEVEL(this);
 
-  PLDHashNumber keyHash = aTable->ops->hashKey(aTable, aKey);
+  PLDHashNumber keyHash = ops->hashKey(this, aKey);
   keyHash *= PL_DHASH_GOLDEN_RATIO;
 
   /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */
   ENSURE_LIVE_KEYHASH(keyHash);
   keyHash &= ~COLLISION_FLAG;
 
   switch (aOp) {
     case PL_DHASH_LOOKUP:
-      METER(aTable->stats.lookups++);
-      entry = SearchTable(aTable, aKey, keyHash, aOp);
+      METER(stats.lookups++);
+      entry = SearchTable(aKey, keyHash, aOp);
       break;
 
     case PL_DHASH_ADD: {
       /*
        * If alpha is >= .75, grow or compress the table.  If aKey is already
        * in the table, we may grow once more than necessary, but only if we
        * are on the edge of being overloaded.
        */
-      uint32_t capacity = PL_DHASH_TABLE_CAPACITY(aTable);
-      if (aTable->entryCount + aTable->removedCount >= MaxLoad(capacity)) {
+      uint32_t capacity = Capacity();
+      if (entryCount + removedCount >= MaxLoad(capacity)) {
         /* Compress if a quarter or more of all entries are removed. */
         int deltaLog2;
-        if (aTable->removedCount >= capacity >> 2) {
-          METER(aTable->stats.compresses++);
+        if (removedCount >= capacity >> 2) {
+          METER(stats.compresses++);
           deltaLog2 = 0;
         } else {
-          METER(aTable->stats.grows++);
+          METER(stats.grows++);
           deltaLog2 = 1;
         }
 
         /*
-         * Grow or compress aTable.  If ChangeTable() fails, allow
+         * Grow or compress the table.  If ChangeTable() fails, allow
          * overloading up to the secondary max.  Once we hit the secondary
          * max, return null.
          */
-        if (!ChangeTable(aTable, deltaLog2) &&
-            aTable->entryCount + aTable->removedCount >=
+        if (!ChangeTable(deltaLog2) &&
+            entryCount + removedCount >=
             MaxLoadOnGrowthFailure(capacity)) {
-          METER(aTable->stats.addFailures++);
+          METER(stats.addFailures++);
           entry = nullptr;
           break;
         }
       }
 
       /*
        * Look for entry after possibly growing, so we don't have to add it,
        * then skip it while growing the table and re-add it after.
        */
-      entry = SearchTable(aTable, aKey, keyHash, aOp);
+      entry = SearchTable(aKey, keyHash, aOp);
       if (!ENTRY_IS_LIVE(entry)) {
         /* Initialize the entry, indicating that it's no longer free. */
-        METER(aTable->stats.addMisses++);
+        METER(stats.addMisses++);
         if (ENTRY_IS_REMOVED(entry)) {
-          METER(aTable->stats.addOverRemoved++);
-          aTable->removedCount--;
+          METER(stats.addOverRemoved++);
+          removedCount--;
           keyHash |= COLLISION_FLAG;
         }
-        if (aTable->ops->initEntry &&
-            !aTable->ops->initEntry(aTable, entry, aKey)) {
+        if (ops->initEntry && !ops->initEntry(this, entry, aKey)) {
           /* We haven't claimed entry yet; fail with null return. */
-          memset(entry + 1, 0, aTable->entrySize - sizeof(*entry));
+          memset(entry + 1, 0, entrySize - sizeof(*entry));
           entry = nullptr;
           break;
         }
         entry->keyHash = keyHash;
-        aTable->entryCount++;
+        entryCount++;
       }
       METER(else {
-        aTable->stats.addHits++;
+        stats.addHits++;
       });
       break;
     }
 
     case PL_DHASH_REMOVE:
-      entry = SearchTable(aTable, aKey, keyHash, aOp);
+      entry = SearchTable(aKey, keyHash, aOp);
       if (ENTRY_IS_LIVE(entry)) {
         /* Clear this entry and mark it as "removed". */
-        METER(aTable->stats.removeHits++);
-        PL_DHashTableRawRemove(aTable, entry);
+        METER(stats.removeHits++);
+        PL_DHashTableRawRemove(this, entry);
 
-        /* Shrink if alpha is <= .25 and aTable isn't too small already. */
-        uint32_t capacity = PL_DHASH_TABLE_CAPACITY(aTable);
+        /* Shrink if alpha is <= .25 and the table isn't too small already. */
+        uint32_t capacity = Capacity();
         if (capacity > PL_DHASH_MIN_CAPACITY &&
-            aTable->entryCount <= MinLoad(capacity)) {
-          METER(aTable->stats.shrinks++);
-          (void) ChangeTable(aTable, -1);
+            entryCount <= MinLoad(capacity)) {
+          METER(stats.shrinks++);
+          (void) ChangeTable(-1);
         }
       }
       METER(else {
-        aTable->stats.removeMisses++;
+        stats.removeMisses++;
       });
       entry = nullptr;
       break;
 
     default:
       NS_NOTREACHED("0");
       entry = nullptr;
   }
 
-  DECREMENT_RECURSION_LEVEL(aTable);
+  DECREMENT_RECURSION_LEVEL(this);
 
   return entry;
 }
 
+PLDHashEntryHdr* PL_DHASH_FASTCALL
+PL_DHashTableOperate(PLDHashTable* aTable, const void* aKey, PLDHashOperator aOp)
+{
+  return aTable->Operate(aKey, aOp);
+}
+
+MOZ_ALWAYS_INLINE void
+PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
+{
+  MOZ_ASSERT(recursionLevel != IMMUTABLE_RECURSION_LEVEL);
+
+  NS_ASSERTION(ENTRY_IS_LIVE(aEntry), "ENTRY_IS_LIVE(aEntry)");
+
+  /* Load keyHash first in case clearEntry() goofs it. */
+  PLDHashNumber keyHash = aEntry->keyHash;
+  ops->clearEntry(this, aEntry);
+  if (keyHash & COLLISION_FLAG) {
+    MARK_ENTRY_REMOVED(aEntry);
+    removedCount++;
+  } else {
+    METER(stats.removeFrees++);
+    MARK_ENTRY_FREE(aEntry);
+  }
+  entryCount--;
+}
+
 void
 PL_DHashTableRawRemove(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
-  MOZ_ASSERT(aTable->recursionLevel != IMMUTABLE_RECURSION_LEVEL);
-
-  NS_ASSERTION(PL_DHASH_ENTRY_IS_LIVE(aEntry),
-               "PL_DHASH_ENTRY_IS_LIVE(aEntry)");
-
-  /* Load keyHash first in case clearEntry() goofs it. */
-  PLDHashNumber keyHash = aEntry->keyHash;
-  aTable->ops->clearEntry(aTable, aEntry);
-  if (keyHash & COLLISION_FLAG) {
-    MARK_ENTRY_REMOVED(aEntry);
-    aTable->removedCount++;
-  } else {
-    METER(aTable->stats.removeFrees++);
-    MARK_ENTRY_FREE(aEntry);
-  }
-  aTable->entryCount--;
+  aTable->RawRemove(aEntry);
 }
 
-uint32_t
-PL_DHashTableEnumerate(PLDHashTable* aTable, PLDHashEnumerator aEtor, void* aArg)
+MOZ_ALWAYS_INLINE uint32_t
+PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
 {
-  INCREMENT_RECURSION_LEVEL(aTable);
+  INCREMENT_RECURSION_LEVEL(this);
 
-  char* entryAddr = aTable->entryStore;
-  uint32_t entrySize = aTable->entrySize;
-  uint32_t capacity = PL_DHASH_TABLE_CAPACITY(aTable);
+  char* entryAddr = entryStore;
+  uint32_t capacity = Capacity();
   uint32_t tableSize = capacity * entrySize;
   char* entryLimit = entryAddr + tableSize;
   uint32_t i = 0;
   bool didRemove = false;
 
   if (ChaosMode::isActive()) {
     // Start iterating at a random point in the hashtable. It would be
     // even more chaotic to iterate in fully random order, but that's a lot
@@ -690,63 +723,70 @@ PL_DHashTableEnumerate(PLDHashTable* aTa
     if (entryAddr >= entryLimit) {
       entryAddr -= tableSize;
     }
   }
 
   for (uint32_t e = 0; e < capacity; ++e) {
     PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
     if (ENTRY_IS_LIVE(entry)) {
-      PLDHashOperator op = aEtor(aTable, entry, i++, aArg);
+      PLDHashOperator op = aEtor(this, entry, i++, aArg);
       if (op & PL_DHASH_REMOVE) {
-        METER(aTable->stats.removeEnums++);
-        PL_DHashTableRawRemove(aTable, entry);
+        METER(stats.removeEnums++);
+        PL_DHashTableRawRemove(this, entry);
         didRemove = true;
       }
       if (op & PL_DHASH_STOP) {
         break;
       }
     }
     entryAddr += entrySize;
     if (entryAddr >= entryLimit) {
       entryAddr -= tableSize;
     }
   }
 
-  MOZ_ASSERT(!didRemove || aTable->recursionLevel == 1);
+  MOZ_ASSERT(!didRemove || recursionLevel == 1);
 
   /*
    * Shrink or compress if a quarter or more of all entries are removed, or
    * if the table is underloaded according to the minimum alpha, and is not
    * minimal-size already.  Do this only if we removed above, so non-removing
-   * enumerations can count on stable aTable->entryStore until the next
+   * enumerations can count on stable |entryStore| until the next
    * non-lookup-Operate or removing-Enumerate.
    */
   if (didRemove &&
-      (aTable->removedCount >= capacity >> 2 ||
+      (removedCount >= capacity >> 2 ||
        (capacity > PL_DHASH_MIN_CAPACITY &&
-        aTable->entryCount <= MinLoad(capacity)))) {
-    METER(aTable->stats.enumShrinks++);
-    capacity = aTable->entryCount;
+        entryCount <= MinLoad(capacity)))) {
+    METER(stats.enumShrinks++);
+    capacity = entryCount;
     capacity += capacity >> 1;
     if (capacity < PL_DHASH_MIN_CAPACITY) {
       capacity = PL_DHASH_MIN_CAPACITY;
     }
 
     uint32_t ceiling = CeilingLog2(capacity);
-    ceiling -= PL_DHASH_BITS - aTable->hashShift;
+    ceiling -= PL_DHASH_BITS - hashShift;
 
-    (void) ChangeTable(aTable, ceiling);
+    (void) ChangeTable(ceiling);
   }
 
-  DECREMENT_RECURSION_LEVEL(aTable);
+  DECREMENT_RECURSION_LEVEL(this);
 
   return i;
 }
 
+uint32_t
+PL_DHashTableEnumerate(PLDHashTable* aTable, PLDHashEnumerator aEtor,
+                       void* aArg)
+{
+  return aTable->Enumerate(aEtor, aArg);
+}
+
 struct SizeOfEntryExcludingThisArg
 {
   size_t total;
   PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis;
   MallocSizeOf mallocSizeOf;
   void* arg;  // the arg passed by the user
 };
 
@@ -754,155 +794,180 @@ static PLDHashOperator
 SizeOfEntryExcludingThisEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
                                    uint32_t aNumber, void* aArg)
 {
   SizeOfEntryExcludingThisArg* e = (SizeOfEntryExcludingThisArg*)aArg;
   e->total += e->sizeOfEntryExcludingThis(aHdr, e->mallocSizeOf, e->arg);
   return PL_DHASH_NEXT;
 }
 
+MOZ_ALWAYS_INLINE size_t
+PLDHashTable::SizeOfExcludingThis(
+    PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
+    MallocSizeOf aMallocSizeOf, void* aArg /* = nullptr */) const
+{
+  size_t n = 0;
+  n += aMallocSizeOf(entryStore);
+  if (aSizeOfEntryExcludingThis) {
+    SizeOfEntryExcludingThisArg arg2 = {
+      0, aSizeOfEntryExcludingThis, aMallocSizeOf, aArg
+    };
+    PL_DHashTableEnumerate(const_cast<PLDHashTable*>(this),
+                           SizeOfEntryExcludingThisEnumerator, &arg2);
+    n += arg2.total;
+  }
+  return n;
+}
+
+MOZ_ALWAYS_INLINE size_t
+PLDHashTable::SizeOfIncludingThis(
+    PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
+    MallocSizeOf aMallocSizeOf, void* aArg /* = nullptr */) const
+{
+  return aMallocSizeOf(this) +
+         SizeOfExcludingThis(aSizeOfEntryExcludingThis, aMallocSizeOf, aArg);
+}
+
 size_t
 PL_DHashTableSizeOfExcludingThis(
     const PLDHashTable* aTable,
     PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
     MallocSizeOf aMallocSizeOf, void* aArg /* = nullptr */)
 {
-  size_t n = 0;
-  n += aMallocSizeOf(aTable->entryStore);
-  if (aSizeOfEntryExcludingThis) {
-    SizeOfEntryExcludingThisArg arg2 = {
-      0, aSizeOfEntryExcludingThis, aMallocSizeOf, aArg
-    };
-    PL_DHashTableEnumerate(const_cast<PLDHashTable*>(aTable),
-                           SizeOfEntryExcludingThisEnumerator, &arg2);
-    n += arg2.total;
-  }
-  return n;
+  return aTable->SizeOfExcludingThis(aSizeOfEntryExcludingThis,
+                                     aMallocSizeOf, aArg);
 }
 
 size_t
 PL_DHashTableSizeOfIncludingThis(
     const PLDHashTable* aTable,
     PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
     MallocSizeOf aMallocSizeOf, void* aArg /* = nullptr */)
 {
-  return aMallocSizeOf(aTable) +
-         PL_DHashTableSizeOfExcludingThis(aTable, aSizeOfEntryExcludingThis,
-                                          aMallocSizeOf, aArg);
+  return aTable->SizeOfIncludingThis(aSizeOfEntryExcludingThis,
+                                     aMallocSizeOf, aArg);
 }
 
 #ifdef DEBUG
+MOZ_ALWAYS_INLINE void
+PLDHashTable::MarkImmutable()
+{
+  recursionLevel = IMMUTABLE_RECURSION_LEVEL;
+}
+
 void
 PL_DHashMarkTableImmutable(PLDHashTable* aTable)
 {
-  aTable->recursionLevel = IMMUTABLE_RECURSION_LEVEL;
+  aTable->MarkImmutable();
 }
 #endif
 
 #ifdef PL_DHASHMETER
 #include <math.h>
 
 void
-PL_DHashTableDumpMeter(PLDHashTable* aTable, PLDHashEnumerator aDump, FILE* aFp)
+PLDHashTable::DumpMeter(PLDHashEnumerator aDump, FILE* aFp)
 {
   PLDHashNumber hash1, hash2, maxChainHash1, maxChainHash2;
   double sqsum, mean, variance, sigma;
   PLDHashEntryHdr* entry;
 
-  char* entryAddr = aTable->entryStore;
-  uint32_t entrySize = aTable->entrySize;
-  int hashShift = aTable->hashShift;
+  char* entryAddr = entryStore;
   int sizeLog2 = PL_DHASH_BITS - hashShift;
-  uint32_t capacity = PL_DHASH_TABLE_CAPACITY(aTable);
+  uint32_t capacity = Capacity();
   uint32_t sizeMask = (1u << sizeLog2) - 1;
   uint32_t chainCount = 0, maxChainLen = 0;
   hash2 = 0;
   sqsum = 0;
 
   for (uint32_t i = 0; i < capacity; i++) {
     entry = (PLDHashEntryHdr*)entryAddr;
     entryAddr += entrySize;
     if (!ENTRY_IS_LIVE(entry)) {
       continue;
     }
     hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift);
     PLDHashNumber saveHash1 = hash1;
-    PLDHashEntryHdr* probe = ADDRESS_ENTRY(aTable, hash1);
+    PLDHashEntryHdr* probe = ADDRESS_ENTRY(this, hash1);
     uint32_t chainLen = 1;
     if (probe == entry) {
       /* Start of a (possibly unit-length) chain. */
       chainCount++;
     } else {
       hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2,
                     hashShift);
       do {
         chainLen++;
         hash1 -= hash2;
         hash1 &= sizeMask;
-        probe = ADDRESS_ENTRY(aTable, hash1);
+        probe = ADDRESS_ENTRY(this, hash1);
       } while (probe != entry);
     }
     sqsum += chainLen * chainLen;
     if (chainLen > maxChainLen) {
       maxChainLen = chainLen;
       maxChainHash1 = saveHash1;
       maxChainHash2 = hash2;
     }
   }
 
-  uint32_t entryCount = aTable->entryCount;
   if (entryCount && chainCount) {
     mean = (double)entryCount / chainCount;
     variance = chainCount * sqsum - entryCount * entryCount;
     if (variance < 0 || chainCount == 1) {
       variance = 0;
     } else {
       variance /= chainCount * (chainCount - 1);
     }
     sigma = sqrt(variance);
   } else {
     mean = sigma = 0;
   }
 
   fprintf(aFp, "Double hashing statistics:\n");
   fprintf(aFp, "    table size (in entries): %u\n", tableSize);
-  fprintf(aFp, "          number of entries: %u\n", aTable->entryCount);
-  fprintf(aFp, "  number of removed entries: %u\n", aTable->removedCount);
-  fprintf(aFp, "         number of searches: %u\n", aTable->stats.searches);
-  fprintf(aFp, "             number of hits: %u\n", aTable->stats.hits);
-  fprintf(aFp, "           number of misses: %u\n", aTable->stats.misses);
+  fprintf(aFp, "          number of entries: %u\n", entryCount);
+  fprintf(aFp, "  number of removed entries: %u\n", removedCount);
+  fprintf(aFp, "         number of searches: %u\n", stats.searches);
+  fprintf(aFp, "             number of hits: %u\n", stats.hits);
+  fprintf(aFp, "           number of misses: %u\n", stats.misses);
   fprintf(aFp, "      mean steps per search: %g\n",
-          aTable->stats.searches ?
-            (double)aTable->stats.steps / aTable->stats.searches : 0.);
+          stats.searches ? (double)stats.steps / stats.searches : 0.);
   fprintf(aFp, "     mean hash chain length: %g\n", mean);
   fprintf(aFp, "         standard deviation: %g\n", sigma);
   fprintf(aFp, "  maximum hash chain length: %u\n", maxChainLen);
-  fprintf(aFp, "          number of lookups: %u\n", aTable->stats.lookups);
-  fprintf(aFp, " adds that made a new entry: %u\n", aTable->stats.addMisses);
-  fprintf(aFp, "adds that recycled removeds: %u\n", aTable->stats.addOverRemoved);
-  fprintf(aFp, "   adds that found an entry: %u\n", aTable->stats.addHits);
-  fprintf(aFp, "               add failures: %u\n", aTable->stats.addFailures);
-  fprintf(aFp, "             useful removes: %u\n", aTable->stats.removeHits);
-  fprintf(aFp, "            useless removes: %u\n", aTable->stats.removeMisses);
-  fprintf(aFp, "removes that freed an entry: %u\n", aTable->stats.removeFrees);
-  fprintf(aFp, "  removes while enumerating: %u\n", aTable->stats.removeEnums);
-  fprintf(aFp, "            number of grows: %u\n", aTable->stats.grows);
-  fprintf(aFp, "          number of shrinks: %u\n", aTable->stats.shrinks);
-  fprintf(aFp, "       number of compresses: %u\n", aTable->stats.compresses);
-  fprintf(aFp, "number of enumerate shrinks: %u\n", aTable->stats.enumShrinks);
+  fprintf(aFp, "          number of lookups: %u\n", stats.lookups);
+  fprintf(aFp, " adds that made a new entry: %u\n", stats.addMisses);
+  fprintf(aFp, "adds that recycled removeds: %u\n", stats.addOverRemoved);
+  fprintf(aFp, "   adds that found an entry: %u\n", stats.addHits);
+  fprintf(aFp, "               add failures: %u\n", stats.addFailures);
+  fprintf(aFp, "             useful removes: %u\n", stats.removeHits);
+  fprintf(aFp, "            useless removes: %u\n", stats.removeMisses);
+  fprintf(aFp, "removes that freed an entry: %u\n", stats.removeFrees);
+  fprintf(aFp, "  removes while enumerating: %u\n", stats.removeEnums);
+  fprintf(aFp, "            number of grows: %u\n", stats.grows);
+  fprintf(aFp, "          number of shrinks: %u\n", stats.shrinks);
+  fprintf(aFp, "       number of compresses: %u\n", stats.compresses);
+  fprintf(aFp, "number of enumerate shrinks: %u\n", stats.enumShrinks);
 
   if (aDump && maxChainLen && hash2) {
     fputs("Maximum hash chain:\n", aFp);
     hash1 = maxChainHash1;
     hash2 = maxChainHash2;
-    entry = ADDRESS_ENTRY(aTable, hash1);
+    entry = ADDRESS_ENTRY(this, hash1);
     uint32_t i = 0;
     do {
-      if (aDump(aTable, entry, i++, aFp) != PL_DHASH_NEXT) {
+      if (aDump(this, entry, i++, aFp) != PL_DHASH_NEXT) {
         break;
       }
       hash1 -= hash2;
       hash1 &= sizeMask;
-      entry = ADDRESS_ENTRY(aTable, hash1);
+      entry = ADDRESS_ENTRY(this, hash1);
     } while (PL_DHASH_ENTRY_IS_BUSY(entry));
   }
 }
+
+void
+PL_DHashTableDumpMeter(PLDHashTable* aTable, PLDHashEnumerator aDump, FILE* aFp)
+{
+  aTable->DumpMeter(aDump, aFp);
+}
 #endif /* PL_DHASHMETER */
--- a/xpcom/glue/pldhash.h
+++ b/xpcom/glue/pldhash.h
@@ -70,21 +70,16 @@ typedef struct PLDHashTableOps  PLDHashT
  * Each hash table sub-type should nest the PLDHashEntryHdr structure at the
  * front of its particular entry type.  The keyHash member contains the result
  * of multiplying the hash code returned from the hashKey callback (see below)
  * by PL_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0
  * and 1 values.  The stored keyHash value is table size invariant, and it is
  * maintained automatically by PL_DHashTableOperate -- users should never set
  * it, and its only uses should be via the entry macros below.
  *
- * The PL_DHASH_ENTRY_IS_LIVE function tests whether entry is neither free nor
- * removed.  An entry may be either busy or free; if busy, it may be live or
- * removed.  Consumers of this API should not access members of entries that
- * are not live.
- *
  * However, use PL_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries
  * returned by PL_DHashTableOperate, as PL_DHashTableOperate never returns a
  * non-live, busy (i.e., removed) entry pointer to its caller.  See below for
  * more details on PL_DHashTableOperate's calling rules.
  */
 struct PLDHashEntryHdr
 {
   PLDHashNumber keyHash;  /* every entry must begin like this */
@@ -97,21 +92,76 @@ PL_DHASH_ENTRY_IS_FREE(PLDHashEntryHdr* 
 }
 
 MOZ_ALWAYS_INLINE bool
 PL_DHASH_ENTRY_IS_BUSY(PLDHashEntryHdr* aEntry)
 {
   return !PL_DHASH_ENTRY_IS_FREE(aEntry);
 }
 
-MOZ_ALWAYS_INLINE bool
-PL_DHASH_ENTRY_IS_LIVE(PLDHashEntryHdr* aEntry)
+/*
+ * To consolidate keyHash computation and table grow/shrink code, we use a
+ * single entry point for lookup, add, and remove operations.  The operation
+ * codes are declared here, along with codes returned by PLDHashEnumerator
+ * functions, which control PL_DHashTableEnumerate's behavior.
+ */
+typedef enum PLDHashOperator
 {
-  return aEntry->keyHash >= 2;
-}
+  PL_DHASH_LOOKUP = 0,        /* lookup entry */
+  PL_DHASH_ADD = 1,           /* add entry */
+  PL_DHASH_REMOVE = 2,        /* remove entry, or enumerator says remove */
+  PL_DHASH_NEXT = 0,          /* enumerator says continue */
+  PL_DHASH_STOP = 1           /* enumerator says stop */
+} PLDHashOperator;
+
+/*
+ * Enumerate entries in table using etor:
+ *
+ *   count = PL_DHashTableEnumerate(table, etor, arg);
+ *
+ * PL_DHashTableEnumerate calls etor like so:
+ *
+ *   op = etor(table, entry, number, arg);
+ *
+ * where number is a zero-based ordinal assigned to live entries according to
+ * their order in aTable->entryStore.
+ *
+ * The return value, op, is treated as a set of flags.  If op is PL_DHASH_NEXT,
+ * then continue enumerating.  If op contains PL_DHASH_REMOVE, then clear (via
+ * aTable->ops->clearEntry) and free entry.  Then we check whether op contains
+ * PL_DHASH_STOP; if so, stop enumerating and return the number of live entries
+ * that were enumerated so far.  Return the total number of live entries when
+ * enumeration completes normally.
+ *
+ * If etor calls PL_DHashTableOperate on table with op != PL_DHASH_LOOKUP, it
+ * must return PL_DHASH_STOP; otherwise undefined behavior results.
+ *
+ * If any enumerator returns PL_DHASH_REMOVE, aTable->entryStore may be shrunk
+ * or compressed after enumeration, but before PL_DHashTableEnumerate returns.
+ * Such an enumerator therefore can't safely set aside entry pointers, but an
+ * enumerator that never returns PL_DHASH_REMOVE can set pointers to entries
+ * aside, e.g., to avoid copying live entries into an array of the entry type.
+ * Copying entry pointers is cheaper, and safe so long as the caller of such a
+ * "stable" Enumerate doesn't use the set-aside pointers after any call either
+ * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might
+ * grow or shrink entryStore.
+ *
+ * If your enumerator wants to remove certain entries, but set aside pointers
+ * to other entries that it retains, it can use PL_DHashTableRawRemove on the
+ * entries to be removed, returning PL_DHASH_NEXT to skip them.  Likewise, if
+ * you want to remove entries, but for some reason you do not want entryStore
+ * to be shrunk or compressed, you can call PL_DHashTableRawRemove safely on
+ * the entry being enumerated, rather than returning PL_DHASH_REMOVE.
+ */
+typedef PLDHashOperator (*PLDHashEnumerator)(PLDHashTable* aTable,
+                                             PLDHashEntryHdr* aHdr,
+                                             uint32_t aNumber, void* aArg);
+
+typedef size_t (*PLDHashSizeOfEntryExcludingThisFun)(
+  PLDHashEntryHdr* aHdr, mozilla::MallocSizeOf aMallocSizeOf, void* aArg);
 
 /*
  * A PLDHashTable is currently 8 words (without the PL_DHASHMETER overhead)
  * on most architectures, and may be allocated on the stack or within another
  * structure or class (see below for the Init and Finish functions to use).
  *
  * To decide whether to use double hashing vs. chaining, we need to develop a
  * trade-off relation, as follows:
@@ -177,31 +227,39 @@ PL_DHASH_ENTRY_IS_LIVE(PLDHashEntryHdr* 
  * only if aTable->generation has not changed.
  *
  * The moral of this story: there is no one-size-fits-all hash table scheme,
  * but for small table entry size, and assuming entry address stability is not
  * required, double hashing wins.
  */
 struct PLDHashTable
 {
-  const PLDHashTableOps* ops;         /* virtual operations, see below */
+  /*
+   * Virtual operations; see below. This field is public because it's commonly
+   * zeroed to indicate that a table is no longer live.
+   */
+  const PLDHashTableOps* ops;
+
   void*               data;           /* ops- and instance-specific data */
+
+private:
   int16_t             hashShift;      /* multiplicative hash shift */
   /*
    * |recursionLevel| is only used in debug builds, but is present in opt
    * builds to avoid binary compatibility problems when mixing DEBUG and
    * non-DEBUG components.  (Actually, even if it were removed,
    * sizeof(PLDHashTable) wouldn't change, due to struct padding.)
    */
   uint16_t            recursionLevel; /* used to detect unsafe re-entry */
   uint32_t            entrySize;      /* number of bytes in an entry */
   uint32_t            entryCount;     /* number of entries in table */
   uint32_t            removedCount;   /* removed entry sentinels in table */
   uint32_t            generation;     /* entry storage generation number */
   char*               entryStore;     /* entry storage */
+
 #ifdef PL_DHASHMETER
   struct PLDHashStats
   {
     uint32_t        searches;       /* total number of table searches */
     uint32_t        steps;          /* hash chain links traversed */
     uint32_t        hits;           /* searches that found key */
     uint32_t        misses;         /* searches that didn't find key */
     uint32_t        lookups;        /* number of PL_DHASH_LOOKUPs */
@@ -214,25 +272,73 @@ struct PLDHashTable
     uint32_t        removeFrees;    /* removes that freed entry directly */
     uint32_t        removeEnums;    /* removes done by Enumerate */
     uint32_t        grows;          /* table expansions */
     uint32_t        shrinks;        /* table contractions */
     uint32_t        compresses;     /* table compressions */
     uint32_t        enumShrinks;    /* contractions after Enumerate */
   } stats;
 #endif
-};
+
+public:
+  /*
+   * Size in entries (gross, not net of free and removed sentinels) for table.
+   * We store hashShift rather than sizeLog2 to optimize the collision-free case
+   * in SearchTable.
+   */
+  uint32_t Capacity() const
+  {
+    return ((uint32_t)1 << (PL_DHASH_BITS - hashShift));
+  }
+
+  uint32_t EntrySize()  const { return entrySize; }
+  uint32_t EntryCount() const { return entryCount; }
+  uint32_t Generation() const { return generation; }
+
+  bool Init(const PLDHashTableOps* aOps, void* aData, uint32_t aEntrySize,
+            const mozilla::fallible_t&, uint32_t aLength);
+
+  void Finish();
+
+  PLDHashEntryHdr* Operate(const void* aKey, PLDHashOperator aOp);
+
+  void RawRemove(PLDHashEntryHdr* aEntry);
+
+  uint32_t Enumerate(PLDHashEnumerator aEtor, void* aArg);
 
-/*
- * Size in entries (gross, not net of free and removed sentinels) for table.
- * We store hashShift rather than sizeLog2 to optimize the collision-free case
- * in SearchTable.
- */
-#define PL_DHASH_TABLE_CAPACITY(table) \
-    ((uint32_t)1 << (PL_DHASH_BITS - (table)->hashShift))
+  size_t SizeOfIncludingThis(
+    PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
+    mozilla::MallocSizeOf aMallocSizeOf, void* aArg = nullptr) const;
+
+  size_t SizeOfExcludingThis(
+    PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
+    mozilla::MallocSizeOf aMallocSizeOf, void* aArg = nullptr) const;
+
+#ifdef DEBUG
+  void MarkImmutable();
+#endif
+
+  void MoveEntryStub(const PLDHashEntryHdr* aFrom, PLDHashEntryHdr* aTo);
+
+  void ClearEntryStub(PLDHashEntryHdr* aEntry);
+
+  void FreeStringKey(PLDHashEntryHdr* aEntry);
+
+#ifdef PL_DHASHMETER
+  void DumpMeter(PLDHashEnumerator aDump, FILE* aFp);
+#endif
+
+private:
+  PLDHashEntryHdr* PL_DHASH_FASTCALL
+    SearchTable(const void* aKey, PLDHashNumber aKeyHash, PLDHashOperator aOp);
+
+  PLDHashEntryHdr* PL_DHASH_FASTCALL FindFreeEntry(PLDHashNumber aKeyHash);
+
+  bool ChangeTable(int aDeltaLog2);
+};
 
 /*
  * Table space at entryStore is allocated and freed using these callbacks.
  * The allocator should return null on error only (not if called with aNBytes
  * equal to 0; but note that pldhash.c code will never call with 0 aNBytes).
  */
 typedef void* (*PLDHashAllocTable)(PLDHashTable* aTable, uint32_t aNBytes);
 
@@ -420,31 +526,16 @@ MOZ_WARN_UNUSED_RESULT NS_COM_GLUE bool 
  * Finalize aTable's data, free its entry storage using aTable->ops->freeTable,
  * and leave its members unchanged from their last live values (which leaves
  * pointers dangling).  If you want to burn cycles clearing aTable, it's up to
  * your code to call memset.
  */
 NS_COM_GLUE void PL_DHashTableFinish(PLDHashTable* aTable);
 
 /*
- * To consolidate keyHash computation and table grow/shrink code, we use a
- * single entry point for lookup, add, and remove operations.  The operation
- * codes are declared here, along with codes returned by PLDHashEnumerator
- * functions, which control PL_DHashTableEnumerate's behavior.
- */
-typedef enum PLDHashOperator
-{
-  PL_DHASH_LOOKUP = 0,        /* lookup entry */
-  PL_DHASH_ADD = 1,           /* add entry */
-  PL_DHASH_REMOVE = 2,        /* remove entry, or enumerator says remove */
-  PL_DHASH_NEXT = 0,          /* enumerator says continue */
-  PL_DHASH_STOP = 1           /* enumerator says stop */
-} PLDHashOperator;
-
-/*
  * To lookup a key in table, call:
  *
  *  entry = PL_DHashTableOperate(table, key, PL_DHASH_LOOKUP);
  *
  * If PL_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies
  * entry.  If PL_DHASH_ENTRY_IS_FREE(entry) is true, key was not found.
  *
  * To add an entry identified by key to table, call:
@@ -480,66 +571,20 @@ PL_DHashTableOperate(PLDHashTable* aTabl
  * the inefficiency of a full PL_DHashTableOperate (which rehashes in order
  * to find the entry given its key) is not tolerable.  This function does not
  * shrink the table if it is underloaded.  It does not update stats #ifdef
  * PL_DHASHMETER, either.
  */
 NS_COM_GLUE void PL_DHashTableRawRemove(PLDHashTable* aTable,
                                         PLDHashEntryHdr* aEntry);
 
-/*
- * Enumerate entries in table using etor:
- *
- *   count = PL_DHashTableEnumerate(table, etor, arg);
- *
- * PL_DHashTableEnumerate calls etor like so:
- *
- *   op = etor(table, entry, number, arg);
- *
- * where number is a zero-based ordinal assigned to live entries according to
- * their order in aTable->entryStore.
- *
- * The return value, op, is treated as a set of flags.  If op is PL_DHASH_NEXT,
- * then continue enumerating.  If op contains PL_DHASH_REMOVE, then clear (via
- * aTable->ops->clearEntry) and free entry.  Then we check whether op contains
- * PL_DHASH_STOP; if so, stop enumerating and return the number of live entries
- * that were enumerated so far.  Return the total number of live entries when
- * enumeration completes normally.
- *
- * If etor calls PL_DHashTableOperate on table with op != PL_DHASH_LOOKUP, it
- * must return PL_DHASH_STOP; otherwise undefined behavior results.
- *
- * If any enumerator returns PL_DHASH_REMOVE, aTable->entryStore may be shrunk
- * or compressed after enumeration, but before PL_DHashTableEnumerate returns.
- * Such an enumerator therefore can't safely set aside entry pointers, but an
- * enumerator that never returns PL_DHASH_REMOVE can set pointers to entries
- * aside, e.g., to avoid copying live entries into an array of the entry type.
- * Copying entry pointers is cheaper, and safe so long as the caller of such a
- * "stable" Enumerate doesn't use the set-aside pointers after any call either
- * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might
- * grow or shrink entryStore.
- *
- * If your enumerator wants to remove certain entries, but set aside pointers
- * to other entries that it retains, it can use PL_DHashTableRawRemove on the
- * entries to be removed, returning PL_DHASH_NEXT to skip them.  Likewise, if
- * you want to remove entries, but for some reason you do not want entryStore
- * to be shrunk or compressed, you can call PL_DHashTableRawRemove safely on
- * the entry being enumerated, rather than returning PL_DHASH_REMOVE.
- */
-typedef PLDHashOperator (*PLDHashEnumerator)(PLDHashTable* aTable,
-                                             PLDHashEntryHdr* aHdr,
-                                             uint32_t aNumber, void* aArg);
-
 NS_COM_GLUE uint32_t
 PL_DHashTableEnumerate(PLDHashTable* aTable, PLDHashEnumerator aEtor,
                        void* aArg);
 
-typedef size_t (*PLDHashSizeOfEntryExcludingThisFun)(
-  PLDHashEntryHdr* aHdr, mozilla::MallocSizeOf aMallocSizeOf, void* aArg);
-
 /**
  * Measure the size of the table's entry storage, and if
  * |aSizeOfEntryExcludingThis| is non-nullptr, measure the size of things
  * pointed to by entries.  Doesn't measure |ops| because it's often shared
  * between tables, nor |data| because it's opaque.
  */
 NS_COM_GLUE size_t PL_DHashTableSizeOfExcludingThis(
   const PLDHashTable* aTable,