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/WidthIterator.h"
72 #include "platform/text/TextRun.h"
73 #include "wtf/CurrentTime.h"
74 #include "wtf/text/CString.h"
75 #include "wtf/text/StringConcatenate.h"
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*, PassRefPtrWillBeRawPtr<EnableCallback> prpCallback)
458 m_state->setBoolean(CSSAgentState::cssAgentEnabled, true);
459 if (!m_pageAgent->resourceContentLoader()) {
461 prpCallback->sendSuccess();
464 m_pageAgent->resourceContentLoader()->ensureResourcesContentLoaded(new InspectorCSSAgent::InspectorResourceContentLoaderCallback(this, prpCallback));
467 void InspectorCSSAgent::wasEnabled()
469 if (!m_state->getBoolean(CSSAgentState::cssAgentEnabled)) {
470 // We were disabled while fetching resources.
474 m_instrumentingAgents->setInspectorCSSAgent(this);
475 WillBeHeapVector<RawPtrWillBeMember<Document> > documents = m_domAgent->documents();
476 for (WillBeHeapVector<RawPtrWillBeMember<Document> >::iterator it = documents.begin(); it != documents.end(); ++it)
477 updateActiveStyleSheets(*it, InitialFrontendLoad);
480 void InspectorCSSAgent::disable(ErrorString*)
483 m_instrumentingAgents->setInspectorCSSAgent(0);
484 m_state->setBoolean(CSSAgentState::cssAgentEnabled, false);
487 void InspectorCSSAgent::didCommitLoadForMainFrame()
490 m_pageAgent->clearEditedResourcesContent();
493 void InspectorCSSAgent::mediaQueryResultChanged()
495 flushPendingFrontendMessages();
496 m_frontend->mediaQueryResultChanged();
499 void InspectorCSSAgent::willMutateRules()
501 ++m_styleSheetsPendingMutation;
504 void InspectorCSSAgent::didMutateRules(CSSStyleSheet* styleSheet)
506 --m_styleSheetsPendingMutation;
507 ASSERT(m_styleSheetsPendingMutation >= 0);
509 if (!styleSheetEditInProgress()) {
510 Document* owner = styleSheet->ownerDocument();
512 owner->modifiedStyleSheet(styleSheet, FullStyleUpdate);
516 void InspectorCSSAgent::willMutateStyle()
518 m_styleDeclarationPendingMutation = true;
521 void InspectorCSSAgent::didMutateStyle(CSSStyleDeclaration* style, bool isInlineStyle)
523 ASSERT(m_styleDeclarationPendingMutation);
524 m_styleDeclarationPendingMutation = false;
525 if (!styleSheetEditInProgress() && !isInlineStyle) {
526 CSSStyleSheet* parentSheet = style->parentStyleSheet();
527 Document* owner = parentSheet ? parentSheet->ownerDocument() : 0;
529 owner->modifiedStyleSheet(parentSheet, FullStyleUpdate);
533 void InspectorCSSAgent::activeStyleSheetsUpdated(Document* document)
535 if (styleSheetEditInProgress())
538 m_invalidatedDocuments.add(document);
539 if (m_creatingViaInspectorStyleSheet)
540 flushPendingFrontendMessages();
543 void InspectorCSSAgent::updateActiveStyleSheets(Document* document, StyleSheetsUpdateType styleSheetsUpdateType)
545 WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > newSheetsVector;
546 InspectorCSSAgent::collectAllDocumentStyleSheets(document, newSheetsVector);
547 setActiveStyleSheets(document, newSheetsVector, styleSheetsUpdateType);
550 void InspectorCSSAgent::setActiveStyleSheets(Document* document, const WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& allSheetsVector, StyleSheetsUpdateType styleSheetsUpdateType)
552 bool isInitialFrontendLoad = styleSheetsUpdateType == InitialFrontendLoad;
554 WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >* documentCSSStyleSheets = m_documentToCSSStyleSheets.get(document);
555 if (!documentCSSStyleSheets) {
556 documentCSSStyleSheets = new WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >();
557 OwnPtrWillBeRawPtr<WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > > documentCSSStyleSheetsPtr = adoptPtrWillBeNoop(documentCSSStyleSheets);
558 m_documentToCSSStyleSheets.set(document, documentCSSStyleSheetsPtr.release());
561 WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> > removedSheets(*documentCSSStyleSheets);
562 WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > addedSheets;
563 for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::const_iterator it = allSheetsVector.begin(); it != allSheetsVector.end(); ++it) {
564 CSSStyleSheet* cssStyleSheet = *it;
565 if (removedSheets.contains(cssStyleSheet)) {
566 removedSheets.remove(cssStyleSheet);
567 if (isInitialFrontendLoad)
568 addedSheets.append(cssStyleSheet);
570 addedSheets.append(cssStyleSheet);
574 for (WillBeHeapHashSet<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = removedSheets.begin(); it != removedSheets.end(); ++it) {
575 CSSStyleSheet* cssStyleSheet = *it;
576 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet);
577 ASSERT(inspectorStyleSheet);
579 documentCSSStyleSheets->remove(cssStyleSheet);
580 if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
581 String id = unbindStyleSheet(inspectorStyleSheet.get());
582 if (m_frontend && !isInitialFrontendLoad)
583 m_frontend->styleSheetRemoved(id);
587 for (WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >::iterator it = addedSheets.begin(); it != addedSheets.end(); ++it) {
588 CSSStyleSheet* cssStyleSheet = *it;
589 bool isNew = isInitialFrontendLoad || !m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet);
591 InspectorStyleSheet* newStyleSheet = bindStyleSheet(cssStyleSheet);
592 documentCSSStyleSheets->add(cssStyleSheet);
594 m_frontend->styleSheetAdded(newStyleSheet->buildObjectForStyleSheetInfo());
598 if (documentCSSStyleSheets->isEmpty())
599 m_documentToCSSStyleSheets.remove(document);
602 void InspectorCSSAgent::documentDetached(Document* document)
604 m_invalidatedDocuments.remove(document);
605 setActiveStyleSheets(document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >(), ExistingFrontendRefresh);
608 bool InspectorCSSAgent::forcePseudoState(Element* element, CSSSelector::PseudoType pseudoType)
610 if (m_nodeIdToForcedPseudoState.isEmpty())
613 int nodeId = m_domAgent->boundNodeId(element);
617 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
618 if (it == m_nodeIdToForcedPseudoState.end())
621 unsigned forcedPseudoState = it->value;
622 switch (pseudoType) {
623 case CSSSelector::PseudoActive:
624 return forcedPseudoState & PseudoActive;
625 case CSSSelector::PseudoFocus:
626 return forcedPseudoState & PseudoFocus;
627 case CSSSelector::PseudoHover:
628 return forcedPseudoState & PseudoHover;
629 case CSSSelector::PseudoVisited:
630 return forcedPseudoState & PseudoVisited;
636 void InspectorCSSAgent::getMediaQueries(ErrorString* errorString, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> >& medias)
638 medias = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
639 for (IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.begin(); it != m_idToInspectorStyleSheet.end(); ++it) {
640 RefPtrWillBeRawPtr<InspectorStyleSheet> styleSheet = it->value;
641 collectMediaQueriesFromStyleSheet(styleSheet->pageStyleSheet(), medias.get());
642 const CSSRuleVector& flatRules = styleSheet->flatRules();
643 for (unsigned i = 0; i < flatRules.size(); ++i) {
644 CSSRule* rule = flatRules.at(i).get();
645 if (rule->type() == CSSRule::MEDIA_RULE)
646 collectMediaQueriesFromRule(rule, medias.get());
651 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)
653 Element* element = elementForId(errorString, nodeId);
655 *errorString = "Node not found";
659 Element* originalElement = element;
660 PseudoId elementPseudoId = element->pseudoId();
661 if (elementPseudoId) {
662 element = element->parentOrShadowHostElement();
664 *errorString = "Pseudo element has no parent";
669 Document* ownerDocument = element->ownerDocument();
670 // A non-active document has no styles.
671 if (!ownerDocument->isActive())
674 // FIXME: It's really gross for the inspector to reach in and access StyleResolver
675 // directly here. We need to provide the Inspector better APIs to get this information
676 // without grabbing at internal style classes!
679 StyleResolver& styleResolver = ownerDocument->ensureStyleResolver();
681 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
682 matchedCSSRules = buildArrayForMatchedRuleList(matchedRules.get(), originalElement);
685 if (!elementPseudoId && !asBool(excludePseudo)) {
686 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches> > pseudoElements = TypeBuilder::Array<TypeBuilder::CSS::PseudoIdMatches>::create();
687 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
688 RefPtrWillBeRawPtr<CSSRuleList> matchedRules = styleResolver.pseudoCSSRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
689 if (matchedRules && matchedRules->length()) {
690 RefPtr<TypeBuilder::CSS::PseudoIdMatches> matches = TypeBuilder::CSS::PseudoIdMatches::create()
691 .setPseudoId(static_cast<int>(pseudoId))
692 .setMatches(buildArrayForMatchedRuleList(matchedRules.get(), element));
693 pseudoElements->addItem(matches.release());
697 pseudoIdMatches = pseudoElements.release();
701 if (!elementPseudoId && !asBool(excludeInherited)) {
702 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry> > entries = TypeBuilder::Array<TypeBuilder::CSS::InheritedStyleEntry>::create();
703 Element* parentElement = element->parentElement();
704 while (parentElement) {
705 StyleResolver& parentStyleResolver = parentElement->ownerDocument()->ensureStyleResolver();
706 RefPtrWillBeRawPtr<CSSRuleList> parentMatchedRules = parentStyleResolver.cssRulesForElement(parentElement, StyleResolver::AllCSSRules);
707 RefPtr<TypeBuilder::CSS::InheritedStyleEntry> entry = TypeBuilder::CSS::InheritedStyleEntry::create()
708 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules.get(), parentElement));
709 if (parentElement->style() && parentElement->style()->length()) {
710 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement);
712 entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0))));
715 entries->addItem(entry.release());
716 parentElement = parentElement->parentElement();
719 inheritedEntries = entries.release();
723 void InspectorCSSAgent::getInlineStylesForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::CSS::CSSStyle>& inlineStyle, RefPtr<TypeBuilder::CSS::CSSStyle>& attributesStyle)
725 Element* element = elementForId(errorString, nodeId);
729 InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element);
733 inlineStyle = styleSheet->buildObjectForStyle(element->style());
734 RefPtr<TypeBuilder::CSS::CSSStyle> attributes = buildObjectForAttributesStyle(element);
735 attributesStyle = attributes ? attributes.release() : nullptr;
738 void InspectorCSSAgent::getComputedStyleForNode(ErrorString* errorString, int nodeId, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> >& style)
740 Node* node = m_domAgent->assertNode(errorString, nodeId);
744 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
745 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, 0);
746 style = inspectorStyle->buildArrayForComputedStyle();
749 void InspectorCSSAgent::collectPlatformFontsForRenderer(RenderText* renderer, HashCountedSet<String>* fontStats)
751 for (InlineTextBox* box = renderer->firstTextBox(); box; box = box->nextTextBox()) {
752 RenderStyle* style = renderer->style(box->isFirstLineStyle());
753 const Font& font = style->font();
754 TextRun run = box->constructTextRunForInspector(style, font);
755 WidthIterator it(&font, run, 0, false);
756 GlyphBuffer glyphBuffer;
757 it.advance(run.length(), &glyphBuffer);
758 for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
759 String familyName = glyphBuffer.fontDataAt(i)->platformData().fontFamilyName();
760 if (familyName.isNull())
762 fontStats->add(familyName);
767 void InspectorCSSAgent::getPlatformFontsForNode(ErrorString* errorString, int nodeId,
768 String* cssFamilyName, RefPtr<TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage> >& platformFonts)
770 Node* node = m_domAgent->assertNode(errorString, nodeId);
774 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleInfo = CSSComputedStyleDeclaration::create(node, true);
775 *cssFamilyName = computedStyleInfo->getPropertyValue(CSSPropertyFontFamily);
777 WillBeHeapVector<RawPtrWillBeMember<Text> > textNodes;
778 if (node->nodeType() == Node::TEXT_NODE) {
779 if (node->renderer())
780 textNodes.append(toText(node));
782 for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
783 if (child->nodeType() == Node::TEXT_NODE && child->renderer())
784 textNodes.append(toText(child));
788 HashCountedSet<String> fontStats;
789 for (size_t i = 0; i < textNodes.size(); ++i) {
790 RenderText* renderer = textNodes[i]->renderer();
791 collectPlatformFontsForRenderer(renderer, &fontStats);
792 if (renderer->isTextFragment()) {
793 RenderTextFragment* textFragment = toRenderTextFragment(renderer);
794 if (textFragment->firstLetter()) {
795 RenderBoxModelObject* firstLetter = textFragment->firstLetter();
796 for (RenderObject* current = firstLetter->slowFirstChild(); current; current = current->nextSibling()) {
797 if (current->isText())
798 collectPlatformFontsForRenderer(toRenderText(current), &fontStats);
804 platformFonts = TypeBuilder::Array<TypeBuilder::CSS::PlatformFontUsage>::create();
805 for (HashCountedSet<String>::iterator it = fontStats.begin(), end = fontStats.end(); it != end; ++it) {
806 RefPtr<TypeBuilder::CSS::PlatformFontUsage> platformFont = TypeBuilder::CSS::PlatformFontUsage::create()
807 .setFamilyName(it->key)
808 .setGlyphCount(it->value);
809 platformFonts->addItem(platformFont);
813 void InspectorCSSAgent::getStyleSheetText(ErrorString* errorString, const String& styleSheetId, String* result)
815 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
816 if (!inspectorStyleSheet)
819 inspectorStyleSheet->getText(result);
822 void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
824 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
825 if (!inspectorStyleSheet) {
826 *errorString = "Style sheet with id " + styleSheetId + " not found";
830 TrackExceptionState exceptionState;
831 m_domAgent->history()->perform(adoptRefWillBeNoop(new SetStyleSheetTextAction(inspectorStyleSheet, text)), exceptionState);
832 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
835 static bool extractRangeComponent(ErrorString* errorString, const RefPtr<JSONObject>& range, const String& component, unsigned& result)
838 if (!range->getNumber(component, &parsedValue) || parsedValue < 0) {
839 *errorString = "range." + component + " must be a non-negative integer";
842 result = parsedValue;
846 static bool jsonRangeToSourceRange(ErrorString* errorString, InspectorStyleSheetBase* inspectorStyleSheet, const RefPtr<JSONObject>& range, SourceRange* sourceRange)
848 unsigned startLineNumber = 0;
849 unsigned startColumn = 0;
850 unsigned endLineNumber = 0;
851 unsigned endColumn = 0;
852 if (!extractRangeComponent(errorString, range, "startLine", startLineNumber)
853 || !extractRangeComponent(errorString, range, "startColumn", startColumn)
854 || !extractRangeComponent(errorString, range, "endLine", endLineNumber)
855 || !extractRangeComponent(errorString, range, "endColumn", endColumn))
858 unsigned startOffset = 0;
859 unsigned endOffset = 0;
860 bool success = inspectorStyleSheet->lineNumberAndColumnToOffset(startLineNumber, startColumn, &startOffset)
861 && inspectorStyleSheet->lineNumberAndColumnToOffset(endLineNumber, endColumn, &endOffset);
863 *errorString = "Specified range is out of bounds";
867 if (startOffset > endOffset) {
868 *errorString = "Range start must not succeed its end";
871 sourceRange->start = startOffset;
872 sourceRange->end = endOffset;
876 void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& text, RefPtr<TypeBuilder::CSS::CSSStyle>& result)
878 InspectorStyleSheetBase* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
879 if (!inspectorStyleSheet)
881 SourceRange propertyRange;
882 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &propertyRange))
884 InspectorCSSId compoundId;
885 unsigned propertyIndex;
887 if (!inspectorStyleSheet->findPropertyByRange(propertyRange, &compoundId, &propertyIndex, &overwrite)) {
888 *errorString = "Source range didn't match any existing property source range nor any property insertion point";
892 TrackExceptionState exceptionState;
893 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetPropertyTextAction(inspectorStyleSheet, compoundId, propertyIndex, text, overwrite)), exceptionState);
895 result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
896 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
899 void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const String& styleSheetId, const RefPtr<JSONObject>& range, const String& selector, RefPtr<TypeBuilder::CSS::CSSRule>& result)
901 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
902 if (!inspectorStyleSheet)
904 SourceRange selectorRange;
905 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, range, &selectorRange))
907 InspectorCSSId compoundId;
908 if (!inspectorStyleSheet->findRuleBySelectorRange(selectorRange, &compoundId)) {
909 *errorString = "Source range didn't match any rule selector source range";
913 TrackExceptionState exceptionState;
914 bool success = m_domAgent->history()->perform(adoptRefWillBeNoop(new SetRuleSelectorAction(inspectorStyleSheet, compoundId, selector)), exceptionState);
916 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(compoundId);
917 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
919 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
922 void InspectorCSSAgent::createStyleSheet(ErrorString* errorString, const String& frameId, TypeBuilder::CSS::StyleSheetId* outStyleSheetId)
924 LocalFrame* frame = m_pageAgent->frameForId(frameId);
926 *errorString = "Frame not found";
930 Document* document = frame->document();
932 *errorString = "Frame does not have a document";
936 InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(document, true);
937 if (!inspectorStyleSheet) {
938 *errorString = "No target stylesheet found";
942 updateActiveStyleSheets(document, ExistingFrontendRefresh);
944 *outStyleSheetId = inspectorStyleSheet->id();
947 void InspectorCSSAgent::addRule(ErrorString* errorString, const String& styleSheetId, const String& ruleText, const RefPtr<JSONObject>& location, RefPtr<TypeBuilder::CSS::CSSRule>& result)
949 InspectorStyleSheet* inspectorStyleSheet = assertInspectorStyleSheetForId(errorString, styleSheetId);
950 if (!inspectorStyleSheet)
952 SourceRange ruleLocation;
953 if (!jsonRangeToSourceRange(errorString, inspectorStyleSheet, location, &ruleLocation))
956 TrackExceptionState exceptionState;
957 RefPtrWillBeRawPtr<AddRuleAction> action = adoptRefWillBeNoop(new AddRuleAction(inspectorStyleSheet, ruleText, ruleLocation));
958 bool success = m_domAgent->history()->perform(action, exceptionState);
960 *errorString = InspectorDOMAgent::toErrorString(exceptionState);
964 InspectorCSSId ruleId = action->newRuleId();
965 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
966 result = inspectorStyleSheet->buildObjectForRule(rule, buildMediaListChain(rule));
969 void InspectorCSSAgent::forcePseudoState(ErrorString* errorString, int nodeId, const RefPtr<JSONArray>& forcedPseudoClasses)
971 Element* element = m_domAgent->assertElement(errorString, nodeId);
975 unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses.get());
976 NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.find(nodeId);
977 unsigned currentForcedPseudoState = it == m_nodeIdToForcedPseudoState.end() ? 0 : it->value;
978 bool needStyleRecalc = forcedPseudoState != currentForcedPseudoState;
979 if (!needStyleRecalc)
982 if (forcedPseudoState)
983 m_nodeIdToForcedPseudoState.set(nodeId, forcedPseudoState);
985 m_nodeIdToForcedPseudoState.remove(nodeId);
986 element->ownerDocument()->setNeedsStyleRecalc(SubtreeStyleChange);
989 PassRefPtr<TypeBuilder::CSS::CSSMedia> InspectorCSSAgent::buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL, CSSStyleSheet* parentStyleSheet)
991 // Make certain compilers happy by initializing |source| up-front.
992 TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
993 switch (mediaListSource) {
994 case MediaListSourceMediaRule:
995 source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
997 case MediaListSourceImportRule:
998 source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
1000 case MediaListSourceLinkedSheet:
1001 source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
1003 case MediaListSourceInlineSheet:
1004 source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
1008 const MediaQuerySet* queries = media->queries();
1009 const WillBeHeapVector<OwnPtrWillBeMember<MediaQuery> >& queryVector = queries->queryVector();
1010 OwnPtr<MediaQueryEvaluator> mediaEvaluator = adoptPtr(new MediaQueryEvaluator(parentStyleSheet->ownerDocument()->frame()));
1012 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQuery> > mediaListArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQuery>::create();
1013 RefPtr<MediaValues> mediaValues = MediaValues::createDynamicIfFrameExists(parentStyleSheet->ownerDocument()->frame());
1014 bool hasMediaQueryItems = false;
1015 for (size_t i = 0; i < queryVector.size(); ++i) {
1016 MediaQuery* query = queryVector.at(i).get();
1017 const ExpressionHeapVector& expressions = query->expressions();
1018 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression> > expressionArray = TypeBuilder::Array<TypeBuilder::CSS::MediaQueryExpression>::create();
1019 bool hasExpressionItems = false;
1020 for (size_t j = 0; j < expressions.size(); ++j) {
1021 MediaQueryExp* mediaQueryExp = expressions.at(j).get();
1022 MediaQueryExpValue expValue = mediaQueryExp->expValue();
1023 if (!expValue.isValue)
1025 const char* valueName = CSSPrimitiveValue::unitTypeToString(expValue.unit);
1026 RefPtr<TypeBuilder::CSS::MediaQueryExpression> mediaQueryExpression = TypeBuilder::CSS::MediaQueryExpression::create()
1027 .setValue(expValue.value)
1028 .setUnit(String(valueName))
1029 .setFeature(mediaQueryExp->mediaFeature());
1032 if (mediaValues->computeLength(expValue.value, expValue.unit, computedLength))
1033 mediaQueryExpression->setComputedLength(computedLength);
1035 expressionArray->addItem(mediaQueryExpression);
1036 hasExpressionItems = true;
1038 if (!hasExpressionItems)
1040 RefPtr<TypeBuilder::CSS::MediaQuery> mediaQuery = TypeBuilder::CSS::MediaQuery::create()
1041 .setActive(mediaEvaluator->eval(query, nullptr))
1042 .setExpressions(expressionArray);
1043 mediaListArray->addItem(mediaQuery);
1044 hasMediaQueryItems = true;
1047 RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
1048 .setText(media->mediaText())
1050 if (hasMediaQueryItems)
1051 mediaObject->setMediaList(mediaListArray);
1053 if (parentStyleSheet && mediaListSource != MediaListSourceLinkedSheet) {
1054 if (InspectorStyleSheet* inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(parentStyleSheet))
1055 mediaObject->setParentStyleSheetId(inspectorStyleSheet->id());
1058 if (!sourceURL.isEmpty()) {
1059 mediaObject->setSourceURL(sourceURL);
1061 CSSRule* parentRule = media->parentRule();
1063 return mediaObject.release();
1064 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(parentRule->parentStyleSheet());
1065 RefPtr<TypeBuilder::CSS::SourceRange> mediaRange = inspectorStyleSheet->ruleHeaderSourceRange(parentRule);
1067 mediaObject->setRange(mediaRange);
1069 return mediaObject.release();
1072 bool InspectorCSSAgent::collectMediaQueriesFromStyleSheet(CSSStyleSheet* styleSheet, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1074 bool addedItems = false;
1075 MediaList* mediaList = styleSheet->media();
1077 if (mediaList && mediaList->length()) {
1078 Document* doc = styleSheet->ownerDocument();
1080 sourceURL = doc->url();
1081 else if (!styleSheet->contents()->baseURL().isEmpty())
1082 sourceURL = styleSheet->contents()->baseURL();
1085 mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL, styleSheet));
1091 bool InspectorCSSAgent::collectMediaQueriesFromRule(CSSRule* rule, TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
1093 MediaList* mediaList;
1095 CSSStyleSheet* parentStyleSheet = 0;
1096 bool isMediaRule = true;
1097 bool addedItems = false;
1098 if (rule->type() == CSSRule::MEDIA_RULE) {
1099 CSSMediaRule* mediaRule = toCSSMediaRule(rule);
1100 mediaList = mediaRule->media();
1101 parentStyleSheet = mediaRule->parentStyleSheet();
1102 } else if (rule->type() == CSSRule::IMPORT_RULE) {
1103 CSSImportRule* importRule = toCSSImportRule(rule);
1104 mediaList = importRule->media();
1105 parentStyleSheet = importRule->parentStyleSheet();
1106 isMediaRule = false;
1111 if (parentStyleSheet) {
1112 sourceURL = parentStyleSheet->contents()->baseURL();
1113 if (sourceURL.isEmpty())
1114 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
1119 if (mediaList && mediaList->length()) {
1120 mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL, parentStyleSheet));
1126 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > InspectorCSSAgent::buildMediaListChain(CSSRule* rule)
1130 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSMedia> > mediaArray = TypeBuilder::Array<TypeBuilder::CSS::CSSMedia>::create();
1131 bool hasItems = false;
1132 CSSRule* parentRule = rule;
1133 while (parentRule) {
1134 hasItems = collectMediaQueriesFromRule(parentRule, mediaArray.get()) || hasItems;
1135 if (parentRule->parentRule()) {
1136 parentRule = parentRule->parentRule();
1138 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
1139 while (styleSheet) {
1140 hasItems = collectMediaQueriesFromStyleSheet(styleSheet, mediaArray.get()) || hasItems;
1141 parentRule = styleSheet->ownerRule();
1144 styleSheet = styleSheet->parentStyleSheet();
1148 return hasItems ? mediaArray : nullptr;
1151 InspectorStyleSheetForInlineStyle* InspectorCSSAgent::asInspectorStyleSheet(Element* element)
1153 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1154 if (it != m_nodeToInspectorStyleSheet.end())
1155 return it->value.get();
1157 CSSStyleDeclaration* style = element->style();
1161 String newStyleSheetId = String::number(m_lastStyleSheetId++);
1162 RefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(newStyleSheetId, element, this);
1163 m_idToInspectorStyleSheetForInlineStyle.set(newStyleSheetId, inspectorStyleSheet);
1164 m_nodeToInspectorStyleSheet.set(element, inspectorStyleSheet);
1165 return inspectorStyleSheet.get();
1168 Element* InspectorCSSAgent::elementForId(ErrorString* errorString, int nodeId)
1170 Node* node = m_domAgent->nodeForId(nodeId);
1172 *errorString = "No node with given id found";
1175 if (!node->isElementNode()) {
1176 *errorString = "Not an element node";
1179 return toElement(node);
1183 void InspectorCSSAgent::collectAllDocumentStyleSheets(Document* document, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1185 const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets = document->styleEngine()->activeStyleSheetsForInspector();
1186 for (WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >::const_iterator it = activeStyleSheets.begin(); it != activeStyleSheets.end(); ++it) {
1187 CSSStyleSheet* styleSheet = it->get();
1188 InspectorCSSAgent::collectStyleSheets(styleSheet, result);
1193 void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> >& result)
1195 result.append(styleSheet);
1196 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
1197 CSSRule* rule = styleSheet->item(i);
1198 if (rule->type() == CSSRule::IMPORT_RULE) {
1199 CSSStyleSheet* importedStyleSheet = toCSSImportRule(rule)->styleSheet();
1200 if (importedStyleSheet)
1201 InspectorCSSAgent::collectStyleSheets(importedStyleSheet, result);
1206 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
1208 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
1209 if (!inspectorStyleSheet) {
1210 String id = String::number(m_lastStyleSheetId++);
1211 Document* document = styleSheet->ownerDocument();
1212 inspectorStyleSheet = InspectorStyleSheet::create(m_pageAgent, m_resourceAgent, id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
1213 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
1214 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
1215 if (m_creatingViaInspectorStyleSheet)
1216 m_documentToViaInspectorStyleSheet.add(document, inspectorStyleSheet);
1218 return inspectorStyleSheet.get();
1221 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
1223 String id = inspectorStyleSheet->id();
1224 m_idToInspectorStyleSheet.remove(id);
1225 if (inspectorStyleSheet->pageStyleSheet())
1226 m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
1230 InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
1233 ASSERT(!createIfAbsent);
1237 if (!document->isHTMLDocument() && !document->isSVGDocument())
1240 RefPtrWillBeRawPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToViaInspectorStyleSheet.get(document);
1241 if (inspectorStyleSheet || !createIfAbsent)
1242 return inspectorStyleSheet.get();
1244 TrackExceptionState exceptionState;
1245 RefPtrWillBeRawPtr<Element> styleElement = document->createElement("style", exceptionState);
1246 if (!exceptionState.hadException())
1247 styleElement->setAttribute("type", "text/css", exceptionState);
1248 if (!exceptionState.hadException()) {
1249 ContainerNode* targetNode;
1250 // HEAD is absent in ImageDocuments, for example.
1251 if (document->head())
1252 targetNode = document->head();
1253 else if (document->body())
1254 targetNode = document->body();
1258 InlineStyleOverrideScope overrideScope(document);
1259 m_creatingViaInspectorStyleSheet = true;
1260 targetNode->appendChild(styleElement, exceptionState);
1261 // At this point the added stylesheet will get bound through the updateActiveStyleSheets() invocation.
1262 // We just need to pick the respective InspectorStyleSheet from m_documentToViaInspectorStyleSheet.
1263 m_creatingViaInspectorStyleSheet = false;
1265 if (exceptionState.hadException())
1268 return m_documentToViaInspectorStyleSheet.get(document);
1271 InspectorStyleSheet* InspectorCSSAgent::assertInspectorStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1273 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
1274 if (it == m_idToInspectorStyleSheet.end()) {
1275 *errorString = "No style sheet with given id found";
1278 return it->value.get();
1281 InspectorStyleSheetBase* InspectorCSSAgent::assertStyleSheetForId(ErrorString* errorString, const String& styleSheetId)
1284 InspectorStyleSheetBase* result = assertInspectorStyleSheetForId(&placeholder, styleSheetId);
1287 IdToInspectorStyleSheetForInlineStyle::iterator it = m_idToInspectorStyleSheetForInlineStyle.find(styleSheetId);
1288 if (it == m_idToInspectorStyleSheetForInlineStyle.end()) {
1289 *errorString = "No style sheet with given id found";
1292 return it->value.get();
1295 TypeBuilder::CSS::StyleSheetOrigin::Enum InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
1297 if (m_creatingViaInspectorStyleSheet)
1298 return TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1300 TypeBuilder::CSS::StyleSheetOrigin::Enum origin = TypeBuilder::CSS::StyleSheetOrigin::Regular;
1301 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
1302 origin = TypeBuilder::CSS::StyleSheetOrigin::User_agent;
1303 else if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->isDocumentNode())
1304 origin = TypeBuilder::CSS::StyleSheetOrigin::User;
1306 InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
1307 if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
1308 origin = TypeBuilder::CSS::StyleSheetOrigin::Inspector;
1313 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
1318 // CSSRules returned by StyleResolver::pseudoCSSRulesForElement lack parent pointers if they are coming from
1319 // user agent stylesheets. To work around this issue, we use CSSOM wrapper created by inspector.
1320 if (!rule->parentStyleSheet()) {
1321 if (!m_inspectorUserAgentStyleSheet)
1322 m_inspectorUserAgentStyleSheet = CSSStyleSheet::create(CSSDefaultStyleSheets::instance().defaultStyleSheet());
1323 rule->setParentStyleSheet(m_inspectorUserAgentStyleSheet.get());
1325 return bindStyleSheet(rule->parentStyleSheet())->buildObjectForRule(rule, buildMediaListChain(rule));
1328 static inline bool matchesPseudoElement(const CSSSelector* selector, PseudoId elementPseudoId)
1330 // According to http://www.w3.org/TR/css3-selectors/#pseudo-elements, "Only one pseudo-element may appear per selector."
1331 // As such, check the last selector in the tag history.
1332 for (; !selector->isLastInTagHistory(); ++selector) { }
1333 PseudoId selectorPseudoId = selector->matchesPseudoElement() ? CSSSelector::pseudoId(selector->pseudoType()) : NOPSEUDO;
1335 // FIXME: This only covers the case of matching pseudo-element selectors against PseudoElements.
1336 // We should come up with a solution for matching pseudo-element selectors against ordinary Elements, too.
1337 return selectorPseudoId == elementPseudoId;
1340 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > InspectorCSSAgent::buildArrayForMatchedRuleList(CSSRuleList* ruleList, Element* element)
1342 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::RuleMatch> > result = TypeBuilder::Array<TypeBuilder::CSS::RuleMatch>::create();
1344 return result.release();
1346 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1347 CSSStyleRule* rule = asCSSStyleRule(ruleList->item(i));
1348 RefPtr<TypeBuilder::CSS::CSSRule> ruleObject = buildObjectForRule(rule);
1351 RefPtr<TypeBuilder::Array<int> > matchingSelectors = TypeBuilder::Array<int>::create();
1352 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1354 PseudoId elementPseudoId = element->pseudoId();
1355 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) {
1356 const CSSSelector* firstTagHistorySelector = selector;
1357 bool matched = false;
1358 if (elementPseudoId)
1359 matched = matchesPseudoElement(selector, elementPseudoId); // Modifies |selector|.
1360 matched |= element->matches(firstTagHistorySelector->selectorText(), IGNORE_EXCEPTION);
1362 matchingSelectors->addItem(index);
1365 RefPtr<TypeBuilder::CSS::RuleMatch> match = TypeBuilder::CSS::RuleMatch::create()
1366 .setRule(ruleObject.release())
1367 .setMatchingSelectors(matchingSelectors.release());
1368 result->addItem(match);
1374 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(Element* element)
1376 if (!element->isStyledElement())
1379 // FIXME: Ugliness below.
1380 StylePropertySet* attributeStyle = const_cast<StylePropertySet*>(element->presentationAttributeStyle());
1381 if (!attributeStyle)
1384 MutableStylePropertySet* mutableAttributeStyle = toMutableStylePropertySet(attributeStyle);
1386 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle->ensureCSSStyleDeclaration(), 0);
1387 return inspectorStyle->buildObjectForStyle();
1390 void InspectorCSSAgent::didRemoveDocument(Document* document)
1393 m_documentToViaInspectorStyleSheet.remove(document);
1396 void InspectorCSSAgent::didRemoveDOMNode(Node* node)
1401 int nodeId = m_domAgent->boundNodeId(node);
1403 m_nodeIdToForcedPseudoState.remove(nodeId);
1405 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(node);
1406 if (it == m_nodeToInspectorStyleSheet.end())
1409 m_idToInspectorStyleSheetForInlineStyle.remove(it->value->id());
1410 m_nodeToInspectorStyleSheet.remove(node);
1413 void InspectorCSSAgent::didModifyDOMAttr(Element* element)
1418 NodeToInspectorStyleSheet::iterator it = m_nodeToInspectorStyleSheet.find(element);
1419 if (it == m_nodeToInspectorStyleSheet.end())
1422 it->value->didModifyElementAttribute();
1425 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheetBase* styleSheet)
1427 flushPendingFrontendMessages();
1428 m_frontend->styleSheetChanged(styleSheet->id());
1431 void InspectorCSSAgent::willReparseStyleSheet()
1433 ASSERT(!m_isSettingStyleSheetText);
1434 m_isSettingStyleSheetText = true;
1437 void InspectorCSSAgent::didReparseStyleSheet()
1439 ASSERT(m_isSettingStyleSheetText);
1440 m_isSettingStyleSheetText = false;
1443 void InspectorCSSAgent::resetPseudoStates()
1445 WillBeHeapHashSet<RawPtrWillBeMember<Document> > documentsToChange;
1446 for (NodeIdToForcedPseudoState::iterator it = m_nodeIdToForcedPseudoState.begin(), end = m_nodeIdToForcedPseudoState.end(); it != end; ++it) {
1447 Element* element = toElement(m_domAgent->nodeForId(it->key));
1448 if (element && element->ownerDocument())
1449 documentsToChange.add(element->ownerDocument());
1452 m_nodeIdToForcedPseudoState.clear();
1453 for (WillBeHeapHashSet<RawPtrWillBeMember<Document> >::iterator it = documentsToChange.begin(), end = documentsToChange.end(); it != end; ++it)
1454 (*it)->setNeedsStyleRecalc(SubtreeStyleChange);
1457 void InspectorCSSAgent::trace(Visitor* visitor)
1459 visitor->trace(m_domAgent);
1460 visitor->trace(m_pageAgent);
1461 visitor->trace(m_resourceAgent);
1463 visitor->trace(m_idToInspectorStyleSheet);
1464 visitor->trace(m_idToInspectorStyleSheetForInlineStyle);
1465 visitor->trace(m_cssStyleSheetToInspectorStyleSheet);
1466 visitor->trace(m_documentToCSSStyleSheets);
1467 visitor->trace(m_invalidatedDocuments);
1468 visitor->trace(m_nodeToInspectorStyleSheet);
1469 visitor->trace(m_documentToViaInspectorStyleSheet);
1471 visitor->trace(m_inspectorUserAgentStyleSheet);
1472 InspectorBaseAgent::trace(visitor);
1475 } // namespace blink