Web Inspector: Page: re-add enable/disable after r248454
[WebKit-https.git] / Source / WebCore / inspector / agents / InspectorCSSAgent.cpp
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
70 namespace WebCore {
71
72 using namespace Inspector;
73
74 enum ForcePseudoClassFlags {
75     PseudoClassNone = 0,
76     PseudoClassHover = 1 << 0,
77     PseudoClassFocus = 1 << 1,
78     PseudoClassActive = 1 << 2,
79     PseudoClassVisited = 1 << 3
80 };
81
82 static 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
110 class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
111     WTF_MAKE_NONCOPYABLE(StyleSheetAction);
112 public:
113     StyleSheetAction(InspectorStyleSheet* styleSheet)
114         : InspectorHistory::Action()
115         , m_styleSheet(styleSheet)
116     {
117     }
118
119 protected:
120     RefPtr<InspectorStyleSheet> m_styleSheet;
121 };
122
123 class InspectorCSSAgent::SetStyleSheetTextAction final : public InspectorCSSAgent::StyleSheetAction {
124     WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
125 public:
126     SetStyleSheetTextAction(InspectorStyleSheet* styleSheet, const String& text)
127         : InspectorCSSAgent::StyleSheetAction(styleSheet)
128         , m_text(text)
129     {
130     }
131
132 private:
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
175 class InspectorCSSAgent::SetStyleTextAction final : public InspectorCSSAgent::StyleSheetAction {
176     WTF_MAKE_NONCOPYABLE(SetStyleTextAction);
177 public:
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
214 private:
215     InspectorCSSId m_cssId;
216     String m_text;
217     String m_oldText;
218 };
219
220 class InspectorCSSAgent::SetRuleSelectorAction final : public InspectorCSSAgent::StyleSheetAction {
221     WTF_MAKE_NONCOPYABLE(SetRuleSelectorAction);
222 public:
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
230 private:
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
255 class InspectorCSSAgent::AddRuleAction final : public InspectorCSSAgent::StyleSheetAction {
256     WTF_MAKE_NONCOPYABLE(AddRuleAction);
257 public:
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
266 private:
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
291 CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule& rule)
292 {
293     if (!is<CSSStyleRule>(rule))
294         return nullptr;
295     return downcast<CSSStyleRule>(&rule);
296 }
297
298 InspectorCSSAgent::InspectorCSSAgent(WebAgentContext& context)
299     : InspectorAgentBase("CSS"_s, context)
300     , m_frontendDispatcher(makeUnique<CSSFrontendDispatcher>(context.frontendRouter))
301     , m_backendDispatcher(CSSBackendDispatcher::create(context.backendDispatcher, this))
302 {
303 }
304
305 void InspectorCSSAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
306 {
307 }
308
309 void InspectorCSSAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
310 {
311     String unused;
312     disable(unused);
313 }
314
315 void 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
326 void 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
339 void InspectorCSSAgent::disable(ErrorString&)
340 {
341     m_instrumentingAgents.setInspectorCSSAgent(nullptr);
342
343     reset();
344 }
345
346 void 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
356 void InspectorCSSAgent::mediaQueryResultChanged()
357 {
358     m_frontendDispatcher->mediaQueryResultChanged();
359 }
360
361 void InspectorCSSAgent::activeStyleSheetsUpdated(Document& document)
362 {
363     Vector<CSSStyleSheet*> cssStyleSheets;
364     collectAllDocumentStyleSheets(document, cssStyleSheets);
365
366     setActiveStyleSheetsForDocument(document, cssStyleSheets);
367 }
368
369 void 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
400 bool 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
428 static 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
464 void 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
529 void 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
544 void 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
555 void 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
565 void 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
577 void 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
584 void 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
597 void 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
606 void 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
617 void 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(makeUnique<SetStyleSheetTextAction>(inspectorStyleSheet, text));
630     if (result.hasException())
631         errorString = InspectorDOMAgent::toErrorString(result.releaseException());
632 }
633
634 void 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(makeUnique<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
658 void 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(makeUnique<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
682 void InspectorCSSAgent::createStyleSheet(ErrorString& errorString, const String& frameId, String* styleSheetId)
683 {
684     auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
685     if (!pageAgent) {
686         errorString = "Page domain must be enabled"_s;
687         return;
688     }
689
690     auto* frame = pageAgent->assertFrame(errorString, frameId);
691     if (!frame)
692         return;
693
694     Document* document = frame->document();
695     if (!document) {
696         errorString = "No document for frame"_s;
697         return;
698     }
699
700     InspectorStyleSheet* inspectorStyleSheet = createInspectorStyleSheetForDocument(*document);
701     if (!inspectorStyleSheet) {
702         errorString = "Could not create stylesheet for the frame."_s;
703         return;
704     }
705
706     *styleSheetId = inspectorStyleSheet->id();
707 }
708
709 InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Document& document)
710 {
711     if (!document.isHTMLDocument() && !document.isSVGDocument())
712         return nullptr;
713
714     auto styleElement = HTMLStyleElement::create(document);
715     styleElement->setAttributeWithoutSynchronization(HTMLNames::typeAttr, AtomString("text/css", AtomString::ConstructFromLiteral));
716
717     ContainerNode* targetNode;
718     // HEAD is absent in ImageDocuments, for example.
719     if (auto* head = document.head())
720         targetNode = head;
721     else if (auto* body = document.bodyOrFrameset())
722         targetNode = body;
723     else
724         return nullptr;
725
726     // Inserting this <style> into the document will trigger activeStyleSheetsUpdated
727     // and we will create an InspectorStyleSheet for this <style>'s CSSStyleSheet.
728     // Set this flag, so when we create it, we put it into the via inspector map.
729     m_creatingViaInspectorStyleSheet = true;
730     InlineStyleOverrideScope overrideScope(document);
731     auto appendResult = targetNode->appendChild(styleElement);
732     document.styleScope().flushPendingUpdate();
733     m_creatingViaInspectorStyleSheet = false;
734     if (appendResult.hasException())
735         return nullptr;
736
737     auto iterator = m_documentToInspectorStyleSheet.find(&document);
738     ASSERT(iterator != m_documentToInspectorStyleSheet.end());
739     if (iterator == m_documentToInspectorStyleSheet.end())
740         return nullptr;
741
742     auto& inspectorStyleSheetsForDocument = iterator->value;
743     ASSERT(!inspectorStyleSheetsForDocument.isEmpty());
744     if (inspectorStyleSheetsForDocument.isEmpty())
745         return nullptr;
746
747     return inspectorStyleSheetsForDocument.last().get();
748 }
749
750 void InspectorCSSAgent::addRule(ErrorString& errorString, const String& styleSheetId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result)
751 {
752     InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
753     if (!inspectorStyleSheet) {
754         errorString = "No target stylesheet found"_s;
755         return;
756     }
757
758     auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
759     if (!domAgent) {
760         errorString = "Missing DOM agent"_s;
761         return;
762     }
763
764     auto action = makeUnique<AddRuleAction>(inspectorStyleSheet, selector);
765     auto& rawAction = *action;
766     auto performResult = domAgent->history()->perform(WTFMove(action));
767     if (performResult.hasException()) {
768         errorString = InspectorDOMAgent::toErrorString(performResult.releaseException());
769         return;
770     }
771
772     InspectorCSSId ruleId = rawAction.newRuleId();
773     CSSStyleRule* rule = inspectorStyleSheet->ruleForId(ruleId);
774     result = inspectorStyleSheet->buildObjectForRule(rule, nullptr);
775 }
776
777 void InspectorCSSAgent::getSupportedCSSProperties(ErrorString&, RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSPropertyInfo>>& cssProperties)
778 {
779     auto properties = JSON::ArrayOf<Inspector::Protocol::CSS::CSSPropertyInfo>::create();
780     for (int i = firstCSSProperty; i <= lastCSSProperty; ++i) {
781         CSSPropertyID propertyID = convertToCSSPropertyID(i);
782         if (isInternalCSSProperty(propertyID) || !isEnabledCSSProperty(propertyID))
783             continue;
784
785         auto property = Inspector::Protocol::CSS::CSSPropertyInfo::create()
786             .setName(getPropertyNameString(propertyID))
787             .release();
788
789         auto aliases = CSSProperty::aliasesForProperty(propertyID);
790         if (!aliases.isEmpty()) {
791             auto aliasesArray = JSON::ArrayOf<String>::create();
792             for (auto& alias : aliases)
793                 aliasesArray->addItem(alias);
794             property->setAliases(WTFMove(aliasesArray));
795         }
796
797         const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
798         if (shorthand.length()) {
799             auto longhands = JSON::ArrayOf<String>::create();
800             for (unsigned j = 0; j < shorthand.length(); ++j) {
801                 CSSPropertyID longhandID = shorthand.properties()[j];
802                 if (isEnabledCSSProperty(longhandID))
803                     longhands->addItem(getPropertyNameString(longhandID));
804             }
805             property->setLonghands(WTFMove(longhands));
806         }
807
808         if (CSSParserFastPaths::isKeywordPropertyID(propertyID)) {
809             auto values = JSON::ArrayOf<String>::create();
810             for (int j = firstCSSValueKeyword; j <= lastCSSValueKeyword; ++j) {
811                 CSSValueID valueID = convertToCSSValueID(j);
812                 if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyID, valueID, strictCSSParserContext()))
813                     values->addItem(getValueNameString(valueID));
814             }
815             if (values->length())
816                 property->setValues(WTFMove(values));
817         }
818
819         if (CSSProperty::isInheritedProperty(propertyID))
820             property->setInherited(true);
821
822         properties->addItem(WTFMove(property));
823     }
824     cssProperties = WTFMove(properties);
825 }
826
827 void InspectorCSSAgent::getSupportedSystemFontFamilyNames(ErrorString&, RefPtr<JSON::ArrayOf<String>>& fontFamilyNames)
828 {
829     auto families = JSON::ArrayOf<String>::create();
830
831     Vector<String> systemFontFamilies = FontCache::singleton().systemFontFamilies();
832     for (const auto& familyName : systemFontFamilies)
833         families->addItem(familyName);
834
835     fontFamilyNames = WTFMove(families);
836 }
837
838 void InspectorCSSAgent::forcePseudoState(ErrorString& errorString, int nodeId, const JSON::Array& forcedPseudoClasses)
839 {
840     auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
841     if (!domAgent) {
842         errorString = "Missing DOM agent"_s;
843         return;
844     }
845
846     Element* element = domAgent->assertElement(errorString, nodeId);
847     if (!element)
848         return;
849
850     // Return early if the forced pseudo state was already set correctly.
851     unsigned forcedPseudoState = computePseudoClassMask(forcedPseudoClasses);
852     if (forcedPseudoState) {
853         auto iterator = m_nodeIdToForcedPseudoState.add(nodeId, 0).iterator;
854         if (forcedPseudoState == iterator->value)
855             return;
856         iterator->value = forcedPseudoState;
857         m_documentsWithForcedPseudoStates.add(&element->document());
858     } else {
859         if (!m_nodeIdToForcedPseudoState.remove(nodeId))
860             return;
861         if (m_nodeIdToForcedPseudoState.isEmpty())
862             m_documentsWithForcedPseudoStates.clear();
863     }
864
865     element->document().styleScope().didChangeStyleSheetEnvironment();
866 }
867
868 InspectorStyleSheetForInlineStyle& InspectorCSSAgent::asInspectorStyleSheet(StyledElement& element)
869 {
870     return m_nodeToInspectorStyleSheet.ensure(&element, [this, &element] {
871         String newStyleSheetId = String::number(m_lastStyleSheetId++);
872         auto inspectorStyleSheet = InspectorStyleSheetForInlineStyle::create(m_instrumentingAgents.inspectorPageAgent(), newStyleSheetId, element, Inspector::Protocol::CSS::StyleSheetOrigin::Regular, this);
873         m_idToInspectorStyleSheet.set(newStyleSheetId, inspectorStyleSheet.copyRef());
874         return inspectorStyleSheet;
875     }).iterator->value;
876 }
877
878 Element* InspectorCSSAgent::elementForId(ErrorString& errorString, int nodeId)
879 {
880     auto* domAgent = m_instrumentingAgents.inspectorDOMAgent();
881     if (!domAgent) {
882         errorString = "Missing DOM agent"_s;
883         return nullptr;
884     }
885
886     Node* node = domAgent->nodeForId(nodeId);
887     if (!node) {
888         errorString = "No node with given id found"_s;
889         return nullptr;
890     }
891     if (!is<Element>(*node)) {
892         errorString = "Not an element node"_s;
893         return nullptr;
894     }
895     return downcast<Element>(node);
896 }
897
898 String InspectorCSSAgent::unbindStyleSheet(InspectorStyleSheet* inspectorStyleSheet)
899 {
900     String id = inspectorStyleSheet->id();
901     m_idToInspectorStyleSheet.remove(id);
902     if (inspectorStyleSheet->pageStyleSheet())
903         m_cssStyleSheetToInspectorStyleSheet.remove(inspectorStyleSheet->pageStyleSheet());
904     return id;
905 }
906
907 InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet)
908 {
909     RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_cssStyleSheetToInspectorStyleSheet.get(styleSheet);
910     if (!inspectorStyleSheet) {
911         String id = String::number(m_lastStyleSheetId++);
912         Document* document = styleSheet->ownerDocument();
913         inspectorStyleSheet = InspectorStyleSheet::create(m_instrumentingAgents.inspectorPageAgent(), id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
914         m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
915         m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
916         if (m_creatingViaInspectorStyleSheet) {
917             auto& inspectorStyleSheetsForDocument = m_documentToInspectorStyleSheet.add(document, Vector<RefPtr<InspectorStyleSheet>>()).iterator->value;
918             inspectorStyleSheetsForDocument.append(inspectorStyleSheet);
919         }
920     }
921     return inspectorStyleSheet.get();
922 }
923
924 InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString& errorString, const String& styleSheetId)
925 {
926     IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
927     if (it == m_idToInspectorStyleSheet.end()) {
928         errorString = "No stylesheet with given id found"_s;
929         return nullptr;
930     }
931     return it->value.get();
932 }
933
934 Inspector::Protocol::CSS::StyleSheetOrigin InspectorCSSAgent::detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument)
935 {
936     if (m_creatingViaInspectorStyleSheet)
937         return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
938
939     if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
940         return Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent;
941
942     if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
943         return Inspector::Protocol::CSS::StyleSheetOrigin::User;
944
945     auto iterator = m_documentToInspectorStyleSheet.find(ownerDocument);
946     if (iterator != m_documentToInspectorStyleSheet.end()) {
947         for (auto& inspectorStyleSheet : iterator->value) {
948             if (pageStyleSheet == inspectorStyleSheet->pageStyleSheet())
949                 return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
950         }
951     }
952
953     return Inspector::Protocol::CSS::StyleSheetOrigin::Regular;
954 }
955
956 RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(StyleRule* styleRule, StyleResolver& styleResolver, Element& element)
957 {
958     if (!styleRule)
959         return nullptr;
960
961     // StyleRules returned by StyleResolver::styleRulesForElement lack parent pointers since that infomation is not cheaply available.
962     // Since the inspector wants to walk the parent chain, we construct the full wrappers here.
963     styleResolver.inspectorCSSOMWrappers().collectDocumentWrappers(styleResolver.document().extensionStyleSheets());
964     styleResolver.inspectorCSSOMWrappers().collectScopeWrappers(Style::Scope::forNode(element));
965
966     // Possiblity of :host styles if this element has a shadow root.
967     if (ShadowRoot* shadowRoot = element.shadowRoot())
968         styleResolver.inspectorCSSOMWrappers().collectScopeWrappers(shadowRoot->styleScope());
969
970     CSSStyleRule* cssomWrapper = styleResolver.inspectorCSSOMWrappers().getWrapperForRuleInSheets(styleRule);
971     if (!cssomWrapper)
972         return nullptr;
973
974     InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(cssomWrapper->parentStyleSheet());
975     return inspectorStyleSheet ? inspectorStyleSheet->buildObjectForRule(cssomWrapper, &element) : nullptr;
976 }
977
978 RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorCSSAgent::buildObjectForRule(CSSStyleRule* rule)
979 {
980     if (!rule)
981         return nullptr;
982
983     ASSERT(rule->parentStyleSheet());
984     InspectorStyleSheet* inspectorStyleSheet = bindStyleSheet(rule->parentStyleSheet());
985     return inspectorStyleSheet ? inspectorStyleSheet->buildObjectForRule(rule, nullptr) : nullptr;
986 }
987
988 RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::RuleMatch>> InspectorCSSAgent::buildArrayForMatchedRuleList(const Vector<RefPtr<StyleRule>>& matchedRules, StyleResolver& styleResolver, Element& element, PseudoId pseudoId)
989 {
990     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::RuleMatch>::create();
991
992     SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
993     context.pseudoId = pseudoId != PseudoId::None ? pseudoId : element.pseudoId();
994     SelectorChecker selectorChecker(element.document());
995
996     for (auto& matchedRule : matchedRules) {
997         RefPtr<Inspector::Protocol::CSS::CSSRule> ruleObject = buildObjectForRule(matchedRule.get(), styleResolver, element);
998         if (!ruleObject)
999             continue;
1000
1001         auto matchingSelectors = JSON::ArrayOf<int>::create();
1002         const CSSSelectorList& selectorList = matchedRule->selectorList();
1003         int index = 0;
1004         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
1005             unsigned ignoredSpecificity;
1006             bool matched = selectorChecker.match(*selector, element, context, ignoredSpecificity);
1007             if (matched)
1008                 matchingSelectors->addItem(index);
1009             ++index;
1010         }
1011
1012         auto match = Inspector::Protocol::CSS::RuleMatch::create()
1013             .setRule(WTFMove(ruleObject))
1014             .setMatchingSelectors(WTFMove(matchingSelectors))
1015             .release();
1016         result->addItem(WTFMove(match));
1017     }
1018
1019     return result;
1020 }
1021
1022 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorCSSAgent::buildObjectForAttributesStyle(StyledElement& element)
1023 {
1024     // FIXME: Ugliness below.
1025     auto* attributeStyle = const_cast<StyleProperties*>(element.presentationAttributeStyle());
1026     if (!attributeStyle)
1027         return nullptr;
1028
1029     auto& mutableAttributeStyle = downcast<MutableStyleProperties>(*attributeStyle);
1030     auto inspectorStyle = InspectorStyle::create(InspectorCSSId(), mutableAttributeStyle.ensureCSSStyleDeclaration(), nullptr);
1031     return inspectorStyle->buildObjectForStyle();
1032 }
1033
1034 void InspectorCSSAgent::didRemoveDOMNode(Node& node, int nodeId)
1035 {
1036     m_nodeIdToForcedPseudoState.remove(nodeId);
1037
1038     auto sheet = m_nodeToInspectorStyleSheet.take(&node);
1039     if (!sheet)
1040         return;
1041     m_idToInspectorStyleSheet.remove(sheet.value()->id());
1042 }
1043
1044 void InspectorCSSAgent::didModifyDOMAttr(Element& element)
1045 {
1046     auto sheet = m_nodeToInspectorStyleSheet.get(&element);
1047     if (!sheet)
1048         return;
1049     sheet->didModifyElementAttribute();
1050 }
1051
1052 void InspectorCSSAgent::styleSheetChanged(InspectorStyleSheet* styleSheet)
1053 {
1054     m_frontendDispatcher->styleSheetChanged(styleSheet->id());
1055 }
1056
1057 void InspectorCSSAgent::resetPseudoStates()
1058 {
1059     for (auto& document : m_documentsWithForcedPseudoStates)
1060         document->styleScope().didChangeStyleSheetEnvironment();
1061
1062     m_nodeIdToForcedPseudoState.clear();
1063     m_documentsWithForcedPseudoStates.clear();
1064 }
1065
1066 } // namespace WebCore