Web Inspector: introduce "source" column in the CSS profiler.
https://bugs.webkit.org/show_bug.cgi?id=75378

Reviewed by Pavel Feldman.

Rules are no longer merged by their selectors but are shown one per profile entry, with a link to
their selector in the source whenever the latter is available.

* inspector/Inspector.json:
* inspector/InspectorCSSAgent.cpp:
(WebCore::RuleMatchingStats::RuleMatchingStats):
(WebCore::SelectorProfile::makeKey):
(WebCore::SelectorProfile::startSelector):
(WebCore::SelectorProfile::commitSelector):
(WebCore::SelectorProfile::commitSelectorTime):
(WebCore::SelectorProfile::toInspectorObject):
(WebCore::InspectorCSSAgent::willMatchRule):
(WebCore::InspectorCSSAgent::willProcessRule):
(WebCore::InspectorCSSAgent::buildArrayForRuleList):
* inspector/InspectorStyleSheet.cpp:
(WebCore::InspectorStyleSheet::styleSheetURL):
(WebCore::InspectorStyleSheet::finalURL):
(WebCore::InspectorStyleSheet::setRuleSelector):
* inspector/InspectorStyleSheet.h:
* inspector/front-end/CSSSelectorProfileView.js:
(WebInspector.CSSSelectorDataGridNode.prototype.get rawData):
(WebInspector.CSSSelectorDataGridNode.prototype.createCell):
(WebInspector.CSSSelectorProfileView):
(WebInspector.CSSSelectorProfileView.prototype._createProfileNodes):
(WebInspector.CSSSelectorProfileView.prototype._sortProfile.sourceComparator):
(WebInspector.CSSSelectorProfileView.prototype._sortProfile):
* inspector/front-end/ProfilesPanel.js:
(WebInspector.ProfilesPanel.prototype.sidebarResized):
* inspector/front-end/dataGrid.css:
(.data-grid:focus tr.selected a):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@104710 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/inspector/InspectorCSSAgent.cpp b/Source/WebCore/inspector/InspectorCSSAgent.cpp
index 8754a1b..59601b8 100644
--- a/Source/WebCore/inspector/InspectorCSSAgent.cpp
+++ b/Source/WebCore/inspector/InspectorCSSAgent.cpp
@@ -51,6 +51,7 @@
 #include <wtf/HashSet.h>
 #include <wtf/Vector.h>
 #include <wtf/text/CString.h>
+#include <wtf/text/StringConcatenate.h>
 
 namespace CSSAgentState {
 static const char cssAgentEnabled[] = "cssAgentEnabled";
@@ -67,15 +68,26 @@
     PseudoVisited = 1 << 3
 };
 
+struct RuleMatchData {
+    String selector;
+    String url;
+    unsigned lineNumber;
+    double startTime;
+};
+
 struct RuleMatchingStats {
     RuleMatchingStats()
-        : totalTime(0.0), hits(0), matches(0)
+        : lineNumber(0), totalTime(0.0), hits(0), matches(0)
     {
     }
-    RuleMatchingStats(double totalTime, unsigned hits, unsigned matches)
-        : totalTime(totalTime), hits(hits), matches(matches)
+    RuleMatchingStats(const RuleMatchData& data, double totalTime, unsigned hits, unsigned matches)
+        : selector(data.selector), url(data.url), lineNumber(data.lineNumber), totalTime(totalTime), hits(hits), matches(matches)
     {
     }
+
+    String selector;
+    String url;
+    unsigned lineNumber;
     double totalTime;
     unsigned hits;
     unsigned matches;
@@ -83,7 +95,6 @@
 
 class SelectorProfile {
 public:
-    // FIXME: handle different rules with the same selector differently?
     SelectorProfile()
         : m_totalMatchingTimeMs(0.0)
     {
@@ -94,17 +105,16 @@
 
     double totalMatchingTimeMs() const { return m_totalMatchingTimeMs; }
 
-    void startSelector(const String&);
+    String makeKey();
+    void startSelector(const CSSStyleRule*);
     void commitSelector(bool);
     void commitSelectorTime();
     PassRefPtr<InspectorObject> toInspectorObject() const;
 
 private:
+
+    // Key is "selector?url:line".
     typedef HashMap<String, RuleMatchingStats> RuleMatchingStatsMap;
-    struct RuleMatchData {
-        String selector;
-        double startTime;
-    };
 
     double m_totalMatchingTimeMs;
     RuleMatchingStatsMap m_ruleMatchingStats;
@@ -141,9 +151,23 @@
     return result;
 }
 
-inline void SelectorProfile::startSelector(const String& selectorText)
+inline String SelectorProfile::makeKey()
 {
-    m_currentMatchData.selector = selectorText;
+    return makeString(m_currentMatchData.selector, "?", m_currentMatchData.url, ":", String::number(m_currentMatchData.lineNumber));
+}
+
+inline void SelectorProfile::startSelector(const CSSStyleRule* rule)
+{
+    m_currentMatchData.selector = rule->selectorText();
+    CSSStyleSheet* styleSheet = rule->parentStyleSheet();
+    String url = emptyString();
+    if (styleSheet) {
+        url = InspectorStyleSheet::styleSheetURL(styleSheet);
+        if (url.isEmpty())
+            url = InspectorDOMAgent::documentURLString(styleSheet->findDocument());
+    }
+    m_currentMatchData.url = url;
+    m_currentMatchData.lineNumber = rule->sourceLine();
     m_currentMatchData.startTime = WTF::currentTimeMS();
 }
 
@@ -151,7 +175,8 @@
 {
     double matchTimeMs = WTF::currentTimeMS() - m_currentMatchData.startTime;
     m_totalMatchingTimeMs += matchTimeMs;
-    pair<RuleMatchingStatsMap::iterator, bool> result = m_ruleMatchingStats.add(m_currentMatchData.selector, RuleMatchingStats(matchTimeMs, 1, matched ? 1 : 0));
+
+    pair<RuleMatchingStatsMap::iterator, bool> result = m_ruleMatchingStats.add(makeKey(), RuleMatchingStats(m_currentMatchData, matchTimeMs, 1, matched ? 1 : 0));
     if (!result.second) {
         result.first->second.totalTime += matchTimeMs;
         result.first->second.hits += 1;
@@ -164,41 +189,35 @@
 {
     double processingTimeMs = WTF::currentTimeMS() - m_currentMatchData.startTime;
     m_totalMatchingTimeMs += processingTimeMs;
-    RuleMatchingStatsMap::iterator it = m_ruleMatchingStats.find(m_currentMatchData.selector);
+
+    RuleMatchingStatsMap::iterator it = m_ruleMatchingStats.find(makeKey());
     if (it == m_ruleMatchingStats.end())
         return;
 
-    it->second.totalTime += WTF::currentTimeMS() - m_currentMatchData.startTime;
+    it->second.totalTime += processingTimeMs;
 }
 
 PassRefPtr<InspectorObject> SelectorProfile::toInspectorObject() const
 {
-    RefPtr<InspectorArray> data = InspectorArray::create();
+    RefPtr<InspectorArray> selectorProfileData = InspectorArray::create();
     for (RuleMatchingStatsMap::const_iterator it = m_ruleMatchingStats.begin(); it != m_ruleMatchingStats.end(); ++it) {
-        RefPtr<TypeBuilder::CSS::SelectorProfileEntry> stat = TypeBuilder::CSS::SelectorProfileEntry::create()
-            .setSelector(it->first)
+        RefPtr<TypeBuilder::CSS::SelectorProfileEntry> entry = TypeBuilder::CSS::SelectorProfileEntry::create()
+            .setSelector(it->second.selector)
+            .setUrl(it->second.url)
+            .setLineNumber(it->second.lineNumber)
             .setTime(it->second.totalTime)
             .setHitCount(it->second.hits)
             .setMatchCount(it->second.matches);
-        data->pushObject(stat.release());
+        selectorProfileData->pushObject(entry.release());
     }
 
     RefPtr<TypeBuilder::CSS::SelectorProfile> result = TypeBuilder::CSS::SelectorProfile::create()
         .setTotalTime(totalMatchingTimeMs())
-        .setData(data);
+        .setData(selectorProfileData);
     return result.release();
 }
 
 // static
-CSSStyleSheet* InspectorCSSAgent::parentStyleSheet(CSSRule* rule)
-{
-    if (!rule)
-        return 0;
-
-    return rule->parentStyleSheet();
-}
-
-// static
 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
 {
     if (!rule->isStyleRule())
@@ -527,7 +546,7 @@
 
 void InspectorCSSAgent::willMatchRule(const CSSStyleRule* rule)
 {
-    m_currentSelectorProfile->startSelector(rule->selectorText());
+    m_currentSelectorProfile->startSelector(rule);
 }
 
 void InspectorCSSAgent::didMatchRule(bool matched)
@@ -537,7 +556,7 @@
 
 void InspectorCSSAgent::willProcessRule(const CSSStyleRule* rule)
 {
-    m_currentSelectorProfile->startSelector(rule->selectorText());
+    m_currentSelectorProfile->startSelector(rule);
 }
 
 void InspectorCSSAgent::didProcessRule()
@@ -685,7 +704,7 @@
         if (!rule)
             continue;
 
-        InspectorStyleSheet* styleSheet = bindStyleSheet(parentStyleSheet(rule));
+        InspectorStyleSheet* styleSheet = bindStyleSheet(rule->parentStyleSheet());
         if (styleSheet)
             result->pushObject(styleSheet->buildObjectForRule(rule));
     }