Index: Source/core/html/HTMLStyleElement.cpp |
diff --git a/Source/core/html/HTMLStyleElement.cpp b/Source/core/html/HTMLStyleElement.cpp |
index 8f3353943335d8ad296d1d52f1102808c0e3b15a..e4db64902d6d8a7a7a5b4c62477f2a602e2dfc6c 100644 |
--- a/Source/core/html/HTMLStyleElement.cpp |
+++ b/Source/core/html/HTMLStyleElement.cpp |
@@ -49,12 +49,15 @@ |
, StyleElement(&document, createdByParser) |
, m_firedLoad(false) |
, m_loadedSheet(false) |
+ , m_scopedStyleRegistrationState(NotRegistered) |
{ |
ScriptWrappable::init(this); |
} |
HTMLStyleElement::~HTMLStyleElement() |
{ |
+ // During tear-down, willRemove isn't called, so m_scopedStyleRegistrationState may still be RegisteredAsScoped or RegisteredInShadowRoot here. |
+ // Therefore we can't ASSERT(m_scopedStyleRegistrationState == NotRegistered). |
#if !ENABLE(OILPAN) |
StyleElement::clearDocumentData(document(), this); |
#endif |
@@ -71,6 +74,8 @@ |
{ |
if (name == titleAttr && m_sheet) { |
m_sheet->setTitle(value); |
+ } else if (name == scopedAttr && ContextFeatures::styleScopedEnabled(&document())) { |
+ scopedAttributeChanged(!value.isNull()); |
} else if (name == mediaAttr && inDocument() && document().isActive() && m_sheet) { |
m_sheet->setMediaQueries(MediaQuerySet::create(value)); |
document().modifiedStyleSheet(m_sheet.get()); |
@@ -79,18 +84,101 @@ |
} |
} |
+void HTMLStyleElement::scopedAttributeChanged(bool scoped) |
+{ |
+ ASSERT(ContextFeatures::styleScopedEnabled(&document())); |
+ |
+ if (!inDocument()) |
+ return; |
+ |
+ if (scoped) { |
+ if (m_scopedStyleRegistrationState == RegisteredAsScoped) |
+ return; |
+ |
+ // As any <style> in a shadow tree is treated as "scoped", |
+ // need to remove the <style> from its shadow root. |
+ ContainerNode* scopingNode = 0; |
+ if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) { |
+ scopingNode = containingShadowRoot(); |
+ unregisterWithScopingNode(scopingNode); |
+ } |
+ document().styleEngine()->removeStyleSheetCandidateNode(this, scopingNode, treeScope()); |
+ registerWithScopingNode(true); |
+ |
+ document().styleEngine()->addStyleSheetCandidateNode(this, false); |
+ document().modifiedStyleSheet(sheet()); |
+ return; |
+ } |
+ |
+ // If the <style> was scoped, need to remove the <style> from the scoping |
+ // element, i.e. the parent node. |
+ if (m_scopedStyleRegistrationState != RegisteredAsScoped) |
+ return; |
+ |
+ unregisterWithScopingNode(parentNode()); |
+ document().styleEngine()->removeStyleSheetCandidateNode(this, parentNode(), treeScope()); |
+ |
+ // As any <style> in a shadow tree is treated as "scoped", |
+ // need to add the <style> to its shadow root. |
+ if (isInShadowTree()) |
+ registerWithScopingNode(false); |
+ |
+ document().styleEngine()->addStyleSheetCandidateNode(this, false); |
+ // FIXME: currently need to use FullStyleUpdate here. |
+ // Because ShadowTreeStyleSheetCollection doesn't know old scoping node. |
+ // So setNeedsStyleRecalc for old scoping node is not invoked. |
+ document().modifiedStyleSheet(sheet()); |
+} |
+ |
void HTMLStyleElement::finishParsingChildren() |
{ |
StyleElement::finishParsingChildren(this); |
HTMLElement::finishParsingChildren(); |
} |
+void HTMLStyleElement::registerWithScopingNode(bool scoped) |
+{ |
+ // Note: We cannot rely on the 'scoped' element already being present when this method is invoked. |
+ // Therefore we cannot rely on scoped()! |
+ ASSERT(m_scopedStyleRegistrationState == NotRegistered); |
+ ASSERT(inDocument()); |
+ if (m_scopedStyleRegistrationState != NotRegistered) |
+ return; |
+ |
+ ContainerNode* scope = scoped ? parentNode() : containingShadowRoot(); |
+ if (!scope) |
+ return; |
+ if (!scope->isElementNode() && !scope->isShadowRoot()) { |
+ // DocumentFragment nodes should never be inDocument, |
+ // <style> should not be a child of Document, PI or some such. |
+ ASSERT_NOT_REACHED(); |
+ return; |
+ } |
+ scope->registerScopedHTMLStyleChild(); |
+ m_scopedStyleRegistrationState = scoped ? RegisteredAsScoped : RegisteredInShadowRoot; |
+} |
+ |
+void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope) |
+{ |
+ ASSERT(m_scopedStyleRegistrationState != NotRegistered || !ContextFeatures::styleScopedEnabled(&document())); |
+ if (!isRegisteredAsScoped()) |
+ return; |
+ |
+ ASSERT(scope); |
+ if (scope) { |
+ ASSERT(scope->hasScopedHTMLStyleChild()); |
+ scope->unregisterScopedHTMLStyleChild(); |
+ } |
+ |
+ m_scopedStyleRegistrationState = NotRegistered; |
+} |
+ |
Node::InsertionNotificationRequest HTMLStyleElement::insertedInto(ContainerNode* insertionPoint) |
{ |
HTMLElement::insertedInto(insertionPoint); |
- if (insertionPoint->inDocument() && isInShadowTree()) { |
- if (ShadowRoot* scope = containingShadowRoot()) |
- scope->registerScopedHTMLStyleChild(); |
+ if (insertionPoint->inDocument()) { |
+ if (m_scopedStyleRegistrationState == NotRegistered && (scoped() || isInShadowTree())) |
+ registerWithScopingNode(scoped()); |
} |
return InsertionShouldCallDidNotifySubtreeInsertions; |
} |
@@ -99,18 +187,27 @@ |
{ |
HTMLElement::removedFrom(insertionPoint); |
- ShadowRoot* scopingNode = containingShadowRoot(); |
- if (!scopingNode) |
- scopingNode = insertionPoint->containingShadowRoot(); |
- |
- if (scopingNode) |
- scopingNode->unregisterScopedHTMLStyleChild(); |
- |
- if (!insertionPoint->inDocument()) |
- return; |
- |
- TreeScope* containingScope = containingShadowRoot(); |
- StyleElement::removedFromDocument(document(), this, scopingNode, containingScope ? *containingScope : insertionPoint->treeScope()); |
+ // In the current implementation, <style scoped> is only registered if the node is in the document. |
+ // That is, because willRemove() is also called if an ancestor is removed from the document. |
+ // Now, if we want to register <style scoped> even if it's not inDocument, |
+ // we'd need to find a way to discern whether that is the case, or whether <style scoped> itself is about to be removed. |
+ ContainerNode* scopingNode = 0; |
+ if (m_scopedStyleRegistrationState != NotRegistered) { |
+ if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) { |
+ scopingNode = containingShadowRoot(); |
+ if (!scopingNode) |
+ scopingNode = insertionPoint->containingShadowRoot(); |
+ } else { |
+ scopingNode = parentNode() ? parentNode() : insertionPoint; |
+ } |
+ |
+ unregisterWithScopingNode(scopingNode); |
+ } |
+ |
+ if (insertionPoint->inDocument()) { |
+ TreeScope* containingScope = containingShadowRoot(); |
+ StyleElement::removedFromDocument(document(), this, scopingNode, containingScope ? *containingScope : insertionPoint->treeScope()); |
+ } |
} |
void HTMLStyleElement::didNotifySubtreeInsertionsToDocument() |
@@ -134,15 +231,28 @@ |
return getAttribute(typeAttr); |
} |
+bool HTMLStyleElement::scoped() const |
+{ |
+ return fastHasAttribute(scopedAttr) && ContextFeatures::styleScopedEnabled(&document()); |
+} |
+ |
+void HTMLStyleElement::setScoped(bool scopedValue) |
+{ |
+ setBooleanAttribute(scopedAttr, scopedValue); |
+} |
+ |
ContainerNode* HTMLStyleElement::scopingNode() |
{ |
if (!inDocument()) |
return 0; |
- if (isInShadowTree()) |
+ if (!isRegisteredAsScoped()) |
+ return &document(); |
+ |
+ if (isRegisteredInShadowRoot()) |
return containingShadowRoot(); |
- return &document(); |
+ return parentNode(); |
} |
void HTMLStyleElement::dispatchPendingLoadEvents() |