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 "bindings/v8/ExceptionState.h"
29 #include "bindings/v8/ExceptionStatePlaceholder.h"
30 #include "core/CSSPropertyNames.h"
31 #include "core/InspectorTypeBuilder.h"
32 #include "core/StylePropertyShorthand.h"
33 #include "core/css/CSSComputedStyleDeclaration.h"
34 #include "core/css/CSSDefaultStyleSheets.h"
35 #include "core/css/CSSImportRule.h"
36 #include "core/css/CSSMediaRule.h"
37 #include "core/css/CSSRule.h"
38 #include "core/css/CSSRuleList.h"
39 #include "core/css/CSSStyleRule.h"
40 #include "core/css/CSSStyleSheet.h"
41 #include "core/css/MediaList.h"
42 #include "core/css/MediaQuery.h"
43 #include "core/css/MediaValues.h"
44 #include "core/css/StylePropertySet.h"
45 #include "core/css/StyleRule.h"
46 #include "core/css/StyleSheet.h"
47 #include "core/css/StyleSheetContents.h"
48 #include "core/css/StyleSheetList.h"
49 #include "core/css/resolver/StyleResolver.h"
50 #include "core/dom/Node.h"
51 #include "core/frame/LocalFrame.h"
52 #include "core/html/HTMLHeadElement.h"
53 #include "core/html/VoidCallback.h"
54 #include "core/inspector/InspectorHistory.h"
55 #include "core/inspector/InspectorPageAgent.h"
56 #include "core/inspector/InspectorResourceAgent.h"
57 #include "core/inspector/InspectorResourceContentLoader.h"
58 #include "core/inspector/InspectorState.h"
59 #include "core/inspector/InstrumentingAgents.h"
60 #include "core/loader/DocumentLoader.h"
61 #include "core/page/Page.h"
62 #include "core/rendering/InlineTextBox.h"
63 #include "core/rendering/RenderObject.h"
64 #include "core/rendering/RenderText.h"
65 #include "core/rendering/RenderTextFragment.h"
66 #include "platform/fonts/Font.h"
67 #include "platform/fonts/GlyphBuffer.h"
68 #include "platform/fonts/WidthIterator.h"
69 #include "platform/text/TextRun.h"
70 #include "wtf/CurrentTime.h"
71 #include "wtf/text/CString.h"
72 #include "wtf/text/StringConcatenate.h"
73
74 namespace CSSAgentState {
75 static const char cssAgentEnabled[] = "cssAgentEnabled";
76 }
77
78 typedef WebCore::InspectorBackendDispatcher::CSSCommandHandler::EnableCallback EnableCallback;
79
80 namespace WebCore {
81
82 enum ForcePseudoClassFlags {
83 PseudoNone = 0,
84 PseudoHover = 1 << 0,
85 PseudoFocus = 1 << 1,
86 PseudoActive = 1 << 2,
87 PseudoVisited = 1 << 3
88 };
89
computePseudoClassMask(JSONArray * pseudoClassArray)90 static unsigned computePseudoClassMask(JSONArray* pseudoClassArray)
91 {
92 DEFINE_STATIC_LOCAL(String, active, ("active"));
93 DEFINE_STATIC_LOCAL(String, hover, ("hover"));
94 DEFINE_STATIC_LOCAL(String, focus, ("focus"));
95 DEFINE_STATIC_LOCAL(String, visited, ("visited"));
96 if (!pseudoClassArray || !pseudoClassArray->length())
97 return PseudoNone;
98
99 unsigned result = PseudoNone;
100 for (size_t i = 0; i < pseudoClassArray->length(); ++i) {
101 RefPtr<JSONValue> pseudoClassValue = pseudoClassArray->get(i);
102 String pseudoClass;
103 bool success = pseudoClassValue->asString(&pseudoClass);
104 if (!success)
105 continue;
106 if (pseudoClass == active)
107 result |= PseudoActive;
108 else if (pseudoClass == hover)
109 result |= PseudoHover;
110 else if (pseudoClass == focus)
111 result |= PseudoFocus;
112 else if (pseudoClass == visited)
113 result |= PseudoVisited;
114 }
115
116 return result;
117 }
118
119 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
120 WTF_MAKE_NONCOPYABLE(StyleSheetAction);
121 public:
StyleSheetAction(const String & name)122 StyleSheetAction(const String& name)
123 : InspectorHistory::Action(name)
124 {
125 }
126 };
127
128 class InspectorCSSAgent::InspectorResourceContentLoaderCallback FINAL : public VoidCallback {
129 public:
130 InspectorResourceContentLoaderCallback(InspectorCSSAgent*, PassRefPtr<EnableCallback>);
131 virtual void handleEvent() OVERRIDE;
132
133 private:
134 InspectorCSSAgent* m_cssAgent;
135 RefPtr<EnableCallback> m_callback;
136 };
137
InspectorResourceContentLoaderCallback(InspectorCSSAgent * cssAgent,PassRefPtr<EnableCallback> callback)138 InspectorCSSAgent::InspectorResourceContentLoaderCallback::InspectorResourceContentLoaderCallback(InspectorCSSAgent* cssAgent, PassRefPtr<EnableCallback> callback)
139 : m_cssAgent(cssAgent)
140 , m_callback(callback)
141 {
142 }
143
handleEvent()144 void InspectorCSSAgent::InspectorResourceContentLoaderCallback::handleEvent()
145 {
146 // enable always succeeds.
147 if (!m_callback->isActive())
148 return;
149
150 m_cssAgent->wasEnabled();
151 m_callback->sendSuccess();
152 }
153
154 class InspectorCSSAgent::SetStyleSheetTextAction FINAL : public InspectorCSSAgent::StyleSheetAction {
155 WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
156 public:
SetStyleSheetTextAction(InspectorStyleSheetBase * styleSheet,const String & text)157 SetStyleSheetTextAction(InspectorStyleSheetBase* styleSheet, const String& text)
158 : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText")
159 , m_styleSheet(styleSheet)
160 , m_text(text)
161 {
162 }
163
perform(ExceptionState & exceptionState)164 virtual bool perform(ExceptionState& exceptionState) OVERRIDE
165 {
166 if (!m_styleSheet->getText(&m_oldText))
167 return false;
168 return redo(exceptionState);
169 }
170
undo(ExceptionState & exceptionState)171 virtual bool undo(ExceptionState& exceptionState) OVERRIDE
172 {
173 return m_styleSheet->setText(m_oldText, exceptionState);
174 }
175
redo(ExceptionState & exceptionState)176 virtual bool redo(ExceptionState& exceptionState) OVERRIDE
177 {
178 return m_styleSheet->setText(m_text, exceptionState);
179 }
180
mergeId()181 virtual String mergeId() OVERRIDE
182 {
183 return String::format("SetStyleSheetText %s", m_styleSheet->id().utf8().data());
184 }
185
merge(PassRefPtrWillBeRawPtr<Action> action)186 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) OVERRIDE
187 {
188 ASSERT(action->mergeId() == mergeId());
189
190 SetStyleSheetTextAction* other = static_cast<SetStyleSheetTextAction*>(action.get());
191 m_text = other->m_text;
192 }
193
194 private:
195 RefPtr<InspectorStyleSheetBase> m_styleSheet;
196 String m_text;
197 String m_oldText;
198 };
199
200 class InspectorCSSAgent::SetPropertyTextAction FINAL : public InspectorCSSAgent::StyleSheetAction {
201 WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
202 public:
SetPropertyTextAction(InspectorStyleSheetBase * styleSheet,const InspectorCSSId & cssId,unsigned propertyIndex,const String & text,bool overwrite)203 SetPropertyTextAction(InspectorStyleSheetBase* styleSheet, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
204 : InspectorCSSAgent::StyleSheetAction("SetPropertyText")
205 , m_styleSheet(styleSheet)
206 , m_cssId(cssId)
207 , m_propertyIndex(propertyIndex)
208 , m_text(text)
209 , m_overwrite(overwrite)
210 {
211 }
212
toString()213 virtual String toString() OVERRIDE
214 {
215 return mergeId() + ": " + m_oldStyleText + " -> " + m_text;
216 }
217
perform(ExceptionState & exceptionState)218 virtual bool perform(ExceptionState& exceptionState) OVERRIDE
219 {
220 return redo(exceptionState);
221 }
222
undo(ExceptionState & exceptionState)223 virtual bool undo(ExceptionState& exceptionState) OVERRIDE
224 {
225 String placeholder;
226 return m_styleSheet->setStyleText(m_cssId, m_oldStyleText);
227 }
228
redo(ExceptionState & exceptionState)229 virtual bool redo(ExceptionState& exceptionState) OVERRIDE
230 {
231 if (!m_styleSheet->getStyleText(m_cssId, &m_oldStyleText))
232 return false;
233 bool result = m_styleSheet->setPropertyText(m_cssId, m_propertyIndex, m_text, m_overwrite, exceptionState);
234 return result;
235 }
236
mergeId()237 virtual String mergeId() OVERRIDE
238 {
239 return String::format("SetPropertyText %s:%u:%s", m_styleSheet->id().utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
240 }
241
merge(PassRefPtrWillBeRawPtr<Action> action)242 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) OVERRIDE
243 {
244 ASSERT(action->mergeId() == mergeId());
245
246 SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
247 m_text = other->m_text;
248 }
249
250 private:
251 RefPtr<InspectorStyleSheetBase> m_styleSheet;
252 InspectorCSSId m_cssId;
253 unsigned m_propertyIndex;
254 String m_text;
255 String m_oldStyleText;
256 bool m_overwrite;
257 };
258
259 class InspectorCSSAgent::SetRuleSelectorAction FINAL : public InspectorCSSAgent::StyleSheetAction {
260 WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
261 public:
SetRuleSelectorAction(InspectorStyleSheet * styleSheet,const InspectorCSSId & cssId,const String & selector)262 SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector)
263 : InspectorCSSAgent::StyleSheetAction("SetRuleSelector")
264 , m_styleSheet(styleSheet)
265 , m_cssId(cssId)
266 , m_selector(selector)
267 {
268 }
269
perform(ExceptionState & exceptionState)270 virtual bool perform(ExceptionState& exceptionState) OVERRIDE
271 {
272 m_oldSelector = m_styleSheet->ruleSelector(m_cssId, exceptionState);
273 if (exceptionState.hadException())
274 return false;
275 return redo(exceptionState);
276 }
277
undo(ExceptionState & exceptionState)278 virtual bool undo(ExceptionState& exceptionState) OVERRIDE
279 {
280 return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector, exceptionState);
281 }
282
redo(ExceptionState & exceptionState)283 virtual bool redo(ExceptionState& exceptionState) OVERRIDE
284 {
285 return m_styleSheet->setRuleSelector(m_cssId, m_selector, exceptionState);
286 }
287
288 private:
289 RefPtr<InspectorStyleSheet> m_styleSheet;
290 InspectorCSSId m_cssId;
291 String m_selector;
292 String m_oldSelector;
293 };
294
295 class InspectorCSSAgent::AddRuleAction FINAL : public InspectorCSSAgent::StyleSheetAction {
296 WTF_MAKE_NONCOPYABLE(AddRuleAction);
297 public:
AddRuleAction(InspectorStyleSheet * styleSheet,const String & selector)298 AddRuleAction(InspectorStyleSheet* styleSheet, const String& selector)
299 : InspectorCSSAgent::StyleSheetAction("AddRule")
300 , m_styleSheet(styleSheet)
301 , m_selector(selector)
302 {
303 }
304
perform(ExceptionState & exceptionState)305 virtual bool perform(ExceptionState& exceptionState) OVERRIDE
306 {
307 return redo(exceptionState);
308 }
309
undo(ExceptionState & exceptionState)310 virtual bool undo(ExceptionState& exceptionState) OVERRIDE
311 {
312 return m_styleSheet->deleteRule(m_newId, exceptionState);
313 }
314
redo(ExceptionState & exceptionState)315 virtual bool redo(ExceptionState& exceptionState) OVERRIDE
316 {
317 CSSStyleRule* cssStyleRule = m_styleSheet->addRule(m_selector, exceptionState);
318 if (exceptionState.hadException())
319 return false;
320 m_newId = m_styleSheet->ruleId(cssStyleRule);
321 return true;
322 }
323
newRuleId()324 InspectorCSSId newRuleId() { return m_newId; }
325
326 private:
327 RefPtr<InspectorStyleSheet> m_styleSheet;
328 InspectorCSSId m_newId;
329 String m_selector;
330 String m_oldSelector;
331 };
332
333 // static
asCSSStyleRule(CSSRule * rule)334 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
335 {
336 if (!rule || rule->type() != CSSRule::STYLE_RULE)
337 return 0;
338 return toCSSStyleRule(rule);
339 }
340
InspectorCSSAgent(InspectorDOMAgent * domAgent,InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent)341 InspectorCSSAgent::InspectorCSSAgent(InspectorDOMAgent* domAgent, InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent)
342 : InspectorBaseAgent<InspectorCSSAgent>("CSS")
343 , m_frontend(0)
344 , m_domAgent(domAgent)
345 , m_pageAgent(pageAgent)
346 , m_resourceAgent(resourceAgent)
347 , m_lastStyleSheetId(1)
348 , m_styleSheetsPendingMutation(0)
349 , m_styleDeclarationPendingMutation(false)
350 , m_creatingViaInspectorStyleSheet(false)
351 , m_isSettingStyleSheetText(false)
352 {
353 m_domAgent->setDOMListener(this);
354 }
355
~InspectorCSSAgent()356 InspectorCSSAgent::~InspectorCSSAgent()
357 {
358 ASSERT(!m_domAgent);
359 reset();
360 }
361
setFrontend(InspectorFrontend * frontend)362 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
363 {
364 ASSERT(!m_frontend);
365 m_frontend = frontend->css();
366 }
367
clearFrontend()368 void InspectorCSSAgent::clearFrontend()
369 {
370 ASSERT(m_frontend);
371 ErrorString error;
372 disable(&error);
373 m_frontend = 0;
374 resetNonPersistentData();
375 }
376
discardAgent()377 void InspectorCSSAgent::discardAgent()
378 {
379 m_domAgent->setDOMListener(0);
380 m_domAgent = 0;
381 }
382
restore()383 void InspectorCSSAgent::restore()
384 {
385 if (m_state->getBoolean(CSSAgentState::cssAgentEnabled))
386 wasEnabled();
387 }
388
flushPendingFrontendMessages()389 void InspectorCSSAgent::flushPendingFrontendMessages()
390 {
391 if (!m_invalidatedDocuments.size())
392 return;
393 HashSet<Document*> invalidatedDocuments;
394 m_invalidatedDocuments.swap(&invalidatedDocuments);
395 for (HashSet<Document*>::iterator it = invalidatedDocuments.begin(); it != invalidatedDocuments.end(); ++it)
396 updateActiveStyleSheets(*it, ExistingFrontendRefresh);
397 }
398
reset()399 void InspectorCSSAgent::reset()
400 {
401 m_idToInspectorStyleSheet.clear();
402 m_idToInspectorStyleSheetForInlineStyle.clear();
403 m_cssStyleSheetToInspectorStyleSheet.clear();
404 m_documentToCSSStyleSheets.clear();
405 m_invalidatedDocuments.clear();
406 m_nodeToInspectorStyleSheet.clear();
407 m_documentToViaInspectorStyleSheet.clear();
408 resetNonPersistentData();
409 }
410
resetNonPersistentData()411 void InspectorCSSAgent::resetNonPersistentData()
412 {
413 resetPseudoStates();
414 }
415
enable(ErrorString *,PassRefPtr<EnableCallback> prpCallback)416 void InspectorCSSAgent::enable(ErrorString*, PassRefPtr<EnableCallback> prpCallback)
417 {
418 m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
419 if (!m_pageAgent->resourceContentLoader()) {
420 wasEnabled();
421 prpCallback->sendSuccess();
422 return;
423 }
424 m_pageAgent->resourceContentLoader()->addListener(adoptPtr(new InspectorCSSAgent::InspectorResourceContentLoaderCallback(this, prpCallback)));
425 }
426
wasEnabled()427 void InspectorCSSAgent::wasEnabled()
428 {
429 if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
430 // We were disabled while fetching resources.
431 return;
432 }
433
434 m_instrumentingAgents->setInspectorCSSAgent(this);
435 Vector<Document*> documents = m_domAgent->documents();
436 for (Vector<Document*>::iterator it = documents.begin(); it != documents.end(); ++it)
437 updateActiveStyleSheets(*it, InitialFrontendLoad);
438 }
439
disable(ErrorString *)440 void InspectorCSSAgent::disable(ErrorString*)
441 {
442 reset();
443 m_instrumentingAgents->setInspectorCSSAgent(0);
444 m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
445 }
446
didCommitLoadForMainFrame()447 void InspectorCSSAgent::didCommitLoadForMainFrame()
448 {
449 reset();
450 }
451
mediaQueryResultChanged()452 void InspectorCSSAgent::mediaQueryResultChanged()
453 {
454 flushPendingFrontendMessages();
455 m_frontend->mediaQueryResultChanged();
456 }
457
willMutateRules()458 void InspectorCSSAgent::willMutateRules()
459 {
460 ++m_styleSheetsPendingMutation;
461 }
462
didMutateRules(CSSStyleSheet * styleSheet)463 void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet)
464 {
465 --m_styleSheetsPendingMutation;
466 ASSERT(m_styleSheetsPendingMutation >= 0);
467
468 if (!styleSheetEditInProgress()) {
469 Document* owner = styleSheet->ownerDocument();
470 if (owner)
471 owner->modifiedStyleSheet(styleSheet, FullStyleUpdate);
472 }
473 }
474
willMutateStyle()475 void InspectorCSSAgent::willMutateStyle()
476 {
477 m_styleDeclarationPendingMutation = true;
478 }
479
didMutateStyle(CSSStyleDeclaration * style,bool isInlineStyle)480 void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle)
481 {
482 ASSERT(m_styleDeclarationPendingMutation);
483 m_styleDeclarationPendingMutation = false;
484 if (!styleSheetEditInProgress() && !isInlineStyle) {
485 CSSStyleSheet* parentSheet = style->parentStyleSheet();
486 Document* owner = parentSheet ? parentSheet->ownerDocument() : 0;
487 if (owner)
488 owner->modifiedStyleSheet(parentSheet, FullStyleUpdate);
489 }
490 }
491
activeStyleSheetsUpdated(Document * document)492 void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document)
493 {
494 if (styleSheetEditInProgress())
495 return;
496 m_invalidatedDocuments.add(document);
497 if (m_creatingViaInspectorStyleSheet)
498 flushPendingFrontendMessages();
499 }
500
updateActiveStyleSheets(Document * document,StyleSheetsUpdateType styleSheetsUpdateType)501 void InspectorCSSAgent::updateActiveStyleSheets(Document* document, StyleSheetsUpdateType styleSheetsUpdateType)
502 {
503 Vector<CSSStyleSheet*> newSheetsVector;
504 InspectorCSSAgent::collectAllDocumentStyleSheets(document, newSheetsVector);
505 setActiveStyleSheets(document, newSheetsVector, styleSheetsUpdateType);
506 }
507
setActiveStyleSheets(Document * document,const Vector<CSSStyleSheet * > & allSheetsVector,StyleSheetsUpdateType styleSheetsUpdateType)508 void InspectorCSSAgent::setActiveStyleSheets(Document* document, const Vector<CSSStyleSheet*>& allSheetsVector, StyleSheetsUpdateType styleSheetsUpdateType)
509 {
510 bool isInitialFrontendLoad = styleSheetsUpdateType == InitialFrontendLoad;
511
512 HashSet<CSSStyleSheet*>* documentCSSStyleSheets = m_documentToCSSStyleSheets.get(document);
513 if (!documentCSSStyleSheets) {
514 documentCSSStyleSheets = new HashSet<CSSStyleSheet*>();
515 OwnPtr<HashSet<CSSStyleSheet*> > documentCSSStyleSheetsPtr = adoptPtr(documentCSSStyleSheets);
516 m_documentToCSSStyleSheets.set(document, documentCSSStyleSheetsPtr.release());
517 }
518
519 HashSet<CSSStyleSheet*> removedSheets(*documentCSSStyleSheets);
520 Vector<CSSStyleSheet*> addedSheets;
521 for (Vector<CSSStyleSheet*>::const_iterator it = allSheetsVector.begin(); it != allSheetsVector.end(); ++it) {
522 CSSStyleSheet* cssStyleSheet = *it;
523 if (removedSheets.contains(cssStyleSheet)) {
524 removedSheets.remove(cssStyleSheet);
525 if (isInitialFrontendLoad)
526 addedSheets.append(cssStyleSheet);
527 } else {
528 addedSheets.append(cssStyleSheet);
529 }
530 }
531
532 for (HashSet<CSSStyleSheet*>::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) {
533 CSSStyleSheet* cssStyleSheet = *it;
534 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet);
535 ASSERT(inspectorStyleSheet);
536
537 documentCSSStyleSheets->remove(cssStyleSheet);
538 if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
539 String id = unbindStyleSheet(inspectorStyleSheet.get());
540 if (m_frontend && !isInitialFrontendLoad)
541 m_frontend->styleSheetRemoved(id);
542 }
543 }
544
545 for (Vector<CSSStyleSheet*>::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) {
546 CSSStyleSheet* cssStyleSheet = *it;
547 bool isNew = isInitialFrontendLoad || !m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet);
548 if (isNew) {
549 InspectorStyleSheet* newStyleSheet = bindStyleSheet(cssStyleSheet);
550 documentCSSStyleSheets->add(cssStyleSheet);
551 if (m_frontend)
552 m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo());
553 }
554 }
555
556 if (documentCSSStyleSheets->isEmpty())
557 m_documentToCSSStyleSheets.remove(document);
558 }
559
documentDetached(Document * document)560 void InspectorCSSAgent::documentDetached(Document* document)
561 {
562 m_invalidatedDocuments.remove(document);
563 setActiveStyleSheets(document, Vector<CSSStyleSheet*>(), ExistingFrontendRefresh);
564 }
565
forcePseudoState(Element * element,CSSSelector::PseudoType pseudoType)566 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
567 {
568 if (m_nodeIdToForcedPseudoState.isEmpty())
569 return false;
570
571 int nodeId = m_domAgent->boundNodeId(element);
572 if (!nodeId)
573 return false;
574
575 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
576 if (it == m_nodeIdToForcedPseudoState.end())
577 return false;
578
579 unsigned forcedPseudoState = it->value;
580 switch (pseudoType) {
581 case CSSSelector::PseudoActive:
582 return forcedPseudoState & PseudoActive;
583 case CSSSelector::PseudoFocus:
584 return forcedPseudoState & PseudoFocus;
585 case CSSSelector::PseudoHover:
586 return forcedPseudoState & PseudoHover;
587 case CSSSelector::PseudoVisited:
588 return forcedPseudoState & PseudoVisited;
589 default:
590 return false;
591 }
592 }
593
getMediaQueries(ErrorString * errorString,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>> & medias)594 void InspectorCSSAgent::getMediaQueries(ErrorString* errorString, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> >& medias)
595 {
596 medias = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
597 for (IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.begin(); it != m_idToInspectorStyleSheet.end(); ++it) {
598 RefPtr<InspectorStyleSheet> styleSheet = it->value;
599 collectMediaQueriesFromStyleSheet(styleSheet->pageStyleSheet(), medias.get());
600 const CSSRuleVector& flatRules = styleSheet->flatRules();
601 for (unsigned i = 0; i < flatRules.size(); ++i) {
602 CSSRule* rule = flatRules.at(i).get();
603 if (rule->type() == CSSRule::MEDIA_RULE)
604 collectMediaQueriesFromRule(rule, medias.get());
605 }
606 }
607 }
608
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)609 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)
610 {
611 Element* element = elementForId(errorString, nodeId);
612 if (!element) {
613 *errorString = "Node not found";
614 return;
615 }
616
617 Element* originalElement = element;
618 PseudoId elementPseudoId = element->pseudoId();
619 if (elementPseudoId) {
620 element = element->parentOrShadowHostElement();
621 if (!element) {
622 *errorString = "Pseudo element has no parent";
623 return;
624 }
625 }
626
627 Document* ownerDocument = element->ownerDocument();
628 // A non-active document has no styles.
629 if (!ownerDocument->isActive())
630 return;
631
632 // FIXME: It's really gross for the inspector to reach in and access StyleResolver
633 // directly here. We need to provide the Inspector better APIs to get this information
634 // without grabbing at internal style classes!
635
636 // Matched rules.
637 StyleResolver& styleResolver = ownerDocument->ensureStyleResolver();
638
639 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
640 matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), originalElement);
641
642 // Pseudo elements.
643 if (!elementPseudoId && (!includePseudo || *includePseudo)) {
644 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create();
645 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
646 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
647 if (matchedRules && matchedRules->length()) {
648 RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create()
649 .setPseudoId(static_cast<int>(pseudoId))
650 .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), element));
651 pseudoElements->addItem(matches.release());
652 }
653 }
654
655 pseudoIdMatches = pseudoElements.release();
656 }
657
658 // Inherited styles.
659 if (!elementPseudoId && (!includeInherited || *includeInherited)) {
660 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create();
661 Element* parentElement = element->parentElement();
662 while (parentElement) {
663 StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver();
664 RefPtrWillBeRawPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules);
665 RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create()
666 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), parentElement));
667 if (parentElement->style() && parentElement->style()->length()) {
668 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
669 if (styleSheet)
670 entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
671 }
672
673 entries->addItem(entry.release());
674 parentElement = parentElement->parentElement();
675 }
676
677 inheritedEntries = entries.release();
678 }
679 }
680
getInlineStylesForNode(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::CSS::CSSStyle> & inlineStyle,RefPtr<TypeBuilder::CSS::CSSStyle> & attributesStyle)681 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle)
682 {
683 Element* element = elementForId(errorString, nodeId);
684 if (!element)
685 return;
686
687 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
688 if (!styleSheet)
689 return;
690
691 inlineStyle = styleSheet->buildObjectForStyle(element->style());
692 RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element);
693 attributesStyle = attributes ? attributes.release() : nullptr;
694 }
695
getComputedStyleForNode(ErrorString * errorString,int nodeId,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>> & style)696 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style)
697 {
698 Node* node = m_domAgent->assertNode(errorString, nodeId);
699 if (!node)
700 return;
701
702 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
703 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
704 style = inspectorStyle->buildArrayForComputedStyle();
705 }
706
collectPlatformFontsForRenderer(RenderText * renderer,HashCountedSet<String> * fontStats)707 void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats)
708 {
709 for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) {
710 RenderStyle* style = renderer->style(box->isFirstLineStyle());
711 const Font& font = style->font();
712 TextRun run = box->constructTextRunForInspector(style, font);
713 WidthIterator it(&font, run, 0, false);
714 GlyphBuffer glyphBuffer;
715 it.advance(run.length(), &glyphBuffer);
716 for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
717 String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName();
718 if (familyName.isNull())
719 familyName = "";
720 fontStats->add(familyName);
721 }
722 }
723 }
724
getPlatformFontsForNode(ErrorString * errorString,int nodeId,String * cssFamilyName,RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>> & platformFonts)725 void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId,
726 String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts)
727 {
728 Node* node = m_domAgent->assertNode(errorString, nodeId);
729 if (!node)
730 return;
731
732 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
733 *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily);
734
735 Vector<Node*> textNodes;
736 if (node->nodeType() == Node::TEXT_NODE) {
737 if (node->renderer())
738 textNodes.append(node);
739 } else {
740 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
741 if (child->nodeType() == Node::TEXT_NODE && child->renderer())
742 textNodes.append(child);
743 }
744 }
745
746 HashCountedSet<String> fontStats;
747 for (size_t i = 0; i < textNodes.size(); ++i) {
748 RenderText* renderer = toRenderText(textNodes[i]->renderer());
749 collectPlatformFontsForRenderer(renderer, &fontStats);
750 if (renderer->isTextFragment()) {
751 RenderTextFragment* textFragment = toRenderTextFragment(renderer);
752 if (textFragment->firstLetter()) {
753 RenderBoxModelObject* firstLetter = textFragment->firstLetter();
754 for (RenderObject* current = firstLetter->slowFirstChild(); current; current = current->nextSibling()) {
755 if (current->isText())
756 collectPlatformFontsForRenderer(toRenderText(current), &fontStats);
757 }
758 }
759 }
760 }
761
762 platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create();
763 for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) {
764 RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create()
765 .setFamilyName(it->key)
766 .setGlyphCount(it->value);
767 platformFonts->addItem(platformFont);
768 }
769 }
770
getStyleSheetText(ErrorString * errorString,const String & styleSheetId,String * result)771 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
772 {
773 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
774 if (!inspectorStyleSheet)
775 return;
776
777 inspectorStyleSheet->getText(result);
778 }
779
setStyleSheetText(ErrorString * errorString,const String & styleSheetId,const String & text)780 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
781 {
782 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
783 if (!inspectorStyleSheet) {
784 *errorString = "Style sheet with id " + styleSheetId + " not found";
785 return;
786 }
787
788 TrackExceptionState exceptionState;
789 m_domAgent->history()->perform(adoptRefWillBeNoop(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState);
790 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
791 }
792
extractRangeComponent(ErrorString * errorString,const RefPtr<JSONObject> & range,const String & component,unsigned & result)793 static bool extractRangeComponent(ErrorString* errorString, const RefPtr<JSONObject>& range, const String& component, unsigned& result)
794 {
795 int parsedValue;
796 if (!range->getNumber(component, &parsedValue) || parsedValue < 0) {
797 *errorString = "range." + component + " must be a non-negative integer";
798 return false;
799 }
800 result = parsedValue;
801 return true;
802 }
803
jsonRangeToSourceRange(ErrorString * errorString,InspectorStyleSheetBase * inspectorStyleSheet,const RefPtr<JSONObject> & range,SourceRange * sourceRange)804 static bool jsonRangeToSourceRange(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const RefPtr<JSONObject>& range, SourceRange* sourceRange)
805 {
806 unsigned startLineNumber;
807 unsigned startColumn;
808 unsigned endLineNumber;
809 unsigned endColumn;
810 if (!extractRangeComponent(errorString, range, "startLine", startLineNumber)
811 || !extractRangeComponent(errorString, range, "startColumn", startColumn)
812 || !extractRangeComponent(errorString, range, "endLine", endLineNumber)
813 || !extractRangeComponent(errorString, range, "endColumn", endColumn))
814 return false;
815
816 unsigned startOffset;
817 unsigned endOffset;
818 bool success = inspectorStyleSheet->lineNumberAndColumnToOffset(startLineNumber, startColumn, &startOffset)
819 && inspectorStyleSheet->lineNumberAndColumnToOffset(endLineNumber, endColumn, &endOffset);
820 if (!success) {
821 *errorString = "Specified range is out of bounds";
822 return false;
823 }
824
825 if (startOffset > endOffset) {
826 *errorString = "Range start must not succeed its end";
827 return false;
828 }
829 sourceRange->start = startOffset;
830 sourceRange->end = endOffset;
831 return true;
832 }
833
setPropertyText(ErrorString * errorString,const String & styleSheetId,const RefPtr<JSONObject> & range,const String & text,RefPtr<TypeBuilder::CSS::CSSStyle> & result)834 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
835 {
836 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
837 if (!inspectorStyleSheet)
838 return;
839 SourceRange propertyRange;
840 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &propertyRange))
841 return;
842 InspectorCSSId compoundId;
843 unsigned propertyIndex;
844 bool overwrite;
845 if (!inspectorStyleSheet->findPropertyByRange(propertyRange, &compoundId, &propertyIndex, &overwrite)) {
846 *errorString = "Source range didn't match any existing property source range nor any property insertion point";
847 return;
848 }
849
850 TrackExceptionState exceptionState;
851 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState);
852 if (success)
853 result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
854 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
855 }
856
setRuleSelector(ErrorString * errorString,const String & styleSheetId,const RefPtr<JSONObject> & range,const String & selector,RefPtr<TypeBuilder::CSS::CSSRule> & result)857 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
858 {
859 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
860 if (!inspectorStyleSheet)
861 return;
862 SourceRange selectorRange;
863 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange))
864 return;
865 InspectorCSSId compoundId;
866 if (!inspectorStyleSheet->findRuleBySelectorRange(selectorRange, &compoundId)) {
867 *errorString = "Source range didn't match any rule selector source range";
868 return;
869 }
870
871 TrackExceptionState exceptionState;
872 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState);
873 if (success) {
874 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId);
875 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
876 }
877 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
878 }
879
createStyleSheet(ErrorString * errorString,const String & frameId,TypeBuilder::CSS::StyleSheetId * outStyleSheetId)880 void InspectorCSSAgent::createStyleSheet(ErrorString* errorString, const String& frameId, TypeBuilder::CSS::StyleSheetId* outStyleSheetId)
881 {
882 LocalFrame* frame = m_pageAgent->frameForId(frameId);
883 if (!frame) {
884 *errorString = "Frame not found";
885 return;
886 }
887
888 Document* document = frame->document();
889 if (!document) {
890 *errorString = "Frame does not have a document";
891 return;
892 }
893
894 InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(document, true);
895 if (!inspectorStyleSheet) {
896 *errorString = "No target stylesheet found";
897 return;
898 }
899
900 updateActiveStyleSheets(document, ExistingFrontendRefresh);
901
902 *outStyleSheetId = inspectorStyleSheet->id();
903 }
904
addRule(ErrorString * errorString,const String & styleSheetId,const String & selector,RefPtr<TypeBuilder::CSS::CSSRule> & result)905 void InspectorCSSAgent::addRule(ErrorString* errorString, const String& styleSheetId, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
906 {
907 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
908 if (!inspectorStyleSheet)
909 return;
910
911 TrackExceptionState exceptionState;
912 RefPtrWillBeRawPtr<AddRuleAction> action = adoptRefWillBeNoop(new AddRuleAction(inspectorStyleSheet, selector));
913 bool success = m_domAgent->history()->perform(action, exceptionState);
914 if (!success) {
915 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
916 return;
917 }
918
919 InspectorCSSId ruleId = action->newRuleId();
920 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
921 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
922 }
923
forcePseudoState(ErrorString * errorString,int nodeId,const RefPtr<JSONArray> & forcedPseudoClasses)924 void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses)
925 {
926 Element* element = m_domAgent->assertElement(errorString, nodeId);
927 if (!element)
928 return;
929
930 unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get());
931 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
932 unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value;
933 bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
934 if (!needStyleRecalc)
935 return;
936
937 if (forcedPseudoState)
938 m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
939 else
940 m_nodeIdToForcedPseudoState.remove(nodeId);
941 element->ownerDocument()->setNeedsStyleRecalc(SubtreeStyleChange);
942 }
943
buildMediaObject(const MediaList * media,MediaListSource mediaListSource,const String & sourceURL,CSSStyleSheet * parentStyleSheet)944 PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet)
945 {
946 // Make certain compilers happy by initializing |source| up-front.
947 TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
948 switch (mediaListSource) {
949 case MediaListSourceMediaRule:
950 source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
951 break;
952 case MediaListSourceImportRule:
953 source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
954 break;
955 case MediaListSourceLinkedSheet:
956 source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
957 break;
958 case MediaListSourceInlineSheet:
959 source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
960 break;
961 }
962
963 const MediaQuerySet* queries = media->queries();
964 const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queryVector = queries->queryVector();
965 bool hasMediaQueryItems = false;
966 RefPtr<TypeBuilder::Array<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > > mediaListArray = TypeBuilder::Array<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> >::create();
967 for (size_t i = 0; i < queryVector.size(); ++i) {
968 MediaQuery* query = queryVector.at(i).get();
969 const ExpressionHeapVector& expressions = query->expressions();
970 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > expressionArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression>::create();
971 bool hasExpressionItems = false;
972 for (size_t j = 0; j < expressions.size(); ++j) {
973 MediaQueryExp* mediaQueryExp = expressions.at(j).get();
974 MediaQueryExpValue expValue = mediaQueryExp->expValue();
975 if (!expValue.isValue)
976 continue;
977 const char* valueName = CSSPrimitiveValue::unitTypeToString(expValue.unit);
978 RefPtr<TypeBuilder::CSS::MediaQueryExpression> mediaQueryExpression = TypeBuilder::CSS::MediaQueryExpression::create()
979 .setValue(expValue.value)
980 .setUnit(String(valueName))
981 .setFeature(mediaQueryExp->mediaFeature());
982 RefPtr<MediaValues> mediaValues = MediaValues::createDynamicIfFrameExists(parentStyleSheet->ownerDocument()->frame());
983 int computedLength;
984 if (mediaValues->computeLength(expValue.value, expValue.unit, computedLength))
985 mediaQueryExpression->setComputedLength(computedLength);
986
987 expressionArray->addItem(mediaQueryExpression);
988 hasExpressionItems = true;
989 }
990 if (hasExpressionItems) {
991 mediaListArray->addItem(expressionArray);
992 hasMediaQueryItems = true;
993 }
994 }
995
996 RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
997 .setText(media->mediaText())
998 .setSource(source);
999 if (hasMediaQueryItems)
1000 mediaObject->setMediaList(mediaListArray);
1001
1002 if (parentStyleSheet && mediaListSource != MediaListSourceLinkedSheet) {
1003 if (InspectorStyleSheet* inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet))
1004 mediaObject->setParentStyleSheetId(inspectorStyleSheet->id());
1005 }
1006
1007 if (!sourceURL.isEmpty()) {
1008 mediaObject->setSourceURL(sourceURL);
1009
1010 CSSRule* parentRule = media->parentRule();
1011 if (!parentRule)
1012 return mediaObject.release();
1013 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet());
1014 RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule);
1015 if (mediaRange)
1016 mediaObject->setRange(mediaRange);
1017 }
1018 return mediaObject.release();
1019 }
1020
collectMediaQueriesFromStyleSheet(CSSStyleSheet * styleSheet,TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> * mediaArray)1021 bool InspectorCSSAgent::collectMediaQueriesFromStyleSheet(CSSStyleSheet* styleSheet, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1022 {
1023 bool addedItems = false;
1024 MediaList* mediaList = styleSheet->media();
1025 String sourceURL;
1026 if (mediaList && mediaList->length()) {
1027 Document* doc = styleSheet->ownerDocument();
1028 if (doc)
1029 sourceURL = doc->url();
1030 else if (!styleSheet->contents()->baseURL().isEmpty())
1031 sourceURL = styleSheet->contents()->baseURL();
1032 else
1033 sourceURL = "";
1034 mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet));
1035 addedItems = true;
1036 }
1037 return addedItems;
1038 }
1039
collectMediaQueriesFromRule(CSSRule * rule,TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> * mediaArray)1040 bool InspectorCSSAgent::collectMediaQueriesFromRule(CSSRule* rule, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1041 {
1042 MediaList* mediaList;
1043 String sourceURL;
1044 CSSStyleSheet* parentStyleSheet = 0;
1045 bool isMediaRule = true;
1046 bool addedItems = false;
1047 if (rule->type() == CSSRule::MEDIA_RULE) {
1048 CSSMediaRule* mediaRule = toCSSMediaRule(rule);
1049 mediaList = mediaRule->media();
1050 parentStyleSheet = mediaRule->parentStyleSheet();
1051 } else if (rule->type() == CSSRule::IMPORT_RULE) {
1052 CSSImportRule* importRule = toCSSImportRule(rule);
1053 mediaList = importRule->media();
1054 parentStyleSheet = importRule->parentStyleSheet();
1055 isMediaRule = false;
1056 } else {
1057 mediaList = 0;
1058 }
1059
1060 if (parentStyleSheet) {
1061 sourceURL = parentStyleSheet->contents()->baseURL();
1062 if (sourceURL.isEmpty())
1063 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
1064 } else {
1065 sourceURL = "";
1066 }
1067
1068 if (mediaList && mediaList->length()) {
1069 mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet));
1070 addedItems = true;
1071 }
1072 return addedItems;
1073 }
1074
buildMediaListChain(CSSRule * rule)1075 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule)
1076 {
1077 if (!rule)
1078 return nullptr;
1079 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
1080 bool hasItems = false;
1081 CSSRule* parentRule = rule;
1082 while (parentRule) {
1083 hasItems = collectMediaQueriesFromRule(parentRule, mediaArray.get()) || hasItems;
1084 if (parentRule->parentRule()) {
1085 parentRule = parentRule->parentRule();
1086 } else {
1087 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
1088 while (styleSheet) {
1089 hasItems = collectMediaQueriesFromStyleSheet(styleSheet, mediaArray.get()) || hasItems;
1090 parentRule = styleSheet->ownerRule();
1091 if (parentRule)
1092 break;
1093 styleSheet = styleSheet->parentStyleSheet();
1094 }
1095 }
1096 }
1097 return hasItems ? mediaArray : nullptr;
1098 }
1099
asInspectorStyleSheet(Element * element)1100 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
1101 {
1102 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1103 if (it != m_nodeToInspectorStyleSheet.end())
1104 return it->value.get();
1105
1106 CSSStyleDeclaration* style = element->isStyledElement() ? element->style() : 0;
1107 if (!style)
1108 return 0;
1109
1110 String newStyleSheetId = String::number(m_lastStyleSheetId++);
1111 RefPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, this);
1112 m_idToInspectorStyleSheetForInlineStyle.set(newStyleSheetId, inspectorStyleSheet);
1113 m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
1114 return inspectorStyleSheet.get();
1115 }
1116
elementForId(ErrorString * errorString,int nodeId)1117 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
1118 {
1119 Node* node = m_domAgent->nodeForId(nodeId);
1120 if (!node) {
1121 *errorString = "No node with given id found";
1122 return 0;
1123 }
1124 if (!node->isElementNode()) {
1125 *errorString = "Not an element node";
1126 return 0;
1127 }
1128 return toElement(node);
1129 }
1130
1131 // static
collectAllDocumentStyleSheets(Document * document,Vector<CSSStyleSheet * > & result)1132 void InspectorCSSAgent::collectAllDocumentStyleSheets(Document* document, Vector<CSSStyleSheet*>& result)
1133 {
1134 const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets = document->styleEngine()->activeStyleSheetsForInspector();
1135 for (WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >::const_iterator it = activeStyleSheets.begin(); it != activeStyleSheets.end(); ++it) {
1136 CSSStyleSheet* styleSheet = it->get();
1137 InspectorCSSAgent::collectStyleSheets(styleSheet, result);
1138 }
1139 }
1140
1141 // static
collectStyleSheets(CSSStyleSheet * styleSheet,Vector<CSSStyleSheet * > & result)1142 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, Vector<CSSStyleSheet*>& result)
1143 {
1144 result.append(styleSheet);
1145 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
1146 CSSRule* rule = styleSheet->item(i);
1147 if (rule->type() == CSSRule::IMPORT_RULE) {
1148 CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
1149 if (importedStyleSheet)
1150 InspectorCSSAgent::collectStyleSheets(importedStyleSheet, result);
1151 }
1152 }
1153 }
1154
bindStyleSheet(CSSStyleSheet * styleSheet)1155 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
1156 {
1157 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
1158 if (!inspectorStyleSheet) {
1159 String id = String::number(m_lastStyleSheetId++);
1160 Document* document = styleSheet->ownerDocument();
1161 inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
1162 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
1163 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
1164 if (m_creatingViaInspectorStyleSheet)
1165 m_documentToViaInspectorStyleSheet.add(document, inspectorStyleSheet);
1166 }
1167 return inspectorStyleSheet.get();
1168 }
1169
unbindStyleSheet(InspectorStyleSheet * inspectorStyleSheet)1170 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
1171 {
1172 String id = inspectorStyleSheet->id();
1173 m_idToInspectorStyleSheet.remove(id);
1174 if (inspectorStyleSheet->pageStyleSheet())
1175 m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
1176 return id;
1177 }
1178
viaInspectorStyleSheet(Document * document,bool createIfAbsent)1179 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
1180 {
1181 if (!document) {
1182 ASSERT(!createIfAbsent);
1183 return 0;
1184 }
1185
1186 if (!document->isHTMLDocument() && !document->isSVGDocument())
1187 return 0;
1188
1189 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToViaInspectorStyleSheet.get(document);
1190 if (inspectorStyleSheet || !createIfAbsent)
1191 return inspectorStyleSheet.get();
1192
1193 TrackExceptionState exceptionState;
1194 RefPtrWillBeRawPtr<Element> styleElement = document->createElement("style", exceptionState);
1195 if (!exceptionState.hadException())
1196 styleElement->setAttribute("type", "text/css", exceptionState);
1197 if (!exceptionState.hadException()) {
1198 ContainerNode* targetNode;
1199 // HEAD is absent in ImageDocuments, for example.
1200 if (document->head())
1201 targetNode = document->head();
1202 else if (document->body())
1203 targetNode = document->body();
1204 else
1205 return 0;
1206
1207 InlineStyleOverrideScope overrideScope(document);
1208 m_creatingViaInspectorStyleSheet = true;
1209 targetNode->appendChild(styleElement, exceptionState);
1210 // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation.
1211 // We just need to pick the respective InspectorStyleSheet from m_documentToViaInspectorStyleSheet.
1212 m_creatingViaInspectorStyleSheet = false;
1213 }
1214 if (exceptionState.hadException())
1215 return 0;
1216
1217 return m_documentToViaInspectorStyleSheet.get(document);
1218 }
1219
assertInspectorStyleSheetForId(ErrorString * errorString,const String & styleSheetId)1220 InspectorStyleSheet* InspectorCSSAgent::assertInspectorStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1221 {
1222 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
1223 if (it == m_idToInspectorStyleSheet.end()) {
1224 *errorString = "No style sheet with given id found";
1225 return 0;
1226 }
1227 return it->value.get();
1228 }
1229
assertStyleSheetForId(ErrorString * errorString,const String & styleSheetId)1230 InspectorStyleSheetBase* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1231 {
1232 String placeholder;
1233 InspectorStyleSheetBase* result = assertInspectorStyleSheetForId(&placeholder, styleSheetId);
1234 if (result)
1235 return result;
1236 IdToInspectorStyleSheetForInlineStyle::iterator it = m_idToInspectorStyleSheetForInlineStyle.find(styleSheetId);
1237 if (it == m_idToInspectorStyleSheetForInlineStyle.end()) {
1238 *errorString = "No style sheet with given id found";
1239 return 0;
1240 }
1241 return it->value.get();
1242 }
1243
detectOrigin(CSSStyleSheet * pageStyleSheet,Document * ownerDocument)1244 TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
1245 {
1246 if (m_creatingViaInspectorStyleSheet)
1247 return TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1248
1249 TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular;
1250 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
1251 origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent;
1252 else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->isDocumentNode())
1253 origin = TypeBuilder::CSS::StyleSheetOrigin::User;
1254 else {
1255 InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
1256 if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
1257 origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1258 }
1259 return origin;
1260 }
1261
buildObjectForRule(CSSStyleRule * rule)1262 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
1263 {
1264 if (!rule)
1265 return nullptr;
1266
1267 // CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent pointers if they are coming from
1268 // user agent stylesheets. To work around this issue, we use CSSOM wrapper created by inspector.
1269 if (!rule->parentStyleSheet()) {
1270 if (!m_inspectorUserAgentStyleSheet)
1271 m_inspectorUserAgentStyleSheet = CSSStyleSheet::create(CSSDefaultStyleSheets::instance().defaultStyleSheet());
1272 rule->setParentStyleSheet(m_inspectorUserAgentStyleSheet.get());
1273 }
1274 return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule));
1275 }
1276
matchesPseudoElement(const CSSSelector * selector,PseudoId elementPseudoId)1277 static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId)
1278 {
1279 // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector."
1280 // As such, check the last selector in the tag history.
1281 for (; !selector->isLastInTagHistory(); ++selector) { }
1282 PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO;
1283
1284 // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements.
1285 // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too.
1286 return selectorPseudoId == elementPseudoId;
1287 }
1288
buildArrayForMatchedRuleList(CSSRuleList * ruleList,Element * element)1289 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, Element* element)
1290 {
1291 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create();
1292 if (!ruleList)
1293 return result.release();
1294
1295 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1296 CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
1297 RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule);
1298 if (!ruleObject)
1299 continue;
1300 RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create();
1301 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1302 long index = 0;
1303 PseudoId elementPseudoId = element->pseudoId();
1304 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) {
1305 const CSSSelector* firstTagHistorySelector = selector;
1306 bool matched = false;
1307 if (elementPseudoId)
1308 matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|.
1309 matched |= element->matches(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION);
1310 if (matched)
1311 matchingSelectors->addItem(index);
1312 ++index;
1313 }
1314 RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create()
1315 .setRule(ruleObject.release())
1316 .setMatchingSelectors(matchingSelectors.release());
1317 result->addItem(match);
1318 }
1319
1320 return result;
1321 }
1322
buildObjectForAttributesStyle(Element * element)1323 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
1324 {
1325 if (!element->isStyledElement())
1326 return nullptr;
1327
1328 // FIXME: Ugliness below.
1329 StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle());
1330 if (!attributeStyle)
1331 return nullptr;
1332
1333 MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle);
1334
1335 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0);
1336 return inspectorStyle->buildObjectForStyle();
1337 }
1338
didRemoveDocument(Document * document)1339 void InspectorCSSAgent::didRemoveDocument(Document* document)
1340 {
1341 if (document)
1342 m_documentToViaInspectorStyleSheet.remove(document);
1343 }
1344
didRemoveDOMNode(Node * node)1345 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
1346 {
1347 if (!node)
1348 return;
1349
1350 int nodeId = m_domAgent->boundNodeId(node);
1351 if (nodeId)
1352 m_nodeIdToForcedPseudoState.remove(nodeId);
1353
1354 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
1355 if (it == m_nodeToInspectorStyleSheet.end())
1356 return;
1357
1358 m_idToInspectorStyleSheetForInlineStyle.remove(it->value->id());
1359 m_nodeToInspectorStyleSheet.remove(node);
1360 }
1361
didModifyDOMAttr(Element * element)1362 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
1363 {
1364 if (!element)
1365 return;
1366
1367 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1368 if (it == m_nodeToInspectorStyleSheet.end())
1369 return;
1370
1371 it->value->didModifyElementAttribute();
1372 }
1373
styleSheetChanged(InspectorStyleSheetBase * styleSheet)1374 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheetBase* styleSheet)
1375 {
1376 flushPendingFrontendMessages();
1377 m_frontend->styleSheetChanged(styleSheet->id());
1378 }
1379
willReparseStyleSheet()1380 void InspectorCSSAgent::willReparseStyleSheet()
1381 {
1382 ASSERT(!m_isSettingStyleSheetText);
1383 m_isSettingStyleSheetText = true;
1384 }
1385
didReparseStyleSheet()1386 void InspectorCSSAgent::didReparseStyleSheet()
1387 {
1388 ASSERT(m_isSettingStyleSheetText);
1389 m_isSettingStyleSheetText = false;
1390 }
1391
resetPseudoStates()1392 void InspectorCSSAgent::resetPseudoStates()
1393 {
1394 HashSet<Document*> documentsToChange;
1395 for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
1396 Element* element = toElement(m_domAgent->nodeForId(it->key));
1397 if (element && element->ownerDocument())
1398 documentsToChange.add(element->ownerDocument());
1399 }
1400
1401 m_nodeIdToForcedPseudoState.clear();
1402 for (HashSet<Document*>::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
1403 (*it)->setNeedsStyleRecalc(SubtreeStyleChange);
1404 }
1405
1406 } // namespace WebCore
1407
1408