2 * Copyright (C) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "core/inspector/InspectorCSSAgent.h"
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/CSSPropertyNames.h"
31 #include "core/InspectorTypeBuilder.h"
32 #include "core/StylePropertyShorthand.h"
33 #include "core/css/CSSComputedStyleDeclaration.h"
34 #include "core/css/CSSDefaultStyleSheets.h"
35 #include "core/css/CSSImportRule.h"
36 #include "core/css/CSSMediaRule.h"
37 #include "core/css/CSSRule.h"
38 #include "core/css/CSSRuleList.h"
39 #include "core/css/CSSStyleRule.h"
40 #include "core/css/CSSStyleSheet.h"
41 #include "core/css/MediaList.h"
42 #include "core/css/MediaQuery.h"
43 #include "core/css/MediaValues.h"
44 #include "core/css/StylePropertySet.h"
45 #include "core/css/StyleRule.h"
46 #include "core/css/StyleSheet.h"
47 #include "core/css/StyleSheetContents.h"
48 #include "core/css/StyleSheetList.h"
49 #include "core/css/resolver/StyleResolver.h"
50 #include "core/dom/Node.h"
51 #include "core/dom/StyleEngine.h"
52 #include "core/dom/Text.h"
53 #include "core/frame/LocalFrame.h"
54 #include "core/html/HTMLHeadElement.h"
55 #include "core/html/VoidCallback.h"
56 #include "core/inspector/InspectorHistory.h"
57 #include "core/inspector/InspectorPageAgent.h"
58 #include "core/inspector/InspectorResourceAgent.h"
59 #include "core/inspector/InspectorResourceContentLoader.h"
60 #include "core/inspector/InspectorState.h"
61 #include "core/inspector/InstrumentingAgents.h"
62 #include "core/loader/DocumentLoader.h"
63 #include "core/page/Page.h"
64 #include "core/rendering/InlineTextBox.h"
65 #include "core/rendering/RenderObject.h"
66 #include "core/rendering/RenderObjectInlines.h"
67 #include "core/rendering/RenderText.h"
68 #include "core/rendering/RenderTextFragment.h"
69 #include "platform/fonts/Font.h"
70 #include "platform/fonts/GlyphBuffer.h"
71 #include "platform/fonts/shaping/SimpleShaper.h"
72 #include "platform/text/TextRun.h"
73 #include "wtf/CurrentTime.h"
74 #include "wtf/text/CString.h"
75 #include "wtf/text/StringConcatenate.h"
77 namespace CSSAgentState {
78 static const char cssAgentEnabled[] = "cssAgentEnabled";
81 typedef blink::InspectorBackendDispatcher::CSSCommandHandler::EnableCallback EnableCallback;
85 enum ForcePseudoClassFlags {
89 PseudoActive = 1 << 2,
90 PseudoVisited = 1 << 3
93 static unsigned computePseudoClassMask(JSONArray* pseudoClassArray)
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())
102 unsigned result = PseudoNone;
103 for (size_t i = 0; i < pseudoClassArray->length(); ++i) {
104 RefPtr<JSONValue> pseudoClassValue = pseudoClassArray->get(i);
106 bool success = pseudoClassValue->asString(&pseudoClass);
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;
122 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
123 WTF_MAKE_NONCOPYABLE(StyleSheetAction);
125 StyleSheetAction(const String& name)
126 : InspectorHistory::Action(name)
131 class InspectorCSSAgent::InspectorResourceContentLoaderCallback final : public VoidCallback {
133 InspectorResourceContentLoaderCallback(InspectorCSSAgent*, PassRefPtrWillBeRawPtr<EnableCallback>);
134 virtual void trace(Visitor*) override;
135 virtual void handleEvent() override;
138 RawPtrWillBeMember<InspectorCSSAgent> m_cssAgent;
139 RefPtrWillBeMember<EnableCallback> m_callback;
142 InspectorCSSAgent::InspectorResourceContentLoaderCallback::InspectorResourceContentLoaderCallback(InspectorCSSAgent* cssAgent, PassRefPtrWillBeRawPtr<EnableCallback> callback)
143 : m_cssAgent(cssAgent)
144 , m_callback(callback)
148 void InspectorCSSAgent::InspectorResourceContentLoaderCallback::trace(Visitor* visitor)
150 visitor->trace(m_cssAgent);
151 visitor->trace(m_callback);
152 VoidCallback::trace(visitor);
155 void InspectorCSSAgent::InspectorResourceContentLoaderCallback::handleEvent()
157 // enable always succeeds.
158 if (!m_callback->isActive())
161 m_cssAgent->wasEnabled();
162 m_callback->sendSuccess();
165 class InspectorCSSAgent::SetStyleSheetTextAction final : public InspectorCSSAgent::StyleSheetAction {
166 WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
168 SetStyleSheetTextAction(InspectorStyleSheetBase* styleSheet, const String& text)
169 : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText")
170 , m_styleSheet(styleSheet)
175 virtual bool perform(ExceptionState& exceptionState) override
177 if (!m_styleSheet->getText(&m_oldText))
179 return redo(exceptionState);
182 virtual bool undo(ExceptionState& exceptionState) override
184 return m_styleSheet->setText(m_oldText, exceptionState);
187 virtual bool redo(ExceptionState& exceptionState) override
189 return m_styleSheet->setText(m_text, exceptionState);
192 virtual String mergeId() override
194 return String::format("SetStyleSheetText %s", m_styleSheet->id().utf8().data());
197 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) override
199 ASSERT(action->mergeId() == mergeId());
201 SetStyleSheetTextAction* other = static_cast<SetStyleSheetTextAction*>(action.get());
202 m_text = other->m_text;
205 virtual void trace(Visitor* visitor) override
207 visitor->trace(m_styleSheet);
208 InspectorCSSAgent::StyleSheetAction::trace(visitor);
212 RefPtrWillBeMember<InspectorStyleSheetBase> m_styleSheet;
217 class InspectorCSSAgent::SetPropertyTextAction final : public InspectorCSSAgent::StyleSheetAction {
218 WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
220 SetPropertyTextAction(InspectorStyleSheetBase* styleSheet, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
221 : InspectorCSSAgent::StyleSheetAction("SetPropertyText")
222 , m_styleSheet(styleSheet)
224 , m_propertyIndex(propertyIndex)
226 , m_overwrite(overwrite)
230 virtual String toString() override
232 return mergeId() + ": " + m_oldStyleText + " -> " + m_text;
235 virtual bool perform(ExceptionState& exceptionState) override
237 return redo(exceptionState);
240 virtual bool undo(ExceptionState& exceptionState) override
243 return m_styleSheet->setStyleText(m_cssId, m_oldStyleText);
246 virtual bool redo(ExceptionState& exceptionState) override
248 if (!m_styleSheet->getStyleText(m_cssId, &m_oldStyleText))
250 bool result = m_styleSheet->setPropertyText(m_cssId, m_propertyIndex, m_text, m_overwrite, exceptionState);
254 virtual String mergeId() override
256 return String::format("SetPropertyText %s:%u:%s", m_styleSheet->id().utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
259 virtual void merge(PassRefPtrWillBeRawPtr<Action> action) override
261 ASSERT(action->mergeId() == mergeId());
263 SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
264 m_text = other->m_text;
267 virtual void trace(Visitor* visitor) override
269 visitor->trace(m_styleSheet);
270 InspectorCSSAgent::StyleSheetAction::trace(visitor);
274 RefPtrWillBeMember<InspectorStyleSheetBase> m_styleSheet;
275 InspectorCSSId m_cssId;
276 unsigned m_propertyIndex;
278 String m_oldStyleText;
282 class InspectorCSSAgent::SetRuleSelectorAction final : public InspectorCSSAgent::StyleSheetAction {
283 WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
285 SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector)
286 : InspectorCSSAgent::StyleSheetAction("SetRuleSelector")
287 , m_styleSheet(styleSheet)
289 , m_selector(selector)
293 virtual bool perform(ExceptionState& exceptionState) override
295 m_oldSelector = m_styleSheet->ruleSelector(m_cssId, exceptionState);
296 if (exceptionState.hadException())
298 return redo(exceptionState);
301 virtual bool undo(ExceptionState& exceptionState) override
303 return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector, exceptionState);
306 virtual bool redo(ExceptionState& exceptionState) override
308 return m_styleSheet->setRuleSelector(m_cssId, m_selector, exceptionState);
311 virtual void trace(Visitor* visitor) override
313 visitor->trace(m_styleSheet);
314 InspectorCSSAgent::StyleSheetAction::trace(visitor);
318 RefPtrWillBeMember<InspectorStyleSheet> m_styleSheet;
319 InspectorCSSId m_cssId;
321 String m_oldSelector;
324 class InspectorCSSAgent::AddRuleAction final : public InspectorCSSAgent::StyleSheetAction {
325 WTF_MAKE_NONCOPYABLE(AddRuleAction);
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)
335 virtual bool perform(ExceptionState& exceptionState) override
337 return redo(exceptionState);
340 virtual bool undo(ExceptionState& exceptionState) override
342 return m_styleSheet->deleteRule(m_newId, m_oldText, exceptionState);
345 virtual bool redo(ExceptionState& exceptionState) override
347 if (!m_styleSheet->getText(&m_oldText))
349 CSSStyleRule* cssStyleRule = m_styleSheet->addRule(m_ruleText, m_location, exceptionState);
350 if (exceptionState.hadException())
352 m_newId = m_styleSheet->ruleId(cssStyleRule);
356 InspectorCSSId newRuleId() { return m_newId; }
358 virtual void trace(Visitor* visitor) override
360 visitor->trace(m_styleSheet);
361 InspectorCSSAgent::StyleSheetAction::trace(visitor);
365 RefPtrWillBeMember<InspectorStyleSheet> m_styleSheet;
366 InspectorCSSId m_newId;
369 SourceRange m_location;
373 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
375 if (!rule || rule->type() != CSSRule::STYLE_RULE)
377 return toCSSStyleRule(rule);
380 InspectorCSSAgent::InspectorCSSAgent(InspectorDOMAgent* domAgent, InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent)
381 : InspectorBaseAgent<InspectorCSSAgent>("CSS")
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)
392 m_domAgent->setDOMListener(this);
395 InspectorCSSAgent::~InspectorCSSAgent()
403 void InspectorCSSAgent::setFrontend(InspectorFrontend* frontend)
406 m_frontend = frontend->css();
409 void InspectorCSSAgent::clearFrontend()
417 void InspectorCSSAgent::discardAgent()
419 m_domAgent->setDOMListener(0);
420 m_domAgent = nullptr;
423 void InspectorCSSAgent::restore()
425 if (m_state->getBoolean(CSSAgentState::cssAgentEnabled))
429 void InspectorCSSAgent::flushPendingFrontendMessages()
431 if (!m_invalidatedDocuments.size())
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);
439 void InspectorCSSAgent::reset()
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();
451 void InspectorCSSAgent::resetNonPersistentData()
456 void InspectorCSSAgent::enable(ErrorString* errorString, PassRefPtrWillBeRawPtr<EnableCallback> prpCallback)
458 if (!m_domAgent->enabled()) {
459 *errorString = "DOM agent needs to be enabled first.";
462 m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
463 if (!m_pageAgent->resourceContentLoader()) {
465 prpCallback->sendSuccess();
468 m_pageAgent->resourceContentLoader()->ensureResourcesContentLoaded(new InspectorCSSAgent::InspectorResourceContentLoaderCallback(this, prpCallback));
471 void InspectorCSSAgent::wasEnabled()
473 if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
474 // We were disabled while fetching resources.
478 m_instrumentingAgents->setInspectorCSSAgent(this);
479 WillBeHeapVector<RawPtrWillBeMember<Document> > documents = m_domAgent->documents();
480 for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = documents.begin(); it != documents.end(); ++it)
481 updateActiveStyleSheets(*it, InitialFrontendLoad);
484 void InspectorCSSAgent::disable(ErrorString*)
487 m_instrumentingAgents->setInspectorCSSAgent(0);
488 m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
491 void InspectorCSSAgent::didCommitLoadForMainFrame()
494 m_pageAgent->clearEditedResourcesContent();
497 void InspectorCSSAgent::mediaQueryResultChanged()
499 flushPendingFrontendMessages();
500 m_frontend->mediaQueryResultChanged();
503 void InspectorCSSAgent::willMutateRules()
505 ++m_styleSheetsPendingMutation;
508 void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet)
510 --m_styleSheetsPendingMutation;
511 ASSERT(m_styleSheetsPendingMutation >= 0);
513 if (!styleSheetEditInProgress()) {
514 Document* owner = styleSheet->ownerDocument();
516 owner->modifiedStyleSheet(styleSheet, FullStyleUpdate);
520 void InspectorCSSAgent::willMutateStyle()
522 m_styleDeclarationPendingMutation = true;
525 void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle)
527 ASSERT(m_styleDeclarationPendingMutation);
528 m_styleDeclarationPendingMutation = false;
529 if (!styleSheetEditInProgress() && !isInlineStyle) {
530 CSSStyleSheet* parentSheet = style->parentStyleSheet();
531 Document* owner = parentSheet ? parentSheet->ownerDocument() : 0;
533 owner->modifiedStyleSheet(parentSheet, FullStyleUpdate);
537 void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document)
539 if (styleSheetEditInProgress())
542 m_invalidatedDocuments.add(document);
543 if (m_creatingViaInspectorStyleSheet)
544 flushPendingFrontendMessages();
547 void InspectorCSSAgent::updateActiveStyleSheets(Document* document, StyleSheetsUpdateType styleSheetsUpdateType)
549 WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > newSheetsVector;
550 InspectorCSSAgent::collectAllDocumentStyleSheets(document, newSheetsVector);
551 setActiveStyleSheets(document, newSheetsVector, styleSheetsUpdateType);
554 void InspectorCSSAgent::setActiveStyleSheets(Document* document, const WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& allSheetsVector, StyleSheetsUpdateType styleSheetsUpdateType)
556 bool isInitialFrontendLoad = styleSheetsUpdateType == InitialFrontendLoad;
558 WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >* documentCSSStyleSheets = m_documentToCSSStyleSheets.get(document);
559 if (!documentCSSStyleSheets) {
560 documentCSSStyleSheets = new WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >();
561 OwnPtrWillBeRawPtr<WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > > documentCSSStyleSheetsPtr = adoptPtrWillBeNoop(documentCSSStyleSheets);
562 m_documentToCSSStyleSheets.set(document, documentCSSStyleSheetsPtr.release());
565 WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > removedSheets(*documentCSSStyleSheets);
566 WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > addedSheets;
567 for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::const_iterator it = allSheetsVector.begin(); it != allSheetsVector.end(); ++it) {
568 CSSStyleSheet* cssStyleSheet = *it;
569 if (removedSheets.contains(cssStyleSheet)) {
570 removedSheets.remove(cssStyleSheet);
571 if (isInitialFrontendLoad)
572 addedSheets.append(cssStyleSheet);
574 addedSheets.append(cssStyleSheet);
578 for (WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) {
579 CSSStyleSheet* cssStyleSheet = *it;
580 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet);
581 ASSERT(inspectorStyleSheet);
583 documentCSSStyleSheets->remove(cssStyleSheet);
584 if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
585 String id = unbindStyleSheet(inspectorStyleSheet.get());
586 if (m_frontend && !isInitialFrontendLoad)
587 m_frontend->styleSheetRemoved(id);
591 for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) {
592 CSSStyleSheet* cssStyleSheet = *it;
593 bool isNew = isInitialFrontendLoad || !m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet);
595 InspectorStyleSheet* newStyleSheet = bindStyleSheet(cssStyleSheet);
596 documentCSSStyleSheets->add(cssStyleSheet);
598 m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo());
602 if (documentCSSStyleSheets->isEmpty())
603 m_documentToCSSStyleSheets.remove(document);
606 void InspectorCSSAgent::documentDetached(Document* document)
608 m_invalidatedDocuments.remove(document);
609 setActiveStyleSheets(document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >(), ExistingFrontendRefresh);
612 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
614 if (m_nodeIdToForcedPseudoState.isEmpty())
617 int nodeId = m_domAgent->boundNodeId(element);
621 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
622 if (it == m_nodeIdToForcedPseudoState.end())
625 unsigned forcedPseudoState = it->value;
626 switch (pseudoType) {
627 case CSSSelector::PseudoActive:
628 return forcedPseudoState & PseudoActive;
629 case CSSSelector::PseudoFocus:
630 return forcedPseudoState & PseudoFocus;
631 case CSSSelector::PseudoHover:
632 return forcedPseudoState & PseudoHover;
633 case CSSSelector::PseudoVisited:
634 return forcedPseudoState & PseudoVisited;
640 void InspectorCSSAgent::getMediaQueries(ErrorString* errorString, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> >& medias)
642 medias = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
643 for (IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.begin(); it != m_idToInspectorStyleSheet.end(); ++it) {
644 RefPtrWillBeRawPtr<InspectorStyleSheet> styleSheet = it->value;
645 collectMediaQueriesFromStyleSheet(styleSheet->pageStyleSheet(), medias.get());
646 const CSSRuleVector& flatRules = styleSheet->flatRules();
647 for (unsigned i = 0; i < flatRules.size(); ++i) {
648 CSSRule* rule = flatRules.at(i).get();
649 if (rule->type() == CSSRule::MEDIA_RULE || rule->type() == CSSRule::IMPORT_RULE)
650 collectMediaQueriesFromRule(rule, medias.get());
655 void InspectorCSSAgent::getMatchedStylesForNode(ErrorString* errorString, int nodeId, const bool* excludePseudo, const bool* excludeInherited, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> >& matchedCSSRules, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> >& pseudoIdMatches, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> >& inheritedEntries)
657 Element* element = elementForId(errorString, nodeId);
659 *errorString = "Node not found";
663 Element* originalElement = element;
664 PseudoId elementPseudoId = element->pseudoId();
665 if (elementPseudoId) {
666 element = element->parentOrShadowHostElement();
668 *errorString = "Pseudo element has no parent";
673 Document* ownerDocument = element->ownerDocument();
674 // A non-active document has no styles.
675 if (!ownerDocument->isActive())
678 // FIXME: It's really gross for the inspector to reach in and access StyleResolver
679 // directly here. We need to provide the Inspector better APIs to get this information
680 // without grabbing at internal style classes!
683 StyleResolver& styleResolver = ownerDocument->ensureStyleResolver();
685 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
686 matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), originalElement, NOPSEUDO);
689 if (!elementPseudoId && !asBool(excludePseudo)) {
690 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create();
691 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
692 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
693 if (matchedRules && matchedRules->length()) {
694 RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create()
695 .setPseudoId(static_cast<int>(pseudoId))
696 .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), element, pseudoId));
697 pseudoElements->addItem(matches.release());
701 pseudoIdMatches = pseudoElements.release();
705 if (!elementPseudoId && !asBool(excludeInherited)) {
706 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create();
707 Element* parentElement = element->parentElement();
708 while (parentElement) {
709 StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver();
710 RefPtrWillBeRawPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules);
711 RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create()
712 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), parentElement, NOPSEUDO));
713 if (parentElement->style() && parentElement->style()->length()) {
714 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
716 entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
719 entries->addItem(entry.release());
720 parentElement = parentElement->parentElement();
723 inheritedEntries = entries.release();
727 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle)
729 Element* element = elementForId(errorString, nodeId);
733 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
737 inlineStyle = styleSheet->buildObjectForStyle(element->style());
738 RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element);
739 attributesStyle = attributes ? attributes.release() : nullptr;
742 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style)
744 Node* node = m_domAgent->assertNode(errorString, nodeId);
748 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
749 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
750 style = inspectorStyle->buildArrayForComputedStyle();
753 void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats)
755 for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) {
756 RenderStyle* style = renderer->style(box->isFirstLineStyle());
757 const Font& font = style->font();
758 TextRun run = box->constructTextRunForInspector(style, font);
759 SimpleShaper shaper(&font, run);
760 GlyphBuffer glyphBuffer;
761 shaper.advance(run.length(), &glyphBuffer);
762 for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
763 String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName();
764 if (familyName.isNull())
766 fontStats->add(familyName);
771 void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId,
772 String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts)
774 Node* node = m_domAgent->assertNode(errorString, nodeId);
778 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
779 *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily);
781 WillBeHeapVector<RawPtrWillBeMember<Text> > textNodes;
782 if (node->nodeType() == Node::TEXT_NODE) {
783 if (node->renderer())
784 textNodes.append(toText(node));
786 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
787 if (child->nodeType() == Node::TEXT_NODE && child->renderer())
788 textNodes.append(toText(child));
792 HashCountedSet<String> fontStats;
793 for (size_t i = 0; i < textNodes.size(); ++i) {
794 RenderText* renderer = textNodes[i]->renderer();
795 collectPlatformFontsForRenderer(renderer, &fontStats);
796 if (renderer->isTextFragment()) {
797 RenderTextFragment* textFragment = toRenderTextFragment(renderer);
798 if (textFragment->firstLetter()) {
799 RenderBoxModelObject* firstLetter = textFragment->firstLetter();
800 for (RenderObject* current = firstLetter->slowFirstChild(); current; current = current->nextSibling()) {
801 if (current->isText())
802 collectPlatformFontsForRenderer(toRenderText(current), &fontStats);
808 platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create();
809 for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) {
810 RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create()
811 .setFamilyName(it->key)
812 .setGlyphCount(it->value);
813 platformFonts->addItem(platformFont);
817 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
819 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
820 if (!inspectorStyleSheet)
823 inspectorStyleSheet->getText(result);
826 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
828 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
829 if (!inspectorStyleSheet) {
830 *errorString = "Style sheet with id " + styleSheetId + " not found";
834 TrackExceptionState exceptionState;
835 m_domAgent->history()->perform(adoptRefWillBeNoop(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState);
836 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
839 static bool extractRangeComponent(ErrorString* errorString, const RefPtr<JSONObject>& range, const String& component, unsigned& result)
842 if (!range->getNumber(component, &parsedValue) || parsedValue < 0) {
843 *errorString = "range." + component + " must be a non-negative integer";
846 result = parsedValue;
850 static bool jsonRangeToSourceRange(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const RefPtr<JSONObject>& range, SourceRange* sourceRange)
852 unsigned startLineNumber = 0;
853 unsigned startColumn = 0;
854 unsigned endLineNumber = 0;
855 unsigned endColumn = 0;
856 if (!extractRangeComponent(errorString, range, "startLine", startLineNumber)
857 || !extractRangeComponent(errorString, range, "startColumn", startColumn)
858 || !extractRangeComponent(errorString, range, "endLine", endLineNumber)
859 || !extractRangeComponent(errorString, range, "endColumn", endColumn))
862 unsigned startOffset = 0;
863 unsigned endOffset = 0;
864 bool success = inspectorStyleSheet->lineNumberAndColumnToOffset(startLineNumber, startColumn, &startOffset)
865 && inspectorStyleSheet->lineNumberAndColumnToOffset(endLineNumber, endColumn, &endOffset);
867 *errorString = "Specified range is out of bounds";
871 if (startOffset > endOffset) {
872 *errorString = "Range start must not succeed its end";
875 sourceRange->start = startOffset;
876 sourceRange->end = endOffset;
880 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
882 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
883 if (!inspectorStyleSheet)
885 SourceRange propertyRange;
886 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &propertyRange))
888 InspectorCSSId compoundId;
889 unsigned propertyIndex;
891 if (!inspectorStyleSheet->findPropertyByRange(propertyRange, &compoundId, &propertyIndex, &overwrite)) {
892 *errorString = "Source range didn't match any existing property source range nor any property insertion point";
896 TrackExceptionState exceptionState;
897 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState);
899 result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
900 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
903 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
905 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
906 if (!inspectorStyleSheet)
908 SourceRange selectorRange;
909 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange))
911 InspectorCSSId compoundId;
912 if (!inspectorStyleSheet->findRuleBySelectorRange(selectorRange, &compoundId)) {
913 *errorString = "Source range didn't match any rule selector source range";
917 TrackExceptionState exceptionState;
918 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState);
920 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId);
921 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
923 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
926 void InspectorCSSAgent::createStyleSheet(ErrorString* errorString, const String& frameId, TypeBuilder::CSS::StyleSheetId* outStyleSheetId)
928 LocalFrame* frame = m_pageAgent->frameForId(frameId);
930 *errorString = "Frame not found";
934 Document* document = frame->document();
936 *errorString = "Frame does not have a document";
940 InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(document, true);
941 if (!inspectorStyleSheet) {
942 *errorString = "No target stylesheet found";
946 updateActiveStyleSheets(document, ExistingFrontendRefresh);
948 *outStyleSheetId = inspectorStyleSheet->id();
951 void InspectorCSSAgent::addRule(ErrorString* errorString, const String& styleSheetId, const String& ruleText, const RefPtr<JSONObject>& location, RefPtr<TypeBuilder::CSS::CSSRule>& result)
953 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
954 if (!inspectorStyleSheet)
956 SourceRange ruleLocation;
957 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, location, &ruleLocation))
960 TrackExceptionState exceptionState;
961 RefPtrWillBeRawPtr<AddRuleAction> action = adoptRefWillBeNoop(new AddRuleAction(inspectorStyleSheet, ruleText, ruleLocation));
962 bool success = m_domAgent->history()->perform(action, exceptionState);
964 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
968 InspectorCSSId ruleId = action->newRuleId();
969 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
970 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
973 void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses)
975 Element* element = m_domAgent->assertElement(errorString, nodeId);
979 unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get());
980 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
981 unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value;
982 bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
983 if (!needStyleRecalc)
986 if (forcedPseudoState)
987 m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
989 m_nodeIdToForcedPseudoState.remove(nodeId);
990 element->ownerDocument()->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Inspector));
993 PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet)
995 // Make certain compilers happy by initializing |source| up-front.
996 TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
997 switch (mediaListSource) {
998 case MediaListSourceMediaRule:
999 source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
1001 case MediaListSourceImportRule:
1002 source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
1004 case MediaListSourceLinkedSheet:
1005 source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
1007 case MediaListSourceInlineSheet:
1008 source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
1012 const MediaQuerySet* queries = media->queries();
1013 const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queryVector = queries->queryVector();
1014 OwnPtr<MediaQueryEvaluator> mediaEvaluator = adoptPtr(new MediaQueryEvaluator(parentStyleSheet->ownerDocument()->frame()));
1016 InspectorStyleSheet* inspectorStyleSheet = parentStyleSheet ? m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet) : nullptr;
1017 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQuery> > mediaListArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQuery>::create();
1018 RefPtr<MediaValues> mediaValues = MediaValues::createDynamicIfFrameExists(parentStyleSheet->ownerDocument()->frame());
1019 bool hasMediaQueryItems = false;
1020 for (size_t i = 0; i < queryVector.size(); ++i) {
1021 MediaQuery* query = queryVector.at(i).get();
1022 const ExpressionHeapVector& expressions = query->expressions();
1023 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > expressionArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression>::create();
1024 bool hasExpressionItems = false;
1025 for (size_t j = 0; j < expressions.size(); ++j) {
1026 MediaQueryExp* mediaQueryExp = expressions.at(j).get();
1027 MediaQueryExpValue expValue = mediaQueryExp->expValue();
1028 if (!expValue.isValue)
1030 const char* valueName = CSSPrimitiveValue::unitTypeToString(expValue.unit);
1031 RefPtr<TypeBuilder::CSS::MediaQueryExpression> mediaQueryExpression = TypeBuilder::CSS::MediaQueryExpression::create()
1032 .setValue(expValue.value)
1033 .setUnit(String(valueName))
1034 .setFeature(mediaQueryExp->mediaFeature());
1036 if (inspectorStyleSheet && media->parentRule()) {
1037 RefPtr<TypeBuilder::CSS::SourceRange> valueRange = inspectorStyleSheet->mediaQueryExpValueSourceRange(media->parentRule(), i, j);
1039 mediaQueryExpression->setValueRange(valueRange);
1043 if (mediaValues->computeLength(expValue.value, expValue.unit, computedLength))
1044 mediaQueryExpression->setComputedLength(computedLength);
1046 expressionArray->addItem(mediaQueryExpression);
1047 hasExpressionItems = true;
1049 if (!hasExpressionItems)
1051 RefPtr<TypeBuilder::CSS::MediaQuery> mediaQuery = TypeBuilder::CSS::MediaQuery::create()
1052 .setActive(mediaEvaluator->eval(query, nullptr))
1053 .setExpressions(expressionArray);
1054 mediaListArray->addItem(mediaQuery);
1055 hasMediaQueryItems = true;
1058 RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
1059 .setText(media->mediaText())
1061 if (hasMediaQueryItems)
1062 mediaObject->setMediaList(mediaListArray);
1064 if (inspectorStyleSheet && mediaListSource != MediaListSourceLinkedSheet)
1065 mediaObject->setParentStyleSheetId(inspectorStyleSheet->id());
1067 if (!sourceURL.isEmpty()) {
1068 mediaObject->setSourceURL(sourceURL);
1070 CSSRule* parentRule = media->parentRule();
1072 return mediaObject.release();
1073 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet());
1074 RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule);
1076 mediaObject->setRange(mediaRange);
1078 return mediaObject.release();
1081 bool InspectorCSSAgent::collectMediaQueriesFromStyleSheet(CSSStyleSheet* styleSheet, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1083 bool addedItems = false;
1084 MediaList* mediaList = styleSheet->media();
1086 if (mediaList && mediaList->length()) {
1087 Document* doc = styleSheet->ownerDocument();
1089 sourceURL = doc->url();
1090 else if (!styleSheet->contents()->baseURL().isEmpty())
1091 sourceURL = styleSheet->contents()->baseURL();
1094 mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet));
1100 bool InspectorCSSAgent::collectMediaQueriesFromRule(CSSRule* rule, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1102 MediaList* mediaList;
1104 CSSStyleSheet* parentStyleSheet = 0;
1105 bool isMediaRule = true;
1106 bool addedItems = false;
1107 if (rule->type() == CSSRule::MEDIA_RULE) {
1108 CSSMediaRule* mediaRule = toCSSMediaRule(rule);
1109 mediaList = mediaRule->media();
1110 parentStyleSheet = mediaRule->parentStyleSheet();
1111 } else if (rule->type() == CSSRule::IMPORT_RULE) {
1112 CSSImportRule* importRule = toCSSImportRule(rule);
1113 mediaList = importRule->media();
1114 parentStyleSheet = importRule->parentStyleSheet();
1115 isMediaRule = false;
1120 if (parentStyleSheet) {
1121 sourceURL = parentStyleSheet->contents()->baseURL();
1122 if (sourceURL.isEmpty())
1123 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
1128 if (mediaList && mediaList->length()) {
1129 mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet));
1135 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule)
1139 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
1140 bool hasItems = false;
1141 CSSRule* parentRule = rule;
1142 while (parentRule) {
1143 hasItems = collectMediaQueriesFromRule(parentRule, mediaArray.get()) || hasItems;
1144 if (parentRule->parentRule()) {
1145 parentRule = parentRule->parentRule();
1147 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
1148 while (styleSheet) {
1149 hasItems = collectMediaQueriesFromStyleSheet(styleSheet, mediaArray.get()) || hasItems;
1150 parentRule = styleSheet->ownerRule();
1153 styleSheet = styleSheet->parentStyleSheet();
1157 return hasItems ? mediaArray : nullptr;
1160 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
1162 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1163 if (it != m_nodeToInspectorStyleSheet.end())
1164 return it->value.get();
1166 CSSStyleDeclaration* style = element->style();
1170 String newStyleSheetId = String::number(m_lastStyleSheetId++);
1171 RefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, this);
1172 m_idToInspectorStyleSheetForInlineStyle.set(newStyleSheetId, inspectorStyleSheet);
1173 m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
1174 return inspectorStyleSheet.get();
1177 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
1179 Node* node = m_domAgent->nodeForId(nodeId);
1181 *errorString = "No node with given id found";
1184 if (!node->isElementNode()) {
1185 *errorString = "Not an element node";
1188 return toElement(node);
1192 void InspectorCSSAgent::collectAllDocumentStyleSheets(Document* document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1194 const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets = document->styleEngine()->activeStyleSheetsForInspector();
1195 for (WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >::const_iterator it = activeStyleSheets.begin(); it != activeStyleSheets.end(); ++it) {
1196 CSSStyleSheet* styleSheet = it->get();
1197 InspectorCSSAgent::collectStyleSheets(styleSheet, result);
1202 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1204 result.append(styleSheet);
1205 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
1206 CSSRule* rule = styleSheet->item(i);
1207 if (rule->type() == CSSRule::IMPORT_RULE) {
1208 CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
1209 if (importedStyleSheet)
1210 InspectorCSSAgent::collectStyleSheets(importedStyleSheet, result);
1215 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
1217 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
1218 if (!inspectorStyleSheet) {
1219 String id = String::number(m_lastStyleSheetId++);
1220 Document* document = styleSheet->ownerDocument();
1221 inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
1222 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
1223 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
1224 if (m_creatingViaInspectorStyleSheet)
1225 m_documentToViaInspectorStyleSheet.add(document, inspectorStyleSheet);
1227 return inspectorStyleSheet.get();
1230 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
1232 String id = inspectorStyleSheet->id();
1233 m_idToInspectorStyleSheet.remove(id);
1234 if (inspectorStyleSheet->pageStyleSheet())
1235 m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
1239 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
1242 ASSERT(!createIfAbsent);
1246 if (!document->isHTMLDocument() && !document->isSVGDocument())
1249 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToViaInspectorStyleSheet.get(document);
1250 if (inspectorStyleSheet || !createIfAbsent)
1251 return inspectorStyleSheet.get();
1253 TrackExceptionState exceptionState;
1254 RefPtrWillBeRawPtr<Element> styleElement = document->createElement("style", exceptionState);
1255 if (!exceptionState.hadException())
1256 styleElement->setAttribute("type", "text/css", exceptionState);
1257 if (!exceptionState.hadException()) {
1258 ContainerNode* targetNode;
1259 // HEAD is absent in ImageDocuments, for example.
1260 if (document->head())
1261 targetNode = document->head();
1262 else if (document->body())
1263 targetNode = document->body();
1267 InlineStyleOverrideScope overrideScope(document);
1268 m_creatingViaInspectorStyleSheet = true;
1269 targetNode->appendChild(styleElement, exceptionState);
1270 // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation.
1271 // We just need to pick the respective InspectorStyleSheet from m_documentToViaInspectorStyleSheet.
1272 m_creatingViaInspectorStyleSheet = false;
1274 if (exceptionState.hadException())
1277 return m_documentToViaInspectorStyleSheet.get(document);
1280 InspectorStyleSheet* InspectorCSSAgent::assertInspectorStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1282 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
1283 if (it == m_idToInspectorStyleSheet.end()) {
1284 *errorString = "No style sheet with given id found";
1287 return it->value.get();
1290 InspectorStyleSheetBase* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1293 InspectorStyleSheetBase* result = assertInspectorStyleSheetForId(&placeholder, styleSheetId);
1296 IdToInspectorStyleSheetForInlineStyle::iterator it = m_idToInspectorStyleSheetForInlineStyle.find(styleSheetId);
1297 if (it == m_idToInspectorStyleSheetForInlineStyle.end()) {
1298 *errorString = "No style sheet with given id found";
1301 return it->value.get();
1304 TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
1306 if (m_creatingViaInspectorStyleSheet)
1307 return TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1309 TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular;
1310 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
1311 origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent;
1312 else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->isDocumentNode())
1313 origin = TypeBuilder::CSS::StyleSheetOrigin::User;
1315 InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
1316 if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
1317 origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1322 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
1327 // CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent pointers if they are coming from
1328 // user agent stylesheets. To work around this issue, we use CSSOM wrapper created by inspector.
1329 if (!rule->parentStyleSheet()) {
1330 if (!m_inspectorUserAgentStyleSheet)
1331 m_inspectorUserAgentStyleSheet = CSSStyleSheet::create(CSSDefaultStyleSheets::instance().defaultStyleSheet());
1332 rule->setParentStyleSheet(m_inspectorUserAgentStyleSheet.get());
1334 return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule));
1337 static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId)
1339 // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector."
1340 // As such, check the last selector in the tag history.
1341 for (; !selector->isLastInTagHistory(); ++selector) { }
1342 PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO;
1344 // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements.
1345 // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too.
1346 return selectorPseudoId == elementPseudoId;
1349 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, Element* element, PseudoId matchesForPseudoId)
1351 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create();
1353 return result.release();
1355 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1356 CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
1357 RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule);
1360 RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create();
1361 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1363 PseudoId elementPseudoId = matchesForPseudoId ? matchesForPseudoId : element->pseudoId();
1364 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) {
1365 const CSSSelector* firstTagHistorySelector = selector;
1366 bool matched = false;
1367 if (elementPseudoId)
1368 matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|.
1370 matched = element->matches(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION);
1372 matchingSelectors->addItem(index);
1375 RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create()
1376 .setRule(ruleObject.release())
1377 .setMatchingSelectors(matchingSelectors.release());
1378 result->addItem(match);
1384 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
1386 if (!element->isStyledElement())
1389 // FIXME: Ugliness below.
1390 StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle());
1391 if (!attributeStyle)
1394 MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle);
1396 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0);
1397 return inspectorStyle->buildObjectForStyle();
1400 void InspectorCSSAgent::didRemoveDocument(Document* document)
1403 m_documentToViaInspectorStyleSheet.remove(document);
1406 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
1411 int nodeId = m_domAgent->boundNodeId(node);
1413 m_nodeIdToForcedPseudoState.remove(nodeId);
1415 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
1416 if (it == m_nodeToInspectorStyleSheet.end())
1419 m_idToInspectorStyleSheetForInlineStyle.remove(it->value->id());
1420 m_nodeToInspectorStyleSheet.remove(node);
1423 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
1428 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1429 if (it == m_nodeToInspectorStyleSheet.end())
1432 it->value->didModifyElementAttribute();
1435 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheetBase* styleSheet)
1437 flushPendingFrontendMessages();
1438 m_frontend->styleSheetChanged(styleSheet->id());
1441 void InspectorCSSAgent::willReparseStyleSheet()
1443 ASSERT(!m_isSettingStyleSheetText);
1444 m_isSettingStyleSheetText = true;
1447 void InspectorCSSAgent::didReparseStyleSheet()
1449 ASSERT(m_isSettingStyleSheetText);
1450 m_isSettingStyleSheetText = false;
1453 void InspectorCSSAgent::resetPseudoStates()
1455 WillBeHeapHashSet<RawPtrWillBeMember<Document> > documentsToChange;
1456 for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
1457 Element* element = toElement(m_domAgent->nodeForId(it->key));
1458 if (element && element->ownerDocument())
1459 documentsToChange.add(element->ownerDocument());
1462 m_nodeIdToForcedPseudoState.clear();
1463 for (WillBeHeapHashSet<RawPtrWillBeMember<Document> >::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
1464 (*it)->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Inspector));
1467 void InspectorCSSAgent::trace(Visitor* visitor)
1469 visitor->trace(m_domAgent);
1470 visitor->trace(m_pageAgent);
1471 visitor->trace(m_resourceAgent);
1473 visitor->trace(m_idToInspectorStyleSheet);
1474 visitor->trace(m_idToInspectorStyleSheetForInlineStyle);
1475 visitor->trace(m_cssStyleSheetToInspectorStyleSheet);
1476 visitor->trace(m_documentToCSSStyleSheets);
1477 visitor->trace(m_invalidatedDocuments);
1478 visitor->trace(m_nodeToInspectorStyleSheet);
1479 visitor->trace(m_documentToViaInspectorStyleSheet);
1481 visitor->trace(m_inspectorUserAgentStyleSheet);
1482 InspectorBaseAgent::trace(visitor);
1485 } // namespace blink