Bug 929885 - Implement web components ShadowRoot style sheet behavior. r=mrbkap
authorWilliam Chen <wchen@mozilla.com>
Mon, 02 Dec 2013 02:26:12 -0800
changeset 158309 1ac0576fa66f
parent 158308 79b61f9909c0
child 158310 01a00ce2b662
push id25741
push userryanvm@gmail.com
push dateMon, 02 Dec 2013 21:39:06 +0000
treeherdermozilla-central@492fbc8095b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs929885
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
Bug 929885 - Implement web components ShadowRoot style sheet behavior. r=mrbkap
content/base/src/Element.cpp
content/base/src/ShadowRoot.cpp
content/base/src/ShadowRoot.h
content/base/src/nsDocument.h
content/base/src/nsStyleLinkElement.cpp
content/base/src/nsStyleLinkElement.h
content/html/content/src/HTMLLinkElement.cpp
content/html/content/src/HTMLStyleElement.cpp
content/svg/content/src/SVGStyleElement.cpp
content/xbl/src/nsXBLPrototypeBinding.cpp
content/xbl/src/nsXBLPrototypeBinding.h
content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
content/xml/content/src/XMLStylesheetProcessingInstruction.h
dom/base/nsDOMClassInfo.cpp
dom/bindings/Bindings.conf
dom/tests/mochitest/webcomponents/inert_style.css
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
dom/tests/mochitest/webcomponents/test_shadowroot_style.html
dom/tests/mochitest/webcomponents/test_shadowroot_style_multiple_shadow.html
dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
dom/tests/mochitest/webcomponents/test_style_fallback_content.html
dom/webidl/ShadowRoot.webidl
layout/base/RestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsFrameManager.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/style/Loader.cpp
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -740,24 +740,29 @@ Element::CreateShadowRoot(ErrorResult& a
   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());
+  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) {
--- a/content/base/src/ShadowRoot.cpp
+++ b/content/base/src/ShadowRoot.cpp
@@ -6,16 +6,17 @@
 #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
@@ -27,42 +28,47 @@ IdentifierMapEntryTraverse(nsIdentifierM
   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)
+                       already_AddRefed<nsINodeInfo> aNodeInfo,
+                       nsXBLPrototypeBinding* aProtoBinding)
   : DocumentFragment(aNodeInfo), mHost(aContent),
-    mInsertionPointChanged(false)
+    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;
 
@@ -96,16 +102,76 @@ ShadowRoot::FromNode(nsINode* aNode)
     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>
@@ -327,16 +393,45 @@ ShadowRoot::GetInnerHTML(nsAString& aInn
 }
 
 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)
@@ -436,8 +531,60 @@ ShadowRoot::ContentRemoved(nsIDocument* 
 
   // 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;
+}
+
--- a/content/base/src/ShadowRoot.h
+++ b/content/base/src/ShadowRoot.h
@@ -20,36 +20,44 @@ 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);
+  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);
 
   /**
@@ -64,16 +72,21 @@ public:
    */
   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;
@@ -87,31 +100,57 @@ public:
   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/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -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/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/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/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/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',
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
@@ -3,8 +3,14 @@
 [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_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/ShadowRoot.webidl
+++ b/dom/webidl/ShadowRoot.webidl
@@ -14,10 +14,12 @@
 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/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
--- 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
@@ -5729,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();
 
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -43,16 +43,17 @@
 #include "nsICSSLoaderObserver.h"
 #include "nsCSSParser.h"
 #include "mozilla/css/ImportRule.h"
 #include "nsThreadUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIThreadInternal.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsINetworkSeer.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/URL.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 
 #include "nsIMediaList.h"
 #include "nsIDOMStyleSheet.h"
@@ -1873,18 +1874,24 @@ Loader::LoadInlineStyle(nsIContent* aEle
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(state == eSheetNeedsParser,
                "Inline sheets should not be cached");
 
   LOG(("  Sheet is alternate: %d", *aIsAlternate));
 
   PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
 
-  rv = InsertSheetInDoc(sheet, aElement, mDocument);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (aElement->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
+    ShadowRoot* containingShadow = aElement->GetContainingShadow();
+    MOZ_ASSERT(containingShadow);
+    containingShadow->InsertSheet(sheet, aElement);
+  } else {
+    rv = InsertSheetInDoc(sheet, aElement, mDocument);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   SheetLoadData* data = new SheetLoadData(this, aTitle, nullptr, sheet,
                                           owningElement, *aIsAlternate,
                                           aObserver, nullptr);
 
   // We never actually load this, so just set its principal directly
   sheet->SetPrincipal(aElement->NodePrincipal());