05d22bd06c55088427dc24280736aac98cb1a884
[WebKit-https.git] / Source / WebCore / inspector / InspectorCSSAgent.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "InspectorCSSAgent.h"
27
28 #if ENABLE(INSPECTOR)
29
30 #include "CSSComputedStyleDeclaration.h"
31 #include "CSSImportRule.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSPropertySourceData.h"
34 #include "CSSRule.h"
35 #include "CSSRuleList.h"
36 #include "CSSStyleRule.h"
37 #include "CSSStyleSelector.h"
38 #include "CSSStyleSheet.h"
39 #include "DOMWindow.h"
40 #include "HTMLHeadElement.h"
41 #include "InspectorDOMAgent.h"
42 #include "InspectorHistory.h"
43 #include "InspectorState.h"
44 #include "InspectorValues.h"
45 #include "InstrumentingAgents.h"
46 #include "Node.h"
47 #include "NodeList.h"
48 #include "StylePropertySet.h"
49 #include "StyleSheetList.h"
50
51 #include <wtf/CurrentTime.h>
52 #include <wtf/HashSet.h>
53 #include <wtf/Vector.h>
54 #include <wtf/text/CString.h>
55 #include <wtf/text/StringConcatenate.h>
56
57 namespace CSSAgentState {
58 static const char cssAgentEnabled[] = "cssAgentEnabled";
59 static const char isSelectorProfiling[] = "isSelectorProfiling";
60 }
61
62 namespace WebCore {
63
64 enum ForcePseudoClassFlags {
65     PseudoNone = 0,
66     PseudoHover = 1 << 0,
67     PseudoFocus = 1 << 1,
68     PseudoActive = 1 << 2,
69     PseudoVisited = 1 << 3
70 };
71
72 struct RuleMatchData {
73     String selector;
74     String url;
75     unsigned lineNumber;
76     double startTime;
77 };
78
79 struct RuleMatchingStats {
80     RuleMatchingStats()
81         : lineNumber(0), totalTime(0.0), hits(0), matches(0)
82     {
83     }
84     RuleMatchingStats(const RuleMatchData& data, double totalTime, unsigned hits, unsigned matches)
85         : selector(data.selector), url(data.url), lineNumber(data.lineNumber), totalTime(totalTime), hits(hits), matches(matches)
86     {
87     }
88
89     String selector;
90     String url;
91     unsigned lineNumber;
92     double totalTime;
93     unsigned hits;
94     unsigned matches;
95 };
96
97 class SelectorProfile {
98 public:
99     SelectorProfile()
100         : m_totalMatchingTimeMs(0.0)
101     {
102     }
103     virtual ~SelectorProfile()
104     {
105     }
106
107     double totalMatchingTimeMs() const { return m_totalMatchingTimeMs; }
108
109     String makeKey();
110     void startSelector(const CSSStyleRule*);
111     void commitSelector(bool);
112     void commitSelectorTime();
113     PassRefPtr<InspectorObject> toInspectorObject() const;
114
115 private:
116
117     // Key is "selector?url:line".
118     typedef HashMap<String, RuleMatchingStats> RuleMatchingStatsMap;
119
120     double m_totalMatchingTimeMs;
121     RuleMatchingStatsMap m_ruleMatchingStats;
122     RuleMatchData m_currentMatchData;
123 };
124
125
126 static unsigned computePseudoClassMask(InspectorArray* pseudoClassArray)
127 {
128     DEFINE_STATIC_LOCAL(String, active, ("active"));
129     DEFINE_STATIC_LOCAL(String, hover, ("hover"));
130     DEFINE_STATIC_LOCAL(String, focus, ("focus"));
131     DEFINE_STATIC_LOCAL(String, visited, ("visited"));
132     if (!pseudoClassArray || !pseudoClassArray->length())
133         return PseudoNone;
134
135     unsigned result = PseudoNone;
136     for (size_t i = 0; i < pseudoClassArray->length(); ++i) {
137         RefPtr<InspectorValue> pseudoClassValue = pseudoClassArray->get(i);
138         String pseudoClass;
139         bool success = pseudoClassValue->asString(&pseudoClass);
140         if (!success)
141             continue;
142         if (pseudoClass == active)
143             result |= PseudoActive;
144         else if (pseudoClass == hover)
145             result |= PseudoHover;
146         else if (pseudoClass == focus)
147             result |= PseudoFocus;
148         else if (pseudoClass == visited)
149             result |= PseudoVisited;
150     }
151
152     return result;
153 }
154
155 inline String SelectorProfile::makeKey()
156 {
157     return makeString(m_currentMatchData.selector, "?", m_currentMatchData.url, ":", String::number(m_currentMatchData.lineNumber));
158 }
159
160 inline void SelectorProfile::startSelector(const CSSStyleRule* rule)
161 {
162     m_currentMatchData.selector = rule->selectorText();
163     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
164     String url = emptyString();
165     if (styleSheet) {
166         url = InspectorStyleSheet::styleSheetURL(styleSheet);
167         if (url.isEmpty())
168             url = InspectorDOMAgent::documentURLString(styleSheet->findDocument());
169     }
170     m_currentMatchData.url = url;
171     m_currentMatchData.lineNumber = rule->sourceLine();
172     m_currentMatchData.startTime = WTF::currentTimeMS();
173 }
174
175 inline void SelectorProfile::commitSelector(bool matched)
176 {
177     double matchTimeMs = WTF::currentTimeMS() - m_currentMatchData.startTime;
178     m_totalMatchingTimeMs += matchTimeMs;
179
180     pair<RuleMatchingStatsMap::iterator, bool> result = m_ruleMatchingStats.add(makeKey(), RuleMatchingStats(m_currentMatchData, matchTimeMs, 1, matched ? 1 : 0));
181     if (!result.second) {
182         result.first->second.totalTime += matchTimeMs;
183         result.first->second.hits += 1;
184         if (matched)
185             result.first->second.matches += 1;
186     }
187 }
188
189 inline void SelectorProfile::commitSelectorTime()
190 {
191     double processingTimeMs = WTF::currentTimeMS() - m_currentMatchData.startTime;
192     m_totalMatchingTimeMs += processingTimeMs;
193
194     RuleMatchingStatsMap::iterator it = m_ruleMatchingStats.find(makeKey());
195     if (it == m_ruleMatchingStats.end())
196         return;
197
198     it->second.totalTime += processingTimeMs;
199 }
200
201 PassRefPtr<InspectorObject> SelectorProfile::toInspectorObject() const
202 {
203     RefPtr<InspectorArray> selectorProfileData = InspectorArray::create();
204     for (RuleMatchingStatsMap::const_iterator it = m_ruleMatchingStats.begin(); it != m_ruleMatchingStats.end(); ++it) {
205         RefPtr<TypeBuilder::CSS::SelectorProfileEntry> entry = TypeBuilder::CSS::SelectorProfileEntry::create()
206             .setSelector(it->second.selector)
207             .setUrl(it->second.url)
208             .setLineNumber(it->second.lineNumber)
209             .setTime(it->second.totalTime)
210             .setHitCount(it->second.hits)
211             .setMatchCount(it->second.matches);
212         selectorProfileData->pushObject(entry.release());
213     }
214
215     RefPtr<TypeBuilder::CSS::SelectorProfile> result = TypeBuilder::CSS::SelectorProfile::create()
216         .setTotalTime(totalMatchingTimeMs())
217         .setData(selectorProfileData);
218     return result.release();
219 }
220
221 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
222     WTF_MAKE_NONCOPYABLE(StyleSheetAction);
223 public:
224     StyleSheetAction(const String& name, InspectorCSSAgent* cssAgent, const String& styleSheetId)
225         : InspectorHistory::Action(name)
226         , m_cssAgent(cssAgent)
227         , m_styleSheetId(styleSheetId)
228     {
229     }
230
231     virtual bool perform(ErrorString* errorString)
232     {
233         InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(errorString, m_styleSheetId);
234         if (!styleSheet)
235             return false;
236         return perform(styleSheet, errorString);
237     }
238
239     virtual bool undo(ErrorString* errorString)
240     {
241         InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(errorString, m_styleSheetId);
242         if (!styleSheet)
243             return false;
244         return undo(styleSheet, errorString);
245     }
246
247     virtual bool perform(InspectorStyleSheet*, ErrorString*) = 0;
248
249     virtual bool undo(InspectorStyleSheet*, ErrorString*) = 0;
250
251 protected:
252     InspectorCSSAgent* m_cssAgent;
253     String m_styleSheetId;
254 };
255
256 class InspectorCSSAgent::SetStyleSheetTextAction : public InspectorCSSAgent::StyleSheetAction {
257     WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
258 public:
259     SetStyleSheetTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const String& text)
260         : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText", cssAgent, styleSheetId)
261         , m_text(text)
262     {
263     }
264
265     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString*)
266     {
267         if (!inspectorStyleSheet->getText(&m_oldText))
268             return false;
269
270         if (inspectorStyleSheet->setText(m_text)) {
271             inspectorStyleSheet->reparseStyleSheet(m_text);
272             return true;
273         }
274         return false;
275     }
276
277     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString*)
278     {
279         if (inspectorStyleSheet->setText(m_oldText)) {
280             inspectorStyleSheet->reparseStyleSheet(m_oldText);
281             return true;
282         }
283         return false;
284     }
285
286 private:
287     String m_text;
288     String m_oldText;
289 };
290
291 class InspectorCSSAgent::SetPropertyTextAction : public InspectorCSSAgent::StyleSheetAction {
292     WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
293 public:
294     SetPropertyTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
295         : InspectorCSSAgent::StyleSheetAction("SetPropertyText", cssAgent, styleSheetId)
296         , m_cssId(cssId)
297         , m_propertyIndex(propertyIndex)
298         , m_text(text)
299         , m_overwrite(overwrite)
300     {
301     }
302
303     virtual String toString()
304     {
305         return mergeId() + ": " + m_oldText + " -> " + m_text;
306     }
307
308     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
309     {
310         String oldText;
311         bool result = inspectorStyleSheet->setPropertyText(errorString, m_cssId, m_propertyIndex, m_text, m_overwrite, &oldText);
312         m_oldText = oldText.stripWhiteSpace();
313         // FIXME: remove this once the model handles this case.
314         if (!m_oldText.endsWith(";"))
315             m_oldText += ";";
316         return result;
317     }
318
319     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
320     {
321         String placeholder;
322         return inspectorStyleSheet->setPropertyText(errorString, m_cssId, m_propertyIndex, m_overwrite ? m_oldText : "", true, &placeholder);
323     }
324
325     virtual String mergeId()
326     {
327         return String::format("SetPropertyText %s:%u:%s", m_styleSheetId.utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
328     }
329
330     virtual void merge(PassOwnPtr<Action> action)
331     {
332         ASSERT(action->mergeId() == mergeId());
333
334         SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
335         m_text = other->m_text;
336     }
337
338 private:
339     InspectorCSSId m_cssId;
340     unsigned m_propertyIndex;
341     String m_text;
342     String m_oldText;
343     bool m_overwrite;
344 };
345
346 class InspectorCSSAgent::TogglePropertyAction : public InspectorCSSAgent::StyleSheetAction {
347     WTF_MAKE_NONCOPYABLE(TogglePropertyAction);
348 public:
349     TogglePropertyAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, bool disable)
350         : InspectorCSSAgent::StyleSheetAction("ToggleProperty", cssAgent, styleSheetId)
351         , m_cssId(cssId)
352         , m_propertyIndex(propertyIndex)
353         , m_disable(disable)
354     {
355     }
356
357     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
358     {
359         return inspectorStyleSheet->toggleProperty(errorString, m_cssId, m_propertyIndex, m_disable);
360     }
361
362     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
363     {
364       return inspectorStyleSheet->toggleProperty(errorString, m_cssId, m_propertyIndex, !m_disable);
365     }
366
367 private:
368     InspectorCSSId m_cssId;
369     unsigned m_propertyIndex;
370     bool m_disable;
371 };
372
373 // static
374 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
375 {
376     if (!rule->isStyleRule())
377         return 0;
378     return static_cast<CSSStyleRule*>(rule);
379 }
380
381 InspectorCSSAgent::InspectorCSSAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InspectorDOMAgent* domAgent)
382     : InspectorBaseAgent<InspectorCSSAgent>("CSS", instrumentingAgents, state)
383     , m_frontend(0)
384     , m_domAgent(domAgent)
385     , m_lastPseudoState(0)
386     , m_lastStyleSheetId(1)
387     , m_lastRuleId(1)
388     , m_lastStyleId(1)
389 {
390     m_domAgent->setDOMListener(this);
391     m_instrumentingAgents->setInspectorCSSAgent(this);
392 }
393
394 InspectorCSSAgent::~InspectorCSSAgent()
395 {
396     ASSERT(!m_domAgent);
397     m_instrumentingAgents->setInspectorCSSAgent(0);
398     reset();
399 }
400
401 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
402 {
403     ASSERT(!m_frontend);
404     m_frontend = frontend->css();
405 }
406
407 void InspectorCSSAgent::clearFrontend()
408 {
409     ASSERT(m_frontend);
410     m_frontend = 0;
411     clearPseudoState(true);
412     String errorString;
413     stopSelectorProfilerImpl(&errorString);
414 }
415
416 void InspectorCSSAgent::discardAgent()
417 {
418     m_domAgent->setDOMListener(0);
419     m_domAgent = 0;
420 }
421
422 void InspectorCSSAgent::restore()
423 {
424     if (m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
425         ErrorString error;
426         enable(&error);
427     }
428     if (m_state->getBoolean(CSSAgentState::isSelectorProfiling)) {
429         String errorString;
430         startSelectorProfiler(&errorString);
431     }
432 }
433
434 void InspectorCSSAgent::reset()
435 {
436     m_idToInspectorStyleSheet.clear();
437     m_cssStyleSheetToInspectorStyleSheet.clear();
438     m_nodeToInspectorStyleSheet.clear();
439     m_documentToInspectorStyleSheet.clear();
440 }
441
442 void InspectorCSSAgent::enable(ErrorString*)
443 {
444     m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
445 }
446
447 void InspectorCSSAgent::disable(ErrorString*)
448 {
449     m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
450 }
451
452 void InspectorCSSAgent::mediaQueryResultChanged()
453 {
454     if (m_frontend)
455         m_frontend->mediaQueryResultChanged();
456 }
457
458 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
459 {
460     if (m_lastElementWithPseudoState != element)
461         return false;
462
463     switch (pseudoType) {
464     case CSSSelector::PseudoActive:
465         return m_lastPseudoState & PseudoActive;
466     case CSSSelector::PseudoFocus:
467         return m_lastPseudoState & PseudoFocus;
468     case CSSSelector::PseudoHover:
469         return m_lastPseudoState & PseudoHover;
470     case CSSSelector::PseudoVisited:
471         return m_lastPseudoState & PseudoVisited;
472     default:
473         return false;
474     }
475 }
476
477 void InspectorCSSAgent::recalcStyleForPseudoStateIfNeeded(Element* element, InspectorArray* forcedPseudoClasses)
478 {
479     unsigned forcePseudoState = computePseudoClassMask(forcedPseudoClasses);
480     bool needStyleRecalc = element != m_lastElementWithPseudoState || forcePseudoState != m_lastPseudoState;
481     m_lastPseudoState = forcePseudoState;
482     m_lastElementWithPseudoState = element;
483     if (needStyleRecalc)
484         element->ownerDocument()->styleSelectorChanged(RecalcStyleImmediately);
485 }
486
487 void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int nodeId, const RefPtr<InspectorArray>* forcedPseudoClasses, const bool* needPseudo, const bool* needInherited, RefPtr<InspectorArray>& matchedCSSRules, RefPtr<InspectorArray>& pseudoIdRules, RefPtr<InspectorArray>& inheritedEntries)
488 {
489     Element* element = elementForId(errorString, nodeId);
490     if (!element)
491         return;
492
493     recalcStyleForPseudoStateIfNeeded(element, forcedPseudoClasses ? forcedPseudoClasses->get() : 0);
494
495     // Matched rules.
496     CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
497     RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, CSSStyleSelector::AllCSSRules);
498     matchedCSSRules = buildArrayForRuleList(matchedRules.get());
499
500     // Pseudo elements.
501     if (!needPseudo || *needPseudo) {
502         RefPtr<InspectorArray> pseudoElements = InspectorArray::create();
503         for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
504             RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, CSSStyleSelector::AllCSSRules);
505             if (matchedRules && matchedRules->length()) {
506                 RefPtr<InspectorObject> pseudoStyles = InspectorObject::create();
507                 pseudoStyles->setNumber("pseudoId", static_cast<int>(pseudoId));
508                 pseudoStyles->setArray("rules", buildArrayForRuleList(matchedRules.get()));
509                 pseudoElements->pushObject(pseudoStyles.release());
510             }
511         }
512
513         pseudoIdRules = pseudoElements.release();
514     }
515
516     // Inherited styles.
517     if (!needInherited || *needInherited) {
518         RefPtr<InspectorArray> inheritedStyles = InspectorArray::create();
519         Element* parentElement = element->parentElement();
520         while (parentElement) {
521             RefPtr<InspectorObject> parentStyle = InspectorObject::create();
522             if (parentElement->style() && parentElement->style()->length()) {
523                 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
524                 if (styleSheet)
525                     parentStyle->setObject("inlineStyle", styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
526             }
527
528             CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector();
529             RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, CSSStyleSelector::AllCSSRules);
530             parentStyle->setArray("matchedCSSRules", buildArrayForRuleList(parentMatchedRules.get()));
531             inheritedStyles->pushObject(parentStyle.release());
532             parentElement = parentElement->parentElement();
533         }
534
535         inheritedEntries = inheritedStyles.release();
536     }
537 }
538
539 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<InspectorObject>& inlineStyle, RefPtr<InspectorObject>& attributesStyle)
540 {
541     Element* element = elementForId(errorString, nodeId);
542     if (!element)
543         return;
544
545     InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
546     if (!styleSheet)
547         return;
548
549     inlineStyle = styleSheet->buildObjectForStyle(element->style());
550     RefPtr<InspectorObject> attributes = buildObjectForAttributesStyle(element);
551     attributesStyle = attributes ? attributes.release() : 0;
552 }
553
554 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, const RefPtr<InspectorArray>* forcedPseudoClasses, RefPtr<InspectorArray>& style)
555 {
556     Element* element = elementForId(errorString, nodeId);
557     if (!element)
558         return;
559
560     recalcStyleForPseudoStateIfNeeded(element, forcedPseudoClasses ? forcedPseudoClasses->get() : 0);
561
562     RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(element, true);
563     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
564     style = inspectorStyle->buildArrayForComputedStyle();
565 }
566
567 void InspectorCSSAgent::getAllStyleSheets(ErrorString*, RefPtr<InspectorArray>& styleInfos)
568 {
569     Vector<Document*> documents = m_domAgent->documents();
570     for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it) {
571         StyleSheetList* list = (*it)->styleSheets();
572         for (unsigned i = 0; i < list->length(); ++i) {
573             StyleSheet* styleSheet = list->item(i);
574             if (styleSheet->isCSSStyleSheet())
575                 collectStyleSheets(static_cast<CSSStyleSheet*>(styleSheet), styleInfos.get());
576         }
577     }
578 }
579
580 void InspectorCSSAgent::getStyleSheet(ErrorString* errorString, const String& styleSheetId, RefPtr<InspectorObject>& styleSheetObject)
581 {
582     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
583     if (!inspectorStyleSheet)
584         return;
585
586     styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet();
587 }
588
589 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
590 {
591     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
592     if (!inspectorStyleSheet)
593         return;
594
595     inspectorStyleSheet->getText(result);
596 }
597
598 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
599 {
600     m_domAgent->history()->perform(adoptPtr(new SetStyleSheetTextAction(this, styleSheetId, text)), errorString);
601     m_domAgent->history()->markUndoableState();
602 }
603
604 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const RefPtr<InspectorObject>& fullStyleId, int propertyIndex, const String& text, bool overwrite, RefPtr<InspectorObject>& result)
605 {
606     InspectorCSSId compoundId(fullStyleId);
607     ASSERT(!compoundId.isEmpty());
608
609     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
610     if (!inspectorStyleSheet)
611         return;
612
613     bool success = m_domAgent->history()->perform(adoptPtr(new SetPropertyTextAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, text, overwrite)), errorString);
614     if (success)
615         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
616 }
617
618 void InspectorCSSAgent::toggleProperty(ErrorString* errorString, const RefPtr<InspectorObject>& fullStyleId, int propertyIndex, bool disable, RefPtr<InspectorObject>& result)
619 {
620     InspectorCSSId compoundId(fullStyleId);
621     ASSERT(!compoundId.isEmpty());
622
623     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
624     if (!inspectorStyleSheet)
625         return;
626
627     bool success = m_domAgent->history()->perform(adoptPtr(new TogglePropertyAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, disable)), errorString);
628     if (success)
629         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
630     m_domAgent->history()->markUndoableState();
631 }
632
633 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const RefPtr<InspectorObject>& fullRuleId, const String& selector, RefPtr<InspectorObject>& result)
634 {
635     InspectorCSSId compoundId(fullRuleId);
636     ASSERT(!compoundId.isEmpty());
637
638     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
639     if (!inspectorStyleSheet)
640         return;
641
642     bool success = inspectorStyleSheet->setRuleSelector(compoundId, selector);
643     if (!success)
644         return;
645
646     result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(compoundId));
647 }
648
649 void InspectorCSSAgent::addRule(ErrorString*, const int contextNodeId, const String& selector, RefPtr<InspectorObject>& result)
650 {
651     Node* node = m_domAgent->nodeForId(contextNodeId);
652     if (!node)
653         return;
654
655     InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(node->document(), true);
656     if (!inspectorStyleSheet)
657         return;
658     CSSStyleRule* newRule = inspectorStyleSheet->addRule(selector);
659     if (!newRule)
660         return;
661
662     result = inspectorStyleSheet->buildObjectForRule(newRule);
663 }
664
665 void InspectorCSSAgent::getSupportedCSSProperties(ErrorString*, RefPtr<InspectorArray>& cssProperties)
666 {
667     RefPtr<InspectorArray> properties = InspectorArray::create();
668     for (int i = 0; i < numCSSProperties; ++i)
669         properties->pushString(propertyNameStrings[i]);
670
671     cssProperties = properties.release();
672 }
673
674 void InspectorCSSAgent::startSelectorProfiler(ErrorString*)
675 {
676     m_currentSelectorProfile = adoptPtr(new SelectorProfile());
677     m_state->setBoolean(CSSAgentState::isSelectorProfiling, true);
678 }
679
680 void InspectorCSSAgent::stopSelectorProfiler(ErrorString* errorString, RefPtr<InspectorObject>& result)
681 {
682     stopSelectorProfilerImpl(errorString, &result);
683 }
684
685 void InspectorCSSAgent::stopSelectorProfilerImpl(ErrorString*, RefPtr<InspectorObject>* result)
686 {
687     if (!m_state->getBoolean(CSSAgentState::isSelectorProfiling))
688         return;
689     m_state->setBoolean(CSSAgentState::isSelectorProfiling, false);
690     if (m_frontend && result)
691         *result = m_currentSelectorProfile->toInspectorObject();
692     m_currentSelectorProfile.clear();
693 }
694
695 void InspectorCSSAgent::willMatchRule(const CSSStyleRule* rule)
696 {
697     if (m_currentSelectorProfile)
698         m_currentSelectorProfile->startSelector(rule);
699 }
700
701 void InspectorCSSAgent::didMatchRule(bool matched)
702 {
703     if (m_currentSelectorProfile)
704         m_currentSelectorProfile->commitSelector(matched);
705 }
706
707 void InspectorCSSAgent::willProcessRule(const CSSStyleRule* rule)
708 {
709     if (m_currentSelectorProfile)
710         m_currentSelectorProfile->startSelector(rule);
711 }
712
713 void InspectorCSSAgent::didProcessRule()
714 {
715     if (m_currentSelectorProfile)
716         m_currentSelectorProfile->commitSelectorTime();
717 }
718
719 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
720 {
721     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
722     if (it == m_nodeToInspectorStyleSheet.end()) {
723         CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0;
724         if (!style)
725             return 0;
726
727         String newStyleSheetId = String::number(m_lastStyleSheetId++);
728         RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, "regular");
729         m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet);
730         m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
731         return inspectorStyleSheet.get();
732     }
733
734     return it->second.get();
735 }
736
737 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
738 {
739     Node* node = m_domAgent->nodeForId(nodeId);
740     if (!node) {
741         *errorString = "No node with given id found";
742         return 0;
743     }
744     if (node->nodeType() != Node::ELEMENT_NODE) {
745         *errorString = "Not an element node";
746         return 0;
747     }
748     return static_cast<Element*>(node);
749 }
750
751 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, InspectorArray* result)
752 {
753     InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(static_cast<CSSStyleSheet*>(styleSheet));
754     result->pushObject(inspectorStyleSheet->buildObjectForStyleSheetInfo());
755     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
756         CSSRule* rule = styleSheet->item(i);
757         if (rule->isImportRule()) {
758             CSSStyleSheet* importedStyleSheet = static_cast<CSSImportRule*>(rule)->styleSheet();
759             if (importedStyleSheet)
760                 collectStyleSheets(importedStyleSheet, result);
761         }
762     }
763 }
764
765 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
766 {
767     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
768     if (!inspectorStyleSheet) {
769         String id = String::number(m_lastStyleSheetId++);
770         Document* document = styleSheet->findDocument();
771         inspectorStyleSheet = InspectorStyleSheet::create(id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document));
772         m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
773         m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
774     }
775     return inspectorStyleSheet.get();
776 }
777
778 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
779 {
780     if (!document) {
781         ASSERT(!createIfAbsent);
782         return 0;
783     }
784
785     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document);
786     if (inspectorStyleSheet || !createIfAbsent)
787         return inspectorStyleSheet.get();
788
789     ExceptionCode ec = 0;
790     RefPtr<Element> styleElement = document->createElement("style", ec);
791     if (!ec)
792         styleElement->setAttribute("type", "text/css", ec);
793     if (!ec) {
794         ContainerNode* targetNode;
795         // HEAD is absent in ImageDocuments, for example.
796         if (document->head())
797             targetNode = document->head();
798         else if (document->body())
799             targetNode = document->body();
800         else
801             return 0;
802         targetNode->appendChild(styleElement, ec);
803     }
804     if (ec)
805         return 0;
806     StyleSheetList* styleSheets = document->styleSheets();
807     StyleSheet* styleSheet = styleSheets->item(styleSheets->length() - 1);
808     if (!styleSheet->isCSSStyleSheet())
809         return 0;
810     CSSStyleSheet* cssStyleSheet = static_cast<CSSStyleSheet*>(styleSheet);
811     String id = String::number(m_lastStyleSheetId++);
812     inspectorStyleSheet = InspectorStyleSheet::create(id, cssStyleSheet, "inspector", InspectorDOMAgent::documentURLString(document));
813     m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
814     m_cssStyleSheetToInspectorStyleSheet.set(cssStyleSheet, inspectorStyleSheet);
815     m_documentToInspectorStyleSheet.set(document, inspectorStyleSheet);
816     return inspectorStyleSheet.get();
817 }
818
819 InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
820 {
821     IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
822     if (it == m_idToInspectorStyleSheet.end()) {
823         *errorString = "No style sheet with given id found";
824         return 0;
825     }
826     return it->second.get();
827 }
828
829 String InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
830 {
831     DEFINE_STATIC_LOCAL(String, userAgent, ("user-agent"));
832     DEFINE_STATIC_LOCAL(String, user, ("user"));
833     DEFINE_STATIC_LOCAL(String, inspector, ("inspector"));
834
835     String origin("regular");
836     if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
837         origin = userAgent;
838     else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
839         origin = user;
840     else {
841         InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
842         if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
843             origin = inspector;
844     }
845     return origin;
846 }
847
848 PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList)
849 {
850     RefPtr<InspectorArray> result = InspectorArray::create();
851     if (!ruleList)
852         return result.release();
853
854     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
855         CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
856         if (!rule)
857             continue;
858
859         InspectorStyleSheet* styleSheet = bindStyleSheet(rule->parentStyleSheet());
860         if (styleSheet)
861             result->pushObject(styleSheet->buildObjectForRule(rule));
862     }
863     return result.release();
864 }
865
866 PassRefPtr<InspectorObject> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
867 {
868     if (!element->isStyledElement())
869         return 0;
870
871     StylePropertySet* attributeStyle = static_cast<StyledElement*>(element)->attributeStyle();
872     if (!attributeStyle)
873         return 0;
874
875     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), attributeStyle->ensureCSSStyleDeclaration(), 0);
876     return inspectorStyle->buildObjectForStyle();
877 }
878
879 void InspectorCSSAgent::didRemoveDocument(Document* document)
880 {
881     if (document)
882         m_documentToInspectorStyleSheet.remove(document);
883     clearPseudoState(false);
884 }
885
886 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
887 {
888     if (!node)
889         return;
890
891     if (m_lastElementWithPseudoState.get() == node) {
892         clearPseudoState(false);
893         return;
894     }
895
896     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
897     if (it == m_nodeToInspectorStyleSheet.end())
898         return;
899
900     m_idToInspectorStyleSheet.remove(it->second->id());
901     m_nodeToInspectorStyleSheet.remove(node);
902 }
903
904 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
905 {
906     if (!element)
907         return;
908
909     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
910     if (it == m_nodeToInspectorStyleSheet.end())
911         return;
912
913     it->second->didModifyElementAttribute();
914 }
915
916 void InspectorCSSAgent::clearPseudoState(bool recalcStyles)
917 {
918     RefPtr<Element> element = m_lastElementWithPseudoState;
919     m_lastElementWithPseudoState = 0;
920     m_lastPseudoState = 0;
921     if (recalcStyles && element) {
922         Document* document = element->ownerDocument();
923         if (document)
924             document->styleSelectorChanged(RecalcStyleImmediately);
925     }
926 }
927
928 } // namespace WebCore
929
930 #endif // ENABLE(INSPECTOR)