Commit e1127317 authored by rune@opera.com's avatar rune@opera.com

Fix crash when removing stylesheets from shadow tree.

The added sheets passed into StyleSheetInvalidationAnalysis may either be
added or removed. When they are removed style elements from a shadow tree,
the style element may no longer have a containingShadowRoot if the element
has already been removed. That caused a crash when the containingShadowRoot
was accessed without a nullptr check.

Instead of deducing the scope from the style element, pass the stylesheet
collection TreeScope to the analysis object. The m_scopingNodes, which is
probably a left-over from <style scoped> has been removed.

Now, the shadow tree root node, or outermost shadow host if ::content
rules makes that necessary, is always marked sub-tree dirty. The previous
code-path could trigger id/class invalidation of separate elements when
the stylesheet contents had multiple owner nodes, but that was most likely
wrong if it could ever happen.

BUG=522860

Review URL: https://codereview.chromium.org/1313443003

git-svn-id: svn://svn.chromium.org/blink/trunk@201058 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 10584b73
Insert a style element into a shadow tree affecting a distributed node.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS getComputedStyle(outerSpan).color is "rgb(255, 0, 0)"
PASS internals.updateStyleAndReturnAffectedElementCount() is 9
PASS getComputedStyle(outerSpan).color is "rgb(0, 128, 0)"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<script src="../../../resources/js-test.js"></script>
<style>
#outerHost { color: red }
</style>
<div>
<div id="outerHost">
<span id="outerSpan"></span>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<script>
description("Insert a style element into a shadow tree affecting a distributed node.");
var outerRoot = outerHost.createShadowRoot();
outerRoot.innerHTML = "<div id='host1'><content/></div>";
var host1 = outerRoot.querySelector("#host1");
var root1 = host1.createShadowRoot();
root1.innerHTML = "<div id='host2'><content/></div>";
var host2 = root1.querySelector("#host2");
var root2 = host2.createShadowRoot();
root2.innerHTML = "<content/>";
shouldBeEqualToString("getComputedStyle(outerSpan).color", "rgb(255, 0, 0)");
var sheet = document.createElement("style");
sheet.appendChild(document.createTextNode("::content #outerSpan { color: green }"));
root2.appendChild(sheet);
if (window.internals)
shouldBe("internals.updateStyleAndReturnAffectedElementCount()", "9");
shouldBeEqualToString("getComputedStyle(outerSpan).color", "rgb(0, 128, 0)");
</script>
<!DOCTYPE html>
<style></style>
<script>
if (window.testRunner)
testRunner.dumpAsText();
onload = function() {
var link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", "file:notfound.css");
document.head.appendChild(link);
var root = host.createShadowRoot();
root.innerHTML = "<span><style></style><style></style>";
root.innerHTML = "";
}
</script>
<p>PASS if no crash</p>
<div id="host"></div>
......@@ -32,13 +32,14 @@
#include "core/dom/ContainerNode.h"
#include "core/dom/Document.h"
#include "core/dom/ElementTraversal.h"
#include "core/dom/TreeScope.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/html/HTMLStyleElement.h"
namespace blink {
StyleSheetInvalidationAnalysis::StyleSheetInvalidationAnalysis(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>& sheets)
: m_dirtiesAllStyle(false)
StyleSheetInvalidationAnalysis::StyleSheetInvalidationAnalysis(const TreeScope& treeScope, const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>& sheets)
: m_treeScope(treeScope)
{
for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i)
analyzeStyleSheet(sheets[i]);
......@@ -92,21 +93,6 @@ static bool hasDistributedRule(StyleSheetContents* styleSheetContents)
return false;
}
static Node* determineScopingNodeForStyleInShadow(HTMLStyleElement* ownerElement, StyleSheetContents* styleSheetContents)
{
ASSERT(ownerElement && ownerElement->isInShadowTree());
if (hasDistributedRule(styleSheetContents)) {
ContainerNode* scope = ownerElement;
do {
scope = scope->containingShadowRoot()->shadowHost();
} while (scope->isInShadowTree());
return scope;
}
return ownerElement->containingShadowRoot()->shadowHost();
}
static bool ruleAdditionMightRequireDocumentStyleRecalc(StyleRuleBase* rule)
{
// This funciton is conservative. We only return false when we know that
......@@ -152,12 +138,11 @@ void StyleSheetInvalidationAnalysis::analyzeStyleSheet(StyleSheetContents* style
if (m_dirtiesAllStyle)
return;
}
if (styleSheetContents->hasSingleOwnerNode()) {
Node* ownerNode = styleSheetContents->singleOwnerNode();
if (isHTMLStyleElement(ownerNode) && toHTMLStyleElement(*ownerNode).isInShadowTree()) {
m_scopingNodes.append(determineScopingNodeForStyleInShadow(toHTMLStyleElement(ownerNode), styleSheetContents));
return;
}
if (m_treeScope.rootNode().isShadowRoot()) {
if (hasDistributedRule(styleSheetContents))
m_hasDistributedRules = true;
return;
}
const WillBeHeapVector<RefPtrWillBeMember<StyleRuleBase>>& rules = styleSheetContents->childRules();
......@@ -192,18 +177,29 @@ static bool elementMatchesSelectorScopes(const Element* element, const HashSet<S
return false;
}
void StyleSheetInvalidationAnalysis::invalidateStyle(Document& document)
static ContainerNode* outermostShadowHost(const ShadowRoot& root)
{
ContainerNode* host = root.host();
while (host->isInShadowTree())
host = host->containingShadowRoot()->host();
return host;
}
void StyleSheetInvalidationAnalysis::invalidateStyle()
{
ASSERT(!m_dirtiesAllStyle);
if (!m_scopingNodes.isEmpty()) {
for (unsigned i = 0; i < m_scopingNodes.size(); ++i)
m_scopingNodes.at(i)->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleSheetChange));
if (m_treeScope.rootNode().isShadowRoot()) {
ContainerNode* invalidationRoot = &m_treeScope.rootNode();
if (m_hasDistributedRules)
invalidationRoot = outermostShadowHost(*toShadowRoot(invalidationRoot));
invalidationRoot->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleSheetChange));
return;
}
if (m_idScopes.isEmpty() && m_classScopes.isEmpty())
return;
Element* element = ElementTraversal::firstWithin(document);
Element* element = ElementTraversal::firstWithin(m_treeScope.document());
while (element) {
if (elementMatchesSelectorScopes(element, m_idScopes, m_classScopes)) {
element->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleSheetChange));
......
......@@ -36,23 +36,26 @@ namespace blink {
class Document;
class Node;
class StyleSheetContents;
class TreeScope;
class StyleSheetInvalidationAnalysis {
STACK_ALLOCATED();
public:
StyleSheetInvalidationAnalysis(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>&);
StyleSheetInvalidationAnalysis(const TreeScope&, const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>&);
bool dirtiesAllStyle() const { return m_dirtiesAllStyle; }
void invalidateStyle(Document&);
void invalidateStyle();
private:
void analyzeStyleSheet(StyleSheetContents*);
bool m_dirtiesAllStyle;
const TreeScope& m_treeScope;
HashSet<StringImpl*> m_idScopes;
HashSet<StringImpl*> m_classScopes;
WillBeHeapVector<RawPtrWillBeMember<Node>, 8> m_scopingNodes;
bool m_dirtiesAllStyle = false;
bool m_hasDistributedRules = false;
};
}
......
......@@ -158,10 +158,10 @@ void TreeScopeStyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateM
// If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
if (!document().body() || document().hasNodesWithPlaceholderStyle())
return;
StyleSheetInvalidationAnalysis invalidationAnalysis(addedSheets);
StyleSheetInvalidationAnalysis invalidationAnalysis(*m_treeScope, addedSheets);
if (invalidationAnalysis.dirtiesAllStyle())
return;
invalidationAnalysis.invalidateStyle(document());
invalidationAnalysis.invalidateStyle();
change.requiresFullStyleRecalc = false;
return;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment