Bug 929885 - Implement web components ShadowRoot style sheet behavior. r=mrbkap

This commit is contained in:
William Chen 2013-12-02 02:26:12 -08:00
parent b92be20c45
commit ad649152bc
30 changed files with 615 additions and 70 deletions

View File

@ -745,14 +745,19 @@ Element::CreateShadowRoot(ErrorResult& aError)
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);

View File

@ -11,6 +11,7 @@
#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"
@ -32,6 +33,8 @@ 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
@ -41,6 +44,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ShadowRoot,
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
@ -55,9 +60,10 @@ 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);
@ -101,6 +107,66 @@ ShadowRoot::FromNode(nsINode* 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)
{
@ -332,6 +398,35 @@ 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
@ -441,3 +536,55 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
}
}
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;
}

View File

@ -25,10 +25,12 @@ 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)
@ -39,12 +41,18 @@ public:
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
@ -69,6 +77,11 @@ public:
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
void SetAssociatedBinding(nsXBLBinding* aBinding)
{
mAssociatedBinding = aBinding;
}
nsISupports* GetParentObject() const
{
return mHost;
@ -92,6 +105,8 @@ public:
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
@ -102,6 +117,14 @@ protected:
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
@ -110,6 +133,22 @@ protected:
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

View File

@ -279,22 +279,6 @@ public:
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;

View File

@ -14,6 +14,7 @@
#include "mozilla/css/Loader.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsCSSStyleSheet.h"
#include "nsIContent.h"
#include "nsIDocument.h"
@ -176,17 +177,18 @@ 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
@ -272,7 +274,8 @@ GetScopeElement(nsIStyleSheet* aSheet)
}
nsresult
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
ShadowRoot* aOldShadowRoot,
nsICSSLoaderObserver* aObserver,
bool* aWillNotify,
bool* aIsAlternate,
@ -283,6 +286,17 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
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) {
@ -290,17 +304,20 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
// 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()) {
@ -328,9 +345,15 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
}
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);
}
@ -420,13 +443,22 @@ nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped)
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);
document->AddStyleSheet(mStyleSheet);
document->EndUpdate(UPDATE_STYLE);
containingShadow->InsertSheet(mStyleSheet, thisContent);
} else {
document->BeginUpdate(UPDATE_STYLE);
document->RemoveStyleSheet(mStyleSheet);
mStyleSheet->SetScopeElement(newScopeElement);
document->AddStyleSheet(mStyleSheet);
document->EndUpdate(UPDATE_STYLE);
}
if (oldScopeElement) {
UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);

View File

@ -29,6 +29,12 @@
class nsIDocument;
class nsIURI;
namespace mozilla {
namespace dom {
class ShadowRoot;
} // namespace dom
} // namespace mozilla
class nsStyleLinkElement : public nsIStyleSheetLinkingElement
{
public:
@ -53,8 +59,11 @@ public:
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
@ -65,6 +74,7 @@ protected:
* changed but the URI may not have changed.
*/
nsresult UpdateStyleSheetInternal(nsIDocument *aOldDocument,
mozilla::dom::ShadowRoot *aOldShadowRoot,
bool aForceUpdate = false);
void UpdateStyleSheetScopedness(bool aIsNowScoped);
@ -90,12 +100,17 @@ 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,

View File

@ -131,8 +131,9 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument) {
// Link must be inert in ShadowRoot.
if (aDocument && !GetContainingShadow()) {
aDocument->RegisterPendingLinkUpdate(this);
}
@ -166,12 +167,19 @@ HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
// 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
@ -248,7 +256,7 @@ HTMLLinkElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
dropSheet = !(linkTypes & STYLESHEET);
}
UpdateStyleSheetInternal(nullptr,
UpdateStyleSheetInternal(nullptr, nullptr,
dropSheet ||
(aName == nsGkAtoms::title ||
aName == nsGkAtoms::media ||
@ -272,7 +280,7 @@ HTMLLinkElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
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

View File

@ -133,7 +133,7 @@ void
HTMLStyleElement::ContentChanged(nsIContent* aContent)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
UpdateStyleSheetInternal(nullptr);
UpdateStyleSheetInternal(nullptr, nullptr);
}
}
@ -157,9 +157,9 @@ void
HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
ShadowRoot* oldShadow = GetContainingShadow();
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
UpdateStyleSheetInternal(oldDoc);
UpdateStyleSheetInternal(oldDoc, oldShadow);
}
nsresult
@ -173,7 +173,7 @@ HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aName == nsGkAtoms::title ||
aName == nsGkAtoms::media ||
aName == nsGkAtoms::type) {
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
} else if (aName == nsGkAtoms::scoped) {
UpdateStyleSheetScopedness(true);
}
@ -192,7 +192,7 @@ HTMLStyleElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
if (aAttribute == nsGkAtoms::title ||
aAttribute == nsGkAtoms::media ||
aAttribute == nsGkAtoms::type) {
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
} else if (aAttribute == nsGkAtoms::scoped) {
UpdateStyleSheetScopedness(false);
}
@ -218,7 +218,7 @@ HTMLStyleElement::SetInnerHTML(const nsAString& aInnerHTML,
SetEnableUpdates(true);
UpdateStyleSheetInternal(nullptr);
UpdateStyleSheetInternal(nullptr, nullptr);
}
already_AddRefed<nsIURI>

View File

@ -83,9 +83,9 @@ void
SVGStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
ShadowRoot* oldShadow = GetShadowRoot();
SVGStyleElementBase::UnbindFromTree(aDeep, aNullParent);
UpdateStyleSheetInternal(oldDoc);
UpdateStyleSheetInternal(oldDoc, oldShadow);
}
nsresult
@ -99,7 +99,7 @@ SVGStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
if (aName == nsGkAtoms::title ||
aName == nsGkAtoms::media ||
aName == nsGkAtoms::type) {
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
} else if (aName == nsGkAtoms::scoped) {
UpdateStyleSheetScopedness(true);
}
@ -118,7 +118,7 @@ SVGStyleElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
if (aAttribute == nsGkAtoms::title ||
aAttribute == nsGkAtoms::media ||
aAttribute == nsGkAtoms::type) {
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
} else if (aAttribute == nsGkAtoms::scoped) {
UpdateStyleSheetScopedness(false);
}
@ -186,7 +186,7 @@ void
SVGStyleElement::ContentChanged(nsIContent* aContent)
{
if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
UpdateStyleSheetInternal(nullptr);
UpdateStyleSheetInternal(nullptr, nullptr);
}
}

View File

@ -596,6 +596,16 @@ nsXBLPrototypeBinding::GetRuleProcessor()
return nullptr;
}
nsXBLPrototypeResources::sheet_array_type*
nsXBLPrototypeBinding::GetOrCreateStyleSheets()
{
if (!mResources) {
mResources = new nsXBLPrototypeResources(this);
}
return &mResources->mStyleSheetList;
}
nsXBLPrototypeResources::sheet_array_type*
nsXBLPrototypeBinding::GetStyleSheets()
{

View File

@ -57,6 +57,7 @@ public:
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; }
@ -116,6 +117,7 @@ public:
void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
nsIStyleRuleProcessor* GetRuleProcessor();
nsXBLPrototypeResources::sheet_array_type* GetOrCreateStyleSheets();
nsXBLPrototypeResources::sheet_array_type* GetStyleSheets();
bool HasStyleSheets() {

View File

@ -72,7 +72,7 @@ XMLStylesheetProcessingInstruction::UnbindFromTree(bool aDeep, bool aNullParent)
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
ProcessingInstruction::UnbindFromTree(aDeep, aNullParent);
UpdateStyleSheetInternal(oldDoc);
UpdateStyleSheetInternal(oldDoc, nullptr);
}
// nsIDOMNode
@ -83,7 +83,7 @@ XMLStylesheetProcessingInstruction::SetNodeValueInternal(const nsAString& aNodeV
{
nsGenericDOMDataNode::SetNodeValueInternal(aNodeValue, aError);
if (!aError.Failed()) {
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
}
}

View File

@ -69,7 +69,7 @@ public:
if (rv.Failed()) {
return;
}
UpdateStyleSheetInternal(nullptr, true);
UpdateStyleSheetInternal(nullptr, nullptr, true);
}
using ProcessingInstruction::SetData; // Prevent hiding overloaded virtual function.

View File

@ -4278,9 +4278,10 @@ 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;
}

View File

@ -964,6 +964,12 @@ DOMInterfaces = {
'nativeType': 'nsDOMScrollAreaEvent',
},
'ShadowRoot': {
'resultNotAddRefed': [
'styleSheets'
]
},
'SharedWorker': {
'nativeType': 'mozilla::dom::workers::SharedWorker',
'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',

View File

@ -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;
}

View File

@ -8,3 +8,9 @@
[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]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -19,5 +19,7 @@ interface ShadowRoot : DocumentFragment
HTMLCollection getElementsByClassName(DOMString classNames);
[SetterThrows,TreatNullAs=EmptyString]
attribute DOMString innerHTML;
attribute boolean applyAuthorStyles;
readonly attribute StyleSheetList styleSheets;
};

View File

@ -2577,7 +2577,7 @@ ElementRestyler::RestyleUndisplayedChildren(nsRestyleHint aChildRestyleHint)
// 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);
}
@ -2748,7 +2748,7 @@ ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
// 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);
}

View File

@ -3540,7 +3540,7 @@ nsCSSFrameConstructor::ConstructFrameFromItemInternal(FrameConstructionItem& aIt
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 {

View File

@ -611,7 +611,7 @@ nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent)
// 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;

View File

@ -98,6 +98,7 @@ class Selection;
namespace dom {
class Element;
class Touch;
class ShadowRoot;
} // namespace dom
namespace layers{
@ -527,6 +528,11 @@ public:
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.

View File

@ -40,6 +40,7 @@
#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"
@ -159,6 +160,7 @@
#include "nsIScreenManager.h"
#include "nsPlaceholderFrame.h"
#include "nsTransitionManager.h"
#include "ChildIterator.h"
#include "RestyleManager.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDragSession.h"
@ -5733,6 +5735,22 @@ private:
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,

View File

@ -348,6 +348,8 @@ public:
virtual bool AssumeAllImagesVisible() MOZ_OVERRIDE;
virtual void RestyleShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot);
protected:
virtual ~PresShell();

View File

@ -48,6 +48,7 @@
#include "nsIThreadInternal.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsINetworkSeer.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/URL.h"
#ifdef MOZ_XUL
@ -1878,8 +1879,14 @@ Loader::LoadInlineStyle(nsIContent* aElement,
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,