• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/inspector/InspectorCSSAgent.h"
27 
28 #include "CSSPropertyNames.h"
29 #include "FetchInitiatorTypeNames.h"
30 #include "InspectorTypeBuilder.h"
31 #include "StylePropertyShorthand.h"
32 #include "bindings/v8/ExceptionState.h"
33 #include "bindings/v8/ExceptionStatePlaceholder.h"
34 #include "core/css/CSSComputedStyleDeclaration.h"
35 #include "core/css/CSSImportRule.h"
36 #include "core/css/CSSMediaRule.h"
37 #include "core/css/CSSParser.h"
38 #include "core/css/CSSRule.h"
39 #include "core/css/CSSRuleList.h"
40 #include "core/css/CSSStyleRule.h"
41 #include "core/css/CSSStyleSheet.h"
42 #include "core/css/MediaList.h"
43 #include "core/css/StylePropertySet.h"
44 #include "core/css/StyleRule.h"
45 #include "core/css/StyleSheet.h"
46 #include "core/css/StyleSheetContents.h"
47 #include "core/css/StyleSheetList.h"
48 #include "core/css/resolver/StyleResolver.h"
49 #include "core/dom/NamedFlow.h"
50 #include "core/dom/NamedFlowCollection.h"
51 #include "core/dom/Node.h"
52 #include "core/dom/NodeList.h"
53 #include "core/fetch/CSSStyleSheetResource.h"
54 #include "core/fetch/ResourceClient.h"
55 #include "core/fetch/ResourceFetcher.h"
56 #include "core/fetch/StyleSheetResourceClient.h"
57 #include "core/html/HTMLHeadElement.h"
58 #include "core/inspector/InspectorHistory.h"
59 #include "core/inspector/InspectorPageAgent.h"
60 #include "core/inspector/InspectorResourceAgent.h"
61 #include "core/inspector/InspectorState.h"
62 #include "core/inspector/InstrumentingAgents.h"
63 #include "core/loader/DocumentLoader.h"
64 #include "core/frame/Frame.h"
65 #include "core/page/Page.h"
66 #include "core/rendering/InlineTextBox.h"
67 #include "core/rendering/RenderObject.h"
68 #include "core/rendering/RenderRegion.h"
69 #include "core/rendering/RenderText.h"
70 #include "core/rendering/RenderTextFragment.h"
71 #include "platform/fonts/Font.h"
72 #include "platform/fonts/GlyphBuffer.h"
73 #include "platform/fonts/WidthIterator.h"
74 #include "platform/text/TextRun.h"
75 #include "wtf/CurrentTime.h"
76 #include "wtf/text/CString.h"
77 #include "wtf/text/StringConcatenate.h"
78 
79 namespace CSSAgentState {
80 static const char cssAgentEnabled[] = "cssAgentEnabled";
81 }
82 
83 typedef WebCore::InspectorBackendDispatcher::CSSCommandHandler::EnableCallback EnableCallback;
84 
85 namespace WebCore {
86 
87 enum ForcePseudoClassFlags {
88     PseudoNone = 0,
89     PseudoHover = 1 << 0,
90     PseudoFocus = 1 << 1,
91     PseudoActive = 1 << 2,
92     PseudoVisited = 1 << 3
93 };
94 
95 class StyleSheetAppender {
96 public:
StyleSheetAppender(Vector<CSSStyleSheet * > & result)97     StyleSheetAppender(Vector<CSSStyleSheet*>& result)
98         : m_result(result) { }
99 
run(CSSStyleSheet * styleSheet)100     void run(CSSStyleSheet* styleSheet)
101     {
102         m_result.append(styleSheet);
103         for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
104             CSSRule* rule = styleSheet->item(i);
105             if (rule->type() == CSSRule::IMPORT_RULE) {
106                 CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
107                 if (importedStyleSheet)
108                     run(importedStyleSheet);
109             }
110         }
111     }
112 
113 private:
114     Vector<CSSStyleSheet*>& m_result;
115 };
116 
computePseudoClassMask(JSONArray * pseudoClassArray)117 static unsigned computePseudoClassMask(JSONArray* pseudoClassArray)
118 {
119     DEFINE_STATIC_LOCAL(String, active, ("active"));
120     DEFINE_STATIC_LOCAL(String, hover, ("hover"));
121     DEFINE_STATIC_LOCAL(String, focus, ("focus"));
122     DEFINE_STATIC_LOCAL(String, visited, ("visited"));
123     if (!pseudoClassArray || !pseudoClassArray->length())
124         return PseudoNone;
125 
126     unsigned result = PseudoNone;
127     for (size_t i = 0; i < pseudoClassArray->length(); ++i) {
128         RefPtr<JSONValue> pseudoClassValue = pseudoClassArray->get(i);
129         String pseudoClass;
130         bool success = pseudoClassValue->asString(&pseudoClass);
131         if (!success)
132             continue;
133         if (pseudoClass == active)
134             result |= PseudoActive;
135         else if (pseudoClass == hover)
136             result |= PseudoHover;
137         else if (pseudoClass == focus)
138             result |= PseudoFocus;
139         else if (pseudoClass == visited)
140             result |= PseudoVisited;
141     }
142 
143     return result;
144 }
145 
146 class UpdateRegionLayoutTask {
147 public:
148     UpdateRegionLayoutTask(InspectorCSSAgent*);
149     void scheduleFor(NamedFlow*, int documentNodeId);
150     void unschedule(NamedFlow*);
151     void reset();
152     void onTimer(Timer<UpdateRegionLayoutTask>*);
153 
154 private:
155     InspectorCSSAgent* m_cssAgent;
156     Timer<UpdateRegionLayoutTask> m_timer;
157     HashMap<NamedFlow*, int> m_namedFlows;
158 };
159 
UpdateRegionLayoutTask(InspectorCSSAgent * cssAgent)160 UpdateRegionLayoutTask::UpdateRegionLayoutTask(InspectorCSSAgent* cssAgent)
161     : m_cssAgent(cssAgent)
162     , m_timer(this, &UpdateRegionLayoutTask::onTimer)
163 {
164 }
165 
scheduleFor(NamedFlow * namedFlow,int documentNodeId)166 void UpdateRegionLayoutTask::scheduleFor(NamedFlow* namedFlow, int documentNodeId)
167 {
168     m_namedFlows.add(namedFlow, documentNodeId);
169 
170     if (!m_timer.isActive())
171         m_timer.startOneShot(0);
172 }
173 
unschedule(NamedFlow * namedFlow)174 void UpdateRegionLayoutTask::unschedule(NamedFlow* namedFlow)
175 {
176     m_namedFlows.remove(namedFlow);
177 }
178 
reset()179 void UpdateRegionLayoutTask::reset()
180 {
181     m_timer.stop();
182     m_namedFlows.clear();
183 }
184 
onTimer(Timer<UpdateRegionLayoutTask> *)185 void UpdateRegionLayoutTask::onTimer(Timer<UpdateRegionLayoutTask>*)
186 {
187     // The timer is stopped on m_cssAgent destruction, so this method will never be called after m_cssAgent has been destroyed.
188     Vector<std::pair<NamedFlow*, int> > namedFlows;
189 
190     for (HashMap<NamedFlow*, int>::iterator it = m_namedFlows.begin(), end = m_namedFlows.end(); it != end; ++it)
191         namedFlows.append(std::make_pair(it->key, it->value));
192 
193     for (unsigned i = 0, size = namedFlows.size(); i < size; ++i) {
194         NamedFlow* namedFlow = namedFlows.at(i).first;
195         int documentNodeId = namedFlows.at(i).second;
196 
197         if (m_namedFlows.contains(namedFlow)) {
198             m_cssAgent->regionLayoutUpdated(namedFlow, documentNodeId);
199             m_namedFlows.remove(namedFlow);
200         }
201     }
202 
203     if (!m_namedFlows.isEmpty() && !m_timer.isActive())
204         m_timer.startOneShot(0);
205 }
206 
207 class ChangeRegionOversetTask {
208 public:
209     ChangeRegionOversetTask(InspectorCSSAgent*);
210     void scheduleFor(NamedFlow*, int documentNodeId);
211     void unschedule(NamedFlow*);
212     void reset();
213     void onTimer(Timer<ChangeRegionOversetTask>*);
214 
215 private:
216     InspectorCSSAgent* m_cssAgent;
217     Timer<ChangeRegionOversetTask> m_timer;
218     HashMap<NamedFlow*, int> m_namedFlows;
219 };
220 
ChangeRegionOversetTask(InspectorCSSAgent * cssAgent)221 ChangeRegionOversetTask::ChangeRegionOversetTask(InspectorCSSAgent* cssAgent)
222     : m_cssAgent(cssAgent)
223     , m_timer(this, &ChangeRegionOversetTask::onTimer)
224 {
225 }
226 
scheduleFor(NamedFlow * namedFlow,int documentNodeId)227 void ChangeRegionOversetTask::scheduleFor(NamedFlow* namedFlow, int documentNodeId)
228 {
229     m_namedFlows.add(namedFlow, documentNodeId);
230 
231     if (!m_timer.isActive())
232         m_timer.startOneShot(0);
233 }
234 
unschedule(NamedFlow * namedFlow)235 void ChangeRegionOversetTask::unschedule(NamedFlow* namedFlow)
236 {
237     m_namedFlows.remove(namedFlow);
238 }
239 
reset()240 void ChangeRegionOversetTask::reset()
241 {
242     m_timer.stop();
243     m_namedFlows.clear();
244 }
245 
onTimer(Timer<ChangeRegionOversetTask> *)246 void ChangeRegionOversetTask::onTimer(Timer<ChangeRegionOversetTask>*)
247 {
248     // The timer is stopped on m_cssAgent destruction, so this method will never be called after m_cssAgent has been destroyed.
249     for (HashMap<NamedFlow*, int>::iterator it = m_namedFlows.begin(), end = m_namedFlows.end(); it != end; ++it)
250         m_cssAgent->regionOversetChanged(it->key, it->value);
251 
252     m_namedFlows.clear();
253 }
254 
255 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
256     WTF_MAKE_NONCOPYABLE(StyleSheetAction);
257 public:
StyleSheetAction(const String & name,InspectorStyleSheet * styleSheet)258     StyleSheetAction(const String& name, InspectorStyleSheet* styleSheet)
259         : InspectorHistory::Action(name)
260         , m_styleSheet(styleSheet)
261     {
262     }
263 
264 protected:
265     RefPtr<InspectorStyleSheet> m_styleSheet;
266 };
267 
268 class InspectorCSSAgent::EnableResourceClient : public StyleSheetResourceClient {
269 public:
270     EnableResourceClient(InspectorCSSAgent*, const Vector<InspectorStyleSheet*>&, PassRefPtr<EnableCallback>);
271 
272     virtual void setCSSStyleSheet(const String&, const KURL&, const String&, const CSSStyleSheetResource*) OVERRIDE;
273 
274 private:
275     RefPtr<EnableCallback> m_callback;
276     InspectorCSSAgent* m_cssAgent;
277     int m_pendingResources;
278     Vector<InspectorStyleSheet*> m_styleSheets;
279 };
280 
EnableResourceClient(InspectorCSSAgent * cssAgent,const Vector<InspectorStyleSheet * > & styleSheets,PassRefPtr<EnableCallback> callback)281 InspectorCSSAgent::EnableResourceClient::EnableResourceClient(InspectorCSSAgent* cssAgent, const Vector<InspectorStyleSheet*>& styleSheets, PassRefPtr<EnableCallback> callback)
282     : m_callback(callback)
283     , m_cssAgent(cssAgent)
284     , m_pendingResources(styleSheets.size())
285     , m_styleSheets(styleSheets)
286 {
287     for (size_t i = 0; i < styleSheets.size(); ++i) {
288         InspectorStyleSheet* styleSheet = styleSheets.at(i);
289         Document* document = styleSheet->ownerDocument();
290         FetchRequest request(ResourceRequest(styleSheet->finalURL()), FetchInitiatorTypeNames::internal);
291         ResourcePtr<Resource> resource = document->fetcher()->fetchCSSStyleSheet(request);
292         resource->addClient(this);
293     }
294 }
295 
setCSSStyleSheet(const String &,const KURL & url,const String &,const CSSStyleSheetResource * resource)296 void InspectorCSSAgent::EnableResourceClient::setCSSStyleSheet(const String&, const KURL& url, const String&, const CSSStyleSheetResource* resource)
297 {
298     const_cast<CSSStyleSheetResource*>(resource)->removeClient(this);
299     --m_pendingResources;
300     if (m_pendingResources)
301         return;
302 
303     // enable always succeeds.
304     if (m_callback->isActive())
305         m_cssAgent->wasEnabled(m_callback.release());
306     delete this;
307 }
308 
309 class InspectorCSSAgent::SetStyleSheetTextAction : public InspectorCSSAgent::StyleSheetAction {
310     WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
311 public:
SetStyleSheetTextAction(InspectorStyleSheet * styleSheet,const String & text)312     SetStyleSheetTextAction(InspectorStyleSheet* styleSheet, const String& text)
313         : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText", styleSheet)
314         , m_text(text)
315     {
316     }
317 
perform(ExceptionState & exceptionState)318     virtual bool perform(ExceptionState& exceptionState)
319     {
320         if (!m_styleSheet->getText(&m_oldText))
321             return false;
322         return redo(exceptionState);
323     }
324 
undo(ExceptionState & exceptionState)325     virtual bool undo(ExceptionState& exceptionState)
326     {
327         if (m_styleSheet->setText(m_oldText, exceptionState)) {
328             m_styleSheet->reparseStyleSheet(m_oldText);
329             return true;
330         }
331         return false;
332     }
333 
redo(ExceptionState & exceptionState)334     virtual bool redo(ExceptionState& exceptionState)
335     {
336         if (m_styleSheet->setText(m_text, exceptionState)) {
337             m_styleSheet->reparseStyleSheet(m_text);
338             return true;
339         }
340         return false;
341     }
342 
mergeId()343     virtual String mergeId()
344     {
345         return String::format("SetStyleSheetText %s", m_styleSheet->id().utf8().data());
346     }
347 
merge(PassOwnPtr<Action> action)348     virtual void merge(PassOwnPtr<Action> action)
349     {
350         ASSERT(action->mergeId() == mergeId());
351 
352         SetStyleSheetTextAction* other = static_cast<SetStyleSheetTextAction*>(action.get());
353         m_text = other->m_text;
354     }
355 
356 private:
357     String m_text;
358     String m_oldText;
359 };
360 
361 class InspectorCSSAgent::SetStyleTextAction : public InspectorCSSAgent::StyleSheetAction {
362     WTF_MAKE_NONCOPYABLE(SetStyleTextAction);
363 public:
SetStyleTextAction(InspectorStyleSheet * styleSheet,const InspectorCSSId & cssId,const String & text)364     SetStyleTextAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& text)
365         : InspectorCSSAgent::StyleSheetAction("SetPropertyText", styleSheet)
366         , m_cssId(cssId)
367         , m_text(text)
368     {
369     }
370 
toString()371     virtual String toString()
372     {
373         return mergeId() + ": " + m_oldText + " -> " + m_text;
374     }
375 
perform(ExceptionState & exceptionState)376     virtual bool perform(ExceptionState& exceptionState)
377     {
378         return redo(exceptionState);
379     }
380 
undo(ExceptionState & exceptionState)381     virtual bool undo(ExceptionState& exceptionState)
382     {
383         String placeholder;
384         return m_styleSheet->setStyleText(m_cssId, m_oldText, &placeholder, exceptionState);
385     }
386 
redo(ExceptionState & exceptionState)387     virtual bool redo(ExceptionState& exceptionState)
388     {
389         return m_styleSheet->setStyleText(m_cssId, m_text, &m_oldText, exceptionState);
390     }
391 
mergeId()392     virtual String mergeId()
393     {
394         return String::format("SetStyleText %s:%u", m_cssId.styleSheetId().utf8().data(), m_cssId.ordinal());
395     }
396 
merge(PassOwnPtr<Action> action)397     virtual void merge(PassOwnPtr<Action> action)
398     {
399         ASSERT(action->mergeId() == mergeId());
400 
401         SetStyleTextAction* other = static_cast<SetStyleTextAction*>(action.get());
402         m_text = other->m_text;
403     }
404 
405 private:
406     InspectorCSSId m_cssId;
407     String m_text;
408     String m_oldText;
409 };
410 
411 class InspectorCSSAgent::SetPropertyTextAction : public InspectorCSSAgent::StyleSheetAction {
412     WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
413 public:
SetPropertyTextAction(InspectorStyleSheet * styleSheet,const InspectorCSSId & cssId,unsigned propertyIndex,const String & text,bool overwrite)414     SetPropertyTextAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
415         : InspectorCSSAgent::StyleSheetAction("SetPropertyText", styleSheet)
416         , m_cssId(cssId)
417         , m_propertyIndex(propertyIndex)
418         , m_text(text)
419         , m_overwrite(overwrite)
420     {
421     }
422 
toString()423     virtual String toString()
424     {
425         return mergeId() + ": " + m_oldText + " -> " + m_text;
426     }
427 
perform(ExceptionState & exceptionState)428     virtual bool perform(ExceptionState& exceptionState)
429     {
430         return redo(exceptionState);
431     }
432 
undo(ExceptionState & exceptionState)433     virtual bool undo(ExceptionState& exceptionState)
434     {
435         String placeholder;
436         return m_styleSheet->setPropertyText(m_cssId, m_propertyIndex, m_overwrite ? m_oldText : "", true, &placeholder, exceptionState);
437     }
438 
redo(ExceptionState & exceptionState)439     virtual bool redo(ExceptionState& exceptionState)
440     {
441         String oldText;
442         bool result = m_styleSheet->setPropertyText(m_cssId, m_propertyIndex, m_text, m_overwrite, &oldText, exceptionState);
443         m_oldText = oldText.stripWhiteSpace();
444         // FIXME: remove this once the model handles this case.
445         if (!m_oldText.endsWith(';'))
446             m_oldText.append(';');
447         return result;
448     }
449 
mergeId()450     virtual String mergeId()
451     {
452         return String::format("SetPropertyText %s:%u:%s", m_styleSheet->id().utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
453     }
454 
merge(PassOwnPtr<Action> action)455     virtual void merge(PassOwnPtr<Action> action)
456     {
457         ASSERT(action->mergeId() == mergeId());
458 
459         SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
460         m_text = other->m_text;
461     }
462 
463 private:
464     InspectorCSSId m_cssId;
465     unsigned m_propertyIndex;
466     String m_text;
467     String m_oldText;
468     bool m_overwrite;
469 };
470 
471 class InspectorCSSAgent::TogglePropertyAction : public InspectorCSSAgent::StyleSheetAction {
472     WTF_MAKE_NONCOPYABLE(TogglePropertyAction);
473 public:
TogglePropertyAction(InspectorStyleSheet * styleSheet,const InspectorCSSId & cssId,unsigned propertyIndex,bool disable)474     TogglePropertyAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, unsigned propertyIndex, bool disable)
475         : InspectorCSSAgent::StyleSheetAction("ToggleProperty", styleSheet)
476         , m_cssId(cssId)
477         , m_propertyIndex(propertyIndex)
478         , m_disable(disable)
479     {
480     }
481 
perform(ExceptionState & exceptionState)482     virtual bool perform(ExceptionState& exceptionState)
483     {
484         return redo(exceptionState);
485     }
486 
undo(ExceptionState & exceptionState)487     virtual bool undo(ExceptionState& exceptionState)
488     {
489         return m_styleSheet->toggleProperty(m_cssId, m_propertyIndex, !m_disable, exceptionState);
490     }
491 
redo(ExceptionState & exceptionState)492     virtual bool redo(ExceptionState& exceptionState)
493     {
494         return m_styleSheet->toggleProperty(m_cssId, m_propertyIndex, m_disable, exceptionState);
495     }
496 
497 private:
498     InspectorCSSId m_cssId;
499     unsigned m_propertyIndex;
500     bool m_disable;
501 };
502 
503 class InspectorCSSAgent::SetRuleSelectorAction : public InspectorCSSAgent::StyleSheetAction {
504     WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
505 public:
SetRuleSelectorAction(InspectorStyleSheet * styleSheet,const InspectorCSSId & cssId,const String & selector)506     SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector)
507         : InspectorCSSAgent::StyleSheetAction("SetRuleSelector", styleSheet)
508         , m_cssId(cssId)
509         , m_selector(selector)
510     {
511     }
512 
perform(ExceptionState & exceptionState)513     virtual bool perform(ExceptionState& exceptionState)
514     {
515         m_oldSelector = m_styleSheet->ruleSelector(m_cssId, exceptionState);
516         if (exceptionState.hadException())
517             return false;
518         return redo(exceptionState);
519     }
520 
undo(ExceptionState & exceptionState)521     virtual bool undo(ExceptionState& exceptionState)
522     {
523         return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector, exceptionState);
524     }
525 
redo(ExceptionState & exceptionState)526     virtual bool redo(ExceptionState& exceptionState)
527     {
528         return m_styleSheet->setRuleSelector(m_cssId, m_selector, exceptionState);
529     }
530 
531 private:
532     InspectorCSSId m_cssId;
533     String m_selector;
534     String m_oldSelector;
535 };
536 
537 class InspectorCSSAgent::AddRuleAction : public InspectorCSSAgent::StyleSheetAction {
538     WTF_MAKE_NONCOPYABLE(AddRuleAction);
539 public:
AddRuleAction(InspectorStyleSheet * styleSheet,const String & selector)540     AddRuleAction(InspectorStyleSheet* styleSheet, const String& selector)
541         : InspectorCSSAgent::StyleSheetAction("AddRule", styleSheet)
542         , m_selector(selector)
543     {
544     }
545 
perform(ExceptionState & exceptionState)546     virtual bool perform(ExceptionState& exceptionState)
547     {
548         return redo(exceptionState);
549     }
550 
undo(ExceptionState & exceptionState)551     virtual bool undo(ExceptionState& exceptionState)
552     {
553         return m_styleSheet->deleteRule(m_newId, exceptionState);
554     }
555 
redo(ExceptionState & exceptionState)556     virtual bool redo(ExceptionState& exceptionState)
557     {
558         CSSStyleRule* cssStyleRule = m_styleSheet->addRule(m_selector, exceptionState);
559         if (exceptionState.hadException())
560             return false;
561         m_newId = m_styleSheet->ruleId(cssStyleRule);
562         return true;
563     }
564 
newRuleId()565     InspectorCSSId newRuleId() { return m_newId; }
566 
567 private:
568     InspectorCSSId m_newId;
569     String m_selector;
570     String m_oldSelector;
571 };
572 
573 // static
asCSSStyleRule(CSSRule * rule)574 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
575 {
576     if (!rule || rule->type() != CSSRule::STYLE_RULE)
577         return 0;
578     return toCSSStyleRule(rule);
579 }
580 
581 template <typename CharType, size_t bufferLength>
vendorPrefixLowerCase(const CharType * string,size_t stringLength,char (& buffer)[bufferLength])582 static size_t vendorPrefixLowerCase(const CharType* string, size_t stringLength, char (&buffer)[bufferLength])
583 {
584     static const char lowerCaseOffset = 'a' - 'A';
585 
586     if (string[0] != '-')
587         return 0;
588 
589     for (size_t i = 0; i < stringLength - 1; i++) {
590         CharType c = string[i + 1];
591         if (c == '-')
592             return i;
593         if (i == bufferLength)
594             break;
595         if (c < 'A' || c > 'z')
596             break;
597         if (c >= 'a')
598             buffer[i] = c;
599         else if (c <= 'Z')
600             buffer[i] = c + lowerCaseOffset;
601         else
602             break;
603     }
604     return 0;
605 }
606 
InspectorCSSAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * state,InspectorDOMAgent * domAgent,InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent)607 InspectorCSSAgent::InspectorCSSAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* state, InspectorDOMAgent* domAgent, InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent)
608     : InspectorBaseAgent<InspectorCSSAgent>("CSS", instrumentingAgents, state)
609     , m_frontend(0)
610     , m_domAgent(domAgent)
611     , m_pageAgent(pageAgent)
612     , m_resourceAgent(resourceAgent)
613     , m_lastStyleSheetId(1)
614     , m_styleSheetsPendingMutation(0)
615     , m_styleDeclarationPendingMutation(false)
616     , m_creatingViaInspectorStyleSheet(false)
617     , m_isSettingStyleSheetText(false)
618 {
619     m_domAgent->setDOMListener(this);
620 }
621 
~InspectorCSSAgent()622 InspectorCSSAgent::~InspectorCSSAgent()
623 {
624     ASSERT(!m_domAgent);
625     reset();
626 }
627 
setFrontend(InspectorFrontend * frontend)628 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
629 {
630     ASSERT(!m_frontend);
631     m_frontend = frontend->css();
632 }
633 
clearFrontend()634 void InspectorCSSAgent::clearFrontend()
635 {
636     ASSERT(m_frontend);
637     m_frontend = 0;
638     resetNonPersistentData();
639 }
640 
discardAgent()641 void InspectorCSSAgent::discardAgent()
642 {
643     m_domAgent->setDOMListener(0);
644     m_domAgent = 0;
645 }
646 
restore()647 void InspectorCSSAgent::restore()
648 {
649     if (m_state->getBoolean(CSSAgentState::cssAgentEnabled))
650         wasEnabled(0);
651 }
652 
reset()653 void InspectorCSSAgent::reset()
654 {
655     m_idToInspectorStyleSheet.clear();
656     m_cssStyleSheetToInspectorStyleSheet.clear();
657     m_nodeToInspectorStyleSheet.clear();
658     m_documentToInspectorStyleSheet.clear();
659     resetNonPersistentData();
660 }
661 
resetNonPersistentData()662 void InspectorCSSAgent::resetNonPersistentData()
663 {
664     m_namedFlowCollectionsRequested.clear();
665     if (m_updateRegionLayoutTask)
666         m_updateRegionLayoutTask->reset();
667     if (m_changeRegionOversetTask)
668         m_changeRegionOversetTask->reset();
669     resetPseudoStates();
670 }
671 
enable(ErrorString *,PassRefPtr<EnableCallback> prpCallback)672 void InspectorCSSAgent::enable(ErrorString*, PassRefPtr<EnableCallback> prpCallback)
673 {
674     m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
675 
676     Vector<InspectorStyleSheet*> styleSheets;
677     collectAllStyleSheets(styleSheets);
678 
679     // Re-issue stylesheet requets for resources that are no longer in memory cache.
680     Vector<InspectorStyleSheet*> styleSheetsToFetch;
681     HashSet<String> urlsToFetch;
682     for (size_t i = 0; i < styleSheets.size(); ++i) {
683         InspectorStyleSheet* styleSheet = styleSheets.at(i);
684         String url = styleSheet->finalURL();
685         if (urlsToFetch.contains(url))
686             continue;
687         CSSStyleSheet* pageStyleSheet = styleSheet->pageStyleSheet();
688         if (pageStyleSheet->isInline() || !pageStyleSheet->contents()->loadCompleted())
689             continue;
690         Document* document = styleSheet->ownerDocument();
691         if (!document)
692             continue;
693         Resource* cachedResource = document->fetcher()->cachedResource(url);
694         if (cachedResource)
695             continue;
696         urlsToFetch.add(styleSheet->finalURL());
697         styleSheetsToFetch.append(styleSheet);
698     }
699 
700     if (styleSheetsToFetch.isEmpty()) {
701         wasEnabled(prpCallback);
702         return;
703     }
704     new EnableResourceClient(this, styleSheetsToFetch, prpCallback);
705 }
706 
wasEnabled(PassRefPtr<EnableCallback> callback)707 void InspectorCSSAgent::wasEnabled(PassRefPtr<EnableCallback> callback)
708 {
709     if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
710         // We were disabled while fetching resources.
711         return;
712     }
713 
714     Vector<InspectorStyleSheet*> styleSheets;
715     collectAllStyleSheets(styleSheets);
716     for (size_t i = 0; i < styleSheets.size(); ++i)
717         m_frontend->styleSheetAdded(styleSheets.at(i)->buildObjectForStyleSheetInfo());
718 
719     // More styleSheetAdded events will be generated below.
720     m_instrumentingAgents->setInspectorCSSAgent(this);
721     Vector<Document*> documents = m_domAgent->documents();
722     for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it)
723         (*it)->styleEngine()->updateActiveStyleSheets(FullStyleUpdate);
724 
725     if (callback)
726         callback->sendSuccess();
727 }
728 
disable(ErrorString *)729 void InspectorCSSAgent::disable(ErrorString*)
730 {
731     m_instrumentingAgents->setInspectorCSSAgent(0);
732     m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
733 }
734 
didCommitLoad(Frame * frame,DocumentLoader * loader)735 void InspectorCSSAgent::didCommitLoad(Frame* frame, DocumentLoader* loader)
736 {
737     if (loader->frame() != frame->page()->mainFrame())
738         return;
739 
740     reset();
741 }
742 
mediaQueryResultChanged()743 void InspectorCSSAgent::mediaQueryResultChanged()
744 {
745     if (m_frontend)
746         m_frontend->mediaQueryResultChanged();
747 }
748 
didCreateNamedFlow(Document * document,NamedFlow * namedFlow)749 void InspectorCSSAgent::didCreateNamedFlow(Document* document, NamedFlow* namedFlow)
750 {
751     int documentNodeId = documentNodeWithRequestedFlowsId(document);
752     if (!documentNodeId)
753         return;
754 
755     ErrorString errorString;
756     m_frontend->namedFlowCreated(buildObjectForNamedFlow(&errorString, namedFlow, documentNodeId));
757 }
758 
willRemoveNamedFlow(Document * document,NamedFlow * namedFlow)759 void InspectorCSSAgent::willRemoveNamedFlow(Document* document, NamedFlow* namedFlow)
760 {
761     int documentNodeId = documentNodeWithRequestedFlowsId(document);
762     if (!documentNodeId)
763         return;
764 
765     if (m_updateRegionLayoutTask)
766         m_updateRegionLayoutTask->unschedule(namedFlow);
767 
768     m_frontend->namedFlowRemoved(documentNodeId, namedFlow->name().string());
769 }
770 
willMutateRules()771 void InspectorCSSAgent::willMutateRules()
772 {
773     ++m_styleSheetsPendingMutation;
774 }
775 
didMutateRules(CSSStyleSheet * styleSheet)776 void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet)
777 {
778     --m_styleSheetsPendingMutation;
779     ASSERT(m_styleSheetsPendingMutation >= 0);
780 
781     if (!styleSheetEditInProgress()) {
782         Document* owner = styleSheet->ownerDocument();
783         if (owner)
784             owner->modifiedStyleSheet(styleSheet, RecalcStyleDeferred, FullStyleUpdate);
785     }
786 }
787 
willMutateStyle()788 void InspectorCSSAgent::willMutateStyle()
789 {
790     m_styleDeclarationPendingMutation = true;
791 }
792 
didMutateStyle(CSSStyleDeclaration * style,bool isInlineStyle)793 void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle)
794 {
795     ASSERT(m_styleDeclarationPendingMutation);
796     m_styleDeclarationPendingMutation = false;
797     if (!styleSheetEditInProgress() && !isInlineStyle) {
798         CSSStyleSheet* parentSheet = style->parentStyleSheet();
799         Document* owner = parentSheet ? parentSheet->ownerDocument() : 0;
800         if (owner)
801             owner->modifiedStyleSheet(parentSheet, RecalcStyleDeferred, FullStyleUpdate);
802     }
803 }
804 
didUpdateRegionLayout(Document * document,NamedFlow * namedFlow)805 void InspectorCSSAgent::didUpdateRegionLayout(Document* document, NamedFlow* namedFlow)
806 {
807     int documentNodeId = documentNodeWithRequestedFlowsId(document);
808     if (!documentNodeId)
809         return;
810 
811     if (!m_updateRegionLayoutTask)
812         m_updateRegionLayoutTask = adoptPtr(new UpdateRegionLayoutTask(this));
813     m_updateRegionLayoutTask->scheduleFor(namedFlow, documentNodeId);
814 }
815 
regionLayoutUpdated(NamedFlow * namedFlow,int documentNodeId)816 void InspectorCSSAgent::regionLayoutUpdated(NamedFlow* namedFlow, int documentNodeId)
817 {
818     if (namedFlow->flowState() == NamedFlow::FlowStateNull)
819         return;
820 
821     ErrorString errorString;
822     RefPtr<NamedFlow> protector(namedFlow);
823 
824     m_frontend->regionLayoutUpdated(buildObjectForNamedFlow(&errorString, namedFlow, documentNodeId));
825 }
826 
didChangeRegionOverset(Document * document,NamedFlow * namedFlow)827 void InspectorCSSAgent::didChangeRegionOverset(Document* document, NamedFlow* namedFlow)
828 {
829     int documentNodeId = documentNodeWithRequestedFlowsId(document);
830     if (!documentNodeId)
831         return;
832 
833     if (!m_changeRegionOversetTask)
834         m_changeRegionOversetTask = adoptPtr(new ChangeRegionOversetTask(this));
835     m_changeRegionOversetTask->scheduleFor(namedFlow, documentNodeId);
836 }
837 
regionOversetChanged(NamedFlow * namedFlow,int documentNodeId)838 void InspectorCSSAgent::regionOversetChanged(NamedFlow* namedFlow, int documentNodeId)
839 {
840     if (namedFlow->flowState() == NamedFlow::FlowStateNull)
841         return;
842 
843     ErrorString errorString;
844     RefPtr<NamedFlow> protector(namedFlow);
845 
846     m_frontend->regionOversetChanged(buildObjectForNamedFlow(&errorString, namedFlow, documentNodeId));
847 }
848 
activeStyleSheetsUpdated(Document * document,const StyleSheetVector & newSheets)849 void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document, const StyleSheetVector& newSheets)
850 {
851     if (styleSheetEditInProgress())
852         return;
853 
854     HashSet<CSSStyleSheet*> removedSheets;
855     for (CSSStyleSheetToInspectorStyleSheet::iterator it = m_cssStyleSheetToInspectorStyleSheet.begin(); it != m_cssStyleSheetToInspectorStyleSheet.end(); ++it) {
856         if (it->value->canBind() && (!it->key->ownerDocument() || it->key->ownerDocument() == document))
857             removedSheets.add(it->key);
858     }
859 
860     Vector<CSSStyleSheet*> newSheetsVector;
861     for (size_t i = 0, size = newSheets.size(); i < size; ++i) {
862         StyleSheet* newSheet = newSheets.at(i).get();
863         if (newSheet->isCSSStyleSheet()) {
864             StyleSheetAppender appender(newSheetsVector);
865             appender.run(toCSSStyleSheet(newSheet));
866         }
867     }
868 
869     HashSet<CSSStyleSheet*> addedSheets;
870     for (size_t i = 0; i < newSheetsVector.size(); ++i) {
871         CSSStyleSheet* newCSSSheet = newSheetsVector.at(i);
872         if (removedSheets.contains(newCSSSheet))
873             removedSheets.remove(newCSSSheet);
874         else
875             addedSheets.add(newCSSSheet);
876     }
877 
878     for (HashSet<CSSStyleSheet*>::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) {
879         RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(*it);
880         ASSERT(inspectorStyleSheet);
881         if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
882             String id = unbindStyleSheet(inspectorStyleSheet.get());
883             if (m_frontend)
884                 m_frontend->styleSheetRemoved(id);
885         }
886     }
887 
888     for (HashSet<CSSStyleSheet*>::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) {
889         if (!m_cssStyleSheetToInspectorStyleSheet.contains(*it)) {
890             InspectorStyleSheet* newStyleSheet = bindStyleSheet(*it);
891             if (m_frontend)
892                 m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo());
893         }
894     }
895 }
896 
frameDetachedFromParent(Frame * frame)897 void InspectorCSSAgent::frameDetachedFromParent(Frame* frame)
898 {
899     Document* document = frame->document();
900     if (!document)
901         return;
902     StyleSheetVector newSheets;
903     activeStyleSheetsUpdated(document, newSheets);
904 }
905 
forcePseudoState(Element * element,CSSSelector::PseudoType pseudoType)906 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
907 {
908     if (m_nodeIdToForcedPseudoState.isEmpty())
909         return false;
910 
911     int nodeId = m_domAgent->boundNodeId(element);
912     if (!nodeId)
913         return false;
914 
915     NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
916     if (it == m_nodeIdToForcedPseudoState.end())
917         return false;
918 
919     unsigned forcedPseudoState = it->value;
920     switch (pseudoType) {
921     case CSSSelector::PseudoActive:
922         return forcedPseudoState & PseudoActive;
923     case CSSSelector::PseudoFocus:
924         return forcedPseudoState & PseudoFocus;
925     case CSSSelector::PseudoHover:
926         return forcedPseudoState & PseudoHover;
927     case CSSSelector::PseudoVisited:
928         return forcedPseudoState & PseudoVisited;
929     default:
930         return false;
931     }
932 }
933 
getMatchedStylesForNode(ErrorString * errorString,int nodeId,const bool * includePseudo,const bool * includeInherited,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>> & matchedCSSRules,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>> & pseudoIdMatches,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>> & inheritedEntries)934 void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int nodeId, const bool* includePseudo, const bool* includeInherited, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> >& matchedCSSRules, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> >& pseudoIdMatches, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> >& inheritedEntries)
935 {
936     Element* element = elementForId(errorString, nodeId);
937     if (!element)
938         return;
939 
940     Element* originalElement = element;
941     PseudoId elementPseudoId = element->pseudoId();
942     if (elementPseudoId)
943         element = element->parentOrShadowHostElement();
944 
945     Document* ownerDocument = element->ownerDocument();
946     // A non-active document has no styles.
947     if (!ownerDocument->isActive())
948         return;
949 
950     // FIXME: It's really gross for the inspector to reach in and access StyleResolver
951     // directly here. We need to provide the Inspector better APIs to get this information
952     // without grabbing at internal style classes!
953 
954     // Matched rules.
955     StyleResolver& styleResolver = ownerDocument->ensureStyleResolver();
956     // FIXME: This code should not pass DoNotIncludeStyleSheetInCSSOMWrapper. All CSSOMWrappers should always have a parent sheet or rule.
957     RefPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules, DoNotIncludeStyleSheetInCSSOMWrapper);
958     matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), styleResolver, originalElement);
959 
960     // Pseudo elements.
961     if (!elementPseudoId && (!includePseudo || *includePseudo)) {
962         RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create();
963         for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
964             RefPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules, DoNotIncludeStyleSheetInCSSOMWrapper);
965             if (matchedRules && matchedRules->length()) {
966                 RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create()
967                     .setPseudoId(static_cast<int>(pseudoId))
968                     .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), styleResolver, element));
969                 pseudoElements->addItem(matches.release());
970             }
971         }
972 
973         pseudoIdMatches = pseudoElements.release();
974     }
975 
976     // Inherited styles.
977     if (!elementPseudoId && (!includeInherited || *includeInherited)) {
978         RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create();
979         Element* parentElement = element->parentElement();
980         while (parentElement) {
981             StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver();
982             RefPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules, DoNotIncludeStyleSheetInCSSOMWrapper);
983             RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create()
984                 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), styleResolver, parentElement));
985             if (parentElement->style() && parentElement->style()->length()) {
986                 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
987                 if (styleSheet)
988                     entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
989             }
990 
991             entries->addItem(entry.release());
992             parentElement = parentElement->parentElement();
993         }
994 
995         inheritedEntries = entries.release();
996     }
997 }
998 
getInlineStylesForNode(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::CSS::CSSStyle> & inlineStyle,RefPtr<TypeBuilder::CSS::CSSStyle> & attributesStyle)999 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle)
1000 {
1001     Element* element = elementForId(errorString, nodeId);
1002     if (!element)
1003         return;
1004 
1005     InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
1006     if (!styleSheet)
1007         return;
1008 
1009     inlineStyle = styleSheet->buildObjectForStyle(element->style());
1010     RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element);
1011     attributesStyle = attributes ? attributes.release() : 0;
1012 }
1013 
getComputedStyleForNode(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>> & style)1014 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style)
1015 {
1016     Node* node = m_domAgent->assertNode(errorString, nodeId);
1017     if (!node)
1018         return;
1019 
1020     RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
1021     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
1022     style = inspectorStyle->buildArrayForComputedStyle();
1023 }
1024 
collectPlatformFontsForRenderer(RenderText * renderer,HashCountedSet<String> * fontStats)1025 void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats)
1026 {
1027     for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) {
1028         RenderStyle* style = renderer->style(box->isFirstLineStyle());
1029         const Font& font = style->font();
1030         TextRun run = box->constructTextRunForInspector(style, font);
1031         WidthIterator it(&font, run, 0, false);
1032         GlyphBuffer glyphBuffer;
1033         it.advance(run.length(), &glyphBuffer);
1034         for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
1035             String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName();
1036             if (familyName.isNull())
1037                 familyName = "";
1038             fontStats->add(familyName);
1039         }
1040     }
1041 }
1042 
getPlatformFontsForNode(ErrorString * errorString,int nodeId,String * cssFamilyName,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>> & platformFonts)1043 void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId,
1044     String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts)
1045 {
1046     Node* node = m_domAgent->assertNode(errorString, nodeId);
1047     if (!node)
1048         return;
1049 
1050     RefPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
1051     *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily);
1052 
1053     Vector<Node*> textNodes;
1054     if (node->nodeType() == Node::TEXT_NODE) {
1055         if (node->renderer())
1056             textNodes.append(node);
1057     } else {
1058         for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
1059             if (child->nodeType() == Node::TEXT_NODE && child->renderer())
1060                 textNodes.append(child);
1061         }
1062     }
1063 
1064     HashCountedSet<String> fontStats;
1065     for (size_t i = 0; i < textNodes.size(); ++i) {
1066         RenderText* renderer = toRenderText(textNodes[i]->renderer());
1067         collectPlatformFontsForRenderer(renderer, &fontStats);
1068         if (renderer->isTextFragment()) {
1069             RenderTextFragment* textFragment = toRenderTextFragment(renderer);
1070             if (textFragment->firstLetter()) {
1071                 RenderObject* firstLetter = textFragment->firstLetter();
1072                 for (RenderObject* current = firstLetter->firstChild(); current; current = current->nextSibling()) {
1073                     if (current->isText())
1074                         collectPlatformFontsForRenderer(toRenderText(current), &fontStats);
1075                 }
1076             }
1077         }
1078     }
1079 
1080     platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create();
1081     for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) {
1082         RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create()
1083             .setFamilyName(it->key)
1084             .setGlyphCount(it->value);
1085         platformFonts->addItem(platformFont);
1086     }
1087 }
1088 
getAllStyleSheets(ErrorString *,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSStyleSheetHeader>> & styleInfos)1089 void InspectorCSSAgent::getAllStyleSheets(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSStyleSheetHeader> >& styleInfos)
1090 {
1091     styleInfos = TypeBuilder::Array<TypeBuilder::CSS::CSSStyleSheetHeader>::create();
1092     Vector<InspectorStyleSheet*> styleSheets;
1093     collectAllStyleSheets(styleSheets);
1094     for (size_t i = 0; i < styleSheets.size(); ++i)
1095         styleInfos->addItem(styleSheets.at(i)->buildObjectForStyleSheetInfo());
1096 }
1097 
getStyleSheet(ErrorString * errorString,const String & styleSheetId,RefPtr<TypeBuilder::CSS::CSSStyleSheetBody> & styleSheetObject)1098 void InspectorCSSAgent::getStyleSheet(ErrorString* errorString, const String& styleSheetId, RefPtr<TypeBuilder::CSS::CSSStyleSheetBody>& styleSheetObject)
1099 {
1100     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
1101     if (!inspectorStyleSheet)
1102         return;
1103 
1104     Document* doc = inspectorStyleSheet->pageStyleSheet() ? inspectorStyleSheet->pageStyleSheet()->ownerDocument() : 0;
1105     if (!doc)
1106         return;
1107 
1108     RefPtr<TypeBuilder::CSS::CSSStyleSheetBody> result = TypeBuilder::CSS::CSSStyleSheetBody::create()
1109         .setStyleSheetId(styleSheetId)
1110         .setRules(buildArrayForRuleList(inspectorStyleSheet->pageStyleSheet()->rules().get(), doc->ensureStyleResolver()));
1111 
1112     bool success = inspectorStyleSheet->fillObjectForStyleSheet(result);
1113     if (success)
1114         styleSheetObject = result;
1115 }
1116 
getStyleSheetText(ErrorString * errorString,const String & styleSheetId,String * result)1117 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
1118 {
1119     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
1120     if (!inspectorStyleSheet)
1121         return;
1122 
1123     inspectorStyleSheet->getText(result);
1124 }
1125 
setStyleSheetText(ErrorString * errorString,const String & styleSheetId,const String & text)1126 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
1127 {
1128     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
1129     if (!inspectorStyleSheet)
1130         return;
1131 
1132     TrackExceptionState exceptionState;
1133     m_domAgent->history()->perform(adoptPtr(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState);
1134     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1135 }
1136 
setStyleText(ErrorString * errorString,const RefPtr<JSONObject> & fullStyleId,const String & text,RefPtr<TypeBuilder::CSS::CSSStyle> & result)1137 void InspectorCSSAgent::setStyleText(ErrorString* errorString, const RefPtr<JSONObject>& fullStyleId, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
1138 {
1139     InspectorCSSId compoundId(fullStyleId);
1140     ASSERT(!compoundId.isEmpty());
1141 
1142     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
1143     if (!inspectorStyleSheet)
1144         return;
1145 
1146     TrackExceptionState exceptionState;
1147     m_domAgent->history()->perform(adoptPtr(new SetStyleTextAction(inspectorStyleSheet, compoundId, text)), exceptionState);
1148     if (!exceptionState.hadException())
1149         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
1150     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1151 }
1152 
setPropertyText(ErrorString * errorString,const RefPtr<JSONObject> & fullStyleId,int propertyIndex,const String & text,bool overwrite,RefPtr<TypeBuilder::CSS::CSSStyle> & result)1153 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const RefPtr<JSONObject>& fullStyleId, int propertyIndex, const String& text, bool overwrite, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
1154 {
1155     InspectorCSSId compoundId(fullStyleId);
1156     ASSERT(!compoundId.isEmpty());
1157 
1158     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
1159     if (!inspectorStyleSheet)
1160         return;
1161 
1162     TrackExceptionState exceptionState;
1163     bool success = m_domAgent->history()->perform(adoptPtr(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState);
1164     if (success)
1165         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
1166     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1167 }
1168 
toggleProperty(ErrorString * errorString,const RefPtr<JSONObject> & fullStyleId,int propertyIndex,bool disable,RefPtr<TypeBuilder::CSS::CSSStyle> & result)1169 void InspectorCSSAgent::toggleProperty(ErrorString* errorString, const RefPtr<JSONObject>& fullStyleId, int propertyIndex, bool disable, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
1170 {
1171     InspectorCSSId compoundId(fullStyleId);
1172     ASSERT(!compoundId.isEmpty());
1173 
1174     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
1175     if (!inspectorStyleSheet)
1176         return;
1177 
1178     TrackExceptionState exceptionState;
1179     bool success = m_domAgent->history()->perform(adoptPtr(new TogglePropertyAction(inspectorStyleSheet, compoundId, propertyIndex, disable)), exceptionState);
1180     if (success)
1181         result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
1182     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1183 }
1184 
setRuleSelector(ErrorString * errorString,const RefPtr<JSONObject> & fullRuleId,const String & selector,RefPtr<TypeBuilder::CSS::CSSRule> & result)1185 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const RefPtr<JSONObject>& fullRuleId, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
1186 {
1187     InspectorCSSId compoundId(fullRuleId);
1188     ASSERT(!compoundId.isEmpty());
1189 
1190     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
1191     if (!inspectorStyleSheet)
1192         return;
1193 
1194     TrackExceptionState exceptionState;
1195     bool success = m_domAgent->history()->perform(adoptPtr(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState);
1196 
1197     if (success) {
1198         CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId);
1199         result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
1200     }
1201     *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1202 }
1203 
addRule(ErrorString * errorString,const int contextNodeId,const String & selector,RefPtr<TypeBuilder::CSS::CSSRule> & result)1204 void InspectorCSSAgent::addRule(ErrorString* errorString, const int contextNodeId, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
1205 {
1206     Node* node = m_domAgent->assertNode(errorString, contextNodeId);
1207     if (!node)
1208         return;
1209 
1210     InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(&node->document(), true);
1211     if (!inspectorStyleSheet) {
1212         *errorString = "No target stylesheet found";
1213         return;
1214     }
1215 
1216     TrackExceptionState exceptionState;
1217     OwnPtr<AddRuleAction> action = adoptPtr(new AddRuleAction(inspectorStyleSheet, selector));
1218     AddRuleAction* rawAction = action.get();
1219     bool success = m_domAgent->history()->perform(action.release(), exceptionState);
1220     if (!success) {
1221         *errorString = InspectorDOMAgent::toErrorString(exceptionState);
1222         return;
1223     }
1224 
1225     InspectorCSSId ruleId = rawAction->newRuleId();
1226     CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
1227     result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
1228 }
1229 
getSupportedCSSProperties(ErrorString *,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSPropertyInfo>> & cssProperties)1230 void InspectorCSSAgent::getSupportedCSSProperties(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSPropertyInfo> >& cssProperties)
1231 {
1232     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSPropertyInfo> > properties = TypeBuilder::Array<TypeBuilder::CSS::CSSPropertyInfo>::create();
1233     for (int i = firstCSSProperty; i <= lastCSSProperty; ++i) {
1234         CSSPropertyID id = convertToCSSPropertyID(i);
1235         RefPtr<TypeBuilder::CSS::CSSPropertyInfo> property = TypeBuilder::CSS::CSSPropertyInfo::create()
1236             .setName(getPropertyNameString(id));
1237 
1238         const StylePropertyShorthand& shorthand = shorthandForProperty(id);
1239         if (!shorthand.length()) {
1240             properties->addItem(property.release());
1241             continue;
1242         }
1243         RefPtr<TypeBuilder::Array<String> > longhands = TypeBuilder::Array<String>::create();
1244         for (unsigned j = 0; j < shorthand.length(); ++j) {
1245             CSSPropertyID longhandID = shorthand.properties()[j];
1246             longhands->addItem(getPropertyNameString(longhandID));
1247         }
1248         property->setLonghands(longhands);
1249         properties->addItem(property.release());
1250     }
1251     cssProperties = properties.release();
1252 }
1253 
forcePseudoState(ErrorString * errorString,int nodeId,const RefPtr<JSONArray> & forcedPseudoClasses)1254 void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses)
1255 {
1256     Element* element = m_domAgent->assertElement(errorString, nodeId);
1257     if (!element)
1258         return;
1259 
1260     unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get());
1261     NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
1262     unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value;
1263     bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
1264     if (!needStyleRecalc)
1265         return;
1266 
1267     if (forcedPseudoState)
1268         m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
1269     else
1270         m_nodeIdToForcedPseudoState.remove(nodeId);
1271     element->ownerDocument()->setNeedsStyleRecalc();
1272 }
1273 
getNamedFlowCollection(ErrorString * errorString,int documentNodeId,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::NamedFlow>> & result)1274 void InspectorCSSAgent::getNamedFlowCollection(ErrorString* errorString, int documentNodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::NamedFlow> >& result)
1275 {
1276     Document* document = m_domAgent->assertDocument(errorString, documentNodeId);
1277     if (!document)
1278         return;
1279 
1280     m_namedFlowCollectionsRequested.add(documentNodeId);
1281 
1282     Vector<RefPtr<NamedFlow> > namedFlowsVector = document->namedFlows()->namedFlows();
1283     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::NamedFlow> > namedFlows = TypeBuilder::Array<TypeBuilder::CSS::NamedFlow>::create();
1284 
1285     for (Vector<RefPtr<NamedFlow> >::iterator it = namedFlowsVector.begin(); it != namedFlowsVector.end(); ++it)
1286         namedFlows->addItem(buildObjectForNamedFlow(errorString, it->get(), documentNodeId));
1287 
1288     result = namedFlows.release();
1289 }
1290 
buildMediaObject(const MediaList * media,MediaListSource mediaListSource,const String & sourceURL,CSSStyleSheet * parentStyleSheet)1291 PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet)
1292 {
1293     // Make certain compilers happy by initializing |source| up-front.
1294     TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
1295     switch (mediaListSource) {
1296     case MediaListSourceMediaRule:
1297         source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
1298         break;
1299     case MediaListSourceImportRule:
1300         source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
1301         break;
1302     case MediaListSourceLinkedSheet:
1303         source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
1304         break;
1305     case MediaListSourceInlineSheet:
1306         source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
1307         break;
1308     }
1309 
1310     RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
1311         .setText(media->mediaText())
1312         .setSource(source);
1313 
1314     if (parentStyleSheet && mediaListSource != MediaListSourceLinkedSheet) {
1315         if (InspectorStyleSheet* inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet))
1316             mediaObject->setParentStyleSheetId(inspectorStyleSheet->id());
1317     }
1318     if (!sourceURL.isEmpty()) {
1319         mediaObject->setSourceURL(sourceURL);
1320 
1321         CSSRule* parentRule = media->parentRule();
1322         if (!parentRule)
1323             return mediaObject.release();
1324         InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet());
1325         RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule);
1326         if (mediaRange)
1327             mediaObject->setRange(mediaRange);
1328     }
1329     return mediaObject.release();
1330 }
1331 
buildMediaListChain(CSSRule * rule)1332 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule)
1333 {
1334     if (!rule)
1335         return 0;
1336     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
1337     bool hasItems = false;
1338     MediaList* mediaList;
1339     CSSRule* parentRule = rule;
1340     String sourceURL;
1341     while (parentRule) {
1342         CSSStyleSheet* parentStyleSheet = 0;
1343         bool isMediaRule = true;
1344         if (parentRule->type() == CSSRule::MEDIA_RULE) {
1345             CSSMediaRule* mediaRule = toCSSMediaRule(parentRule);
1346             mediaList = mediaRule->media();
1347             parentStyleSheet = mediaRule->parentStyleSheet();
1348         } else if (parentRule->type() == CSSRule::IMPORT_RULE) {
1349             CSSImportRule* importRule = toCSSImportRule(parentRule);
1350             mediaList = importRule->media();
1351             parentStyleSheet = importRule->parentStyleSheet();
1352             isMediaRule = false;
1353         } else {
1354             mediaList = 0;
1355         }
1356 
1357         if (parentStyleSheet) {
1358             sourceURL = parentStyleSheet->contents()->baseURL();
1359             if (sourceURL.isEmpty())
1360                 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
1361         } else {
1362             sourceURL = "";
1363         }
1364 
1365         if (mediaList && mediaList->length()) {
1366             mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet));
1367             hasItems = true;
1368         }
1369 
1370         if (parentRule->parentRule()) {
1371             parentRule = parentRule->parentRule();
1372         } else {
1373             CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
1374             while (styleSheet) {
1375                 mediaList = styleSheet->media();
1376                 if (mediaList && mediaList->length()) {
1377                     Document* doc = styleSheet->ownerDocument();
1378                     if (doc)
1379                         sourceURL = doc->url();
1380                     else if (!styleSheet->contents()->baseURL().isEmpty())
1381                         sourceURL = styleSheet->contents()->baseURL();
1382                     else
1383                         sourceURL = "";
1384                     mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet));
1385                     hasItems = true;
1386                 }
1387                 parentRule = styleSheet->ownerRule();
1388                 if (parentRule)
1389                     break;
1390                 styleSheet = styleSheet->parentStyleSheet();
1391             }
1392         }
1393     }
1394     return hasItems ? mediaArray : 0;
1395 }
1396 
asInspectorStyleSheet(Element * element)1397 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
1398 {
1399     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1400     if (it != m_nodeToInspectorStyleSheet.end())
1401         return it->value.get();
1402 
1403     CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0;
1404     if (!style)
1405         return 0;
1406 
1407     String newStyleSheetId = String::number(m_lastStyleSheetId++);
1408     RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(m_pageAgent, m_resourceAgent, newStyleSheetId, element, TypeBuilder::CSS::StyleSheetOrigin::Regular, this);
1409     m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet);
1410     m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
1411     return inspectorStyleSheet.get();
1412 }
1413 
elementForId(ErrorString * errorString,int nodeId)1414 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
1415 {
1416     Node* node = m_domAgent->nodeForId(nodeId);
1417     if (!node) {
1418         *errorString = "No node with given id found";
1419         return 0;
1420     }
1421     if (!node->isElementNode()) {
1422         *errorString = "Not an element node";
1423         return 0;
1424     }
1425     return toElement(node);
1426 }
1427 
documentNodeWithRequestedFlowsId(Document * document)1428 int InspectorCSSAgent::documentNodeWithRequestedFlowsId(Document* document)
1429 {
1430     int documentNodeId = m_domAgent->boundNodeId(document);
1431     if (!documentNodeId || !m_namedFlowCollectionsRequested.contains(documentNodeId))
1432         return 0;
1433 
1434     return documentNodeId;
1435 }
1436 
collectAllStyleSheets(Vector<InspectorStyleSheet * > & result)1437 void InspectorCSSAgent::collectAllStyleSheets(Vector<InspectorStyleSheet*>& result)
1438 {
1439     Vector<Document*> documents = m_domAgent->documents();
1440     for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it) {
1441         StyleSheetList* list = (*it)->styleSheets();
1442         for (unsigned i = 0; i < list->length(); ++i) {
1443             StyleSheet* styleSheet = list->item(i);
1444             if (styleSheet->isCSSStyleSheet())
1445                 collectStyleSheets(toCSSStyleSheet(styleSheet), result);
1446         }
1447     }
1448 }
1449 
collectStyleSheets(CSSStyleSheet * styleSheet,Vector<InspectorStyleSheet * > & result)1450 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, Vector<InspectorStyleSheet*>& result)
1451 {
1452     InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(styleSheet);
1453     result.append(inspectorStyleSheet);
1454     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
1455         CSSRule* rule = styleSheet->item(i);
1456         if (rule->type() == CSSRule::IMPORT_RULE) {
1457             CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
1458             if (importedStyleSheet)
1459                 collectStyleSheets(importedStyleSheet, result);
1460         }
1461     }
1462 }
1463 
bindStyleSheet(CSSStyleSheet * styleSheet)1464 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
1465 {
1466     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
1467     if (!inspectorStyleSheet) {
1468         String id = String::number(m_lastStyleSheetId++);
1469         Document* document = styleSheet->ownerDocument();
1470         inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
1471         m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
1472         m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
1473         if (m_creatingViaInspectorStyleSheet)
1474             m_documentToInspectorStyleSheet.add(document, inspectorStyleSheet);
1475     }
1476     return inspectorStyleSheet.get();
1477 }
1478 
unbindStyleSheet(InspectorStyleSheet * inspectorStyleSheet)1479 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
1480 {
1481     String id = inspectorStyleSheet->id();
1482     m_idToInspectorStyleSheet.remove(id);
1483     if (inspectorStyleSheet->pageStyleSheet())
1484         m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
1485     return id;
1486 }
1487 
viaInspectorStyleSheet(Document * document,bool createIfAbsent)1488 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
1489 {
1490     if (!document) {
1491         ASSERT(!createIfAbsent);
1492         return 0;
1493     }
1494 
1495     if (!document->isHTMLDocument() && !document->isSVGDocument())
1496         return 0;
1497 
1498     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document);
1499     if (inspectorStyleSheet || !createIfAbsent)
1500         return inspectorStyleSheet.get();
1501 
1502     TrackExceptionState exceptionState;
1503     RefPtr<Element> styleElement = document->createElement("style", exceptionState);
1504     if (!exceptionState.hadException())
1505         styleElement->setAttribute("type", "text/css", exceptionState);
1506     if (!exceptionState.hadException()) {
1507         ContainerNode* targetNode;
1508         // HEAD is absent in ImageDocuments, for example.
1509         if (document->head())
1510             targetNode = document->head();
1511         else if (document->body())
1512             targetNode = document->body();
1513         else
1514             return 0;
1515 
1516         InlineStyleOverrideScope overrideScope(document);
1517         m_creatingViaInspectorStyleSheet = true;
1518         targetNode->appendChild(styleElement, exceptionState);
1519         // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation.
1520         // We just need to pick the respective InspectorStyleSheet from m_documentToInspectorStyleSheet.
1521         m_creatingViaInspectorStyleSheet = false;
1522     }
1523     if (exceptionState.hadException())
1524         return 0;
1525 
1526     return m_documentToInspectorStyleSheet.get(document);
1527 }
1528 
assertStyleSheetForId(ErrorString * errorString,const String & styleSheetId)1529 InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1530 {
1531     IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
1532     if (it == m_idToInspectorStyleSheet.end()) {
1533         *errorString = "No style sheet with given id found";
1534         return 0;
1535     }
1536     return it->value.get();
1537 }
1538 
detectOrigin(CSSStyleSheet * pageStyleSheet,Document * ownerDocument)1539 TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
1540 {
1541     if (m_creatingViaInspectorStyleSheet)
1542         return TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1543 
1544     TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular;
1545     if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
1546         origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent;
1547     else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
1548         origin = TypeBuilder::CSS::StyleSheetOrigin::User;
1549     else {
1550         InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
1551         if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
1552             origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1553     }
1554     return origin;
1555 }
1556 
buildObjectForRule(CSSStyleRule * rule,StyleResolver & styleResolver)1557 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule, StyleResolver& styleResolver)
1558 {
1559     if (!rule)
1560         return 0;
1561 
1562     // CSSRules returned by StyleResolver::cssRulesForElement lack parent pointers since that infomation is not cheaply available.
1563     // Since the inspector wants to walk the parent chain, we construct the full wrappers here.
1564     // FIXME: This could be factored better. StyleResolver::cssRulesForElement should return a StyleRule vector, not a CSSRuleList.
1565     if (!rule->parentStyleSheet()) {
1566         rule = styleResolver.inspectorCSSOMWrappers().getWrapperForRuleInSheets(rule->styleRule(), styleResolver.document().styleEngine());
1567         if (!rule)
1568             return 0;
1569     }
1570     return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule));
1571 }
1572 
buildArrayForRuleList(CSSRuleList * ruleList,StyleResolver & styleResolver)1573 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > InspectorCSSAgent::buildArrayForRuleList(CSSRuleList* ruleList, StyleResolver& styleResolver)
1574 {
1575     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSRule>::create();
1576     if (!ruleList)
1577         return result.release();
1578 
1579     RefPtr<CSSRuleList> refRuleList = ruleList;
1580     CSSRuleVector rules;
1581     InspectorStyleSheet::collectFlatRules(refRuleList, &rules);
1582 
1583     for (unsigned i = 0, size = rules.size(); i < size; ++i) {
1584         CSSStyleRule* styleRule = asCSSStyleRule(rules.at(i).get());
1585         if (!styleRule)
1586             continue;
1587         result->addItem(buildObjectForRule(styleRule, styleResolver));
1588     }
1589 
1590     return result.release();
1591 }
1592 
matchesPseudoElement(const CSSSelector * selector,PseudoId elementPseudoId)1593 static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId)
1594 {
1595     // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector."
1596     // As such, check the last selector in the tag history.
1597     for (; !selector->isLastInTagHistory(); ++selector) { }
1598     PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO;
1599 
1600     // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements.
1601     // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too.
1602     return selectorPseudoId == elementPseudoId;
1603 }
1604 
buildArrayForMatchedRuleList(CSSRuleList * ruleList,StyleResolver & styleResolver,Element * element)1605 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, StyleResolver& styleResolver, Element* element)
1606 {
1607     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create();
1608     if (!ruleList)
1609         return result.release();
1610 
1611     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1612         CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
1613         RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule, styleResolver);
1614         if (!ruleObject)
1615             continue;
1616         RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create();
1617         const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1618         long index = 0;
1619         PseudoId elementPseudoId = element->pseudoId();
1620         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
1621             const CSSSelector* firstTagHistorySelector = selector;
1622             bool matched = false;
1623             if (elementPseudoId)
1624                 matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|.
1625             matched |= element->webkitMatchesSelector(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION);
1626             if (matched)
1627                 matchingSelectors->addItem(index);
1628             ++index;
1629         }
1630         RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create()
1631             .setRule(ruleObject.release())
1632             .setMatchingSelectors(matchingSelectors.release());
1633         result->addItem(match);
1634     }
1635 
1636     return result;
1637 }
1638 
buildObjectForAttributesStyle(Element * element)1639 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
1640 {
1641     if (!element->isStyledElement())
1642         return 0;
1643 
1644     // FIXME: Ugliness below.
1645     StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle());
1646     if (!attributeStyle)
1647         return 0;
1648 
1649     MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle);
1650 
1651     RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0);
1652     return inspectorStyle->buildObjectForStyle();
1653 }
1654 
buildArrayForRegions(ErrorString * errorString,PassRefPtr<NodeList> regionList,int documentNodeId)1655 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Region> > InspectorCSSAgent::buildArrayForRegions(ErrorString* errorString, PassRefPtr<NodeList> regionList, int documentNodeId)
1656 {
1657     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Region> > regions = TypeBuilder::Array<TypeBuilder::CSS::Region>::create();
1658 
1659     for (unsigned i = 0; i < regionList->length(); ++i) {
1660         TypeBuilder::CSS::Region::RegionOverset::Enum regionOverset;
1661 
1662         switch (toElement(regionList->item(i))->renderRegion()->regionOversetState()) {
1663         case RegionFit:
1664             regionOverset = TypeBuilder::CSS::Region::RegionOverset::Fit;
1665             break;
1666         case RegionEmpty:
1667             regionOverset = TypeBuilder::CSS::Region::RegionOverset::Empty;
1668             break;
1669         case RegionOverset:
1670             regionOverset = TypeBuilder::CSS::Region::RegionOverset::Overset;
1671             break;
1672         case RegionUndefined:
1673             continue;
1674         default:
1675             ASSERT_NOT_REACHED();
1676             continue;
1677         }
1678 
1679         RefPtr<TypeBuilder::CSS::Region> region = TypeBuilder::CSS::Region::create()
1680             .setRegionOverset(regionOverset)
1681             // documentNodeId was previously asserted
1682             .setNodeId(m_domAgent->pushNodeToFrontend(errorString, documentNodeId, regionList->item(i)));
1683 
1684         regions->addItem(region);
1685     }
1686 
1687     return regions.release();
1688 }
1689 
buildObjectForNamedFlow(ErrorString * errorString,NamedFlow * webkitNamedFlow,int documentNodeId)1690 PassRefPtr<TypeBuilder::CSS::NamedFlow> InspectorCSSAgent::buildObjectForNamedFlow(ErrorString* errorString, NamedFlow* webkitNamedFlow, int documentNodeId)
1691 {
1692     RefPtr<NodeList> contentList = webkitNamedFlow->getContent();
1693     RefPtr<TypeBuilder::Array<int> > content = TypeBuilder::Array<int>::create();
1694 
1695     for (unsigned i = 0; i < contentList->length(); ++i) {
1696         // documentNodeId was previously asserted
1697         content->addItem(m_domAgent->pushNodeToFrontend(errorString, documentNodeId, contentList->item(i)));
1698     }
1699 
1700     RefPtr<TypeBuilder::CSS::NamedFlow> namedFlow = TypeBuilder::CSS::NamedFlow::create()
1701         .setDocumentNodeId(documentNodeId)
1702         .setName(webkitNamedFlow->name().string())
1703         .setOverset(webkitNamedFlow->overset())
1704         .setContent(content)
1705         .setRegions(buildArrayForRegions(errorString, webkitNamedFlow->getRegions(), documentNodeId));
1706 
1707     return namedFlow.release();
1708 }
1709 
didRemoveDocument(Document * document)1710 void InspectorCSSAgent::didRemoveDocument(Document* document)
1711 {
1712     if (document)
1713         m_documentToInspectorStyleSheet.remove(document);
1714 }
1715 
didRemoveDOMNode(Node * node)1716 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
1717 {
1718     if (!node)
1719         return;
1720 
1721     int nodeId = m_domAgent->boundNodeId(node);
1722     if (nodeId)
1723         m_nodeIdToForcedPseudoState.remove(nodeId);
1724 
1725     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
1726     if (it == m_nodeToInspectorStyleSheet.end())
1727         return;
1728 
1729     m_idToInspectorStyleSheet.remove(it->value->id());
1730     m_nodeToInspectorStyleSheet.remove(node);
1731 }
1732 
didModifyDOMAttr(Element * element)1733 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
1734 {
1735     if (!element)
1736         return;
1737 
1738     NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1739     if (it == m_nodeToInspectorStyleSheet.end())
1740         return;
1741 
1742     it->value->didModifyElementAttribute();
1743 }
1744 
styleSheetChanged(InspectorStyleSheet * styleSheet)1745 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheet* styleSheet)
1746 {
1747     if (m_frontend)
1748         m_frontend->styleSheetChanged(styleSheet->id());
1749 }
1750 
willReparseStyleSheet()1751 void InspectorCSSAgent::willReparseStyleSheet()
1752 {
1753     ASSERT(!m_isSettingStyleSheetText);
1754     m_isSettingStyleSheetText = true;
1755 }
1756 
didReparseStyleSheet()1757 void InspectorCSSAgent::didReparseStyleSheet()
1758 {
1759     ASSERT(m_isSettingStyleSheetText);
1760     m_isSettingStyleSheetText = false;
1761 }
1762 
resetPseudoStates()1763 void InspectorCSSAgent::resetPseudoStates()
1764 {
1765     HashSet<Document*> documentsToChange;
1766     for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
1767         Element* element = toElement(m_domAgent->nodeForId(it->key));
1768         if (element && element->ownerDocument())
1769             documentsToChange.add(element->ownerDocument());
1770     }
1771 
1772     m_nodeIdToForcedPseudoState.clear();
1773     for (HashSet<Document*>::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
1774         (*it)->setNeedsStyleRecalc();
1775 }
1776 
1777 } // namespace WebCore
1778 
1779