Web Inspector: implement undo for setOuterHTML via undo-ing nested primitive commands.
[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(ExceptionCode& ec)
232     {
233         ErrorString errorString;
234         InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(&errorString, m_styleSheetId);
235         if (!styleSheet)
236             return false;
237         return perform(styleSheet, ec);
238     }
239
240     virtual bool undo(ExceptionCode& ec)
241     {
242         InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(0, m_styleSheetId);
243         if (!styleSheet)
244             return false;
245         return undo(styleSheet, ec);
246     }
247
248     virtual bool perform(InspectorStyleSheet*, ExceptionCode&) = 0;
249
250     virtual bool undo(InspectorStyleSheet*, ExceptionCode&) = 0;
251
252 protected:
253     InspectorCSSAgent* m_cssAgent;
254     String m_styleSheetId;
255 };
256
257 class InspectorCSSAgent::SetStyleSheetTextAction : public InspectorCSSAgent::StyleSheetAction {
258     WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
259 public:
260     SetStyleSheetTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const String& text)
261         : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText", cssAgent, styleSheetId)
262         , m_text(text)
263     {
264     }
265
266     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode&)
267     {
268         if (!inspectorStyleSheet->getText(&m_oldText))
269             return false;
270
271         if (inspectorStyleSheet->setText(m_text)) {
272             inspectorStyleSheet->reparseStyleSheet(m_text);
273             return true;
274         }
275         return false;
276     }
277
278     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode&)
279     {
280         if (inspectorStyleSheet->setText(m_oldText)) {
281             inspectorStyleSheet->reparseStyleSheet(m_oldText);
282             return true;
283         }
284         return false;
285     }
286
287 private:
288     String m_text;
289     String m_oldText;
290 };
291
292 class InspectorCSSAgent::SetPropertyTextAction : public InspectorCSSAgent::StyleSheetAction {
293     WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
294 public:
295     SetPropertyTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
296         : InspectorCSSAgent::StyleSheetAction("SetPropertyText", cssAgent, styleSheetId)
297         , m_cssId(cssId)
298         , m_propertyIndex(propertyIndex)
299         , m_text(text)
300         , m_overwrite(overwrite)
301     {
302     }
303
304     virtual String toString()
305     {
306         return mergeId() + ": " + m_oldText + " -> " + m_text;
307     }
308
309     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode& ec)
310     {
311         String oldText;
312         bool result = inspectorStyleSheet->setPropertyText(m_cssId, m_propertyIndex, m_text, m_overwrite, &oldText, ec);
313         m_oldText = oldText.stripWhiteSpace();
314         // FIXME: remove this once the model handles this case.
315         if (!m_oldText.endsWith(";"))
316             m_oldText += ";";
317         return result;
318     }
319
320     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode& ec)
321     {
322         String placeholder;
323         return inspectorStyleSheet->setPropertyText(m_cssId, m_propertyIndex, m_overwrite ? m_oldText : "", true, &placeholder, ec);
324     }
325
326     virtual String mergeId()
327     {
328         return String::format("SetPropertyText %s:%u:%s", m_styleSheetId.utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
329     }
330
331     virtual void merge(PassOwnPtr<Action> action)
332     {
333         ASSERT(action->mergeId() == mergeId());
334
335         SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
336         m_text = other->m_text;
337     }
338
339 private:
340     InspectorCSSId m_cssId;
341     unsigned m_propertyIndex;
342     String m_text;
343     String m_oldText;
344     bool m_overwrite;
345 };
346
347 class InspectorCSSAgent::TogglePropertyAction : public InspectorCSSAgent::StyleSheetAction {
348     WTF_MAKE_NONCOPYABLE(TogglePropertyAction);
349 public:
350     TogglePropertyAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, bool disable)
351         : InspectorCSSAgent::StyleSheetAction("ToggleProperty", cssAgent, styleSheetId)
352         , m_cssId(cssId)
353         , m_propertyIndex(propertyIndex)
354         , m_disable(disable)
355     {
356     }
357
358     virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode& ec)
359     {
360         return inspectorStyleSheet->toggleProperty(m_cssId, m_propertyIndex, m_disable, ec);
361     }
362
363     virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ExceptionCode& ec)
364     {
365         return inspectorStyleSheet->toggleProperty(m_cssId, m_propertyIndex, !m_disable, ec);
366     }
367
368 private:
369     InspectorCSSId m_cssId;
370     unsigned m_propertyIndex;
371     bool m_disable;
372 };
373
374 // static
375 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
376 {
377     if (!rule->isStyleRule())
378         return 0;
379     return static_cast<CSSStyleRule*>(rule);
380 }
381
382 InspectorCSSAgent::InspectorCSSAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, InspectorDOMAgent* domAgent)
383     : InspectorBaseAgent<InspectorCSSAgent>("CSS", instrumentingAgents, state)
384     , m_frontend(0)
385     , m_domAgent(domAgent)
386     , m_lastPseudoState(0)
387     , m_lastStyleSheetId(1)
388     , m_lastRuleId(1)
389     , m_lastStyleId(1)
390 {
391     m_domAgent->setDOMListener(this);
392     m_instrumentingAgents->setInspectorCSSAgent(this);
393 }
394
395 InspectorCSSAgent::~InspectorCSSAgent()
396 {
397     ASSERT(!m_domAgent);
398     m_instrumentingAgents->setInspectorCSSAgent(0);
399     reset();
400 }
401
402 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
403 {
404     ASSERT(!m_frontend);
405     m_frontend = frontend->css();
406 }
407
408 void InspectorCSSAgent::clearFrontend()
409 {
410     ASSERT(m_frontend);
411     m_frontend = 0;
412     clearPseudoState(true);
413     String errorString;
414     stopSelectorProfilerImpl(&errorString);
415 }
416
417 void InspectorCSSAgent::discardAgent()
418 {
419     m_domAgent->setDOMListener(0);
420     m_domAgent = 0;
421 }
422
423 void InspectorCSSAgent::restore()
424 {
425     if (m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
426         ErrorString error;
427         enable(&error);
428     }
429     if (m_state->getBoolean(CSSAgentState::isSelectorProfiling)) {
430         String errorString;
431         startSelectorProfiler(&errorString);
432     }
433 }
434
435 void InspectorCSSAgent::reset()
436 {
437     m_idToInspectorStyleSheet.clear();
438     m_cssStyleSheetToInspectorStyleSheet.clear();
439     m_nodeToInspectorStyleSheet.clear();
440     m_documentToInspectorStyleSheet.clear();
441 }
442
443 void InspectorCSSAgent::enable(ErrorString*)
444 {
445     m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
446 }
447
448 void InspectorCSSAgent::disable(ErrorString*)
449 {
450     m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
451 }
452
453 void InspectorCSSAgent::mediaQueryResultChanged()
454 {
455     if (m_frontend)
456         m_frontend->mediaQueryResultChanged();
457 }
458
459 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
460 {
461     if (m_lastElementWithPseudoState != element)
462         return false;
463
464     switch (pseudoType) {
465     case CSSSelector::PseudoActive:
466         return m_lastPseudoState & PseudoActive;
467     case CSSSelector::PseudoFocus:
468         return m_lastPseudoState & PseudoFocus;
469     case CSSSelector::PseudoHover:
470         return m_lastPseudoState & PseudoHover;
471     case CSSSelector::PseudoVisited:
472         return m_lastPseudoState & PseudoVisited;
473     default:
474         return false;
475     }
476 }
477
478 void InspectorCSSAgent::recalcStyleForPseudoStateIfNeeded(Element* element, InspectorArray* forcedPseudoClasses)
479 {
480     unsigned forcePseudoState = computePseudoClassMask(forcedPseudoClasses);
481     bool needStyleRecalc = element != m_lastElementWithPseudoState || forcePseudoState != m_lastPseudoState;
482     m_lastPseudoState = forcePseudoState;
483     m_lastElementWithPseudoState = element;
484     if (needStyleRecalc)
485         element->ownerDocument()->styleSelectorChanged(RecalcStyleImmediately);
486 }
487
488 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)
489 {
490     Element* element = elementForId(errorString, nodeId);
491     if (!element)
492         return;
493
494     recalcStyleForPseudoStateIfNeeded(element, forcedPseudoClasses ? forcedPseudoClasses->get() : 0);
495
496     // Matched rules.
497     CSSStyleSelector* selector = element->ownerDocument()->styleSelector();
498     RefPtr<CSSRuleList> matchedRules = selector->styleRulesForElement(element, CSSStyleSelector::AllCSSRules);
499     matchedCSSRules = buildArrayForRuleList(matchedRules.get());
500
501     // Pseudo elements.
502     if (!needPseudo || *needPseudo) {
503         RefPtr<InspectorArray> pseudoElements = InspectorArray::create();
504         for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
505             RefPtr<CSSRuleList> matchedRules = selector->pseudoStyleRulesForElement(element, pseudoId, CSSStyleSelector::AllCSSRules);
506             if (matchedRules && matchedRules->length()) {
507                 RefPtr<InspectorObject> pseudoStyles = InspectorObject::create();
508                 pseudoStyles->setNumber("pseudoId", static_cast<int>(pseudoId));
509                 pseudoStyles->setArray("rules", buildArrayForRuleList(matchedRules.get()));
510                 pseudoElements->pushObject(pseudoStyles.release());
511             }
512         }
513
514         pseudoIdRules = pseudoElements.release();
515     }
516
517     // Inherited styles.
518     if (!needInherited || *needInherited) {
519         RefPtr<InspectorArray> inheritedStyles = InspectorArray::create();
520         Element* parentElement = element->parentElement();
521         while (parentElement) {
522             RefPtr<InspectorObject> parentStyle = InspectorObject::create();
523             if (parentElement->style() && parentElement->style()->length()) {
524                 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
525                 if (styleSheet)
526                     parentStyle->setObject("inlineStyle", styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
527             }
528
529             CSSStyleSelector* parentSelector = parentElement->ownerDocument()->styleSelector();
530             RefPtr<CSSRuleList> parentMatchedRules = parentSelector->styleRulesForElement(parentElement, CSSStyleSelector::AllCSSRules);
531             parentStyle->setArray("matchedCSSRules", buildArrayForRuleList(parentMatchedRules.get()));
532             inheritedStyles->pushObject(parentStyle.release());
533             parentElement = parentElement->parentElement();
534         }
535
536         inheritedEntries = inheritedStyles.release();
537     }
538 }
539
540 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<InspectorObject>& inlineStyle, RefPtr<InspectorObject>& attributesStyle)
541 {
542     Element* element = elementForId(errorString, nodeId);
543     if (!element)
544         return;
545
546     InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
547     if (!styleSheet)
548         return;
549
550     inlineStyle = styleSheet->buildObjectForStyle(element->style());
551     RefPtr<InspectorObject> attributes = buildObjectForAttributesStyle(element);
552     attributesStyle = attributes ? attributes.release() : 0;
553 }
554
555 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, const RefPtr<InspectorArray>* forcedPseudoClasses, RefPtr<InspectorArray>& style)
556 {
557     Element* element = elementForId(errorString, nodeId);
558     if (!element)
559         return;
560
561     recalcStyleForPseudoStateIfNeeded(element, forcedPseudoClasses ? forcedPseudoClasses->get() : 0);
562
563     RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = computedStyle(element, true);
564     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
565     style = inspectorStyle->buildArrayForComputedStyle();
566 }
567
568 void InspectorCSSAgent::getAllStyleSheets(ErrorString*, RefPtr<InspectorArray>& styleInfos)
569 {
570     Vector<Document*> documents = m_domAgent->documents();
571     for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it) {
572         StyleSheetList* list = (*it)->styleSheets();
573         for (unsigned i = 0; i < list->length(); ++i) {
574             StyleSheet* styleSheet = list->item(i);
575             if (styleSheet->isCSSStyleSheet())
576                 collectStyleSheets(static_cast<CSSStyleSheet*>(styleSheet), styleInfos.get());
577         }
578     }
579 }
580
581 void InspectorCSSAgent::getStyleSheet(ErrorString* errorString, const String& styleSheetId, RefPtr<InspectorObject>& styleSheetObject)
582 {
583     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
584     if (!inspectorStyleSheet)
585         return;
586
587     styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet();
588 }
589
590 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
591 {
592     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
593     if (!inspectorStyleSheet)
594         return;
595
596     inspectorStyleSheet->getText(result);
597 }
598
599 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
600 {
601     ExceptionCode ec = 0;
602     m_domAgent->history()->perform(adoptPtr(new SetStyleSheetTextAction(this, styleSheetId, text)), ec);
603     *errorString = InspectorDOMAgent::toErrorString(ec);
604     m_domAgent->history()->markUndoableState();
605 }
606
607 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const RefPtr<InspectorObject>& fullStyleId, int propertyIndex, const String& text, bool overwrite, RefPtr<InspectorObject>& result)
608 {
609     InspectorCSSId compoundId(fullStyleId);
610     ASSERT(!compoundId.isEmpty());
611
612     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
613     if (!inspectorStyleSheet)
614         return;
615
616     ExceptionCode ec = 0;
617     bool success = m_domAgent->history()->perform(adoptPtr(new SetPropertyTextAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, text, overwrite)), ec);
618     if (success)
619         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
620     *errorString = InspectorDOMAgent::toErrorString(ec);
621 }
622
623 void InspectorCSSAgent::toggleProperty(ErrorString* errorString, const RefPtr<InspectorObject>& fullStyleId, int propertyIndex, bool disable, RefPtr<InspectorObject>& result)
624 {
625     InspectorCSSId compoundId(fullStyleId);
626     ASSERT(!compoundId.isEmpty());
627
628     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
629     if (!inspectorStyleSheet)
630         return;
631
632     ExceptionCode ec = 0;
633     bool success = m_domAgent->history()->perform(adoptPtr(new TogglePropertyAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, disable)), ec);
634     if (success)
635         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
636     *errorString = InspectorDOMAgent::toErrorString(ec);
637     m_domAgent->history()->markUndoableState();
638 }
639
640 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const RefPtr<InspectorObject>& fullRuleId, const String& selector, RefPtr<InspectorObject>& result)
641 {
642     InspectorCSSId compoundId(fullRuleId);
643     ASSERT(!compoundId.isEmpty());
644
645     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
646     if (!inspectorStyleSheet)
647         return;
648
649     bool success = inspectorStyleSheet->setRuleSelector(compoundId, selector);
650     if (!success)
651         return;
652
653     result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(compoundId));
654 }
655
656 void InspectorCSSAgent::addRule(ErrorString*, const int contextNodeId, const String& selector, RefPtr<InspectorObject>& result)
657 {
658     Node* node = m_domAgent->nodeForId(contextNodeId);
659     if (!node)
660         return;
661
662     InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(node->document(), true);
663     if (!inspectorStyleSheet)
664         return;
665     CSSStyleRule* newRule = inspectorStyleSheet->addRule(selector);
666     if (!newRule)
667         return;
668
669     result = inspectorStyleSheet->buildObjectForRule(newRule);
670 }
671
672 void InspectorCSSAgent::getSupportedCSSProperties(ErrorString*, RefPtr<InspectorArray>& cssProperties)
673 {
674     RefPtr<InspectorArray> properties = InspectorArray::create();
675     for (int i = 0; i < numCSSProperties; ++i)
676         properties->pushString(propertyNameStrings[i]);
677
678     cssProperties = properties.release();
679 }
680
681 void InspectorCSSAgent::startSelectorProfiler(ErrorString*)
682 {
683     m_currentSelectorProfile = adoptPtr(new SelectorProfile());
684     m_state->setBoolean(CSSAgentState::isSelectorProfiling, true);
685 }
686
687 void InspectorCSSAgent::stopSelectorProfiler(ErrorString* errorString, RefPtr<InspectorObject>& result)
688 {
689     stopSelectorProfilerImpl(errorString, &result);
690 }
691
692 void InspectorCSSAgent::stopSelectorProfilerImpl(ErrorString*, RefPtr<InspectorObject>* result)
693 {
694     if (!m_state->getBoolean(CSSAgentState::isSelectorProfiling))
695         return;
696     m_state->setBoolean(CSSAgentState::isSelectorProfiling, false);
697     if (m_frontend && result)
698         *result = m_currentSelectorProfile->toInspectorObject();
699     m_currentSelectorProfile.clear();
700 }
701
702 void InspectorCSSAgent::willMatchRule(const CSSStyleRule* rule)
703 {
704     if (m_currentSelectorProfile)
705         m_currentSelectorProfile->startSelector(rule);
706 }
707
708 void InspectorCSSAgent::didMatchRule(bool matched)
709 {
710     if (m_currentSelectorProfile)
711         m_currentSelectorProfile->commitSelector(matched);
712 }
713
714 void InspectorCSSAgent::willProcessRule(const CSSStyleRule* rule)
715 {
716     if (m_currentSelectorProfile)
717         m_currentSelectorProfile->startSelector(rule);
718 }
719
720 void InspectorCSSAgent::didProcessRule()
721 {
722     if (m_currentSelectorProfile)
723         m_currentSelectorProfile->commitSelectorTime();
724 }
725
726 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
727 {
728     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
729     if (it == m_nodeToInspectorStyleSheet.end()) {
730         CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0;
731         if (!style)
732             return 0;
733
734         String newStyleSheetId = String::number(m_lastStyleSheetId++);
735         RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, "regular");
736         m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet);
737         m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
738         return inspectorStyleSheet.get();
739     }
740
741     return it->second.get();
742 }
743
744 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
745 {
746     Node* node = m_domAgent->nodeForId(nodeId);
747     if (!node) {
748         *errorString = "No node with given id found";
749         return 0;
750     }
751     if (node->nodeType() != Node::ELEMENT_NODE) {
752         *errorString = "Not an element node";
753         return 0;
754     }
755     return static_cast<Element*>(node);
756 }
757
758 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, InspectorArray* result)
759 {
760     InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(static_cast<CSSStyleSheet*>(styleSheet));
761     result->pushObject(inspectorStyleSheet->buildObjectForStyleSheetInfo());
762     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
763         CSSRule* rule = styleSheet->item(i);
764         if (rule->isImportRule()) {
765             CSSStyleSheet* importedStyleSheet = static_cast<CSSImportRule*>(rule)->styleSheet();
766             if (importedStyleSheet)
767                 collectStyleSheets(importedStyleSheet, result);
768         }
769     }
770 }
771
772 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
773 {
774     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
775     if (!inspectorStyleSheet) {
776         String id = String::number(m_lastStyleSheetId++);
777         Document* document = styleSheet->findDocument();
778         inspectorStyleSheet = InspectorStyleSheet::create(id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document));
779         m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
780         m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
781     }
782     return inspectorStyleSheet.get();
783 }
784
785 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
786 {
787     if (!document) {
788         ASSERT(!createIfAbsent);
789         return 0;
790     }
791
792     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document);
793     if (inspectorStyleSheet || !createIfAbsent)
794         return inspectorStyleSheet.get();
795
796     ExceptionCode ec = 0;
797     RefPtr<Element> styleElement = document->createElement("style", ec);
798     if (!ec)
799         styleElement->setAttribute("type", "text/css", ec);
800     if (!ec) {
801         ContainerNode* targetNode;
802         // HEAD is absent in ImageDocuments, for example.
803         if (document->head())
804             targetNode = document->head();
805         else if (document->body())
806             targetNode = document->body();
807         else
808             return 0;
809         targetNode->appendChild(styleElement, ec);
810     }
811     if (ec)
812         return 0;
813     StyleSheetList* styleSheets = document->styleSheets();
814     StyleSheet* styleSheet = styleSheets->item(styleSheets->length() - 1);
815     if (!styleSheet->isCSSStyleSheet())
816         return 0;
817     CSSStyleSheet* cssStyleSheet = static_cast<CSSStyleSheet*>(styleSheet);
818     String id = String::number(m_lastStyleSheetId++);
819     inspectorStyleSheet = InspectorStyleSheet::create(id, cssStyleSheet, "inspector", InspectorDOMAgent::documentURLString(document));
820     m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
821     m_cssStyleSheetToInspectorStyleSheet.set(cssStyleSheet, inspectorStyleSheet);
822     m_documentToInspectorStyleSheet.set(document, inspectorStyleSheet);
823     return inspectorStyleSheet.get();
824 }
825
826 InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
827 {
828     IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
829     if (it == m_idToInspectorStyleSheet.end()) {
830         *errorString = "No style sheet with given id found";
831         return 0;
832     }
833     return it->second.get();
834 }
835
836 String InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
837 {
838     DEFINE_STATIC_LOCAL(String, userAgent, ("user-agent"));
839     DEFINE_STATIC_LOCAL(String, user, ("user"));
840     DEFINE_STATIC_LOCAL(String, inspector, ("inspector"));
841
842     String origin("regular");
843     if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
844         origin = userAgent;
845     else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
846         origin = user;
847     else {
848         InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
849         if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
850             origin = inspector;
851     }
852     return origin;
853 }
854
855 PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList)
856 {
857     RefPtr<InspectorArray> result = InspectorArray::create();
858     if (!ruleList)
859         return result.release();
860
861     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
862         CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
863         if (!rule)
864             continue;
865
866         InspectorStyleSheet* styleSheet = bindStyleSheet(rule->parentStyleSheet());
867         if (styleSheet)
868             result->pushObject(styleSheet->buildObjectForRule(rule));
869     }
870     return result.release();
871 }
872
873 PassRefPtr<InspectorObject> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
874 {
875     if (!element->isStyledElement())
876         return 0;
877
878     StylePropertySet* attributeStyle = static_cast<StyledElement*>(element)->attributeStyle();
879     if (!attributeStyle)
880         return 0;
881
882     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), attributeStyle->ensureCSSStyleDeclaration(), 0);
883     return inspectorStyle->buildObjectForStyle();
884 }
885
886 void InspectorCSSAgent::didRemoveDocument(Document* document)
887 {
888     if (document)
889         m_documentToInspectorStyleSheet.remove(document);
890     clearPseudoState(false);
891 }
892
893 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
894 {
895     if (!node)
896         return;
897
898     if (m_lastElementWithPseudoState.get() == node) {
899         clearPseudoState(false);
900         return;
901     }
902
903     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
904     if (it == m_nodeToInspectorStyleSheet.end())
905         return;
906
907     m_idToInspectorStyleSheet.remove(it->second->id());
908     m_nodeToInspectorStyleSheet.remove(node);
909 }
910
911 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
912 {
913     if (!element)
914         return;
915
916     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
917     if (it == m_nodeToInspectorStyleSheet.end())
918         return;
919
920     it->second->didModifyElementAttribute();
921 }
922
923 void InspectorCSSAgent::clearPseudoState(bool recalcStyles)
924 {
925     RefPtr<Element> element = m_lastElementWithPseudoState;
926     m_lastElementWithPseudoState = 0;
927     m_lastPseudoState = 0;
928     if (recalcStyles && element) {
929         Document* document = element->ownerDocument();
930         if (document)
931             document->styleSelectorChanged(RecalcStyleImmediately);
932     }
933 }
934
935 } // namespace WebCore
936
937 #endif // ENABLE(INSPECTOR)