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