Bug 806506 - Part 1: Implement ShadowRoot interface with DOM accessor methods. r=mrbkap,ehsan
authorWilliam Chen <wchen@mozilla.com>
Mon, 02 Dec 2013 02:26:11 -0800
changeset 158551 08afb5a033eb234e535b4e5a7ef97979f10c97ca
parent 158550 51097a104f9bb57d6eaa4b7c01e04caafe096977
child 158552 28a139b0cf38ef3844b95d191573cb1f06b74e9d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmrbkap, ehsan
bugs806506
milestone28.0a1
Bug 806506 - Part 1: Implement ShadowRoot interface with DOM accessor methods. r=mrbkap,ehsan
content/base/public/Element.h
content/base/public/ElementInlines.h
content/base/public/FragmentOrElement.h
content/base/public/nsIContent.h
content/base/public/nsINode.h
content/base/src/DocumentFragment.h
content/base/src/Element.cpp
content/base/src/FragmentOrElement.cpp
content/base/src/ShadowRoot.cpp
content/base/src/ShadowRoot.h
content/base/src/moz.build
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsDocumentEncoder.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsINode.cpp
content/base/src/nsNodeUtils.cpp
content/base/src/nsRange.cpp
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLBinding.h
dom/tests/mochitest/general/test_interfaces.html
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_shadowroot.html
dom/webidl/Element.webidl
dom/webidl/ShadowRoot.webidl
dom/webidl/moz.build
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsPresShell.cpp
layout/generic/nsSelection.cpp
layout/reftests/reftest.list
layout/reftests/webcomponents/cross-tree-selection-1-ref.html
layout/reftests/webcomponents/cross-tree-selection-1.html
layout/reftests/webcomponents/reftest.list
modules/libpref/src/init/all.js
--- 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
--- 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,16 +354,26 @@ 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;
--- 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/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
@@ -118,16 +118,17 @@
 #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"
 
@@ -681,16 +682,101 @@ 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;
+  }
+
+  // Calling SetPrototypeBinding takes ownership of protoBinding.
+  docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
+
+  nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget());
+  SetShadowRoot(shadowRoot);
+
+  // xblBinding takes ownership of docInfo.
+  nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
+  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 +1078,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 +1178,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 +1298,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);
@@ -3086,18 +3180,18 @@ Serialize(Element* aRoot, bool aDescende
       }
 
       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) {
+        nsIContent* fragHost = frag->GetHost();
+        if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
           current = fragHost;
         }
       }
 
       if (aDescendentsOnly && current == aRoot) {
         return builder.ToString(aOut);
       }
     }
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -115,16 +115,18 @@
 #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 "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 +543,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 +569,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 +965,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;
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "nsContentUtils.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMHTMLElement.h"
+#include "mozilla/dom/Element.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)
+  tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
+                                                DocumentFragment)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHost)
+  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_END_INHERITING(DocumentFragment)
+
+NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
+NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
+
+ShadowRoot::ShadowRoot(nsIContent* aContent,
+                       already_AddRefed<nsINodeInfo> aNodeInfo)
+  : DocumentFragment(aNodeInfo), mHost(aContent)
+{
+  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;
+}
+
+ShadowRoot::~ShadowRoot()
+{
+  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;
+}
+
+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);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/src/ShadowRoot.h
@@ -0,0 +1,69 @@
+/* -*- 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 ShadowRoot : public DocumentFragment
+{
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
+                                           DocumentFragment)
+  NS_DECL_ISUPPORTS_INHERITED
+
+  ShadowRoot(nsIContent* aContent, already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~ShadowRoot();
+
+  void AddToIdTable(Element* aElement, nsIAtom* aId);
+  void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
+  static bool PrefEnabled();
+
+  nsISupports* GetParentObject() const
+  {
+    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);
+protected:
+  nsCOMPtr<nsIContent> mHost;
+  nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
+};
+
+} // 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/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -32,16 +32,17 @@
 #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/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 +4325,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();
--- 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;
   }
--- 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/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);
@@ -1546,17 +1565,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/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/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/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -445,16 +445,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},
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 
 [test_bug900724.html]
 [test_document_register.html]
 [test_document_register_lifecycle.html]
 [test_template.html]
+[test_shadow_root.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
@@ -0,0 +1,78 @@
+<!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.");
+}
+
+</script>
+</body>
+</html>
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -189,11 +189,19 @@ partial interface Element {
 // http://www.w3.org/TR/selectors-api/#interface-definitions
 partial interface Element {
   [Throws]
   Element?  querySelector(DOMString selectors);
   [Throws]
   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/ShadowRoot.webidl
@@ -0,0 +1,21 @@
+/* -*- 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);
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -268,16 +268,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/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -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/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4204,18 +4204,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 +4240,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))) &&
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -70,16 +70,17 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsIFrameInlines.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif // IBMBIDI
 
 #include "nsError.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 using namespace mozilla;
 
 //#define DEBUG_TABLE 1
 
 static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
 
 static nsIAtom *GetTag(nsINode *aNode);
@@ -1740,17 +1741,26 @@ nsFrameSelection::GetFrameForNodeOffset(
           // If we're at a collapsed whitespace content node (which
           // does not have a primary frame), just use the original node
           // to get the frame on which we should put the caret.
           theNode = aNode;
         }
       }
     }
   }
-  
+
+  // If the node is a ShadowRoot, the frame needs to be adjusted,
+  // because a ShadowRoot does not get a frame. Its children are rendered
+  // as children of the host.
+  mozilla::dom::ShadowRoot* shadowRoot =
+    mozilla::dom::ShadowRoot::FromNode(theNode);
+  if (shadowRoot) {
+    theNode = shadowRoot->GetHost();
+  }
+
   nsIFrame* returnFrame = theNode->GetPrimaryFrame();
   if (!returnFrame)
     return nullptr;
 
   // find the child frame containing the offset we want
   returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == HINTRIGHT,
                                              &aOffset, &returnFrame);
   return returnFrame;
@@ -4642,31 +4652,45 @@ Selection::Extend(nsINode* aParentNode, 
   int32_t startOffset = range->StartOffset();
   int32_t endOffset = range->EndOffset();
 
   nsDirection dir = GetDirection();
 
   //compare anchor to old cursor.
 
   // We pass |disconnected| to the following ComparePoints calls in order
-  // to avoid assertions, and there is no special handling required, since
-  // ComparePoints returns 1 in the disconnected case.
+  // to avoid assertions. ComparePoints returns 1 in the disconnected case
+  // and we can end up in various cases below, but it is assumed that in
+  // any of the cases we end up, the nsRange implementation will collapse
+  // the range to the new point because we can not make a valid range with
+  // a disconnected point. This means that whatever range is currently
+  // selected will be cleared.
   bool disconnected = false;
+  bool shouldClearRange = false;
   int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
                                                   focusNode, focusOffset,
                                                   &disconnected);
   //compare old cursor to new cursor
+  shouldClearRange |= disconnected;
   int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
                                                   aParentNode, aOffset,
                                                   &disconnected);
   //compare anchor to new cursor
+  shouldClearRange |= disconnected;
   int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
                                                   aParentNode, aOffset,
                                                   &disconnected);
 
+  // If the points are disconnected, the range will be collapsed below,
+  // resulting in a range that selects nothing.
+  if (shouldClearRange) {
+    // Repaint the current range with the selection removed.
+    selectFrames(presContext, range, false);
+  }
+
   nsRefPtr<nsRange> difRange = new nsRange(aParentNode);
   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
     //select from 1 to 2 unless they are collapsed
     res = range->SetEnd(aParentNode, aOffset);
     if (NS_FAILED(res))
       return res;
     dir = eDirNext;
     res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -304,16 +304,19 @@ skip-if(B2G) include ../../toolkit/theme
 include transform/reftest.list
 
 # 3d transforms
 include transform-3d/reftest.list
 
 # unicode/ (verify that we don't do expend effort doing unicode-aware case checks)
 skip-if(B2G) include unicode/reftest.list
 
+# webcomonents/
+include webcomponents/reftest.list
+
 # widget/
 skip-if(B2G) include ../../widget/reftests/reftest.list
 
 # xml-stylesheet/
 skip-if(B2G) include ../../content/test/reftest/xml-stylesheet/reftest.list
 
 # xul-document-load/
 skip-if(B2G) include xul-document-load/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/cross-tree-selection-1-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div>
+Hello World
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/cross-tree-selection-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+  <script>
+    function tweak() {
+      var host = document.getElementById("host");
+      var shadow = host.createShadowRoot();
+
+      var textNode = document.createTextNode(" World");
+      shadow.appendChild(textNode);
+
+      // Create a selection with focus preceeding anchor
+      var selection = window.getSelection();
+      var range = document.createRange();
+      range.setStart(shadow, 1);
+      range.setEnd(shadow, 1);
+      selection.addRange(range);
+      selection.extend(shadow, 0);
+
+      // Extend selection into a different node tree
+      // (from ShadowRoot into the previous node in the parent node tree).
+      setTimeout(function() {
+        selection.extend(document.getElementById("previous"), 0);
+        document.documentElement.className = '';
+      }, 100);
+    }
+  </script>
+</head>
+<body onload="tweak()">
+<span id="previous">Hello</span><span id="host"></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reftest.list
@@ -0,0 +1,1 @@
+pref(dom.webcomponents.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -891,16 +891,18 @@ pref("privacy.popups.disable_from_plugin
 pref("privacy.donottrackheader.enabled",    false);
 //   0 = tracking is acceptable
 //   1 = tracking is unacceptable
 pref("privacy.donottrackheader.value",      1);
 
 pref("dom.event.contextmenu.enabled",       true);
 pref("dom.event.clipboardevents.enabled",   true);
 
+pref("dom.webcomponents.enabled",           false);
+
 pref("javascript.enabled",                  true);
 pref("javascript.options.strict",           false);
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     true);
 #endif
 pref("javascript.options.baselinejit.content", true);
 pref("javascript.options.baselinejit.chrome",  true);
 pref("javascript.options.ion.content",      true);