Add mechanism for mapping from StyleRules back to fully constructed CSSStyleRules
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 Apr 2012 11:50:00 +0000 (11:50 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 Apr 2012 11:50:00 +0000 (11:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=82847

Reviewed by Andreas Kling.

Inspector is using CSSStyleSelector to calculate the CSS rules matched by a given element and
expects to be able to walk the parent chain. After 82728 the stylesheet object tree won't have
parent pointers and we are going to need another mechanism to support this.

The new code does not actually run without 82728.

* css/CSSStyleSelector.cpp:
(WebCore):
(WebCore::CSSStyleSelector::appendAuthorStylesheets):
(WebCore::loadFullDefaultStyle):
(WebCore::ensureDefaultStyleSheetsForElement):
(WebCore::CSSStyleSelector::collectMatchingRulesForList):
* css/CSSStyleSelector.h:
(CSSStyleSelector):

Add ensureFullCSSOMWrapperForStyleRule() method which traverses through all style
sheets that apply to the document and constucts wrappers for the rules. These wrappers
are cached to a map. The map can then be used for StyleRule -> CSSStyleRule lookups.

This uses quite a bit of memory so should not be used for any normal engine functions.

* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::getMatchedStylesForNode):
(WebCore::InspectorCSSAgent::buildArrayForRuleList):

Use the new mechanism to get fully functional wrappers for rule objects without parent pointer.

* inspector/InspectorCSSAgent.h:
(InspectorCSSAgent):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@112858 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/css/CSSStyleSelector.cpp
Source/WebCore/css/CSSStyleSelector.h
Source/WebCore/inspector/InspectorCSSAgent.cpp
Source/WebCore/inspector/InspectorCSSAgent.h

index 272d76c..5d786cf 100644 (file)
@@ -1,3 +1,40 @@
+2012-04-01  Antti Koivisto  <antti@apple.com>
+
+        Add mechanism for mapping from StyleRules back to fully constructed CSSStyleRules 
+        https://bugs.webkit.org/show_bug.cgi?id=82847
+
+        Reviewed by Andreas Kling.
+
+        Inspector is using CSSStyleSelector to calculate the CSS rules matched by a given element and
+        expects to be able to walk the parent chain. After 82728 the stylesheet object tree won't have
+        parent pointers and we are going to need another mechanism to support this.
+        
+        The new code does not actually run without 82728.
+        
+        * css/CSSStyleSelector.cpp:
+        (WebCore):
+        (WebCore::CSSStyleSelector::appendAuthorStylesheets):
+        (WebCore::loadFullDefaultStyle):
+        (WebCore::ensureDefaultStyleSheetsForElement):
+        (WebCore::CSSStyleSelector::collectMatchingRulesForList):
+        * css/CSSStyleSelector.h:
+        (CSSStyleSelector):
+    
+        Add ensureFullCSSOMWrapperForStyleRule() method which traverses through all style
+        sheets that apply to the document and constucts wrappers for the rules. These wrappers
+        are cached to a map. The map can then be used for StyleRule -> CSSStyleRule lookups.
+        
+        This uses quite a bit of memory so should not be used for any normal engine functions.
+    
+        * inspector/InspectorCSSAgent.cpp:
+        (WebCore::InspectorCSSAgent::getMatchedStylesForNode):
+        (WebCore::InspectorCSSAgent::buildArrayForRuleList):
+    
+        Use the new mechanism to get fully functional wrappers for rule objects without parent pointer.
+    
+        * inspector/InspectorCSSAgent.h:
+        (InspectorCSSAgent):
+
 2012-04-02  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Unreviewed. Fix make distcheck issues.
index 139bc59..8fc6392 100644 (file)
@@ -286,11 +286,19 @@ static RuleSet* defaultQuirksStyle;
 static RuleSet* defaultPrintStyle;
 static RuleSet* defaultViewSourceStyle;
 static CSSStyleSheet* simpleDefaultStyleSheet;
+static CSSStyleSheet* defaultStyleSheet;
+static CSSStyleSheet* quirksStyleSheet;
+static CSSStyleSheet* svgStyleSheet;
+static CSSStyleSheet* mathMLStyleSheet;
+static CSSStyleSheet* mediaControlsStyleSheet;
+static CSSStyleSheet* fullscreenStyleSheet;
 
 RenderStyle* CSSStyleSelector::s_styleNotYetAvailable;
 
 static void loadFullDefaultStyle();
 static void loadSimpleDefaultStyle();
+static void collectCSSOMWrappers(HashMap<StyleRule*, RefPtr<CSSStyleRule> >&, CSSStyleSheet*);
+
 // FIXME: It would be nice to use some mechanism that guarantees this is in sync with the real UA stylesheet.
 static const char* simpleUserAgentStyleSheet = "html,body,div{display:block}head{display:none}body{margin:8px}div:focus,span:focus{outline:auto 5px -webkit-focus-ring-color}a:-webkit-any-link{color:-webkit-link;text-decoration:underline}a:-webkit-any-link:active{color:-webkit-activelink}";
 
@@ -508,6 +516,8 @@ void CSSStyleSelector::appendAuthorStylesheets(unsigned firstNew, const Vector<R
         }
 #endif
         m_authorStyle->addRulesFromSheet(cssSheet, *m_medium, this);
+        if (!m_styleRuleToCSSOMWrapperMap.isEmpty())
+            collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, cssSheet);
     }
     m_authorStyle->shrinkToFit();
     collectFeatures();
@@ -702,14 +712,14 @@ static void loadFullDefaultStyle()
 
     // Strict-mode rules.
     String defaultRules = String(htmlUserAgentStyleSheet, sizeof(htmlUserAgentStyleSheet)) + RenderTheme::defaultTheme()->extraDefaultStyleSheet();
-    CSSStyleSheet* defaultSheet = parseUASheet(defaultRules);
-    defaultStyle->addRulesFromSheet(defaultSheet, screenEval());
-    defaultPrintStyle->addRulesFromSheet(defaultSheet, printEval());
+    defaultStyleSheet = parseUASheet(defaultRules);
+    defaultStyle->addRulesFromSheet(defaultStyleSheet, screenEval());
+    defaultPrintStyle->addRulesFromSheet(defaultStyleSheet, printEval());
 
     // Quirks-mode rules.
     String quirksRules = String(quirksUserAgentStyleSheet, sizeof(quirksUserAgentStyleSheet)) + RenderTheme::defaultTheme()->extraQuirksStyleSheet();
-    CSSStyleSheet* quirksSheet = parseUASheet(quirksRules);
-    defaultQuirksStyle->addRulesFromSheet(quirksSheet, screenEval());
+    quirksStyleSheet = parseUASheet(quirksRules);
+    defaultQuirksStyle->addRulesFromSheet(quirksStyleSheet, screenEval());
 }
 
 static void loadSimpleDefaultStyle()
@@ -741,51 +751,43 @@ static void ensureDefaultStyleSheetsForElement(Element* element)
         loadFullDefaultStyle();
 
 #if ENABLE(SVG)
-    static bool loadedSVGUserAgentSheet;
-    if (element->isSVGElement() && !loadedSVGUserAgentSheet) {
+    if (element->isSVGElement() && !svgStyleSheet) {
         // SVG rules.
-        loadedSVGUserAgentSheet = true;
-        CSSStyleSheet* svgSheet = parseUASheet(svgUserAgentStyleSheet, sizeof(svgUserAgentStyleSheet));
-        defaultStyle->addRulesFromSheet(svgSheet, screenEval());
-        defaultPrintStyle->addRulesFromSheet(svgSheet, printEval());
+        svgStyleSheet = parseUASheet(svgUserAgentStyleSheet, sizeof(svgUserAgentStyleSheet));
+        defaultStyle->addRulesFromSheet(svgStyleSheet, screenEval());
+        defaultPrintStyle->addRulesFromSheet(svgStyleSheet, printEval());
     }
 #endif
 
-    static bool loadedMathMLUserAgentSheet;
 #if ENABLE(MATHML)
-    if (element->isMathMLElement() && !loadedMathMLUserAgentSheet) {
+    if (element->isMathMLElement() && !mathMLStyleSheet) {
         // MathML rules.
-        loadedMathMLUserAgentSheet = true;
-        CSSStyleSheet* mathMLSheet = parseUASheet(mathmlUserAgentStyleSheet, sizeof(mathmlUserAgentStyleSheet));
-        defaultStyle->addRulesFromSheet(mathMLSheet, screenEval());
-        defaultPrintStyle->addRulesFromSheet(mathMLSheet, printEval());
+        mathMLStyleSheet = parseUASheet(mathmlUserAgentStyleSheet, sizeof(mathmlUserAgentStyleSheet));
+        defaultStyle->addRulesFromSheet(mathMLStyleSheet, screenEval());
+        defaultPrintStyle->addRulesFromSheet(mathMLStyleSheet, printEval());
     }
 #endif
 
 #if ENABLE(VIDEO)
-    static bool loadedMediaStyleSheet;
-    if (!loadedMediaStyleSheet && (element->hasTagName(videoTag) || element->hasTagName(audioTag))) {
-        loadedMediaStyleSheet = true;
+    if (!mediaControlsStyleSheet && (element->hasTagName(videoTag) || element->hasTagName(audioTag))) {
         String mediaRules = String(mediaControlsUserAgentStyleSheet, sizeof(mediaControlsUserAgentStyleSheet)) + RenderTheme::themeForPage(element->document()->page())->extraMediaControlsStyleSheet();
-        CSSStyleSheet* mediaControlsSheet = parseUASheet(mediaRules);
-        defaultStyle->addRulesFromSheet(mediaControlsSheet, screenEval());
-        defaultPrintStyle->addRulesFromSheet(mediaControlsSheet, printEval());
+        mediaControlsStyleSheet = parseUASheet(mediaRules);
+        defaultStyle->addRulesFromSheet(mediaControlsStyleSheet, screenEval());
+        defaultPrintStyle->addRulesFromSheet(mediaControlsStyleSheet, printEval());
     }
 #endif
 
 #if ENABLE(FULLSCREEN_API)
-    static bool loadedFullScreenStyleSheet;
-    if (!loadedFullScreenStyleSheet && element->document()->webkitIsFullScreen()) {
-        loadedFullScreenStyleSheet = true;
+    if (!fullscreenStyleSheet && element->document()->webkitIsFullScreen()) {
         String fullscreenRules = String(fullscreenUserAgentStyleSheet, sizeof(fullscreenUserAgentStyleSheet)) + RenderTheme::defaultTheme()->extraFullScreenStyleSheet();
-        CSSStyleSheet* fullscreenSheet = parseUASheet(fullscreenRules);
-        defaultStyle->addRulesFromSheet(fullscreenSheet, screenEval());
-        defaultQuirksStyle->addRulesFromSheet(fullscreenSheet, screenEval());
+        fullscreenStyleSheet = parseUASheet(fullscreenRules);
+        defaultStyle->addRulesFromSheet(fullscreenStyleSheet, screenEval());
+        defaultQuirksStyle->addRulesFromSheet(fullscreenStyleSheet, screenEval());
     }
 #endif
 
     ASSERT(defaultStyle->features().idsInRules.isEmpty());
-    ASSERT_UNUSED(loadedMathMLUserAgentSheet, loadedMathMLUserAgentSheet || defaultStyle->features().siblingRules.isEmpty());
+    ASSERT(mathMLStyleSheet || defaultStyle->features().siblingRules.isEmpty());
 }
 
 void CSSStyleSelector::addMatchedProperties(MatchResult& matchResult, StylePropertySet* properties, StyleRule* rule, unsigned linkMatchType, bool inRegionRule)
@@ -2874,6 +2876,59 @@ String CSSStyleSelector::pageName(int /* pageIndex */) const
     return "";
 }
 
+static void collectCSSOMWrappers(HashMap<StyleRule*, RefPtr<CSSStyleRule> >& wrapperMap, CSSRuleList* ruleList)
+{
+    unsigned size = ruleList->length();
+    for (unsigned i = 0; i < size; ++i) {
+        CSSRule* cssRule = ruleList->item(i);
+        if (cssRule->isImportRule())
+            collectCSSOMWrappers(wrapperMap, static_cast<CSSImportRule*>(cssRule)->styleSheet());
+        else if (cssRule->isMediaRule())
+            collectCSSOMWrappers(wrapperMap, static_cast<CSSMediaRule*>(cssRule)->cssRules());
+        else if (cssRule->isRegionRule())
+            collectCSSOMWrappers(wrapperMap, static_cast<WebKitCSSRegionRule*>(cssRule)->cssRules());
+        else if (cssRule->isStyleRule()) {
+            CSSStyleRule* cssStyleRule = static_cast<CSSStyleRule*>(cssRule);
+            wrapperMap.add(cssStyleRule->styleRule(), cssStyleRule);
+        }
+    }
+}
+
+static void collectCSSOMWrappers(HashMap<StyleRule*, RefPtr<CSSStyleRule> >& wrapperMap, CSSStyleSheet* styleSheet)
+{
+    if (!styleSheet)
+        return;
+    collectCSSOMWrappers(wrapperMap, styleSheet->cssRules().get());
+}
+
+static void collectCSSOMWrappers(HashMap<StyleRule*, RefPtr<CSSStyleRule> >& wrapperMap, Document* document)
+{
+    const Vector<RefPtr<StyleSheet> >& styleSheets = document->styleSheets()->vector();
+    for (unsigned i = 0; i < styleSheets.size(); ++i) {
+        StyleSheet* styleSheet = styleSheets[i].get();
+        if (!styleSheet->isCSSStyleSheet())
+            continue;
+        collectCSSOMWrappers(wrapperMap, static_cast<CSSStyleSheet*>(styleSheet));
+    }
+    collectCSSOMWrappers(wrapperMap, document->pageUserSheet());
+}
+
+CSSStyleRule* CSSStyleSelector::ensureFullCSSOMWrapperForInspector(StyleRule* rule)
+{
+    if (m_styleRuleToCSSOMWrapperMap.isEmpty()) {
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, simpleDefaultStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, defaultStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, quirksStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, svgStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, mathMLStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, mediaControlsStyleSheet);
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, fullscreenStyleSheet);
+
+        collectCSSOMWrappers(m_styleRuleToCSSOMWrapperMap, document());
+    }
+    return m_styleRuleToCSSOMWrapperMap.get(rule).get();
+}
+
 void CSSStyleSelector::applyPropertyToStyle(int id, CSSValue* value, RenderStyle* style)
 {
     initElement(0);
index 5818367..b78c57f 100644 (file)
@@ -50,6 +50,7 @@ class CSSImageSetValue;
 class CSSImageValue;
 class CSSSelector;
 class CSSStyleApplyProperty;
+class CSSStyleRule;
 class CSSStyleSheet;
 class CSSValue;
 class ContainerNode;
@@ -240,6 +241,10 @@ public:
     static bool createTransformOperations(CSSValue* inValue, RenderStyle* inStyle, RenderStyle* rootStyle, TransformOperations& outOperations);
     
     void invalidateMatchedPropertiesCache();
+    
+    // WARNING. This will construct CSSOM wrappers for all style rules and cache then in a map for significant memory cost.
+    // It is here to support inspector. Don't use for any regular engine functions.
+    CSSStyleRule* ensureFullCSSOMWrapperForInspector(StyleRule*);
 
 #if ENABLE(CSS_FILTERS)
     bool createFilterOperations(CSSValue* inValue, RenderStyle* inStyle, RenderStyle* rootStyle, FilterOperations& outOperations);
@@ -486,6 +491,8 @@ private:
     bool m_applyPropertyToRegularStyle;
     bool m_applyPropertyToVisitedLinkStyle;
     const CSSStyleApplyProperty& m_applyProperty;
+    
+    HashMap<StyleRule*, RefPtr<CSSStyleRule> > m_styleRuleToCSSOMWrapperMap;
 
 #if ENABLE(CSS_SHADERS)
     bool m_hasPendingShaders;
index 064b862..1610cd7 100644 (file)
@@ -574,7 +574,7 @@ void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int no
     // Matched rules.
     CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
     RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, CSSStyleSelector::AllCSSRules);
-    matchedCSSRules = buildArrayForRuleList(matchedRules.get());
+    matchedCSSRules = buildArrayForRuleList(matchedRules.get(), selector);
 
     // Pseudo elements.
     if (!includePseudo || *includePseudo) {
@@ -584,7 +584,7 @@ void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int no
             if (matchedRules && matchedRules->length()) {
                 RefPtr<TypeBuilder::CSS::PseudoIdRules> pseudoStyles = TypeBuilder::CSS::PseudoIdRules::create()
                     .setPseudoId(static_cast<int>(pseudoId))
-                    .setRules(buildArrayForRuleList(matchedRules.get()));
+                    .setRules(buildArrayForRuleList(matchedRules.get(), selector));
                 pseudoElements->addItem(pseudoStyles.release());
             }
         }
@@ -600,7 +600,7 @@ void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int no
             CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector();
             RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, CSSStyleSelector::AllCSSRules);
             RefPtr<TypeBuilder::CSS::InheritedStyleEntry> parentStyle = TypeBuilder::CSS::InheritedStyleEntry::create()
-                .setMatchedCSSRules(buildArrayForRuleList(parentMatchedRules.get()));
+                .setMatchedCSSRules(buildArrayForRuleList(parentMatchedRules.get(), selector));
             if (parentElement->style() && parentElement->style()->length()) {
                 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
                 if (styleSheet)
@@ -948,7 +948,7 @@ String InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document*
     return origin;
 }
 
-PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList)
+PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList, CSSStyleSelector* styleSelector)
 {
     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSRule>::create();
     if (!ruleList)
@@ -959,9 +959,17 @@ PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > InspectorCSSAgent::bu
         if (!rule)
             continue;
 
-        InspectorStyleSheet* styleSheet = bindStyleSheet(rule->parentStyleSheet());
-        if (styleSheet)
-            result->addItem(styleSheet->buildObjectForRule(rule));
+        // CSSRules returned by CSSStyleSelector::styleRulesForElement lack parent pointers since that infomation is not cheaply available.
+        // Since the inspector wants to walk the parent chain, we construct the full wrappers here.
+        // FIXME: This could be factored better. CSSStyleSelector::styleRulesForElement should return a StyleRule vector, not a CSSRuleList.
+        if (!rule->parentStyleSheet()) {
+            rule = styleSelector->ensureFullCSSOMWrapperForInspector(rule->styleRule());
+            if (!rule)
+                continue;
+        }
+        InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(rule->parentStyleSheet());
+        if (inspectorStyleSheet)
+            result->addItem(inspectorStyleSheet->buildObjectForRule(rule));
     }
     return result.release();
 }
index df9b21d..227b9a8 100644 (file)
@@ -145,7 +145,7 @@ private:
     InspectorStyleSheet* assertStyleSheetForId(ErrorString*, const String&);
     String detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument);
 
-    PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > buildArrayForRuleList(CSSRuleList*);
+    PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > buildArrayForRuleList(CSSRuleList*, CSSStyleSelector*);
     PassRefPtr<TypeBuilder::CSS::CSSStyle> buildObjectForAttributesStyle(Element*);
 
     // InspectorDOMAgent::DOMListener implementation