Rename "forced style recalc" to "full style rebuild"
[WebKit-https.git] / Source / WebCore / style / StyleScope.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  *           (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6  * Copyright (C) 2004-2009, 2011-2012, 2015-2017 Apple Inc. All rights reserved.
7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
9  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10  * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27
28 #include "config.h"
29 #include "StyleScope.h"
30
31 #include "CSSFontSelector.h"
32 #include "CSSStyleSheet.h"
33 #include "Element.h"
34 #include "ElementChildIterator.h"
35 #include "ExtensionStyleSheets.h"
36 #include "HTMLHeadElement.h"
37 #include "HTMLIFrameElement.h"
38 #include "HTMLLinkElement.h"
39 #include "HTMLSlotElement.h"
40 #include "HTMLStyleElement.h"
41 #include "InspectorInstrumentation.h"
42 #include "ProcessingInstruction.h"
43 #include "SVGStyleElement.h"
44 #include "Settings.h"
45 #include "ShadowRoot.h"
46 #include "StyleInvalidator.h"
47 #include "StyleResolver.h"
48 #include "StyleSheetContents.h"
49 #include "StyleSheetList.h"
50 #include "UserContentController.h"
51 #include "UserContentURLPattern.h"
52 #include "UserStyleSheet.h"
53 #include <wtf/SetForScope.h>
54
55 namespace WebCore {
56
57 using namespace ContentExtensions;
58 using namespace HTMLNames;
59
60 namespace Style {
61
62 Scope::Scope(Document& document)
63     : m_document(document)
64     , m_pendingUpdateTimer(*this, &Scope::pendingUpdateTimerFired)
65 {
66 }
67
68 Scope::Scope(ShadowRoot& shadowRoot)
69     : m_document(shadowRoot.documentScope())
70     , m_shadowRoot(&shadowRoot)
71     , m_pendingUpdateTimer(*this, &Scope::pendingUpdateTimerFired)
72 {
73 }
74
75 Scope::~Scope()
76 {
77     ASSERT(!hasPendingSheets());
78 }
79
80 bool Scope::shouldUseSharedUserAgentShadowTreeStyleResolver() const
81 {
82     if (!m_shadowRoot)
83         return false;
84     if (m_shadowRoot->mode() != ShadowRootMode::UserAgent)
85         return false;
86     // If we have stylesheets in the user agent shadow tree use per-scope resolver.
87     if (!m_styleSheetCandidateNodes.isEmpty())
88         return false;
89     return true;
90 }
91
92 StyleResolver& Scope::resolver()
93 {
94     if (shouldUseSharedUserAgentShadowTreeStyleResolver())
95         return m_document.userAgentShadowTreeStyleResolver();
96
97     if (!m_resolver) {
98         SetForScope<bool> isUpdatingStyleResolver { m_isUpdatingStyleResolver, true };
99
100         m_resolver = std::make_unique<StyleResolver>(m_document);
101
102         if (!m_shadowRoot) {
103             m_document.fontSelector().buildStarted();
104             m_resolver->ruleSets().initializeUserStyle();
105         } else {
106             m_resolver->ruleSets().setIsForShadowScope();
107             m_resolver->ruleSets().setUsesSharedUserStyle(m_shadowRoot->mode() != ShadowRootMode::UserAgent);
108         }
109
110         m_resolver->addCurrentSVGFontFaceRules();
111         m_resolver->appendAuthorStyleSheets(m_activeStyleSheets);
112
113         if (!m_shadowRoot)
114             m_document.fontSelector().buildCompleted();
115     }
116     ASSERT(!m_shadowRoot || &m_document == &m_shadowRoot->document());
117     ASSERT(&m_resolver->document() == &m_document);
118     return *m_resolver;
119 }
120
121 StyleResolver* Scope::resolverIfExists()
122 {
123     if (shouldUseSharedUserAgentShadowTreeStyleResolver())
124         return &m_document.userAgentShadowTreeStyleResolver();
125
126     return m_resolver.get();
127 }
128
129 void Scope::clearResolver()
130 {
131     m_resolver = nullptr;
132
133     if (!m_shadowRoot)
134         m_document.didClearStyleResolver();
135 }
136
137 void Scope::releaseMemory()
138 {
139     if (!m_shadowRoot) {
140         for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots())
141             descendantShadowRoot->styleScope().releaseMemory();
142     }
143
144 #if ENABLE(CSS_SELECTOR_JIT)
145     for (auto& sheet : m_activeStyleSheets) {
146         sheet->contents().traverseRules([] (const StyleRuleBase& rule) {
147             if (is<StyleRule>(rule))
148                 downcast<StyleRule>(rule).releaseCompiledSelectors();
149             return false;
150         });
151     }
152 #endif
153     clearResolver();
154 }
155
156 Scope& Scope::forNode(Node& node)
157 {
158     ASSERT(node.isConnected());
159     auto* shadowRoot = node.containingShadowRoot();
160     if (shadowRoot)
161         return shadowRoot->styleScope();
162     return node.document().styleScope();
163 }
164
165 Scope* Scope::forOrdinal(Element& element, ScopeOrdinal ordinal)
166 {
167     switch (ordinal) {
168     case ScopeOrdinal::Element:
169         return &forNode(element);
170     case ScopeOrdinal::ContainingHost: {
171         auto* containingShadowRoot = element.containingShadowRoot();
172         if (!containingShadowRoot)
173             return nullptr;
174         return &forNode(*containingShadowRoot->host());
175     }
176     case ScopeOrdinal::Shadow: {
177         auto* shadowRoot = element.shadowRoot();
178         if (!shadowRoot)
179             return nullptr;
180         return &shadowRoot->styleScope();
181     }
182     default: {
183         ASSERT(ordinal >= ScopeOrdinal::FirstSlot);
184         auto slotIndex = ScopeOrdinal::FirstSlot;
185         for (auto* slot = element.assignedSlot(); slot; slot = slot->assignedSlot(), ++slotIndex) {
186             if (slotIndex == ordinal)
187                 return &forNode(*slot);
188         }
189         return nullptr;
190     }
191     }
192 }
193
194 void Scope::setPreferredStylesheetSetName(const String& name)
195 {
196     if (m_preferredStylesheetSetName == name)
197         return;
198     m_preferredStylesheetSetName = name;
199     didChangeActiveStyleSheetCandidates();
200 }
201
202 void Scope::addPendingSheet(const Element& element)
203 {
204     ASSERT(!hasPendingSheet(element));
205
206     bool isInHead = ancestorsOfType<HTMLHeadElement>(element).first();
207     if (isInHead)
208         m_elementsInHeadWithPendingSheets.add(&element);
209     else
210         m_elementsInBodyWithPendingSheets.add(&element);
211 }
212
213 // This method is called whenever a top-level stylesheet has finished loading.
214 void Scope::removePendingSheet(const Element& element)
215 {
216     ASSERT(hasPendingSheet(element));
217
218     if (!m_elementsInHeadWithPendingSheets.remove(&element))
219         m_elementsInBodyWithPendingSheets.remove(&element);
220
221     didRemovePendingStylesheet();
222 }
223
224 void Scope::addPendingSheet(const ProcessingInstruction& processingInstruction)
225 {
226     ASSERT(!m_processingInstructionsWithPendingSheets.contains(&processingInstruction));
227
228     m_processingInstructionsWithPendingSheets.add(&processingInstruction);
229 }
230
231 void Scope::removePendingSheet(const ProcessingInstruction& processingInstruction)
232 {
233     ASSERT(m_processingInstructionsWithPendingSheets.contains(&processingInstruction));
234
235     m_processingInstructionsWithPendingSheets.remove(&processingInstruction);
236
237     didRemovePendingStylesheet();
238 }
239
240 void Scope::didRemovePendingStylesheet()
241 {
242     if (hasPendingSheets())
243         return;
244
245     didChangeActiveStyleSheetCandidates();
246
247     if (!m_shadowRoot)
248         m_document.didRemoveAllPendingStylesheet();
249 }
250
251 bool Scope::hasPendingSheet(const Element& element) const
252 {
253     return m_elementsInHeadWithPendingSheets.contains(&element) || hasPendingSheetInBody(element);
254 }
255
256 bool Scope::hasPendingSheetInBody(const Element& element) const
257 {
258     return m_elementsInBodyWithPendingSheets.contains(&element);
259 }
260
261 bool Scope::hasPendingSheet(const ProcessingInstruction& processingInstruction) const
262 {
263     return m_processingInstructionsWithPendingSheets.contains(&processingInstruction);
264 }
265
266 void Scope::addStyleSheetCandidateNode(Node& node, bool createdByParser)
267 {
268     if (!node.isConnected())
269         return;
270     
271     // Until the <body> exists, we have no choice but to compare document positions,
272     // since styles outside of the body and head continue to be shunted into the head
273     // (and thus can shift to end up before dynamically added DOM content that is also
274     // outside the body).
275     if ((createdByParser && m_document.bodyOrFrameset()) || m_styleSheetCandidateNodes.isEmpty()) {
276         m_styleSheetCandidateNodes.add(&node);
277         return;
278     }
279
280     // Determine an appropriate insertion point.
281     auto begin = m_styleSheetCandidateNodes.begin();
282     auto end = m_styleSheetCandidateNodes.end();
283     auto it = end;
284     Node* followingNode = nullptr;
285     do {
286         --it;
287         Node* n = *it;
288         unsigned short position = n->compareDocumentPosition(node);
289         if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
290             m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
291             return;
292         }
293         followingNode = n;
294     } while (it != begin);
295     
296     m_styleSheetCandidateNodes.insertBefore(followingNode, &node);
297 }
298
299 void Scope::removeStyleSheetCandidateNode(Node& node)
300 {
301     if (m_styleSheetCandidateNodes.remove(&node))
302         didChangeActiveStyleSheetCandidates();
303 }
304
305 #if ENABLE(XSLT)
306 // FIXME: <https://webkit.org/b/178830> Remove XSLT relaed code from Style::Scope.
307 Vector<Ref<ProcessingInstruction>> Scope::collectXSLTransforms()
308 {
309     Vector<Ref<ProcessingInstruction>> processingInstructions;
310     for (auto& node : m_styleSheetCandidateNodes) {
311         if (is<ProcessingInstruction>(*node) && downcast<ProcessingInstruction>(*node).isXSL())
312             processingInstructions.append(downcast<ProcessingInstruction>(*node));
313     }
314     return processingInstructions;
315 }
316 #endif
317
318 void Scope::collectActiveStyleSheets(Vector<RefPtr<StyleSheet>>& sheets)
319 {
320     if (!m_document.settings().authorAndUserStylesEnabled())
321         return;
322
323     for (auto& node : m_styleSheetCandidateNodes) {
324         RefPtr<StyleSheet> sheet;
325         if (is<ProcessingInstruction>(*node)) {
326             if (!downcast<ProcessingInstruction>(*node).isCSS())
327                 continue;
328             // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
329             sheet = downcast<ProcessingInstruction>(*node).sheet();
330         } else if (is<HTMLLinkElement>(*node) || is<HTMLStyleElement>(*node) || is<SVGStyleElement>(*node)) {
331             Element& element = downcast<Element>(*node);
332             AtomicString title = element.isInShadowTree() ? nullAtom() : element.attributeWithoutSynchronization(titleAttr);
333             bool enabledViaScript = false;
334             if (is<HTMLLinkElement>(element)) {
335                 // <LINK> element
336                 HTMLLinkElement& linkElement = downcast<HTMLLinkElement>(element);
337                 if (linkElement.isDisabled())
338                     continue;
339                 enabledViaScript = linkElement.isEnabledViaScript();
340                 if (linkElement.styleSheetIsLoading()) {
341                     // it is loading but we should still decide which style sheet set to use
342                     if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
343                         if (!linkElement.attributeWithoutSynchronization(relAttr).contains("alternate"))
344                             m_preferredStylesheetSetName = title;
345                     }
346                     continue;
347                 }
348                 if (!linkElement.sheet())
349                     title = nullAtom();
350             }
351             // Get the current preferred styleset. This is the
352             // set of sheets that will be enabled.
353             if (is<SVGStyleElement>(element))
354                 sheet = downcast<SVGStyleElement>(element).sheet();
355             else if (is<HTMLLinkElement>(element))
356                 sheet = downcast<HTMLLinkElement>(element).sheet();
357             else
358                 sheet = downcast<HTMLStyleElement>(element).sheet();
359             // Check to see if this sheet belongs to a styleset
360             // (thus making it PREFERRED or ALTERNATE rather than
361             // PERSISTENT).
362             auto& rel = element.attributeWithoutSynchronization(relAttr);
363             if (!enabledViaScript && !title.isEmpty()) {
364                 // Yes, we have a title.
365                 if (m_preferredStylesheetSetName.isEmpty()) {
366                     // No preferred set has been established. If
367                     // we are NOT an alternate sheet, then establish
368                     // us as the preferred set. Otherwise, just ignore
369                     // this sheet.
370                     if (is<HTMLStyleElement>(element) || !rel.contains("alternate"))
371                         m_preferredStylesheetSetName = title;
372                 }
373                 if (title != m_preferredStylesheetSetName)
374                     sheet = nullptr;
375             }
376
377             if (rel.contains("alternate") && title.isEmpty())
378                 sheet = nullptr;
379         }
380         if (sheet)
381             sheets.append(WTFMove(sheet));
382     }
383 }
384
385 Scope::StyleResolverUpdateType Scope::analyzeStyleSheetChange(const Vector<RefPtr<CSSStyleSheet>>& newStylesheets, bool& requiresFullStyleRecalc)
386 {
387     requiresFullStyleRecalc = true;
388     
389     unsigned newStylesheetCount = newStylesheets.size();
390
391     if (!resolverIfExists())
392         return Reconstruct;
393
394     auto& styleResolver = *resolverIfExists();
395
396     // Find out which stylesheets are new.
397     unsigned oldStylesheetCount = m_activeStyleSheets.size();
398     if (newStylesheetCount < oldStylesheetCount)
399         return Reconstruct;
400
401     Vector<StyleSheetContents*> addedSheets;
402     unsigned newIndex = 0;
403     for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) {
404         if (newIndex >= newStylesheetCount)
405             return Reconstruct;
406         while (m_activeStyleSheets[oldIndex] != newStylesheets[newIndex]) {
407             addedSheets.append(&newStylesheets[newIndex]->contents());
408             ++newIndex;
409             if (newIndex == newStylesheetCount)
410                 return Reconstruct;
411         }
412         ++newIndex;
413     }
414     bool hasInsertions = !addedSheets.isEmpty();
415     while (newIndex < newStylesheetCount) {
416         addedSheets.append(&newStylesheets[newIndex]->contents());
417         ++newIndex;
418     }
419     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
420     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
421     auto styleResolverUpdateType = hasInsertions ? Reset : Additive;
422
423     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
424     if (!m_document.bodyOrFrameset() || m_document.hasNodesWithNonFinalStyle() || m_document.hasNodesWithMissingStyle())
425         return styleResolverUpdateType;
426
427     Invalidator invalidator(addedSheets, styleResolver.mediaQueryEvaluator());
428     if (invalidator.dirtiesAllStyle())
429         return styleResolverUpdateType;
430
431     if (m_shadowRoot)
432         invalidator.invalidateStyle(*m_shadowRoot);
433     else
434         invalidator.invalidateStyle(m_document);
435
436     requiresFullStyleRecalc = false;
437
438     return styleResolverUpdateType;
439 }
440
441 static void filterEnabledNonemptyCSSStyleSheets(Vector<RefPtr<CSSStyleSheet>>& result, const Vector<RefPtr<StyleSheet>>& sheets)
442 {
443     for (auto& sheet : sheets) {
444         if (!is<CSSStyleSheet>(*sheet))
445             continue;
446         CSSStyleSheet& styleSheet = downcast<CSSStyleSheet>(*sheet);
447         if (styleSheet.isLoading())
448             continue;
449         if (styleSheet.disabled())
450             continue;
451         if (!styleSheet.length())
452             continue;
453         result.append(&styleSheet);
454     }
455 }
456
457 static void invalidateHostAndSlottedStyleIfNeeded(ShadowRoot& shadowRoot, StyleResolver& resolver)
458 {
459     auto& host = *shadowRoot.host();
460     if (!resolver.ruleSets().authorStyle().hostPseudoClassRules().isEmpty())
461         host.invalidateStyle();
462
463     if (!resolver.ruleSets().authorStyle().slottedPseudoElementRules().isEmpty()) {
464         for (auto& shadowChild : childrenOfType<Element>(host))
465             shadowChild.invalidateStyle();
466     }
467 }
468
469 void Scope::updateActiveStyleSheets(UpdateType updateType)
470 {
471     ASSERT(!m_pendingUpdate);
472
473     if (!m_document.hasLivingRenderTree())
474         return;
475
476     if (m_document.inStyleRecalc() || m_document.inRenderTreeUpdate()) {
477         // Protect against deleting style resolver in the middle of a style resolution.
478         // Crash stacks indicate we can get here when a resource load fails synchronously (for example due to content blocking).
479         // FIXME: These kind of cases should be eliminated and this path replaced by an assert.
480         m_pendingUpdate = UpdateType::ContentsOrInterpretation;
481         m_document.scheduleFullStyleRebuild();
482         return;
483     }
484
485     Vector<RefPtr<StyleSheet>> activeStyleSheets;
486     collectActiveStyleSheets(activeStyleSheets);
487
488     Vector<RefPtr<CSSStyleSheet>> activeCSSStyleSheets;
489     activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets());
490     activeCSSStyleSheets.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting());
491     filterEnabledNonemptyCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets);
492
493     bool requiresFullStyleRecalc = true;
494     StyleResolverUpdateType styleResolverUpdateType = Reconstruct;
495     if (updateType == UpdateType::ActiveSet)
496         styleResolverUpdateType = analyzeStyleSheetChange(activeCSSStyleSheets, requiresFullStyleRecalc);
497
498     updateStyleResolver(activeCSSStyleSheets, styleResolverUpdateType);
499
500     m_weakCopyOfActiveStyleSheetListForFastLookup = nullptr;
501     m_activeStyleSheets.swap(activeCSSStyleSheets);
502     m_styleSheetsForStyleSheetList.swap(activeStyleSheets);
503
504     InspectorInstrumentation::activeStyleSheetsUpdated(m_document);
505
506     for (const auto& sheet : m_activeStyleSheets) {
507         if (sheet->contents().usesStyleBasedEditability())
508             m_usesStyleBasedEditability = true;
509     }
510
511     // FIXME: Move this code somewhere else.
512     if (requiresFullStyleRecalc) {
513         if (m_shadowRoot) {
514             for (auto& shadowChild : childrenOfType<Element>(*m_shadowRoot))
515                 shadowChild.invalidateStyleForSubtree();
516             invalidateHostAndSlottedStyleIfNeeded(*m_shadowRoot, resolver());
517         } else
518             m_document.scheduleFullStyleRebuild();
519     }
520 }
521
522 void Scope::updateStyleResolver(Vector<RefPtr<CSSStyleSheet>>& activeStyleSheets, StyleResolverUpdateType updateType)
523 {
524     if (updateType == Reconstruct) {
525         clearResolver();
526         return;
527     }
528     auto& styleResolver = resolver();
529
530     SetForScope<bool> isUpdatingStyleResolver { m_isUpdatingStyleResolver, true };
531     if (updateType == Reset) {
532         styleResolver.ruleSets().resetAuthorStyle();
533         styleResolver.appendAuthorStyleSheets(activeStyleSheets);
534     } else {
535         ASSERT(updateType == Additive);
536         unsigned firstNewIndex = m_activeStyleSheets.size();
537         Vector<RefPtr<CSSStyleSheet>> newStyleSheets;
538         newStyleSheets.appendRange(activeStyleSheets.begin() + firstNewIndex, activeStyleSheets.end());
539         styleResolver.appendAuthorStyleSheets(newStyleSheets);
540     }
541 }
542
543 const Vector<RefPtr<CSSStyleSheet>> Scope::activeStyleSheetsForInspector()
544 {
545     Vector<RefPtr<CSSStyleSheet>> result;
546
547     result.appendVector(m_document.extensionStyleSheets().injectedAuthorStyleSheets());
548     result.appendVector(m_document.extensionStyleSheets().authorStyleSheetsForTesting());
549
550     for (auto& styleSheet : m_styleSheetsForStyleSheetList) {
551         if (!is<CSSStyleSheet>(*styleSheet))
552             continue;
553
554         CSSStyleSheet& sheet = downcast<CSSStyleSheet>(*styleSheet);
555         if (sheet.disabled())
556             continue;
557
558         result.append(&sheet);
559     }
560
561     return result;
562 }
563
564 bool Scope::activeStyleSheetsContains(const CSSStyleSheet* sheet) const
565 {
566     if (!m_weakCopyOfActiveStyleSheetListForFastLookup) {
567         m_weakCopyOfActiveStyleSheetListForFastLookup = std::make_unique<HashSet<const CSSStyleSheet*>>();
568         for (auto& activeStyleSheet : m_activeStyleSheets)
569             m_weakCopyOfActiveStyleSheetListForFastLookup->add(activeStyleSheet.get());
570     }
571     return m_weakCopyOfActiveStyleSheetListForFastLookup->contains(sheet);
572 }
573
574 void Scope::flushPendingSelfUpdate()
575 {
576     ASSERT(m_pendingUpdate);
577
578     auto updateType = *m_pendingUpdate;
579
580     clearPendingUpdate();
581     updateActiveStyleSheets(updateType);
582 }
583
584 void Scope::flushPendingDescendantUpdates()
585 {
586     ASSERT(m_hasDescendantWithPendingUpdate);
587     ASSERT(!m_shadowRoot);
588     for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots())
589         descendantShadowRoot->styleScope().flushPendingUpdate();
590     m_hasDescendantWithPendingUpdate = false;
591 }
592
593 void Scope::clearPendingUpdate()
594 {
595     m_pendingUpdateTimer.stop();
596     m_pendingUpdate = { };
597 }
598
599 void Scope::scheduleUpdate(UpdateType update)
600 {
601     if (update == UpdateType::ContentsOrInterpretation) {
602         // :host and ::slotted rules might go away.
603         if (m_shadowRoot && m_resolver)
604             invalidateHostAndSlottedStyleIfNeeded(*m_shadowRoot, *m_resolver);
605         // FIXME: The m_isUpdatingStyleResolver test is here because extension stylesheets can get us here from StyleResolver::appendAuthorStyleSheets.
606         if (!m_isUpdatingStyleResolver)
607             clearResolver();
608     }
609
610     if (!m_pendingUpdate || *m_pendingUpdate < update) {
611         m_pendingUpdate = update;
612         if (m_shadowRoot)
613             m_document.styleScope().m_hasDescendantWithPendingUpdate = true;
614     }
615
616     if (m_pendingUpdateTimer.isActive())
617         return;
618     m_pendingUpdateTimer.startOneShot(0_s);
619 }
620
621 void Scope::evaluateMediaQueriesForViewportChange()
622 {
623     evaluateMediaQueries([] (StyleResolver& resolver) {
624         return resolver.hasMediaQueriesAffectedByViewportChange();
625     });
626 }
627
628 void Scope::evaluateMediaQueriesForAccessibilitySettingsChange()
629 {
630     evaluateMediaQueries([] (StyleResolver& resolver) {
631         return resolver.hasMediaQueriesAffectedByAccessibilitySettingsChange();
632     });
633 }
634
635 void Scope::evaluateMediaQueriesForAppearanceChange()
636 {
637     evaluateMediaQueries([] (StyleResolver& resolver) {
638         return resolver.hasMediaQueriesAffectedByAppearanceChange();
639     });
640 }
641
642 template <typename TestFunction>
643 void Scope::evaluateMediaQueries(TestFunction&& testFunction)
644 {
645     if (!m_shadowRoot) {
646         for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots())
647             descendantShadowRoot->styleScope().evaluateMediaQueries(testFunction);
648     }
649     auto* resolver = resolverIfExists();
650     if (!resolver)
651         return;
652     if (!testFunction(*resolver))
653         return;
654     scheduleUpdate(UpdateType::ContentsOrInterpretation);
655     InspectorInstrumentation::mediaQueryResultChanged(m_document);
656 }
657
658 void Scope::didChangeActiveStyleSheetCandidates()
659 {
660     scheduleUpdate(UpdateType::ActiveSet);
661 }
662
663 void Scope::didChangeStyleSheetContents()
664 {
665     scheduleUpdate(UpdateType::ContentsOrInterpretation);
666 }
667
668 void Scope::didChangeStyleSheetEnvironment()
669 {
670     if (!m_shadowRoot) {
671         for (auto* descendantShadowRoot : m_document.inDocumentShadowRoots()) {
672             // Stylesheets is author shadow roots are potentially affected.
673             if (descendantShadowRoot->mode() != ShadowRootMode::UserAgent)
674                 descendantShadowRoot->styleScope().scheduleUpdate(UpdateType::ContentsOrInterpretation);
675         }
676     }
677     scheduleUpdate(UpdateType::ContentsOrInterpretation);
678 }
679
680 void Scope::pendingUpdateTimerFired()
681 {
682     flushPendingUpdate();
683 }
684
685 const Vector<RefPtr<StyleSheet>>& Scope::styleSheetsForStyleSheetList()
686 {
687     // FIXME: StyleSheetList content should be updated separately from style resolver updates.
688     flushPendingUpdate();
689     return m_styleSheetsForStyleSheetList;
690 }
691
692 }
693 }