/* * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "InspectorCSSAgent.h" #include "CSSComputedStyleDeclaration.h" #include "CSSImportRule.h" #include "CSSPropertyNames.h" #include "CSSPropertySourceData.h" #include "CSSRule.h" #include "CSSRuleList.h" #include "CSSStyleRule.h" #include "CSSStyleSheet.h" #include "ContentSecurityPolicy.h" #include "DOMWindow.h" #include "FontCache.h" #include "HTMLHeadElement.h" #include "HTMLStyleElement.h" #include "InspectorHistory.h" #include "InspectorPageAgent.h" #include "InstrumentingAgents.h" #include "NamedFlowCollection.h" #include "Node.h" #include "NodeList.h" #include "PseudoElement.h" #include "RenderNamedFlowFragment.h" #include "SVGStyleElement.h" #include "SelectorChecker.h" #include "ShadowRoot.h" #include "StyleProperties.h" #include "StylePropertyShorthand.h" #include "StyleResolver.h" #include "StyleRule.h" #include "StyleScope.h" #include "StyleSheetList.h" #include "WebKitNamedFlow.h" #include #include #include #include #include using namespace Inspector; namespace WebCore { enum ForcePseudoClassFlags { PseudoClassNone = 0, PseudoClassHover = 1 << 0, PseudoClassFocus = 1 << 1, PseudoClassActive = 1 << 2, PseudoClassVisited = 1 << 3 }; static unsigned computePseudoClassMask(const InspectorArray& pseudoClassArray) { static NeverDestroyed active(ASCIILiteral("active")); static NeverDestroyed hover(ASCIILiteral("hover")); static NeverDestroyed focus(ASCIILiteral("focus")); static NeverDestroyed visited(ASCIILiteral("visited")); if (!pseudoClassArray.length()) return PseudoClassNone; unsigned result = PseudoClassNone; for (auto& pseudoClassValue : pseudoClassArray) { String pseudoClass; bool success = pseudoClassValue->asString(pseudoClass); if (!success) continue; if (pseudoClass == active) result |= PseudoClassActive; else if (pseudoClass == hover) result |= PseudoClassHover; else if (pseudoClass == focus) result |= PseudoClassFocus; else if (pseudoClass == visited) result |= PseudoClassVisited; } return result; } class ChangeRegionOversetTask { public: ChangeRegionOversetTask(InspectorCSSAgent*); void scheduleFor(WebKitNamedFlow*, int documentNodeId); void unschedule(WebKitNamedFlow*); void reset(); void timerFired(); private: InspectorCSSAgent* m_cssAgent; Timer m_timer; HashMap m_namedFlows; }; ChangeRegionOversetTask::ChangeRegionOversetTask(InspectorCSSAgent* cssAgent) : m_cssAgent(cssAgent) , m_timer(*this, &ChangeRegionOversetTask::timerFired) { } void ChangeRegionOversetTask::scheduleFor(WebKitNamedFlow* namedFlow, int documentNodeId) { m_namedFlows.add(namedFlow, documentNodeId); if (!m_timer.isActive()) m_timer.startOneShot(0); } void ChangeRegionOversetTask::unschedule(WebKitNamedFlow* namedFlow) { m_namedFlows.remove(namedFlow); } void ChangeRegionOversetTask::reset() { m_timer.stop(); m_namedFlows.clear(); } void ChangeRegionOversetTask::timerFired() { // The timer is stopped on m_cssAgent destruction, so this method will never be called after m_cssAgent has been destroyed. for (auto& namedFlow : m_namedFlows) m_cssAgent->regionOversetChanged(namedFlow.key, namedFlow.value); m_namedFlows.clear(); } class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action { WTF_MAKE_NONCOPYABLE(StyleSheetAction); public: StyleSheetAction(const String& name, InspectorStyleSheet* styleSheet) : InspectorHistory::Action(name) , m_styleSheet(styleSheet) { } protected: RefPtr m_styleSheet; }; class InspectorCSSAgent::SetStyleSheetTextAction final : public InspectorCSSAgent::StyleSheetAction { WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction); public: SetStyleSheetTextAction(InspectorStyleSheet* styleSheet, const String& text) : InspectorCSSAgent::StyleSheetAction(ASCIILiteral("SetStyleSheetText"), styleSheet) , m_text(text) { } private: ExceptionOr perform() final { auto result = m_styleSheet->text(); if (result.hasException()) return result.releaseException(); m_oldText = result.releaseReturnValue(); return redo(); } ExceptionOr undo() final { auto result = m_styleSheet->setText(m_oldText); if (result.hasException()) return result.releaseException(); m_styleSheet->reparseStyleSheet(m_oldText); return { }; } ExceptionOr redo() final { auto result = m_styleSheet->setText(m_text); if (result.hasException()) return result.releaseException(); m_styleSheet->reparseStyleSheet(m_text); return { }; } String mergeId() final { return String::format("SetStyleSheetText %s", m_styleSheet->id().utf8().data()); } void merge(std::unique_ptr action) override { ASSERT(action->mergeId() == mergeId()); m_text = static_cast(*action).m_text; } String m_text; String m_oldText; }; class InspectorCSSAgent::SetStyleTextAction final : public InspectorCSSAgent::StyleSheetAction { WTF_MAKE_NONCOPYABLE(SetStyleTextAction); public: SetStyleTextAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& text) : InspectorCSSAgent::StyleSheetAction(ASCIILiteral("SetStyleText"), styleSheet) , m_cssId(cssId) , m_text(text) { } ExceptionOr perform() override { return redo(); } ExceptionOr undo() override { return m_styleSheet->setStyleText(m_cssId, m_oldText, nullptr); } ExceptionOr redo() override { return m_styleSheet->setStyleText(m_cssId, m_text, &m_oldText); } String mergeId() override { ASSERT(m_styleSheet->id() == m_cssId.styleSheetId()); return String::format("SetStyleText %s:%u", m_styleSheet->id().utf8().data(), m_cssId.ordinal()); } void merge(std::unique_ptr action) override { ASSERT(action->mergeId() == mergeId()); SetStyleTextAction* other = static_cast(action.get()); m_text = other->m_text; } private: InspectorCSSId m_cssId; String m_text; String m_oldText; }; class InspectorCSSAgent::SetRuleSelectorAction final : public InspectorCSSAgent::StyleSheetAction { WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction); public: SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector) : InspectorCSSAgent::StyleSheetAction(ASCIILiteral("SetRuleSelector"), styleSheet) , m_cssId(cssId) , m_selector(selector) { } private: ExceptionOr perform() final { auto result = m_styleSheet->ruleSelector(m_cssId); if (result.hasException()) return result.releaseException(); m_oldSelector = result.releaseReturnValue(); return redo(); } ExceptionOr undo() final { return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector); } ExceptionOr redo() final { return m_styleSheet->setRuleSelector(m_cssId, m_selector); } InspectorCSSId m_cssId; String m_selector; String m_oldSelector; }; class InspectorCSSAgent::AddRuleAction final : public InspectorCSSAgent::StyleSheetAction { WTF_MAKE_NONCOPYABLE(AddRuleAction); public: AddRuleAction(InspectorStyleSheet* styleSheet, const String& selector) : InspectorCSSAgent::StyleSheetAction(ASCIILiteral("AddRule"), styleSheet) , m_selector(selector) { } InspectorCSSId newRuleId() const { return m_newId; } private: ExceptionOr perform() final { return redo(); } ExceptionOr undo() final { return m_styleSheet->deleteRule(m_newId); } ExceptionOr redo() final { auto result = m_styleSheet->addRule(m_selector); if (result.hasException()) return result.releaseException(); m_newId = m_styleSheet->ruleId(result.releaseReturnValue()); return { }; } InspectorCSSId m_newId; String m_selector; String m_oldSelector; }; CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule& rule) { if (!is(rule)) return nullptr; return downcast(&rule); } InspectorCSSAgent::InspectorCSSAgent(WebAgentContext& context, InspectorDOMAgent* domAgent) : InspectorAgentBase(ASCIILiteral("CSS"), context) , m_frontendDispatcher(std::make_unique(context.frontendRouter)) , m_backendDispatcher(CSSBackendDispatcher::create(context.backendDispatcher, this)) , m_domAgent(domAgent) { m_domAgent->setDOMListener(this); } InspectorCSSAgent::~InspectorCSSAgent() { ASSERT(!m_domAgent); reset(); } void InspectorCSSAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) { } void InspectorCSSAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) { resetNonPersistentData(); String unused; disable(unused); } void InspectorCSSAgent::discardAgent() { m_domAgent->setDOMListener(nullptr); m_domAgent = nullptr; } void InspectorCSSAgent::reset() { // FIXME: Should we be resetting on main frame navigations? m_idToInspectorStyleSheet.clear(); m_cssStyleSheetToInspectorStyleSheet.clear(); m_nodeToInspectorStyleSheet.clear(); m_documentToInspectorStyleSheet.clear(); m_documentToKnownCSSStyleSheets.clear(); resetNonPersistentData(); } void InspectorCSSAgent::resetNonPersistentData() { m_namedFlowCollectionsRequested.clear(); if (m_changeRegionOversetTask) m_changeRegionOversetTask->reset(); resetPseudoStates(); } void InspectorCSSAgent::enable(ErrorString&) { m_instrumentingAgents.setInspectorCSSAgent(this); for (auto* document : m_domAgent->documents()) activeStyleSheetsUpdated(*document); } void InspectorCSSAgent::disable(ErrorString&) { m_instrumentingAgents.setInspectorCSSAgent(nullptr); } void InspectorCSSAgent::documentDetached(Document& document) { Vector emptyList; setActiveStyleSheetsForDocument(document, emptyList); m_documentToKnownCSSStyleSheets.remove(&document); m_documentToInspectorStyleSheet.remove(&document); m_documentsWithForcedPseudoStates.remove(&document); } void InspectorCSSAgent::mediaQueryResultChanged() { m_frontendDispatcher->mediaQueryResultChanged(); } void InspectorCSSAgent::activeStyleSheetsUpdated(Document& document) { Vector cssStyleSheets; collectAllDocumentStyleSheets(document, cssStyleSheets); setActiveStyleSheetsForDocument(document, cssStyleSheets); } void InspectorCSSAgent::setActiveStyleSheetsForDocument(Document& document, Vector& activeStyleSheets) { HashSet& previouslyKnownActiveStyleSheets = m_documentToKnownCSSStyleSheets.add(&document, HashSet()).iterator->value; HashSet removedStyleSheets(previouslyKnownActiveStyleSheets); Vector addedStyleSheets; for (auto& activeStyleSheet : activeStyleSheets) { if (removedStyleSheets.contains(activeStyleSheet)) removedStyleSheets.remove(activeStyleSheet); else addedStyleSheets.append(activeStyleSheet); } for (auto* cssStyleSheet : removedStyleSheets) { previouslyKnownActiveStyleSheets.remove(cssStyleSheet); RefPtr inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet); if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) { String id = unbindStyleSheet(inspectorStyleSheet.get()); m_frontendDispatcher->styleSheetRemoved(id); } } for (auto* cssStyleSheet : addedStyleSheets) { previouslyKnownActiveStyleSheets.add(cssStyleSheet); if (!m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet)) { InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(cssStyleSheet); m_frontendDispatcher->styleSheetAdded(inspectorStyleSheet->buildObjectForStyleSheetInfo()); } } } void InspectorCSSAgent::didCreateNamedFlow(Document& document, WebKitNamedFlow& namedFlow) { int documentNodeId = documentNodeWithRequestedFlowsId(&document); if (!documentNodeId) return; ErrorString unused; m_frontendDispatcher->namedFlowCreated(buildObjectForNamedFlow(unused, &namedFlow, documentNodeId)); } void InspectorCSSAgent::willRemoveNamedFlow(Document& document, WebKitNamedFlow& namedFlow) { int documentNodeId = documentNodeWithRequestedFlowsId(&document); if (!documentNodeId) return; if (m_changeRegionOversetTask) m_changeRegionOversetTask->unschedule(&namedFlow); m_frontendDispatcher->namedFlowRemoved(documentNodeId, namedFlow.name().string()); } void InspectorCSSAgent::didChangeRegionOverset(Document& document, WebKitNamedFlow& namedFlow) { int documentNodeId = documentNodeWithRequestedFlowsId(&document); if (!documentNodeId) return; if (!m_changeRegionOversetTask) m_changeRegionOversetTask = std::make_unique(this); m_changeRegionOversetTask->scheduleFor(&namedFlow, documentNodeId); } void InspectorCSSAgent::regionOversetChanged(WebKitNamedFlow* namedFlow, int documentNodeId) { if (namedFlow->flowState() == WebKitNamedFlow::FlowStateNull) return; ErrorString unused; Ref protect(*namedFlow); m_frontendDispatcher->regionOversetChanged(buildObjectForNamedFlow(unused, namedFlow, documentNodeId)); } void InspectorCSSAgent::didRegisterNamedFlowContentElement(Document& document, WebKitNamedFlow& namedFlow, Node& contentElement, Node* nextContentElement) { int documentNodeId = documentNodeWithRequestedFlowsId(&document); if (!documentNodeId) return; ErrorString unused; int contentElementNodeId = m_domAgent->pushNodeToFrontend(unused, documentNodeId, &contentElement); int nextContentElementNodeId = nextContentElement ? m_domAgent->pushNodeToFrontend(unused, documentNodeId, nextContentElement) : 0; m_frontendDispatcher->registeredNamedFlowContentElement(documentNodeId, namedFlow.name().string(), contentElementNodeId, nextContentElementNodeId); } void InspectorCSSAgent::didUnregisterNamedFlowContentElement(Document& document, WebKitNamedFlow& namedFlow, Node& contentElement) { int documentNodeId = documentNodeWithRequestedFlowsId(&document); if (!documentNodeId) return; ErrorString unused; int contentElementNodeId = m_domAgent->pushNodeToFrontend(unused, documentNodeId, &contentElement); if (!contentElementNodeId) { // We've already notified that the DOM node was removed from the DOM, so there's no need to send another event. return; } m_frontendDispatcher->unregisteredNamedFlowContentElement(documentNodeId, namedFlow.name().string(), contentElementNodeId); } bool InspectorCSSAgent::forcePseudoState(const Element& element, CSSSelector::PseudoClassType pseudoClassType) { if (m_nodeIdToForcedPseudoState.isEmpty()) return false; int nodeId = m_domAgent->boundNodeId(&element); if (!nodeId) return false; auto it = m_nodeIdToForcedPseudoState.find(nodeId); if (it == m_nodeIdToForcedPseudoState.end()) return false; unsigned forcedPseudoState = it->value; switch (pseudoClassType) { case CSSSelector::PseudoClassActive: return forcedPseudoState & PseudoClassActive; case CSSSelector::PseudoClassFocus: return forcedPseudoState & PseudoClassFocus; case CSSSelector::PseudoClassHover: return forcedPseudoState & PseudoClassHover; case CSSSelector::PseudoClassVisited: return forcedPseudoState & PseudoClassVisited; default: return false; } } void InspectorCSSAgent::getMatchedStylesForNode(ErrorString& errorString, int nodeId, const bool* includePseudo, const bool* includeInherited, RefPtr>& matchedCSSRules, RefPtr>& pseudoIdMatches, RefPtr>& inheritedEntries) { Element* element = elementForId(errorString, nodeId); if (!element) return; Element* originalElement = element; PseudoId elementPseudoId = element->pseudoId(); if (elementPseudoId) { element = downcast(*element).hostElement(); if (!element) { errorString = ASCIILiteral("Pseudo element has no parent"); return; } } // Matched rules. StyleResolver& styleResolver = element->styleResolver(); auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules); matchedCSSRules = buildArrayForMatchedRuleList(matchedRules, styleResolver, *element, elementPseudoId); if (!originalElement->isPseudoElement()) { // Pseudo elements. if (!includePseudo || *includePseudo) { auto pseudoElements = Inspector::Protocol::Array::create(); for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < AFTER_LAST_INTERNAL_PSEUDOID; pseudoId = static_cast(pseudoId + 1)) { auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, pseudoId, StyleResolver::AllCSSRules); if (!matchedRules.isEmpty()) { auto matches = Inspector::Protocol::CSS::PseudoIdMatches::create() .setPseudoId(static_cast(pseudoId)) .setMatches(buildArrayForMatchedRuleList(matchedRules, styleResolver, *element, pseudoId)) .release(); pseudoElements->addItem(WTFMove(matches)); } } pseudoIdMatches = WTFMove(pseudoElements); } // Inherited styles. if (!includeInherited || *includeInherited) { auto entries = Inspector::Protocol::Array::create(); Element* parentElement = element->parentElement(); while (parentElement) { StyleResolver& parentStyleResolver = parentElement->styleResolver(); auto parentMatchedRules = parentStyleResolver.styleRulesForElement(parentElement, StyleResolver::AllCSSRules); auto entry = Inspector::Protocol::CSS::InheritedStyleEntry::create() .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules, styleResolver, *parentElement, NOPSEUDO)) .release(); if (parentElement->cssomStyle() && parentElement->cssomStyle()->length()) { if (InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(parentElement)) entry->setInlineStyle(styleSheet->buildObjectForStyle(styleSheet->styleForId(InspectorCSSId(styleSheet->id(), 0)))); } entries->addItem(WTFMove(entry)); parentElement = parentElement->parentElement(); } inheritedEntries = WTFMove(entries); } } } void InspectorCSSAgent::getInlineStylesForNode(ErrorString& errorString, int nodeId, RefPtr& inlineStyle, RefPtr& attributesStyle) { Element* element = elementForId(errorString, nodeId); if (!element) return; InspectorStyleSheetForInlineStyle* styleSheet = asInspectorStyleSheet(element); if (!styleSheet) return; inlineStyle = styleSheet->buildObjectForStyle(element->cssomStyle()); if (auto attributes = buildObjectForAttributesStyle(element)) attributesStyle = WTFMove(attributes); else attributesStyle = nullptr; } void InspectorCSSAgent::getComputedStyleForNode(ErrorString& errorString, int nodeId, RefPtr>& style) { Element* element = elementForId(errorString, nodeId); if (!element) return; RefPtr computedStyleInfo = CSSComputedStyleDeclaration::create(*element, true); Ref inspectorStyle = InspectorStyle::create(InspectorCSSId(), computedStyleInfo, nullptr); style = inspectorStyle->buildArrayForComputedStyle(); } void InspectorCSSAgent::getAllStyleSheets(ErrorString&, RefPtr>& styleInfos) { styleInfos = Inspector::Protocol::Array::create(); Vector inspectorStyleSheets; collectAllStyleSheets(inspectorStyleSheets); for (auto* inspectorStyleSheet : inspectorStyleSheets) styleInfos->addItem(inspectorStyleSheet->buildObjectForStyleSheetInfo()); } void InspectorCSSAgent::collectAllStyleSheets(Vector& result) { Vector cssStyleSheets; for (auto* document : m_domAgent->documents()) collectAllDocumentStyleSheets(*document, cssStyleSheets); for (auto* cssStyleSheet : cssStyleSheets) result.append(bindStyleSheet(cssStyleSheet)); } void InspectorCSSAgent::collectAllDocumentStyleSheets(Document& document, Vector& result) { auto cssStyleSheets = document.styleScope().activeStyleSheetsForInspector(); for (auto& cssStyleSheet : cssStyleSheets) collectStyleSheets(cssStyleSheet.get(), result); } void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, Vector& result) { result.append(styleSheet); for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) { CSSRule* rule = styleSheet->item(i); if (is(*rule)) { if (CSSStyleSheet* importedStyleSheet = downcast(*rule).styleSheet()) collectStyleSheets(importedStyleSheet, result); } } } void InspectorCSSAgent::getStyleSheet(ErrorString& errorString, const String& styleSheetId, RefPtr& styleSheetObject) { InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); if (!inspectorStyleSheet) return; styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet(); } void InspectorCSSAgent::getStyleSheetText(ErrorString& errorString, const String& styleSheetId, String* result) { InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); if (!inspectorStyleSheet) return; auto text = inspectorStyleSheet->text(); if (!text.hasException()) *result = text.releaseReturnValue(); } void InspectorCSSAgent::setStyleSheetText(ErrorString& errorString, const String& styleSheetId, const String& text) { InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId); if (!inspectorStyleSheet) return; auto result = m_domAgent->history()->perform(std::make_unique(inspectorStyleSheet, text)); if (result.hasException()) errorString = InspectorDOMAgent::toErrorString(result.releaseException()); } void InspectorCSSAgent::setStyleText(ErrorString& errorString, const InspectorObject& fullStyleId, const String& text, RefPtr& result) { InspectorCSSId compoundId(fullStyleId); ASSERT(!compoundId.isEmpty()); InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId()); if (!inspectorStyleSheet) return; auto performResult = m_domAgent->history()->perform(std::make_unique(inspectorStyleSheet, compoundId, text)); if (performResult.hasException()) { errorString = InspectorDOMAgent::toErrorString(performResult.releaseException()); return; } result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId)); } void InspectorCSSAgent::setRuleSelector(ErrorString& errorString, const InspectorObject& fullRuleId, const String& selector, RefPtr& result) { InspectorCSSId compoundId(fullRuleId); ASSERT(!compoundId.isEmpty()); InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId()); if (!inspectorStyleSheet) return; auto performResult = m_domAgent->history()->perform(std::make_unique(inspectorStyleSheet, compoundId, selector)); if (performResult.hasException()) { errorString = InspectorDOMAgent::toErrorString(performResult.releaseException()); return; } result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(compoundId), nullptr); } void InspectorCSSAgent::createStyleSheet(ErrorString& errorString, const String& frameId, String* styleSheetId) { Frame* frame = m_domAgent->pageAgent()->frameForId(frameId); if (!frame) { errorString = ASCIILiteral("No frame for given id found"); return; } Document* document = frame->document(); if (!document) { errorString = ASCIILiteral("No document for frame"); return; } InspectorStyleSheet* inspectorStyleSheet = createInspectorStyleSheetForDocument(*document); if (!inspectorStyleSheet) { errorString = ASCIILiteral("Could not create stylesheet for the frame."); return; } *styleSheetId = inspectorStyleSheet->id(); } InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Document& document) { if (!document.isHTMLDocument() && !document.isSVGDocument()) return nullptr; auto styleElement = HTMLStyleElement::create(document); styleElement->setAttributeWithoutSynchronization(HTMLNames::typeAttr, AtomicString("text/css", AtomicString::ConstructFromLiteral)); ContainerNode* targetNode; // HEAD is absent in ImageDocuments, for example. if (auto* head = document.head()) targetNode = head; else if (auto* body = document.bodyOrFrameset()) targetNode = body; else return nullptr; // Inserting this