Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 02 Dec 2013 16:39:00 -0500
changeset 158379 492fbc8095b807132f96446fcc6a8db62faf9ff8
parent 158378 f9d8f53e8739d89bbbcf91564cf267c22d094f1e (current diff)
parent 158315 044c28763a8d3fe3871b1d6cfadf67a62343b326 (diff)
child 158404 c93cfe704487cf493fc4f289e015f068e49c4644
push id36978
push userryanvm@gmail.com
push dateMon, 02 Dec 2013 21:58:15 +0000
treeherdermozilla-inbound@612b104050ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.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.
CLOBBER
gfx/layers/opengl/CompositorOGL.cpp
modules/libpref/src/init/all.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 915533 - need clobber as source files are relocated
+More Windows webidl changes
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -701,19 +701,25 @@
 
             onLocationChange: function (aWebProgress, aRequest, aLocation,
                                         aFlags) {
               // OnLocationChange is called for both the top-level content
               // and the subframes.
               let topLevel = aWebProgress.isTopLevel;
 
               if (topLevel) {
-                // The document loaded correctly, clear the value if we should
+                // If userTypedClear > 0, the document loaded correctly and we should be
+                // clearing the user typed value. We also need to clear the typed value
+                // if the document failed to load, to make sure the urlbar reflects the
+                // failed URI (particularly for SSL errors). However, don't clear the value
+                // if the error page's URI is about:blank, because that causes complete
+                // loss of urlbar contents for invalid URI errors (see bug 867957).
                 if (this.mBrowser.userTypedClear > 0 ||
-                    (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE))
+                    ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
+                     aLocation.spec != "about:blank"))
                   this.mBrowser.userTypedValue = null;
 
                 // Clear out the missing plugins list since it's related to the
                 // previous location.
                 this.mBrowser.missingPlugins = null;
 
                 if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
                   let findBar = this.mTabBrowser.getFindBar(this.mTab);
--- a/config/system-headers
+++ b/config/system-headers
@@ -1126,8 +1126,9 @@ unicode/uclean.h
 unicode/ucol.h
 unicode/udat.h
 unicode/udatpg.h
 unicode/uenum.h
 unicode/unum.h
 unicode/ustring.h
 unicode/utypes.h
 #endif
+libutil.h
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/942979.html
@@ -0,0 +1,42 @@
+<html>
+<head></head>
+<body>
+<script>
+var QNBABBAH=document.createElement('ROTFL');
+
+QNBABBAH.addEventListener('DOMNodeRemoved',function EventHandlerEventHandlerIMQFGDGL()
+{
+         QNBABBAH.normalize();
+}, false);
+
+var MBHOMIBK=document.createElement('whatever');
+QNBABBAH.appendChild(MBHOMIBK);
+
+var BKDKEDBI=document.createElement('whatever');
+MBHOMIBK.appendChild(BKDKEDBI);
+
+var QQFOJBAM=document.createElement('whatever');
+BKDKEDBI.appendChild(QQFOJBAM); 
+
+var JNCNIPON=document.createElement('whatever');
+QQFOJBAM.appendChild(JNCNIPON);
+var a=document.createTextNode('AAAA');
+
+JNCNIPON.addEventListener('DOMNodeInserted',function EventHandlerEventHandlerGFNKONKK()
+{
+   JNCNIPON.insertAdjacentHTML('afterbegin','.');
+
+}, false);
+
+JNCNIPON.appendChild(a);
+
+var CACEQGDA=document.createElement('whatever2');
+
+CACEQGDA.addEventListener('DOMSubtreeModified',function EventHandlerEventHandlerMOOLBGNC(){
+
+}, false);
+
+JNCNIPON.innerHTML = 'ALBERTDAJEDUPY';
+</script>
+</body>
+</html>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -141,8 +141,9 @@ load 847127.html
 load 845093-1.html
 load 845093-2.html
 load 849727.html
 load 849732.html
 load 849601.html
 skip-if(Android) load 851353-1.html
 load 863950.html
 load 864448.html
+load 942979.html
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -643,16 +643,19 @@ public:
                                              ErrorResult& aError);
   Attr* GetAttributeNodeNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName);
   already_AddRefed<Attr> SetAttributeNodeNS(Attr& aNewAttr,
                                             ErrorResult& aError);
 
   already_AddRefed<DOMRectList> GetClientRects();
   already_AddRefed<DOMRect> GetBoundingClientRect();
+
+  already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
+
   void ScrollIntoView()
   {
     ScrollIntoView(true);
   }
   void ScrollIntoView(bool aTop);
   int32_t ScrollTop()
   {
     nsIScrollableFrame* sf = GetScrollFrame();
@@ -1074,18 +1077,18 @@ protected:
                                    const nsAString& aLocalName);
 
   inline void RegisterFreezableElement();
   inline void UnregisterFreezableElement();
 
   /**
    * Add/remove this element to the documents id cache
    */
-  inline void AddToIdTable(nsIAtom* aId);
-  inline void RemoveFromIdTable();
+  void AddToIdTable(nsIAtom* aId);
+  void RemoveFromIdTable();
 
   /**
    * Functions to carry out event default actions for links of all types
    * (HTML links, XLinks, SVG "XLinks", etc.)
    */
 
   /**
    * Check that we meet the conditions to handle a link event
@@ -1125,18 +1128,16 @@ private:
    * Get this element's client area rect in app units.
    * @return the frame's client area
    */
   nsRect GetClientAreaRect();
 
   nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr,
                                      bool aFlushLayout = true);
 
-  void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
-
   // Data members
   nsEventStates mState;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID)
 
 inline bool
 Element::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
--- a/content/base/public/ElementInlines.h
+++ b/content/base/public/ElementInlines.h
@@ -9,43 +9,16 @@
 
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 
 namespace mozilla {
 namespace dom {
 
 inline void
-Element::AddToIdTable(nsIAtom* aId)
-{
-  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
-  nsIDocument* doc = GetCurrentDoc();
-  if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
-    doc->AddToIdTable(this, aId);
-  }
-}
-
-inline void
-Element::RemoveFromIdTable()
-{
-  if (HasID()) {
-    nsIDocument* doc = GetCurrentDoc();
-    if (doc) {
-      nsIAtom* id = DoGetID();
-      // id can be null during mutation events evilness. Also, XUL elements
-      // loose their proto attributes during cc-unlink, so this can happen
-      // during cc-unlink too.
-      if (id) {
-        doc->RemoveFromIdTable(this, DoGetID());
-      }
-    }
-  }
-}
-
-inline void
 Element::MozRequestPointerLock()
 {
   OwnerDoc()->RequestPointerLock(this);
 }
 
 inline void
 Element::RegisterFreezableElement()
 {
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -155,16 +155,17 @@ private:
 
 /**
  * A generic base class for DOM elements, implementing many nsIContent,
  * nsIDOMNode and nsIDOMElement methods.
  */
 namespace mozilla {
 namespace dom {
 
+class ShadowRoot;
 class UndoManager;
 
 class FragmentOrElement : public nsIContent
 {
 public:
   FragmentOrElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~FragmentOrElement();
 
@@ -198,16 +199,19 @@ public:
   virtual nsresult AppendText(const PRUnichar* aBuffer, uint32_t aLength,
                               bool aNotify) MOZ_OVERRIDE;
   virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE;
   virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
+  virtual ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
+  virtual ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
+  virtual void SetShadowRoot(ShadowRoot* aBinding) MOZ_OVERRIDE;
   virtual nsIContent *GetXBLInsertionParent() const;
   virtual void SetXBLInsertionParent(nsIContent* aContent);
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual void DestroyContent() MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
@@ -350,27 +354,40 @@ public:
     nsRefPtr<nsContentList> mChildrenList;
 
     /**
      * An object implementing the .classList property for this element.
      */
     nsRefPtr<nsDOMTokenList> mClassList;
 
     /**
+     * ShadowRoot bound to the element.
+     */
+    nsRefPtr<ShadowRoot> mShadowRoot;
+
+    /**
+     * The root ShadowRoot of this element if it is in a shadow tree.
+     */
+    nsRefPtr<ShadowRoot> mContainingShadow;
+
+    /**
      * XBL binding installed on the element.
      */
     nsRefPtr<nsXBLBinding> mXBLBinding;
 
     /**
      * XBL binding installed on the lement.
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
   };
 
 protected:
+  void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
+  void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
+
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
 
   nsDOMSlots *DOMSlots()
   {
     return static_cast<nsDOMSlots*>(Slots());
   }
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -22,16 +22,21 @@
 #include "js/RootingAPI.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TimeStamp.h"
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "Units.h"
 
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
 class imgICache;
 class imgIContainer;
 class imgINotificationObserver;
 class imgIRequest;
 class imgLoader;
 class imgRequestProxy;
 class nsAutoScriptBlockerSuppressNodeRemoved;
 class nsEventListenerManager;
@@ -1836,16 +1841,28 @@ public:
    * Returns true if the doc tree branch which contains aDoc contains any
    * plugins which we don't control event dispatch for, i.e. do any plugins
    * in the same tab as this document receive key events outside of our
    * control? This always returns false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
 
   /**
+   * Fire mutation events for changes caused by parsing directly into a
+   * context node.
+   *
+   * @param aDoc the document of the node
+   * @param aDest the destination node that got stuff appended to it
+   * @param aOldChildCount the number of children the node had before parsing
+   */
+  static void FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+                                                 nsIContent* aDest,
+                                                 int32_t aOldChildCount);
+
+  /**
    * Returns true if the content is in a document and contains a plugin
    * which we don't control event dispatch for, i.e. do any plugins in this
    * doc tree receive key events outside of our control? This always returns
    * false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIContent* aContent);
 
   /**
@@ -2088,16 +2105,27 @@ public:
                                   const nsAString& aFeature,
                                   const nsAString& aVersion);
 
   /**
    * Return true if the browser.dom.window.dump.enabled pref is set.
    */
   static bool DOMWindowDumpEnabled();
 
+  /**
+   * Returns whether a content is an insertion point for XBL
+   * bindings or web components ShadowRoot. In web components,
+   * this corresponds to a <content> element that participates
+   * in node distribution. In XBL this corresponds to an
+   * <xbl:children> element in anonymous content.
+   *
+   * @param aContent The content to test for being an insertion point.
+   */
+  static bool IsContentInsertionPoint(const nsIContent* aContent);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -16,31 +16,34 @@ class nsIURI;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIFrame;
 class nsXBLBinding;
 
 namespace mozilla {
+namespace dom {
+class ShadowRoot;
+} // namespace dom
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3 
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID \
-{ 0x34117ca3, 0x45d0, 0x479e, \
-  { 0x91, 0x30, 0x54, 0x49, 0xa9, 0x5f, 0x25, 0x99 } }
+{ 0x4b05faf2, 0x12e0, 0x4f56, \
+  { 0xb5, 0x2e, 0x3e, 0xb6, 0xad, 0x9c, 0x6e, 0xbe } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
   typedef mozilla::widget::IMEState IMEState;
@@ -621,16 +624,40 @@ public:
    * @param aOldBindingManager The old binding manager that contains
    *                           this content if this content was adopted
    *                           to another document.
    */
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) = 0;
 
   /**
+   * Sets the ShadowRoot binding for this element. The contents of the
+   * binding is rendered in place of this node's children.
+   *
+   * @param aShadowRoot The ShadowRoot to be bound to this element.
+   */
+  virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) = 0;
+
+  /**
+   * Gets the ShadowRoot binding for this element.
+   *
+   * @return The ShadowRoot currently bound to this element.
+   */
+  virtual mozilla::dom::ShadowRoot *GetShadowRoot() const = 0;
+
+  /**
+   * Gets the root of the node tree for this content if it is in a shadow tree.
+   * This method is called |GetContainingShadow| instead of |GetRootShadowRoot|
+   * to avoid confusion with |GetShadowRoot|.
+   *
+   * @return The ShadowRoot that is the root of the node tree.
+   */
+  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0;
+
+  /**
    * Gets the insertion parent element of the XBL binding.
    * The insertion parent is our one true parent in the transformed DOM.
    *
    * @return the insertion parent element.
    */
   virtual nsIContent *GetXBLInsertionParent() const = 0;
 
   /**
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -108,16 +108,19 @@ enum {
   NODE_MAY_BE_IN_BINDING_MNGR =           NODE_FLAG_BIT(6),
 
   NODE_IS_EDITABLE =                      NODE_FLAG_BIT(7),
 
   // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
   // node in fact has a class, but may be set even if it doesn't.
   NODE_MAY_HAVE_CLASS =                   NODE_FLAG_BIT(8),
 
+  // Whether the node participates in a shadow tree.
+  NODE_IS_IN_SHADOW_TREE =                NODE_FLAG_BIT(9),
+
   // Node has an :empty or :-moz-only-whitespace selector
   NODE_HAS_EMPTY_SELECTOR =               NODE_FLAG_BIT(10),
 
   // A child of the node has a selector such that any insertion,
   // removal, or appending of children requires restyling the parent.
   NODE_HAS_SLOW_SELECTOR =                NODE_FLAG_BIT(11),
 
   // A child of the node has a :first-child, :-moz-first-node,
--- a/content/base/src/ChildIterator.cpp
+++ b/content/base/src/ChildIterator.cpp
@@ -1,66 +1,120 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChildIterator.h"
+#include "nsContentUtils.h"
 #include "mozilla/dom/XBLChildrenElement.h"
+#include "mozilla/dom/HTMLContentElement.h"
 
 namespace mozilla {
 namespace dom {
 
+class MatchedNodes {
+public:
+  MatchedNodes(HTMLContentElement* aInsertionPoint)
+    : mIsContentElement(true), mContentElement(aInsertionPoint) {}
+
+  MatchedNodes(XBLChildrenElement* aInsertionPoint)
+    : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}
+
+  uint32_t Length() const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes().Length()
+                             : mChildrenElement->mInsertedChildren.Length();
+  }
+
+  nsIContent* operator[](int32_t aIndex) const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
+                             : mChildrenElement->mInsertedChildren[aIndex];
+  }
+
+  bool IsEmpty() const
+  {
+    return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
+                             : mChildrenElement->mInsertedChildren.IsEmpty();
+  }
+protected:
+  bool mIsContentElement;
+  union {
+    HTMLContentElement* mContentElement;
+    XBLChildrenElement* mChildrenElement;
+  };
+};
+
+static inline MatchedNodes
+GetMatchedNodesForPoint(nsIContent* aContent)
+{
+  if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+    // XBL case
+    return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
+  }
+
+  // Web components case
+  MOZ_ASSERT(aContent->IsHTML(nsGkAtoms::content));
+  return MatchedNodes(static_cast<HTMLContentElement*>(aContent));
+}
+
 nsIContent*
 ExplicitChildIterator::GetNextChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     MOZ_ASSERT(mChild);
-    MOZ_ASSERT(mChild->IsActiveChildrenElement());
+    MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
     MOZ_ASSERT(!mDefaultChild);
 
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (mIndexInInserted < point->mInsertedChildren.Length()) {
-      return point->mInsertedChildren[mIndexInInserted++];
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (mIndexInInserted < assignedChildren.Length()) {
+      return assignedChildren[mIndexInInserted++];
     }
     mIndexInInserted = 0;
     mChild = mChild->GetNextSibling();
   } else if (mDefaultChild) {
     // If we're already in default content, check if there are more nodes there
     MOZ_ASSERT(mChild);
-    MOZ_ASSERT(mChild->IsActiveChildrenElement());
+    MOZ_ASSERT(nsContentUtils::IsContentInsertionPoint(mChild));
 
     mDefaultChild = mDefaultChild->GetNextSibling();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
     mChild = mChild->GetNextSibling();
   } else if (mIsFirst) {  // at the beginning of the child list
     mChild = mParent->GetFirstChild();
     mIsFirst = false;
   } else if (mChild) { // in the middle of the child list
     mChild = mChild->GetNextSibling();
   }
 
-  // Iterate until we find a non-<children>, or a <children> with content.
-  while (mChild && mChild->IsActiveChildrenElement()) {
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (!point->mInsertedChildren.IsEmpty()) {
+  // Iterate until we find a non-insertion point, or an insertion point with
+  // content.
+  while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (!assignedChildren.IsEmpty()) {
+      // Iterate through elements projected on insertion point.
       mIndexInInserted = 1;
-      return point->mInsertedChildren[0];
+      return assignedChildren[0];
     }
 
+    // Insertion points inside fallback/default content
+    // are considered inactive and do not get assigned nodes.
     mDefaultChild = mChild->GetFirstChild();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
+    // If we have an insertion point with no assigned nodes and
+    // no default content, move on to the next node.
     mChild = mChild->GetNextSibling();
   }
 
   return mChild;
 }
 
 FlattenedChildIterator::FlattenedChildIterator(nsIContent* aParent)
   : ExplicitChildIterator(aParent), mXBLInvolved(false)
@@ -104,19 +158,19 @@ nsIContent* FlattenedChildIterator::Get(
 }
 
 nsIContent* FlattenedChildIterator::GetPreviousChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     // NB: mIndexInInserted points one past the last returned child so we need
     // to look *two* indices back in order to return the previous child.
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     if (--mIndexInInserted) {
-      return point->mInsertedChildren[mIndexInInserted - 1];
+      return assignedChildren[mIndexInInserted - 1];
     }
     mChild = mChild->GetPreviousSibling();
   } else if (mDefaultChild) {
     // If we're already in default content, check if there are more nodes there
     mDefaultChild = mDefaultChild->GetPreviousSibling();
     if (mDefaultChild) {
       return mDefaultChild;
     }
@@ -125,22 +179,23 @@ nsIContent* FlattenedChildIterator::GetP
   } else if (mIsFirst) { // at the beginning of the child list
     return nullptr;
   } else if (mChild) { // in the middle of the child list
     mChild = mChild->GetPreviousSibling();
   } else { // at the end of the child list
     mChild = mParent->GetLastChild();
   }
 
-  // Iterate until we find a non-<children>, or a <children> with content.
-  while (mChild && mChild->IsActiveChildrenElement()) {
-    XBLChildrenElement* point = static_cast<XBLChildrenElement*>(mChild);
-    if (!point->mInsertedChildren.IsEmpty()) {
-      mIndexInInserted = point->InsertedChildrenLength();
-      return point->mInsertedChildren[mIndexInInserted - 1];
+  // Iterate until we find a non-insertion point, or an insertion point with
+  // content.
+  while (mChild && nsContentUtils::IsContentInsertionPoint(mChild)) {
+    MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
+    if (!assignedChildren.IsEmpty()) {
+      mIndexInInserted = assignedChildren.Length();
+      return assignedChildren[mIndexInInserted - 1];
     }
 
     mDefaultChild = mChild->GetLastChild();
     if (mDefaultChild) {
       return mDefaultChild;
     }
 
     mChild = mChild->GetPreviousSibling();
--- a/content/base/src/ChildIterator.h
+++ b/content/base/src/ChildIterator.h
@@ -34,16 +34,34 @@ public:
       mDefaultChild(nullptr),
       mIndexInInserted(0),
       mIsFirst(true)
   {
   }
 
   nsIContent* GetNextChild();
 
+  // Looks for aChildToFind respecting insertion points until aChildToFind
+  // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
+  // whether aChildToFind was found as an explicit child prior to encountering
+  // aBound.
+  bool Seek(nsIContent* aChildToFind, nsIContent* aBound = nullptr)
+  {
+    // It would be nice to assert that we find aChildToFind, but bz thinks that
+    // we might not find aChildToFind when called from ContentInserted
+    // if first-letter frames are about.
+
+    nsIContent* child;
+    do {
+      child = GetNextChild();
+    } while (child && child != aChildToFind && child != aBound);
+
+    return child == aChildToFind;
+  }
+
 protected:
   // The parent of the children being iterated. For the FlattenedChildIterator,
   // if there is a binding attached to the original parent, mParent points to
   // the <xbl:content> element for the binding.
   nsIContent* mParent;
 
   // The current child. When we encounter an <xbl:children> insertion point,
   // mChild remains as the insertion point whose content we're iterating (and
@@ -79,29 +97,16 @@ public:
   // child of the node, default content for an <xbl:children> element or
   // an inserted child for an <xbl:children> element.
   nsIContent* Get();
 
   // The inverse of GetNextChild. Properly steps in and out of <xbl:children>
   // elements.
   nsIContent* GetPreviousChild();
 
-  // Looks for aChildToFind respecting XBL insertion points.
-  void Seek(nsIContent* aChildToFind)
-  {
-    // It would be nice to assert that we find aChildToFind, but bz thinks that
-    // we might not find aChildToFind when called from ContentInserted
-    // if first-letter frames are about.
-
-    nsIContent* child;
-    do {
-      child = GetNextChild();
-    } while (child && child != aChildToFind);
-  }
-
   bool XBLInvolved() { return mXBLInvolved; }
 
 private:
   // For certain optimizations, nsCSSFrameConstructor needs to know if the
   // child list of the element that we're iterating matches its .childNodes.
   bool mXBLInvolved;
 };
 
--- a/content/base/src/DocumentFragment.h
+++ b/content/base/src/DocumentFragment.h
@@ -16,20 +16,19 @@ class nsIAtom;
 class nsAString;
 class nsIDocument;
 class nsIContent;
 
 namespace mozilla {
 namespace dom {
 
 class Element;
-class HTMLTemplateElement;
 
-class DocumentFragment MOZ_FINAL : public FragmentOrElement,
-                                   public nsIDOMDocumentFragment
+class DocumentFragment : public FragmentOrElement,
+                         public nsIDOMDocumentFragment
 {
 private:
   void Init()
   {
     NS_ABORT_IF_FALSE(mNodeInfo->NodeType() ==
                       nsIDOMNode::DOCUMENT_FRAGMENT_NODE &&
                       mNodeInfo->Equals(nsGkAtoms::documentFragmentNodeName,
                                         kNameSpaceID_None),
@@ -122,36 +121,36 @@ public:
     return;
   }
 
   virtual Element* GetNameSpaceElement()
   {
     return nullptr;
   }
 
-  HTMLTemplateElement* GetHost() const
+  nsIContent* GetHost() const
   {
     return mHost;
   }
 
-  void SetHost(HTMLTemplateElement* aHost)
+  void SetHost(nsIContent* aHost)
   {
     mHost = aHost;
   }
 
   static already_AddRefed<DocumentFragment>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const MOZ_OVERRIDE;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE;
 #endif
 
 protected:
   nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
-  mozilla::dom::HTMLTemplateElement* mHost; // Weak
+  nsIContent* mHost; // Weak
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
 #endif // mozilla_dom_DocumentFragment_h__
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -13,17 +13,16 @@
 #include "mozilla/dom/ElementInlines.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsIContentInlines.h"
 #include "nsINodeInfo.h"
-#include "nsIDocumentEncoder.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIContentIterator.h"
 #include "nsEventListenerManager.h"
 #include "nsFocusManager.h"
 #include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
@@ -97,17 +96,16 @@
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsTextNode.h"
-#include "mozilla/dom/HTMLTemplateElement.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -118,20 +116,20 @@
 #include "nsDOMMutationObserver.h"
 #include "nsSVGFeatures.h"
 #include "nsWrapperCacheInlines.h"
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 #include "nsStyledElement.h"
 #include "nsXBLService.h"
-#include "nsContentCID.h"
 #include "nsITextControlElement.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/dom/DocumentFragment.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMETHODIMP
@@ -681,16 +679,106 @@ Element::GetClientRects()
           nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
           nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   return rectList.forget();
 }
 
 
 //----------------------------------------------------------------------
 
+void
+Element::AddToIdTable(nsIAtom* aId)
+{
+  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
+  if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    ShadowRoot* containingShadow = GetContainingShadow();
+    containingShadow->AddToIdTable(this, aId);
+  } else {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
+      doc->AddToIdTable(this, aId);
+    }
+  }
+}
+
+void
+Element::RemoveFromIdTable()
+{
+  if (HasID()) {
+    if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ShadowRoot* containingShadow = GetContainingShadow();
+      // Check for containingShadow because it may have
+      // been deleted during unlinking.
+      if (containingShadow) {
+        containingShadow->RemoveFromIdTable(this, DoGetID());
+      }
+    } else {
+      nsIDocument* doc = GetCurrentDoc();
+      if (doc) {
+        nsIAtom* id = DoGetID();
+        // id can be null during mutation events evilness. Also, XUL elements
+        // loose their proto attributes during cc-unlink, so this can happen
+        // during cc-unlink too.
+        if (id) {
+          doc->RemoveFromIdTable(this, DoGetID());
+        }
+      }
+    }
+  }
+}
+
+already_AddRefed<ShadowRoot>
+Element::CreateShadowRoot(ErrorResult& aError)
+{
+  nsAutoScriptBlocker scriptBlocker;
+
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+  nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo(
+    nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
+    nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
+
+  nsRefPtr<nsXBLDocumentInfo> docInfo = new nsXBLDocumentInfo(OwnerDoc());
+
+  nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding();
+  aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"),
+                              docInfo, this, true);
+  if (aError.Failed()) {
+    delete protoBinding;
+    return nullptr;
+  }
+
+  // Unlike for XBL, false is the default for inheriting style.
+  protoBinding->SetInheritsStyle(false);
+
+  // Calling SetPrototypeBinding takes ownership of protoBinding.
+  docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
+
+  nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
+                                                   protoBinding);
+  SetShadowRoot(shadowRoot);
+
+  // xblBinding takes ownership of docInfo.
+  nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
+  shadowRoot->SetAssociatedBinding(xblBinding);
+  xblBinding->SetBoundElement(this);
+
+  SetXBLBinding(xblBinding);
+
+  // Recreate the frame for the bound content because binding a ShadowRoot
+  // changes how things are rendered.
+  nsIDocument* doc = GetCurrentDoc();
+  if (doc) {
+    nsIPresShell *shell = doc->GetShell();
+    if (shell) {
+      shell->RecreateFramesFor(this);
+    }
+  }
+
+  return shadowRoot.forget();
+}
 
 void
 Element::GetAttribute(const nsAString& aName, DOMString& aReturn)
 {
   const nsAttrValue* val =
     mAttrsAndChildren.GetAttr(aName,
                               IsHTML() && IsInHTMLDocument() ?
                                 eIgnoreCase : eCaseMatters);
@@ -992,16 +1080,23 @@ Element::BindToTree(nsIDocument* aDocume
                "non-native anonymous parent!");
   if (aParent) {
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
+    if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      SetFlags(NODE_IS_IN_SHADOW_TREE);
+    }
+    ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
+    if (parentContainingShadow) {
+      DOMSlots()->mContainingShadow = parentContainingShadow;
+    }
   }
 
   bool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS);
 
   // Now set the parent and set the "Force attach xbl" flag if needed.
   if (aParent) {
     if (!GetParent()) {
       NS_ADDREF(aParent);
@@ -1085,18 +1180,18 @@ Element::BindToTree(nsIDocument* aDocume
        child = child->GetNextSibling()) {
     rv = child->BindToTree(aDocument, this, aBindingParent,
                            aCompileEventHandlers);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsNodeUtils::ParentChainChanged(this);
 
-  if (aDocument && HasID() && !aBindingParent) {
-    aDocument->AddToIdTable(this, DoGetID());
+  if (HasID()) {
+    AddToIdTable(DoGetID());
   }
 
   if (MayHaveStyle() && !IsXUL()) {
     // XXXbz if we already have a style attr parsed, this won't do
     // anything... need to fix that.
     // If MayHaveStyle() is true, we must be an nsStyledElement
     static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false);
   }
@@ -1205,29 +1300,30 @@ Element::UnbindFromTree(bool aDeep, bool
     DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
     DeleteProperty(nsGkAtoms::transitionsProperty);
     DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
     DeleteProperty(nsGkAtoms::animationsProperty);
   }
 
   // Unset this since that's what the old code effectively did.
-  UnsetFlags(NODE_FORCE_XBL_BINDINGS);
+  UnsetFlags(NODE_FORCE_XBL_BINDINGS | NODE_IS_IN_SHADOW_TREE);
   
 #ifdef MOZ_XUL
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(nullptr);
   }
   else
 #endif
   {
     nsDOMSlots *slots = GetExistingDOMSlots();
     if (slots) {
       slots->mBindingParent = nullptr;
+      slots->mContainingShadow = nullptr;
     }
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, 
   //  because it has to happen after unsetting the parent pointer, but before
   //  recursively unbinding the kids.
   if (IsHTML()) {
     ResetDir(this);
@@ -2474,803 +2570,27 @@ Element::MozRequestFullScreen()
     return;
   }
 
   OwnerDoc()->AsyncRequestFullScreen(this);
 
   return;
 }
 
-// Try to keep the size of StringBuilder close to a jemalloc bucket size.
-#define STRING_BUFFER_UNITS 1020
-
-namespace {
-
-// We put StringBuilder in the anonymous namespace to prevent anything outside
-// this file from accidentally being linked against it.
-
-class StringBuilder
-{
-private:
-  class Unit
-  {
-  public:
-    Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
-    {
-      MOZ_COUNT_CTOR(StringBuilder::Unit);
-    }
-    ~Unit()
-    {
-      if (mType == eString || mType == eStringWithEncode) {
-        delete mString;
-      }
-      MOZ_COUNT_DTOR(StringBuilder::Unit);
-    }
-
-    enum Type
-    {
-      eUnknown,
-      eAtom,
-      eString,
-      eStringWithEncode,
-      eLiteral,
-      eTextFragment,
-      eTextFragmentWithEncode,
-    };
-
-    union
-    {
-      nsIAtom*              mAtom;
-      const char*           mLiteral;
-      nsAutoString*         mString;
-      const nsTextFragment* mTextFragment;
-    };
-    Type     mType;
-    uint32_t mLength;
-  };
-public:
-  StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
-  {
-    MOZ_COUNT_CTOR(StringBuilder);
-  }
-
-  ~StringBuilder()
-  {
-    MOZ_COUNT_DTOR(StringBuilder);
-  }
-
-  void Append(nsIAtom* aAtom)
-  {
-    Unit* u = AddUnit();
-    u->mAtom = aAtom;
-    u->mType = Unit::eAtom;
-    uint32_t len = aAtom->GetLength();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  template<int N>
-  void Append(const char (&aLiteral)[N])
-  {
-    Unit* u = AddUnit();
-    u->mLiteral = aLiteral;
-    u->mType = Unit::eLiteral;
-    uint32_t len = N - 1;
-    u->mLength = len;
-    mLength += len;
-  }
-
-  template<int N>
-  void Append(char (&aLiteral)[N])
-  {
-    Unit* u = AddUnit();
-    u->mLiteral = aLiteral;
-    u->mType = Unit::eLiteral;
-    uint32_t len = N - 1;
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void Append(const nsAString& aString)
-  {
-    Unit* u = AddUnit();
-    u->mString = new nsAutoString(aString);
-    u->mType = Unit::eString;
-    uint32_t len = aString.Length();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void Append(nsAutoString* aString)
-  {
-    Unit* u = AddUnit();
-    u->mString = aString;
-    u->mType = Unit::eString;
-    uint32_t len = aString->Length();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
-  {
-    Unit* u = AddUnit();
-    u->mString = aString;
-    u->mType = Unit::eStringWithEncode;
-    u->mLength = aLen;
-    mLength += aLen;
-  }
-
-  void Append(const nsTextFragment* aTextFragment)
-  {
-    Unit* u = AddUnit();
-    u->mTextFragment = aTextFragment;
-    u->mType = Unit::eTextFragment;
-    uint32_t len = aTextFragment->GetLength();
-    u->mLength = len;
-    mLength += len;
-  }
-
-  void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
-  {
-    Unit* u = AddUnit();
-    u->mTextFragment = aTextFragment;
-    u->mType = Unit::eTextFragmentWithEncode;
-    u->mLength = aLen;
-    mLength += aLen;
-  }
-
-  bool ToString(nsAString& aOut)
-  {
-    if (!aOut.SetCapacity(mLength, fallible_t())) {
-      return false;
-    }
-
-    for (StringBuilder* current = this; current; current = current->mNext) {
-      uint32_t len = current->mUnits.Length();
-      for (uint32_t i = 0; i < len; ++i) {
-        Unit& u = current->mUnits[i];
-        switch (u.mType) {
-          case Unit::eAtom:
-            aOut.Append(nsDependentAtomString(u.mAtom));
-            break;
-          case Unit::eString:
-            aOut.Append(*(u.mString));
-            break;
-          case Unit::eStringWithEncode:
-            EncodeAttrString(*(u.mString), aOut);
-            break;
-          case Unit::eLiteral:
-            aOut.AppendASCII(u.mLiteral, u.mLength);
-            break;
-          case Unit::eTextFragment:
-            u.mTextFragment->AppendTo(aOut);
-            break;
-          case Unit::eTextFragmentWithEncode:
-            EncodeTextFragment(u.mTextFragment, aOut);
-            break;
-          default:
-            MOZ_CRASH("Unknown unit type?");
-        }
-      }
-    }
-    return true;
-  }
-private:
-  Unit* AddUnit()
-  {
-    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
-      new StringBuilder(this);
-    }
-    return mLast->mUnits.AppendElement();
-  }
-
-  StringBuilder(StringBuilder* aFirst)
-  : mLast(nullptr), mLength(0)
-  {
-    MOZ_COUNT_CTOR(StringBuilder);
-    aFirst->mLast->mNext = this;
-    aFirst->mLast = this;
-  }
-
-  void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
-  {
-    const PRUnichar* c = aValue.BeginReading();
-    const PRUnichar* end = aValue.EndReading();
-    while (c < end) {
-      switch (*c) {
-      case '"':
-        aOut.AppendLiteral("&quot;");
-        break;
-      case '&':
-        aOut.AppendLiteral("&amp;");
-        break;
-      case 0x00A0:
-        aOut.AppendLiteral("&nbsp;");
-        break;
-      default:
-        aOut.Append(*c);
-        break;
-      }
-      ++c;
-    }
-  }
-
-  void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
-  {
-    uint32_t len = aValue->GetLength();
-    if (aValue->Is2b()) {
-      const PRUnichar* data = aValue->Get2b();
-      for (uint32_t i = 0; i < len; ++i) {
-        const PRUnichar c = data[i];
-        switch (c) {
-          case '<':
-            aOut.AppendLiteral("&lt;");
-            break;
-          case '>':
-            aOut.AppendLiteral("&gt;");
-            break;
-          case '&':
-            aOut.AppendLiteral("&amp;");
-            break;
-          case 0x00A0:
-            aOut.AppendLiteral("&nbsp;");
-            break;
-          default:
-            aOut.Append(c);
-            break;
-        }
-      }
-    } else {
-      const char* data = aValue->Get1b();
-      for (uint32_t i = 0; i < len; ++i) {
-        const unsigned char c = data[i];
-        switch (c) {
-          case '<':
-            aOut.AppendLiteral("&lt;");
-            break;
-          case '>':
-            aOut.AppendLiteral("&gt;");
-            break;
-          case '&':
-            aOut.AppendLiteral("&amp;");
-            break;
-          case 0x00A0:
-            aOut.AppendLiteral("&nbsp;");
-            break;
-          default:
-            aOut.Append(c);
-            break;
-        }
-      }
-    }
-  }
-
-  nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
-  nsAutoPtr<StringBuilder>                mNext;
-  StringBuilder*                          mLast;
-  // mLength is used only in the first StringBuilder object in the linked list.
-  uint32_t                                mLength;
-};
-
-} // anonymous namespace
-
-static void
-AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
-{
-  uint32_t extraSpaceNeeded = 0;
-  uint32_t len = aText->GetLength();
-  if (aText->Is2b()) {
-    const PRUnichar* data = aText->Get2b();
-    for (uint32_t i = 0; i < len; ++i) {
-      const PRUnichar c = data[i];
-      switch (c) {
-        case '<':
-          extraSpaceNeeded += ArrayLength("&lt;") - 2;
-          break;
-        case '>':
-          extraSpaceNeeded += ArrayLength("&gt;") - 2;
-          break;
-        case '&':
-          extraSpaceNeeded += ArrayLength("&amp;") - 2;
-          break;
-        case 0x00A0:
-          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-          break;
-        default:
-          break;
-      }
-    }
-  } else {
-    const char* data = aText->Get1b();
-    for (uint32_t i = 0; i < len; ++i) {
-      const unsigned char c = data[i];
-      switch (c) {
-        case '<':
-          extraSpaceNeeded += ArrayLength("&lt;") - 2;
-          break;
-        case '>':
-          extraSpaceNeeded += ArrayLength("&gt;") - 2;
-          break;
-        case '&':
-          extraSpaceNeeded += ArrayLength("&amp;") - 2;
-          break;
-        case 0x00A0:
-          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-          break;
-        default:
-          break;
-      }
-    }
-  }
-
-  if (extraSpaceNeeded) {
-    aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
-  } else {
-    aBuilder.Append(aText);
-  }
-}
-
-static void
-AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
-{
-  const PRUnichar* c = aValue->BeginReading();
-  const PRUnichar* end = aValue->EndReading();
-
-  uint32_t extraSpaceNeeded = 0;
-  while (c < end) {
-    switch (*c) {
-      case '"':
-        extraSpaceNeeded += ArrayLength("&quot;") - 2;
-        break;
-      case '&':
-        extraSpaceNeeded += ArrayLength("&amp;") - 2;
-        break;
-      case 0x00A0:
-        extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
-        break;
-      default:
-        break;
-    }
-    ++c;
-  }
-
-  if (extraSpaceNeeded) {
-    aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
-  } else {
-    aBuilder.Append(aValue);
-  }
-}
-
-static void
-StartElement(Element* aContent, StringBuilder& aBuilder)
-{
-  nsIAtom* localName = aContent->Tag();
-  int32_t tagNS = aContent->GetNameSpaceID();
-
-  aBuilder.Append("<");
-  if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
-    aBuilder.Append(localName);
-  } else {
-    aBuilder.Append(aContent->NodeName());
-  }
-
-  int32_t count = aContent->GetAttrCount();
-  for (int32_t i = count; i > 0;) {
-    --i;
-    const nsAttrName* name = aContent->GetAttrNameAt(i);
-    int32_t attNs = name->NamespaceID();
-    nsIAtom* attName = name->LocalName();
-
-    // Filter out any attribute starting with [-|_]moz
-    nsDependentAtomString attrNameStr(attName);
-    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
-        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
-      continue;
-    }
-
-    nsAutoString* attValue = new nsAutoString();
-    aContent->GetAttr(attNs, attName, *attValue);
-
-    // Filter out special case of <br type="_moz*"> used by the editor.
-    // Bug 16988.  Yuck.
-    if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
-        attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
-        StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
-      delete attValue;
-      continue;
-    }
-    
-    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
-        (attNs == kNameSpaceID_XMLNS &&
-         attName == nsGkAtoms::xmlns)) {
-      aBuilder.Append(" ");
-    } else if (attNs == kNameSpaceID_XML) {
-      aBuilder.Append(" xml:");
-    } else if (attNs == kNameSpaceID_XMLNS) {
-      aBuilder.Append(" xmlns:");
-    } else if (attNs == kNameSpaceID_XLink) {
-      aBuilder.Append(" xlink:");
-    } else {
-      nsIAtom* prefix = name->GetPrefix();
-      if (prefix) {
-        aBuilder.Append(" ");
-        aBuilder.Append(prefix);
-        aBuilder.Append(":");
-      }
-    }
-
-    aBuilder.Append(attName);
-    aBuilder.Append("=\"");
-    AppendEncodedAttributeValue(attValue, aBuilder);
-    aBuilder.Append("\"");
-  }
-
-  aBuilder.Append(">");
-
-  /*
-  // Per HTML spec we should append one \n if the first child of
-  // pre/textarea/listing is a textnode and starts with a \n.
-  // But because browsers haven't traditionally had that behavior,
-  // we're not changing our behavior either - yet.
-  if (aContent->IsHTML()) {
-    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
-        localName == nsGkAtoms::listing) {
-      nsIContent* fc = aContent->GetFirstChild();
-      if (fc &&
-          (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
-           fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
-        const nsTextFragment* text = fc->GetText();
-        if (text && text->GetLength() && text->CharAt(0) == PRUnichar('\n')) {
-          aBuilder.Append("\n");
-        }
-      }
-    }
-  }*/
-}
-
-static inline bool
-ShouldEscape(nsIContent* aParent)
-{
-  if (!aParent || !aParent->IsHTML()) {
-    return true;
-  }
-
-  static const nsIAtom* nonEscapingElements[] = {
-    nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
-    nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
-    nsGkAtoms::plaintext, 
-    // Per the current spec noscript should be escaped in case
-    // scripts are disabled or if document doesn't have
-    // browsing context. However the latter seems to be a spec bug
-    // and Gecko hasn't traditionally done the former.
-    nsGkAtoms::noscript    
-  };
-  static mozilla::BloomFilter<12, nsIAtom> sFilter;
-  static bool sInitialized = false;
-  if (!sInitialized) {
-    sInitialized = true;
-    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
-      sFilter.add(nonEscapingElements[i]);
-    }
-  }
-
-  nsIAtom* tag = aParent->Tag();
-  if (sFilter.mightContain(tag)) {
-    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
-      if (tag == nonEscapingElements[i]) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-static inline bool
-IsVoidTag(Element* aElement)
-{
-  if (!aElement->IsHTML()) {
-    return false;
-  }
-
-  static const nsIAtom* voidElements[] = {
-    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
-    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
-    nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
-    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
-    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
-    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
-    nsGkAtoms::wbr
-  };
-
-  static mozilla::BloomFilter<12, nsIAtom> sFilter;
-  static bool sInitialized = false;
-  if (!sInitialized) {
-    sInitialized = true;
-    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
-      sFilter.add(voidElements[i]);
-    }
-  }
-  
-  nsIAtom* tag = aElement->Tag();
-  if (sFilter.mightContain(tag)) {
-    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
-      if (tag == voidElements[i]) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-static bool
-Serialize(Element* aRoot, bool aDescendentsOnly, nsAString& aOut)
-{
-  nsINode* current = aDescendentsOnly ?
-    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
-
-  if (!current) {
-    return true;
-  }
-
-  StringBuilder builder;
-  nsIContent* next;
-  while (true) {
-    bool isVoid = false;
-    switch (current->NodeType()) {
-      case nsIDOMNode::ELEMENT_NODE: {
-        Element* elem = current->AsElement();
-        StartElement(elem, builder);
-        isVoid = IsVoidTag(elem);
-        if (!isVoid &&
-            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
-          current = next;
-          continue;
-        }
-        break;
-      }
-
-      case nsIDOMNode::TEXT_NODE:
-      case nsIDOMNode::CDATA_SECTION_NODE: {
-        const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
-        nsIContent* parent = current->GetParent();
-        if (ShouldEscape(parent)) {
-          AppendEncodedCharacters(text, builder);
-        } else {
-          builder.Append(text);
-        }
-        break;
-      }
-
-      case nsIDOMNode::COMMENT_NODE: {
-        builder.Append("<!--");
-        builder.Append(static_cast<nsIContent*>(current)->GetText());
-        builder.Append("-->");
-        break;
-      }
-
-      case nsIDOMNode::DOCUMENT_TYPE_NODE: {
-        builder.Append("<!DOCTYPE ");
-        builder.Append(current->NodeName());
-        builder.Append(">");
-        break;
-      }
-
-      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
-        builder.Append("<?");
-        builder.Append(current->NodeName());
-        builder.Append(" ");
-        builder.Append(static_cast<nsIContent*>(current)->GetText());
-        builder.Append(">");
-        break;
-      }
-    }
-
-    while (true) {
-      if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
-        builder.Append("</");
-        nsIContent* elem = static_cast<nsIContent*>(current);
-        if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
-          builder.Append(elem->Tag());
-        } else {
-          builder.Append(current->NodeName());
-        }
-        builder.Append(">");
-      }
-      isVoid = false;
-
-      if (current == aRoot) {
-        return builder.ToString(aOut);
-      }
-
-      if ((next = current->GetNextSibling())) {
-        current = next;
-        break;
-      }
-
-      current = current->GetParentNode();
-
-      // Template case, if we are in a template's content, then the parent
-      // should be the host template element.
-      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
-        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
-        HTMLTemplateElement* fragHost = frag->GetHost();
-        if (fragHost) {
-          current = fragHost;
-        }
-      }
-
-      if (aDescendentsOnly && current == aRoot) {
-        return builder.ToString(aOut);
-      }
-    }
-  }
-}
-
-void
-Element::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
-{
-  aMarkup.Truncate();
-
-  nsIDocument* doc = OwnerDoc();
-  if (IsInHTMLDocument()) {
-    Serialize(this, !aIncludeSelf, aMarkup);
-    return;
-  }
-
-  nsAutoString contentType;
-  doc->GetContentType(contentType);
-  bool tryToCacheEncoder = !aIncludeSelf;
-
-  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
-  if (!docEncoder) {
-    docEncoder =
-      do_CreateInstance(PromiseFlatCString(
-        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
-        NS_ConvertUTF16toUTF8(contentType)
-      ).get());
-  }
-  if (!docEncoder) {
-    // This could be some type for which we create a synthetic document.  Try
-    // again as XML
-    contentType.AssignLiteral("application/xml");
-    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
-    // Don't try to cache the encoder since it would point to a different
-    // contentType once it has been reinitialized.
-    tryToCacheEncoder = false;
-  }
-
-  NS_ENSURE_TRUE_VOID(docEncoder);
-
-  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
-                   // Output DOM-standard newlines
-                   nsIDocumentEncoder::OutputLFLineBreak |
-                   // Don't do linebreaking that's not present in
-                   // the source
-                   nsIDocumentEncoder::OutputRaw |
-                   // Only check for mozdirty when necessary (bug 599983)
-                   nsIDocumentEncoder::OutputIgnoreMozDirty;
-
-  if (IsEditable()) {
-    nsIEditor* editor = GetEditorInternal();
-    if (editor && editor->OutputsMozDirty()) {
-      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
-    }
-  }
-
-  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-  if (aIncludeSelf) {
-    docEncoder->SetNativeNode(this);
-  } else {
-    docEncoder->SetNativeContainerNode(this);
-  }
-  rv = docEncoder->EncodeToString(aMarkup);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (tryToCacheEncoder) {
-    doc->SetCachedEncoder(docEncoder.forget());
-  }
-}
-
-/**
- * Fire mutation events for changes caused by parsing directly into a
- * context node.
- *
- * @param aDoc the document of the node
- * @param aDest the destination node that got stuff appended to it
- * @param aOldChildCount the number of children the node had before parsing
- */
-static void
-FireMutationEventsForDirectParsing(nsIDocument* aDoc, nsIContent* aDest,
-                                   int32_t aOldChildCount)
-{
-  // Fire mutation events. Optimize for the case when there are no listeners
-  int32_t newChildCount = aDest->GetChildCount();
-  if (newChildCount && nsContentUtils::
-        HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
-    nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
-    NS_ASSERTION(newChildCount - aOldChildCount >= 0,
-                 "What, some unexpected dom mutation has happened?");
-    childNodes.SetCapacity(newChildCount - aOldChildCount);
-    for (nsIContent* child = aDest->GetFirstChild();
-         child;
-         child = child->GetNextSibling()) {
-      childNodes.AppendElement(child);
-    }
-    Element::FireNodeInserted(aDoc, aDest, childNodes);
-  }
-}
-
 NS_IMETHODIMP
 Element::GetInnerHTML(nsAString& aInnerHTML)
 {
   GetMarkup(false, aInnerHTML);
   return NS_OK;
 }
 
 void
 Element::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
 {
-  FragmentOrElement* target = this;
-  // Handle template case.
-  if (nsNodeUtils::IsTemplateElement(target)) {
-    DocumentFragment* frag =
-      static_cast<HTMLTemplateElement*>(target)->Content();
-    MOZ_ASSERT(frag);
-    target = frag;
-  }
-
-  nsIDocument* doc = target->OwnerDoc();
-
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(doc, nullptr);
-
-  target->FireNodeRemovedForChildren();
-
-  // Needed when innerHTML is used in combination with contenteditable
-  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
-
-  // Remove childnodes.
-  uint32_t childCount = target->GetChildCount();
-  nsAutoMutationBatch mb(target, true, false);
-  for (uint32_t i = 0; i < childCount; ++i) {
-    target->RemoveChildAt(0, true);
-  }
-  mb.RemovalDone();
-
-  nsAutoScriptLoaderDisabler sld(doc);
-
-  if (doc->IsHTML()) {
-    int32_t oldChildCount = target->GetChildCount();
-    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
-                                               target,
-                                               Tag(),
-                                               GetNameSpaceID(),
-                                               doc->GetCompatibilityMode() ==
-                                                 eCompatibility_NavQuirks,
-                                               true);
-    mb.NodesAdded();
-    // HTML5 parser has notified, but not fired mutation events.
-    FireMutationEventsForDirectParsing(doc, target, oldChildCount);
-  } else {
-    nsCOMPtr<nsIDOMDocumentFragment> df;
-    aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
-                                                      true,
-                                                      getter_AddRefs(df));
-    nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
-    if (!aError.Failed()) {
-      // Suppress assertion about node removal mutation events that can't have
-      // listeners anyway, because no one has had the chance to register mutation
-      // listeners on the fragment that comes from the parser.
-      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
-
-      static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
-      mb.NodesAdded();
-    }
-  }
+  SetInnerHTMLInternal(aInnerHTML, aError);
 }
 
 void
 Element::GetOuterHTML(nsAString& aOuterHTML)
 {
   GetMarkup(true, aOuterHTML);
 }
 
@@ -3400,17 +2720,18 @@ Element::InsertAdjacentHTML(const nsAStr
     aError = nsContentUtils::ParseFragmentHTML(aText,
                                                destination,
                                                contextLocal,
                                                contextNs,
                                                doc->GetCompatibilityMode() ==
                                                  eCompatibility_NavQuirks,
                                                true);
     // HTML5 parser has notified, but not fired mutation events.
-    FireMutationEventsForDirectParsing(doc, destination, oldChildCount);
+    nsContentUtils::FireMutationEventsForDirectParsing(doc, destination,
+                                                       oldChildCount);
     return;
   }
 
   // couldn't parse directly
   nsCOMPtr<nsIDOMDocumentFragment> df;
   aError = nsContentUtils::CreateContextualFragment(destination,
                                                     aText,
                                                     true,
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -16,16 +16,17 @@
 
 #include "mozilla/dom/FragmentOrElement.h"
 
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "nsINodeInfo.h"
 #include "nsIDocumentInlines.h"
+#include "nsIDocumentEncoder.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIContentIterator.h"
 #include "nsEventListenerManager.h"
 #include "nsFocusManager.h"
 #include "nsILinkHandler.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIURL.h"
@@ -68,16 +69,18 @@
 #include "nsBindingManager.h"
 #include "nsXBLBinding.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIBoxObject.h"
 #include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
+#include "nsTextFragment.h"
+#include "nsContentCID.h"
 
 #include "nsIDOMEventListener.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "js/GCAPI.h"
 
@@ -115,16 +118,19 @@
 #include "nsWrapperCacheInlines.h"
 #include "nsCycleCollector.h"
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
 
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/HTMLTemplateElement.h"
+
 #include "nsStyledElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
 bool nsIContent::sTabFocusModelAppliesToXUL = false;
 uint32_t nsMutationGuard::sMutationCount = 0;
@@ -541,16 +547,22 @@ FragmentOrElement::nsDOMSlots::Traverse(
   }
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
   cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
   cb.NoteXPCOMChild(mXBLInsertionParent.get());
 
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
+
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
 }
 
 void
@@ -561,16 +573,18 @@ FragmentOrElement::nsDOMSlots::Unlink(bo
   if (mAttributeMap) {
     mAttributeMap->DropReference();
     mAttributeMap = nullptr;
   }
   if (aIsXUL)
     NS_IF_RELEASE(mControllers);
   mXBLBinding = nullptr;
   mXBLInsertionParent = nullptr;
+  mShadowRoot = nullptr;
+  mContainingShadow = nullptr;
   mChildrenList = nullptr;
   mUndoManager = nullptr;
   if (mClassList) {
     mClassList->DropReference();
     mClassList = nullptr;
   }
 }
 
@@ -955,16 +969,43 @@ FragmentOrElement::GetXBLInsertionParent
     if (slots) {
       return slots->mXBLInsertionParent;
     }
   }
 
   return nullptr;
 }
 
+ShadowRoot*
+FragmentOrElement::GetShadowRoot() const
+{
+  nsDOMSlots *slots = GetExistingDOMSlots();
+  if (slots) {
+    return slots->mShadowRoot;
+  }
+  return nullptr;
+}
+
+ShadowRoot*
+FragmentOrElement::GetContainingShadow() const
+{
+  nsDOMSlots *slots = GetExistingDOMSlots();
+  if (slots) {
+    return slots->mContainingShadow;
+  }
+  return nullptr;
+}
+
+void
+FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
+{
+  nsDOMSlots *slots = DOMSlots();
+  slots->mShadowRoot = aShadowRoot;
+}
+
 void
 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
 {
   nsDOMSlots *slots = DOMSlots();
   if (aContent) {
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   }
   slots->mXBLInsertionParent = aContent;
@@ -1860,16 +1901,781 @@ FragmentOrElement::GetChildArray(uint32_
 }
 
 int32_t
 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
 {
   return mAttrsAndChildren.IndexOfChild(aPossibleChild);
 }
 
+// Try to keep the size of StringBuilder close to a jemalloc bucket size.
+#define STRING_BUFFER_UNITS 1020
+
+namespace {
+
+// We put StringBuilder in the anonymous namespace to prevent anything outside
+// this file from accidentally being linked against it.
+
+class StringBuilder
+{
+private:
+  class Unit
+  {
+  public:
+    Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
+    {
+      MOZ_COUNT_CTOR(StringBuilder::Unit);
+    }
+    ~Unit()
+    {
+      if (mType == eString || mType == eStringWithEncode) {
+        delete mString;
+      }
+      MOZ_COUNT_DTOR(StringBuilder::Unit);
+    }
+
+    enum Type
+    {
+      eUnknown,
+      eAtom,
+      eString,
+      eStringWithEncode,
+      eLiteral,
+      eTextFragment,
+      eTextFragmentWithEncode,
+    };
+
+    union
+    {
+      nsIAtom*              mAtom;
+      const char*           mLiteral;
+      nsAutoString*         mString;
+      const nsTextFragment* mTextFragment;
+    };
+    Type     mType;
+    uint32_t mLength;
+  };
+public:
+  StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
+  {
+    MOZ_COUNT_CTOR(StringBuilder);
+  }
+
+  ~StringBuilder()
+  {
+    MOZ_COUNT_DTOR(StringBuilder);
+  }
+
+  void Append(nsIAtom* aAtom)
+  {
+    Unit* u = AddUnit();
+    u->mAtom = aAtom;
+    u->mType = Unit::eAtom;
+    uint32_t len = aAtom->GetLength();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  template<int N>
+  void Append(const char (&aLiteral)[N])
+  {
+    Unit* u = AddUnit();
+    u->mLiteral = aLiteral;
+    u->mType = Unit::eLiteral;
+    uint32_t len = N - 1;
+    u->mLength = len;
+    mLength += len;
+  }
+
+  template<int N>
+  void Append(char (&aLiteral)[N])
+  {
+    Unit* u = AddUnit();
+    u->mLiteral = aLiteral;
+    u->mType = Unit::eLiteral;
+    uint32_t len = N - 1;
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void Append(const nsAString& aString)
+  {
+    Unit* u = AddUnit();
+    u->mString = new nsAutoString(aString);
+    u->mType = Unit::eString;
+    uint32_t len = aString.Length();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void Append(nsAutoString* aString)
+  {
+    Unit* u = AddUnit();
+    u->mString = aString;
+    u->mType = Unit::eString;
+    uint32_t len = aString->Length();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
+  {
+    Unit* u = AddUnit();
+    u->mString = aString;
+    u->mType = Unit::eStringWithEncode;
+    u->mLength = aLen;
+    mLength += aLen;
+  }
+
+  void Append(const nsTextFragment* aTextFragment)
+  {
+    Unit* u = AddUnit();
+    u->mTextFragment = aTextFragment;
+    u->mType = Unit::eTextFragment;
+    uint32_t len = aTextFragment->GetLength();
+    u->mLength = len;
+    mLength += len;
+  }
+
+  void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
+  {
+    Unit* u = AddUnit();
+    u->mTextFragment = aTextFragment;
+    u->mType = Unit::eTextFragmentWithEncode;
+    u->mLength = aLen;
+    mLength += aLen;
+  }
+
+  bool ToString(nsAString& aOut)
+  {
+    if (!aOut.SetCapacity(mLength, fallible_t())) {
+      return false;
+    }
+
+    for (StringBuilder* current = this; current; current = current->mNext) {
+      uint32_t len = current->mUnits.Length();
+      for (uint32_t i = 0; i < len; ++i) {
+        Unit& u = current->mUnits[i];
+        switch (u.mType) {
+          case Unit::eAtom:
+            aOut.Append(nsDependentAtomString(u.mAtom));
+            break;
+          case Unit::eString:
+            aOut.Append(*(u.mString));
+            break;
+          case Unit::eStringWithEncode:
+            EncodeAttrString(*(u.mString), aOut);
+            break;
+          case Unit::eLiteral:
+            aOut.AppendASCII(u.mLiteral, u.mLength);
+            break;
+          case Unit::eTextFragment:
+            u.mTextFragment->AppendTo(aOut);
+            break;
+          case Unit::eTextFragmentWithEncode:
+            EncodeTextFragment(u.mTextFragment, aOut);
+            break;
+          default:
+            MOZ_CRASH("Unknown unit type?");
+        }
+      }
+    }
+    return true;
+  }
+private:
+  Unit* AddUnit()
+  {
+    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
+      new StringBuilder(this);
+    }
+    return mLast->mUnits.AppendElement();
+  }
+
+  StringBuilder(StringBuilder* aFirst)
+  : mLast(nullptr), mLength(0)
+  {
+    MOZ_COUNT_CTOR(StringBuilder);
+    aFirst->mLast->mNext = this;
+    aFirst->mLast = this;
+  }
+
+  void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
+  {
+    const PRUnichar* c = aValue.BeginReading();
+    const PRUnichar* end = aValue.EndReading();
+    while (c < end) {
+      switch (*c) {
+      case '"':
+        aOut.AppendLiteral("&quot;");
+        break;
+      case '&':
+        aOut.AppendLiteral("&amp;");
+        break;
+      case 0x00A0:
+        aOut.AppendLiteral("&nbsp;");
+        break;
+      default:
+        aOut.Append(*c);
+        break;
+      }
+      ++c;
+    }
+  }
+
+  void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
+  {
+    uint32_t len = aValue->GetLength();
+    if (aValue->Is2b()) {
+      const PRUnichar* data = aValue->Get2b();
+      for (uint32_t i = 0; i < len; ++i) {
+        const PRUnichar c = data[i];
+        switch (c) {
+          case '<':
+            aOut.AppendLiteral("&lt;");
+            break;
+          case '>':
+            aOut.AppendLiteral("&gt;");
+            break;
+          case '&':
+            aOut.AppendLiteral("&amp;");
+            break;
+          case 0x00A0:
+            aOut.AppendLiteral("&nbsp;");
+            break;
+          default:
+            aOut.Append(c);
+            break;
+        }
+      }
+    } else {
+      const char* data = aValue->Get1b();
+      for (uint32_t i = 0; i < len; ++i) {
+        const unsigned char c = data[i];
+        switch (c) {
+          case '<':
+            aOut.AppendLiteral("&lt;");
+            break;
+          case '>':
+            aOut.AppendLiteral("&gt;");
+            break;
+          case '&':
+            aOut.AppendLiteral("&amp;");
+            break;
+          case 0x00A0:
+            aOut.AppendLiteral("&nbsp;");
+            break;
+          default:
+            aOut.Append(c);
+            break;
+        }
+      }
+    }
+  }
+
+  nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
+  nsAutoPtr<StringBuilder>                mNext;
+  StringBuilder*                          mLast;
+  // mLength is used only in the first StringBuilder object in the linked list.
+  uint32_t                                mLength;
+};
+
+} // anonymous namespace
+
+static void
+AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
+{
+  uint32_t extraSpaceNeeded = 0;
+  uint32_t len = aText->GetLength();
+  if (aText->Is2b()) {
+    const PRUnichar* data = aText->Get2b();
+    for (uint32_t i = 0; i < len; ++i) {
+      const PRUnichar c = data[i];
+      switch (c) {
+        case '<':
+          extraSpaceNeeded += ArrayLength("&lt;") - 2;
+          break;
+        case '>':
+          extraSpaceNeeded += ArrayLength("&gt;") - 2;
+          break;
+        case '&':
+          extraSpaceNeeded += ArrayLength("&amp;") - 2;
+          break;
+        case 0x00A0:
+          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+          break;
+        default:
+          break;
+      }
+    }
+  } else {
+    const char* data = aText->Get1b();
+    for (uint32_t i = 0; i < len; ++i) {
+      const unsigned char c = data[i];
+      switch (c) {
+        case '<':
+          extraSpaceNeeded += ArrayLength("&lt;") - 2;
+          break;
+        case '>':
+          extraSpaceNeeded += ArrayLength("&gt;") - 2;
+          break;
+        case '&':
+          extraSpaceNeeded += ArrayLength("&amp;") - 2;
+          break;
+        case 0x00A0:
+          extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  if (extraSpaceNeeded) {
+    aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
+  } else {
+    aBuilder.Append(aText);
+  }
+}
+
+static void
+AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
+{
+  const PRUnichar* c = aValue->BeginReading();
+  const PRUnichar* end = aValue->EndReading();
+
+  uint32_t extraSpaceNeeded = 0;
+  while (c < end) {
+    switch (*c) {
+      case '"':
+        extraSpaceNeeded += ArrayLength("&quot;") - 2;
+        break;
+      case '&':
+        extraSpaceNeeded += ArrayLength("&amp;") - 2;
+        break;
+      case 0x00A0:
+        extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
+        break;
+      default:
+        break;
+    }
+    ++c;
+  }
+
+  if (extraSpaceNeeded) {
+    aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
+  } else {
+    aBuilder.Append(aValue);
+  }
+}
+
+static void
+StartElement(Element* aContent, StringBuilder& aBuilder)
+{
+  nsIAtom* localName = aContent->Tag();
+  int32_t tagNS = aContent->GetNameSpaceID();
+
+  aBuilder.Append("<");
+  if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
+    aBuilder.Append(localName);
+  } else {
+    aBuilder.Append(aContent->NodeName());
+  }
+
+  int32_t count = aContent->GetAttrCount();
+  for (int32_t i = count; i > 0;) {
+    --i;
+    const nsAttrName* name = aContent->GetAttrNameAt(i);
+    int32_t attNs = name->NamespaceID();
+    nsIAtom* attName = name->LocalName();
+
+    // Filter out any attribute starting with [-|_]moz
+    nsDependentAtomString attrNameStr(attName);
+    if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
+        StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
+      continue;
+    }
+
+    nsAutoString* attValue = new nsAutoString();
+    aContent->GetAttr(attNs, attName, *attValue);
+
+    // Filter out special case of <br type="_moz*"> used by the editor.
+    // Bug 16988.  Yuck.
+    if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
+        attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
+        StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
+      delete attValue;
+      continue;
+    }
+    
+    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
+        (attNs == kNameSpaceID_XMLNS &&
+         attName == nsGkAtoms::xmlns)) {
+      aBuilder.Append(" ");
+    } else if (attNs == kNameSpaceID_XML) {
+      aBuilder.Append(" xml:");
+    } else if (attNs == kNameSpaceID_XMLNS) {
+      aBuilder.Append(" xmlns:");
+    } else if (attNs == kNameSpaceID_XLink) {
+      aBuilder.Append(" xlink:");
+    } else {
+      nsIAtom* prefix = name->GetPrefix();
+      if (prefix) {
+        aBuilder.Append(" ");
+        aBuilder.Append(prefix);
+        aBuilder.Append(":");
+      }
+    }
+
+    aBuilder.Append(attName);
+    aBuilder.Append("=\"");
+    AppendEncodedAttributeValue(attValue, aBuilder);
+    aBuilder.Append("\"");
+  }
+
+  aBuilder.Append(">");
+
+  /*
+  // Per HTML spec we should append one \n if the first child of
+  // pre/textarea/listing is a textnode and starts with a \n.
+  // But because browsers haven't traditionally had that behavior,
+  // we're not changing our behavior either - yet.
+  if (aContent->IsHTML()) {
+    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
+        localName == nsGkAtoms::listing) {
+      nsIContent* fc = aContent->GetFirstChild();
+      if (fc &&
+          (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
+           fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
+        const nsTextFragment* text = fc->GetText();
+        if (text && text->GetLength() && text->CharAt(0) == PRUnichar('\n')) {
+          aBuilder.Append("\n");
+        }
+      }
+    }
+  }*/
+}
+
+static inline bool
+ShouldEscape(nsIContent* aParent)
+{
+  if (!aParent || !aParent->IsHTML()) {
+    return true;
+  }
+
+  static const nsIAtom* nonEscapingElements[] = {
+    nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
+    nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
+    nsGkAtoms::plaintext, 
+    // Per the current spec noscript should be escaped in case
+    // scripts are disabled or if document doesn't have
+    // browsing context. However the latter seems to be a spec bug
+    // and Gecko hasn't traditionally done the former.
+    nsGkAtoms::noscript    
+  };
+  static mozilla::BloomFilter<12, nsIAtom> sFilter;
+  static bool sInitialized = false;
+  if (!sInitialized) {
+    sInitialized = true;
+    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+      sFilter.add(nonEscapingElements[i]);
+    }
+  }
+
+  nsIAtom* tag = aParent->Tag();
+  if (sFilter.mightContain(tag)) {
+    for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
+      if (tag == nonEscapingElements[i]) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+static inline bool
+IsVoidTag(Element* aElement)
+{
+  if (!aElement->IsHTML()) {
+    return false;
+  }
+
+  static const nsIAtom* voidElements[] = {
+    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
+    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
+    nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
+    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
+    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
+    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
+    nsGkAtoms::wbr
+  };
+
+  static mozilla::BloomFilter<12, nsIAtom> sFilter;
+  static bool sInitialized = false;
+  if (!sInitialized) {
+    sInitialized = true;
+    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
+      sFilter.add(voidElements[i]);
+    }
+  }
+  
+  nsIAtom* tag = aElement->Tag();
+  if (sFilter.mightContain(tag)) {
+    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
+      if (tag == voidElements[i]) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+static bool
+Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
+{
+  nsINode* current = aDescendentsOnly ?
+    nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
+
+  if (!current) {
+    return true;
+  }
+
+  StringBuilder builder;
+  nsIContent* next;
+  while (true) {
+    bool isVoid = false;
+    switch (current->NodeType()) {
+      case nsIDOMNode::ELEMENT_NODE: {
+        Element* elem = current->AsElement();
+        StartElement(elem, builder);
+        isVoid = IsVoidTag(elem);
+        if (!isVoid &&
+            (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
+          current = next;
+          continue;
+        }
+        break;
+      }
+
+      case nsIDOMNode::TEXT_NODE:
+      case nsIDOMNode::CDATA_SECTION_NODE: {
+        const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
+        nsIContent* parent = current->GetParent();
+        if (ShouldEscape(parent)) {
+          AppendEncodedCharacters(text, builder);
+        } else {
+          builder.Append(text);
+        }
+        break;
+      }
+
+      case nsIDOMNode::COMMENT_NODE: {
+        builder.Append("<!--");
+        builder.Append(static_cast<nsIContent*>(current)->GetText());
+        builder.Append("-->");
+        break;
+      }
+
+      case nsIDOMNode::DOCUMENT_TYPE_NODE: {
+        builder.Append("<!DOCTYPE ");
+        builder.Append(current->NodeName());
+        builder.Append(">");
+        break;
+      }
+
+      case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
+        builder.Append("<?");
+        builder.Append(current->NodeName());
+        builder.Append(" ");
+        builder.Append(static_cast<nsIContent*>(current)->GetText());
+        builder.Append(">");
+        break;
+      }
+    }
+
+    while (true) {
+      if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
+        builder.Append("</");
+        nsIContent* elem = static_cast<nsIContent*>(current);
+        if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
+          builder.Append(elem->Tag());
+        } else {
+          builder.Append(current->NodeName());
+        }
+        builder.Append(">");
+      }
+      isVoid = false;
+
+      if (current == aRoot) {
+        return builder.ToString(aOut);
+      }
+
+      if ((next = current->GetNextSibling())) {
+        current = next;
+        break;
+      }
+
+      current = current->GetParentNode();
+
+      // Template case, if we are in a template's content, then the parent
+      // should be the host template element.
+      if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
+        nsIContent* fragHost = frag->GetHost();
+        if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
+          current = fragHost;
+        }
+      }
+
+      if (aDescendentsOnly && current == aRoot) {
+        return builder.ToString(aOut);
+      }
+    }
+  }
+}
+
+void
+FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
+{
+  aMarkup.Truncate();
+
+  nsIDocument* doc = OwnerDoc();
+  if (IsInHTMLDocument()) {
+    Serialize(this, !aIncludeSelf, aMarkup);
+    return;
+  }
+
+  nsAutoString contentType;
+  doc->GetContentType(contentType);
+  bool tryToCacheEncoder = !aIncludeSelf;
+
+  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
+  if (!docEncoder) {
+    docEncoder =
+      do_CreateInstance(PromiseFlatCString(
+        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
+        NS_ConvertUTF16toUTF8(contentType)
+      ).get());
+  }
+  if (!docEncoder) {
+    // This could be some type for which we create a synthetic document.  Try
+    // again as XML
+    contentType.AssignLiteral("application/xml");
+    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
+    // Don't try to cache the encoder since it would point to a different
+    // contentType once it has been reinitialized.
+    tryToCacheEncoder = false;
+  }
+
+  NS_ENSURE_TRUE_VOID(docEncoder);
+
+  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
+                   // Output DOM-standard newlines
+                   nsIDocumentEncoder::OutputLFLineBreak |
+                   // Don't do linebreaking that's not present in
+                   // the source
+                   nsIDocumentEncoder::OutputRaw |
+                   // Only check for mozdirty when necessary (bug 599983)
+                   nsIDocumentEncoder::OutputIgnoreMozDirty;
+
+  if (IsEditable()) {
+    nsCOMPtr<Element> elem = do_QueryInterface(this);
+    nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
+    if (editor && editor->OutputsMozDirty()) {
+      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
+    }
+  }
+
+  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  if (aIncludeSelf) {
+    docEncoder->SetNativeNode(this);
+  } else {
+    docEncoder->SetNativeContainerNode(this);
+  }
+  rv = docEncoder->EncodeToString(aMarkup);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  if (tryToCacheEncoder) {
+    doc->SetCachedEncoder(docEncoder.forget());
+  }
+}
+
+void
+FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
+{
+  FragmentOrElement* target = this;
+  // Handle template case.
+  if (nsNodeUtils::IsTemplateElement(target)) {
+    DocumentFragment* frag =
+      static_cast<HTMLTemplateElement*>(target)->Content();
+    MOZ_ASSERT(frag);
+    target = frag;
+  }
+
+  nsIDocument* doc = target->OwnerDoc();
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nullptr);
+
+  target->FireNodeRemovedForChildren();
+
+  // Needed when innerHTML is used in combination with contenteditable
+  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
+
+  // Remove childnodes.
+  uint32_t childCount = target->GetChildCount();
+  nsAutoMutationBatch mb(target, true, false);
+  for (uint32_t i = 0; i < childCount; ++i) {
+    target->RemoveChildAt(0, true);
+  }
+  mb.RemovalDone();
+
+  nsAutoScriptLoaderDisabler sld(doc);
+
+  nsIAtom* contextLocalName = Tag();
+  int32_t contextNameSpaceID = GetNameSpaceID();
+
+  ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
+  if (shadowRoot) {
+    // Fix up the context to be the host of the ShadowRoot.
+    contextLocalName = shadowRoot->GetHost()->Tag();
+    contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
+  }
+
+  if (doc->IsHTML()) {
+    int32_t oldChildCount = target->GetChildCount();
+    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
+                                               target,
+                                               contextLocalName,
+                                               contextNameSpaceID,
+                                               doc->GetCompatibilityMode() ==
+                                                 eCompatibility_NavQuirks,
+                                               true);
+    mb.NodesAdded();
+    // HTML5 parser has notified, but not fired mutation events.
+    nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
+                                                       oldChildCount);
+  } else {
+    nsCOMPtr<nsIDOMDocumentFragment> df;
+    aError = nsContentUtils::CreateContextualFragment(target, aInnerHTML,
+                                                      true,
+                                                      getter_AddRefs(df));
+    nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
+    if (!aError.Failed()) {
+      // Suppress assertion about node removal mutation events that can't have
+      // listeners anyway, because no one has had the chance to register mutation
+      // listeners on the fragment that comes from the parser.
+      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
+
+      static_cast<nsINode*>(target)->AppendChild(*fragment, aError);
+      mb.NodesAdded();
+    }
+  }
+}
+
 nsINode::nsSlots*
 FragmentOrElement::CreateSlots()
 {
   return new nsDOMSlots();
 }
 
 void
 FragmentOrElement::FireNodeRemovedForChildren()
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.cpp
@@ -0,0 +1,590 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/ShadowRootBinding.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "ChildIterator.h"
+#include "nsContentUtils.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsIStyleSheetLinkingElement.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLContentElement.h"
+#include "nsXBLPrototypeBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static PLDHashOperator
+IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
+{
+  nsCycleCollectionTraversalCallback *cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aArg);
+  aEntry->Traverse(cb);
+  return PL_DHASH_NEXT;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot,
+                                                  DocumentFragment)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHost)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetList)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssociatedBinding)
+  tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
+                                                DocumentFragment)
+  if (tmp->mHost) {
+    tmp->mHost->RemoveMutationObserver(tmp);
+  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHost)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheetList)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAssociatedBinding)
+  tmp->mIdentifierMap.Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+DOMCI_DATA(ShadowRoot, ShadowRoot)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRoot)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
+  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
+
+NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
+NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
+
+ShadowRoot::ShadowRoot(nsIContent* aContent,
+                       already_AddRefed<nsINodeInfo> aNodeInfo,
+                       nsXBLPrototypeBinding* aProtoBinding)
+  : DocumentFragment(aNodeInfo), mHost(aContent),
+    mProtoBinding(aProtoBinding), mInsertionPointChanged(false)
+{
+  SetHost(aContent);
+  SetFlags(NODE_IS_IN_SHADOW_TREE);
+  // ShadowRoot isn't really in the document but it behaves like it is.
+  SetInDocument();
+  DOMSlots()->mBindingParent = aContent;
+  DOMSlots()->mContainingShadow = this;
+
+  // Add the ShadowRoot as a mutation observer on the host to watch
+  // for mutations because the insertion points in this ShadowRoot
+  // may need to be updated when the host children are modified.
+  mHost->AddMutationObserver(this);
+}
+
+ShadowRoot::~ShadowRoot()
+{
+  if (mHost) {
+    // mHost may have been unlinked.
+    mHost->RemoveMutationObserver(this);
+  }
+
+  ClearInDocument();
+  SetHost(nullptr);
+}
+
+JSObject*
+ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return mozilla::dom::ShadowRootBinding::Wrap(aCx, aScope, this);
+}
+
+ShadowRoot*
+ShadowRoot::FromNode(nsINode* aNode)
+{
+  if (aNode->HasFlag(NODE_IS_IN_SHADOW_TREE) && !aNode->GetParentNode()) {
+    MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
+               "ShadowRoot is a document fragment.");
+    return static_cast<ShadowRoot*>(aNode);
+  }
+
+  return nullptr;
+}
+
+void
+ShadowRoot::Restyle()
+{
+  mProtoBinding->FlushSkinSheets();
+
+  nsIPresShell* shell = OwnerDoc()->GetShell();
+  if (shell) {
+    OwnerDoc()->BeginUpdate(UPDATE_STYLE);
+    shell->RestyleShadowRoot(this);
+    OwnerDoc()->EndUpdate(UPDATE_STYLE);
+  }
+}
+
+void
+ShadowRoot::InsertSheet(nsCSSStyleSheet* aSheet,
+                        nsIContent* aLinkingContent)
+{
+  nsCOMPtr<nsIStyleSheetLinkingElement>
+    linkingElement = do_QueryInterface(aLinkingContent);
+  MOZ_ASSERT(linkingElement, "The only styles in a ShadowRoot should come "
+                             "from <style>.");
+
+  linkingElement->SetStyleSheet(aSheet); // This sets the ownerNode on the sheet
+
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mProtoBinding->GetOrCreateStyleSheets();
+  MOZ_ASSERT(sheets, "Style sheets array should never be null.");
+
+  // Find the correct position to insert into the style sheet list (must
+  // be in tree order).
+  for (uint32_t i = 0; i <= sheets->Length(); i++) {
+    if (i == sheets->Length()) {
+      sheets->AppendElement(aSheet);
+      break;
+    }
+
+    nsINode* sheetOwnerNode = sheets->ElementAt(i)->GetOwnerNode();
+    if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwnerNode)) {
+      sheets->InsertElementAt(i, aSheet);
+      break;
+    }
+  }
+
+  Restyle();
+}
+
+void
+ShadowRoot::RemoveSheet(nsCSSStyleSheet* aSheet)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mProtoBinding->GetOrCreateStyleSheets();
+  MOZ_ASSERT(sheets, "Style sheets array should never be null.");
+
+  DebugOnly<bool> found = sheets->RemoveElement(aSheet);
+  MOZ_ASSERT(found, "Trying to remove a sheet from a ShadowRoot "
+                    "that does not exist.");
+
+  Restyle();
+}
+
+Element*
+ShadowRoot::GetElementById(const nsAString& aElementId)
+{
+  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
+  return entry ? entry->GetIdElement() : nullptr;
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByTagName(const nsAString& aTagName)
+{
+  return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName);
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
+                                   const nsAString& aLocalName)
+{
+  int32_t nameSpaceId = kNameSpaceID_Wildcard;
+
+  if (!aNamespaceURI.EqualsLiteral("*")) {
+    nsresult rv =
+      nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
+                                                            nameSpaceId);
+    NS_ENSURE_SUCCESS(rv, nullptr);
+  }
+
+  NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
+
+  return NS_GetContentList(this, nameSpaceId, aLocalName);
+}
+
+void
+ShadowRoot::AddToIdTable(Element* aElement, nsIAtom* aId)
+{
+  nsIdentifierMapEntry *entry =
+    mIdentifierMap.PutEntry(nsDependentAtomString(aId));
+  if (entry) {
+    entry->AddIdElement(aElement);
+  }
+}
+
+void
+ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId)
+{
+  nsIdentifierMapEntry *entry =
+    mIdentifierMap.GetEntry(nsDependentAtomString(aId));
+  if (entry) {
+    entry->RemoveIdElement(aElement);
+    if (entry->IsEmpty()) {
+      mIdentifierMap.RawRemoveEntry(entry);
+    }
+  }
+}
+
+already_AddRefed<nsContentList>
+ShadowRoot::GetElementsByClassName(const nsAString& aClasses)
+{
+  return nsContentUtils::GetElementsByClassName(this, aClasses);
+}
+
+bool
+ShadowRoot::PrefEnabled()
+{
+  return Preferences::GetBool("dom.webcomponents.enabled", false);
+}
+
+class TreeOrderComparator {
+public:
+  bool Equals(nsINode* aElem1, nsINode* aElem2) const {
+    return aElem1 == aElem2;
+  }
+  bool LessThan(nsINode* aElem1, nsINode* aElem2) const {
+    return nsContentUtils::PositionIsBefore(aElem1, aElem2);
+  }
+};
+
+void
+ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint)
+{
+  TreeOrderComparator comparator;
+  mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator);
+}
+
+void
+ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
+{
+  mInsertionPoints.RemoveElement(aInsertionPoint);
+}
+
+void
+ShadowRoot::DistributeSingleNode(nsIContent* aContent)
+{
+  // Find the insertion point to which the content belongs.
+  HTMLContentElement* insertionPoint = nullptr;
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    if (mInsertionPoints[i]->Match(aContent)) {
+      // Matching may cause the insertion point to drop fallback content.
+      if (mInsertionPoints[i]->MatchedNodes().IsEmpty() &&
+          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+        // This match will cause the insertion point to drop all fallback
+        // content and used matched nodes instead. Give up on the optimization
+        // and just distribute all nodes.
+        DistributeAllNodes();
+        return;
+      }
+      insertionPoint = mInsertionPoints[i];
+      break;
+    }
+  }
+
+  // Find the index into the insertion point.
+  if (insertionPoint) {
+    nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes();
+    // Find the appropriate position in the matched node list for the
+    // newly distributed content.
+    bool isIndexFound = false;
+    ExplicitChildIterator childIterator(GetHost());
+    for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
+      // Seek through the host's explicit children until the inserted content
+      // is found or when the current matched node is reached.
+      if (childIterator.Seek(aContent, matchedNodes[i])) {
+        // aContent was found before the current matched node.
+        matchedNodes.InsertElementAt(i, aContent);
+        isIndexFound = true;
+        break;
+      }
+    }
+
+    if (!isIndexFound) {
+      // We have still not found an index in the insertion point,
+      // thus it must be at the end.
+      MOZ_ASSERT(childIterator.Seek(aContent),
+                 "Trying to match a node that is not a candidate to be matched");
+      matchedNodes.AppendElement(aContent);
+    }
+
+    // Handle the case where the parent of the insertion point has a ShadowRoot.
+    // The node distributed into the insertion point must be reprojected to the
+    // insertion points of the parent's ShadowRoot.
+    ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+    if (parentShadow) {
+      parentShadow->DistributeSingleNode(aContent);
+    }
+  }
+}
+
+void
+ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
+{
+  // Find insertion point containing the content and remove the node.
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
+      // Removing the matched node may cause the insertion point to use
+      // fallback content.
+      if (mInsertionPoints[i]->MatchedNodes().Length() == 1 &&
+          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+        // Removing the matched node will cause fallback content to be
+        // used instead. Give up optimization and distribute all nodes.
+        DistributeAllNodes();
+        return;
+      }
+
+      mInsertionPoints[i]->MatchedNodes().RemoveElement(aContent);
+
+      // Handle the case where the parent of the insertion point has a ShadowRoot.
+      // The removed node needs to be removed from the insertion points of the
+      // parent's ShadowRoot.
+      ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot();
+      if (parentShadow) {
+        parentShadow->RemoveDistributedNode(aContent);
+      }
+
+      break;
+    }
+  }
+}
+
+void
+ShadowRoot::DistributeAllNodes()
+{
+  // Create node pool.
+  nsTArray<nsIContent*> nodePool;
+  ExplicitChildIterator childIterator(GetHost());
+  for (nsIContent* content = childIterator.GetNextChild();
+       content;
+       content = childIterator.GetNextChild()) {
+    nodePool.AppendElement(content);
+  }
+
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    mInsertionPoints[i]->ClearMatchedNodes();
+    // Assign matching nodes from node pool.
+    for (uint32_t j = 0; j < nodePool.Length(); j++) {
+      if (mInsertionPoints[i]->Match(nodePool[j])) {
+        mInsertionPoints[i]->MatchedNodes().AppendElement(nodePool[j]);
+        nodePool[j]->SetXBLInsertionParent(mInsertionPoints[i]);
+        nodePool.RemoveElementAt(j--);
+      }
+    }
+  }
+
+  // Distribute nodes in outer ShadowRoot for the case of an insertion point being
+  // distributed into an outer ShadowRoot.
+  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
+    nsIContent* insertionParent = mInsertionPoints[i]->GetParent();
+    MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
+                                "mInsertionPoints array is to be a descendant of a"
+                                "ShadowRoot, in which case, it should have a parent");
+
+    // If the parent of the insertion point has as ShadowRoot, the nodes distributed
+    // to the insertion point must be reprojected to the insertion points of the
+    // parent's ShadowRoot.
+    ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
+    if (parentShadow) {
+      parentShadow->DistributeAllNodes();
+    }
+  }
+}
+
+void
+ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
+{
+  GetMarkup(false, aInnerHTML);
+}
+
+void
+ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
+{
+  SetInnerHTMLInternal(aInnerHTML, aError);
+}
+
+bool
+ShadowRoot::ApplyAuthorStyles()
+{
+  return mProtoBinding->InheritsStyle();
+}
+
+void
+ShadowRoot::SetApplyAuthorStyles(bool aApplyAuthorStyles)
+{
+  mProtoBinding->SetInheritsStyle(aApplyAuthorStyles);
+
+  nsIPresShell* shell = OwnerDoc()->GetShell();
+  if (shell) {
+    OwnerDoc()->BeginUpdate(UPDATE_STYLE);
+    shell->RestyleShadowRoot(this);
+    OwnerDoc()->EndUpdate(UPDATE_STYLE);
+  }
+}
+
+nsIDOMStyleSheetList*
+ShadowRoot::StyleSheets()
+{
+  if (!mStyleSheetList) {
+    mStyleSheetList = new ShadowRootStyleSheetList(this);
+  }
+
+  return mStyleSheetList;
+}
+
+/**
+ * Returns whether the web components pool population algorithm
+ * on the host would contain |aContent|. This function ignores
+ * insertion points in the pool, thus should only be used to
+ * test nodes that have not yet been distributed.
+ */
+static bool
+IsPooledNode(nsIContent* aContent, nsIContent* aContainer, nsIContent* aHost)
+{
+  if (nsContentUtils::IsContentInsertionPoint(aContent)) {
+    // Insertion points never end up in the pool.
+    return false;
+  }
+
+  if (aContainer->IsHTML(nsGkAtoms::content)) {
+    // Fallback content will end up in pool if its parent is a child of the host.
+    HTMLContentElement* content = static_cast<HTMLContentElement*>(aContainer);
+    return content->IsInsertionPoint() && content->MatchedNodes().IsEmpty() &&
+           aContainer->GetParentNode() == aHost;
+  }
+
+  if (aContainer == aHost) {
+    // Any other child nodes of the host will end up in the pool.
+    return true;
+  }
+
+  return false;
+}
+
+void
+ShadowRoot::AttributeChanged(nsIDocument* aDocument,
+                             Element* aElement,
+                             int32_t aNameSpaceID,
+                             nsIAtom* aAttribute,
+                             int32_t aModType)
+{
+  // Watch for attribute changes to nodes in the pool because
+  // insertion points can select on attributes.
+  if (!IsPooledNode(aElement, aElement->GetParent(), mHost)) {
+    return;
+  }
+
+  // Attributes may change insertion point matching, find its new distribution.
+  RemoveDistributedNode(aElement);
+  DistributeSingleNode(aElement);
+}
+
+void
+ShadowRoot::ContentAppended(nsIDocument* aDocument,
+                            nsIContent* aContainer,
+                            nsIContent* aFirstNewContent,
+                            int32_t aNewIndexInContainer)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for new nodes added to the pool because the node
+  // may need to be added to an insertion point.
+  nsIContent* currentChild = aFirstNewContent;
+  while (currentChild) {
+    if (IsPooledNode(currentChild, aContainer, mHost)) {
+      DistributeSingleNode(currentChild);
+    }
+    currentChild = currentChild->GetNextSibling();
+  }
+}
+
+void
+ShadowRoot::ContentInserted(nsIDocument* aDocument,
+                            nsIContent* aContainer,
+                            nsIContent* aChild,
+                            int32_t aIndexInContainer)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for new nodes added to the pool because the node
+  // may need to be added to an insertion point.
+  if (IsPooledNode(aChild, aContainer, mHost)) {
+    DistributeSingleNode(aChild);
+  }
+}
+
+void
+ShadowRoot::ContentRemoved(nsIDocument* aDocument,
+                           nsIContent* aContainer,
+                           nsIContent* aChild,
+                           int32_t aIndexInContainer,
+                           nsIContent* aPreviousSibling)
+{
+  if (mInsertionPointChanged) {
+    DistributeAllNodes();
+    mInsertionPointChanged = false;
+    return;
+  }
+
+  // Watch for node that is removed from the pool because
+  // it may need to be removed from an insertion point.
+  if (IsPooledNode(aChild, aContainer, mHost)) {
+    RemoveDistributedNode(aChild);
+  }
+}
+
+NS_IMPL_CYCLE_COLLECTION_1(ShadowRootStyleSheetList, mShadowRoot)
+
+NS_INTERFACE_TABLE_HEAD(ShadowRootStyleSheetList)
+  NS_INTERFACE_TABLE1(ShadowRootStyleSheetList, nsIDOMStyleSheetList)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(ShadowRootStyleSheetList)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StyleSheetList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ShadowRootStyleSheetList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ShadowRootStyleSheetList)
+
+ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot)
+  : mShadowRoot(aShadowRoot)
+{
+  MOZ_COUNT_CTOR(ShadowRootStyleSheetList);
+}
+
+ShadowRootStyleSheetList::~ShadowRootStyleSheetList()
+{
+  MOZ_COUNT_DTOR(ShadowRootStyleSheetList);
+}
+
+NS_IMETHODIMP
+ShadowRootStyleSheetList::Item(uint32_t aIndex, nsIDOMStyleSheet** aReturn)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mShadowRoot->mProtoBinding->GetStyleSheets();
+
+  if (sheets) {
+    NS_IF_ADDREF(*aReturn = sheets->SafeElementAt(aIndex));
+  } else {
+    *aReturn = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ShadowRootStyleSheetList::GetLength(uint32_t* aLength)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
+    mShadowRoot->mProtoBinding->GetStyleSheets();
+
+  if (sheets) {
+    *aLength = sheets->Length();
+  } else {
+    *aLength = 0;
+  }
+
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_shadowroot_h__
+#define mozilla_dom_shadowroot_h__
+
+#include "mozilla/dom/DocumentFragment.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashtable.h"
+#include "nsDocument.h"
+
+class nsIAtom;
+class nsIContent;
+class nsIDocument;
+class nsINodeInfo;
+class nsPIDOMWindow;
+class nsXBLPrototypeBinding;
+class nsTagNameMapEntry;
+
+namespace mozilla {
+namespace dom {
+
+class Element;
+class HTMLContentElement;
+class ShadowRootStyleSheetList;
+
+class ShadowRoot : public DocumentFragment,
+                   public nsStubMutationObserver
+{
+  friend class ShadowRootStyleSheetList;
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
+                                           DocumentFragment)
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+
+  ShadowRoot(nsIContent* aContent, already_AddRefed<nsINodeInfo> aNodeInfo,
+             nsXBLPrototypeBinding* aProtoBinding);
+  virtual ~ShadowRoot();
+
+  void AddToIdTable(Element* aElement, nsIAtom* aId);
+  void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
+  static bool PrefEnabled();
+  void InsertSheet(nsCSSStyleSheet* aSheet, nsIContent* aLinkingContent);
+  void RemoveSheet(nsCSSStyleSheet* aSheet);
+  bool ApplyAuthorStyles();
+  void SetApplyAuthorStyles(bool aApplyAuthorStyles);
+  nsIDOMStyleSheetList* StyleSheets();
+
+  /**
+   * Distributes a single explicit child of the host to the content
+   * insertion points in this ShadowRoot.
+   */
+  void DistributeSingleNode(nsIContent* aContent);
+
+  /**
+   * Removes a single explicit child of the host from the content
+   * insertion points in this ShadowRoot.
+   */
+  void RemoveDistributedNode(nsIContent* aContent);
+
+  /**
+   * Distributes all the explicit children of the host to the content
+   * insertion points in this ShadowRoot.
+   */
+  void DistributeAllNodes();
+
+  void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
+  void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
+
+  void SetInsertionPointChanged() { mInsertionPointChanged = true; }
+
+  void SetAssociatedBinding(nsXBLBinding* aBinding)
+  {
+    mAssociatedBinding = aBinding;
+  }
+
+  nsISupports* GetParentObject() const
+  {
+    return mHost;
+  }
+
+  nsIContent* GetHost() { return mHost; }
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  static ShadowRoot* FromNode(nsINode* aNode);
+
+  // WebIDL methods.
+  Element* GetElementById(const nsAString& aElementId);
+  already_AddRefed<nsContentList>
+    GetElementsByTagName(const nsAString& aNamespaceURI);
+  already_AddRefed<nsContentList>
+    GetElementsByTagNameNS(const nsAString& aNamespaceURI,
+                           const nsAString& aLocalName);
+  already_AddRefed<nsContentList>
+    GetElementsByClassName(const nsAString& aClasses);
+  void GetInnerHTML(nsAString& aInnerHTML);
+  void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
+protected:
+  void Restyle();
+
+  nsCOMPtr<nsIContent> mHost;
+
+  // An array of content insertion points that are a descendant of the ShadowRoot
+  // sorted in tree order. Insertion points are responsible for notifying
+  // the ShadowRoot when they are removed or added as a descendant. The insertion
+  // points are kept alive by the parent node, thus weak references are held
+  // by the array.
+  nsTArray<HTMLContentElement*> mInsertionPoints;
+
+  nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
+  nsXBLPrototypeBinding* mProtoBinding;
+
+  // It is necessary to hold a reference to the associated nsXBLBinding
+  // because the binding holds a reference on the nsXBLDocumentInfo that
+  // owns |mProtoBinding|.
+  nsRefPtr<nsXBLBinding> mAssociatedBinding;
+
+  nsRefPtr<ShadowRootStyleSheetList> mStyleSheetList;
+
+  // A boolean that indicates that an insertion point was added or removed
+  // from this ShadowRoot and that the nodes need to be redistributed into
+  // the insertion points. After this flag is set, nodes will be distributed
+  // on the next mutation event.
+  bool mInsertionPointChanged;
+};
+
+class ShadowRootStyleSheetList : public nsIDOMStyleSheetList
+{
+public:
+  ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
+  virtual ~ShadowRootStyleSheetList();
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(ShadowRootStyleSheetList)
+
+  // nsIDOMStyleSheetList
+  NS_DECL_NSIDOMSTYLESHEETLIST
+
+protected:
+  nsRefPtr<ShadowRoot> mShadowRoot;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_shadowroot_h__
+
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -58,16 +58,17 @@ EXPORTS.mozilla.dom += [
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMImplementation.h',
     'DOMParser.h',
     'DOMRect.h',
     'EventSource.h',
     'Link.h',
     'NodeIterator.h',
+    'ShadowRoot.h',
     'Text.h',
     'TreeWalker.h',
 ]
 
 UNIFIED_SOURCES += [
     'Attr.cpp',
     'ChildIterator.cpp',
     'Comment.cpp',
@@ -144,16 +145,17 @@ UNIFIED_SOURCES += [
     'nsTextNode.cpp',
     'nsTraversal.cpp',
     'nsTreeSanitizer.cpp',
     'nsViewportInfo.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
+    'ShadowRoot.cpp',
     'Text.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
     'WebSocket.cpp',
 ]
 
 # These files cannot be built in unified mode because they use FORCE_PR_LOG
 SOURCES += [
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -22,25 +22,23 @@
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsIAtom.h"
 #include "nsGkAtoms.h"
-#include "nsIDOMWindow.h"
 #include "nsNetCID.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsICookieService.h"
-#include "nsIPrompt.h"
 #include "nsContentUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIAppShell.h"
 #include "nsIWidget.h"
 #include "nsWidgetsCID.h"
 #include "nsIDOMNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIWebNavigation.h"
@@ -285,18 +283,17 @@ nsContentSink::ProcessHeaderData(nsIAtom
   nsresult rv = NS_OK;
   // necko doesn't process headers coming in from the parser
 
   mDocument->SetHeaderData(aHeader, aValue);
 
   if (aHeader == nsGkAtoms::setcookie) {
     // Note: Necko already handles cookies set via the channel.  We can't just
     // call SetCookie on the channel because we want to do some security checks
-    // here and want to use the prompt associated to our current window, not
-    // the window where the channel was dispatched.
+    // here.
     nsCOMPtr<nsICookieService> cookieServ =
       do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Get a URI from the document principal
 
@@ -304,29 +301,23 @@ nsContentSink::ProcessHeaderData(nsIAtom
     // by SetDomain
 
     // Note that a non-codebase principal (eg the system principal) will return
     // a null URI.
     nsCOMPtr<nsIURI> codebaseURI;
     rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
     NS_ENSURE_TRUE(codebaseURI, rv);
 
-    nsCOMPtr<nsIPrompt> prompt;
-    nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
-    if (window) {
-      window->GetPrompter(getter_AddRefs(prompt));
-    }
-
     nsCOMPtr<nsIChannel> channel;
     if (mParser) {
       mParser->GetChannel(getter_AddRefs(channel));
     }
 
     rv = cookieServ->SetCookieString(codebaseURI,
-                                     prompt,
+                                     nullptr,
                                      NS_ConvertUTF16toUTF8(aValue).get(),
                                      channel);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
   else if (aHeader == nsGkAtoms::msthemecompatible) {
     // Disable theming for the presshell if the value is no.
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -31,17 +31,19 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Base64.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/TextDecoder.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/MutationEvent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Selection.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/Util.h"
 #include "nsAString.h"
@@ -4324,19 +4326,27 @@ nsContentUtils::IsInSameAnonymousTree(co
      * nodes in the same anonymous subtree as it will have a null
      * bindingParent.
      *
      * XXXbz strictly speaking, that's not true for attribute nodes.
      */
     return aContent->GetBindingParent() == nullptr;
   }
 
-  return static_cast<const nsIContent*>(aNode)->GetBindingParent() ==
-         aContent->GetBindingParent();
- 
+  const nsIContent* nodeAsContent = static_cast<const nsIContent*>(aNode);
+
+  // For nodes in a shadow tree, it is insufficient to simply compare
+  // the binding parent because a node may host multiple ShadowRoots,
+  // thus nodes in different shadow tree may have the same binding parent.
+  if (aNode->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    return nodeAsContent->GetContainingShadow() ==
+      aContent->GetContainingShadow();
+  }
+
+  return nodeAsContent->GetBindingParent() == aContent->GetBindingParent();
 }
 
 class AnonymousContentDestroyer : public nsRunnable {
 public:
   AnonymousContentDestroyer(nsCOMPtr<nsIContent>* aContent) {
     mContent.swap(*aContent);
     mParent = mContent->GetParent();
     mDoc = mContent->OwnerDoc();
@@ -6391,16 +6401,39 @@ nsContentUtils::HasPluginWithUncontrolle
   return false;
 #endif
   bool result = false;
   CheckForWindowedPlugins(aContent, &result);
   return result;
 }
 
 /* static */
+void
+nsContentUtils::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
+                                                   nsIContent* aDest,
+                                                   int32_t aOldChildCount)
+{
+  // Fire mutation events. Optimize for the case when there are no listeners
+  int32_t newChildCount = aDest->GetChildCount();
+  if (newChildCount && nsContentUtils::
+        HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
+    nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
+    NS_ASSERTION(newChildCount - aOldChildCount >= 0,
+                 "What, some unexpected dom mutation has happened?");
+    childNodes.SetCapacity(newChildCount - aOldChildCount);
+    for (nsIContent* child = aDest->GetFirstChild();
+         child;
+         child = child->GetNextSibling()) {
+      childNodes.AppendElement(child);
+    }
+    FragmentOrElement::FireNodeInserted(aDoc, aDest, childNodes);
+  }
+}
+
+/* static */
 nsIDocument*
 nsContentUtils::GetFullscreenAncestor(nsIDocument* aDoc)
 {
   nsIDocument* doc = aDoc;
   while (doc) {
     if (doc->IsFullScreenDoc()) {
       return doc;
     }
@@ -6553,16 +6586,32 @@ nsContentUtils::InternalIsSupported(nsIS
            nsSVGFeatures::HasFeature(aObject, aFeature);
   }
 
   // Otherwise, we claim to support everything
   return true;
 }
 
 bool
+nsContentUtils::IsContentInsertionPoint(const nsIContent* aContent)
+{
+  // Check if the content is a XBL insertion point.
+  if (aContent->IsActiveChildrenElement()) {
+    return true;
+  }
+
+  // Check if the content is a web components content insertion point.
+  if (aContent->IsHTML(nsGkAtoms::content)) {
+    return static_cast<const HTMLContentElement*>(aContent)->IsInsertionPoint();
+  }
+
+  return false;
+}
+
+bool
 nsContentUtils::DOMWindowDumpEnabled()
 {
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   // In optimized builds we check a pref that controls if we should
   // enable output from dump() or not, in debug builds it's always
   // enabled.
   return nsContentUtils::sDOMWindowDumpEnabled;
 #else
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -167,16 +167,17 @@
 #include "nsRefreshDriver.h"
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
 #include "nsCSPService.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "mozilla/dom/DOMImplementation.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Comment.h"
 #include "nsTextNode.h"
 #include "mozilla/dom/Link.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "nsXULAppAPI.h"
 #include "nsDOMTouchEvent.h"
 #include "mozilla/dom/Touch.h"
 #include "DictionaryHelpers.h"
@@ -428,20 +429,20 @@ nsIdentifierMapEntry::SetImageElement(El
   mImageElement = aElement;
   Element* newElement = GetImageIdElement();
   if (oldElement != newElement) {
     FireChangeCallbacks(oldElement, newElement, true);
   }
 }
 
 void
-nsIdentifierMapEntry::AddNameElement(nsIDocument* aDocument, Element* aElement)
+nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
 {
   if (!mNameContentList) {
-    mNameContentList = new nsSimpleContentList(aDocument);
+    mNameContentList = new nsSimpleContentList(aNode);
   }
 
   mNameContentList->AppendElement(aElement);
 }
 
 void
 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
 {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -129,17 +129,17 @@ public:
   }
   nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) :
     nsStringHashKey(&aOther.GetKey())
   {
     NS_ERROR("Should never be called");
   }
   ~nsIdentifierMapEntry();
 
-  void AddNameElement(nsIDocument* aDocument, Element* aElement);
+  void AddNameElement(nsINode* aDocument, Element* aElement);
   void RemoveNameElement(Element* aElement);
   bool IsEmpty();
   nsBaseContentList* GetNameContentList() {
     return mNameContentList;
   }
   bool HasNameElement() const {
     return mNameContentList && mNameContentList->Length() != 0;
   }
@@ -274,32 +274,16 @@ public:
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
   nsIStyleSheet* GetItemAt(uint32_t aIndex);
 
-  static nsDOMStyleSheetList* FromSupports(nsISupports* aSupports)
-  {
-    nsIDOMStyleSheetList* list = static_cast<nsIDOMStyleSheetList*>(aSupports);
-#ifdef DEBUG
-    {
-      nsCOMPtr<nsIDOMStyleSheetList> list_qi = do_QueryInterface(aSupports);
-
-      // If this assertion fires the QI implementation for the object in
-      // question doesn't use the nsIDOMStyleSheetList pointer as the
-      // nsISupports pointer. That must be fixed, or we'll crash...
-      NS_ASSERTION(list_qi == list, "Uh, fix QI!");
-    }
-#endif
-    return static_cast<nsDOMStyleSheetList*>(list);
-  }
-
 protected:
   int32_t       mLength;
   nsIDocument*  mDocument;
 };
 
 class nsOnloadBlocker MOZ_FINAL : public nsIRequest
 {
 public:
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -44,16 +44,17 @@
 #include "nsContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsIDocShell.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
@@ -103,17 +104,26 @@ protected:
 
   nsresult FlushText(nsAString& aString, bool aForce);
 
   bool IsVisibleNode(nsINode* aNode)
   {
     NS_PRECONDITION(aNode, "");
 
     if (mFlags & SkipInvisibleContent) {
-      nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+      // Treat the visibility of the ShadowRoot as if it were
+      // the host content.
+      nsCOMPtr<nsIContent> content;
+      ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode);
+      if (shadowRoot) {
+        content = shadowRoot->GetHost();
+      } else {
+        content = do_QueryInterface(aNode);
+      }
+
       if (content) {
         nsIFrame* frame = content->GetPrimaryFrame();
         if (!frame) {
           if (aNode->IsNodeOfType(nsINode::eTEXT)) {
             // We have already checked that our parent is visible.
             return true;
           }
           return false;
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -8,16 +8,17 @@
  * nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes.
  */
 
 #include "mozilla/DebugOnly.h"
 
 #include "nsGenericDOMDataNode.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsIDocument.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMDocument.h"
 #include "nsReadableUtils.h"
 #include "mozilla/MutationEvent.h"
 #include "nsINameSpaceManager.h"
 #include "nsIURI.h"
 #include "nsIDOMEvent.h"
@@ -475,16 +476,23 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
                  "non-native anonymous parent!");
     DataSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens.
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
+    if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      SetFlags(NODE_IS_IN_SHADOW_TREE);
+    }
+    ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
+    if (parentContainingShadow) {
+      DataSlots()->mContainingShadow = parentContainingShadow;
+    }
   }
 
   // Set parent
   if (aParent) {
     if (!GetParent()) {
       NS_ADDREF(aParent);
     }
     mParent = aParent;
@@ -526,17 +534,20 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
   return NS_OK;
 }
 
 void
 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // Unset frame flags; if we need them again later, they'll get set again.
   UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
-             NS_REFRAME_IF_WHITESPACE);
+             NS_REFRAME_IF_WHITESPACE |
+             // Also unset the shadow tree flag because it can
+             // no longer be a descendant of a ShadowRoot.
+             NODE_IS_IN_SHADOW_TREE);
   
   nsIDocument *document = GetCurrentDoc();
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     // This is needed to update the insertion point.
     document->BindingManager()->RemovedFromDocument(this, document);
   }
@@ -552,16 +563,17 @@ nsGenericDOMDataNode::UnbindFromTree(boo
   ClearInDocument();
 
   // Begin keeping track of our subtree root.
   SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
 
   nsDataSlots *slots = GetExistingDataSlots();
   if (slots) {
     slots->mBindingParent = nullptr;
+    slots->mContainingShadow = nullptr;
   }
 
   nsNodeUtils::ParentChainChanged(this);
 }
 
 already_AddRefed<nsINodeList>
 nsGenericDOMDataNode::GetChildren(uint32_t aFilter)
 {
@@ -640,16 +652,34 @@ nsGenericDOMDataNode::RemoveChildAt(uint
 
 nsIContent *
 nsGenericDOMDataNode::GetBindingParent() const
 {
   nsDataSlots *slots = GetExistingDataSlots();
   return slots ? slots->mBindingParent : nullptr;
 }
 
+ShadowRoot *
+nsGenericDOMDataNode::GetShadowRoot() const
+{
+  return nullptr;
+}
+
+ShadowRoot *
+nsGenericDOMDataNode::GetContainingShadow() const
+{
+  nsDataSlots *slots = GetExistingDataSlots();
+  return slots ? slots->mContainingShadow : nullptr;
+}
+
+void
+nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot)
+{
+}
+
 nsXBLBinding *
 nsGenericDOMDataNode::GetXBLBinding() const
 {
   return nullptr;
 }
 
 void
 nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding,
@@ -720,27 +750,36 @@ nsGenericDOMDataNode::IsLink(nsIURI** aU
 }
 
 nsINode::nsSlots*
 nsGenericDOMDataNode::CreateSlots()
 {
   return new nsDataSlots();
 }
 
+nsGenericDOMDataNode::nsDataSlots::nsDataSlots()
+  : nsINode::nsSlots(), mBindingParent(nullptr)
+{
+}
+
 void
 nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
   cb.NoteXPCOMChild(mXBLInsertionParent.get());
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
+  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
 }
 
 void
 nsGenericDOMDataNode::nsDataSlots::Unlink()
 {
   mXBLInsertionParent = nullptr;
+  mContainingShadow = nullptr;
 }
 
 //----------------------------------------------------------------------
 
 // Implementation of the nsIDOMText interface
 
 nsresult
 nsGenericDOMDataNode::SplitData(uint32_t aOffset, nsIContent** aReturn,
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -153,16 +153,19 @@ public:
   virtual void List(FILE* out, int32_t aIndent) const MOZ_OVERRIDE;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE;
 #endif
 
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
   virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
+  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const MOZ_OVERRIDE;
+  virtual mozilla::dom::ShadowRoot *GetShadowRoot() const MOZ_OVERRIDE;
+  virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) MOZ_OVERRIDE;
   virtual nsIContent *GetXBLInsertionParent() const;
   virtual void SetXBLInsertionParent(nsIContent* aContent);
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual nsIAtom* DoGetID() const MOZ_OVERRIDE;
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
@@ -236,35 +239,36 @@ protected:
    * through the DOM. Rather than burn actual slots in the content
    * objects for each of these instance variables, we put them off
    * in a side structure that's only allocated when the content is
    * accessed through the DOM.
    */
   class nsDataSlots : public nsINode::nsSlots
   {
   public:
-    nsDataSlots()
-      : nsINode::nsSlots(),
-        mBindingParent(nullptr)
-    {
-    }
+    nsDataSlots();
 
     void Traverse(nsCycleCollectionTraversalCallback &cb);
     void Unlink();
 
     /**
      * The nearest enclosing content node with a binding that created us.
      * @see nsIContent::GetBindingParent
      */
     nsIContent* mBindingParent;  // [Weak]
 
     /**
      * @see nsIContent::GetXBLInsertionParent
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
+
+    /**
+     * @see nsIContent::GetContainingShadow
+     */
+    nsRefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
   };
 
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
 
   nsDataSlots* DataSlots()
   {
     return static_cast<nsDataSlots*>(Slots());
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1468,20 +1468,19 @@ GK_ATOM(onrepeatEvent, "onrepeatEvent")
 GK_ATOM(repeatCount, "repeatCount")
 GK_ATOM(repeatDur, "repeatDur")
 GK_ATOM(repeatEvent, "repeatEvent")
 GK_ATOM(restart, "restart")
 GK_ATOM(to, "to")
 GK_ATOM(XML, "XML")
 
 // internal MathML attributes: different from columnalign_, columnlines_,
-// fontstyle_, rowalign_ and rowlines_
+// rowalign_ and rowlines_
 GK_ATOM(_moz_math_columnalign_, "_moz-math-columnalign")
 GK_ATOM(_moz_math_columnline_, "_moz-math-columnline")
-GK_ATOM(_moz_math_fontstyle_, "_moz-math-font-style")
 GK_ATOM(_moz_math_rowalign_, "_moz-math-rowalign")
 GK_ATOM(_moz_math_rowline_, "_moz-math-rowline")
 
 GK_ATOM(abs_, "abs")
 GK_ATOM(accent_, "accent")
 GK_ATOM(accentunder_, "accentunder")
 GK_ATOM(actiontype_, "actiontype")
 GK_ATOM(alignmentscope_, "alignmentscope")
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -92,16 +92,17 @@
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "prprf.h"
 #include "xpcpublic.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsCSSParser.h"
 #include "HTMLLegendElement.h"
 #include "nsWrapperCacheInlines.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "WrapperFactory.h"
 #include "DocumentType.h"
 #include <algorithm>
 #include "nsDOMEvent.h"
 #include "nsGlobalWindow.h"
 #include "nsDOMMutationObserver.h"
 
 using namespace mozilla;
@@ -233,16 +234,25 @@ nsINode::GetTextEditorRootContent(nsIEdi
     return rootContent;
   }
   return nullptr;
 }
 
 static nsIContent* GetRootForContentSubtree(nsIContent* aContent)
 {
   NS_ENSURE_TRUE(aContent, nullptr);
+
+  // Special case for ShadowRoot because the ShadowRoot itself is
+  // the root. This is necessary to prevent selection from crossing
+  // the ShadowRoot boundary.
+  ShadowRoot* containingShadow = aContent->GetContainingShadow();
+  if (containingShadow) {
+    return containingShadow;
+  }
+
   nsIContent* stop = aContent->GetBindingParent();
   while (aContent) {
     nsIContent* parent = aContent->GetParent();
     if (parent == stop) {
       break;
     }
     aContent = parent;
   }
@@ -301,18 +311,27 @@ nsINode::GetSelectionRootContent(nsIPres
       if (!content)
         return nullptr;
     }
   }
 
   // This node might be in another subtree, if so, we should find this subtree's
   // root.  Otherwise, we can return the content simply.
   NS_ENSURE_TRUE(content, nullptr);
-  return nsContentUtils::IsInSameAnonymousTree(this, content) ?
-           content : GetRootForContentSubtree(static_cast<nsIContent*>(this));
+  if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
+    content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
+    // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
+    // Use the host as the root.
+    ShadowRoot* shadowRoot = ShadowRoot::FromNode(content);
+    if (shadowRoot) {
+      content = shadowRoot->GetHost();
+    }
+  }
+
+  return content;
 }
 
 nsINodeList*
 nsINode::ChildNodes()
 {
   nsSlots* slots = Slots();
   if (!slots->mChildNodes) {
     slots->mChildNodes = new nsChildContentList(this);
@@ -515,18 +534,21 @@ nsINode::Normalize()
   mozAutoSubtreeModified subtree(doc, nullptr);
 
   // Fire all DOMNodeRemoved events. Optimize the common case of there being
   // no listeners
   bool hasRemoveListeners = nsContentUtils::
       HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
   if (hasRemoveListeners) {
     for (uint32_t i = 0; i < nodes.Length(); ++i) {
-      nsContentUtils::MaybeFireNodeRemoved(nodes[i], nodes[i]->GetParentNode(),
-                                           doc);
+      nsINode* parentNode = nodes[i]->GetParentNode();
+      if (parentNode) { // Node may have already been removed.
+        nsContentUtils::MaybeFireNodeRemoved(nodes[i], parentNode,
+                                             doc);
+      }
     }
   }
 
   mozAutoDocUpdate batch(doc, UPDATE_CONTENT_MODEL, true);
 
   // Merge and remove all nodes
   nsAutoString tmpStr;
   for (uint32_t i = 0; i < nodes.Length(); ++i) {
@@ -1546,17 +1568,21 @@ bool IsAllowedAsChild(nsIContent* aNewCh
              "can't be parents!");
 
   // A common case is that aNewChild has no kids, in which case
   // aParent can't be a descendant of aNewChild unless they're
   // actually equal to each other.  Fast-path that case, since aParent
   // could be pretty deep in the DOM tree.
   if (aNewChild == aParent ||
       ((aNewChild->GetFirstChild() ||
-        aNewChild->Tag() == nsGkAtoms::_template) &&
+        // HTML template elements and ShadowRoot hosts need
+        // to be checked to ensure that they are not inserted into
+        // the hosted content.
+        aNewChild->Tag() == nsGkAtoms::_template ||
+        aNewChild->GetShadowRoot()) &&
        nsContentUtils::ContentIsHostIncludingDescendantOf(aParent,
                                                           aNewChild))) {
     return false;
   }
 
   // The allowed child nodes differ for documents and elements
   switch (aNewChild->NodeType()) {
   case nsIDOMNode::COMMENT_NODE :
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -43,16 +43,21 @@
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsEventDispatcher.h"
 #include "nsSVGEffects.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/dom/Element.h"
 
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
 using namespace mozilla;
 
 #ifdef DEBUG_chb
 static void PrintReqURL(imgIRequest* req) {
   if (!req) {
     printf("(null req)\n");
     return;
   }
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -27,16 +27,17 @@
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsDOMMutationObserver.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 using namespace mozilla::dom;
 using mozilla::AutoJSContext;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
   PR_BEGIN_MACRO                                                  \
@@ -53,17 +54,22 @@ using mozilla::AutoJSContext;
     nsINode::nsSlots* slots = node->GetExistingSlots();           \
     if (slots && !slots->mMutationObservers.IsEmpty()) {          \
       /* No need to explicitly notify the first observer first    \
          since that'll happen anyway. */                          \
       NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(                         \
         slots->mMutationObservers, nsIMutationObserver,           \
         func_, params_);                                          \
     }                                                             \
-    node = node->GetParentNode();                                 \
+    ShadowRoot* shadow = ShadowRoot::FromNode(node);              \
+    if (shadow) {                                                 \
+      node = shadow->GetHost();                                   \
+    } else {                                                      \
+      node = node->GetParentNode();                               \
+    }                                                             \
   } while (node);                                                 \
   if (needsEnterLeave) {                                          \
     nsDOMMutationObserver::LeaveMutationHandling();               \
   }                                                               \
   PR_END_MACRO
 
 void
 nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -25,16 +25,17 @@
 #include "nsGenericDOMDataNode.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsFontFaceList.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/DOMRect.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Likely.h"
 #include "nsCSSFrameConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 JSObject*
@@ -1071,16 +1072,22 @@ nsRange::IsValidBoundary(nsINode* aNode)
 
   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
     nsIContent* content = static_cast<nsIContent*>(aNode);
     if (content->Tag() == nsGkAtoms::documentTypeNodeName) {
       return nullptr;
     }
 
     if (!mMaySpanAnonymousSubtrees) {
+      // If the node is in a shadow tree then the ShadowRoot is the root.
+      ShadowRoot* containingShadow = content->GetContainingShadow();
+      if (containingShadow) {
+        return containingShadow;
+      }
+
       // If the node has a binding parent, that should be the root.
       // XXXbz maybe only for native anonymous content?
       nsINode* root = content->GetBindingParent();
       if (root) {
         return root;
       }
     }
   }
--- a/content/base/src/nsStyleLinkElement.cpp
+++ b/content/base/src/nsStyleLinkElement.cpp
@@ -9,16 +9,17 @@
  * be subclassed by various content nodes that want to load
  * stylesheets (<style>, <link>, processing instructions, etc).
  */
 
 #include "nsStyleLinkElement.h"
 
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsCSSStyleSheet.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
@@ -171,27 +172,28 @@ uint32_t nsStyleLinkElement::ParseLinkTy
   return linkMask;
 }
 
 NS_IMETHODIMP
 nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
                                      bool* aWillNotify,
                                      bool* aIsAlternate)
 {
-  return DoUpdateStyleSheet(nullptr, aObserver, aWillNotify, aIsAlternate,
-                            false);
+  return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify,
+                            aIsAlternate, false);
 }
 
 nsresult
 nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
+                                             ShadowRoot *aOldShadowRoot,
                                              bool aForceUpdate)
 {
   bool notify, alternate;
-  return DoUpdateStyleSheet(aOldDocument, nullptr, &notify, &alternate,
-                            aForceUpdate);
+  return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, &notify,
+                            &alternate, aForceUpdate);
 }
 
 static bool
 IsScopedStyleElement(nsIContent* aContent)
 {
   // This is quicker than, say, QIing aContent to nsStyleLinkElement
   // and then calling its virtual GetStyleSheetInfo method to find out
   // if it is scoped.
@@ -267,45 +269,60 @@ GetScopeElement(nsIStyleSheet* aSheet)
   if (!cssStyleSheet) {
     return nullptr;
   }
 
   return cssStyleSheet->GetScopeElement();
 }
 
 nsresult
-nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
+nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
+                                       ShadowRoot* aOldShadowRoot,
                                        nsICSSLoaderObserver* aObserver,
                                        bool* aWillNotify,
                                        bool* aIsAlternate,
                                        bool aForceUpdate)
 {
   *aWillNotify = false;
 
   nsCOMPtr<nsIContent> thisContent;
   CallQueryInterface(this, getter_AddRefs(thisContent));
 
+  // All instances of nsStyleLinkElement should implement nsIContent.
+  NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
+
+  // Check for a ShadowRoot because link elements are inert in a
+  // ShadowRoot.
+  ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+  if (thisContent->IsHTML(nsGkAtoms::link) &&
+      (aOldShadowRoot || containingShadow)) {
+    return NS_OK;
+  }
+
   Element* oldScopeElement = GetScopeElement(mStyleSheet);
 
   if (mStyleSheet && aOldDocument) {
     // We're removing the link element from the document, unload the
     // stylesheet.  We want to do this even if updates are disabled, since
     // otherwise a sheet with a stale linking element pointer will be hanging
     // around -- not good!
-    aOldDocument->BeginUpdate(UPDATE_STYLE);
-    aOldDocument->RemoveStyleSheet(mStyleSheet);
-    aOldDocument->EndUpdate(UPDATE_STYLE);
+    if (aOldShadowRoot) {
+      aOldShadowRoot->RemoveSheet(mStyleSheet);
+    } else {
+      aOldDocument->BeginUpdate(UPDATE_STYLE);
+      aOldDocument->RemoveStyleSheet(mStyleSheet);
+      aOldDocument->EndUpdate(UPDATE_STYLE);
+    }
+
     nsStyleLinkElement::SetStyleSheet(nullptr);
     if (oldScopeElement) {
       UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
     }
   }
 
-  NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
-
   // When static documents are created, stylesheets are cloned manually.
   if (mDontLoadStyle || !mUpdatesEnabled ||
       thisContent->OwnerDoc()->IsStaticDocument()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
 
@@ -323,19 +340,25 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
       nsresult rv = oldURI->Equals(uri, &equal);
       if (NS_SUCCEEDED(rv) && equal) {
         return NS_OK; // We already loaded this stylesheet
       }
     }
   }
 
   if (mStyleSheet) {
-    doc->BeginUpdate(UPDATE_STYLE);
-    doc->RemoveStyleSheet(mStyleSheet);
-    doc->EndUpdate(UPDATE_STYLE);
+    if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+      ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+      containingShadow->RemoveSheet(mStyleSheet);
+    } else {
+      doc->BeginUpdate(UPDATE_STYLE);
+      doc->RemoveStyleSheet(mStyleSheet);
+      doc->EndUpdate(UPDATE_STYLE);
+    }
+
     nsStyleLinkElement::SetStyleSheet(nullptr);
   }
 
   if (!uri && !isInline) {
     return NS_OK; // If href is empty and this is not inline style then just bail
   }
 
   nsAutoString title, type, media;
@@ -415,23 +438,32 @@ nsStyleLinkElement::UpdateStyleSheetScop
                                nullptr;
 
   if (oldScopeElement == newScopeElement) {
     return;
   }
 
   nsIDocument* document = thisContent->GetOwnerDocument();
 
-  document->BeginUpdate(UPDATE_STYLE);
-  document->RemoveStyleSheet(mStyleSheet);
+  if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    ShadowRoot* containingShadow = thisContent->GetContainingShadow();
+    containingShadow->RemoveSheet(mStyleSheet);
+
+    mStyleSheet->SetScopeElement(newScopeElement);
 
-  mStyleSheet->SetScopeElement(newScopeElement);
+    containingShadow->InsertSheet(mStyleSheet, thisContent);
+  } else {
+    document->BeginUpdate(UPDATE_STYLE);
+    document->RemoveStyleSheet(mStyleSheet);
 
-  document->AddStyleSheet(mStyleSheet);
-  document->EndUpdate(UPDATE_STYLE);
+    mStyleSheet->SetScopeElement(newScopeElement);
+
+    document->AddStyleSheet(mStyleSheet);
+    document->EndUpdate(UPDATE_STYLE);
+  }
 
   if (oldScopeElement) {
     UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
   }
   if (newScopeElement) {
     SetIsElementInStyleScopeFlagOnSubtree(newScopeElement);
   }
 }
--- a/content/base/src/nsStyleLinkElement.h
+++ b/content/base/src/nsStyleLinkElement.h
@@ -24,16 +24,22 @@
 #define DNS_PREFETCH  0x00000002
 #define STYLESHEET    0x00000004
 #define NEXT          0x00000008
 #define ALTERNATE     0x00000010
 
 class nsIDocument;
 class nsIURI;
 
+namespace mozilla {
+namespace dom {
+class ShadowRoot;
+} // namespace dom
+} // namespace mozilla
+
 class nsStyleLinkElement : public nsIStyleSheetLinkingElement
 {
 public:
   nsStyleLinkElement();
   virtual ~nsStyleLinkElement();
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) MOZ_OVERRIDE = 0;
 
@@ -48,28 +54,32 @@ public:
                               bool* aIsAlternate) MOZ_OVERRIDE;
   NS_IMETHOD SetEnableUpdates(bool aEnableUpdates) MOZ_OVERRIDE;
   NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE;
 
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) MOZ_OVERRIDE;
   virtual void SetLineNumber(uint32_t aLineNumber) MOZ_OVERRIDE;
 
   static uint32_t ParseLinkTypes(const nsAString& aTypes);
-  
-  void UpdateStyleSheetInternal() { UpdateStyleSheetInternal(nullptr); }
+
+  void UpdateStyleSheetInternal()
+  {
+    UpdateStyleSheetInternal(nullptr, nullptr);
+  }
 protected:
   /**
    * @param aOldDocument should be non-null only if we're updating because we
    *                     removed the node from the document.
    * @param aForceUpdate true will force the update even if the URI has not
    *                     changed.  This should be used in cases when something
    *                     about the content that affects the resulting sheet
    *                     changed but the URI may not have changed.
    */
   nsresult UpdateStyleSheetInternal(nsIDocument *aOldDocument,
+                                    mozilla::dom::ShadowRoot *aOldShadowRoot,
                                     bool aForceUpdate = false);
 
   void UpdateStyleSheetScopedness(bool aIsNowScoped);
 
   virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline) = 0;
   virtual void GetStyleSheetInfo(nsAString& aTitle,
                                  nsAString& aType,
                                  nsAString& aMedia,
@@ -85,22 +95,27 @@ protected:
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
   /**
    * @param aOldDocument should be non-null only if we're updating because we
    *                     removed the node from the document.
+   * @param aOldShadowRoot The ShadowRoot that used to contain the style.
+   *                     Passed as a parameter because on an update, the node
+   *                     is removed from the tree before the sheet is removed
+   *                     from the ShadowRoot.
    * @param aForceUpdate true will force the update even if the URI has not
    *                     changed.  This should be used in cases when something
    *                     about the content that affects the resulting sheet
    *                     changed but the URI may not have changed.
    */
-  nsresult DoUpdateStyleSheet(nsIDocument *aOldDocument,
+  nsresult DoUpdateStyleSheet(nsIDocument* aOldDocument,
+                              mozilla::dom::ShadowRoot* aOldShadowRoot,
                               nsICSSLoaderObserver* aObserver,
                               bool* aWillNotify,
                               bool* aIsAlternate,
                               bool aForceUpdate);
 
   nsRefPtr<nsCSSStyleSheet> mStyleSheet;
 protected:
   bool mDontLoadStyle;
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1897,43 +1897,46 @@ WebGLContext::GetUniform(JSContext* cx, 
         GLfloat fv[16] = { GLfloat(0) };
         gl->fGetUniformfv(progname, location->Location(), fv);
         if (unitSize == 1) {
             return JS::DoubleValue(fv[0]);
         } else {
             JSObject* obj = Float32Array::Create(cx, this, unitSize, fv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_INT) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::Int32Value(iv[0]);
         } else {
             JSObject* obj = Int32Array::Create(cx, this, unitSize, iv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     } else if (baseType == LOCAL_GL_BOOL) {
         GLint iv[16] = { 0 };
         gl->fGetUniformiv(progname, location->Location(), iv);
         if (unitSize == 1) {
             return JS::BooleanValue(iv[0] ? true : false);
         } else {
             JS::Value uv[16];
             for (int k = 0; k < unitSize; k++)
                 uv[k] = JS::BooleanValue(iv[k] ? true : false);
             JSObject* obj = JS_NewArrayObject(cx, unitSize, uv);
             if (!obj) {
                 ErrorOutOfMemory("getUniform: out of memory");
+                return JS::NullValue();
             }
             return JS::ObjectOrNullValue(obj);
         }
     }
 
     // Else preserving behavior, but I'm not sure this is correct per spec
     return JS::UndefinedValue();
 }
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLContentElement.cpp
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/HTMLContentElement.h"
+#include "mozilla/dom/HTMLContentElementBinding.h"
+#include "mozilla/dom/NodeListBinding.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/css/StyleRule.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsIAtom.h"
+#include "nsCSSRuleProcessor.h"
+#include "nsRuleData.h"
+#include "nsRuleProcessorData.h"
+#include "nsRuleWalker.h"
+#include "nsCSSParser.h"
+
+NS_IMPL_NS_NEW_HTML_ELEMENT(Content)
+
+using namespace mozilla::dom;
+
+HTMLContentElement::HTMLContentElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false)
+{
+  SetIsDOMBinding();
+}
+
+HTMLContentElement::~HTMLContentElement()
+{
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(HTMLContentElement,
+                                     nsGenericHTMLElement,
+                                     mMatchedNodes)
+
+NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLContentElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ELEMENT_CLONE(HTMLContentElement)
+
+JSObject*
+HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
+{
+  return HTMLContentElementBinding::Wrap(aCx, aScope, this);
+}
+
+nsresult
+HTMLContentElement::BindToTree(nsIDocument* aDocument,
+                               nsIContent* aParent,
+                               nsIContent* aBindingParent,
+                               bool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
+                                                 aBindingParent,
+                                                 aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  ShadowRoot* containingShadow = GetContainingShadow();
+  if (containingShadow) {
+    nsINode* parentNode = nsINode::GetParentNode();
+    while (parentNode && parentNode != containingShadow) {
+      if (parentNode->IsElement() &&
+          parentNode->AsElement()->IsHTML(nsGkAtoms::content)) {
+        // Content element in fallback content is not an insertion point.
+        return NS_OK;
+      }
+      parentNode = parentNode->GetParentNode();
+    }
+
+    // If the content element is being inserted into a ShadowRoot,
+    // add this element to the list of insertion points.
+    mIsInsertionPoint = true;
+    containingShadow->AddInsertionPoint(this);
+    containingShadow->SetInsertionPointChanged();
+  }
+
+  return NS_OK;
+}
+
+void
+HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  if (mIsInsertionPoint) {
+    ShadowRoot* containingShadow = GetContainingShadow();
+    // Make sure that containingShadow exists, it may have been nulled
+    // during unlinking in which case the ShadowRoot is going away.
+    if (containingShadow) {
+      containingShadow->RemoveInsertionPoint(this);
+
+      // Remove all the assigned nodes now that the
+      // insertion point now that the insertion point is
+      // no longer a descendant of a ShadowRoot.
+      ClearMatchedNodes();
+      containingShadow->SetInsertionPointChanged();
+    }
+
+    mIsInsertionPoint = false;
+  }
+
+  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+}
+
+void
+HTMLContentElement::ClearMatchedNodes()
+{
+  for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) {
+    mMatchedNodes[i]->SetXBLInsertionParent(nullptr);
+  }
+  mMatchedNodes.Clear();
+}
+
+static bool
+IsValidContentSelectors(nsCSSSelector* aSelector)
+{
+  nsCSSSelector* currentSelector = aSelector;
+  while (currentSelector) {
+    // Blacklist invalid selector fragments.
+    if (currentSelector->IsPseudoElement() ||
+        currentSelector->mPseudoClassList ||
+        currentSelector->mNegations ||
+        currentSelector->mOperator) {
+      return false;
+    }
+
+    currentSelector = currentSelector->mNext;
+  }
+
+  return true;
+}
+
+nsresult
+HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                            nsIAtom* aPrefix, const nsAString& aValue,
+                            bool aNotify)
+{
+  nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
+                                              aValue, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) {
+    // Select attribute was updated, the insertion point may match different
+    // elements.
+    nsIDocument* doc = OwnerDoc();
+    nsCSSParser parser(doc->CSSLoader());
+
+    mValidSelector = true;
+    mSelectorList = nullptr;
+
+    nsresult rv = parser.ParseSelectorString(aValue,
+                                             doc->GetDocumentURI(),
+                                             // Bug 11240
+                                             0, // XXX get the line number!
+                                             getter_Transfers(mSelectorList));
+
+    // We don't want to return an exception if parsing failed because
+    // the spec does not define it as an exception case.
+    if (NS_SUCCEEDED(rv)) {
+      // Ensure that all the selectors are valid
+      nsCSSSelectorList* selectors = mSelectorList;
+      while (selectors) {
+        if (!IsValidContentSelectors(selectors->mSelectors)) {
+          // If we have an invalid selector, we can not match anything.
+          mValidSelector = false;
+          mSelectorList = nullptr;
+          break;
+        }
+        selectors = selectors->mNext;
+      }
+    }
+
+    ShadowRoot* containingShadow = GetContainingShadow();
+    if (containingShadow) {
+      containingShadow->DistributeAllNodes();
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+HTMLContentElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                              bool aNotify)
+{
+  nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID,
+                                                aAttribute, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::select) {
+    // The select attribute was removed. This insertion point becomes
+    // a universal selector.
+    mValidSelector = true;
+    mSelectorList = nullptr;
+
+    ShadowRoot* containingShadow = GetContainingShadow();
+    if (containingShadow) {
+      containingShadow->DistributeAllNodes();
+    }
+  }
+
+  return NS_OK;
+}
+
+bool
+HTMLContentElement::Match(nsIContent* aContent)
+{
+  if (!mValidSelector) {
+    return false;
+  }
+
+  if (mSelectorList) {
+    nsIDocument* doc = OwnerDoc();
+    ShadowRoot* containingShadow = GetContainingShadow();
+    nsIContent* host = containingShadow->GetHost();
+
+    TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
+                                     doc, TreeMatchContext::eNeverMatchVisited);
+    doc->FlushPendingLinkUpdates();
+    matchingContext.SetHasSpecifiedScope();
+    matchingContext.AddScopeElement(host->AsElement());
+
+    if (!aContent->IsElement()) {
+      return false;
+    }
+
+    return nsCSSRuleProcessor::SelectorListMatches(aContent->AsElement(),
+                                                   matchingContext,
+                                                   mSelectorList);
+  }
+
+  return true;
+}
+
+already_AddRefed<DistributedContentList>
+HTMLContentElement::GetDistributedNodes()
+{
+  nsRefPtr<DistributedContentList> list = new DistributedContentList(this);
+  return list.forget();
+}
+
+NS_IMPL_CYCLE_COLLECTION_2(DistributedContentList, mParent, mDistributedNodes)
+
+NS_INTERFACE_TABLE_HEAD(DistributedContentList)
+  NS_INTERFACE_TABLE1(DistributedContentList, nsINodeList)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList)
+
+DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement)
+  : mParent(aHostElement)
+{
+  MOZ_COUNT_CTOR(DistributedContentList);
+  SetIsDOMBinding();
+
+  if (aHostElement->IsInsertionPoint()) {
+    if (aHostElement->MatchedNodes().IsEmpty()) {
+      // Fallback content.
+      nsINode* contentNode = aHostElement;
+      for (nsIContent* content = contentNode->GetFirstChild();
+           content;
+           content = content->GetNextSibling()) {
+        mDistributedNodes.AppendElement(content);
+      }
+    } else {
+      mDistributedNodes.AppendElements(aHostElement->MatchedNodes());
+    }
+  }
+}
+
+DistributedContentList::~DistributedContentList()
+{
+  MOZ_COUNT_DTOR(DistributedContentList);
+}
+
+nsIContent*
+DistributedContentList::Item(uint32_t aIndex)
+{
+  return mDistributedNodes.SafeElementAt(aIndex);
+}
+
+NS_IMETHODIMP
+DistributedContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
+{
+  nsIContent* item = Item(aIndex);
+  if (!item) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return CallQueryInterface(item, aReturn);
+}
+
+uint32_t
+DistributedContentList::Length() const
+{
+  return mDistributedNodes.Length();
+}
+
+NS_IMETHODIMP
+DistributedContentList::GetLength(uint32_t* aLength)
+{
+  *aLength = Length();
+  return NS_OK;
+}
+
+int32_t
+DistributedContentList::IndexOf(nsIContent* aContent)
+{
+  return mDistributedNodes.IndexOf(aContent);
+}
+
+JSObject*
+DistributedContentList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return NodeListBinding::Wrap(aCx, aScope, this);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLContentElement.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_HTMLContentElement_h__
+#define mozilla_dom_HTMLContentElement_h__
+
+#include "nsINodeList.h"
+#include "nsGenericHTMLElement.h"
+
+class nsCSSSelectorList;
+
+namespace mozilla {
+namespace dom {
+
+class DistributedContentList;
+
+class HTMLContentElement MOZ_FINAL : public nsGenericHTMLElement
+{
+public:
+  HTMLContentElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~HTMLContentElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement,
+                                           nsGenericHTMLElement)
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+  virtual nsIDOMNode* AsDOMNode() { return this; }
+
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers);
+
+  virtual void UnbindFromTree(bool aDeep = true,
+                              bool aNullParent = true);
+
+  /**
+   * Returns whether if the selector of this insertion point
+   * matches the provided content.
+   */
+  bool Match(nsIContent* aContent);
+  bool IsInsertionPoint() const { return mIsInsertionPoint; }
+  nsCOMArray<nsIContent>& MatchedNodes() { return mMatchedNodes; }
+  void ClearMatchedNodes();
+
+  virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                           nsIAtom* aPrefix, const nsAString& aValue,
+                           bool aNotify);
+
+  virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
+                             bool aNotify);
+
+  // WebIDL methods.
+  already_AddRefed<DistributedContentList> GetDistributedNodes();
+  void GetSelect(nsAString& aSelect)
+  {
+    Element::GetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect);
+  }
+  void SetSelect(const nsAString& aSelect)
+  {
+    Element::SetAttr(kNameSpaceID_None, nsGkAtoms::select, aSelect, true);
+  }
+
+protected:
+  virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  /**
+   * An array of nodes from the ShadowRoot host that match the
+   * content insertion selector.
+   */
+  nsCOMArray<nsIContent> mMatchedNodes;
+
+  nsAutoPtr<nsCSSSelectorList> mSelectorList;
+  bool mValidSelector;
+  bool mIsInsertionPoint;
+};
+
+class DistributedContentList : public nsINodeList
+{
+public:
+  DistributedContentList(HTMLContentElement* aHostElement);
+  virtual ~DistributedContentList();
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DistributedContentList)
+
+  // nsIDOMNodeList
+  NS_DECL_NSIDOMNODELIST
+
+  // nsINodeList
+  virtual nsIContent* Item(uint32_t aIndex);
+  virtual int32_t IndexOf(nsIContent* aContent);
+  virtual nsINode* GetParentObject() { return mParent; }
+  virtual uint32_t Length() const;
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+protected:
+  nsRefPtr<HTMLContentElement> mParent;
+  nsCOMArray<nsIContent> mDistributedNodes;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HTMLContentElement_h__
+
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -126,18 +126,19 @@ HTMLLinkElement::BindToTree(nsIDocument*
                             bool aCompileEventHandlers)
 {
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
-  
-  if (aDocument) {
+
+  // Link must be inert in ShadowRoot.
+  if (aDocument && !GetContainingShadow()) {
     aDocument->RegisterPendingLinkUpdate(this);
   }
 
   void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
   nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
 
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
 
@@ -161,22 +162,29 @@ HTMLLinkElement::UnbindFromTree(bool aDe
 {
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   // Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
   // and so this messy event dispatch can go away.
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-  if (oldDoc) {
+
+  // Check for a ShadowRoot because link elements are inert in a
+  // ShadowRoot.
+  ShadowRoot* oldShadowRoot = GetBindingParent() ?
+    GetBindingParent()->GetShadowRoot() : nullptr;
+
+  if (oldDoc && !oldShadowRoot) {
     oldDoc->UnregisterPendingLinkUpdate(this);
   }
   CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+
+  UpdateStyleSheetInternal(oldDoc, oldShadowRoot);
 }
 
 bool
 HTMLLinkElement::ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
@@ -243,17 +251,17 @@ HTMLLinkElement::SetAttr(int32_t aNameSp
        aName == nsGkAtoms::media ||
        aName == nsGkAtoms::type)) {
     bool dropSheet = false;
     if (aName == nsGkAtoms::rel && GetSheet()) {
       uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aValue);
       dropSheet = !(linkTypes & STYLESHEET);          
     }
     
-    UpdateStyleSheetInternal(nullptr,
+    UpdateStyleSheetInternal(nullptr, nullptr,
                              dropSheet ||
                              (aName == nsGkAtoms::title ||
                               aName == nsGkAtoms::media ||
                               aName == nsGkAtoms::type));
   }
 
   return rv;
 }
@@ -267,17 +275,17 @@ HTMLLinkElement::UnsetAttr(int32_t aName
   // Since removing href or rel makes us no longer link to a
   // stylesheet, force updates for those too.
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::href ||
        aAttribute == nsGkAtoms::rel ||
        aAttribute == nsGkAtoms::title ||
        aAttribute == nsGkAtoms::media ||
        aAttribute == nsGkAtoms::type)) {
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
--- a/content/html/content/src/HTMLStyleElement.cpp
+++ b/content/html/content/src/HTMLStyleElement.cpp
@@ -128,17 +128,17 @@ HTMLStyleElement::ContentRemoved(nsIDocu
 {
   ContentChanged(aChild);
 }
 
 void
 HTMLStyleElement::ContentChanged(nsIContent* aContent)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
-    UpdateStyleSheetInternal(nullptr);
+    UpdateStyleSheetInternal(nullptr, nullptr);
   }
 }
 
 nsresult
 HTMLStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                              nsIContent* aBindingParent,
                              bool aCompileEventHandlers)
 {
@@ -152,33 +152,33 @@ HTMLStyleElement::BindToTree(nsIDocument
 
   return rv;  
 }
 
 void
 HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-
+  ShadowRoot* oldShadow = GetContainingShadow();
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                           nsIAtom* aPrefix, const nsAString& aValue,
                           bool aNotify)
 {
   nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
                                               aValue, aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::title ||
         aName == nsGkAtoms::media ||
         aName == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aName == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(true);
     }
   }
 
   return rv;
 }
 
@@ -187,17 +187,17 @@ HTMLStyleElement::UnsetAttr(int32_t aNam
                             bool aNotify)
 {
   nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
                                                 aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::title ||
         aAttribute == nsGkAtoms::media ||
         aAttribute == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aAttribute == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(false);
     }
   }
 
   return rv;
 }
 
@@ -213,17 +213,17 @@ HTMLStyleElement::SetInnerHTML(const nsA
                                ErrorResult& aError)
 {
   SetEnableUpdates(false);
 
   aError = nsContentUtils::SetNodeTextContent(this, aInnerHTML, true);
 
   SetEnableUpdates(true);
 
-  UpdateStyleSheetInternal(nullptr);
+  UpdateStyleSheetInternal(nullptr, nullptr);
 }
 
 already_AddRefed<nsIURI>
 HTMLStyleElement::GetStyleSheetURL(bool* aIsInline)
 {
   *aIsInline = true;
   return nullptr;
 }
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.dom += [
     'HTMLAnchorElement.h',
     'HTMLAreaElement.h',
     'HTMLBodyElement.h',
     'HTMLBRElement.h',
     'HTMLButtonElement.h',
+    'HTMLContentElement.h',
     'HTMLDataElement.h',
     'HTMLDataListElement.h',
     'HTMLDivElement.h',
     'HTMLFieldSetElement.h',
     'HTMLFontElement.h',
     'HTMLFormControlsCollection.h',
     'HTMLFormElement.h',
     'HTMLFrameElement.h',
@@ -78,16 +79,17 @@ EXPORTS.mozilla.dom += [
 UNIFIED_SOURCES += [
     'HTMLAnchorElement.cpp',
     'HTMLAreaElement.cpp',
     'HTMLAudioElement.cpp',
     'HTMLBodyElement.cpp',
     'HTMLBRElement.cpp',
     'HTMLButtonElement.cpp',
     'HTMLCanvasElement.cpp',
+    'HTMLContentElement.cpp',
     'HTMLDataElement.cpp',
     'HTMLDataListElement.cpp',
     'HTMLDivElement.cpp',
     'HTMLElement.cpp',
     'HTMLFieldSetElement.cpp',
     'HTMLFontElement.cpp',
     'HTMLFormControlsCollection.cpp',
     'HTMLFormElement.cpp',
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1746,16 +1746,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedObj
 
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Anchor)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Area)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Audio)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(BR)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Body)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Button)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Canvas)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(Content)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Mod)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Data)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(DataList)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Div)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(FieldSet)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Font)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Form)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Frame)
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -73,17 +73,16 @@
 #include "nsIContentViewer.h"
 #include "nsIWyciwygChannel.h"
 #include "nsIScriptElement.h"
 #include "nsIScriptError.h"
 #include "nsIMutableArray.h"
 #include "nsArrayUtils.h"
 #include "nsIEffectiveTLDService.h"
 
-#include "nsIPrompt.h"
 //AHMED 12-2
 #include "nsBidiUtils.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/FallbackEncoding.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsNodeInfoManager.h"
@@ -1228,35 +1227,29 @@ nsHTMLDocument::SetCookie(const nsAStrin
   if (mSandboxFlags & SANDBOXED_ORIGIN) {
     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   // not having a cookie service isn't an error
   nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
   if (service && mDocumentURI) {
-    nsCOMPtr<nsIPrompt> prompt;
-    nsCOMPtr<nsPIDOMWindow> window = GetWindow();
-    if (window) {
-      window->GetPrompter(getter_AddRefs(prompt));
-    }
-
     // The for getting the URI matches nsNavigator::GetCookieEnabled
     nsCOMPtr<nsIURI> codebaseURI;
     NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
 
     if (!codebaseURI) {
       // Document's principal is not a codebase (may be system), so
       // can't set cookies
 
       return;
     }
 
     NS_ConvertUTF16toUTF8 cookie(aCookie);
-    service->SetCookieString(codebaseURI, prompt, cookie.get(), mChannel);
+    service->SetCookieString(codebaseURI, nullptr, cookie.get(), mChannel);
   }
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::Open(const nsAString& aContentTypeOrUrl,
                      const nsAString& aReplaceOrName,
                      const nsAString& aFeatures,
                      JSContext* cx, uint8_t aOptionalArgCount,
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -162,16 +162,19 @@ static Element::MappedAttributeEntry sMt
   { nullptr }
 };
 
 static Element::MappedAttributeEntry sTokenStyles[] = {
   { &nsGkAtoms::mathsize_ },
   { &nsGkAtoms::fontsize_ },
   { &nsGkAtoms::color },
   { &nsGkAtoms::fontfamily_ },
+  { &nsGkAtoms::fontstyle_ },
+  { &nsGkAtoms::fontweight_ },
+  { &nsGkAtoms::mathvariant_},
   { nullptr }
 };
 
 static Element::MappedAttributeEntry sEnvironmentStyles[] = {
   { &nsGkAtoms::scriptlevel_ },
   { &nsGkAtoms::scriptminsize_ },
   { &nsGkAtoms::scriptsizemultiplier_ },
   { &nsGkAtoms::background },
@@ -638,16 +641,120 @@ nsMathMLElement::MapMathMLAttributesInto
       WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
                      nsGkAtoms::mathvariant_->GetUTF16String(),
                      aData->mPresContext->Document());
     }
     if (value && value->Type() == nsAttrValue::eString &&
         fontFamily->GetUnit() == eCSSUnit_Null) {
       fontFamily->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
     }
+
+    // fontstyle
+    //
+    // "Specified the font style to use for the token. Deprecated in favor of
+    //  mathvariant."
+    //
+    // values: "normal" | "italic"
+    // default:	normal (except on <mi>)
+    //
+    // Note that the font-style property is reset in layout/style/ when
+    // -moz-math-variant is specified.
+    nsCSSValue* fontStyle = aData->ValueForFontStyle();
+    value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
+    if (value) {
+      WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
+                       nsGkAtoms::mathvariant_->GetUTF16String(),
+                       aData->mPresContext->Document());
+      if (value->Type() == nsAttrValue::eString &&
+          fontStyle->GetUnit() == eCSSUnit_Null) {
+        nsAutoString str(value->GetStringValue());
+        str.CompressWhitespace();
+        if (str.EqualsASCII("normal")) {
+          fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_NORMAL,
+                                eCSSUnit_Enumerated);
+        } else if (str.EqualsASCII("italic")) {
+          fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_ITALIC,
+                                eCSSUnit_Enumerated);
+        }
+      }
+    }
+
+    // fontweight
+    //
+    // "Specified the font weight for the token. Deprecated in favor of
+    // mathvariant."
+    //
+    // values: "normal" | "bold"
+    // default: normal
+    //
+    // Note that the font-weight property is reset in layout/style/ when
+    // -moz-math-variant is specified.
+    nsCSSValue* fontWeight = aData->ValueForFontWeight();
+    value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
+    if (value) {
+      WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(),
+                       nsGkAtoms::mathvariant_->GetUTF16String(),
+                       aData->mPresContext->Document());
+      if (value->Type() == nsAttrValue::eString &&
+          fontWeight->GetUnit() == eCSSUnit_Null) {
+        nsAutoString str(value->GetStringValue());
+        str.CompressWhitespace();
+        if (str.EqualsASCII("normal")) {
+          fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_NORMAL,
+                                 eCSSUnit_Enumerated);
+        } else if (str.EqualsASCII("bold")) {
+          fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_BOLD,
+                                  eCSSUnit_Enumerated);
+        }
+      }
+    }
+
+    // mathvariant
+    //
+    // "Specifies the logical class of the token. Note that this class is more
+    // than styling, it typically conveys semantic intent;"
+    //
+    // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
+    // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
+    // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
+    // "monospace" | "initial" | "tailed" | "looped" | "stretched"
+    // default: normal (except on <mi>)
+    //
+    nsCSSValue* mathVariant = aData->ValueForMathVariant();
+    value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
+    if (value && value->Type() == nsAttrValue::eString &&
+        mathVariant->GetUnit() == eCSSUnit_Null) {
+      nsAutoString str(value->GetStringValue());
+      str.CompressWhitespace();
+      static const char sizes[19][23] = {
+        "normal", "bold", "italic", "bold-italic", "script", "bold-script",
+        "fraktur", "double-struck", "bold-fraktur", "sans-serif",
+        "bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic",
+        "monospace", "initial", "tailed", "looped", "stretched"
+      };
+      static const int32_t values[NS_ARRAY_LENGTH(sizes)] = {
+        NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD,
+        NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC,
+        NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT,
+        NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK,
+        NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF,
+        NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF,
+        NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC,
+        NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC,
+        NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL,
+        NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED,
+        NS_MATHML_MATHVARIANT_STRETCHED
+      };
+      for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
+        if (str.EqualsASCII(sizes[i])) {
+          mathVariant->SetIntValue(values[i], eCSSUnit_Enumerated);
+          break;
+        }
+      }
+    }
   }
 
   // mathbackground
   // 
   // "Specifies the background color to be used to fill in the bounding box of
   // the element and its children. The default, 'transparent', lets the
   // background color, if any, used in the current rendering context to show
   // through."
@@ -714,17 +821,28 @@ nsMathMLElement::MapMathMLAttributesInto
     nsCSSValue* colorValue = aData->ValueForColor();
     if (value && value->GetColorValue(color) &&
         colorValue->GetUnit() == eCSSUnit_Null) {
       colorValue->SetColorValue(color);
     }
   }
 
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
-    // width: value
+    // width
+    //
+    // "Specifies the desired width of the entire table and is intended for
+    // visual user agents. When the value is a percentage value, the value is
+    // relative to the horizontal space a MathML renderer has available for the
+    // math element. When the value is "auto", the MathML renderer should
+    // calculate the table width from its contents using whatever layout
+    // algorithm it chooses. "
+    //
+    // values: "auto" | length
+    // default: auto
+    //
     nsCSSValue* width = aData->ValueForWidth();
     if (width->GetUnit() == eCSSUnit_Null) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
       // This does not handle auto and unitless values
       if (value && value->Type() == nsAttrValue::eString) {
         ParseNumericValue(value->GetStringValue(), *width, 0,
                           aData->mPresContext->Document());
       }
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -156,16 +156,18 @@ AudioStream::~AudioStream()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
+
+  InitPreferredSampleRate();
 }
 
 /*static*/ void AudioStream::ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
 
   StaticMutexAutoLock lock(sMutex);
@@ -260,38 +262,26 @@ int64_t AudioStream::GetWritten()
       cubeb_get_max_channel_count(cubebContext,
                                   &maxNumberOfChannels) == CUBEB_OK) {
     return static_cast<int>(maxNumberOfChannels);
   }
 
   return 0;
 }
 
-/*static*/ int AudioStream::PreferredSampleRate()
+/*static */ void AudioStream::InitPreferredSampleRate()
 {
-  const int fallbackSampleRate = 44100;
-  StaticMutexAutoLock lock(sMutex);
-  if (sPreferredSampleRate != 0) {
-    return sPreferredSampleRate;
-  }
-
-  cubeb* cubebContext = GetCubebContextUnlocked();
-  if (!cubebContext) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
   // Get the preferred samplerate for this platform, or fallback to something
   // sensible if we fail. We cache the value, because this might be accessed
   // often, and the complexity of the function call below depends on the
   // backend used.
-  if (cubeb_get_preferred_sample_rate(cubebContext,
+  if (cubeb_get_preferred_sample_rate(GetCubebContext(),
                                       &sPreferredSampleRate) != CUBEB_OK) {
-    sPreferredSampleRate = fallbackSampleRate;
+    sPreferredSampleRate = 44100;
   }
-
-  return sPreferredSampleRate;
 }
 
 static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
 {
   aDest[0] = aValue & 0xFF;
   aDest[1] = aValue >> 8;
 }
 
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -174,19 +174,23 @@ public:
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
   // Returns the maximum number of channels supported by the audio hardware.
   static int MaxNumberOfChannels();
 
+  static void InitPreferredSampleRate();
   // Returns the samplerate the systems prefer, because it is the
   // samplerate the hardware/mixer supports.
-  static int PreferredSampleRate();
+  static int PreferredSampleRate() {
+    MOZ_ASSERT(sPreferredSampleRate);
+    return sPreferredSampleRate;
+  }
 
   AudioStream();
   ~AudioStream();
 
   enum LatencyRequest {
     HighLatency,
     LowLatency
   };
@@ -368,24 +372,24 @@ private:
                  // cubeb, after which StateCallback will indicate drain
                  // completion.
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED      // Stream disabled due to an internal error.
   };
 
   StreamState mState;
 
-  // This mutex protects the static members below.
-  static StaticMutex sMutex;
-  static cubeb* sCubebContext;
-
   // Prefered samplerate, in Hz (characteristic of the
   // hardware/mixer/platform/API used).
   static uint32_t sPreferredSampleRate;
 
+  // This mutex protects the static members below
+  static StaticMutex sMutex;
+  static cubeb* sCubebContext;
+
   static double sVolumeScale;
   static uint32_t sCubebLatency;
   static bool sCubebLatencyPrefSet;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/fmp4/demuxer/audio_decoder_config.cc
+++ b/content/media/fmp4/demuxer/audio_decoder_config.cc
@@ -1,15 +1,16 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "mp4_demuxer/audio_decoder_config.h"
 
 #include <sstream>
+#include <string.h>
 
 namespace mp4_demuxer {
 
 static int SampleFormatToBitsPerChannel(SampleFormat sample_format) {
   switch (sample_format) {
     case kUnknownSampleFormat:
       return 0;
     case kSampleFormatU8:
--- a/content/media/fmp4/demuxer/basictypes.h
+++ b/content/media/fmp4/demuxer/basictypes.h
@@ -1,16 +1,17 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef MEDIA_MP4_BASIC_TYPES_H_
 #define MEDIA_MP4_BASIC_TYPES_H_
 
 #include <iostream>
+#include <limits>
 #include <stdint.h>
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog();
 #endif
 
 namespace mp4_demuxer {
--- a/content/media/fmp4/demuxer/video_decoder_config.cc
+++ b/content/media/fmp4/demuxer/video_decoder_config.cc
@@ -1,15 +1,16 @@
 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "mp4_demuxer/video_decoder_config.h"
 
 #include <sstream>
+#include <string.h>
 
 namespace mp4_demuxer {
 
 VideoDecoderConfig::VideoDecoderConfig()
     : codec_(kUnknownVideoCodec),
       profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
       format_(VideoFrameFormat::INVALID),
       is_encrypted_(false) {
--- a/content/media/fmp4/wmf/MFTDecoder.h
+++ b/content/media/fmp4/wmf/MFTDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(MFTDecoder_h_)
 #define MFTDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIThread.h"
 
 namespace mozilla {
 
 class MFTDecoder {
 public:
--- a/content/media/fmp4/wmf/WMFAudioDecoder.h
+++ b/content/media/fmp4/wmf/WMFAudioDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(WMFAudioDecoder_h_)
 #define WMFAudioDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 
 namespace mozilla {
 
 class WMFAudioDecoder : public MediaDataDecoder {
 public:
   WMFAudioDecoder();
--- a/content/media/fmp4/wmf/WMFDecoderModule.cpp
+++ b/content/media/fmp4/wmf/WMFDecoderModule.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "wmf.h"
+#include "WMF.h"
 #include "WMFDecoderModule.h"
 #include "WMFDecoder.h"
 #include "WMFVideoDecoder.h"
 #include "WMFAudioDecoder.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/DebugOnly.h"
 
 namespace mozilla {
--- a/content/media/fmp4/wmf/WMFVideoDecoder.h
+++ b/content/media/fmp4/wmf/WMFVideoDecoder.h
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(WMFVideoDecoder_h_)
 #define WMFVideoDecoder_h_
 
-#include "wmf.h"
+#include "WMF.h"
 #include "MP4Reader.h"
 #include "MFTDecoder.h"
 #include "nsRect.h"
 
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 
@@ -70,9 +70,9 @@ private:
   const bool mDXVAEnabled;
   bool mUseHwAccel;
 };
 
 
 
 } // namespace mozilla
 
-#endif
\ No newline at end of file
+#endif
--- a/content/media/wmf/WMF.h
+++ b/content/media/wmf/WMF.h
@@ -32,17 +32,17 @@ which makes Windows Media Foundation una
 #include <initguid.h>
 #include <d3d9.h>
 #include <dxva2api.h>
 #include <wmcodecdsp.h>
 #include <codecapi.h>
 
 // Some SDK versions don't define the AAC decoder CLSID.
 #ifndef CLSID_CMSAACDecMFT
-extern const CLSID CLSID_CMSAACDecMFT;
+extern "C" const CLSID CLSID_CMSAACDecMFT;
 #define WMF_MUST_DEFINE_AAC_MFT_CLSID
 #endif
 
 namespace mozilla {
 namespace wmf {
 
 // Loads/Unloads all the DLLs in which the WMF functions are located.
 // The DLLs must be loaded before any of the WMF functions below will work.
--- a/content/svg/content/src/SVGStyleElement.cpp
+++ b/content/svg/content/src/SVGStyleElement.cpp
@@ -78,33 +78,33 @@ SVGStyleElement::BindToTree(nsIDocument*
 
   return rv;
 }
 
 void
 SVGStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
-
+  ShadowRoot* oldShadow = GetShadowRoot();
   SVGStyleElementBase::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 SVGStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                          nsIAtom* aPrefix, const nsAString& aValue,
                          bool aNotify)
 {
   nsresult rv = SVGStyleElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
                                              aValue, aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::title ||
         aName == nsGkAtoms::media ||
         aName == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aName == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(true);
     }
   }
 
   return rv;
 }
 
@@ -113,17 +113,17 @@ SVGStyleElement::UnsetAttr(int32_t aName
                            bool aNotify)
 {
   nsresult rv = SVGStyleElementBase::UnsetAttr(aNameSpaceID, aAttribute,
                                                aNotify);
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::title ||
         aAttribute == nsGkAtoms::media ||
         aAttribute == nsGkAtoms::type) {
-      UpdateStyleSheetInternal(nullptr, true);
+      UpdateStyleSheetInternal(nullptr, nullptr, true);
     } else if (aAttribute == nsGkAtoms::scoped) {
       UpdateStyleSheetScopedness(false);
     }
   }
 
   return rv;
 }
 
@@ -181,17 +181,17 @@ SVGStyleElement::ContentRemoved(nsIDocum
 {
   ContentChanged(aChild);
 }
 
 void
 SVGStyleElement::ContentChanged(nsIContent* aContent)
 {
   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
-    UpdateStyleSheetInternal(nullptr);
+    UpdateStyleSheetInternal(nullptr, nullptr);
   }
 }
 
 //----------------------------------------------------------------------
 
 void
 SVGStyleElement::GetXmlspace(nsAString & aXmlspace)
 {
--- a/content/svg/content/test/test_animLengthReadonly.xhtml
+++ b/content/svg/content/test/test_animLengthReadonly.xhtml
@@ -208,21 +208,14 @@ function checkDiffValue(animLength)
 
   // convertToSpecifiedUnits
   animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX);
   is(animLength.animVal.value, origValue,
     "animated animVal.value changed after calling "
     + "baseValue.convertToSpecifiedUnits");
 }
 
-var animate = document.getElementById('animate');
-if (animate && animate.targetElement) {
-  window.addEventListener("load", main, false);
-} else {
-  // SMIL not enabled: skip tests but don't report 'todo' either
-  ok(true);
-  SimpleTest.finish();
-}
+window.addEventListener("load", main, false);
 ]]>
 </script>
 </pre>
 </body>
 </html>
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -53,16 +53,17 @@
 #include "nsNodeUtils.h"
 #include "nsJSUtils.h"
 
 // Nasty hack.  Maybe we could move some of the classinfo utility methods
 // (e.g. WrapNative) over to nsContentUtils?
 #include "nsDOMClassInfo.h"
 
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Helper classes
 
 /***********************************************************************/
 //
@@ -158,16 +159,26 @@ nsXBLBinding::nsXBLBinding(nsXBLPrototyp
   , mUsingXBLScope(false)
   , mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
+// Constructor used by web components.
+nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
+  : mMarkedForDeath(false),
+    mPrototypeBinding(aBinding),
+    mContent(aShadowRoot)
+{
+  NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
+  // Grab a ref to the document info so the prototype binding won't die
+  NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
+}
 
 nsXBLBinding::~nsXBLBinding(void)
 {
   if (mContent) {
     nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
   }
   nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
   NS_RELEASE(info);
@@ -268,16 +279,23 @@ nsXBLBinding::InstallAnonymousContent(ns
 #endif
   }
 }
 
 void
 nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
                                         nsIContent* aAnonParent)
 {
+  if (aAnonParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // It is unnecessary to uninstall anonymous content in a shadow tree
+    // because the ShadowRoot itself is a DocumentFragment and does not
+    // need any additional cleanup.
+    return;
+  }
+
   nsAutoScriptBlocker scriptBlocker;
   // Hold a strong ref while doing this, just in case.
   nsCOMPtr<nsIContent> anonParent = aAnonParent;
 #ifdef MOZ_XUL
   nsCOMPtr<nsIXULDocument> xuldoc =
     do_QueryInterface(aDocument);
 #endif
   for (nsIContent* child = aAnonParent->GetFirstChild();
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -21,30 +21,32 @@ class nsXBLPrototypeBinding;
 class nsIContent;
 class nsIAtom;
 class nsIDocument;
 class nsIScriptContext;
 
 namespace mozilla {
 namespace dom {
 
+class ShadowRoot;
 class XBLChildrenElement;
 
-}
-}
+} // namespace dom
+} // namespace mozilla
 
 class nsAnonymousContentList;
 
 // *********************************************************************/
 // The XBLBinding class
 
 class nsXBLBinding
 {
 public:
   nsXBLBinding(nsXBLPrototypeBinding* aProtoBinding);
+  nsXBLBinding(mozilla::dom::ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aProtoBinding);
   ~nsXBLBinding();
 
   /**
    * XBLBindings are refcounted.  They are held onto in 3 ways:
    * 1. The binding manager's binding table holds onto all bindings that are
    *    currently attached to a content node.
    * 2. Bindings hold onto their base binding.  This is important since
    *    the base binding itself may not be attached to anything.
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -592,16 +592,26 @@ nsXBLPrototypeBinding::GetRuleProcessor(
   if (mResources) {
     return mResources->mRuleProcessor;
   }
   
   return nullptr;
 }
 
 nsXBLPrototypeResources::sheet_array_type*
+nsXBLPrototypeBinding::GetOrCreateStyleSheets()
+{
+  if (!mResources) {
+    mResources = new nsXBLPrototypeResources(this);
+  }
+
+  return &mResources->mStyleSheetList;
+}
+
+nsXBLPrototypeResources::sheet_array_type*
 nsXBLPrototypeBinding::GetStyleSheets()
 {
   if (mResources) {
     return &mResources->mStyleSheetList;
   }
 
   return nullptr;
 }
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -52,16 +52,17 @@ public:
 
   nsresult BindingAttached(nsIContent* aBoundElement);
   nsresult BindingDetached(nsIContent* aBoundElement);
 
   bool LoadResources();
   nsresult AddResource(nsIAtom* aResourceType, const nsAString& aSrc);
 
   bool InheritsStyle() const { return mInheritStyle; }
+  void SetInheritsStyle(bool aInheritStyle) { mInheritStyle = aInheritStyle; }
 
   nsXBLPrototypeHandler* GetPrototypeHandlers() { return mPrototypeHandler; }
   void SetPrototypeHandlers(nsXBLPrototypeHandler* aHandler) { mPrototypeHandler = aHandler; }
 
   nsXBLProtoImplAnonymousMethod* GetConstructor();
   nsresult SetConstructor(nsXBLProtoImplAnonymousMethod* aConstructor);
   nsXBLProtoImplAnonymousMethod* GetDestructor();
   nsresult SetDestructor(nsXBLProtoImplAnonymousMethod* aDestructor);
@@ -111,16 +112,17 @@ public:
   nsXBLPrototypeBinding* GetBasePrototype() { return mBaseBinding; }
 
   nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
   bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }
   
   void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
 
   nsIStyleRuleProcessor* GetRuleProcessor();
+  nsXBLPrototypeResources::sheet_array_type* GetOrCreateStyleSheets();
   nsXBLPrototypeResources::sheet_array_type* GetStyleSheets();
   
   bool HasStyleSheets() {
     return mResources && mResources->mStyleSheetList.Length() > 0;
   }
 
   nsresult FlushSkinSheets();
 
--- a/content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
+++ b/content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
@@ -67,28 +67,28 @@ XMLStylesheetProcessingInstruction::Bind
 }
 
 void
 XMLStylesheetProcessingInstruction::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
 
   ProcessingInstruction::UnbindFromTree(aDeep, aNullParent);
-  UpdateStyleSheetInternal(oldDoc);
+  UpdateStyleSheetInternal(oldDoc, nullptr);
 }
 
 // nsIDOMNode
 
 void
 XMLStylesheetProcessingInstruction::SetNodeValueInternal(const nsAString& aNodeValue,
                                                          ErrorResult& aError)
 {
   nsGenericDOMDataNode::SetNodeValueInternal(aNodeValue, aError);
   if (!aError.Failed()) {
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
 }
 
 // nsStyleLinkElement
 
 NS_IMETHODIMP
 XMLStylesheetProcessingInstruction::GetCharset(nsAString& aCharset)
 {
--- a/content/xml/content/src/XMLStylesheetProcessingInstruction.h
+++ b/content/xml/content/src/XMLStylesheetProcessingInstruction.h
@@ -64,17 +64,17 @@ public:
   NS_IMETHOD GetCharset(nsAString& aCharset) MOZ_OVERRIDE;
 
   virtual void SetData(const nsAString& aData, mozilla::ErrorResult& rv) MOZ_OVERRIDE
   {
     nsGenericDOMDataNode::SetData(aData, rv);
     if (rv.Failed()) {
       return;
     }
-    UpdateStyleSheetInternal(nullptr, true);
+    UpdateStyleSheetInternal(nullptr, nullptr, true);
   }
   using ProcessingInstruction::SetData; // Prevent hiding overloaded virtual function.
 
 protected:
   nsCOMPtr<nsIURI> mOverriddenBaseURI;
 
   already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline) MOZ_OVERRIDE;
   void GetStyleSheetInfo(nsAString& aTitle,
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4273,19 +4273,20 @@ nsStringArraySH::GetProperty(nsIXPConnec
 
 
 // StyleSheetList helper
 
 nsISupports*
 nsStyleSheetListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                               nsWrapperCache **aCache, nsresult *rv)
 {
-  nsDOMStyleSheetList* list = nsDOMStyleSheetList::FromSupports(aNative);
-
-  return list->GetItemAt(aIndex);
+  nsIDOMStyleSheetList* list = static_cast<nsIDOMStyleSheetList*>(aNative);
+  nsCOMPtr<nsIDOMStyleSheet> sheet;
+  list->Item(aIndex, getter_AddRefs(sheet));
+  return sheet;
 }
 
 
 // CSSRuleList scriptable helper
 
 nsISupports*
 nsCSSRuleListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                            nsWrapperCache **aCache, nsresult *aResult)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -959,16 +959,22 @@ DOMInterfaces = {
 'Screen': {
     'nativeType': 'nsScreen',
 },
 
 'ScrollAreaEvent': {
     'nativeType': 'nsDOMScrollAreaEvent',
 },
 
+'ShadowRoot': {
+    'resultNotAddRefed': [
+        'styleSheets'
+    ]
+},
+
 'SharedWorker': {
     'nativeType': 'mozilla::dom::workers::SharedWorker',
     'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
     'implicitJSContext': [ 'constructor' ],
 },
 
 'SharedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6024,30 +6024,31 @@ class CGMemberJITInfo(CGThing):
     """
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
-    def defineJitInfo(self, infoName, opName, opType, infallible, constant,
-                      pure, hasSlot, slotIndex, returnTypes, args):
+    def defineJitInfo(self, infoName, opName, opType, infallible, movable,
+                      aliasSet, hasSlot, slotIndex, returnTypes, args):
         """
+        aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
+
         args is None if we don't want to output argTypes for some
         reason (e.g. we have overloads or we're not a method) and
         otherwise an iterable of the arguments for this method.
         """
-        assert(not constant or pure) # constants are always pure
-        assert(not hasSlot or pure) # Things with slots had better be pure
+        assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
+        assert(not hasSlot or movable) # Things with slots had better be movable
         protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = toStringBool(infallible)
-        conststr = toStringBool(constant)
-        purestr = toStringBool(pure)
+        movablestr = toStringBool(movable)
         slotStr = toStringBool(hasSlot)
         returnType = reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
                             "")
         if args is not None:
             argTypes = "%s_argTypes" % infoName
             args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
             args.append("JSJitInfo::ArgTypeListEnd")
             argTypesDecl = (
@@ -6059,63 +6060,70 @@ class CGMemberJITInfo(CGThing):
         return ("\n"
                 "%s"
                 "static const JSJitInfo %s = {\n"
                 "  { %s },\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  JSJitInfo::%s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
-                "  %s,  /* isConstant. Only relevant for getters. */\n"
-                "  %s,  /* isPure.  Not relevant for setters. */\n"
+                "  %s,  /* isMovable.  Not relevant for setters. */\n"
+                "  JSJitInfo::%s,  /* aliasSet.  Not relevant for setters. */\n"
                 "  %s,  /* hasSlot.  Only relevant for getters. */\n"
                 "  %d,  /* Reserved slot index, if we're stored in a slot, else 0. */\n"
                 "  %s,  /* returnType.  Not relevant for setters. */\n"
                 "  %s,  /* argTypes.  Only relevant for methods */\n"
                 "  nullptr /* parallelNative */\n"
                 "};\n" % (argTypesDecl, infoName, opName, protoID, depth,
-                          opType, failstr, conststr, purestr, slotStr,
+                          opType, failstr, movablestr, aliasSet, slotStr,
                           slotIndex, returnType, argTypes))
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             # We need the cast here because JSJitGetterOp has a "void* self"
             # while we have the right type.
             getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
             getterconst = (self.member.getExtendedAttribute("SameObject") or
                            self.member.getExtendedAttribute("Constant"))
             getterpure = getterconst or self.member.getExtendedAttribute("Pure")
-            assert (getterinfal or (not getterconst and not getterpure))
+            if getterconst:
+                aliasSet = "AliasNone"
+            elif getterpure:
+                aliasSet = "AliasDOMSets"
+            else:
+                aliasSet = "AliasEverything"
+            movable = getterpure and getterinfal
 
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             isInSlot = self.member.getExtendedAttribute("StoreInSlot")
             if isInSlot:
                 slotIndex = INSTANCE_RESERVED_SLOTS + self.member.slotIndex;
                 if slotIndex >= 16 : # JS engine currently allows 16 fixed slots
                     raise TypeError("Make sure we can actually have this many "
                                     "fixed slots, please!")
             else:
                 slotIndex = 0
 
             result = self.defineJitInfo(getterinfo, getter, "Getter",
-                                        getterinfal, getterconst, getterpure,
+                                        getterinfal, movable, aliasSet,
                                         isInSlot, slotIndex,
                                         [self.member.type], None)
             if (not self.member.readonly or
                 self.member.getExtendedAttribute("PutForwards") is not None or
                 self.member.getExtendedAttribute("Replaceable") is not None):
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
                 # union.
                 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, "Setter",
-                                             False, False, False, False, 0,
+                                             False, False, "AliasEverything",
+                                             False, 0,
                                              [BuiltinTypes[IDLBuiltinType.Types.void]],
                                              None)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             name = CppKeywords.checkMethodName(self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
             method = ("(JSJitGetterOp)%s" % name)
@@ -6141,18 +6149,22 @@ class CGMemberJITInfo(CGThing):
                 else:
                     methodInfal = "infallible" in self.descriptor.getExtendedAttributes(self.member)
                 # For now, only bother to output args if we're pure
                 if methodPure:
                     args = sig[1]
                 else:
                     args = None
 
+            if args:
+                aliasSet = "AliasDOMSets"
+            else:
+                aliasSet = "AliasEverything"
             result = self.defineJitInfo(methodinfo, method, "Method",
-                                        methodInfal, False, methodPure, False, 0,
+                                        methodInfal, False, aliasSet, False, 0,
                                         [s[0] for s in sigs], args)
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
     @staticmethod
     def getJSReturnTypeTag(t):
         if t.nullable():
             # Sometimes it might return null, sometimes not
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -2682,22 +2682,18 @@ class IDLAttribute(IDLInterfaceMember):
         if identifier == "TreatNonCallableAsNull":
             raise WebIDLError("TreatNonCallableAsNull cannot be specified on attributes",
                               [attr.location, self.location])
         elif identifier == "SetterThrows" and self.readonly:
             raise WebIDLError("Readonly attributes must not be flagged as "
                               "[SetterThrows]",
                               [self.location])
         elif (((identifier == "Throws" or identifier == "GetterThrows") and
-               (self.getExtendedAttribute("Pure") or
-                self.getExtendedAttribute("StoreInSlot") or
-                self.getExtendedAttribute("SameObject") or
-                self.getExtendedAttribute("Constant"))) or
-              ((identifier == "Pure" or identifier == "SameObject" or
-                identifier == "Constant" or identifier == "StoreInSlot") and
+               self.getExtendedAttribute("StoreInSlot")) or
+              (identifier == "StoreInSlot" and
                (self.getExtendedAttribute("Throws") or
                 self.getExtendedAttribute("GetterThrows")))):
             raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
                               "or [SameObject] or [StoreInSlot]",
                               [attr.location])
         elif identifier == "LenientThis":
             if not attr.noArguments():
                 raise WebIDLError("[LenientThis] must take no arguments",
@@ -3281,20 +3277,16 @@ class IDLMethod(IDLInterfaceMember, IDLS
                               "[Unforgeable]",
                               [attr.location, self.location])
         elif identifier == "SameObject":
             raise WebIDLError("Methods must not be flagged as [SameObject]",
                               [attr.location, self.location]);
         elif identifier == "Constant":
             raise WebIDLError("Methods must not be flagged as [Constant]",
                               [attr.location, self.location]);
-        elif ((identifier == "Pure" and self.getExtendedAttribute("Throws")) or
-              (identifier == "Throws" and self.getExtendedAttribute("Pure"))):
-            raise WebIDLError("Throwing methods can't be [Pure]",
-                              [attr.location]);
         elif identifier == "PutForwards":
             raise WebIDLError("Only attributes support [PutForwards]",
                               [attr.location, self.location])
         elif identifier == "LenientFloat":
             # This is called before we've done overload resolution
             assert len(self.signatures()) == 1
             sig = self.signatures()[0]
             if not sig[0].isVoid():
--- a/dom/imptests/failures/html/microdata/microdata-dom-api/test_001.html.json
+++ b/dom/imptests/failures/html/microdata/microdata-dom-api/test_001.html.json
@@ -13,11 +13,10 @@
   "itemProp.add should normalize \\r as a space": true,
   "itemProp.add should normalize \\n as a space": true,
   "itemProp.add should normalize \\f as a space": true,
   "itemRef.remove must remove useless whitespace when removing tokens": true,
   "itemRef.add must remove whitespace and duplicate when the token already exists": true,
   "itemRef.add should normalize \\t as a space": true,
   "itemRef.add should normalize \\r as a space": true,
   "itemRef.add should normalize \\n as a space": true,
-  "itemRef.add should normalize \\f as a space": true,
-  "itemValue must reflect the src attribute on track elements": true
+  "itemRef.add should normalize \\f as a space": true
 }
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2673,25 +2673,22 @@ NPError NP_CALLBACK
       if (NS_FAILED(rv))
         return NPERR_GENERIC_ERROR;
 
       nsCOMPtr<nsIURI> uriIn;
       rv = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
       if (NS_FAILED(rv))
         return NPERR_GENERIC_ERROR;
 
-      nsCOMPtr<nsIPrompt> prompt;
-      nsPluginHost::GetPrompt(nullptr, getter_AddRefs(prompt));
-
       nsCOMPtr<nsIChannel> channel = GetChannelFromNPP(instance);
 
       char *cookie = (char*)value;
       char c = cookie[len];
       cookie[len] = '\0';
-      rv = cookieService->SetCookieString(uriIn, prompt, cookie, channel);
+      rv = cookieService->SetCookieString(uriIn, nullptr, cookie, channel);
       cookie[len] = c;
       if (NS_SUCCEEDED(rv))
         return NPERR_NO_ERROR;
     }
 
     break;
   case NPNURLVProxy:
     // We don't support setting proxy values, fall through...
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -417,42 +417,16 @@ nsresult nsPluginHost::UserAgent(const c
     *retstring = nullptr;
   }
 
   PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
 
   return res;
 }
 
-nsresult nsPluginHost::GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt)
-{
-  nsresult rv;
-  nsCOMPtr<nsIPrompt> prompt;
-  nsCOMPtr<nsIWindowWatcher> wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
-
-  if (wwatch) {
-    nsCOMPtr<nsIDOMWindow> domWindow;
-    if (aOwner) {
-      nsCOMPtr<nsIDocument> document;
-      aOwner->GetDocument(getter_AddRefs(document));
-      if (document) {
-        domWindow = document->GetWindow();
-      }
-    }
-
-    if (!domWindow) {
-      wwatch->GetWindowByName(NS_LITERAL_STRING("_content").get(), nullptr, getter_AddRefs(domWindow));
-    }
-    rv = wwatch->GetNewPrompter(domWindow, getter_AddRefs(prompt));
-  }
-
-  NS_IF_ADDREF(*aPrompt = prompt);
-  return rv;
-}
-
 nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
                               const char* url,
                               const char* target,
                               nsNPAPIPluginStreamListener* streamListener,
                               const char* altHost,
                               const char* referrer,
                               bool forceJSEnabled)
 {
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -159,18 +159,16 @@ public:
   // Helper that checks if a type is whitelisted in plugin.allowed_types.
   // Always returns true if plugin.allowed_types is not set
   static bool IsTypeWhitelisted(const char *aType);
 
   // checks whether aTag is a "java" plugin tag (a tag for a plugin
   // that does Java)
   static bool IsJavaMIMEType(const char *aType);
 
-  static nsresult GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt);
-
   static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
 
   void PluginCrashed(nsNPAPIPlugin* plugin,
                      const nsAString& pluginDumpID,
                      const nsAString& browserDumpID);
 
   nsNPAPIPluginInstance *FindInstance(const char *mimetype);
   nsNPAPIPluginInstance *FindOldestStoppedInstance();
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -254,16 +254,17 @@ var interfaceNamesInGlobalScope =
     "HTMLAreaElement",
     "HTMLAudioElement",
     "HTMLBaseElement",
     "HTMLBodyElement",
     "HTMLBRElement",
     "HTMLButtonElement",
     "HTMLCanvasElement",
     "HTMLCollection",
+    "HTMLContentElement",
     "HTMLDataElement",
     "HTMLDataListElement",
     "HTMLDirectoryElement",
     "HTMLDivElement",
     "HTMLDListElement",
     "HTMLDocument",
     "HTMLElement",
     "HTMLEmbedElement",
@@ -313,16 +314,17 @@ var interfaceNamesInGlobalScope =
     "HTMLTableColElement",
     "HTMLTableElement",
     "HTMLTableRowElement",
     "HTMLTableSectionElement",
     "HTMLTemplateElement",
     "HTMLTextAreaElement",
     "HTMLTimeElement",
     "HTMLTitleElement",
+    "HTMLTrackElement",
     "HTMLUListElement",
     "HTMLUnknownElement",
     "HTMLVideoElement",
     {name: "IccChangeEvent", b2g: true, pref: "dom.icc.enabled"},
     {name: "IccCardLockError", b2g: true, pref: "dom.icc.enabled"},
     "IDBCursor",
     "IDBCursorWithValue",
     "IDBDatabase",
@@ -446,16 +448,17 @@ var interfaceNamesInGlobalScope =
     {name: "RTCPeerConnectionIceEvent", pref: "media.peerconnection.enabled"},
     {name: "RTCStatsReport", pref: "media.peerconnection.enabled"},
     "Screen",
     "ScriptProcessorNode",
     "ScrollAreaEvent",
     "Selection",
     "SettingsLock",
     "SettingsManager",
+    {name: "ShadowRoot", pref: "dom.webcomponents.enabled"},
     "SimpleGestureEvent",
     {name: "SimpleTest", xbl: false},
     "SmartCardEvent",
     "SpeechRecognitionError",
     "SpeechRecognitionEvent",
     "SpeechSynthesisEvent",
     {name: "SpeechSynthesis", b2g: true},
     {name: "SpeechSynthesisUtterance", b2g: true},
@@ -587,16 +590,19 @@ var interfaceNamesInGlobalScope =
     "SVGZoomEvent",
     {name: "Telephony", b2g: true, pref: "dom.telephony.enabled"},
     {name: "TelephonyCall", b2g: true, pref: "dom.telephony.enabled"},
     {name: "TelephonyCallGroup", b2g: true, pref: "dom.telephony.enabled"},
     "Text",
     "TextDecoder",
     "TextEncoder",
     "TextMetrics",
+    "TextTrack",
+    "TextTrackCueList",
+    "TextTrackList",
     "TimeEvent",
     "TimeRanges",
     "Touch",
     "TouchEvent",
     "TouchList",
     "TrackEvent",
     "TransitionEvent",
     {name: "TreeColumn", xbl: true},
@@ -607,16 +613,19 @@ var interfaceNamesInGlobalScope =
     "UIEvent",
     "UndoManager",
     "URL",
     {name: "UserDataHandler", xbl: true},
     "UserProximityEvent",
     {name: "USSDReceivedEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
     "ValidityState",
     "VideoStreamTrack",
+    "VTTCue",
+    "VTTRegion",
+    "VTTRegionList",
     "WaveShaperNode",
     "WebGLActiveInfo",
     "WebGLBuffer",
     "WebGLFramebuffer",
     "WebGLProgram",
     "WebGLRenderbuffer",
     "WebGLRenderingContext",
     "WebGLShader",
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/inert_style.css
@@ -0,0 +1,10 @@
+/* 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 style is linked in test_shadowroot_inert_link to ensure
+   that link element in ShadowRoot is inert. */
+span {
+  padding-top: 10px;
+}
+
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,6 +1,16 @@
 [DEFAULT]
 
 [test_bug900724.html]
+[test_content_element.html]
+[test_nested_content_element.html]
+[test_dyanmic_content_element_matching.html]
 [test_document_register.html]
 [test_document_register_lifecycle.html]
 [test_template.html]
+[test_shadow_root.html]
+[test_shadow_root_inert_element.html]
+[inert_style.css]
+[test_shadow_root_style.html]
+[test_shadow_root_style_multiple_shadow.html]
+[test_shadow_root_style_order.html]
+[test_style_fallback_content.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_content_element.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for HTMLContent element</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="grabme"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+// Create a ShadowRoot and append some nodes, containing an insertion point with a universal selector.
+var shadow = $("grabme").createShadowRoot();
+shadow.innerHTML = '<span><content id="point"></content></span>';
+
+// Get the insertion point from the ShadowRoot and check that child of host is distributed.
+// Insertion point should match everything because the selector set is empty.
+var insertionPoint = shadow.getElementById("point");
+$("grabme").innerHTML = '<div id="distme"></div>';
+var distNodes = insertionPoint.getDistributedNodes();
+is(distNodes[0], $("distme"), "Child of bound content should be distributed into insertion point with universal selector.");
+is(distNodes.length, 1, "Should only have one child distributed into insertion point.");
+
+// Add another node to bound content and make sure that the node list is static and does not change.
+var someSpan = document.createElement("span");
+$("grabme").appendChild(someSpan);
+is(distNodes.length, 1, "NodeList from getDistributedNodes should be static.");
+
+// Test content select.
+$("grabme").innerHTML = '<div id="first" class="tall"></div><div id="second" class="skinny"></div>';
+shadow.innerHTML = '<span><content select=".tall" id="point"></content></span>';
+var insertionPoint = shadow.getElementById("point");
+distNodes = insertionPoint.getDistributedNodes();
+is(distNodes.length, 1, "Insertion point should only match element with the 'tall' class.");
+is(distNodes[0], $("first"), "Insertion point should only match element with the 'tall' class.");
+
+// Get rid of the select attribute and check that the insertion point matches everything.
+insertionPoint.removeAttribute("select");
+is(insertionPoint.getDistributedNodes().length, 2, "After removing the 'select' attribute, the insertion point should match everything.");
+
+// Set an invalid selector and make sure that nothing is matched.
+insertionPoint.setAttribute("select", "div:first-child");
+is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
+
+// all compound selectors must only be permitted simple selectors.
+insertionPoint.setAttribute("select", "div:first-child, span");
+is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
+
+// Test multiple compound selectors.
+$("grabme").innerHTML = '<div id="first"></div><span id="second"></span><span data-match-me="pickme" id="third"></span>';
+insertionPoint.setAttribute("select", "span[data-match-me=pickme], div");
+distNodes = insertionPoint.getDistributedNodes();
+is(distNodes.length, 2, "Insertion point selector should only match two nodes.");
+is(distNodes[0], $("first"), "First child node should match selector.");
+is(distNodes[1], $("third"), "Third child node should match selector.");
+
+// Test select property
+insertionPoint.select = "#second, #third";
+distNodes = insertionPoint.getDistributedNodes();
+is(distNodes.length, 2, "Insertion point selector (set using property) should only match two nodes.");
+is(distNodes[0], $("second"), "First child node should match selector.");
+is(distNodes[1], $("third"), "Third child node should match selector.");
+is(insertionPoint.select, "#second, #third", "select property should be transparent.");
+
+// Empty set of selectors should match everything.
+insertionPoint.select = "";
+is(insertionPoint.getDistributedNodes().length, 3, "Empty set of selectors (set using property) should match everything.");
+
+// Remove insertion point and make sure that the point does not have any nodes distributed.
+$("grabme").innerHTML = '<div></div><span></span>';
+insertionPoint.removeAttribute("select");
+is(insertionPoint.getDistributedNodes().length, 2, "Insertion point with univeral selector should match two nodes.");
+var insertionParent = insertionPoint.parentNode;
+insertionParent.removeChild(insertionPoint);
+is(insertionPoint.getDistributedNodes().length, 0, "Insertion point should match no nodes after removal.");
+insertionParent.appendChild(insertionPoint);
+is(insertionPoint.getDistributedNodes().length, 2, "Insertion point should match two nodes after appending.");
+
+// Test multiple insertion points and check tree order distribution of points.
+// Append two divs and three spans into host.
+$("grabme").innerHTML = '<div></div><span></span><div></div><span></span><span></span>';
+shadow.innerHTML = '<content select="div" id="divpoint"></content><content select="div, span" id="allpoint"></content>';
+// Insertion point matching div
+var divPoint = shadow.getElementById("divpoint");
+// Insertion point matching span and div
+var allPoint = shadow.getElementById("allpoint");
+
+is(divPoint.getDistributedNodes().length, 2, "Two div nodes should be distributed into divPoint.");
+is(allPoint.getDistributedNodes().length, 3, "Remaining nodes should be distributed into allPoint.");
+
+shadow.removeChild(allPoint);
+is(divPoint.getDistributedNodes().length, 2, "Number of div distributed into insertion point should not change.");
+is(allPoint.getDistributedNodes().length, 0, "Removed insertion point should not have any nodes.");
+
+shadow.insertBefore(allPoint, divPoint);
+is(allPoint.getDistributedNodes().length, 5, "allPoint should have nodes distributed before divPoint.");
+is(divPoint.getDistributedNodes().length, 0, "divPoint should have no distributed nodes because they are all distributed to allPoint.");
+
+// Make sure that fallback content are in the distributed nodes.
+$("grabme").innerHTML = '<div id="one"></div><div id="two"></div>';
+shadow.innerHTML = '<content select="#nothing" id="point"><span id="fallback"></span></content>';
+insertionPoint = shadow.getElementById("point");
+is(insertionPoint.getDistributedNodes().length, 1, "There should be one distributed node from fallback content.");
+is(insertionPoint.getDistributedNodes()[0].id, "fallback", "Distributed node should be fallback content.");
+
+$("grabme").innerHTML = '';
+shadow.innerHTML = '<content select="div" id="point"><span id="one"></span><span id="two"></span></content>';
+insertionPoint = shadow.getElementById("point");
+// Make sure that two fallback nodes are distributed into the insertion point.
+is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
+is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
+is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
+
+// Append a node that gets matched by the insertion point, thus causing the fallback content to be removed.
+var matchingDiv = document.createElement("div");
+matchingDiv.id = "three";
+$("grabme").appendChild(matchingDiv);
+is(insertionPoint.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
+is(insertionPoint.getDistributedNodes()[0].id, "three", "Node distriubted from host should have id of three.");
+
+// Remove the matching node from the host and make sure that the fallback content gets distributed.
+$("grabme").removeChild(matchingDiv);
+is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
+is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
+is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_dynamic_content_element_matching.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for dynamic changes to content matching content elements</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div class="tall" id="bodydiv"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+// Create ShadowRoot.
+var elem = document.createElement("div");
+var root = elem.createShadowRoot();
+
+var redInsertionPoint = document.createElement("content");
+redInsertionPoint.select = "*[data-color=red]";
+
+var blueInsertionPoint = document.createElement("content");
+blueInsertionPoint.select = "*[data-color=blue]";
+
+root.appendChild(redInsertionPoint);
+root.appendChild(blueInsertionPoint);
+
+is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes.");
+is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should have no distrubted nodes.");
+
+var matchElement = document.createElement("div");
+matchElement.setAttribute("data-color", "red");
+elem.appendChild(matchElement);
+
+is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes.");
+is(redInsertionPoint.getDistributedNodes().length, 1, "Red insertion point should match recently inserted div.");
+
+matchElement.setAttribute("data-color", "blue");
+is(blueInsertionPoint.getDistributedNodes().length, 1, "Blue insertion point should match element after changing attribute value.");
+is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should not match element after changing attribute value.");
+
+matchElement.removeAttribute("data-color");
+
+is(blueInsertionPoint.getDistributedNodes().length, 0, "Blue insertion point should have no distributed nodes after removing the matching attribute.");
+is(redInsertionPoint.getDistributedNodes().length, 0, "Red insertion point should have no distrubted nodes after removing the matching attribute.");
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_nested_content_element.html
@@ -0,0 +1,127 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for HTMLContent element</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="grabme"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+
+/**
+ * Constructs a node with a nested ShadowRoot with the following structure:
+ * <span> - - - - - - - - - - <ShadowRoot>
+ *   <span>                     <span> - - - - - - - - - - <ShadowRoot>
+ *    id=one                      id=four                    <span>
+ *    data-color=red              data-color=orange            id=eleven
+ *   <span>                       <span>                     <content>
+ *    id=two                        id=five                    id=twelve
+ *    data-color=blue               data-color=purple          select=secondSelect
+ *   <span>                       <content>                  <span>
+ *    id=three                      id=six                     id=thirteen
+ *    data-color=green              select=firstSelect
+ *                                  <span>
+ *                                    id=seven
+ *                                  <content>
+ *                                    id=eight
+ *                                  <span>
+ *                                    id=nine
+ *                                <span>
+ *                                  id=ten
+ *                                  data-color=grey
+ */
+function constructTree(firstSelect, secondSelect) {
+  var rootSpan = document.createElement("span");
+  rootSpan.innerHTML = '<span id="one" data-color="red"></span><span id="two" data-color="blue"></span><span id="three" data-color="green"></span>';
+  var firstShadow = rootSpan.createShadowRoot();
+  firstShadow.innerHTML = '<span id="four" data-color="orange"><span id="five" data-color="purple"></span><content id="six" select="' + firstSelect + '"><span id="seven"></span><content id="eight"></content><span id="nine"></span></content><span id="ten"></span></span>';
+  var secondShadow = firstShadow.firstChild.createShadowRoot();
+  secondShadow.innerHTML = '<span id="eleven"></span><content id="twelve" select="' + secondSelect + '"></content><span id="thirteen"></span>';
+  return rootSpan;
+}
+
+// Create a tree with content that matches on everything and check node distribution.
+var allSpan = constructTree("*", "*");
+var firstContent = allSpan.shadowRoot.getElementById("six");
+var firstDistNodes = firstContent.getDistributedNodes();
+is(firstDistNodes.length, 3, "Universal selector should match all nodes.");
+// Check the order of the distributed nodes.
+is(firstDistNodes.item(0).id, "one", "First distributed node should have id of 'one'");
+is(firstDistNodes.item(1).id, "two", "Second distributed node should have id of 'two'");
+is(firstDistNodes.item(2).id, "three", "Third distributed node should have id of 'three'");
+var secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve");
+var secondDistNodes = secondContent.getDistributedNodes();
+is(secondDistNodes.length, 5, "Universial selector should match all nodes including those distributed into content.");
+// Check the order of the distribute nodes.
+is(secondDistNodes.item(0).id, "five", "First distributed node should have id of 'five'");
+is(secondDistNodes.item(1).id, "one", "Second distributed (reprojected) node should have id of 'one'");
+is(secondDistNodes.item(2).id, "two", "Third distributed (reprojected) node should have id of 'two'");
+is(secondDistNodes.item(3).id, "three", "Fourth distributed (reprojected) node should have id of 'three'");
+is(secondDistNodes.item(4).id, "ten", "Fifth distributed node should have id of 'ten'");
+
+// Append an element after id=two and make sure that it is inserted into the corrent
+// position in the insertion points.
+var additionalSpan = document.createElement("span");
+additionalSpan.id = "additional";
+
+// Insert the additional span in the third position, before the span with id=three.
+allSpan.insertBefore(additionalSpan, allSpan.childNodes.item(2));
+firstDistNodes = firstContent.getDistributedNodes();
+secondDistNodes = secondContent.getDistributedNodes();
+is(firstDistNodes.length, 4, "First insertion point should match one more node.");
+is(firstDistNodes.item(2).id, "additional", "Additional span should have been inserted into the third position of the first insertion point.");
+
+is(secondDistNodes.length, 6, "Second insertion point should match one more node.");
+is(secondDistNodes.item(3).id, "additional", "Additional span should have been inserted into the fourth position of the second insertion point.");
+
+function nodeListDoesNotContain(nodeList, element) {
+  for (var i = 0; i < nodeList.length; i++) {
+    if (nodeList[i] == element) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Remove the span with id=one and check that it is removed from all insertion points.
+allSpan = constructTree("*", "*");
+var spanOne = allSpan.firstChild;
+allSpan.removeChild(spanOne);
+firstContent = allSpan.shadowRoot.getElementById("six");
+ok(nodeListDoesNotContain(firstContent.getDistributedNodes(), spanOne), "Child removed from host should not appear in insertion point node list.");
+secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve");
+ok(nodeListDoesNotContain(secondContent.getDistributedNodes(), spanOne), "Child removed from host should not appear in nested insertion point node list.");
+
+// Make sure <content> in fallback content is inactive.
+// First insertion point will not match anything and will use fallback content.
+allSpan = constructTree("#nomatch", "*");
+var fallbackInsertionPoint = allSpan.shadowRoot.getElementById("eight");
+is(fallbackInsertionPoint.getDistributedNodes().length, 0, "Insertion points in default content should be inactive.");
+
+// Insertion points with non-universal selectors.
+allSpan = constructTree("span[data-color=blue]", "*");
+firstContent = allSpan.shadowRoot.getElementById("six");
+is(firstContent.getDistributedNodes().length, 1, "Insertion point selector should only match one node.");
+is(firstContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector.");
+secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve");
+is(secondContent.getDistributedNodes().length, 3, "Second insertion point should match two children and one reprojected node.");
+is(secondContent.getDistributedNodes()[1].dataset.color, "blue", "Projected node should match selector.");
+
+allSpan = constructTree("span[data-color=blue]", "span[data-color=blue]");
+firstContent = allSpan.shadowRoot.getElementById("six");
+is(firstContent.getDistributedNodes().length, 1, "Insertion point selector should only match one node.");
+is(firstContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector.");
+secondContent = allSpan.shadowRoot.firstChild.shadowRoot.getElementById("twelve");
+is(secondContent.getDistributedNodes().length, 1, "Insertion point should only match reprojected node.");
+is(secondContent.getDistributedNodes()[0].dataset.color, "blue", "Projected node should match selector.");
+
+// Make sure that dynamically appended default content will get distributed.
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for ShadowRoot</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="movedtoshadow" class="testclass"></div>
+<svg id="svgmovedtoshadow"></svg>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+// Create ShadowRoot.
+var element = document.createElement("div");
+ok(!element.shadowRoot, "div element should not have a shadow root.");
+var shadow = element.createShadowRoot();
+is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
+
+// Move an element from the document to the ShadowRoot.
+var inShadowEl = document.getElementById("movedtoshadow");
+var inShadowSVGEl = document.getElementById("svgmovedtoshadow");
+
+// Test getElementById
+ok(!shadow.getElementById("movedtoshadow"), "Element not in ShadowRoot should not be accessible from ShadowRoot API.");
+ok(!shadow.getElementById("svgmovedtoshadow"), "SVG element not in ShadowRoot should not be accessible from ShadowRoot API.");
+shadow.appendChild(inShadowEl);
+shadow.appendChild(inShadowSVGEl);
+is(shadow.getElementById("movedtoshadow"), inShadowEl, "Element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+ok(!document.getElementById("movedtoshadow"), "Element appended to a ShadowRoot should not be accessible from document.");
+is(shadow.getElementById("svgmovedtoshadow"), inShadowSVGEl, "SVG element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+ok(!document.getElementById("svgmovedtoshadow"), "SVG element appended to a ShadowRoot should not be accessible from document.");
+
+// Test getElementsByClassName
+is(document.getElementsByClassName("testclass").length, 0, "Element removed from DOM should not be accessible by DOM accessors.");
+is(shadow.getElementsByClassName("testclass").length, 1, "Element added to ShadowRoot should be accessible by ShadowRoot API.");
+
+// Test getElementsByTagName{NS}
+is(document.getElementsByTagName("div").length, 0, "Element removed from DOM should not be accessible from DOM accessors.");
+is(shadow.getElementsByTagName("div").length, 1, "Elements in the ShadowRoot should be accessible from the ShadowRoot API.");
+is(document.getElementsByTagName("svg").length, 0, "SVG elements removed from DOM should not be accessible from DOM accessors.");
+is(shadow.getElementsByTagName("svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+
+// Remove elements from ShadowRoot and make sure that they are no longer accessible via the ShadowRoot API.
+shadow.removeChild(inShadowEl);
+shadow.removeChild(inShadowSVGEl);
+ok(!shadow.getElementById("movedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+ok(!shadow.getElementById("svgmovedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+is(shadow.getElementsByClassName("testclass").length, 0, "ShadowRoot getElementsByClassName should not be able to access elements removed from ShadowRoot.");
+is(shadow.getElementsByTagName("svg").length, 0, "ShadowRoot getElementsByTagName should not be able to access elements removed from ShadowRoot.");
+is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 0, "ShadowRoot getElementsByTagNameNS should not be able to access elements removed from ShadowRoot.");
+
+// Test querySelector on element in a ShadowRoot.
+element = document.createElement("div");
+shadow = element.createShadowRoot();
+var parentDiv = document.createElement("div");
+var childSpan = document.createElement("span");
+childSpan.id = "innerdiv";
+parentDiv.appendChild(childSpan);
+is(parentDiv.querySelector("#innerdiv"), childSpan, "ID query selector should work on element in ShadowRoot.");
+is(parentDiv.querySelector("span"), childSpan, "Tag query selector should work on element in ShadowRoot.");
+
+// Test that exception is thrown when trying to create a cycle with host node.
+element = document.createElement("div");
+shadow = element.createShadowRoot();
+try {
+  shadow.appendChild(element);
+  ok(false, "Excpetion should be thrown when creating a cycle with host content.");
+} catch (ex) {
+  ok(true, "Excpetion should be thrown when creating a cycle with host content.");
+}
+
+// Basic innerHTML tests.
+shadow.innerHTML = '<span id="first"></span><div id="second"></div>';
+is(shadow.childNodes.length, 2, "There should be two children in the ShadowRoot.");
+is(shadow.getElementById("second").tagName, "DIV", "Elements created by innerHTML should be accessible by ShadowRoot API.");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for inert elements in ShadowRoot</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runChecks();">
+<div id="grabme"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+
+var element = document.getElementById("grabme");
+var shadow = element.createShadowRoot();
+
+// Check that <base> is inert.
+shadow.innerHTML = '<base href="http://www.example.org/" />';
+isnot(document.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
+
+SimpleTest.waitForExplicitFinish();
+
+// Check that <link> is inert.
+var numStyleBeforeLoad = document.styleSheets.length;
+
+shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
+shadow.applyAuthorStyles = true;
+var shadowSpan = shadow.getElementById("shadowspan");
+var shadowStyle = shadow.getElementById("shadowlink");
+
+function runChecks() {
+  isnot(getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
+  is(document.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
+  is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
+  // Remove link to make sure we don't get assertions.
+  shadow.removeChild(shadowStyle);
+  SimpleTest.finish();
+};
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for ShadowRoot styling</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div class="tall" id="bodydiv"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+// Create ShadowRoot.
+var elem = document.createElement("div");
+var root = elem.createShadowRoot();
+
+// A style element that will be appended into the ShadowRoot.
+var shadowStyle = document.createElement("style");
+shadowStyle.innerHTML = ".tall { height: 100px; } .fat { padding-left: inherit; }";
+
+root.innerHTML = '<div id="divtostyle" class="tall fat"></div>';
+var divToStyle = root.getElementById("divtostyle");
+
+// Make sure styleSheet counts are correct after appending a style to the ShadowRoot.
+is(document.styleSheets.length, 1, "There should only be one style sheet on the document from the test style sheet.");
+is(root.styleSheets.length, 0, "The ShadowRoot should have no style sheets.");
+root.appendChild(shadowStyle);
+is(document.styleSheets.length, 1, "Styles in the ShadowRoot element should not be accessible from the document.");
+is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet from the appened style.");
+is(root.styleSheets[0].ownerNode, shadowStyle, "First style in ShadowRoot should match the style that was just appended.");
+
+var dummyStyle = document.createElement("style");
+root.appendChild(dummyStyle);
+is(root.styleSheets.length, 2, "ShadowRoot should have an additional style from appending dummyStyle.");
+is(root.styleSheets[1].ownerNode, dummyStyle, "Second style in ShadowRoot should be the dummyStyle.");
+root.removeChild(dummyStyle);
+is(root.styleSheets.length, 1, "Removing dummyStyle should remove it from the ShadowRoot style sheets.");
+is(root.styleSheets[0].ownerNode, shadowStyle, "The style sheet remaining in the ShadowRoot should be shadowStyle.");
+
+// Make sure that elements outside of the ShadowRoot are not affected by the ShadowRoot style.
+isnot(getComputedStyle(document.getElementById("bodydiv"), null).getPropertyValue("height"), "100px", "Style sheets in ShadowRoot should not apply to elements no in the ShadowRoot.");
+
+// Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
+is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
+
+// Tests for applyAuthorStyles.
+var authorStyle = document.createElement("style");
+authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
+document.body.appendChild(authorStyle);
+
+is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
+isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+root.applyAuthorStyles = true;
+is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
+is(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
+root.applyAuthorStyles = false;
+is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
+isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+
+// Test dynamic changes to style in ShadowRoot.
+root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
+divToStyle = root.getElementById("divtostyle");
+var dummyShadowStyle = document.createElement("style");
+dummyShadowStyle.innerHTML = ".dummy { height: 300px; }";
+root.appendChild(dummyShadowStyle);
+is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "300px", "Dummy element in ShadowRoot should be styled by style in ShadowRoot.");
+dummyShadowStyle.innerHTML = ".dummy { height: 200px; }";
+is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Dynamic changes to styles in ShadowRoot should change style of affected elements.");
+
+// Test id selector in ShadowRoot style.
+root.innerHTML = '<style>#divtostyle { padding-top: 10px; }</style><div id="divtostyle"></div>';
+divToStyle = root.getElementById("divtostyle");
+is(getComputedStyle(divToStyle, null).getPropertyValue("padding-top"), "10px", "ID selector in style selector should match element.");
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_multiple_shadow.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for ShadowRoot styles with multiple ShadowRoot on host.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div class="tall" id="bodydiv"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+// Create ShadowRoot.
+var elem = document.createElement("div");
+var firstRoot = elem.createShadowRoot();
+var secondRoot = elem.createShadowRoot();
+var thirdRoot = elem.createShadowRoot();
+
+// A style element that will be appended into the ShadowRoot.
+var firstStyle = document.createElement("style");
+firstRoot.appendChild(firstStyle);
+is(firstRoot.styleSheets.length, 1, "firstStyle should be the only style in firstRoot.");
+is(firstRoot.styleSheets[0].ownerNode, firstStyle, "firstStyle should in the ShadowRoot styleSheets.");
+
+var secondStyle = document.createElement("style");
+secondRoot.appendChild(secondStyle);
+is(secondRoot.styleSheets.length, 1, "secondStyle should be the only style in secondRoot.");
+is(secondRoot.styleSheets[0].ownerNode, secondStyle, "secondStyle should in the ShadowRoot styleSheets.");
+
+var thirdStyle = document.createElement("style");
+thirdRoot.appendChild(thirdStyle);
+is(thirdRoot.styleSheets.length, 1, "thirdStyle should be the only style in thirdRoot.");
+is(thirdRoot.styleSheets[0].ownerNode, thirdStyle, "thirdStyle should in the ShadowRoot styleSheets.");
+
+// Check the stylesheet counts again to make sure that none of the style sheets leaked into the older ShadowRoots.
+is(firstRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot.");
+is(secondRoot.styleSheets.length, 1, "Adding a stylesheet to a younger ShadowRoot should not affect stylesheets in the older ShadowRoot.");
+
+// Remove styles and make sure they are removed from the correct ShadowRoot.
+firstRoot.removeChild(firstStyle);
+is(firstRoot.styleSheets.length, 0, "firstRoot should no longer have any styles.");
+
+thirdRoot.removeChild(thirdStyle);
+is(thirdRoot.styleSheets.length, 0, "thirdRoot should no longer have any styles.");
+
+secondRoot.removeChild(secondStyle);
+is(secondRoot.styleSheets.length, 0, "secondRoot should no longer have any styles.");
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for ShadowRoot style order</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=806506">Bug 806506</a>
+<script>
+// Create ShadowRoot.
+var elem = document.createElement("div");
+var root = elem.createShadowRoot();
+
+// Style elements that will be appended into the ShadowRoot.
+var tallShadowStyle = document.createElement("style");
+tallShadowStyle.innerHTML = ".tall { height: 100px; }";
+
+var veryTallShadowStyle = document.createElement("style");
+veryTallShadowStyle.innerHTML = ".tall { height: 200px; }";
+
+var divToStyle = document.createElement("div");
+divToStyle.setAttribute("class", "tall");
+root.appendChild(divToStyle);
+
+// Make sure the styles are applied in tree order.
+root.appendChild(tallShadowStyle);
+is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet.");
+is(window.getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "Style in ShadowRoot should apply to elements in ShadowRoot.");
+root.appendChild(veryTallShadowStyle);
+is(root.styleSheets.length, 2, "ShadowRoot should have two style sheets.");
+is(window.getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Style in ShadowRoot should apply to elements in ShadowRoot in tree order.");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+-->
+<head>
+  <title>Test for styling fallback content</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="grabme"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+<script>
+var host = document.getElementById("grabme");
+var shadow = host.createShadowRoot();
+shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><content><span id="innerspan">Hello</span></content></span>';
+var innerStyle = shadow.getElementById("innerstyle");
+
+innerStyle.innerHTML = '#innerspan { margin-top: 10px; }';
+var innerSpan = shadow.getElementById("innerspan");
+is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "10px", "Default content should be style by id selector.");
+
+innerStyle.innerHTML = '#container > content > #innerspan { margin-top: 30px; }';
+is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "30px", "Default content should be style by child combinators.");
+</script>
+</body>
+</html>
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -274,19 +274,19 @@ partial interface Document {
 // http://dvcs.w3.org/hg/undomanager/raw-file/tip/undomanager.html
 partial interface Document {
     [Pref="dom.undo_manager.enabled"]
     readonly attribute UndoManager? undoManager;
 };
 
 // http://dev.w3.org/2006/webapi/selectors-api2/#interface-definitions
 partial interface Document {
-  [Throws]
+  [Throws, Pure]
   Element?  querySelector(DOMString selectors);
-  [Throws]
+  [Throws, Pure]
   NodeList  querySelectorAll(DOMString selectors);
 
   //(Not implemented)Element?  find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
   //(Not implemented)NodeList  findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
 };
 
 //  Mozilla extensions of various sorts
 partial interface Document {
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -51,17 +51,17 @@ interface Element : Node {
   void removeAttributeNS(DOMString? namespace, DOMString localName);
   [Pure]
   boolean hasAttribute(DOMString name);
   [Pure]
   boolean hasAttributeNS(DOMString? namespace, DOMString localName);
 
   [Pure]
   HTMLCollection getElementsByTagName(DOMString localName);
-  [Throws]
+  [Throws, Pure]
   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
   [Pure]
   HTMLCollection getElementsByClassName(DOMString classNames);
 
   /**
    * The ratio of font-size-inflated text font size to computed font
    * size for this element. This will query the element for its primary frame,
    * and then use this to get font size inflation information about the frame.
@@ -81,17 +81,17 @@ interface Element : Node {
 
   // Selectors API
   /**
    * Returns whether this element would be selected by the given selector
    * string.
    *
    * See <http://dev.w3.org/2006/webapi/selectors-api2/#matchesselector>
    */
-  [Throws]
+  [Throws, Pure]
   boolean mozMatchesSelector(DOMString selector);
 
   // Proprietary extensions
   /**
    * Set this during a mousedown event to grab and retarget all mouse events
    * to this element until the mouse button is released or releaseCapture is
    * called. If retargetToElement is true, then all events are targetted at
    * this element. If false, events can also fire at descendants of this
@@ -183,17 +183,25 @@ partial interface Element {
   [Pure,SetterThrows,TreatNullAs=EmptyString]
   attribute DOMString outerHTML;
   [Throws]
   void insertAdjacentHTML(DOMString position, DOMString text);
 };
 
 // http://www.w3.org/TR/selectors-api/#interface-definitions
 partial interface Element {
-  [Throws]
+  [Throws, Pure]
   Element?  querySelector(DOMString selectors);
-  [Throws]
+  [Throws, Pure]
   NodeList  querySelectorAll(DOMString selectors);
 };
 
+// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#shadow-root-object
+partial interface Element {
+  [Throws,Pref="dom.webcomponents.enabled"]
+  ShadowRoot createShadowRoot();
+  [Pref="dom.webcomponents.enabled"]
+  readonly attribute ShadowRoot? shadowRoot;
+};
+
 Element implements ChildNode;
 Element implements NonDocumentTypeChildNode;
 Element implements ParentNode;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLContentElement.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
+ *
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+ * Opera Software ASA. You are granted a license to use, reproduce
+ * and create derivative works of this document.
+ */
+
+interface HTMLContentElement : HTMLElement
+{
+  attribute DOMString select;
+  NodeList getDistributedNodes();
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ShadowRoot.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface ShadowRoot : DocumentFragment
+{
+  Element? getElementById(DOMString elementId);
+  HTMLCollection getElementsByTagName(DOMString localName);
+  HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
+  HTMLCollection getElementsByClassName(DOMString classNames);
+  [SetterThrows,TreatNullAs=EmptyString]
+  attribute DOMString innerHTML;
+  attribute boolean applyAuthorStyles;
+  readonly attribute StyleSheetList styleSheets;
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -115,16 +115,17 @@ WEBIDL_FILES = [
     'HTMLAreaElement.webidl',
     'HTMLAudioElement.webidl',
     'HTMLBaseElement.webidl',
     'HTMLBodyElement.webidl',
     'HTMLBRElement.webidl',
     'HTMLButtonElement.webidl',
     'HTMLCanvasElement.webidl',
     'HTMLCollection.webidl',
+    'HTMLContentElement.webidl',
     'HTMLDataElement.webidl',
     'HTMLDataListElement.webidl',
     'HTMLDirectoryElement.webidl',
     'HTMLDivElement.webidl',
     'HTMLDListElement.webidl',
     'HTMLDocument.webidl',
     'HTMLElement.webidl',
     'HTMLEmbedElement.webidl',
@@ -268,16 +269,17 @@ WEBIDL_FILES = [
     'RTCIceCandidate.webidl',
     'RTCPeerConnection.webidl',
     'RTCSessionDescription.webidl',
     'RTCStatsReport.webidl',
     'Screen.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'SettingsManager.webidl',
+    'ShadowRoot.webidl',
     'SharedWorker.webidl',
     'SharedWorkerGlobalScope.webidl',
     'SimpleGestureEvent.webidl',
     'SourceBuffer.webidl',
     'SourceBufferList.webidl',
     'StorageType.webidl',
     'StyleSheet.webidl',
     'SVGAElement.webidl',
--- a/editor/libeditor/html/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/html/nsHTMLEditUtils.cpp
@@ -636,16 +636,17 @@ static const nsElementInfo kElements[eHT
   ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
   ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT,
        GROUP_NONE),
   ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT),
+  ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
   ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(datalist, true, false, GROUP_PHRASE,
        GROUP_OPTIONS | GROUP_INLINE_ELEMENT),
   ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT),
   ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
   ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI),
   ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -11,16 +11,17 @@
 #include "mozilla/RefPtr.h"             // for TemporaryRef, RefCounted
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"
+#include <vector>
 
 /**
  * Different elements of a web pages are rendered into separate "layers" before
  * they are flattened into the final image that is brought to the screen.
  * See Layers.h for more informations about layers and why we use retained
  * structures.
  * Most of the documentation for layers is directly in the source code in the
  * form of doc comments. An overview can also be found in the the wiki:
@@ -279,26 +280,35 @@ public:
 
   /**
    * Declare an offset to use when rendering layers. This will be ignored when
    * rendering to a target instead of the screen.
    */
   virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) = 0;
 
   /**
-   * Tell the compositor to actually draw a quad. What to do draw and how it is
+   * Tell the compositor to draw a quad. What to do draw and how it is
    * drawn is specified by aEffectChain. aRect is the quad to draw, in user space.
    * aTransform transforms from user space to screen space. If texture coords are
    * required, these will be in the primary effect in the effect chain.
    */
   virtual void DrawQuad(const gfx::Rect& aRect, const gfx::Rect& aClipRect,
                         const EffectChain& aEffectChain,
                         gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform) = 0;
 
   /**
+   * Tell the compositor to draw lines connecting the points. Behaves like
+   * DrawQuad.
+   */
+  virtual void DrawLines(const std::vector<gfx::Point>& aLines, const gfx::Rect& aClipRect,
+                         const gfx::Color& aColor,
+                         gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform)
+  { /* Should turn into pure virtual once implemented in D3D */ }
+
+  /**
    * Start a new frame.
    *
    * aInvalidRect is the invalid region of the screen; it can be ignored for
    * compositors where the performance for compositing the entire window is
    * sufficient.
    *
    * aClipRectIn is the clip rect for the window in window space (optional).
    * aTransform is the transform from user space to window space.
--- a/gfx/layers/d3d9/DeviceManagerD3D9.h
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.h
@@ -201,17 +201,17 @@ public:
    */
   TemporaryRef<IDirect3DTexture9> CreateTexture(const gfx::IntSize &aSize,
                                                 _D3DFORMAT aFormat,
                                                 D3DPOOL aPool,
                                                 TextureSourceD3D9* aTextureHostIDirect3DTexture9);
 #ifdef DEBUG
   // Looks for aFind in the list of texture hosts.
   // O(n) so only use for assertions.
-  bool DeviceManagerD3D9::IsInTextureHostList(TextureSourceD3D9* aFind);
+  bool IsInTextureHostList(TextureSourceD3D9* aFind);
 #endif
 
   /**
    * This function verifies the device is ready for rendering, internally this
    * will test the cooperative level of the device and reset the device if
    * needed. If this returns false subsequent rendering calls may return errors.
    */
   DeviceManagerState VerifyReadyForRendering();
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -994,21 +994,41 @@ struct MOZ_STACK_CLASS AutoBindTexture
     mTexture->BindTexture(aTextureUnit);
   }
 
 private:
   TextureSourceOGL* mTexture;
 };
 
 void
-CompositorOGL::DrawQuad(const Rect& aRect,
-                        const Rect& aClipRect,
-                        const EffectChain &aEffectChain,
-                        Float aOpacity,
-                        const gfx::Matrix4x4 &aTransform)
+CompositorOGL::DrawLines(const std::vector<gfx::Point>& aLines, const gfx::Rect& aClipRect,
+                         const gfx::Color& aColor,
+                         gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform)
+{
+  mGLContext->fLineWidth(2.0);
+
+  EffectChain effects;
+  effects.mPrimaryEffect = new EffectSolidColor(aColor);
+
+  for (int32_t i = 0; i < (int32_t)aLines.size() - 1; i++) {
+    const gfx::Point& p1 = aLines[i];
+    const gfx::Point& p2 = aLines[i+1];
+    DrawQuadInternal(Rect(p1.x, p2.y, p2.x - p1.x, p1.y - p2.y),
+                     aClipRect, effects, aOpacity, aTransform,
+                     LOCAL_GL_LINE_STRIP);
+  }
+}
+
+void
+CompositorOGL::DrawQuadInternal(const Rect& aRect,
+                                const Rect& aClipRect,
+                                const EffectChain &aEffectChain,
+                                Float aOpacity,
+                                const gfx::Matrix4x4 &aTransform,
+                                GLuint aDrawMode)
 {
   PROFILER_LABEL("CompositorOGL", "DrawQuad");
   MOZ_ASSERT(mFrameInProgress, "frame not started");
 
   Rect clipRect = aClipRect;
   if (!mTarget) {
     clipRect.MoveBy(mRenderOffset.x, mRenderOffset.y);
   }
@@ -1092,17 +1112,17 @@ CompositorOGL::DrawQuad(const Rect& aRec
 
       AutoBindTexture bindMask;
       if (maskType != MaskNone) {
         bindMask.Bind(sourceMask, LOCAL_GL_TEXTURE0);
         program->SetMaskTextureUnit(0);
         program->SetMaskLayerTransform(maskQuadTransform);
       }
 
-      BindAndDrawQuad(program);
+      BindAndDrawQuad(program, false, aDrawMode);
     }
     break;
 
   case EFFECT_BGRA:
   case EFFECT_BGRX:
   case EFFECT_RGBA:
   case EFFECT_RGBX: {
       TexturedEffect* texturedEffect =
@@ -1538,44 +1558,50 @@ CompositorOGL::QuadVBOFlippedTexCoordsAt
   mGLContext->fVertexAttribPointer(aAttribIndex, 2,
                                     LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0,
                                     (GLvoid*) QuadVBOFlippedTexCoordOffset());
 }
 
 void
 CompositorOGL::BindAndDrawQuad(GLuint aVertAttribIndex,
                                GLuint aTexCoordAttribIndex,
-                               bool aFlipped)
+                               bool aFlipped,
+                               GLuint aDrawMode)
 {
   BindQuadVBO();
   QuadVBOVerticesAttrib(aVertAttribIndex);
 
   if (aTexCoordAttribIndex != GLuint(-1)) {
     if (aFlipped)
       QuadVBOFlippedTexCoordsAttrib(aTexCoordAttribIndex);
     else
       QuadVBOTexCoordsAttrib(aTexCoordAttribIndex);
 
     mGLContext->fEnableVertexAttribArray(aTexCoordAttribIndex);
   }
 
   mGLContext->fEnableVertexAttribArray(aVertAttribIndex);
-  mGLContext->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+  if (aDrawMode == LOCAL_GL_LINE_STRIP) {
+    mGLContext->fDrawArrays(aDrawMode, 1, 2);
+  } else {
+    mGLContext->fDrawArrays(aDrawMode, 0, 4);
+  }
   mGLContext->fDisableVertexAttribArray(aVertAttribIndex);
 
   if (aTexCoordAttribIndex != GLuint(-1)) {
     mGLContext->fDisableVertexAttribArray(aTexCoordAttribIndex);
   }
 }
 
 void
 CompositorOGL::BindAndDrawQuad(ShaderProgramOGL *aProg,
-                               bool aFlipped)
+                               bool aFlipped,
+                               GLuint aDrawMode)
 {
   NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
   BindAndDrawQuad(aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib),
                   aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib),
-                  aFlipped);
+                  aFlipped, aDrawMode);
 }
 
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -93,17 +93,28 @@ public:
 
   virtual void SetRenderTarget(CompositingRenderTarget *aSurface) MOZ_OVERRIDE;
   virtual CompositingRenderTarget* GetCurrentRenderTarget() MOZ_OVERRIDE;
 
   virtual void DrawQuad(const gfx::Rect& aRect,
                         const gfx::Rect& aClipRect,
                         const EffectChain &aEffectChain,
                         gfx::Float aOpacity,
-                        const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE;
+                        const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE
+  {
+    DrawQuadInternal(aRect, aClipRect, aEffectChain,
+                     aOpacity, aTransform, LOCAL_GL_TRIANGLE_STRIP);
+  }
+
+  virtual void DrawLines(const std::vector<gfx::Point>& aLines,
+                         const gfx::Rect& aClipRect,
+                         const gfx::Color& aColor,
+                         gfx::Float aOpacity,
+                         const gfx::Matrix4x4 &aTransform) MOZ_OVERRIDE;
+
 
   virtual void EndFrame() MOZ_OVERRIDE;
   virtual void EndFrameForExternalComposition(const gfxMatrix& aTransform) MOZ_OVERRIDE;
   virtual void AbortFrame() MOZ_OVERRIDE;
 
   virtual bool SupportsPartialTextureUpdate() MOZ_OVERRIDE;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) MOZ_OVERRIDE
@@ -166,16 +177,23 @@ public:
   /**
    * The compositor provides with temporary textures for use with direct
    * textruing like gralloc texture.
    * Doing so lets us use gralloc the way it has been designed to be used
    * (see https://wiki.mozilla.org/Platform/GFX/Gralloc)
    */
   GLuint GetTemporaryTexture(GLenum aUnit);
 private:
+  virtual void DrawQuadInternal(const gfx::Rect& aRect,
+                                const gfx::Rect& aClipRect,
+                                const EffectChain &aEffectChain,
+                                gfx::Float aOpacity,
+                                const gfx::Matrix4x4 &aTransformi,
+                                GLuint aDrawMode);
+
   /** 
    * Context target, nullptr when drawing directly to our swap chain.
    */
   RefPtr<gfx::DrawTarget> mTarget;
 
   /** Widget associated with this compositor */
   nsIWidget *mWidget;
   nsIntSize mWidgetSize;
@@ -286,19 +304,21 @@ private:
   GLintptr QuadVBOFlippedTexCoordOffset() { return sizeof(float)*8*2; }
 
   void BindQuadVBO();
   void QuadVBOVerticesAttrib(GLuint aAttribIndex);
   void QuadVBOTexCoordsAttrib(GLuint aAttribIndex);
   void QuadVBOFlippedTexCoordsAttrib(GLuint aAttribIndex);
   void BindAndDrawQuad(GLuint aVertAttribIndex,
                        GLuint aTexCoordAttribIndex,
-                       bool aFlipped = false);
+                       bool aFlipped = false,
+                       GLuint aDrawMode = LOCAL_GL_TRIANGLE_STRIP);
   void BindAndDrawQuad(ShaderProgramOGL *aProg,
-                       bool aFlipped = false);
+                       bool aFlipped = false,
+                       GLuint aDrawMode = LOCAL_GL_TRIANGLE_STRIP);
   void BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
                                       const gfx::Rect& aTexCoordRect,
                                       TextureSource *aTexture);
 
   void CleanupResources();
 
   /**
    * Copies the content of our backbuffer to the set transaction target.
--- a/js/src/config/system-headers
+++ b/js/src/config/system-headers
@@ -1126,8 +1126,9 @@ unicode/uclean.h
 unicode/ucol.h
 unicode/udat.h
 unicode/udatpg.h
 unicode/uenum.h
 unicode/unum.h
 unicode/ustring.h
 unicode/utypes.h
 #endif
+libutil.h
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3192,17 +3192,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
         JS_ASSERT(generationAddr);
 
         masm.loadPtr(*expandoAndGenerationAddr, tempVal.scratchReg());
         masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(),
                               &failDOMProxyCheck);
 
         masm.load32(*generationAddr, scratch);
         masm.branch32(Assembler::NotEqual,
-                      Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, expando)),
+                      Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, generation)),
                       scratch, &failDOMProxyCheck);
 
         masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal);
     } else {
         masm.loadValue(expandoAddr, tempVal);
     }
 
     // If the incoming object does not have an expando object then we're sure we're not
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -678,22 +678,26 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
     masm.loadValue(expandoSlotAddr, tempVal);
 
     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
         masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck);
 
         ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
         masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg());
 
-        masm.branch32(Assembler::NotEqual, Address(tempVal.scratchReg(), sizeof(Value)),
-                                                   Imm32(expandoAndGeneration->generation),
-                                                   &failDOMProxyCheck);
+        masm.branch32(Assembler::NotEqual,
+                      Address(tempVal.scratchReg(),
+                              ExpandoAndGeneration::offsetOfGeneration()),
+                      Imm32(expandoAndGeneration->generation),
+                      &failDOMProxyCheck);
 
         expandoVal = expandoAndGeneration->expando;
-        masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal);
+        masm.loadValue(Address(tempVal.scratchReg(),
+                               ExpandoAndGeneration::offsetOfExpando()),
+                       tempVal);
     }
 
     // If the incoming object does not have an expando object then we're sure we're not
     // shadowing.
     masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk);
 
     if (expandoVal.isObject()) {
         JS_ASSERT(!expandoVal.toObject().nativeContains(cx, name));
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3381,17 +3381,19 @@ LIRGenerator::visitGetDOMProperty(MGetDO
                                                tempFixed(valueReg));
 
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitGetDOMMember(MGetDOMMember *ins)
 {
-    MOZ_ASSERT(ins->isDomPure(), "Members had better be pure");
+    MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable");
+    MOZ_ASSERT(ins->domAliasSet() == JSJitInfo::AliasNone,
+               "Members had better not alias anything");
     LGetDOMMember *lir =
         new LGetDOMMember(useRegister(ins->object()));
     return defineBox(lir, ins);
 }
 
 static void
 SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1908,17 +1908,19 @@ class MCall
     }
     AliasSet getAliasSet() const {
         if (isDOMFunction()) {
             JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
 
             const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();
             JS_ASSERT(jitInfo);
 
-            if (jitInfo->isPure && jitInfo->argTypes) {
+            JS_ASSERT(jitInfo->aliasSet != JSJitInfo::AliasNone);
+            if (jitInfo->aliasSet == JSJitInfo::AliasDOMSets &&
+                jitInfo->argTypes) {
                 uint32_t argIndex = 0;
                 for (const JSJitInfo::ArgType* argType = jitInfo->argTypes;
                      *argType != JSJitInfo::ArgTypeListEnd;
                      ++argType, ++argIndex)
                 {
                     if (argIndex >= numActualArgs()) {
                         // Passing through undefined can't have side-effects
                         continue;
@@ -7870,18 +7872,20 @@ class MGetDOMProperty
         JS_ASSERT(jitinfo->type == JSJitInfo::Getter);
 
         setOperand(0, obj);
 
         // Pin the guard as an operand if we want to hoist later
         setOperand(1, guard);
 
         // We are movable iff the jitinfo says we can be.
-        if (jitinfo->isPure)
+        if (isDomMovable()) {
+            JS_ASSERT(jitinfo->aliasSet != JSJitInfo::AliasEverything);
             setMovable();
+        }
 
         setResultType(MIRType_Value);
     }
 
     const JSJitInfo *info() const {
         return info_;
     }
 
@@ -7895,57 +7899,55 @@ class MGetDOMProperty
     }
 
     const JSJitGetterOp fun() {
         return info_->getter;
     }
     bool isInfallible() const {
         return info_->isInfallible;
     }
-    bool isDomConstant() const {
-        return info_->isConstant;
-    }
-    bool isDomPure() const {
-        return info_->isPure;
+    bool isDomMovable() const {
+        return info_->isMovable;
+    }
+    JSJitInfo::AliasSet domAliasSet() const {
+        return info_->aliasSet;
     }
     size_t domMemberSlotIndex() const {
         MOZ_ASSERT(info_->isInSlot);
         return info_->slotIndex;
     }
     MDefinition *object() {
         return getOperand(0);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     bool congruentTo(MDefinition *ins) const {
-        if (!isDomPure())
+        if (!isDomMovable())
             return false;
 
         if (!ins->isGetDOMProperty())
             return false;
 
         // Checking the jitinfo is the same as checking the constant function
         if (!(info() == ins->toGetDOMProperty()->info()))
             return false;
 
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const {
-        // The whole point of constancy is that it's non-effectful and doesn't
-        // conflict with anything
-        if (isDomConstant())
+        JSJitInfo::AliasSet aliasSet = domAliasSet();
+        if (aliasSet == JSJitInfo::AliasNone)
             return AliasSet::None();
-        // Pure DOM attributes can only alias things that alias the world or
-        // explicitly alias DOM properties.
-        if (isDomPure())
+        if (aliasSet == JSJitInfo::AliasDOMSets)
             return AliasSet::Load(AliasSet::DOMProperty);
+        JS_ASSERT(aliasSet == JSJitInfo::AliasEverything);
         return AliasSet::Store(AliasSet::Any);
     }
 
     bool possiblyCalls() const {
         return true;
     }
 };
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -877,16 +877,26 @@ struct ExpandoAndGeneration {
   {}
 
   void Unlink()
   {
       ++generation;
       expando.setUndefined();
   }
 
+  static size_t offsetOfExpando()
+  {
+      return offsetof(ExpandoAndGeneration, expando);
+  }
+
+  static size_t offsetOfGeneration()
+  {
+      return offsetof(ExpandoAndGeneration, generation);
+  }
+
   JS::Heap<JS::Value> expando;
   uint32_t generation;
 };
 
 typedef enum DOMProxyShadowsResult {
   ShadowCheckFailed,
   Shadows,
   DoesntShadow,
@@ -1460,30 +1470,52 @@ struct JSJitInfo {
         Primitive = Numeric | Boolean | Null | String,
         ObjectOrNull = Object | Null,
         Any = ObjectOrNull | Primitive,
 
         // Our sentinel value.
         ArgTypeListEnd = (1 << 31)
     };
 
+    enum AliasSet {
+        // An enum that describes what this getter/setter/method aliases.  This
+        // determines what things can be hoisted past this call, and if this
+        // call is movable what it can be hoisted past.
+
+        // Alias nothing: a constant value, getting it can't affect any other
+        // values, nothing can affect it.
+        AliasNone,
+
+        // Alias things that can modify the DOM but nothing else.  Doing the
+        // call can't affect the behavior of any other function.
+        AliasDOMSets,
+
+        // Alias the world.  Calling this can change arbitrary values anywhere
+        // in the system.  Most things fall in this bucket.
+        AliasEverything
+    };
+
     union {
         JSJitGetterOp getter;
         JSJitSetterOp setter;
         JSJitMethodOp method;
     };
 
     uint32_t protoID;
     uint32_t depth;
     OpType type;
     bool isInfallible;      /* Is op fallible? False in setters. */
-    bool isConstant;        /* Getting a construction-time constant? */
-    bool isPure;            /* As long as no non-pure DOM things happen, will
-                               keep returning the same value for the given
-                               "this" object" */
+    bool isMovable;         /* Is op movable?  To be movable the op must not
+                               AliasEverything, but even that might not be
+                               enough (e.g. in cases when it can throw). */
+    AliasSet aliasSet;      /* The alias set for this op.  This is a _minimal_
+                               alias set; in particular for a method it does not
+                               include whatever argument conversions might do.
+                               That's covered by argTypes and runtime analysis
+                               of the actual argument types being passed in. */
     // XXXbz should we have a JSGetterJitInfo subclass or something?
     // XXXbz should we have a JSValueType for the type of the member?
     bool isInSlot;          /* True if this is a getter that can get a member
                                from a slot of the "this" object directly. */
     size_t slotIndex;       /* If isMember is true, the index of the slot to get
                                the value from.  Otherwise 0. */
     JSValueType returnType; /* The return type tag.  Might be JSVAL_TYPE_UNKNOWN */
 
@@ -1505,17 +1537,17 @@ private:
         JS_STATIC_ASSERT(Any & Double);
         JS_STATIC_ASSERT(Any & Boolean);
         JS_STATIC_ASSERT(Any & Object);
         JS_STATIC_ASSERT(Any & Null);
     }
 };
 
 #define JS_JITINFO_NATIVE_PARALLEL(op)                                         \
-    {{nullptr},0,0,JSJitInfo::OpType_None,false,false,false,false,0,JSVAL_TYPE_MISSING,nullptr,op}
+    {{nullptr},0,0,JSJitInfo::OpType_None,false,false,JSJitInfo::AliasEverything,false,0,JSVAL_TYPE_MISSING,nullptr,op}
 
 static JS_ALWAYS_INLINE const JSJitInfo *
 FUNCTION_VALUE_TO_JITINFO(const JS::Value& v)
 {
     JS_ASSERT(js::GetObjectClass(&v.toObject()) == js::FunctionClassPtr);
     return reinterpret_cast<js::shadow::Function *>(&v.toObject())->jitinfo;
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4793,48 +4793,48 @@ dom_doFoo(JSContext* cx, HandleObject ob
 }
 
 static const JSJitInfo dom_x_getterinfo = {
     { (JSJitGetterOp)dom_get_x },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::Getter,
     true,     /* isInfallible. False in setters. */
-    true,     /* isConstant. Only relevant for getters. */
-    true,     /* isPure */
+    true,     /* isMovable */
+    JSJitInfo::AliasNone, /* aliasSet */
     false,    /* isInSlot */
     0,        /* slotIndex */
     JSVAL_TYPE_UNKNOWN, /* returnType */
     nullptr,  /* argTypes */
     nullptr   /* parallelNative */
 };
 
 static const JSJitInfo dom_x_setterinfo = {
     { (JSJitGetterOp)dom_set_x },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::Setter,
     false,    /* isInfallible. False in setters. */
-    false,    /* isConstant. Only relevant for getters. */
-    false,    /* isPure */
+    false,    /* isMovable. */
+    JSJitInfo::AliasEverything, /* aliasSet */
     false,    /* isInSlot */
     0,        /* slotIndex */
     JSVAL_TYPE_UNKNOWN, /* returnType */
     nullptr,  /* argTypes */
     nullptr   /* parallelNative */
 };
 
 static const JSJitInfo doFoo_methodinfo = {
     { (JSJitGetterOp)dom_doFoo },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::Method,
     false,    /* isInfallible. False in setters. */
-    false,    /* isConstant. Only relevant for getters. */
-    false,    /* isPure */
+    false,    /* isMovable */
+    JSJitInfo::AliasEverything, /* aliasSet */
     false,    /* isInSlot */
     0,        /* slotIndex */
     JSVAL_TYPE_UNKNOWN, /* returnType */
     nullptr,  /* argTypes */
     nullptr   /* parallelNative */
 };
 
 static const JSPropertySpec dom_props[] = {
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -24,17 +24,17 @@ from progressbar import ProgressBar, Nul
 from results import TestOutput
 
 TESTS_LIB_DIR = os.path.dirname(os.path.abspath(__file__))
 JS_DIR = os.path.dirname(os.path.dirname(TESTS_LIB_DIR))
 TOP_SRC_DIR = os.path.dirname(os.path.dirname(JS_DIR))
 TEST_DIR = os.path.join(JS_DIR, 'jit-test', 'tests')
 LIB_DIR = os.path.join(JS_DIR, 'jit-test', 'lib') + os.path.sep
 JS_CACHE_DIR = os.path.join(JS_DIR, 'jit-test', '.js-cache')
-ECMA6_DIR = posixpath.join(JS_DIR, 'tests', 'ecma_6')
+JS_TESTS_DIR = posixpath.join(JS_DIR, 'tests')
 
 # Backported from Python 3.1 posixpath.py
 def _relpath(path, start=None):
     """Return a relative version of a path"""
 
     if not path:
         raise ValueError("no path specified")
 
@@ -656,17 +656,19 @@ def run_tests_remote(tests, prefix, opti
     dm.mkDirs(options.remote_test_root)
     push_libs(options, dm)
     push_progs(options, dm, [prefix[0]])
     dm.chmodDir(options.remote_test_root)
 
     Test.CacheDir = posixpath.join(options.remote_test_root, '.js-cache')
     dm.mkDir(Test.CacheDir)
 
-    dm.pushDir(ECMA6_DIR, posixpath.join(jit_tests_dir, 'tests', 'ecma_6'), timeout=600)
+    for path in os.listdir(JS_TESTS_DIR):
+        dm.pushDir(os.path.join(JS_TESTS_DIR, path), posixpath.join(jit_tests_dir, 'tests', path))
+
     dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root, timeout=600)
     prefix[0] = os.path.join(options.remote_test_root, 'js')
 
     # Run all tests.
     gen = get_remote_results(tests, dm, prefix, options)
     ok = process_test_results(gen, len(tests), options)
     return ok
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2572,17 +2572,17 @@ ElementRestyler::RestyleUndisplayedChild
                    "Shouldn't have random pseudo style contexts in the "
                    "undisplayed map");
 
       // Get the parent of the undisplayed content and check if it is a XBL
       // children element. Push the children element as an ancestor here because it does
       // not have a frame and would not otherwise be pushed as an ancestor.
       nsIContent* parent = undisplayed->mContent->GetParent();
       TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
-      if (parent && parent->IsActiveChildrenElement()) {
+      if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
         insertionPointPusher.PushAncestorAndStyleScope(parent);
       }
 
       nsRestyleHint thisChildHint = aChildRestyleHint;
       RestyleTracker::RestyleData undisplayedRestyleData;
       if (mRestyleTracker.GetRestyleData(undisplayed->mContent->AsElement(),
                                          &undisplayedRestyleData)) {
         thisChildHint =
@@ -2743,17 +2743,17 @@ ElementRestyler::RestyleContentChildren(
         // is a XBL children element. Push the children element as an
         // ancestor here because it does not have a frame and would not
         // otherwise be pushed as an ancestor.
 
         // Check if the frame has a content because |child| may be a
         // nsPageFrame that does not have a content.
         nsIContent* parent = child->GetContent() ? child->GetContent()->GetParent() : nullptr;
         TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
-        if (parent && parent->IsActiveChildrenElement()) {
+        if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
           insertionPointPusher.PushAncestorAndStyleScope(parent);
         }
 
         // only do frames that are in flow
         if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder
           // get out of flow frame and recur there
           nsIFrame* outOfFlowFrame =
             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3535,17 +3535,17 @@ nsCSSFrameConstructor::ConstructFrameFro
   // Get the parent of the content and check if it is a XBL children element.
   // Push the children element as an ancestor here because it does
   // not have a frame and would not otherwise be pushed as an ancestor. It is
   // necessary to do so in order to correctly handle style resolution on
   // descendants.
   nsIContent* parent = content->GetParent();
   TreeMatchContext::AutoAncestorPusher
     insertionPointPusher(aState.mTreeMatchContext);
-  if (parent && parent->IsActiveChildrenElement()) {
+  if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
     if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
       insertionPointPusher.PushAncestorAndStyleScope(parent);
     } else {
       insertionPointPusher.PushStyleScope(parent);
     }
   }
 
   // Push the content as a style ancestor now, so we don't have to do
@@ -6263,16 +6263,17 @@ nsCSSFrameConstructor::MaybeConstructLaz
 {
   if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
       aContainer->IsInNativeAnonymousSubtree() || aContainer->IsXUL()) {
     return false;
   }
 
   if (aOperation == CONTENTINSERT) {
     if (aChild->IsRootOfAnonymousSubtree() ||
+        aChild->HasFlag(NODE_IS_IN_SHADOW_TREE) ||
         aChild->IsEditable() || aChild->IsXUL()) {
       return false;
     }
   } else { // CONTENTAPPEND
     NS_ASSERTION(aOperation == CONTENTAPPEND,
                  "operation should be either insert or append");
     for (nsIContent* child = aChild; child; child = child->GetNextSibling()) {
       NS_ASSERTION(!child->IsRootOfAnonymousSubtree(),
@@ -6575,16 +6576,27 @@ nsCSSFrameConstructor::ContentAppended(n
     if (tag == nsGkAtoms::treechildren ||
         tag == nsGkAtoms::treeitem ||
         tag == nsGkAtoms::treerow)
       return NS_OK;
 
   }
 #endif // MOZ_XUL
 
+  if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // Recreate frames if content is appended into a ShadowRoot
+    // because children of ShadowRoot are rendered in place of children
+    // of the host.
+    nsIContent* bindingParent = aContainer->GetBindingParent();
+    LAYOUT_PHASE_TEMP_EXIT();
+    nsresult rv = RecreateFramesForContent(bindingParent, false);
+    LAYOUT_PHASE_TEMP_REENTER();
+    return rv;
+  }
+
   // Get the frame associated with the content
   nsIFrame* parentFrame = GetFrameFor(aContainer);
 
   // See comment in ContentRangeInserted for why this is necessary.
   if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
     return NS_OK;
   }
 
@@ -6998,16 +7010,27 @@ nsCSSFrameConstructor::ContentRangeInser
       accService->ContentRangeInserted(mPresShell, aContainer,
                                        aStartChild, aEndChild);
     }
 #endif
 
     return NS_OK;
   }
 
+  if (aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // Recreate frames if content is inserted into a ShadowRoot
+    // because children of ShadowRoot are rendered in place of
+    // the children of the host.
+    nsIContent* bindingParent = aContainer->GetBindingParent();
+    LAYOUT_PHASE_TEMP_EXIT();
+    nsresult rv = RecreateFramesForContent(bindingParent, false);
+    LAYOUT_PHASE_TEMP_REENTER();
+    return rv;
+  }
+
   nsIFrame* parentFrame = GetFrameFor(aContainer);
   // The xbl:children element won't have a frame, but default content can have the children as
   // a parent. While its uncommon to change the structure of the default content itself, a label,
   // for example, can be reframed by having its value attribute set or removed.
   if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
     return NS_OK;
   }
 
@@ -7478,16 +7501,28 @@ nsCSSFrameConstructor::ContentRemoved(ns
       if (firstChild && firstChild->GetContent() == aChild) {
         isRoot = true;
         childFrame = firstChild;
         NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
       }
     }
   }
 
+  if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    // Recreate frames if content is removed from a ShadowRoot
+    // because it may contain an insertion point which can change
+    // how the host is rendered.
+    nsIContent* bindingParent = aContainer->GetBindingParent();
+    *aDidReconstruct = true;
+    LAYOUT_PHASE_TEMP_EXIT();
+    nsresult rv = RecreateFramesForContent(bindingParent, false);
+    LAYOUT_PHASE_TEMP_REENTER();
+    return rv;
+  }
+
   if (childFrame) {
     InvalidateCanvasIfNeeded(mPresShell, aChild);
     
     // See whether we need to remove more than just childFrame
     LAYOUT_PHASE_TEMP_EXIT();
     if (MaybeRecreateContainerForFrameRemoval(childFrame, &rv)) {
       LAYOUT_PHASE_TEMP_REENTER();
       *aDidReconstruct = true;
@@ -9277,19 +9312,18 @@ nsCSSFrameConstructor::ProcessChildren(n
     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       // Get the parent of the content and check if it is a XBL children element
       // (if the content is a children element then parent != aContent because the
       // FlattenedChildIterator will transitively iterate through <xbl:children>
       // for default content). Push the children element as an ancestor here because
       // it does not have a frame and would not otherwise be pushed as an ancestor.
       nsIContent* parent = child->GetParent();
       MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
-      MOZ_ASSERT(parent->IsElement());
       TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
-      if (parent != aContent) {
+      if (parent != aContent && parent->IsElement()) {
         if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
           ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
         } else {
           ancestorPusher.PushStyleScope(parent->AsElement());
         }
       }
 
       // Frame construction item construction should not post
@@ -10574,19 +10608,18 @@ nsCSSFrameConstructor::BuildInlineChildI
     for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
       // Get the parent of the content and check if it is a XBL children element
       // (if the content is a children element then contentParent != parentContent because the
       // FlattenedChildIterator will transitively iterate through <xbl:children>
       // for default content). Push the children element as an ancestor here because
       // it does not have a frame and would not otherwise be pushed as an ancestor.
       nsIContent* contentParent = content->GetParent();
       MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
-      MOZ_ASSERT(contentParent->IsElement());
       TreeMatchContext::AutoAncestorPusher insertionPointPusher(aState.mTreeMatchContext);
-      if (contentParent != parentContent) {
+      if (contentParent != parentContent && contentParent->IsElement()) {
         if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
           insertionPointPusher.PushAncestorAndStyleScope(contentParent->AsElement());
         } else {
           insertionPointPusher.PushStyleScope(contentParent->AsElement());
         }
       }
 
       // Manually check for comments/PIs, since we don't have a frame to pass to
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -80,17 +80,19 @@ nsCounterUseNode::GetText(nsString& aRes
     const nsCSSValue& styleItem = mCounterStyle->Item(mAllCounters ? 2 : 1);
     int32_t style = styleItem.GetIntValue();
     const PRUnichar* separator;
     if (mAllCounters)
         separator = mCounterStyle->Item(1).GetStringBufferValue();
 
     for (uint32_t i = stack.Length() - 1;; --i) {
         nsCounterNode *n = stack[i];
-        nsBulletFrame::AppendCounterText(style, n->mValueAfter, aResult);
+        bool isTextRTL;
+        nsBulletFrame::AppendCounterText(
+                style, n->mValueAfter, aResult, isTextRTL);
         if (i == 0)
             break;
         NS_ASSERTION(mAllCounters, "yikes, separator is uninitialized");
         aResult.Append(separator);
     }
 }
 
 void
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -606,17 +606,17 @@ nsFrameManagerBase::UndisplayedMap::GetE
   }
 
   // In the case of XBL default content, <xbl:children> elements do not get a
   // frame causing a mismatch between the content tree and the frame tree.
   // |GetEntryFor| is sometimes called with the content tree parent (which may
   // be a <xbl:children> element) but the parent in the frame tree would be the
   // insertion parent (parent of the <xbl:children> element). Here the children
   // elements are normalized to the insertion parent to correct for the mismatch.
-  if (parentContent && parentContent->IsActiveChildrenElement()) {
+  if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) {
     parentContent = parentContent->GetParent();
     // Change the caller's pointer for the parent content to be the insertion parent.
     *aParentContent = parentContent;
   }
 
   PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent);
   PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent);
   if (*entry) {
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -93,16 +93,17 @@ typedef short SelectionType;
 typedef uint64_t nsFrameState;
 
 namespace mozilla {
 class Selection;
 
 namespace dom {
 class Element;
 class Touch;
+class ShadowRoot;
 } // namespace dom
 
 namespace layers{
 class LayerManager;
 } // namespace layers
 } // namespace mozilla
 
 // Flags to pass to SetCapturingContent
@@ -522,16 +523,21 @@ public:
    * Recreates the frames for a node
    */
   virtual NS_HIDDEN_(nsresult) RecreateFramesFor(nsIContent* aContent) = 0;
 
   void PostRecreateFramesFor(mozilla::dom::Element* aElement);
   void RestyleForAnimation(mozilla::dom::Element* aElement,
                            nsRestyleHint aHint);
 
+  // ShadowRoot has APIs that can change styles so we only
+  // want to restyle elements in the ShadowRoot and not the whole
+  // document.
+  virtual void RestyleShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) = 0;
+
   /**
    * Determine if it is safe to flush all pending notifications
    * @param aIsSafeToFlush true if it is safe, false otherwise.
    * 
    */
   virtual NS_HIDDEN_(bool) IsSafeToFlush() const = 0;
 
   /**
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -35,16 +35,17 @@
 #ifdef XP_WIN
 #include "winuser.h"
 #endif
 
 #include "nsPresShell.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsIDocument.h"
 #include "nsCSSStyleSheet.h"
 #include "nsAnimationManager.h"
 #include "nsINameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
 #include "nsFrame.h"
 #include "FrameLayerBuilder.h"
 #include "nsViewManager.h"
 #include "nsView.h"
@@ -154,16 +155,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsCanvasFrame.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIScreen.h"
 #include "nsIScreenManager.h"
 #include "nsPlaceholderFrame.h"
 #include "nsTransitionManager.h"
+#include "ChildIterator.h"
 #include "RestyleManager.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDragSession.h"
 #include "nsIFrameInlines.h"
 
 #ifdef ANDROID
 #include "nsIDocShellTreeOwner.h"
 #endif
@@ -4204,18 +4206,23 @@ PresShell::ContentAppended(nsIDocument *
     return;
   }
   
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
-  mPresContext->RestyleManager()->
-    RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
+  if (aContainer->IsElement()) {
+    // Ensure the container is an element before trying to restyle
+    // because it can be the case that the container is a ShadowRoot
+    // which is a document fragment.
+    mPresContext->RestyleManager()->
+      RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
+  }
 
   mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, true);
 
   if (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument) &&
       aFirstNewContent->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
     NotifyFontSizeInflationEnabledIsDirty();
   }
 
@@ -4235,17 +4242,20 @@ PresShell::ContentInserted(nsIDocument* 
     return;
   }
   
   nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
-  if (aContainer) {
+  if (aContainer && aContainer->IsElement()) {
+    // Ensure the container is an element before trying to restyle
+    // because it can be the case that the container is a ShadowRoot
+    // which is a document fragment.
     mPresContext->RestyleManager()->
       RestyleForInsertOrChange(aContainer->AsElement(), aChild);
   }
 
   mFrameConstructor->ContentInserted(aContainer, aChild, nullptr, true);
 
   if (((!aContainer && aDocument) ||
       (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument))) &&
@@ -5721,16 +5731,32 @@ public:
     tabChild->UpdateHitRegion(region);
   }
 private:
   PresShell* mShell;
   nsIFrame* mFrame;
 };
 
 void
+PresShell::RestyleShadowRoot(ShadowRoot* aShadowRoot)
+{
+  // Mark the children of the ShadowRoot as style changed but not
+  // the ShadowRoot itself because it is a document fragment and does not
+  // have a frame.
+  ExplicitChildIterator iterator(aShadowRoot);
+  for (nsIContent* child = iterator.GetNextChild();
+       child;
+       child = iterator.GetNextChild()) {
+    if (child->IsElement()) {
+      mChangedScopeStyleRoots.AppendElement(child->AsElement());
+    }
+  }
+}
+
+void
 PresShell::Paint(nsView*        aViewToPaint,
                  const nsRegion& aDirtyRegion,
                  uint32_t        aFlags)
 {
   PROFILER_LABEL("Paint", "PresShell::Paint");
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aViewToPaint, "null view");
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -343,16 +343,18 @@ public:
   virtual void RebuildImageVisibility(const nsDisplayList& aList) MOZ_OVERRIDE;
 
   virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) MOZ_OVERRIDE;
 
   virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) MOZ_OVERRIDE;
 
   virtual bool AssumeAllImagesVisible() MOZ_OVERRIDE;
 
+  virtual void RestyleShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot);
+
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks(bool aInterruptible);
   void CancelPostedReflowCallbacks();
 
   void UnsuppressAndInvalidate();
 
new file mode 100644
--- /dev/null
+++ b/layout/forms/crashtests/944198.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body onload="c.removeAttribute('type'); c.removeAttribute('value');">
+<input id="c" type="color">
+</body>
+</html>
--- a/layout/forms/crashtests/crashtests.list
+++ b/layout/forms/crashtests/crashtests.list
@@ -45,8 +45,9 @@ load 570624-1.html
 skip-if(B2G) load 498698-1.html # bug 833371
 asserts(1) load 578604-1.html # bug 584564
 asserts(4-7) load 590302-1.xhtml # bug 584564
 load 626014.xhtml
 load 639733.xhtml
 asserts(0-1) load 669767.html
 load 682684.xhtml
 load 865602.html
+load 944198.html
--- a/layout/forms/nsColorControlFrame.cpp
+++ b/layout/forms/nsColorControlFrame.cpp
@@ -7,16 +7,17 @@
 
 #include "nsContentCreatorFunctions.h"
 #include "nsContentList.h"
 #include "nsContentUtils.h"
 #include "nsFormControlFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMNode.h"
+#include "nsIFormControl.h"
 #include "nsStyleSet.h"
 
 nsColorControlFrame::nsColorControlFrame(nsStyleContext* aContext):
   nsColorControlFrameSuper(aContext)
 {
 }
 
 nsIFrame*
@@ -112,18 +113,22 @@ nsColorControlFrame::UpdateColor()
 
 NS_IMETHODIMP
 nsColorControlFrame::AttributeChanged(int32_t  aNameSpaceID,
                                       nsIAtom* aAttribute,
                                       int32_t  aModType)
 {
   NS_ASSERTION(mColorContent, "The color div must exist");
 
-  // If the value attribute is set, update the color box
-  if (aNameSpaceID == kNameSpaceID_None && nsGkAtoms::value == aAttribute) {
+  // If the value attribute is set, update the color box, but only if we're
+  // still a color control, which might not be the case if the type attribute
+  // was removed/changed.
+  nsCOMPtr<nsIFormControl> fctrl = do_QueryInterface(GetContent());
+  if (fctrl->GetType() == NS_FORM_INPUT_COLOR &&
+      aNameSpaceID == kNameSpaceID_None && nsGkAtoms::value == aAttribute) {
     UpdateColor();
   }
   return nsColorControlFrameSuper::AttributeChanged(aNameSpaceID, aAttribute,
                                                     aModType);
 }
 
 nsIFrame*
 nsColorControlFrame::GetContentInsertionFrame()
new file mode 100644
--- /dev/null
+++ b/layout/generic/MathVariantTextRunFactory.cpp
@@ -0,0 +1,662 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MathVariantTextRunFactory.h"
+#include "nsStyleConsts.h"
+#include "nsStyleContext.h"
+#include "nsTextFrameUtils.h"
+#include "mozilla/Util.h"
+
+using namespace mozilla;
+
+/*
+  Entries for the mathvariant lookup tables.  mKey represents the Unicode
+  character to be transformed and is used for searching the tables.
+  mReplacement represents the mapped mathvariant Unicode character.
+*/
+typedef struct
+{
+  uint32_t mKey;
+  uint32_t mReplacement;
+} MathVarMapping;
+
+/*
+ Lookup tables for use with mathvariant mappings to transform a unicode
+ character point to another unicode character that indicates the proper output.
+ mKey represents one of two concepts.
+ 1.  In the Latin table it represents a hole in the mathematical alphanumeric
+     block, where the character that should occupy that position is located
+     elsewhere.
+ 2.  It represents an Arabic letter.
+
+  As a replacement, 0 is reserved to indicate no mapping was found.
+*/
+static const MathVarMapping gArabicInitialMapTable[] = {
+  { 0x628, 0x1EE21 },
+  { 0x62A, 0x1EE35 },
+  { 0x62B, 0x1EE36 },
+  { 0x62C, 0x1EE22 },
+  { 0x62D, 0x1EE27 },
+  { 0x62E, 0x1EE37 },
+  { 0x633, 0x1EE2E },
+  { 0x634, 0x1EE34 },
+  { 0x635, 0x1EE31 },
+  { 0x636, 0x1EE39 },
+  { 0x639, 0x1EE2F },
+  { 0x63A, 0x1EE3B },
+  { 0x641, 0x1EE30 },
+  { 0x642, 0x1EE32 },
+  { 0x643, 0x1EE2A },
+  { 0x644, 0x1EE2B },
+  { 0x645, 0x1EE2C },
+  { 0x646, 0x1EE2D },
+  { 0x647, 0x1EE24 },
+  { 0x64A, 0x1EE29 }
+};
+
+static const MathVarMapping gArabicTailedMapTable[] = {
+  { 0x62C, 0x1EE42 },
+  { 0x62D, 0x1EE47 },
+  { 0x62E, 0x1EE57 },
+  { 0x633, 0x1EE4E },
+  { 0x634, 0x1EE54 },
+  { 0x635, 0x1EE51 },
+  { 0x636, 0x1EE59 },
+  { 0x639, 0x1EE4F },
+  { 0x63A, 0x1EE5B },
+  { 0x642, 0x1EE52 },
+  { 0x644, 0x1EE4B },
+  { 0x646, 0x1EE4D },
+  { 0x64A, 0x1EE49 },
+  { 0x66F, 0x1EE5F },
+  { 0x6BA, 0x1EE5D }
+};
+
+static const MathVarMapping gArabicStretchedMapTable[] = {
+  { 0x628, 0x1EE61 },
+  { 0x62A, 0x1EE75 },
+  { 0x62B, 0x1EE76 },
+  { 0x62C, 0x1EE62 },
+  { 0x62D, 0x1EE67 },
+  { 0x62E, 0x1EE77 },
+  { 0x633, 0x1EE6E },
+  { 0x634, 0x1EE74 },
+  { 0x635, 0x1EE71 },
+  { 0x636, 0x1EE79 },
+  { 0x637, 0x1EE68 },
+  { 0x638, 0x1EE7A },
+  { 0x639, 0x1EE6F },
+  { 0x63A, 0x1EE7B },
+  { 0x641, 0x1EE70 },
+  { 0x642, 0x1EE72 },
+  { 0x643, 0x1EE6A },
+  { 0x645, 0x1EE6C },
+  { 0x646, 0x1EE6D },
+  { 0x647, 0x1EE64 },
+  { 0x64A, 0x1EE69 },
+  { 0x66E, 0x1EE7C },
+  { 0x6A1, 0x1EE7E }
+};
+
+static const MathVarMapping gArabicLoopedMapTable[] = {
+  { 0x627, 0x1EE80 },
+  { 0x628, 0x1EE81 },
+  { 0x62A, 0x1EE95 },
+  { 0x62B, 0x1EE96 },
+  { 0x62C, 0x1EE82 },
+  { 0x62D, 0x1EE87 },
+  { 0x62E, 0x1EE97 },
+  { 0x62F, 0x1EE83 },
+  { 0x630, 0x1EE98 },
+  { 0x631, 0x1EE93 },
+  { 0x632, 0x1EE86 },
+  { 0x633, 0x1EE8E },
+  { 0x634, 0x1EE94 },
+  { 0x635, 0x1EE91 },
+  { 0x636, 0x1EE99 },
+  { 0x637, 0x1EE88 },
+  { 0x638, 0x1EE9A },
+  { 0x639, 0x1EE8F },
+  { 0x63A, 0x1EE9B },
+  { 0x641, 0x1EE90 },
+  { 0x642, 0x1EE92 },
+  { 0x644, 0x1EE8B },
+  { 0x645, 0x1EE8C },
+  { 0x646, 0x1EE8D },
+  { 0x647, 0x1EE84 },
+  { 0x648, 0x1EE85 },
+  { 0x64A, 0x1EE89 }
+};
+
+static const MathVarMapping gArabicDoubleMapTable[] = {
+  { 0x628, 0x1EEA1 },
+  { 0x62A, 0x1EEB5 },
+  { 0x62B, 0x1EEB6 },
+  { 0x62C, 0x1EEA2 },
+  { 0x62D, 0x1EEA7 },
+  { 0x62E, 0x1EEB7 },
+  { 0x62F, 0x1EEA3 },
+  { 0x630, 0x1EEB8 },
+  { 0x631, 0x1EEB3 },
+  { 0x632, 0x1EEA6 },
+  { 0x633, 0x1EEAE },
+  { 0x634, 0x1EEB4 },
+  { 0x635, 0x1EEB1 },
+  { 0x636, 0x1EEB9 },
+  { 0x637, 0x1EEA8 },
+  { 0x638, 0x1EEBA },
+  { 0x639, 0x1EEAF },
+  { 0x63A, 0x1EEBB },
+  { 0x641, 0x1EEB0 },
+  { 0x642, 0x1EEB2 },
+  { 0x644, 0x1EEAB },
+  { 0x645, 0x1EEAC },
+  { 0x646, 0x1EEAD },
+  { 0x648, 0x1EEA5 },
+  { 0x64A, 0x1EEA9 }
+};
+
+static const MathVarMapping gLatinExceptionMapTable[] = {
+  { 0x1D455, 0x210E },
+  { 0x1D49D, 0x212C },
+  { 0x1D4A0, 0x2130 },
+  { 0x1D4A1, 0x2131 },
+  { 0x1D4A3, 0x210B },
+  { 0x1D4A4, 0x2110 },
+  { 0x1D4A7, 0x2112 },
+  { 0x1D4A8, 0x2133 },
+  { 0x1D4AD, 0x211B },
+  { 0x1D4BA, 0x212F },
+  { 0x1D4BC, 0x210A },
+  { 0x1D4C4, 0x2134 },
+  { 0x1D506, 0x212D },
+  { 0x1D50B, 0x210C },
+  { 0x1D50C, 0x2111 },
+  { 0x1D515, 0x211C },
+  { 0x1D51D, 0x2128 },
+  { 0x1D53A, 0x2102 },
+  { 0x1D53F, 0x210D },
+  { 0x1D545, 0x2115 },
+  { 0x1D547, 0x2119 },
+  { 0x1D548, 0x211A },
+  { 0x1D549, 0x211D },
+  { 0x1D551, 0x2124 }
+};
+
+// Finds a MathVarMapping struct with the specified key (aKey) within aTable.
+// aTable must be an array, whose length is specified by aNumElements
+static uint32_t
+MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements)
+{
+  uint32_t low = 0;
+  uint32_t high = aNumElements;
+  while (high > low) {
+    uint32_t midPoint = (low+high) >> 1;
+    if (aKey == aTable[midPoint].mKey) {
+      return aTable[midPoint].mReplacement;
+    }
+    if (aKey > aTable[midPoint].mKey) {
+      low = midPoint + 1;
+    } else {
+      high = midPoint;
+    }
+  }
+  return 0;
+}
+
+#define GREEK_UPPER_THETA               0x03F4
+#define HOLE_GREEK_UPPER_THETA          0x03A2
+#define NABLA                           0x2207
+#define PARTIAL_DIFFERENTIAL            0x2202
+#define GREEK_UPPER_ALPHA               0x0391
+#define GREEK_UPPER_OMEGA               0x03A9
+#define GREEK_LOWER_ALPHA               0x03B1
+#define GREEK_LOWER_OMEGA               0x03C9
+#define GREEK_LUNATE_EPSILON_SYMBOL     0x03F5
+#define GREEK_THETA_SYMBOL              0x03D1
+#define GREEK_KAPPA_SYMBOL              0x03F0
+#define GREEK_PHI_SYMBOL                0x03D5
+#define GREEK_RHO_SYMBOL                0x03F1
+#define GREEK_PI_SYMBOL                 0x03D6
+#define GREEK_LETTER_DIGAMMA            0x03DC
+#define GREEK_SMALL_LETTER_DIGAMMA      0x03DD
+#define MATH_BOLD_CAPITAL_DIGAMMA       0x1D7CA
+#define MATH_BOLD_SMALL_DIGAMMA         0x1D7CB
+
+#define LATIN_SMALL_LETTER_DOTLESS_I    0x0131
+#define LATIN_SMALL_LETTER_DOTLESS_J    0x0237
+
+#define MATH_ITALIC_SMALL_DOTLESS_I     0x1D6A4
+#define MATH_ITALIC_SMALL_DOTLESS_J     0x1D6A5
+
+#define MATH_BOLD_UPPER_A               0x1D400
+#define MATH_ITALIC_UPPER_A             0x1D434
+#define MATH_BOLD_SMALL_A               0x1D41A
+#define MATH_BOLD_UPPER_ALPHA           0x1D6A8
+#define MATH_BOLD_SMALL_ALPHA           0x1D6C2
+#define MATH_ITALIC_UPPER_ALPHA         0x1D6E2
+#define MATH_BOLD_DIGIT_ZERO            0x1D7CE
+#define MATH_DOUBLE_STRUCK_ZERO         0x1D7D8
+
+#define MATH_BOLD_UPPER_THETA           0x1D6B9
+#define MATH_BOLD_NABLA                 0x1D6C1
+#define MATH_BOLD_PARTIAL_DIFFERENTIAL  0x1D6DB
+#define MATH_BOLD_EPSILON_SYMBOL        0x1D6DC
+#define MATH_BOLD_THETA_SYMBOL          0x1D6DD
+#define MATH_BOLD_KAPPA_SYMBOL          0x1D6DE
+#define MATH_BOLD_PHI_SYMBOL            0x1D6DF
+#define MATH_BOLD_RHO_SYMBOL            0x1D6E0
+#define MATH_BOLD_PI_SYMBOL             0x1D6E1
+
+/*
+  Performs the character mapping needed to implement MathML's mathvariant
+  attribute.  It takes a unicode character and maps it to its appropriate
+  mathvariant counterpart specified by aMathVar.  The mapped character is
+  typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but
+  there are exceptions which this function accounts for.
+  Characters without a valid mapping or valid aMathvar value are returned
+  unaltered.  Characters already in the mathematical blocks (or are one of the
+  exceptions) are never transformed.
+  Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h.
+  The transformable characters can be found at:
+  http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
+  https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
+*/
+static uint32_t
+MathVariant(uint32_t aCh, uint8_t aMathVar)
+{
+  uint32_t baseChar;
+  enum CharacterType {
+    kIsLatin,
+    kIsGreekish,
+    kIsNumber,
+    kIsArabic,
+  };
+  CharacterType varType;
+
+  int8_t multiplier;
+
+  if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) {
+    // nothing to do here
+    return aCh;
+  }
+  if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) {
+    NS_ASSERTION(false, "Illegal mathvariant value");
+    return aCh;
+  }
+
+  // Exceptional characters with at most one possible transformation
+  if (aCh == HOLE_GREEK_UPPER_THETA) {
+    // Nothing at this code point is transformed
+    return aCh;
+  }
+  if (aCh == GREEK_LETTER_DIGAMMA) {
+    if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
+      return MATH_BOLD_CAPITAL_DIGAMMA;
+    }
+    return aCh;
+  }
+  if (aCh == GREEK_SMALL_LETTER_DIGAMMA) {
+    if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
+      return MATH_BOLD_SMALL_DIGAMMA;
+    }
+    return aCh;
+  }
+  if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) {
+    if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+      return MATH_ITALIC_SMALL_DOTLESS_I;
+    }
+    return aCh;
+  }
+  if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) {
+    if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+      return MATH_ITALIC_SMALL_DOTLESS_J;
+    }
+    return aCh;
+  }
+
+  // The Unicode mathematical blocks are divided into four segments: Latin,
+  // Greek, numbers and Arabic.  In the case of the first three
+  // baseChar represents the relative order in which the characters are
+  // encoded in the Unicode mathematical block, normalised to the first
+  // character of that sequence.
+  //
+  if ('A' <= aCh && aCh <= 'Z') {
+    baseChar = aCh - 'A';
+    varType = kIsLatin;
+  } else if ('a' <= aCh && aCh <= 'z') {
+    // Lowercase characters are placed immediately after the uppercase
+    // characters in the Unicode mathematical block.  The constant subtraction
+    // represents the number of characters between the start of the sequence
+    // (capital A) and the first lowercase letter.
+    baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a';
+    varType = kIsLatin;
+  } else if ('0' <= aCh && aCh <= '9') {
+    baseChar = aCh - '0';
+    varType = kIsNumber;
+  } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) {
+    baseChar = aCh-GREEK_UPPER_ALPHA;
+    varType = kIsGreekish;
+  } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) {
+    // Lowercase Greek comes after uppercase Greek.
+    // Note in this instance the presence of an additional character (Nabla)
+    // between the end of the uppercase Greek characters and the lowercase
+    // ones.
+    baseChar =  MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA
+                + aCh-GREEK_LOWER_ALPHA;
+    varType = kIsGreekish;
+  } else if (0x0600 <= aCh && aCh <= 0x06FF) {
+    // Arabic characters are defined within this range
+    varType = kIsArabic;
+  } else {
+    switch (aCh) {
+      case GREEK_UPPER_THETA:
+        baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA;
+        break;
+      case NABLA:
+        baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA;
+        break;
+      case PARTIAL_DIFFERENTIAL:
+        baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_LUNATE_EPSILON_SYMBOL:
+        baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_THETA_SYMBOL:
+        baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_KAPPA_SYMBOL:
+        baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_PHI_SYMBOL:
+        baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_RHO_SYMBOL:
+        baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      case GREEK_PI_SYMBOL:
+        baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
+        break;
+      default:
+        return aCh;
+    }
+
+    varType = kIsGreekish;
+  }
+
+  if (varType == kIsNumber) {
+    switch (aMathVar) {
+      // Each possible number mathvariant is encoded in a single, contiguous
+      // block.  For example the beginning of the double struck number range
+      // follows immediately after the end of the bold number range.
+      // multiplier represents the order of the sequences relative to the first
+      // one.
+      case NS_MATHML_MATHVARIANT_BOLD:
+        multiplier = 0;
+        break;
+      case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
+        multiplier = 1;
+        break;
+      case NS_MATHML_MATHVARIANT_SANS_SERIF:
+        multiplier = 2;
+        break;
+      case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
+        multiplier = 3;
+        break;
+      case NS_MATHML_MATHVARIANT_MONOSPACE:
+        multiplier = 4;
+        break;
+      default:
+        // This mathvariant isn't defined for numbers or is otherwise normal
+        return aCh;
+    }
+    // As the ranges are contiguous, to find the desired mathvariant range it
+    // is sufficient to multiply the position within the sequence order
+    // (multiplier) with the period of the sequence (which is constant for all
+    // number sequences) and to add the character point of the first character
+    // within the number mathvariant range.
+    // To this the baseChar calculated earlier is added to obtain the final
+    // code point.
+    return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO)
+             +MATH_BOLD_DIGIT_ZERO;
+  } else if (varType == kIsGreekish) {
+    switch (aMathVar) {
+      case NS_MATHML_MATHVARIANT_BOLD:
+        multiplier = 0;
+        break;
+      case NS_MATHML_MATHVARIANT_ITALIC:
+        multiplier = 1;
+        break;
+      case NS_MATHML_MATHVARIANT_BOLD_ITALIC:
+        multiplier = 2;
+        break;
+      case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
+        multiplier = 3;
+        break;
+      case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC:
+        multiplier = 4;
+        break;
+      default:
+        // This mathvariant isn't defined for Greek or is otherwise normal
+        return aCh;
+    }
+    // See the kIsNumber case for an explanation of the following calculation
+    return baseChar + MATH_BOLD_UPPER_ALPHA +
+             multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA);
+  }
+
+  uint32_t tempChar;
+  uint32_t newChar;
+  if (varType == kIsArabic) {
+    const MathVarMapping* mapTable;
+    uint32_t tableLength;
+    switch (aMathVar) {
+      /* The Arabic mathematical block is not continuous, nor does it have a
+       * monotonic mapping to the unencoded characters, requiring the use of a
+       * lookup table.
+       */
+      case NS_MATHML_MATHVARIANT_INITIAL:
+        mapTable = gArabicInitialMapTable;
+        tableLength = ArrayLength(gArabicInitialMapTable);
+        break;
+      case NS_MATHML_MATHVARIANT_TAILED:
+        mapTable = gArabicTailedMapTable;
+        tableLength = ArrayLength(gArabicTailedMapTable);
+        break;
+      case NS_MATHML_MATHVARIANT_STRETCHED:
+        mapTable = gArabicStretchedMapTable;
+        tableLength = ArrayLength(gArabicStretchedMapTable);
+        break;
+      case NS_MATHML_MATHVARIANT_LOOPED:
+        mapTable = gArabicLoopedMapTable;
+        tableLength = ArrayLength(gArabicLoopedMapTable);
+        break;
+      case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
+        mapTable = gArabicDoubleMapTable;
+        tableLength = ArrayLength(gArabicDoubleMapTable);
+        break;
+      default:
+        // No valid transformations exist
+        return aCh;
+    }
+    newChar = MathvarMappingSearch(aCh, mapTable, tableLength);
+  } else {
+    // Must be Latin
+    if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) {
+      // Latin doesn't support the Arabic mathvariants
+      return aCh;
+    }
+    multiplier = aMathVar - 2;
+    // This is possible because the values for NS_MATHML_MATHVARIANT_* are
+    // chosen to coincide with the order in which the encoded mathvariant
+    // characters are located within their unicode block (less an offset to
+    // avoid _NONE and _NORMAL variants)
+    // See the kIsNumber case for an explanation of the following calculation
+    tempChar =  baseChar + MATH_BOLD_UPPER_A +
+                multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A);
+    // There are roughly twenty characters that are located outside of the
+    // mathematical block, so the spaces where they ought to be are used
+    // as keys for a lookup table containing the correct character mappings.
+    newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable,
+                                   ArrayLength(gLatinExceptionMapTable));
+  }
+
+  if (newChar) {
+    return newChar;
+  } else if (varType == kIsLatin) {
+    return tempChar;
+  } else {
+    // An Arabic character without a corresponding mapping
+    return aCh;
+  }
+
+}
+
+void
+nsMathVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
+                                            gfxContext* aRefContext)
+{
+  gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
+  gfxFontStyle fontStyle = *fontGroup->GetStyle();
+
+  nsAutoString convertedString;
+  nsAutoTArray<bool,50> charsToMergeArray;
+  nsAutoTArray<bool,50> deletedCharsArray;
+  nsAutoTArray<nsStyleContext*,50> styleArray;
+  nsAutoTArray<uint8_t,50> canBreakBeforeArray;
+  bool mergeNeeded = false;
+
+  bool singleCharMI =
+    aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
+
+  uint32_t length = aTextRun->GetLength();
+  const PRUnichar* str = aTextRun->mString.BeginReading();
+  nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
+
+  uint8_t mathVar;
+  bool doMathvariantStyling = true;
+
+  for (uint32_t i = 0; i < length; ++i) {
+    int extraChars = 0;
+    nsStyleContext* styleContext = styles[i];
+    mathVar = styleContext->StyleFont()->mMathVariant;
+
+    if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) {
+      mathVar = NS_MATHML_MATHVARIANT_ITALIC;
+    }
+
+    uint32_t ch = str[i];
+    if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
+        NS_IS_LOW_SURROGATE(str[i + 1])) {
+      ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
+    }
+    uint32_t ch2 = MathVariant(ch, mathVar);
+
+    if (mathVar == NS_MATHML_MATHVARIANT_BOLD ||
+        mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
+        mathVar == NS_MATHML_MATHVARIANT_ITALIC) {
+      if (ch == ch2  && ch != 0x20 && ch != 0xA0) {
+        // Don't perform the transformation if a character cannot be
+        // transformed. There is an exception for whitespace as it is both
+        // common and innocuous.
+        doMathvariantStyling = false;
+      }
+      // Undo the change as it will be handled as a font styling.
+      ch2 = ch;
+    }
+
+    deletedCharsArray.AppendElement(false);
+    charsToMergeArray.AppendElement(false);
+    styleArray.AppendElement(styleContext);
+    canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
+
+    if (IS_IN_BMP(ch2)) {
+      convertedString.Append(ch2);
+    } else {
+      convertedString.Append(H_SURROGATE(ch2));
+      convertedString.Append(L_SURROGATE(ch2));
+      ++extraChars;
+      if (!IS_IN_BMP(ch)) {
+        deletedCharsArray.AppendElement(true); // not exactly deleted, but
+                                          // the trailing surrogate is skipped
+        ++i;
+      }
+    }
+
+    while (extraChars-- > 0) {
+      mergeNeeded = true;
+      charsToMergeArray.AppendElement(true);
+      styleArray.AppendElement(styleContext);
+      canBreakBeforeArray.AppendElement(false);
+    }
+  }
+
+  uint32_t flags;
+  gfxTextRunFactory::Parameters innerParams =
+      GetParametersForInner(aTextRun, &flags, aRefContext);
+
+  nsAutoPtr<nsTransformedTextRun> transformedChild;
+  nsAutoPtr<gfxTextRun> cachedChild;
+  gfxTextRun* child;
+
+  if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) {
+    fontStyle.style = NS_FONT_STYLE_NORMAL;
+    fontStyle.weight = NS_FONT_WEIGHT_BOLD;
+  } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
+    fontStyle.style = NS_FONT_STYLE_ITALIC;
+    fontStyle.weight = NS_FONT_WEIGHT_NORMAL;
+  } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC &&
+             doMathvariantStyling) {
+    fontStyle.style = NS_FONT_STYLE_ITALIC;
+    fontStyle.weight = NS_FONT_WEIGHT_BOLD;
+  } else {
+    // Mathvariant overrides fontstyle and fontweight
+    fontStyle.style = NS_FONT_STYLE_NORMAL;
+    fontStyle.weight = NS_FONT_WEIGHT_NORMAL;
+  }
+  nsRefPtr<gfxFontGroup> newFontGroup = fontGroup->Copy(&fontStyle);
+
+  if (!newFontGroup)
+    return;
+
+  if (mInnerTransformingTextRunFactory) {
+    transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
+        convertedString.BeginReading(), convertedString.Length(),
+        &innerParams, newFontGroup, flags, styleArray.Elements(), false);
+    child = transformedChild.get();
+  } else {
+    cachedChild = newFontGroup->MakeTextRun(
+        convertedString.BeginReading(), convertedString.Length(),
+        &innerParams, flags);
+    child = cachedChild.get();
+  }
+  if (!child)
+    return;
+  // Copy potential linebreaks into child so they're preserved
+  // (and also child will be shaped appropriately)
+  NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
+               "Dropped characters or break-before values somewhere!");
+  child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
+      canBreakBeforeArray.Elements(), aRefContext);
+  if (transformedChild) {
+    transformedChild->FinishSettingProperties(aRefContext);
+  }
+
+  if (mergeNeeded) {
+    // Now merge multiple characters into one multi-glyph character as required
+    NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
+                 "source length mismatch");
+    NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
+                 "destination length mismatch");
+    MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
+                             deletedCharsArray.Elements());
+  } else {
+    // No merging to do, so just copy; this produces a more optimized textrun.
+    // We can't steal the data because the child may be cached and stealing
+    // the data would break the cache.
+    aTextRun->ResetGlyphRuns();
+    aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/layout/generic/MathVariantTextRunFactory.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MATHVARIANTTEXTRUNFACTORY_H_
+#define MATHVARIANTTEXTRUNFACTORY_H_
+
+#include "nsTextRunTransformations.h"
+
+/**
+ * Builds textruns that render their text using a mathvariant
+ */
+class nsMathVariantTextRunFactory : public nsTransformingTextRunFactory {
+public:
+  nsMathVariantTextRunFactory(nsTransformingTextRunFactory* aInnerTransformingTextRunFactory)
+    : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory) {}
+
+  virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
+                              gfxContext* aRefContext) MOZ_OVERRIDE;
+protected:
+  nsAutoPtr<nsTransformingTextRunFactory> mInnerTransformingTextRunFactory;
+};
+
+#endif /*MATHVARIANTTEXTRUNFACTORY_H_*/
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -38,16 +38,17 @@ EXPORTS.mozilla += [
 ]
 
 EXPORTS.mozilla.layout += [
     'FrameChildList.h',
 ]
 
 UNIFIED_SOURCES += [
     'FrameChildList.cpp',
+    'MathVariantTextRunFactory.cpp',
     'nsAbsoluteContainingBlock.cpp',
     'nsBlockFrame.cpp',
     'nsBlockReflowContext.cpp',
     'nsBlockReflowState.cpp',
     'nsBRFrame.cpp',
     'nsBulletFrame.cpp',
     'nsCanvasFrame.cpp',
     'nsColumnSetFrame.cpp',
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -342,29 +342,39 @@ nsBulletFrame::PaintBullet(nsRenderingCo
                       (rect.height - snapRect.height) / 2);
       aRenderingContext.FillRect(snapRect.x, snapRect.y,
                                  snapRect.width, snapRect.height);
     }
     break;
 
   case NS_STYLE_LIST_STYLE_DECIMAL:
   case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
+  case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
   case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
   case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
   case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
   case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
   case NS_STYLE_LIST_STYLE_LOWER_GREEK:
   case NS_STYLE_LIST_STYLE_HEBREW:
   case NS_STYLE_LIST_STYLE_ARMENIAN:
   case NS_STYLE_LIST_STYLE_GEORGIAN:
   case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
   case NS_STYLE_LIST_STYLE_HIRAGANA:
   case NS_STYLE_LIST_STYLE_KATAKANA:
   case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
   case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
+  case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
+  case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+  case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+  case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+  case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+  case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
+  case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
+  case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
+  case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
   case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
   case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
   case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
@@ -473,43 +483,42 @@ static bool OtherDecimalToText(int32_t o
      ++p;
    }     
    for(; '\0' != *p ; p++) 
       *p += diff;
    return true;
 }
 static bool TamilToText(int32_t ordinal,  nsString& result)
 {
+   if (ordinal < 1 || ordinal > 9999) {
+     // Can't do those in this system.
+     return false;
+   }
    PRUnichar diff = 0x0BE6 - PRUnichar('0');
    // We're going to be appending to whatever is in "result" already, so make
    // sure to only munge the new bits.  Note that we can't just grab the pointer
    // to the new stuff here, since appending to the string can realloc.
    size_t offset = result.Length();
    DecimalToText(ordinal, result); 
-   if (ordinal < 1 || ordinal > 9999) {
-     // Can't do those in this system.
-     return false;
-   }
    PRUnichar* p = result.BeginWriting() + offset;
    for(; '\0' != *p ; p++) 
       if(*p != PRUnichar('0'))
          *p += diff;
    return true;
 }
 
 
 static const char gLowerRomanCharsA[] = "ixcm";
 static const char gUpperRomanCharsA[] = "IXCM";
 static const char gLowerRomanCharsB[] = "vld";
 static const char gUpperRomanCharsB[] = "VLD";
 
 static bool RomanToText(int32_t ordinal, nsString& result, const char* achars, const char* bchars)
 {
   if (ordinal < 1 || ordinal > 3999) {
-    DecimalToText(ordinal, result);
     return false;
   }
   nsAutoString addOn, decStr;
   decStr.AppendInt(ordinal, 10);
   int len = decStr.Length();
   const PRUnichar* dp = decStr.get();
   const PRUnichar* end = dp + len;
   int romanPos = len;
@@ -728,128 +737,245 @@ 0x1338, 0x1340, 0x1348, 0x1350
 
 #define NUM_BUF_SIZE 34 
 
 static bool CharListToText(int32_t ordinal, nsString& result, const PRUnichar* chars, int32_t aBase)
 {
   PRUnichar buf[NUM_BUF_SIZE];
   int32_t idx = NUM_BUF_SIZE;
   if (ordinal < 1) {
-    DecimalToText(ordinal, result);
     return false;
   }
   do {
     ordinal--; // a == 0
     int32_t cur = ordinal % aBase;
     buf[--idx] = chars[cur];
     ordinal /= aBase ;
   } while ( ordinal > 0);
   result.Append(buf+idx,NUM_BUF_SIZE-idx);
   return true;
 }
 
-
-static const PRUnichar gCJKIdeographicDigit1[10] =
-{
-  0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,  // 0 - 4
-  0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d   // 5 - 9
-};
-static const PRUnichar gCJKIdeographicDigit2[10] =
+static const PRUnichar gCJKDecimalChars[10] =
 {
-  0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,  // 0 - 4
-  0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396   // 5 - 9
+  0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+  0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
 };
-static const PRUnichar gCJKIdeographicDigit3[10] =
-{
-  0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,  // 0 - 4
-  0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396   // 5 - 9
-};
-static const PRUnichar gCJKIdeographicUnit1[4] =
+static bool CharListDecimalToText(int32_t ordinal, nsString& result, const PRUnichar* chars)
 {
-  0x000, 0x5341, 0x767e, 0x5343
+  if (ordinal < 0) {
+    return false;
+  }
+  PRUnichar buf[NUM_BUF_SIZE];
+  int32_t idx = NUM_BUF_SIZE;
+  do {
+    buf[--idx] = chars[ordinal % 10];
+    ordinal /= 10;
+  } while (ordinal > 0);
+  result.Append(buf + idx, NUM_BUF_SIZE - idx);
+  return true;
+}
+
+enum CJKIdeographicLang {
+  CHINESE, KOREAN, JAPANESE
+};
+struct CJKIdeographicData {
+  const PRUnichar *negative;
+  PRUnichar digit[10];
+  PRUnichar unit[3];
+  PRUnichar unit10K[2];
+  uint8_t lang;
+  bool informal;
+};
+static const PRUnichar gJapaneseNegative[] = {
+  0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
 };
-static const PRUnichar gCJKIdeographicUnit2[4] =
-{
-  0x000, 0x62FE, 0x4F70, 0x4EDF
+static const CJKIdeographicData gDataJapaneseInformal = {
+  gJapaneseNegative,          // negative
+  {                           // digit
+    0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x4e07, 0x5104 },         // unit10K
+  JAPANESE,                   // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataJapaneseFormal = {
+  gJapaneseNegative,          // negative
+  {                           // digit
+    0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
+    0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x62fe, 0x767e, 0x9621 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  JAPANESE,                   // lang
+  false                       // informal
+};
+static const PRUnichar gKoreanNegative[] = {
+  0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
+};
+static const CJKIdeographicData gDataKoreanHangulFormal = {
+  gKoreanNegative,            // negative
+  {                           // digit
+    0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
+    0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
+  },
+  { 0xc2ed, 0xbc31, 0xcc9c }, // unit
+  { 0xb9cc, 0xc5b5 },         // unit10K
+  KOREAN,                     // lang
+  false                       // informal
 };
-static const PRUnichar gCJKIdeographic10KUnit1[4] =
-{
-  0x000, 0x842c, 0x5104, 0x5146
+static const CJKIdeographicData gDataKoreanHanjaInformal = {
+  gKoreanNegative,            // negative
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  KOREAN,                     // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataKoreanHanjaFormal = {
+  gKoreanNegative,            // negative
+  {                           // digit
+    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x62fe, 0x767e, 0x4edf }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  KOREAN,                     // lang
+  false                       // informal
+};
+static const PRUnichar gSimpChineseNegative[] = {
+  0x8d1f, 0x0000
+};
+static const CJKIdeographicData gDataSimpChineseInformal = {
+  gSimpChineseNegative,       // negative
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x4e07, 0x4ebf },         // unit10K
+  CHINESE,                    // lang
+  true                        // informal
 };
-static const PRUnichar gCJKIdeographic10KUnit2[4] =
-{
-  0x000, 0x4E07, 0x4ebf, 0x5146
+static const CJKIdeographicData gDataSimpChineseFormal = {
+  gSimpChineseNegative,       // negative
+  {                           // digit
+    0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
+    0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
+  },
+  { 0x62fe, 0x4f70, 0x4edf }, // unit
+  { 0x4e07, 0x4ebf },         // unit10K
+  CHINESE,                    // lang
+  false                       // informal
+};
+static const PRUnichar gTradChineseNegative[] = {
+  0x8ca0, 0x0000
 };
-static const PRUnichar gCJKIdeographic10KUnit3[4] =
-{
-  0x000, 0x4E07, 0x5104, 0x5146
+static const CJKIdeographicData gDataTradChineseInformal = {
+  gTradChineseNegative,       // negative
+  {                           // digit
+    0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
+    0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
+  },
+  { 0x5341, 0x767e, 0x5343 }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  CHINESE,                    // lang
+  true                        // informal
+};
+static const CJKIdeographicData gDataTradChineseFormal = {
+  gTradChineseNegative,       // negative
+  {                           // digit
+    0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
+    0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
+  },
+  { 0x62fe, 0x4f70, 0x4edf }, // unit
+  { 0x842c, 0x5104 },         // unit10K
+  CHINESE,                    // lang
+  false                       // informal
 };
 
-static const bool CJKIdeographicToText(int32_t ordinal, nsString& result, 
-                                   const PRUnichar* digits,
-                                   const PRUnichar *unit, 
-                                   const PRUnichar* unit10k)
+static const bool CJKIdeographicToText(int32_t ordinal, nsString& result,
+                                       const CJKIdeographicData& data)
 {
-// In theory, we need the following if condiction,
-// However, the limit, 10 ^ 16, is greater than the max of uint32_t
-// so we don't really need to test it here.
-// if( ordinal > 9999999999999999)
-// {
-//    PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
-//    result.Append(cbuf);
-// } 
-// else 
-// {
-  if (ordinal < 0) {
-    DecimalToText(ordinal, result);
-    return false;
-  }
-  PRUnichar c10kUnit = 0;
-  PRUnichar cUnit = 0;
-  PRUnichar cDigit = 0;
-  uint32_t ud = 0;
   PRUnichar buf[NUM_BUF_SIZE];
   int32_t idx = NUM_BUF_SIZE;
-  bool bOutputZero = ( 0 == ordinal );
+  int32_t pos = 0;
+  bool isNegative = (ordinal < 0);
+  bool needZero = (ordinal == 0);
+  int32_t unitidx = 0, unit10Kidx = 0;
+  if (isNegative) {
+    ordinal = -ordinal;
+  }
   do {
-    if(0 == (ud % 4)) {
-      c10kUnit = unit10k[ud/4];
+    unitidx = pos % 4;
+    if (unitidx == 0) {
+      unit10Kidx = pos / 4;
     }
     int32_t cur = ordinal % 10;
-    cDigit = digits[cur];
-    if( 0 == cur)
-    {
-      cUnit = 0;
-      if(bOutputZero) {
-        bOutputZero = false;
-        if(0 != cDigit)
-          buf[--idx] = cDigit;
+    if (cur == 0) {
+      if (needZero) {
+        needZero = false;
+        buf[--idx] = data.digit[0];
+      }
+    } else {
+      if (data.lang == CHINESE) {
+        needZero = true;
+      }
+      if (unit10Kidx != 0) {
+        if (data.lang == KOREAN) {
+          buf[--idx] = ' ';
+        }
+        buf[--idx] = data.unit10K[unit10Kidx - 1];
+      }
+      if (unitidx != 0) {
+        buf[--idx] = data.unit[unitidx - 1];
       }
-    }
-    else
-    {
-      bOutputZero = true;
-      cUnit = unit[ud%4];
-
-      if(0 != c10kUnit)
-        buf[--idx] = c10kUnit;
-      if(0 != cUnit)
-        buf[--idx] = cUnit;
-      if((0 != cDigit) && 
-         ( (1 != cur) || (1 != (ud%4)) || ( ordinal > 10)) )
-        buf[--idx] = cDigit;
-
-      c10kUnit =  0;
+      if (cur != 1) {
+        buf[--idx] = data.digit[cur];
+      } else {
+        bool needOne = true;
+        if (data.informal) {
+          switch (data.lang) {
+            case CHINESE:
+              if (unitidx == 1 &&
+                  (ordinal == 1 || (pos > 4 && ordinal % 1000 == 1))) {
+                needOne = false;
+              }
+              break;
+            case JAPANESE:
+              if (unitidx > 0 &&
+                  (unitidx != 3 || (pos == 3 && ordinal == 1))) {
+                needOne = false;
+              }
+              break;
+            case KOREAN:
+              if (unitidx > 0 || (pos == 4 && (ordinal % 1000) == 1)) {
+                needOne = false;
+              }
+              break;
+          }
+        }
+        if (needOne) {
+          buf[--idx] = data.digit[1];
+        }
+      }
+      unit10Kidx = 0;
     }
     ordinal /= 10;
-    ++ud;
-
-  } while( ordinal > 0);
-  result.Append(buf+idx,NUM_BUF_SIZE-idx);
-// }
+    pos++;
+  } while (ordinal > 0);
+  if (isNegative) {
+    result.Append(data.negative);
+  }
+  result.Append(buf + idx, NUM_BUF_SIZE - idx);
   return true;
 }
 
 #define HEBREW_GERESH       0x05F3
 static const PRUnichar gHebrewDigit[22] = 
 {
 //   1       2       3       4       5       6       7       8       9
 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
@@ -857,17 +983,16 @@ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 
 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
 // 100     200     300     400
 0x05E7, 0x05E8, 0x05E9, 0x05EA
 };
 
 static bool HebrewToText(int32_t ordinal, nsString& result)
 {
   if (ordinal < 1 || ordinal > 999999) {
-    DecimalToText(ordinal, result);
     return false;
   }
   bool outputSep = false;
   nsAutoString allText, thousandsGroup;
   do {
     thousandsGroup.Truncate();
     int32_t n3 = ordinal % 1000;
     // Process digit for 100 - 900
@@ -916,17 +1041,16 @@ static bool HebrewToText(int32_t ordinal
   result.Append(allText);
   return true;
 }
 
 
 static bool ArmenianToText(int32_t ordinal, nsString& result)
 {
   if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system
-    DecimalToText(ordinal, result);
     return false;
   }
 
   PRUnichar buf[NUM_BUF_SIZE];
   int32_t idx = NUM_BUF_SIZE;
   int32_t d = 0;
   do {
     int32_t cur = ordinal % 10;
@@ -953,17 +1077,16 @@ static const PRUnichar gGeorgianValue [ 
 //   1000    2000    3000    4000    5000    6000    7000    8000    9000
    0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0,
 //  10000
    0x10F5
 };
 static bool GeorgianToText(int32_t ordinal, nsString& result)
 {
   if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system
-    DecimalToText(ordinal, result);
     return false;
   }
 
   PRUnichar buf[NUM_BUF_SIZE];
   int32_t idx = NUM_BUF_SIZE;
   int32_t d = 0;
   do {
     int32_t cur = ordinal % 10;
@@ -986,22 +1109,21 @@ static bool GeorgianToText(int32_t ordin
 // Another reference is Unicode 3.0 standard section 11.1.
 #define ETHIOPIC_ONE             0x1369
 #define ETHIOPIC_TEN             0x1372
 #define ETHIOPIC_HUNDRED         0x137B
 #define ETHIOPIC_TEN_THOUSAND    0x137C
 
 static bool EthiopicToText(int32_t ordinal, nsString& result)
 {
+  if (ordinal < 1) {
+    return false;
+  }
   nsAutoString asciiNumberString;      // decimal string representation of ordinal
   DecimalToText(ordinal, asciiNumberString);
-  if (ordinal < 1) {
-    result.Append(asciiNumberString);
-    return false;
-  }
   uint8_t asciiStringLength = asciiNumberString.Length();
 
   // If number length is odd, add a leading "0"
   // the leading "0" preconditions the string to always have the
   // leading tens place populated, this avoids a check within the loop.
   // If we didn't add the leading "0", decrement asciiStringLength so
   // it will be equivalent to a zero-based index in both cases.
   if (asciiStringLength & 1) {
@@ -1049,22 +1171,25 @@ static bool EthiopicToText(int32_t ordin
         result.Append((PRUnichar) ETHIOPIC_TEN_THOUSAND);
       }
     }
   }
   return true;
 }
 
 
-/* static */ bool
+/* static */ void
 nsBulletFrame::AppendCounterText(int32_t aListStyleType,
                                  int32_t aOrdinal,
-                                 nsString& result)
+                                 nsString& result,
+                                 bool& isRTL)
 {
   bool success = true;
+  int32_t fallback = NS_STYLE_LIST_STYLE_DECIMAL;
+  isRTL = false;
   
   switch (aListStyleType) {
     case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
       break;
 
     case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
       // XXX We really need to do this the same way we do list bullets.
       result.Append(PRUnichar(0x2022));
@@ -1079,22 +1204,27 @@ nsBulletFrame::AppendCounterText(int32_t
       // XXX We really need to do this the same way we do list bullets.
       result.Append(PRUnichar(0x25FE));
       break;
 
     case NS_STYLE_LIST_STYLE_DECIMAL:
     default: // CSS2 say "A users  agent that does not recognize a numbering system
       // should use 'decimal'
       success = DecimalToText(aOrdinal, result);
+      NS_ASSERTION(success, "DecimalToText must never fail");
       break;
 
     case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
       success = DecimalLeadingZeroToText(aOrdinal, result);
       break;
 
+    case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
+      success = CharListDecimalToText(aOrdinal, result, gCJKDecimalChars);
+      break;
+
     case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
       success = RomanToText(aOrdinal, result,
                             gLowerRomanCharsA, gLowerRomanCharsB);
       break;
     case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
       success = RomanToText(aOrdinal, result,
                             gUpperRomanCharsA, gUpperRomanCharsB);
       break;
@@ -1128,53 +1258,73 @@ nsBulletFrame::AppendCounterText(int32_t
       break;
 
     case NS_STYLE_LIST_STYLE_LOWER_GREEK:
       success = CharListToText(aOrdinal, result, gLowerGreekChars ,
                                LOWER_GREEK_CHARS_SIZE);
       break;
 
     case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: 
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
     case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
-                                     gCJKIdeographicUnit1,
-                                     gCJKIdeographic10KUnit1);
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success =
+        CJKIdeographicToText(aOrdinal, result, gDataTradChineseInformal);
       break;
 
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit2,
-                                     gCJKIdeographicUnit2,
-                                     gCJKIdeographic10KUnit1);
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success = CJKIdeographicToText(aOrdinal, result, gDataTradChineseFormal);
+      break;
+
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success =
+        CJKIdeographicToText(aOrdinal, result, gDataSimpChineseInformal);
+      break;
+
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success = CJKIdeographicToText(aOrdinal, result, gDataSimpChineseFormal);
       break;
 
-    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
-                                     gCJKIdeographicUnit1,
-                                     gCJKIdeographic10KUnit2);
+    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseInformal);
+      break;
+
+    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseFormal);
       break;
 
-    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit3,
-                                     gCJKIdeographicUnit2,
-                                     gCJKIdeographic10KUnit2);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success =
+        CJKIdeographicToText(aOrdinal, result, gDataKoreanHangulFormal);
       break;
 
-    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
-                                     gCJKIdeographicUnit1,
-                                     gCJKIdeographic10KUnit3);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success =
+        CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaInformal);
       break;
 
-    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
-      success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit2,
-                                     gCJKIdeographicUnit2,
-                                     gCJKIdeographic10KUnit3);
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
+      success = CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaFormal);
       break;
 
     case NS_STYLE_LIST_STYLE_HEBREW: 
+      isRTL = true;
       success = HebrewToText(aOrdinal, result);
       break;
 
     case NS_STYLE_LIST_STYLE_ARMENIAN: 
       success = ArmenianToText(aOrdinal, result);
       break;
 
     case NS_STYLE_LIST_STYLE_GEORGIAN: 
@@ -1238,21 +1388,23 @@ nsBulletFrame::AppendCounterText(int32_t
       success = OtherDecimalToText(aOrdinal, 0x0C66, result);
       break;
  
     case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
       success = TamilToText(aOrdinal, result);
       break;
 
     case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
       success = CharListToText(aOrdinal, result, gCJKHeavenlyStemChars,
                                CJK_HEAVENLY_STEM_CHARS_SIZE);
       break;
 
     case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
+      fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
       success = CharListToText(aOrdinal, result, gCJKEarthlyBranchChars,
                                CJK_EARTHLY_BRANCH_CHARS_SIZE);
       break;
 
     case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
       success = CharListToText(aOrdinal, result, gHangulChars, HANGUL_CHARS_SIZE);
       break;
 
@@ -1280,47 +1432,92 @@ nsBulletFrame::AppendCounterText(int32_t
                                ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE);
       break;
 
     case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
       success = CharListToText(aOrdinal, result, gEthiopicHalehameTiEtChars,
                                ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE);
       break;
   }
-  return success;
+  if (!success) {
+    AppendCounterText(fallback, aOrdinal, result, isRTL);
+  }
 }
 
-bool
+/* static */ void
+nsBulletFrame::GetListItemSuffix(int32_t aListStyleType,
+                                 nsString& aResult,
+                                 bool& aSuppressPadding)
+{
+  aResult = '.';
+  aSuppressPadding = false;
+
+  switch (aListStyleType) {
+    case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
+    case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
+    case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only
+    case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only
+      aResult.Truncate();
+      break;
+
+    case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
+    case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
+    case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
+      aResult = 0x3001;
+      aSuppressPadding = true;
+      break;
+
+    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+    case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
+    case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
+      aResult = ',';
+      break;
+  }
+}
+
+void
 nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
                                nsString& result)
 {
   const nsStyleVisibility* vis = StyleVisibility();
 
   NS_ASSERTION(aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
                aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
                aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
                aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_SQUARE,
                "we should be using specialized code for these types");
-  bool success =
-    AppendCounterText(aListStyle.mListStyleType, mOrdinal, result);
-  if (success && aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_HEBREW)
-    mTextIsRTL = true;
 
-  // XXX For some of these systems, "." is wrong!  This should really be
-  // pushed down into the individual cases!
-  nsString suffix = NS_LITERAL_STRING(".");
+  result.Truncate();
+  AppendCounterText(aListStyle.mListStyleType, mOrdinal, result, mTextIsRTL);
+
+  nsAutoString suffix;
+  GetListItemSuffix(aListStyle.mListStyleType, suffix, mSuppressPadding);
 
   // We're not going to do proper Bidi reordering on the list item marker, but
   // just display the whole thing as RTL or LTR, so we fake reordering by
   // appending the suffix to the end of the list item marker if the
   // directionality of the characters is the same as the style direction or
   // prepending it to the beginning if they are different.
   result = (mTextIsRTL == (vis->mDirection == NS_STYLE_DIRECTION_RTL)) ?
           result + suffix : suffix + result;
-  return success;
 }
 
 #define MIN_BULLET_SIZE 1
 
 
 void
 nsBulletFrame::GetDesiredSize(nsPresContext*  aCX,
                               nsRenderingContext *aRenderingContext,
@@ -1372,36 +1569,46 @@ nsBulletFrame::GetDesiredSize(nsPresCont
 
     case NS_STYLE_LIST_STYLE_DISC:
     case NS_STYLE_LIST_STYLE_CIRCLE:
     case NS_STYLE_LIST_STYLE_SQUARE:
       ascent = fm->MaxAscent();
       bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
                           NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
       mPadding.bottom = NSToCoordRound(float(ascent) / 8.0f);
-      aMetrics.width = mPadding.right + bulletSize;
-      aMetrics.ascent = aMetrics.height = mPadding.bottom + bulletSize;
+      aMetrics.width = aMetrics.height = bulletSize;
+      aMetrics.ascent = bulletSize + mPadding.bottom;
       break;
 
     default:
     case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
     case NS_STYLE_LIST_STYLE_DECIMAL:
+    case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
     case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
     case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
     case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
     case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
     case NS_STYLE_LIST_STYLE_KATAKANA:
     case NS_STYLE_LIST_STYLE_HIRAGANA:
     case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
     case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
     case NS_STYLE_LIST_STYLE_LOWER_GREEK:
     case NS_STYLE_LIST_STYLE_HEBREW: 
     case NS_STYLE_LIST_STYLE_ARMENIAN: 
     case NS_STYLE_LIST_STYLE_GEORGIAN: 
     case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: 
+    case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
+    case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
+    case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
     case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: 
     case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
     case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
@@ -1429,17 +1636,16 @@ nsBulletFrame::GetDesiredSize(nsPresCont
     case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
     case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
       GetListItemText(*myList, text);
       aMetrics.height = fm->MaxHeight();
       aRenderingContext->SetFont(fm);
       aMetrics.width =
         nsLayoutUtils::GetStringWidth(this, aRenderingContext,
                                       text.get(), text.Length());
-      aMetrics.width += mPadding.right;
       aMetrics.ascent = fm->MaxAscent();
       break;
   }
 }
 
 NS_IMETHODIMP
 nsBulletFrame::Reflow(nsPresContext* aPresContext,
                       nsHTMLReflowMetrics& aMetrics,
@@ -1448,24 +1654,35 @@ nsBulletFrame::Reflow(nsPresContext* aPr
 {
   DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   SetFontSizeInflation(inflation);
 
   // Get the base size
+  // This will also set mSuppressPadding appropriately (via GetListItemText())
+  // for the builtin counter styles with ideographic comma as suffix where the
+  // default padding from ua.css is not desired.
   GetDesiredSize(aPresContext, aReflowState.rendContext, aMetrics, inflation);
 
   // Add in the border and padding; split the top/bottom between the
   // ascent and descent to make things look nice
   const nsMargin& borderPadding = aReflowState.mComputedBorderPadding;
-  aMetrics.width += borderPadding.left + borderPadding.right;
-  aMetrics.height += borderPadding.top + borderPadding.bottom;
-  aMetrics.ascent += borderPadding.top;
+  if (!mSuppressPadding ||
+      aPresContext->HasAuthorSpecifiedRules(this,
+                                            NS_AUTHOR_SPECIFIED_PADDING)) {
+    mPadding.top += NSToCoordRound(borderPadding.top * inflation);
+    mPadding.right += NSToCoordRound(borderPadding.right * inflation);
+    mPadding.bottom += NSToCoordRound(borderPadding.bottom * inflation);
+    mPadding.left += NSToCoordRound(borderPadding.left * inflation);
+  }
+  aMetrics.width += mPadding.left + mPadding.right;
+  aMetrics.height += mPadding.top + mPadding.bottom;
+  aMetrics.ascent += mPadding.top;
 
   // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
   // overflow their font-boxes. It'll do for now; to fix it for real, we really
   // should rewrite all the text-handling code here to use gfxTextRun (bug
   // 397294).
   aMetrics.SetOverflowAreasToDesiredBounds();
 
   aStatus = NS_FRAME_COMPLETE;
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -72,23 +72,28 @@ public:
   virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
 
   // nsBulletFrame
   int32_t SetListItemOrdinal(int32_t aNextOrdinal, bool* aChanged,
                              int32_t aIncrement);
 
 
   /* get list item text, without '.' */
-  static bool AppendCounterText(int32_t aListStyleType,
-                                  int32_t aOrdinal,
-                                  nsString& aResult);
+  static void AppendCounterText(int32_t aListStyleType,
+                                int32_t aOrdinal,
+                                nsString& aResult,
+                                bool& aIsRTL);
+
+  /* get suffix of list item */
+  static void GetListItemSuffix(int32_t aListStyleType,
+                                nsString& aResult,
+                                bool& aSuppressPadding);
 
   /* get list item text, with '.' */
-  bool GetListItemText(const nsStyleList& aStyleList,
-                         nsString& aResult);
+  void GetListItemText(const nsStyleList& aStyleList, nsString& aResult);
                          
   void PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
                    const nsRect& aDirtyRect, uint32_t aFlags);
   
   virtual bool IsEmpty() MOZ_OVERRIDE;
   virtual bool IsSelfEmpty() MOZ_OVERRIDE;
   virtual nscoord GetBaseline() const MOZ_OVERRIDE;
 
@@ -115,16 +120,22 @@ protected: