1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "InspectorCSSAgent.h"
28
29#include "CSSComputedStyleDeclaration.h"
30#include "CSSImportRule.h"
31#include "CSSParserFastPaths.h"
32#include "CSSParserMode.h"
33#include "CSSPropertyNames.h"
34#include "CSSPropertySourceData.h"
35#include "CSSRule.h"
36#include "CSSRuleList.h"
37#include "CSSStyleRule.h"
38#include "CSSStyleSheet.h"
39#include "CSSValueKeywords.h"
40#include "ContentSecurityPolicy.h"
41#include "DOMWindow.h"
42#include "FontCache.h"
43#include "Frame.h"
44#include "HTMLHeadElement.h"
45#include "HTMLStyleElement.h"
46#include "InspectorDOMAgent.h"
47#include "InspectorHistory.h"
48#include "InspectorPageAgent.h"
49#include "InstrumentingAgents.h"
50#include "Node.h"
51#include "NodeList.h"
52#include "PseudoElement.h"
53#include "RenderStyleConstants.h"
54#include "SVGStyleElement.h"
55#include "SelectorChecker.h"
56#include "ShadowRoot.h"
57#include "StyleProperties.h"
58#include "StylePropertyShorthand.h"
59#include "StyleResolver.h"
60#include "StyleRule.h"
61#include "StyleScope.h"
62#include "StyleSheetList.h"
63#include <JavaScriptCore/InspectorProtocolObjects.h>
64#include <wtf/Optional.h>
65#include <wtf/Ref.h>
66#include <wtf/Vector.h>
67#include <wtf/text/CString.h>
68#include <wtf/text/StringConcatenateNumbers.h>
69
70namespace WebCore {
71
72using namespace Inspector;
73
74enum ForcePseudoClassFlags {
75 PseudoClassNone = 0,
76 PseudoClassHover = 1 << 0,
77 PseudoClassFocus = 1 << 1,
78 PseudoClassActive = 1 << 2,
79 PseudoClassVisited = 1 << 3
80};
81
82static unsigned computePseudoClassMask(const JSON::Array& pseudoClassArray)
83{
84 static NeverDestroyed<String> active(MAKE_STATIC_STRING_IMPL("active"));
85 static NeverDestroyed<String> hover(MAKE_STATIC_STRING_IMPL("hover"));
86 static NeverDestroyed<String> focus(MAKE_STATIC_STRING_IMPL("focus"));
87 static NeverDestroyed<String> visited(MAKE_STATIC_STRING_IMPL("visited"));
88 if (!pseudoClassArray.length())
89 return PseudoClassNone;
90
91 unsigned result = PseudoClassNone;
92 for (auto& pseudoClassValue : pseudoClassArray) {
93 String pseudoClass;
94 bool success = pseudoClassValue->asString(pseudoClass);
95 if (!success)
96 continue;
97 if (pseudoClass == active)
98 result |= PseudoClassActive;
99 else if (pseudoClass == hover)
100 result |= PseudoClassHover;
101 else if (pseudoClass == focus)
102 result |= PseudoClassFocus;
103 else if (pseudoClass == visited)
104 result |= PseudoClassVisited;
105 }
106
107 return result;
108}
109
110class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
111 WTF_MAKE_NONCOPYABLE(StyleSheetAction);
112public:
113 StyleSheetAction(InspectorStyleSheet* styleSheet)
114 : InspectorHistory::Action()
115 , m_styleSheet(styleSheet)
116 {
117 }
118
119protected:
120 RefPtr<InspectorStyleSheet> m_styleSheet;
121};
122
123class InspectorCSSAgent::SetStyleSheetTextAction final : public InspectorCSSAgent::StyleSheetAction {
124 WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
125public:
126 SetStyleSheetTextAction(InspectorStyleSheet* styleSheet, const String& text)
127 : InspectorCSSAgent::StyleSheetAction(styleSheet)
128 , m_text(text)
129 {
130 }
131
132private:
133 ExceptionOr<void> perform() final
134 {
135 auto result = m_styleSheet->text();
136 if (result.hasException())
137 return result.releaseException();
138 m_oldText = result.releaseReturnValue();
139 return redo();
140 }
141
142 ExceptionOr<void> undo() final
143 {
144 auto result = m_styleSheet->setText(m_oldText);
145 if (result.hasException())
146 return result.releaseException();
147 m_styleSheet->reparseStyleSheet(m_oldText);
148 return { };
149 }
150
151 ExceptionOr<void> redo() final
152 {
153 auto result = m_styleSheet->setText(m_text);
154 if (result.hasException())
155 return result.releaseException();
156 m_styleSheet->reparseStyleSheet(m_text);
157 return { };
158 }
159
160 String mergeId() final
161 {
162 return "SetStyleSheetText " + m_styleSheet->id();
163 }
164
165 void merge(std::unique_ptr<Action> action) override
166 {
167 ASSERT(action->mergeId() == mergeId());
168 m_text = static_cast<SetStyleSheetTextAction&>(*action).m_text;
169 }
170
171 String m_text;
172 String m_oldText;
173};
174
175class InspectorCSSAgent::SetStyleTextAction final : public InspectorCSSAgent::StyleSheetAction {
176 WTF_MAKE_NONCOPYABLE(SetStyleTextAction);
177public:
178 SetStyleTextAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& text)
179 : InspectorCSSAgent::StyleSheetAction(styleSheet)
180 , m_cssId(cssId)
181 , m_text(text)
182 {
183 }
184
185 ExceptionOr<void> perform() override
186 {
187 return redo();
188 }
189
190 ExceptionOr<void> undo() override
191 {
192 return m_styleSheet->setStyleText(m_cssId, m_oldText, nullptr);
193 }
194
195 ExceptionOr<void> redo() override
196 {
197 return m_styleSheet->setStyleText(m_cssId, m_text, &m_oldText);
198 }
199
200 String mergeId() override
201 {
202 ASSERT(m_styleSheet->id() == m_cssId.styleSheetId());
203 return makeString("SetStyleText ", m_styleSheet->id(), ':', m_cssId.ordinal());
204 }
205
206 void merge(std::unique_ptr<Action> action) override
207 {
208 ASSERT(action->mergeId() == mergeId());
209
210 SetStyleTextAction* other = static_cast<SetStyleTextAction*>(action.get());
211 m_text = other->m_text;
212 }
213
214private:
215 InspectorCSSId m_cssId;
216 String m_text;
217 String m_oldText;
218};
219
220class InspectorCSSAgent::SetRuleSelectorAction final : public InspectorCSSAgent::StyleSheetAction {
221 WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
222public:
223 SetRuleSelectorAction(InspectorStyleSheet* styleSheet, const InspectorCSSId& cssId, const String& selector)
224 : InspectorCSSAgent::StyleSheetAction(styleSheet)
225 , m_cssId(cssId)
226 , m_selector(selector)
227 {
228 }
229
230private:
231 ExceptionOr<void> perform() final
232 {
233 auto result = m_styleSheet->ruleSelector(m_cssId);
234 if (result.hasException())
235 return result.releaseException();
236 m_oldSelector = result.releaseReturnValue();
237 return redo();
238 }
239
240 ExceptionOr<void> undo() final
241 {
242 return m_styleSheet->setRuleSelector(m_cssId, m_oldSelector);
243 }
244
245 ExceptionOr<void> redo() final
246 {
247 return m_styleSheet->setRuleSelector(m_cssId, m_selector);
248 }
249
250 InspectorCSSId m_cssId;
251 String m_selector;
252 String m_oldSelector;
253};
254
255class InspectorCSSAgent::AddRuleAction final : public InspectorCSSAgent::StyleSheetAction {
256 WTF_MAKE_NONCOPYABLE(AddRuleAction);
257public:
258 AddRuleAction(InspectorStyleSheet* styleSheet, const String& selector)
259 : InspectorCSSAgent::StyleSheetAction(styleSheet)
260 , m_selector(selector)
261 {
262 }
263
264 InspectorCSSId newRuleId() const { return m_newId; }
265
266private:
267 ExceptionOr<void> perform() final
268 {
269 return redo();
270 }
271
272 ExceptionOr<void> undo() final
273 {
274 return m_styleSheet->deleteRule(m_newId);
275 }
276
277 ExceptionOr<void> redo() final
278 {
279 auto result = m_styleSheet->addRule(m_selector);
280 if (result.hasException())
281 return result.releaseException();
282 m_newId = m_styleSheet->ruleId(result.releaseReturnValue());
283 return { };
284 }
285
286 InspectorCSSId m_newId;
287 String m_selector;
288 String m_oldSelector;
289};
290
291CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule& rule)
292{
293 if (!is<CSSStyleRule>(rule))
294 return nullptr;
295 return downcast<CSSStyleRule>(&rule);
296}
297
298InspectorCSSAgent::InspectorCSSAgent(WebAgentContext& context)
299 : InspectorAgentBase("CSS"_s, context)
300 , m_frontendDispatcher(std::make_unique<CSSFrontendDispatcher>(context.frontendRouter))
301 , m_backendDispatcher(CSSBackendDispatcher::create(context.backendDispatcher, this))
302{
303}
304
305void InspectorCSSAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
306{
307}
308
309void InspectorCSSAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
310{
311 String unused;
312 disable(unused);
313}
314
315void InspectorCSSAgent::reset()
316{
317 // FIXME: Should we be resetting on main frame navigations?
318 m_idToInspectorStyleSheet.clear();
319 m_cssStyleSheetToInspectorStyleSheet.clear();
320 m_nodeToInspectorStyleSheet.clear();
321 m_documentToInspectorStyleSheet.clear();
322 m_documentToKnownCSSStyleSheets.clear();
323 resetPseudoStates();
324}
325
326void InspectorCSSAgent::enable(ErrorString&)
327{
328 if (m_instrumentingAgents.inspectorCSSAgent() == this)
329 return;
330
331 m_instrumentingAgents.setInspectorCSSAgent(this);
332
333 if (auto* domAgent = m_instrumentingAgents.inspectorDOMAgent()) {
334 for (auto* document : domAgent->documents())
335 activeStyleSheetsUpdated(*document);
336 }
337}
338
339void InspectorCSSAgent::disable(ErrorString&)
340{
341 m_instrumentingAgents.setInspectorCSSAgent(nullptr);
342
343 reset();
344}
345
346void InspectorCSSAgent::documentDetached(Document& document)
347{
348 Vector<CSSStyleSheet*> emptyList;
349 setActiveStyleSheetsForDocument(document, emptyList);
350
351 m_documentToKnownCSSStyleSheets.remove(&document);
352 m_documentToInspectorStyleSheet.remove(&document);
353 m_documentsWithForcedPseudoStates.remove(&document);
354}
355
356void InspectorCSSAgent::mediaQueryResultChanged()
357{
358 m_frontendDispatcher->mediaQueryResultChanged();
359}
360
361void InspectorCSSAgent::activeStyleSheetsUpdated(Document& document)
362{
363 Vector<CSSStyleSheet*> cssStyleSheets;
364 collectAllDocumentStyleSheets(document, cssStyleSheets);
365
366 setActiveStyleSheetsForDocument(document, cssStyleSheets);
367}
368
369void InspectorCSSAgent::setActiveStyleSheetsForDocument(Document& document, Vector<CSSStyleSheet*>& activeStyleSheets)
370{
371 HashSet<CSSStyleSheet*>& previouslyKnownActiveStyleSheets = m_documentToKnownCSSStyleSheets.add(&document, HashSet<CSSStyleSheet*>()).iterator->value;
372
373 HashSet<CSSStyleSheet*> removedStyleSheets(previouslyKnownActiveStyleSheets);
374 Vector<CSSStyleSheet*> addedStyleSheets;
375 for (auto& activeStyleSheet : activeStyleSheets) {
376 if (removedStyleSheets.contains(activeStyleSheet))
377 removedStyleSheets.remove(activeStyleSheet);
378 else
379 addedStyleSheets.append(activeStyleSheet);
380 }
381
382 for (auto* cssStyleSheet : removedStyleSheets) {
383 previouslyKnownActiveStyleSheets.remove(cssStyleSheet);
384 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(cssStyleSheet);
385 if (m_idToInspectorStyleSheet.contains(inspectorStyleSheet->id())) {
386 String id = unbindStyleSheet(inspectorStyleSheet.get());
387 m_frontendDispatcher->styleSheetRemoved(id);
388 }
389 }
390
391 for (auto* cssStyleSheet : addedStyleSheets) {
392 previouslyKnownActiveStyleSheets.add(cssStyleSheet);
393 if (!m_cssStyleSheetToInspectorStyleSheet.contains(cssStyleSheet)) {
394 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(cssStyleSheet);
395 m_frontendDispatcher->styleSheetAdded(inspectorStyleSheet->buildObjectForStyleSheetInfo());
396 }
397 }
398}
399
400bool InspectorCSSAgent::forcePseudoState(const Element& element, CSSSelector::PseudoClassType pseudoClassType)
401{
402 if (m_nodeIdToForcedPseudoState.isEmpty())
403 return false;
404
405 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
406 if (!domAgent)
407 return false;
408
409 int nodeId = domAgent->boundNodeId(&element);
410 if (!nodeId)
411 return false;
412
413 unsigned forcedPseudoState = m_nodeIdToForcedPseudoState.get(nodeId);
414 switch (pseudoClassType) {
415 case CSSSelector::PseudoClassActive:
416 return forcedPseudoState & PseudoClassActive;
417 case CSSSelector::PseudoClassFocus:
418 return forcedPseudoState & PseudoClassFocus;
419 case CSSSelector::PseudoClassHover:
420 return forcedPseudoState & PseudoClassHover;
421 case CSSSelector::PseudoClassVisited:
422 return forcedPseudoState & PseudoClassVisited;
423 default:
424 return false;
425 }
426}
427
428static Optional<Inspector::Protocol::CSS::PseudoId> protocolValueForPseudoId(PseudoId pseudoId)
429{
430 switch (pseudoId) {
431 case PseudoId::FirstLine:
432 return Inspector::Protocol::CSS::PseudoId::FirstLine;
433 case PseudoId::FirstLetter:
434 return Inspector::Protocol::CSS::PseudoId::FirstLetter;
435 case PseudoId::Marker:
436 return Inspector::Protocol::CSS::PseudoId::Marker;
437 case PseudoId::Before:
438 return Inspector::Protocol::CSS::PseudoId::Before;
439 case PseudoId::After:
440 return Inspector::Protocol::CSS::PseudoId::After;
441 case PseudoId::Selection:
442 return Inspector::Protocol::CSS::PseudoId::Selection;
443 case PseudoId::Scrollbar:
444 return Inspector::Protocol::CSS::PseudoId::Scrollbar;
445 case PseudoId::ScrollbarThumb:
446 return Inspector::Protocol::CSS::PseudoId::ScrollbarThumb;
447 case PseudoId::ScrollbarButton:
448 return Inspector::Protocol::CSS::PseudoId::ScrollbarButton;
449 case PseudoId::ScrollbarTrack:
450 return Inspector::Protocol::CSS::PseudoId::ScrollbarTrack;
451 case PseudoId::ScrollbarTrackPiece:
452 return Inspector::Protocol::CSS::PseudoId::ScrollbarTrackPiece;
453 case PseudoId::ScrollbarCorner:
454 return Inspector::Protocol::CSS::PseudoId::ScrollbarCorner;
455 case PseudoId::Resizer:
456 return Inspector::Protocol::CSS::PseudoId::Resizer;
457
458 default:
459 ASSERT_NOT_REACHED();
460 return { };
461 }
462}
463
464void InspectorCSSAgent::getMatchedStylesForNode(ErrorString& errorString, int nodeId, const bool* includePseudo, const bool* includeInherited, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::RuleMatch>>& matchedCSSRules, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::PseudoIdMatches>>& pseudoIdMatches, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::InheritedStyleEntry>>& inheritedEntries)
465{
466 Element* element = elementForId(errorString, nodeId);
467 if (!element)
468 return;
469
470 Element* originalElement = element;
471 PseudoId elementPseudoId = element->pseudoId();
472 if (elementPseudoId != PseudoId::None) {
473 element = downcast<PseudoElement>(*element).hostElement();
474 if (!element) {
475 errorString = "Pseudo element has no parent"_s;
476 return;
477 }
478 }
479
480 // Matched rules.
481 StyleResolver& styleResolver = element->styleResolver();
482 auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, elementPseudoId, StyleResolver::AllCSSRules);
483 matchedCSSRules = buildArrayForMatchedRuleList(matchedRules, styleResolver, *element, elementPseudoId);
484
485 if (!originalElement->isPseudoElement()) {
486 // Pseudo elements.
487 if (!includePseudo || *includePseudo) {
488 auto pseudoElements = JSON::ArrayOf<Inspector::Protocol::CSS::PseudoIdMatches>::create();
489 for (PseudoId pseudoId = PseudoId::FirstPublicPseudoId; pseudoId < PseudoId::AfterLastInternalPseudoId; pseudoId = static_cast<PseudoId>(static_cast<unsigned>(pseudoId) + 1)) {
490 if (auto protocolPseudoId = protocolValueForPseudoId(pseudoId)) {
491 auto matchedRules = styleResolver.pseudoStyleRulesForElement(element, pseudoId, StyleResolver::AllCSSRules);
492 if (!matchedRules.isEmpty()) {
493 auto matches = Inspector::Protocol::CSS::PseudoIdMatches::create()
494 .setPseudoId(protocolPseudoId.value())
495 .setMatches(buildArrayForMatchedRuleList(matchedRules, styleResolver, *element, pseudoId))
496 .release();
497 pseudoElements->addItem(WTFMove(matches));
498 }
499 }
500 }
501
502 pseudoIdMatches = WTFMove(pseudoElements);
503 }
504
505 // Inherited styles.
506 if (!includeInherited || *includeInherited) {
507 auto entries = JSON::ArrayOf<Inspector::Protocol::CSS::InheritedStyleEntry>::create();
508 Element* parentElement = element->parentElement();
509 while (parentElement) {
510 StyleResolver& parentStyleResolver = parentElement->styleResolver();
511 auto parentMatchedRules = parentStyleResolver.styleRulesForElement(parentElement, StyleResolver::AllCSSRules);
512 auto entry = Inspector::Protocol::CSS::InheritedStyleEntry::create()
513 .setMatchedCSSRules(buildArrayForMatchedRuleList(parentMatchedRules, styleResolver, *parentElement, PseudoId::None))
514 .release();
515 if (is<StyledElement>(*parentElement) && downcast<StyledElement>(*parentElement).cssomStyle().length()) {
516 auto& styleSheet = asInspectorStyleSheet(downcast<StyledElement>(*parentElement));
517 entry->setInlineStyle(styleSheet.buildObjectForStyle(styleSheet.styleForId(InspectorCSSId(styleSheet.id(), 0))));
518 }
519
520 entries->addItem(WTFMove(entry));
521 parentElement = parentElement->parentElement();
522 }
523
524 inheritedEntries = WTFMove(entries);
525 }
526 }
527}
528
529void InspectorCSSAgent::getInlineStylesForNode(ErrorString& errorString, int nodeId, RefPtr<Inspector::Protocol::CSS::CSSStyle>& inlineStyle, RefPtr<Inspector::Protocol::CSS::CSSStyle>& attributesStyle)
530{
531 auto* element = elementForId(errorString, nodeId);
532 if (!is<StyledElement>(element))
533 return;
534
535 auto& styledElement = downcast<StyledElement>(*element);
536 auto& styleSheet = asInspectorStyleSheet(styledElement);
537 inlineStyle = styleSheet.buildObjectForStyle(&styledElement.cssomStyle());
538 if (auto attributes = buildObjectForAttributesStyle(styledElement))
539 attributesStyle = WTFMove(attributes);
540 else
541 attributesStyle = nullptr;
542}
543
544void InspectorCSSAgent::getComputedStyleForNode(ErrorString& errorString, int nodeId, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSComputedStyleProperty>>& style)
545{
546 auto* element = elementForId(errorString, nodeId);
547 if (!element)
548 return;
549
550 auto computedStyleInfo = CSSComputedStyleDeclaration::create(*element, true);
551 auto inspectorStyle = InspectorStyle::create(InspectorCSSId(), WTFMove(computedStyleInfo), nullptr);
552 style = inspectorStyle->buildArrayForComputedStyle();
553}
554
555void InspectorCSSAgent::getAllStyleSheets(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSStyleSheetHeader>>& styleInfos)
556{
557 styleInfos = JSON::ArrayOf<Inspector::Protocol::CSS::CSSStyleSheetHeader>::create();
558
559 Vector<InspectorStyleSheet*> inspectorStyleSheets;
560 collectAllStyleSheets(inspectorStyleSheets);
561 for (auto* inspectorStyleSheet : inspectorStyleSheets)
562 styleInfos->addItem(inspectorStyleSheet->buildObjectForStyleSheetInfo());
563}
564
565void InspectorCSSAgent::collectAllStyleSheets(Vector<InspectorStyleSheet*>& result)
566{
567 Vector<CSSStyleSheet*> cssStyleSheets;
568 if (auto* domAgent = m_instrumentingAgents.inspectorDOMAgent()) {
569 for (auto* document : domAgent->documents())
570 collectAllDocumentStyleSheets(*document, cssStyleSheets);
571 }
572
573 for (auto* cssStyleSheet : cssStyleSheets)
574 result.append(bindStyleSheet(cssStyleSheet));
575}
576
577void InspectorCSSAgent::collectAllDocumentStyleSheets(Document& document, Vector<CSSStyleSheet*>& result)
578{
579 auto cssStyleSheets = document.styleScope().activeStyleSheetsForInspector();
580 for (auto& cssStyleSheet : cssStyleSheets)
581 collectStyleSheets(cssStyleSheet.get(), result);
582}
583
584void InspectorCSSAgent::collectStyleSheets(CSSStyleSheet* styleSheet, Vector<CSSStyleSheet*>& result)
585{
586 result.append(styleSheet);
587
588 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
589 CSSRule* rule = styleSheet->item(i);
590 if (is<CSSImportRule>(*rule)) {
591 if (CSSStyleSheet* importedStyleSheet = downcast<CSSImportRule>(*rule).styleSheet())
592 collectStyleSheets(importedStyleSheet, result);
593 }
594 }
595}
596
597void InspectorCSSAgent::getStyleSheet(ErrorString& errorString, const String& styleSheetId, RefPtr<Inspector::Protocol::CSS::CSSStyleSheetBody>& styleSheetObject)
598{
599 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
600 if (!inspectorStyleSheet)
601 return;
602
603 styleSheetObject = inspectorStyleSheet->buildObjectForStyleSheet();
604}
605
606void InspectorCSSAgent::getStyleSheetText(ErrorString& errorString, const String& styleSheetId, String* result)
607{
608 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
609 if (!inspectorStyleSheet)
610 return;
611
612 auto text = inspectorStyleSheet->text();
613 if (!text.hasException())
614 *result = text.releaseReturnValue();
615}
616
617void InspectorCSSAgent::setStyleSheetText(ErrorString& errorString, const String& styleSheetId, const String& text)
618{
619 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
620 if (!inspectorStyleSheet)
621 return;
622
623 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
624 if (!domAgent) {
625 errorString = "Missing DOM agent"_s;
626 return;
627 }
628
629 auto result = domAgent->history()->perform(std::make_unique<SetStyleSheetTextAction>(inspectorStyleSheet, text));
630 if (result.hasException())
631 errorString = InspectorDOMAgent::toErrorString(result.releaseException());
632}
633
634void InspectorCSSAgent::setStyleText(ErrorString& errorString, const JSON::Object& fullStyleId, const String& text, RefPtr<Inspector::Protocol::CSS::CSSStyle>& result)
635{
636 InspectorCSSId compoundId(fullStyleId);
637 ASSERT(!compoundId.isEmpty());
638
639 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
640 if (!inspectorStyleSheet)
641 return;
642
643 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
644 if (!domAgent) {
645 errorString = "Missing DOM agent"_s;
646 return;
647 }
648
649 auto performResult = domAgent->history()->perform(std::make_unique<SetStyleTextAction>(inspectorStyleSheet, compoundId, text));
650 if (performResult.hasException()) {
651 errorString = InspectorDOMAgent::toErrorString(performResult.releaseException());
652 return;
653 }
654
655 result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
656}
657
658void InspectorCSSAgent::setRuleSelector(ErrorString& errorString, const JSON::Object& fullRuleId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result)
659{
660 InspectorCSSId compoundId(fullRuleId);
661 ASSERT(!compoundId.isEmpty());
662
663 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, compoundId.styleSheetId());
664 if (!inspectorStyleSheet)
665 return;
666
667 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
668 if (!domAgent) {
669 errorString = "Missing DOM agent"_s;
670 return;
671 }
672
673 auto performResult = domAgent->history()->perform(std::make_unique<SetRuleSelectorAction>(inspectorStyleSheet, compoundId, selector));
674 if (performResult.hasException()) {
675 errorString = InspectorDOMAgent::toErrorString(performResult.releaseException());
676 return;
677 }
678
679 result = inspectorStyleSheet->buildObjectForRule(inspectorStyleSheet->ruleForId(compoundId), nullptr);
680}
681
682void InspectorCSSAgent::createStyleSheet(ErrorString& errorString, const String& frameId, String* styleSheetId)
683{
684 auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
685 if (!pageAgent) {
686 errorString = "Missing Page agent"_s;
687 return;
688 }
689
690 Frame* frame = pageAgent->frameForId(frameId);
691 if (!frame) {
692 errorString = "No frame for given id found"_s;
693 return;
694 }
695
696 Document* document = frame->document();
697 if (!document) {
698 errorString = "No document for frame"_s;
699 return;
700 }
701
702 InspectorStyleSheet* inspectorStyleSheet = createInspectorStyleSheetForDocument(*document);
703 if (!inspectorStyleSheet) {
704 errorString = "Could not create stylesheet for the frame."_s;
705 return;
706 }
707
708 *styleSheetId = inspectorStyleSheet->id();
709}
710
711InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Document& document)
712{
713 if (!document.isHTMLDocument() && !document.isSVGDocument())
714 return nullptr;
715
716 auto styleElement = HTMLStyleElement::create(document);
717 styleElement->setAttributeWithoutSynchronization(HTMLNames::typeAttr, AtomicString("text/css", AtomicString::ConstructFromLiteral));
718
719 ContainerNode* targetNode;
720 // HEAD is absent in ImageDocuments, for example.
721 if (auto* head = document.head())
722 targetNode = head;
723 else if (auto* body = document.bodyOrFrameset())
724 targetNode = body;
725 else
726 return nullptr;
727
728 // Inserting this <style> into the document will trigger activeStyleSheetsUpdated
729 // and we will create an InspectorStyleSheet for this <style>'s CSSStyleSheet.
730 // Set this flag, so when we create it, we put it into the via inspector map.
731 m_creatingViaInspectorStyleSheet = true;
732 InlineStyleOverrideScope overrideScope(document);
733 auto appendResult = targetNode->appendChild(styleElement);
734 document.styleScope().flushPendingUpdate();
735 m_creatingViaInspectorStyleSheet = false;
736 if (appendResult.hasException())
737 return nullptr;
738
739 auto iterator = m_documentToInspectorStyleSheet.find(&document);
740 ASSERT(iterator != m_documentToInspectorStyleSheet.end());
741 if (iterator == m_documentToInspectorStyleSheet.end())
742 return nullptr;
743
744 auto& inspectorStyleSheetsForDocument = iterator->value;
745 ASSERT(!inspectorStyleSheetsForDocument.isEmpty());
746 if (inspectorStyleSheetsForDocument.isEmpty())
747 return nullptr;
748
749 return inspectorStyleSheetsForDocument.last().get();
750}
751
752void InspectorCSSAgent::addRule(ErrorString& errorString, const String& styleSheetId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result)
753{
754 InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
755 if (!inspectorStyleSheet) {
756 errorString = "No target stylesheet found"_s;
757 return;
758 }
759
760 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
761 if (!domAgent) {
762 errorString = "Missing DOM agent"_s;
763 return;
764 }
765
766 auto action = std::make_unique<AddRuleAction>(inspectorStyleSheet, selector);
767 auto& rawAction = *action;
768 auto performResult = domAgent->history()->perform(WTFMove(action));
769 if (performResult.hasException()) {
770 errorString = InspectorDOMAgent::toErrorString(performResult.releaseException());
771 return;
772 }
773
774 InspectorCSSId ruleId = rawAction.newRuleId();
775 CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
776 result = inspectorStyleSheet->buildObjectForRule(rule, nullptr);
777}
778
779void InspectorCSSAgent::getSupportedCSSProperties(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSPropertyInfo>>& cssProperties)
780{
781 auto properties = JSON::ArrayOf<Inspector::Protocol::CSS::CSSPropertyInfo>::create();
782 for (int i = firstCSSProperty; i <= lastCSSProperty; ++i) {
783 CSSPropertyID propertyID = convertToCSSPropertyID(i);
784 if (isInternalCSSProperty(propertyID) || !isEnabledCSSProperty(propertyID))
785 continue;
786
787 auto property = Inspector::Protocol::CSS::CSSPropertyInfo::create()
788 .setName(getPropertyNameString(propertyID))
789 .release();
790
791 auto aliases = CSSProperty::aliasesForProperty(propertyID);
792 if (!aliases.isEmpty()) {
793 auto aliasesArray = JSON::ArrayOf<String>::create();
794 for (auto& alias : aliases)
795 aliasesArray->addItem(alias);
796 property->setAliases(WTFMove(aliasesArray));
797 }
798
799 const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
800 if (shorthand.length()) {
801 auto longhands = JSON::ArrayOf<String>::create();
802 for (unsigned j = 0; j < shorthand.length(); ++j) {
803 CSSPropertyID longhandID = shorthand.properties()[j];
804 if (isEnabledCSSProperty(longhandID))
805 longhands->addItem(getPropertyNameString(longhandID));
806 }
807 property->setLonghands(WTFMove(longhands));
808 }
809
810 if (CSSParserFastPaths::isKeywordPropertyID(propertyID)) {
811 auto values = JSON::ArrayOf<String>::create();
812 for (int j = firstCSSValueKeyword; j <= lastCSSValueKeyword; ++j) {
813 CSSValueID valueID = convertToCSSValueID(j);
814 if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyID, valueID, strictCSSParserContext()))
815 values->addItem(getValueNameString(valueID));
816 }
817 if (values->length())
818 property->setValues(WTFMove(values));
819 }
820
821 if (CSSProperty::isInheritedProperty(propertyID))
822 property->setInherited(true);
823
824 properties->addItem(WTFMove(property));
825 }
826 cssProperties = WTFMove(properties);
827}
828
829void InspectorCSSAgent::getSupportedSystemFontFamilyNames(ErrorString&, RefPtr<JSON::ArrayOf<String>>& fontFamilyNames)
830{
831 auto families = JSON::ArrayOf<String>::create();
832
833 Vector<String> systemFontFamilies = FontCache::singleton().systemFontFamilies();
834 for (const auto& familyName : systemFontFamilies)
835 families->addItem(familyName);
836
837 fontFamilyNames = WTFMove(families);
838}
839
840void InspectorCSSAgent::forcePseudoState(ErrorString& errorString, int nodeId, const JSON::Array& forcedPseudoClasses)
841{
842 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
843 if (!domAgent) {
844 errorString = "Missing DOM agent"_s;
845 return;
846 }
847
848 Element* element = domAgent->assertElement(errorString, nodeId);
849 if (!element)
850 return;
851
852 // Return early if the forced pseudo state was already set correctly.
853 unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses);
854 if (forcedPseudoState) {
855 auto iterator = m_nodeIdToForcedPseudoState.add(nodeId, 0).iterator;
856 if (forcedPseudoState == iterator->value)
857 return;
858 iterator->value = forcedPseudoState;
859 m_documentsWithForcedPseudoStates.add(&element->document());
860 } else {
861 if (!m_nodeIdToForcedPseudoState.remove(nodeId))
862 return;
863 if (m_nodeIdToForcedPseudoState.isEmpty())
864 m_documentsWithForcedPseudoStates.clear();
865 }
866
867 element->document().styleScope().didChangeStyleSheetEnvironment();
868}
869
870InspectorStyleSheetForInlineStyle& InspectorCSSAgent::asInspectorStyleSheet(StyledElement& element)
871{
872 return m_nodeToInspectorStyleSheet.ensure(&element, [this, &element] {
873 String newStyleSheetId = String::number(m_lastStyleSheetId++);
874 auto inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(m_instrumentingAgents.inspectorPageAgent(), newStyleSheetId, element, Inspector::Protocol::CSS::StyleSheetOrigin::Regular, this);
875 m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet.copyRef());
876 return inspectorStyleSheet;
877 }).iterator->value;
878}
879
880Element* InspectorCSSAgent::elementForId(ErrorString& errorString, int nodeId)
881{
882 auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
883 if (!domAgent) {
884 errorString = "Missing DOM agent"_s;
885 return nullptr;
886 }
887
888 Node* node = domAgent->nodeForId(nodeId);
889 if (!node) {
890 errorString = "No node with given id found"_s;
891 return nullptr;
892 }
893 if (!is<Element>(*node)) {
894 errorString = "Not an element node"_s;
895 return nullptr;
896 }
897 return downcast<Element>(node);
898}
899
900String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
901{
902 String id = inspectorStyleSheet->id();
903 m_idToInspectorStyleSheet.remove(id);
904 if (inspectorStyleSheet->pageStyleSheet())
905 m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
906 return id;
907}
908
909InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
910{
911 RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
912 if (!inspectorStyleSheet) {
913 String id = String::number(m_lastStyleSheetId++);
914 Document* document = styleSheet->ownerDocument();
915 inspectorStyleSheet = InspectorStyleSheet::create(m_instrumentingAgents.inspectorPageAgent(), id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
916 m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
917 m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
918 if (m_creatingViaInspectorStyleSheet) {
919 auto& inspectorStyleSheetsForDocument = m_documentToInspectorStyleSheet.add(document, Vector<RefPtr<InspectorStyleSheet>>()).iterator->value;
920 inspectorStyleSheetsForDocument.append(inspectorStyleSheet);
921 }
922 }
923 return inspectorStyleSheet.get();
924}
925
926InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString& errorString, const String& styleSheetId)
927{
928 IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
929 if (it == m_idToInspectorStyleSheet.end()) {
930 errorString = "No stylesheet with given id found"_s;
931 return nullptr;
932 }
933 return it->value.get();
934}
935
936Inspector::Protocol::CSS::StyleSheetOrigin InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
937{
938 if (m_creatingViaInspectorStyleSheet)
939 return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
940
941 if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
942 return Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent;
943
944 if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
945 return Inspector::Protocol::CSS::StyleSheetOrigin::User;
946
947 auto iterator = m_documentToInspectorStyleSheet.find(ownerDocument);
948 if (iterator != m_documentToInspectorStyleSheet.end()) {
949 for (auto& inspectorStyleSheet : iterator->value) {
950 if (pageStyleSheet == inspectorStyleSheet->pageStyleSheet())
951 return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
952 }
953 }
954
955 return Inspector::Protocol::CSS::StyleSheetOrigin::Regular;
956}
957
958RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(StyleRule* styleRule, StyleResolver& styleResolver, Element& element)
959{
960 if (!styleRule)
961 return nullptr;
962
963 // StyleRules returned by StyleResolver::styleRulesForElement lack parent pointers since that infomation is not cheaply available.
964 // Since the inspector wants to walk the parent chain, we construct the full wrappers here.
965 styleResolver.inspectorCSSOMWrappers().collectDocumentWrappers(styleResolver.document().extensionStyleSheets());
966 styleResolver.inspectorCSSOMWrappers().collectScopeWrappers(Style::Scope::forNode(element));
967
968 // Possiblity of :host styles if this element has a shadow root.
969 if (ShadowRoot* shadowRoot = element.shadowRoot())
970 styleResolver.inspectorCSSOMWrappers().collectScopeWrappers(shadowRoot->styleScope());
971
972 CSSStyleRule* cssomWrapper = styleResolver.inspectorCSSOMWrappers().getWrapperForRuleInSheets(styleRule);
973 if (!cssomWrapper)
974 return nullptr;
975
976 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(cssomWrapper->parentStyleSheet());
977 return inspectorStyleSheet ? inspectorStyleSheet->buildObjectForRule(cssomWrapper, &element) : nullptr;
978}
979
980RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
981{
982 if (!rule)
983 return nullptr;
984
985 ASSERT(rule->parentStyleSheet());
986 InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(rule->parentStyleSheet());
987 return inspectorStyleSheet ? inspectorStyleSheet->buildObjectForRule(rule, nullptr) : nullptr;
988}
989
990RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::RuleMatch>> InspectorCSSAgent::buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>& matchedRules, StyleResolver& styleResolver, Element& element, PseudoId pseudoId)
991{
992 auto result = JSON::ArrayOf<Inspector::Protocol::CSS::RuleMatch>::create();
993
994 SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
995 context.pseudoId = pseudoId != PseudoId::None ? pseudoId : element.pseudoId();
996 SelectorChecker selectorChecker(element.document());
997
998 for (auto& matchedRule : matchedRules) {
999 RefPtr<Inspector::Protocol::CSS::CSSRule> ruleObject = buildObjectForRule(matchedRule.get(), styleResolver, element);
1000 if (!ruleObject)
1001 continue;
1002
1003 auto matchingSelectors = JSON::ArrayOf<int>::create();
1004 const CSSSelectorList& selectorList = matchedRule->selectorList();
1005 int index = 0;
1006 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
1007 unsigned ignoredSpecificity;
1008 bool matched = selectorChecker.match(*selector, element, context, ignoredSpecificity);
1009 if (matched)
1010 matchingSelectors->addItem(index);
1011 ++index;
1012 }
1013
1014 auto match = Inspector::Protocol::CSS::RuleMatch::create()
1015 .setRule(WTFMove(ruleObject))
1016 .setMatchingSelectors(WTFMove(matchingSelectors))
1017 .release();
1018 result->addItem(WTFMove(match));
1019 }
1020
1021 return result;
1022}
1023
1024RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(StyledElement& element)
1025{
1026 // FIXME: Ugliness below.
1027 auto* attributeStyle = const_cast<StyleProperties*>(element.presentationAttributeStyle());
1028 if (!attributeStyle)
1029 return nullptr;
1030
1031 auto& mutableAttributeStyle = downcast<MutableStyleProperties>(*attributeStyle);
1032 auto inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle.ensureCSSStyleDeclaration(), nullptr);
1033 return inspectorStyle->buildObjectForStyle();
1034}
1035
1036void InspectorCSSAgent::didRemoveDOMNode(Node& node, int nodeId)
1037{
1038 m_nodeIdToForcedPseudoState.remove(nodeId);
1039
1040 auto sheet = m_nodeToInspectorStyleSheet.take(&node);
1041 if (!sheet)
1042 return;
1043 m_idToInspectorStyleSheet.remove(sheet.value()->id());
1044}
1045
1046void InspectorCSSAgent::didModifyDOMAttr(Element& element)
1047{
1048 auto sheet = m_nodeToInspectorStyleSheet.get(&element);
1049 if (!sheet)
1050 return;
1051 sheet->didModifyElementAttribute();
1052}
1053
1054void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheet* styleSheet)
1055{
1056 m_frontendDispatcher->styleSheetChanged(styleSheet->id());
1057}
1058
1059void InspectorCSSAgent::resetPseudoStates()
1060{
1061 for (auto& document : m_documentsWithForcedPseudoStates)
1062 document->styleScope().didChangeStyleSheetEnvironment();
1063
1064 m_nodeIdToForcedPseudoState.clear();
1065 m_documentsWithForcedPseudoStates.clear();
1066}
1067
1068} // namespace WebCore
1069