Diff
Modified: branches/safari-537-branch/LayoutTests/ChangeLog (152511 => 152512)
--- branches/safari-537-branch/LayoutTests/ChangeLog 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/LayoutTests/ChangeLog 2013-07-09 19:01:11 UTC (rev 152512)
@@ -1,5 +1,22 @@
2013-07-09 Lucas Forschler <[email protected]>
+ Merge r152453
+
+ 2013-07-08 Andreas Kling <[email protected]>
+
+ REGRESSION(r125294): A style rule with more than 8192 selectors can cause style corruption.
+ <http://webkit.org/b/118369>
+ <rdar://problem/14291428>
+
+ Reviewed by Antti Koivisto.
+
+ Added a test to document the new cap of 8192 selectors per CSS rule.
+
+ * fast/css/rule-selector-overflow-expected.txt: Added.
+ * fast/css/rule-selector-overflow.html: Added.
+
+2013-07-09 Lucas Forschler <[email protected]>
+
Merge r152424
2013-07-05 Zalan Bujtas <[email protected]>
Copied: branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow-expected.txt (from rev 152453, trunk/LayoutTests/fast/css/rule-selector-overflow-expected.txt) (0 => 152512)
--- branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow-expected.txt (rev 0)
+++ branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow-expected.txt 2013-07-09 19:01:11 UTC (rev 152512)
@@ -0,0 +1,27 @@
+This test tests and documents the behavior of CSS style rules with a massive number of selectors. Rules with >8192 selectors get split into multiple rules at the parsing stage. Setting a rule's selectorText via CSSOM will do nothing if there are more than 8192 selectors.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS rule().selectorText = selectorListWithLength(1); rule().selectorText is selectorListWithLength(1)
+PASS rule().selectorText = selectorListWithLength(8192); rule().selectorText is selectorListWithLength(8192)
+PASS rule().selectorText = '.reset'; rule().selectorText is '.reset'
+PASS rule().selectorText = selectorListWithLength(8193); rule().selectorText is '.reset'
+PASS rule().selectorText = '.reset'; rule().selectorText is '.reset'
+PASS rule().selectorText = selectorListWithLength(8193); sheet().rules.length is 1
+PASS rule().selectorText = selectorListWithLength(8192); rule().selectorText is selectorListWithLength(8192)
+PASS rule().selectorText = selectorListWithLength(8192); sheet().rules.length is 1
+PASS rule().selectorText = '.reset'; rule().selectorText is '.reset'
+PASS rule().selectorText = selectorListWithLength(8193); rule().selectorText is '.reset'
+PASS rule().selectorText = selectorListWithLength(8193); sheet().rules.length is 1
+PASS styleElement.innerText = styleSheetWithSelectorLength(1); rule().selectorText is selectorListWithLength(1)
+PASS styleElement.innerText = styleSheetWithSelectorLength(8192); rule().selectorText is selectorListWithLength(8192)
+PASS styleElement.innerText = styleSheetWithSelectorLength(8192); sheet().rules.length is 1
+PASS styleElement.innerText = styleSheetWithSelectorLength(8193); rule().selectorText is selectorListWithLength(8192)
+PASS styleElement.innerText = styleSheetWithSelectorLength(8193); sheet().rules.length is 2
+PASS styleElement.innerText = styleSheetWithSelectorLength(16384); sheet().rules.length is 2
+PASS styleElement.innerText = styleSheetWithSelectorLength(16385); sheet().rules.length is 3
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Copied: branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow.html (from rev 152453, trunk/LayoutTests/fast/css/rule-selector-overflow.html) (0 => 152512)
--- branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow.html (rev 0)
+++ branches/safari-537-branch/LayoutTests/fast/css/rule-selector-overflow.html 2013-07-09 19:01:11 UTC (rev 152512)
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src=""
+<style id="stylebro" type="text/css">
+.peb {
+ color: red;
+}
+</style>
+</head>
+<body>
+<script>
+
+styleElement = document.getElementById("stylebro");
+
+function selectorListWithLength(length)
+{
+ var a = new Array();
+ for (i = 0; i < length; ++i)
+ a.push(".x");
+ return a.join(", ");
+}
+
+function styleSheetWithSelectorLength(length)
+{
+ return selectorListWithLength(length) + " { color: red; }";
+}
+
+function sheet()
+{
+ return styleElement.sheet;
+}
+
+function rule()
+{
+ return sheet().cssRules[0];
+}
+
+description("This test tests and documents the behavior of CSS style rules with a massive number of selectors. Rules with >8192 selectors get split into multiple rules at the parsing stage. Setting a rule's selectorText via CSSOM will do nothing if there are more than 8192 selectors.");
+
+shouldBe("rule().selectorText = selectorListWithLength(1); rule().selectorText", "selectorListWithLength(1)");
+shouldBe("rule().selectorText = selectorListWithLength(8192); rule().selectorText", "selectorListWithLength(8192)");
+shouldBe("rule().selectorText = '.reset'; rule().selectorText", "'.reset'");
+shouldBe("rule().selectorText = selectorListWithLength(8193); rule().selectorText", "'.reset'");
+shouldBe("rule().selectorText = '.reset'; rule().selectorText", "'.reset'");
+shouldBe("rule().selectorText = selectorListWithLength(8193); sheet().rules.length", "1");
+shouldBe("rule().selectorText = selectorListWithLength(8192); rule().selectorText", "selectorListWithLength(8192)");
+shouldBe("rule().selectorText = selectorListWithLength(8192); sheet().rules.length", "1");
+shouldBe("rule().selectorText = '.reset'; rule().selectorText", "'.reset'");
+shouldBe("rule().selectorText = selectorListWithLength(8193); rule().selectorText", "'.reset'");
+shouldBe("rule().selectorText = selectorListWithLength(8193); sheet().rules.length", "1");
+
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(1); rule().selectorText", "selectorListWithLength(1)");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(8192); rule().selectorText", "selectorListWithLength(8192)");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(8192); sheet().rules.length", "1");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(8193); rule().selectorText", "selectorListWithLength(8192)");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(8193); sheet().rules.length", "2");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(16384); sheet().rules.length", "2");
+shouldBe("styleElement.innerText = styleSheetWithSelectorLength(16385); sheet().rules.length", "3");
+
+</script>
+<script src=""
+</body>
+</html>
Modified: branches/safari-537-branch/Source/WebCore/ChangeLog (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/ChangeLog 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/ChangeLog 2013-07-09 19:01:11 UTC (rev 152512)
@@ -1,5 +1,55 @@
2013-07-09 Lucas Forschler <[email protected]>
+ Merge r152453
+
+ 2013-07-08 Andreas Kling <[email protected]>
+
+ REGRESSION(r125294): A style rule with more than 8192 selectors can cause style corruption.
+ <http://webkit.org/b/118369>
+ <rdar://problem/14291428>
+
+ Reviewed by Antti Koivisto.
+
+ Add a semi-hard cap of 8192 selectors per CSS rule. Rules with longer selector lists will be split into
+ multiple rules (with 8192 selectors per rule.) The style properties are shared behind the scenes
+ so it's all pretty efficient.
+
+ This does generate a different CSSOM for gigantic selector lists, I've added a test to document this.
+ Note that setting CSSStyleRule.selectorText with over 8192 selectors will now fail, the splitting into
+ multiple rules only happens when parsing full style sheets.
+
+ Test: fast/css/rule-selector-overflow.html
+
+ * css/CSSSelectorList.cpp:
+ (WebCore::CSSSelectorList::selectorCount):
+
+ Add a helper to count the number of selectors in a list (not the number of raw selector components.)
+
+ * css/RuleSet.h:
+ * css/CSSStyleRule.cpp:
+ (WebCore::CSSStyleRule::setSelectorText):
+
+ Add RuleData::maximumSelectorCount constant and cap CSSStyleRule.selectorText to that many selectors.
+
+ * css/CSSSelectorList.h:
+ (WebCore::CSSSelectorList::adoptSelectorArray):
+ * css/StyleRule.cpp:
+ (WebCore::StyleRule::create):
+ (WebCore::StyleRule::splitIntoMultipleRulesWithMaximumSelectorCount):
+ * css/StyleRule.h:
+ (WebCore::StyleRule::parserAdoptSelectorArray):
+ * css/StyleSheetContents.cpp:
+ (WebCore::StyleSheetContents::parserAppendRule):
+
+ The meat of this change. Split rules with >8K selectors into multiple rules with 8K selectors in each.
+
+ * css/StyleRule.h:
+ (WebCore::toStyleRule):
+
+ Added a toStyleRule() helper for good measure.
+
+2013-07-09 Lucas Forschler <[email protected]>
+
Merge r152434
2013-07-05 Tim Horton <[email protected]>
Modified: branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.cpp (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.cpp 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.cpp 2013-07-09 19:01:11 UTC (rev 152512)
@@ -85,6 +85,14 @@
selectorVector.clear();
}
+unsigned CSSSelectorList::selectorCount() const
+{
+ unsigned count = 0;
+ for (const CSSSelector* s = first(); s; s = next(s))
+ ++count;
+ return count;
+}
+
unsigned CSSSelectorList::length() const
{
if (!m_selectorArray)
Modified: branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.h (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.h 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/CSSSelectorList.h 2013-07-09 19:01:11 UTC (rev 152512)
@@ -42,6 +42,7 @@
void adopt(CSSSelectorList& list);
void adoptSelectorVector(Vector<OwnPtr<CSSParserSelector> >& selectorVector);
+ void adoptSelectorArray(CSSSelector* selectors) { ASSERT(!m_selectorArray); m_selectorArray = selectors; }
bool isValid() const { return !!m_selectorArray; }
const CSSSelector* first() const { return m_selectorArray; }
@@ -63,6 +64,8 @@
String selectorsText() const;
+ unsigned selectorCount() const;
+
private:
unsigned length() const;
void deleteSelectors();
Modified: branches/safari-537-branch/Source/WebCore/css/CSSStyleRule.cpp (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/CSSStyleRule.cpp 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/CSSStyleRule.cpp 2013-07-09 19:01:11 UTC (rev 152512)
@@ -27,6 +27,7 @@
#include "CSSStyleSheet.h"
#include "Document.h"
#include "PropertySetCSSStyleDeclaration.h"
+#include "RuleSet.h"
#include "StylePropertySet.h"
#include "StyleRule.h"
#include <wtf/text/StringBuilder.h>
@@ -98,6 +99,10 @@
if (!selectorList.isValid())
return;
+ // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369>
+ if (selectorList.selectorCount() > RuleData::maximumSelectorCount)
+ return;
+
CSSStyleSheet::RuleMutationScope mutationScope(this);
m_styleRule->wrapperAdoptSelectorList(selectorList);
Modified: branches/safari-537-branch/Source/WebCore/css/RuleSet.h (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/RuleSet.h 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/RuleSet.h 2013-07-09 19:01:11 UTC (rev 152512)
@@ -55,6 +55,8 @@
class RuleData {
public:
+ static const unsigned maximumSelectorCount = 8192;
+
RuleData(StyleRule*, unsigned selectorIndex, unsigned position, AddRuleFlags);
unsigned position() const { return m_position; }
Modified: branches/safari-537-branch/Source/WebCore/css/StyleRule.cpp (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/StyleRule.cpp 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/StyleRule.cpp 2013-07-09 19:01:11 UTC (rev 152512)
@@ -260,6 +260,44 @@
m_properties = properties;
}
+PassRefPtr<StyleRule> StyleRule::create(int sourceLine, const Vector<const CSSSelector*>& selectors, PassRefPtr<StylePropertySet> properties)
+{
+ CSSSelector* selectorListArray = reinterpret_cast<CSSSelector*>(fastMalloc(sizeof(CSSSelector) * selectors.size()));
+ for (unsigned i = 0; i < selectors.size(); ++i)
+ new (NotNull, &selectorListArray[i]) CSSSelector(*selectors.at(i));
+ selectorListArray[selectors.size() - 1].setLastInSelectorList();
+ RefPtr<StyleRule> rule = StyleRule::create(sourceLine);
+ rule->parserAdoptSelectorArray(selectorListArray);
+ rule->setProperties(properties);
+ return rule.release();
+}
+
+Vector<RefPtr<StyleRule> > StyleRule::splitIntoMultipleRulesWithMaximumSelectorCount(unsigned maximumSelectorCount) const
+{
+ ASSERT(selectorList().selectorCount() > maximumSelectorCount);
+
+ Vector<RefPtr<StyleRule> > rules;
+ Vector<const CSSSelector*> selectorsToCopy;
+
+ unsigned selectorCount = 0;
+
+ for (const CSSSelector* selector = selectorList().first(); selector; selector = CSSSelectorList::next(selector)) {
+ for (const CSSSelector* component = selector; component; component = component->tagHistory())
+ selectorsToCopy.append(component);
+
+ if (++selectorCount == maximumSelectorCount) {
+ rules.append(create(sourceLine(), selectorsToCopy, m_properties));
+ selectorsToCopy.clear();
+ selectorCount = 0;
+ }
+ }
+
+ if (selectorCount)
+ rules.append(create(sourceLine(), selectorsToCopy, m_properties));
+
+ return rules;
+}
+
StyleRulePage::StyleRulePage()
: StyleRuleBase(Page)
{
Modified: branches/safari-537-branch/Source/WebCore/css/StyleRule.h (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/StyleRule.h 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/StyleRule.h 2013-07-09 19:01:11 UTC (rev 152512)
@@ -127,20 +127,31 @@
void parserAdoptSelectorVector(Vector<OwnPtr<CSSParserSelector> >& selectors) { m_selectorList.adoptSelectorVector(selectors); }
void wrapperAdoptSelectorList(CSSSelectorList& selectors) { m_selectorList.adopt(selectors); }
+ void parserAdoptSelectorArray(CSSSelector* selectors) { m_selectorList.adoptSelectorArray(selectors); }
void setProperties(PassRefPtr<StylePropertySet>);
PassRefPtr<StyleRule> copy() const { return adoptRef(new StyleRule(*this)); }
+ Vector<RefPtr<StyleRule> > splitIntoMultipleRulesWithMaximumSelectorCount(unsigned maxSelectorCount) const;
+
static unsigned averageSizeInBytes();
private:
StyleRule(int sourceLine);
StyleRule(const StyleRule&);
+ static PassRefPtr<StyleRule> create(int sourceLine, const Vector<const CSSSelector*>&, PassRefPtr<StylePropertySet>);
+
RefPtr<StylePropertySet> m_properties;
CSSSelectorList m_selectorList;
};
+inline const StyleRule* toStyleRule(const StyleRuleBase* rule)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!rule || rule->isStyleRule());
+ return static_cast<const StyleRule*>(rule);
+}
+
class StyleRuleFontFace : public StyleRuleBase {
public:
static PassRefPtr<StyleRuleFontFace> create() { return adoptRef(new StyleRuleFontFace); }
Modified: branches/safari-537-branch/Source/WebCore/css/StyleSheetContents.cpp (152511 => 152512)
--- branches/safari-537-branch/Source/WebCore/css/StyleSheetContents.cpp 2013-07-09 18:54:08 UTC (rev 152511)
+++ branches/safari-537-branch/Source/WebCore/css/StyleSheetContents.cpp 2013-07-09 19:01:11 UTC (rev 152512)
@@ -1,6 +1,6 @@
/*
* (C) 1999-2003 Lars Knoll ([email protected])
- * Copyright (C) 2004, 2006, 2007, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2007, 2012, 2013 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -28,6 +28,7 @@
#include "Document.h"
#include "MediaList.h"
#include "Node.h"
+#include "RuleSet.h"
#include "SecurityOrigin.h"
#include "StylePropertySet.h"
#include "StyleRule.h"
@@ -140,6 +141,14 @@
reportMediaQueryWarningIfNeeded(singleOwnerDocument(), static_cast<StyleRuleMedia*>(rule.get())->mediaQueries());
#endif
+ // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369>
+ // If we're adding a rule with a huge number of selectors, split it up into multiple rules
+ if (rule->isStyleRule() && toStyleRule(rule.get())->selectorList().selectorCount() > RuleData::maximumSelectorCount) {
+ Vector<RefPtr<StyleRule> > rules = toStyleRule(rule.get())->splitIntoMultipleRulesWithMaximumSelectorCount(RuleData::maximumSelectorCount);
+ m_childRules.appendVector(rules);
+ return;
+ }
+
m_childRules.append(rule);
}