• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "config.h"
26 #include "core/inspector/InspectorCSSAgent.h"
27 
28 #include "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