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