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