| 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()
|
|
|