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