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/WidthIterator.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
77namespace CSSAgentState {
78static const char cssAgentEnabled[] = "cssAgentEnabled";
79}
80
81typedef blink::InspectorBackendDispatcher::CSSCommandHandler::EnableCallback EnableCallback;
82
83namespace blink {
84
85enum ForcePseudoClassFlags {
86    PseudoNone = 0,
87    PseudoHover = 1 << 0,
88    PseudoFocus = 1 << 1,
89    PseudoActive = 1 << 2,
90    PseudoVisited = 1 << 3
91};
92
93static 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
122class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
123    WTF_MAKE_NONCOPYABLE(StyleSheetAction);
124public:
125    StyleSheetAction(const String& name)
126        : InspectorHistory::Action(name)
127    {
128    }
129};
130
131class InspectorCSSAgent::InspectorResourceContentLoaderCallback FINAL : public VoidCallback {
132public:
133    InspectorResourceContentLoaderCallback(InspectorCSSAgent*, PassRefPtrWillBeRawPtr<EnableCallback>);
134    virtual void trace(Visitor*) OVERRIDE;
135    virtual void handleEvent() OVERRIDE;
136
137private:
138    RawPtrWillBeMember<InspectorCSSAgent> m_cssAgent;
139    RefPtrWillBeMember<EnableCallback> m_callback;
140};
141
142InspectorCSSAgent::InspectorResourceContentLoaderCallback::InspectorResourceContentLoaderCallback(InspectorCSSAgent* cssAgent, PassRefPtrWillBeRawPtr<EnableCallback> callback)
143    : m_cssAgent(cssAgent)
144    , m_callback(callback)
145{
146}
147
148void InspectorCSSAgent::InspectorResourceContentLoaderCallback::trace(Visitor* visitor)
149{
150    visitor->trace(m_cssAgent);
151    visitor->trace(m_callback);
152    VoidCallback::trace(visitor);
153}
154
155void 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
165class InspectorCSSAgent::SetStyleSheetTextAction FINAL : public InspectorCSSAgent::StyleSheetAction {
166    WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
167public:
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
211private:
212    RefPtrWillBeMember<InspectorStyleSheetBase> m_styleSheet;
213    String m_text;
214    String m_oldText;
215};
216
217class InspectorCSSAgent::SetPropertyTextAction FINAL : public InspectorCSSAgent::StyleSheetAction {
218    WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
219public:
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
273private:
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
282class InspectorCSSAgent::SetRuleSelectorAction FINAL : public InspectorCSSAgent::StyleSheetAction {
283    WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
284public:
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
317private:
318    RefPtrWillBeMember<InspectorStyleSheet> m_styleSheet;
319    InspectorCSSId m_cssId;
320    String m_selector;
321    String m_oldSelector;
322};
323
324class InspectorCSSAgent::AddRuleAction FINAL : public InspectorCSSAgent::StyleSheetAction {
325    WTF_MAKE_NONCOPYABLE(AddRuleAction);
326public:
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
364private:
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
373CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
374{
375    if (!rule || rule->type() != CSSRule::STYLE_RULE)
376        return 0;
377    return toCSSStyleRule(rule);
378}
379
380InspectorCSSAgent::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
395InspectorCSSAgent::~InspectorCSSAgent()
396{
397#if !ENABLE(OILPAN)
398    ASSERT(!m_domAgent);
399    reset();
400#endif
401}
402
403void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
404{
405    ASSERT(!m_frontend);
406    m_frontend = frontend->css();
407}
408
409void InspectorCSSAgent::clearFrontend()
410{
411    ASSERT(m_frontend);
412    ErrorString error;
413    disable(&error);
414    m_frontend = 0;
415}
416
417void InspectorCSSAgent::discardAgent()
418{
419    m_domAgent->setDOMListener(0);
420    m_domAgent = nullptr;
421}
422
423void InspectorCSSAgent::restore()
424{
425    if (m_state->getBoolean(CSSAgentState::cssAgentEnabled))
426        wasEnabled();
427}
428
429void 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
439void 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
451void InspectorCSSAgent::resetNonPersistentData()
452{
453    resetPseudoStates();
454}
455
456void InspectorCSSAgent::enable(ErrorString*, PassRefPtrWillBeRawPtr<EnableCallback> prpCallback)
457{
458    m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
459    if (!m_pageAgent->resourceContentLoader()) {
460        wasEnabled();
461        prpCallback->sendSuccess();
462        return;
463    }
464    m_pageAgent->resourceContentLoader()->ensureResourcesContentLoaded(new InspectorCSSAgent::InspectorResourceContentLoaderCallback(this, prpCallback));
465}
466
467void InspectorCSSAgent::wasEnabled()
468{
469    if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
470        // We were disabled while fetching resources.
471        return;
472    }
473
474    m_instrumentingAgents->setInspectorCSSAgent(this);
475    WillBeHeapVector<RawPtrWillBeMember<Document> > documents = m_domAgent->documents();
476    for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = documents.begin(); it != documents.end(); ++it)
477        updateActiveStyleSheets(*it, InitialFrontendLoad);
478}
479
480void InspectorCSSAgent::disable(ErrorString*)
481{
482    reset();
483    m_instrumentingAgents->setInspectorCSSAgent(0);
484    m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
485}
486
487void InspectorCSSAgent::didCommitLoadForMainFrame()
488{
489    reset();
490    m_pageAgent->clearEditedResourcesContent();
491}
492
493void InspectorCSSAgent::mediaQueryResultChanged()
494{
495    flushPendingFrontendMessages();
496    m_frontend->mediaQueryResultChanged();
497}
498
499void InspectorCSSAgent::willMutateRules()
500{
501    ++m_styleSheetsPendingMutation;
502}
503
504void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet)
505{
506    --m_styleSheetsPendingMutation;
507    ASSERT(m_styleSheetsPendingMutation >= 0);
508
509    if (!styleSheetEditInProgress()) {
510        Document* owner = styleSheet->ownerDocument();
511        if (owner)
512            owner->modifiedStyleSheet(styleSheet, FullStyleUpdate);
513    }
514}
515
516void InspectorCSSAgent::willMutateStyle()
517{
518    m_styleDeclarationPendingMutation = true;
519}
520
521void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle)
522{
523    ASSERT(m_styleDeclarationPendingMutation);
524    m_styleDeclarationPendingMutation = false;
525    if (!styleSheetEditInProgress() && !isInlineStyle) {
526        CSSStyleSheet* parentSheet = style->parentStyleSheet();
527        Document* owner = parentSheet ? parentSheet->ownerDocument() : 0;
528        if (owner)
529            owner->modifiedStyleSheet(parentSheet, FullStyleUpdate);
530    }
531}
532
533void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document)
534{
535    if (styleSheetEditInProgress())
536        return;
537
538    m_invalidatedDocuments.add(document);
539    if (m_creatingViaInspectorStyleSheet)
540        flushPendingFrontendMessages();
541}
542
543void InspectorCSSAgent::updateActiveStyleSheets(Document* document, StyleSheetsUpdateType styleSheetsUpdateType)
544{
545    WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > newSheetsVector;
546    InspectorCSSAgent::collectAllDocumentStyleSheets(document, newSheetsVector);
547    setActiveStyleSheets(document, newSheetsVector, styleSheetsUpdateType);
548}
549
550void InspectorCSSAgent::setActiveStyleSheets(Document* document, const WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& allSheetsVector, StyleSheetsUpdateType styleSheetsUpdateType)
551{
552    bool isInitialFrontendLoad = styleSheetsUpdateType == InitialFrontendLoad;
553
554    WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >* documentCSSStyleSheets = m_documentToCSSStyleSheets.get(document);
555    if (!documentCSSStyleSheets) {
556        documentCSSStyleSheets = new WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >();
557        OwnPtrWillBeRawPtr<WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > > documentCSSStyleSheetsPtr = adoptPtrWillBeNoop(documentCSSStyleSheets);
558        m_documentToCSSStyleSheets.set(document, documentCSSStyleSheetsPtr.release());
559    }
560
561    WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > removedSheets(*documentCSSStyleSheets);
562    WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > addedSheets;
563    for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::const_iterator it = allSheetsVector.begin(); it != allSheetsVector.end(); ++it) {
564        CSSStyleSheet* cssStyleSheet = *it;
565        if (removedSheets.contains(cssStyleSheet)) {
566            removedSheets.remove(cssStyleSheet);
567            if (isInitialFrontendLoad)
568                addedSheets.append(cssStyleSheet);
569        } else {
570            addedSheets.append(cssStyleSheet);
571        }
572    }
573
574    for (WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) {
575        CSSStyleSheet* cssStyleSheet = *it;
576        RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet);
577        ASSERT(inspectorStyleSheet);
578
579        documentCSSStyleSheets->remove(cssStyleSheet);
580        if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
581            String id = unbindStyleSheet(inspectorStyleSheet.get());
582            if (m_frontend && !isInitialFrontendLoad)
583                m_frontend->styleSheetRemoved(id);
584        }
585    }
586
587    for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) {
588        CSSStyleSheet* cssStyleSheet = *it;
589        bool isNew = isInitialFrontendLoad || !m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet);
590        if (isNew) {
591            InspectorStyleSheet* newStyleSheet = bindStyleSheet(cssStyleSheet);
592            documentCSSStyleSheets->add(cssStyleSheet);
593            if (m_frontend)
594                m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo());
595        }
596    }
597
598    if (documentCSSStyleSheets->isEmpty())
599        m_documentToCSSStyleSheets.remove(document);
600}
601
602void InspectorCSSAgent::documentDetached(Document* document)
603{
604    m_invalidatedDocuments.remove(document);
605    setActiveStyleSheets(document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >(), ExistingFrontendRefresh);
606}
607
608bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
609{
610    if (m_nodeIdToForcedPseudoState.isEmpty())
611        return false;
612
613    int nodeId = m_domAgent->boundNodeId(element);
614    if (!nodeId)
615        return false;
616
617    NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
618    if (it == m_nodeIdToForcedPseudoState.end())
619        return false;
620
621    unsigned forcedPseudoState = it->value;
622    switch (pseudoType) {
623    case CSSSelector::PseudoActive:
624        return forcedPseudoState & PseudoActive;
625    case CSSSelector::PseudoFocus:
626        return forcedPseudoState & PseudoFocus;
627    case CSSSelector::PseudoHover:
628        return forcedPseudoState & PseudoHover;
629    case CSSSelector::PseudoVisited:
630        return forcedPseudoState & PseudoVisited;
631    default:
632        return false;
633    }
634}
635
636void InspectorCSSAgent::getMediaQueries(ErrorString* errorString, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> >& medias)
637{
638    medias = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
639    for (IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.begin(); it != m_idToInspectorStyleSheet.end(); ++it) {
640        RefPtrWillBeRawPtr<InspectorStyleSheet> styleSheet = it->value;
641        collectMediaQueriesFromStyleSheet(styleSheet->pageStyleSheet(), medias.get());
642        const CSSRuleVector& flatRules = styleSheet->flatRules();
643        for (unsigned i = 0; i < flatRules.size(); ++i) {
644            CSSRule* rule = flatRules.at(i).get();
645            if (rule->type() == CSSRule::MEDIA_RULE)
646                collectMediaQueriesFromRule(rule, medias.get());
647        }
648    }
649}
650
651void 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)
652{
653    Element* element = elementForId(errorString, nodeId);
654    if (!element) {
655        *errorString = "Node not found";
656        return;
657    }
658
659    Element* originalElement = element;
660    PseudoId elementPseudoId = element->pseudoId();
661    if (elementPseudoId) {
662        element = element->parentOrShadowHostElement();
663        if (!element) {
664            *errorString = "Pseudo element has no parent";
665            return;
666        }
667    }
668
669    Document* ownerDocument = element->ownerDocument();
670    // A non-active document has no styles.
671    if (!ownerDocument->isActive())
672        return;
673
674    // FIXME: It's really gross for the inspector to reach in and access StyleResolver
675    // directly here. We need to provide the Inspector better APIs to get this information
676    // without grabbing at internal style classes!
677
678    // Matched rules.
679    StyleResolver& styleResolver = ownerDocument->ensureStyleResolver();
680
681    RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
682    matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), originalElement);
683
684    // Pseudo elements.
685    if (!elementPseudoId && !asBool(excludePseudo)) {
686        RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create();
687        for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
688            RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
689            if (matchedRules && matchedRules->length()) {
690                RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create()
691                    .setPseudoId(static_cast<int>(pseudoId))
692                    .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), element));
693                pseudoElements->addItem(matches.release());
694            }
695        }
696
697        pseudoIdMatches = pseudoElements.release();
698    }
699
700    // Inherited styles.
701    if (!elementPseudoId && !asBool(excludeInherited)) {
702        RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create();
703        Element* parentElement = element->parentElement();
704        while (parentElement) {
705            StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver();
706            RefPtrWillBeRawPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules);
707            RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create()
708                .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), parentElement));
709            if (parentElement->style() && parentElement->style()->length()) {
710                InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
711                if (styleSheet)
712                    entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
713            }
714
715            entries->addItem(entry.release());
716            parentElement = parentElement->parentElement();
717        }
718
719        inheritedEntries = entries.release();
720    }
721}
722
723void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle)
724{
725    Element* element = elementForId(errorString, nodeId);
726    if (!element)
727        return;
728
729    InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
730    if (!styleSheet)
731        return;
732
733    inlineStyle = styleSheet->buildObjectForStyle(element->style());
734    RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element);
735    attributesStyle = attributes ? attributes.release() : nullptr;
736}
737
738void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style)
739{
740    Node* node = m_domAgent->assertNode(errorString, nodeId);
741    if (!node)
742        return;
743
744    RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
745    RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
746    style = inspectorStyle->buildArrayForComputedStyle();
747}
748
749void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats)
750{
751    for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) {
752        RenderStyle* style = renderer->style(box->isFirstLineStyle());
753        const Font& font = style->font();
754        TextRun run = box->constructTextRunForInspector(style, font);
755        WidthIterator it(&font, run, 0, false);
756        GlyphBuffer glyphBuffer;
757        it.advance(run.length(), &glyphBuffer);
758        for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
759            String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName();
760            if (familyName.isNull())
761                familyName = "";
762            fontStats->add(familyName);
763        }
764    }
765}
766
767void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId,
768    String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts)
769{
770    Node* node = m_domAgent->assertNode(errorString, nodeId);
771    if (!node)
772        return;
773
774    RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
775    *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily);
776
777    WillBeHeapVector<RawPtrWillBeMember<Text> > textNodes;
778    if (node->nodeType() == Node::TEXT_NODE) {
779        if (node->renderer())
780            textNodes.append(toText(node));
781    } else {
782        for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
783            if (child->nodeType() == Node::TEXT_NODE && child->renderer())
784                textNodes.append(toText(child));
785        }
786    }
787
788    HashCountedSet<String> fontStats;
789    for (size_t i = 0; i < textNodes.size(); ++i) {
790        RenderText* renderer = textNodes[i]->renderer();
791        collectPlatformFontsForRenderer(renderer, &fontStats);
792        if (renderer->isTextFragment()) {
793            RenderTextFragment* textFragment = toRenderTextFragment(renderer);
794            if (textFragment->firstLetter()) {
795                RenderBoxModelObject* firstLetter = textFragment->firstLetter();
796                for (RenderObject* current = firstLetter->slowFirstChild(); current; current = current->nextSibling()) {
797                    if (current->isText())
798                        collectPlatformFontsForRenderer(toRenderText(current), &fontStats);
799                }
800            }
801        }
802    }
803
804    platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create();
805    for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) {
806        RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create()
807            .setFamilyName(it->key)
808            .setGlyphCount(it->value);
809        platformFonts->addItem(platformFont);
810    }
811}
812
813void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
814{
815    InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
816    if (!inspectorStyleSheet)
817        return;
818
819    inspectorStyleSheet->getText(result);
820}
821
822void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
823{
824    InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
825    if (!inspectorStyleSheet) {
826        *errorString = "Style sheet with id " + styleSheetId + " not found";
827        return;
828    }
829
830    TrackExceptionState exceptionState;
831    m_domAgent->history()->perform(adoptRefWillBeNoop(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState);
832    *errorString = InspectorDOMAgent::toErrorString(exceptionState);
833}
834
835static bool extractRangeComponent(ErrorString* errorString, const RefPtr<JSONObject>& range, const String& component, unsigned& result)
836{
837    int parsedValue = 0;
838    if (!range->getNumber(component, &parsedValue) || parsedValue < 0) {
839        *errorString = "range." + component + " must be a non-negative integer";
840        return false;
841    }
842    result = parsedValue;
843    return true;
844}
845
846static bool jsonRangeToSourceRange(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const RefPtr<JSONObject>& range, SourceRange* sourceRange)
847{
848    unsigned startLineNumber = 0;
849    unsigned startColumn = 0;
850    unsigned endLineNumber = 0;
851    unsigned endColumn = 0;
852    if (!extractRangeComponent(errorString, range, "startLine", startLineNumber)
853        || !extractRangeComponent(errorString, range, "startColumn", startColumn)
854        || !extractRangeComponent(errorString, range, "endLine", endLineNumber)
855        || !extractRangeComponent(errorString, range, "endColumn", endColumn))
856        return false;
857
858    unsigned startOffset = 0;
859    unsigned endOffset = 0;
860    bool success = inspectorStyleSheet->lineNumberAndColumnToOffset(startLineNumber, startColumn, &startOffset)
861        && inspectorStyleSheet->lineNumberAndColumnToOffset(endLineNumber, endColumn, &endOffset);
862    if (!success) {
863        *errorString = "Specified range is out of bounds";
864        return false;
865    }
866
867    if (startOffset > endOffset) {
868        *errorString = "Range start must not succeed its end";
869        return false;
870    }
871    sourceRange->start = startOffset;
872    sourceRange->end = endOffset;
873    return true;
874}
875
876void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
877{
878    InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
879    if (!inspectorStyleSheet)
880        return;
881    SourceRange propertyRange;
882    if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &propertyRange))
883        return;
884    InspectorCSSId compoundId;
885    unsigned propertyIndex;
886    bool overwrite;
887    if (!inspectorStyleSheet->findPropertyByRange(propertyRange, &compoundId, &propertyIndex, &overwrite)) {
888        *errorString = "Source range didn't match any existing property source range nor any property insertion point";
889        return;
890    }
891
892    TrackExceptionState exceptionState;
893    bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState);
894    if (success)
895        result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
896    *errorString = InspectorDOMAgent::toErrorString(exceptionState);
897}
898
899void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
900{
901    InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
902    if (!inspectorStyleSheet)
903        return;
904    SourceRange selectorRange;
905    if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange))
906        return;
907    InspectorCSSId compoundId;
908    if (!inspectorStyleSheet->findRuleBySelectorRange(selectorRange, &compoundId)) {
909        *errorString = "Source range didn't match any rule selector source range";
910        return;
911    }
912
913    TrackExceptionState exceptionState;
914    bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState);
915    if (success) {
916        CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId);
917        result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
918    }
919    *errorString = InspectorDOMAgent::toErrorString(exceptionState);
920}
921
922void InspectorCSSAgent::createStyleSheet(ErrorString* errorString, const String& frameId, TypeBuilder::CSS::StyleSheetId* outStyleSheetId)
923{
924    LocalFrame* frame = m_pageAgent->frameForId(frameId);
925    if (!frame) {
926        *errorString = "Frame not found";
927        return;
928    }
929
930    Document* document = frame->document();
931    if (!document) {
932        *errorString = "Frame does not have a document";
933        return;
934    }
935
936    InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(document, true);
937    if (!inspectorStyleSheet) {
938        *errorString = "No target stylesheet found";
939        return;
940    }
941
942    updateActiveStyleSheets(document, ExistingFrontendRefresh);
943
944    *outStyleSheetId = inspectorStyleSheet->id();
945}
946
947void InspectorCSSAgent::addRule(ErrorString* errorString, const String& styleSheetId, const String& ruleText, const RefPtr<JSONObject>& location, RefPtr<TypeBuilder::CSS::CSSRule>& result)
948{
949    InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
950    if (!inspectorStyleSheet)
951        return;
952    SourceRange ruleLocation;
953    if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, location, &ruleLocation))
954        return;
955
956    TrackExceptionState exceptionState;
957    RefPtrWillBeRawPtr<AddRuleAction> action = adoptRefWillBeNoop(new AddRuleAction(inspectorStyleSheet, ruleText, ruleLocation));
958    bool success = m_domAgent->history()->perform(action, exceptionState);
959    if (!success) {
960        *errorString = InspectorDOMAgent::toErrorString(exceptionState);
961        return;
962    }
963
964    InspectorCSSId ruleId = action->newRuleId();
965    CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
966    result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
967}
968
969void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses)
970{
971    Element* element = m_domAgent->assertElement(errorString, nodeId);
972    if (!element)
973        return;
974
975    unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get());
976    NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
977    unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value;
978    bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
979    if (!needStyleRecalc)
980        return;
981
982    if (forcedPseudoState)
983        m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
984    else
985        m_nodeIdToForcedPseudoState.remove(nodeId);
986    element->ownerDocument()->setNeedsStyleRecalc(SubtreeStyleChange);
987}
988
989PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet)
990{
991    // Make certain compilers happy by initializing |source| up-front.
992    TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
993    switch (mediaListSource) {
994    case MediaListSourceMediaRule:
995        source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
996        break;
997    case MediaListSourceImportRule:
998        source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
999        break;
1000    case MediaListSourceLinkedSheet:
1001        source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
1002        break;
1003    case MediaListSourceInlineSheet:
1004        source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
1005        break;
1006    }
1007
1008    const MediaQuerySet* queries = media->queries();
1009    const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queryVector = queries->queryVector();
1010    OwnPtr<MediaQueryEvaluator> mediaEvaluator = adoptPtr(new MediaQueryEvaluator(parentStyleSheet->ownerDocument()->frame()));
1011
1012    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQuery> > mediaListArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQuery>::create();
1013    RefPtr<MediaValues> mediaValues = MediaValues::createDynamicIfFrameExists(parentStyleSheet->ownerDocument()->frame());
1014    bool hasMediaQueryItems = false;
1015    for (size_t i = 0; i < queryVector.size(); ++i) {
1016        MediaQuery* query = queryVector.at(i).get();
1017        const ExpressionHeapVector& expressions = query->expressions();
1018        RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > expressionArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression>::create();
1019        bool hasExpressionItems = false;
1020        for (size_t j = 0; j < expressions.size(); ++j) {
1021            MediaQueryExp* mediaQueryExp = expressions.at(j).get();
1022            MediaQueryExpValue expValue = mediaQueryExp->expValue();
1023            if (!expValue.isValue)
1024                continue;
1025            const char* valueName = CSSPrimitiveValue::unitTypeToString(expValue.unit);
1026            RefPtr<TypeBuilder::CSS::MediaQueryExpression> mediaQueryExpression = TypeBuilder::CSS::MediaQueryExpression::create()
1027                .setValue(expValue.value)
1028                .setUnit(String(valueName))
1029                .setFeature(mediaQueryExp->mediaFeature());
1030
1031            int computedLength;
1032            if (mediaValues->computeLength(expValue.value, expValue.unit, computedLength))
1033                mediaQueryExpression->setComputedLength(computedLength);
1034
1035            expressionArray->addItem(mediaQueryExpression);
1036            hasExpressionItems = true;
1037        }
1038        if (!hasExpressionItems)
1039            continue;
1040        RefPtr<TypeBuilder::CSS::MediaQuery> mediaQuery = TypeBuilder::CSS::MediaQuery::create()
1041            .setActive(mediaEvaluator->eval(query, nullptr))
1042            .setExpressions(expressionArray);
1043        mediaListArray->addItem(mediaQuery);
1044        hasMediaQueryItems = true;
1045    }
1046
1047    RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
1048        .setText(media->mediaText())
1049        .setSource(source);
1050    if (hasMediaQueryItems)
1051        mediaObject->setMediaList(mediaListArray);
1052
1053    if (parentStyleSheet && mediaListSource != MediaListSourceLinkedSheet) {
1054        if (InspectorStyleSheet* inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet))
1055            mediaObject->setParentStyleSheetId(inspectorStyleSheet->id());
1056    }
1057
1058    if (!sourceURL.isEmpty()) {
1059        mediaObject->setSourceURL(sourceURL);
1060
1061        CSSRule* parentRule = media->parentRule();
1062        if (!parentRule)
1063            return mediaObject.release();
1064        InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet());
1065        RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule);
1066        if (mediaRange)
1067            mediaObject->setRange(mediaRange);
1068    }
1069    return mediaObject.release();
1070}
1071
1072bool InspectorCSSAgent::collectMediaQueriesFromStyleSheet(CSSStyleSheet* styleSheet, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1073{
1074    bool addedItems = false;
1075    MediaList* mediaList = styleSheet->media();
1076    String sourceURL;
1077    if (mediaList && mediaList->length()) {
1078        Document* doc = styleSheet->ownerDocument();
1079        if (doc)
1080            sourceURL = doc->url();
1081        else if (!styleSheet->contents()->baseURL().isEmpty())
1082            sourceURL = styleSheet->contents()->baseURL();
1083        else
1084            sourceURL = "";
1085        mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet));
1086        addedItems = true;
1087    }
1088    return addedItems;
1089}
1090
1091bool InspectorCSSAgent::collectMediaQueriesFromRule(CSSRule* rule, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1092{
1093    MediaList* mediaList;
1094    String sourceURL;
1095    CSSStyleSheet* parentStyleSheet = 0;
1096    bool isMediaRule = true;
1097    bool addedItems = false;
1098    if (rule->type() == CSSRule::MEDIA_RULE) {
1099        CSSMediaRule* mediaRule = toCSSMediaRule(rule);
1100        mediaList = mediaRule->media();
1101        parentStyleSheet = mediaRule->parentStyleSheet();
1102    } else if (rule->type() == CSSRule::IMPORT_RULE) {
1103        CSSImportRule* importRule = toCSSImportRule(rule);
1104        mediaList = importRule->media();
1105        parentStyleSheet = importRule->parentStyleSheet();
1106        isMediaRule = false;
1107    } else {
1108        mediaList = 0;
1109    }
1110
1111    if (parentStyleSheet) {
1112        sourceURL = parentStyleSheet->contents()->baseURL();
1113        if (sourceURL.isEmpty())
1114            sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
1115    } else {
1116        sourceURL = "";
1117    }
1118
1119    if (mediaList && mediaList->length()) {
1120        mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet));
1121        addedItems = true;
1122    }
1123    return addedItems;
1124}
1125
1126PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule)
1127{
1128    if (!rule)
1129        return nullptr;
1130    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
1131    bool hasItems = false;
1132    CSSRule* parentRule = rule;
1133    while (parentRule) {
1134        hasItems = collectMediaQueriesFromRule(parentRule, mediaArray.get()) || hasItems;
1135        if (parentRule->parentRule()) {
1136            parentRule = parentRule->parentRule();
1137        } else {
1138            CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
1139            while (styleSheet) {
1140                hasItems = collectMediaQueriesFromStyleSheet(styleSheet, mediaArray.get()) || hasItems;
1141                parentRule = styleSheet->ownerRule();
1142                if (parentRule)
1143                    break;
1144                styleSheet = styleSheet->parentStyleSheet();
1145            }
1146        }
1147    }
1148    return hasItems ? mediaArray : nullptr;
1149}
1150
1151InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
1152{
1153    NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1154    if (it != m_nodeToInspectorStyleSheet.end())
1155        return it->value.get();
1156
1157    CSSStyleDeclaration* style = element->style();
1158    if (!style)
1159        return 0;
1160
1161    String newStyleSheetId = String::number(m_lastStyleSheetId++);
1162    RefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, this);
1163    m_idToInspectorStyleSheetForInlineStyle.set(newStyleSheetId, inspectorStyleSheet);
1164    m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
1165    return inspectorStyleSheet.get();
1166}
1167
1168Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
1169{
1170    Node* node = m_domAgent->nodeForId(nodeId);
1171    if (!node) {
1172        *errorString = "No node with given id found";
1173        return 0;
1174    }
1175    if (!node->isElementNode()) {
1176        *errorString = "Not an element node";
1177        return 0;
1178    }
1179    return toElement(node);
1180}
1181
1182// static
1183void InspectorCSSAgent::collectAllDocumentStyleSheets(Document* document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1184{
1185    const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets = document->styleEngine()->activeStyleSheetsForInspector();
1186    for (WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >::const_iterator it = activeStyleSheets.begin(); it != activeStyleSheets.end(); ++it) {
1187        CSSStyleSheet* styleSheet = it->get();
1188        InspectorCSSAgent::collectStyleSheets(styleSheet, result);
1189    }
1190}
1191
1192// static
1193void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1194{
1195    result.append(styleSheet);
1196    for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
1197        CSSRule* rule = styleSheet->item(i);
1198        if (rule->type() == CSSRule::IMPORT_RULE) {
1199            CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
1200            if (importedStyleSheet)
1201                InspectorCSSAgent::collectStyleSheets(importedStyleSheet, result);
1202        }
1203    }
1204}
1205
1206InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
1207{
1208    RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
1209    if (!inspectorStyleSheet) {
1210        String id = String::number(m_lastStyleSheetId++);
1211        Document* document = styleSheet->ownerDocument();
1212        inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
1213        m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
1214        m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
1215        if (m_creatingViaInspectorStyleSheet)
1216            m_documentToViaInspectorStyleSheet.add(document, inspectorStyleSheet);
1217    }
1218    return inspectorStyleSheet.get();
1219}
1220
1221String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
1222{
1223    String id = inspectorStyleSheet->id();
1224    m_idToInspectorStyleSheet.remove(id);
1225    if (inspectorStyleSheet->pageStyleSheet())
1226        m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
1227    return id;
1228}
1229
1230InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
1231{
1232    if (!document) {
1233        ASSERT(!createIfAbsent);
1234        return 0;
1235    }
1236
1237    if (!document->isHTMLDocument() && !document->isSVGDocument())
1238        return 0;
1239
1240    RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToViaInspectorStyleSheet.get(document);
1241    if (inspectorStyleSheet || !createIfAbsent)
1242        return inspectorStyleSheet.get();
1243
1244    TrackExceptionState exceptionState;
1245    RefPtrWillBeRawPtr<Element> styleElement = document->createElement("style", exceptionState);
1246    if (!exceptionState.hadException())
1247        styleElement->setAttribute("type", "text/css", exceptionState);
1248    if (!exceptionState.hadException()) {
1249        ContainerNode* targetNode;
1250        // HEAD is absent in ImageDocuments, for example.
1251        if (document->head())
1252            targetNode = document->head();
1253        else if (document->body())
1254            targetNode = document->body();
1255        else
1256            return 0;
1257
1258        InlineStyleOverrideScope overrideScope(document);
1259        m_creatingViaInspectorStyleSheet = true;
1260        targetNode->appendChild(styleElement, exceptionState);
1261        // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation.
1262        // We just need to pick the respective InspectorStyleSheet from m_documentToViaInspectorStyleSheet.
1263        m_creatingViaInspectorStyleSheet = false;
1264    }
1265    if (exceptionState.hadException())
1266        return 0;
1267
1268    return m_documentToViaInspectorStyleSheet.get(document);
1269}
1270
1271InspectorStyleSheet* InspectorCSSAgent::assertInspectorStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1272{
1273    IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
1274    if (it == m_idToInspectorStyleSheet.end()) {
1275        *errorString = "No style sheet with given id found";
1276        return 0;
1277    }
1278    return it->value.get();
1279}
1280
1281InspectorStyleSheetBase* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1282{
1283    String placeholder;
1284    InspectorStyleSheetBase* result = assertInspectorStyleSheetForId(&placeholder, styleSheetId);
1285    if (result)
1286        return result;
1287    IdToInspectorStyleSheetForInlineStyle::iterator it = m_idToInspectorStyleSheetForInlineStyle.find(styleSheetId);
1288    if (it == m_idToInspectorStyleSheetForInlineStyle.end()) {
1289        *errorString = "No style sheet with given id found";
1290        return 0;
1291    }
1292    return it->value.get();
1293}
1294
1295TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
1296{
1297    if (m_creatingViaInspectorStyleSheet)
1298        return TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1299
1300    TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular;
1301    if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
1302        origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent;
1303    else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->isDocumentNode())
1304        origin = TypeBuilder::CSS::StyleSheetOrigin::User;
1305    else {
1306        InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
1307        if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
1308            origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1309    }
1310    return origin;
1311}
1312
1313PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
1314{
1315    if (!rule)
1316        return nullptr;
1317
1318    // CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent pointers if they are coming from
1319    // user agent stylesheets. To work around this issue, we use CSSOM wrapper created by inspector.
1320    if (!rule->parentStyleSheet()) {
1321        if (!m_inspectorUserAgentStyleSheet)
1322            m_inspectorUserAgentStyleSheet = CSSStyleSheet::create(CSSDefaultStyleSheets::instance().defaultStyleSheet());
1323        rule->setParentStyleSheet(m_inspectorUserAgentStyleSheet.get());
1324    }
1325    return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule));
1326}
1327
1328static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId)
1329{
1330    // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector."
1331    // As such, check the last selector in the tag history.
1332    for (; !selector->isLastInTagHistory(); ++selector) { }
1333    PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO;
1334
1335    // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements.
1336    // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too.
1337    return selectorPseudoId == elementPseudoId;
1338}
1339
1340PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, Element* element)
1341{
1342    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create();
1343    if (!ruleList)
1344        return result.release();
1345
1346    for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1347        CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
1348        RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule);
1349        if (!ruleObject)
1350            continue;
1351        RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create();
1352        const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1353        long index = 0;
1354        PseudoId elementPseudoId = element->pseudoId();
1355        for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) {
1356            const CSSSelector* firstTagHistorySelector = selector;
1357            bool matched = false;
1358            if (elementPseudoId)
1359                matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|.
1360            matched |= element->matches(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION);
1361            if (matched)
1362                matchingSelectors->addItem(index);
1363            ++index;
1364        }
1365        RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create()
1366            .setRule(ruleObject.release())
1367            .setMatchingSelectors(matchingSelectors.release());
1368        result->addItem(match);
1369    }
1370
1371    return result;
1372}
1373
1374PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
1375{
1376    if (!element->isStyledElement())
1377        return nullptr;
1378
1379    // FIXME: Ugliness below.
1380    StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle());
1381    if (!attributeStyle)
1382        return nullptr;
1383
1384    MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle);
1385
1386    RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0);
1387    return inspectorStyle->buildObjectForStyle();
1388}
1389
1390void InspectorCSSAgent::didRemoveDocument(Document* document)
1391{
1392    if (document)
1393        m_documentToViaInspectorStyleSheet.remove(document);
1394}
1395
1396void InspectorCSSAgent::didRemoveDOMNode(Node* node)
1397{
1398    if (!node)
1399        return;
1400
1401    int nodeId = m_domAgent->boundNodeId(node);
1402    if (nodeId)
1403        m_nodeIdToForcedPseudoState.remove(nodeId);
1404
1405    NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
1406    if (it == m_nodeToInspectorStyleSheet.end())
1407        return;
1408
1409    m_idToInspectorStyleSheetForInlineStyle.remove(it->value->id());
1410    m_nodeToInspectorStyleSheet.remove(node);
1411}
1412
1413void InspectorCSSAgent::didModifyDOMAttr(Element* element)
1414{
1415    if (!element)
1416        return;
1417
1418    NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1419    if (it == m_nodeToInspectorStyleSheet.end())
1420        return;
1421
1422    it->value->didModifyElementAttribute();
1423}
1424
1425void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheetBase* styleSheet)
1426{
1427    flushPendingFrontendMessages();
1428    m_frontend->styleSheetChanged(styleSheet->id());
1429}
1430
1431void InspectorCSSAgent::willReparseStyleSheet()
1432{
1433    ASSERT(!m_isSettingStyleSheetText);
1434    m_isSettingStyleSheetText = true;
1435}
1436
1437void InspectorCSSAgent::didReparseStyleSheet()
1438{
1439    ASSERT(m_isSettingStyleSheetText);
1440    m_isSettingStyleSheetText = false;
1441}
1442
1443void InspectorCSSAgent::resetPseudoStates()
1444{
1445    WillBeHeapHashSet<RawPtrWillBeMember<Document> > documentsToChange;
1446    for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
1447        Element* element = toElement(m_domAgent->nodeForId(it->key));
1448        if (element && element->ownerDocument())
1449            documentsToChange.add(element->ownerDocument());
1450    }
1451
1452    m_nodeIdToForcedPseudoState.clear();
1453    for (WillBeHeapHashSet<RawPtrWillBeMember<Document> >::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
1454        (*it)->setNeedsStyleRecalc(SubtreeStyleChange);
1455}
1456
1457void InspectorCSSAgent::trace(Visitor* visitor)
1458{
1459    visitor->trace(m_domAgent);
1460    visitor->trace(m_pageAgent);
1461    visitor->trace(m_resourceAgent);
1462#if ENABLE(OILPAN)
1463    visitor->trace(m_idToInspectorStyleSheet);
1464    visitor->trace(m_idToInspectorStyleSheetForInlineStyle);
1465    visitor->trace(m_cssStyleSheetToInspectorStyleSheet);
1466    visitor->trace(m_documentToCSSStyleSheets);
1467    visitor->trace(m_invalidatedDocuments);
1468    visitor->trace(m_nodeToInspectorStyleSheet);
1469    visitor->trace(m_documentToViaInspectorStyleSheet);
1470#endif
1471    visitor->trace(m_inspectorUserAgentStyleSheet);
1472    InspectorBaseAgent::trace(visitor);
1473}
1474
1475} // namespace blink
1476
1477