1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 | * (C) 2001 Peter Kelly (pmk@post.com) |
5 | * (C) 2001 Dirk Mueller (mueller@kde.org) |
6 | * (C) 2007 David Smith (catfish.man@gmail.com) |
7 | * Copyright (C) 2004-2010, 2012-2016 Apple Inc. All rights reserved. |
8 | * (C) 2007 Eric Seidel (eric@webkit.org) |
9 | * |
10 | * This library is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU Library General Public |
12 | * License as published by the Free Software Foundation; either |
13 | * version 2 of the License, or (at your option) any later version. |
14 | * |
15 | * This library is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | * Library General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU Library General Public License |
21 | * along with this library; see the file COPYING.LIB. If not, write to |
22 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
23 | * Boston, MA 02110-1301, USA. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "StyleTreeResolver.h" |
28 | |
29 | #include "CSSAnimationController.h" |
30 | #include "CSSFontSelector.h" |
31 | #include "ComposedTreeAncestorIterator.h" |
32 | #include "ComposedTreeIterator.h" |
33 | #include "DocumentTimeline.h" |
34 | #include "ElementIterator.h" |
35 | #include "Frame.h" |
36 | #include "HTMLBodyElement.h" |
37 | #include "HTMLMeterElement.h" |
38 | #include "HTMLNames.h" |
39 | #include "HTMLProgressElement.h" |
40 | #include "HTMLSlotElement.h" |
41 | #include "LoaderStrategy.h" |
42 | #include "NodeRenderStyle.h" |
43 | #include "Page.h" |
44 | #include "PlatformStrategies.h" |
45 | #include "Quirks.h" |
46 | #include "RenderElement.h" |
47 | #include "RenderStyle.h" |
48 | #include "RenderView.h" |
49 | #include "RuntimeEnabledFeatures.h" |
50 | #include "Settings.h" |
51 | #include "ShadowRoot.h" |
52 | #include "StyleFontSizeFunctions.h" |
53 | #include "StyleResolver.h" |
54 | #include "StyleScope.h" |
55 | #include "Text.h" |
56 | |
57 | namespace WebCore { |
58 | |
59 | namespace Style { |
60 | |
61 | TreeResolver::TreeResolver(Document& document) |
62 | : m_document(document) |
63 | { |
64 | } |
65 | |
66 | TreeResolver::~TreeResolver() = default; |
67 | |
68 | TreeResolver::Scope::Scope(Document& document) |
69 | : styleResolver(document.styleScope().resolver()) |
70 | , sharingResolver(document, styleResolver.ruleSets(), selectorFilter) |
71 | { |
72 | document.setIsResolvingTreeStyle(true); |
73 | } |
74 | |
75 | TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope) |
76 | : styleResolver(shadowRoot.styleScope().resolver()) |
77 | , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter) |
78 | , shadowRoot(&shadowRoot) |
79 | , enclosingScope(&enclosingScope) |
80 | { |
81 | styleResolver.setOverrideDocumentElementStyle(enclosingScope.styleResolver.overrideDocumentElementStyle()); |
82 | } |
83 | |
84 | TreeResolver::Scope::~Scope() |
85 | { |
86 | if (!shadowRoot) |
87 | styleResolver.document().setIsResolvingTreeStyle(false); |
88 | |
89 | styleResolver.setOverrideDocumentElementStyle(nullptr); |
90 | } |
91 | |
92 | TreeResolver::Parent::Parent(Document& document) |
93 | : element(nullptr) |
94 | , style(*document.renderStyle()) |
95 | { |
96 | } |
97 | |
98 | TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve) |
99 | : element(&element) |
100 | , style(style) |
101 | , change(change) |
102 | , descendantsToResolve(descendantsToResolve) |
103 | { |
104 | } |
105 | |
106 | void TreeResolver::pushScope(ShadowRoot& shadowRoot) |
107 | { |
108 | m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope()))); |
109 | } |
110 | |
111 | void TreeResolver::pushEnclosingScope() |
112 | { |
113 | ASSERT(scope().enclosingScope); |
114 | m_scopeStack.append(*scope().enclosingScope); |
115 | } |
116 | |
117 | void TreeResolver::popScope() |
118 | { |
119 | return m_scopeStack.removeLast(); |
120 | } |
121 | |
122 | std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle) |
123 | { |
124 | if (element.hasCustomStyleResolveCallbacks()) { |
125 | RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr; |
126 | if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) { |
127 | if (customStyle->relations) |
128 | commitRelations(WTFMove(customStyle->relations), *m_update); |
129 | |
130 | return WTFMove(customStyle->renderStyle); |
131 | } |
132 | } |
133 | |
134 | if (auto style = scope().sharingResolver.resolve(element, *m_update)) |
135 | return style; |
136 | |
137 | auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, parentBoxStyle(), RuleMatchingBehavior::MatchAllRules, &scope().selectorFilter); |
138 | |
139 | if (elementStyle.relations) |
140 | commitRelations(WTFMove(elementStyle.relations), *m_update); |
141 | |
142 | return WTFMove(elementStyle.renderStyle); |
143 | } |
144 | |
145 | static void resetStyleForNonRenderedDescendants(Element& current) |
146 | { |
147 | for (auto& child : childrenOfType<Element>(current)) { |
148 | if (child.needsStyleRecalc()) { |
149 | child.resetComputedStyle(); |
150 | child.resetStyleRelations(); |
151 | child.setHasValidStyle(); |
152 | } |
153 | |
154 | if (child.childNeedsStyleRecalc()) |
155 | resetStyleForNonRenderedDescendants(child); |
156 | } |
157 | current.clearChildNeedsStyleRecalc(); |
158 | } |
159 | |
160 | static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle) |
161 | { |
162 | if (newStyle.display() != DisplayType::None) |
163 | return true; |
164 | if (element.renderOrDisplayContentsStyle()) |
165 | return true; |
166 | if (element.rendererIsNeeded(newStyle)) |
167 | return true; |
168 | return false; |
169 | } |
170 | |
171 | static DescendantsToResolve computeDescendantsToResolve(Change change, Validity validity, DescendantsToResolve parentDescendantsToResolve) |
172 | { |
173 | if (parentDescendantsToResolve == DescendantsToResolve::All) |
174 | return DescendantsToResolve::All; |
175 | if (validity >= Validity::SubtreeInvalid) |
176 | return DescendantsToResolve::All; |
177 | switch (change) { |
178 | case NoChange: |
179 | return DescendantsToResolve::None; |
180 | case NoInherit: |
181 | return DescendantsToResolve::ChildrenWithExplicitInherit; |
182 | case Inherit: |
183 | return DescendantsToResolve::Children; |
184 | case Detach: |
185 | return DescendantsToResolve::All; |
186 | }; |
187 | ASSERT_NOT_REACHED(); |
188 | return DescendantsToResolve::None; |
189 | }; |
190 | |
191 | ElementUpdates TreeResolver::resolveElement(Element& element) |
192 | { |
193 | if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) { |
194 | m_document.setHasNodesWithMissingStyle(); |
195 | return { }; |
196 | } |
197 | |
198 | auto newStyle = styleForElement(element, parent().style); |
199 | |
200 | if (!affectsRenderedSubtree(element, *newStyle)) |
201 | return { }; |
202 | |
203 | auto* existingStyle = element.renderOrDisplayContentsStyle(); |
204 | |
205 | if (m_didSeePendingStylesheet && (!existingStyle || existingStyle->isNotFinal())) { |
206 | newStyle->setIsNotFinal(); |
207 | m_document.setHasNodesWithNonFinalStyle(); |
208 | } |
209 | |
210 | auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change); |
211 | auto descendantsToResolve = computeDescendantsToResolve(update.change, element.styleValidity(), parent().descendantsToResolve); |
212 | |
213 | if (&element == m_document.documentElement()) { |
214 | m_documentElementStyle = RenderStyle::clonePtr(*update.style); |
215 | scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get()); |
216 | |
217 | if (update.change != NoChange && existingStyle && existingStyle->computedFontPixelSize() != update.style->computedFontPixelSize()) { |
218 | // "rem" units are relative to the document element's font size so we need to recompute everything. |
219 | // In practice this is rare. |
220 | scope().styleResolver.invalidateMatchedPropertiesCache(); |
221 | descendantsToResolve = DescendantsToResolve::All; |
222 | } |
223 | } |
224 | |
225 | // This is needed for resolving color:-webkit-text for subsequent elements. |
226 | // FIXME: We shouldn't mutate document when resolving style. |
227 | if (&element == m_document.body()) |
228 | m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor)); |
229 | |
230 | // FIXME: These elements should not change renderer based on appearance property. |
231 | if (element.hasTagName(HTMLNames::meterTag) || is<HTMLProgressElement>(element)) { |
232 | if (existingStyle && update.style->appearance() != existingStyle->appearance()) { |
233 | update.change = Detach; |
234 | descendantsToResolve = DescendantsToResolve::All; |
235 | } |
236 | } |
237 | |
238 | auto beforeUpdate = resolvePseudoStyle(element, update, PseudoId::Before); |
239 | auto afterUpdate = resolvePseudoStyle(element, update, PseudoId::After); |
240 | |
241 | #if ENABLE(POINTER_EVENTS) && PLATFORM(IOS_FAMILY) |
242 | if (!m_document.quirks().shouldDisablePointerEventsQuirk() && RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) |
243 | m_document.updateTouchActionElements(element, *update.style.get()); |
244 | #endif |
245 | |
246 | return { WTFMove(update), descendantsToResolve, WTFMove(beforeUpdate), WTFMove(afterUpdate) }; |
247 | } |
248 | |
249 | ElementUpdate TreeResolver::resolvePseudoStyle(Element& element, const ElementUpdate& elementUpdate, PseudoId pseudoId) |
250 | { |
251 | if (elementUpdate.style->display() == DisplayType::None) |
252 | return { }; |
253 | if (!elementUpdate.style->hasPseudoStyle(pseudoId)) |
254 | return { }; |
255 | |
256 | auto pseudoStyle = scope().styleResolver.pseudoStyleForElement(element, { pseudoId }, *elementUpdate.style, &scope().selectorFilter); |
257 | if (!pseudoElementRendererIsNeeded(pseudoStyle.get())) |
258 | return { }; |
259 | |
260 | PseudoElement* pseudoElement = pseudoId == PseudoId::Before ? element.beforePseudoElement() : element.afterPseudoElement(); |
261 | if (!pseudoElement) { |
262 | auto newPseudoElement = PseudoElement::create(element, pseudoId); |
263 | pseudoElement = newPseudoElement.ptr(); |
264 | if (pseudoId == PseudoId::Before) |
265 | element.setBeforePseudoElement(WTFMove(newPseudoElement)); |
266 | else |
267 | element.setAfterPseudoElement(WTFMove(newPseudoElement)); |
268 | } |
269 | |
270 | return createAnimatedElementUpdate(WTFMove(pseudoStyle), *pseudoElement, elementUpdate.change); |
271 | } |
272 | |
273 | const RenderStyle* TreeResolver::parentBoxStyle() const |
274 | { |
275 | // 'display: contents' doesn't generate boxes. |
276 | for (unsigned i = m_parentStack.size(); i; --i) { |
277 | auto& parent = m_parentStack[i - 1]; |
278 | if (parent.style.display() == DisplayType::None) |
279 | return nullptr; |
280 | if (parent.style.display() != DisplayType::Contents) |
281 | return &parent.style; |
282 | } |
283 | ASSERT_NOT_REACHED(); |
284 | return nullptr; |
285 | } |
286 | |
287 | ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange) |
288 | { |
289 | auto* oldStyle = element.renderOrDisplayContentsStyle(); |
290 | |
291 | bool shouldRecompositeLayer = false; |
292 | |
293 | // New code path for CSS Animations and CSS Transitions. |
294 | if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) { |
295 | // First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation |
296 | // on the document timeline. Note that we get timeline() on the Document here because we need a timeline created |
297 | // in case no Web Animations have been created through the JS API. |
298 | if (element.document().pageCacheState() == Document::NotInPageCache && !element.document().renderView()->printing()) { |
299 | if (oldStyle && (oldStyle->hasTransitions() || newStyle->hasTransitions())) |
300 | m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle); |
301 | |
302 | if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations()) |
303 | m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle); |
304 | } |
305 | } |
306 | |
307 | if (auto timeline = m_document.existingTimeline()) { |
308 | // Now we can update all Web animations, which will include CSS Animations as well |
309 | // as animations created via the JS API. |
310 | auto animatedStyle = RenderStyle::clonePtr(*newStyle); |
311 | shouldRecompositeLayer = timeline->resolveAnimationsForElement(element, *animatedStyle); |
312 | newStyle = WTFMove(animatedStyle); |
313 | } |
314 | |
315 | // Old code path for CSS Animations and CSS Transitions. |
316 | if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) { |
317 | auto& animationController = m_document.frame()->animation(); |
318 | |
319 | auto animationUpdate = animationController.updateAnimations(element, *newStyle, oldStyle); |
320 | shouldRecompositeLayer = animationUpdate.animationChangeRequiresRecomposite; |
321 | |
322 | if (animationUpdate.style) |
323 | newStyle = WTFMove(animationUpdate.style); |
324 | } |
325 | |
326 | auto change = oldStyle ? determineChange(*oldStyle, *newStyle) : Detach; |
327 | |
328 | auto validity = element.styleValidity(); |
329 | if (validity >= Validity::SubtreeAndRenderersInvalid || parentChange == Detach) |
330 | change = Detach; |
331 | |
332 | shouldRecompositeLayer |= element.styleResolutionShouldRecompositeLayer(); |
333 | |
334 | return { WTFMove(newStyle), change, shouldRecompositeLayer }; |
335 | } |
336 | |
337 | void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve) |
338 | { |
339 | scope().selectorFilter.pushParent(&element); |
340 | |
341 | Parent parent(element, style, change, descendantsToResolve); |
342 | |
343 | if (auto* shadowRoot = element.shadowRoot()) { |
344 | pushScope(*shadowRoot); |
345 | parent.didPushScope = true; |
346 | } |
347 | else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) { |
348 | pushEnclosingScope(); |
349 | parent.didPushScope = true; |
350 | } |
351 | |
352 | m_parentStack.append(WTFMove(parent)); |
353 | } |
354 | |
355 | void TreeResolver::popParent() |
356 | { |
357 | auto& parentElement = *parent().element; |
358 | |
359 | parentElement.setHasValidStyle(); |
360 | parentElement.clearChildNeedsStyleRecalc(); |
361 | |
362 | if (parent().didPushScope) |
363 | popScope(); |
364 | |
365 | scope().selectorFilter.popParent(); |
366 | |
367 | m_parentStack.removeLast(); |
368 | } |
369 | |
370 | void TreeResolver::popParentsToDepth(unsigned depth) |
371 | { |
372 | ASSERT(depth); |
373 | ASSERT(m_parentStack.size() >= depth); |
374 | |
375 | while (m_parentStack.size() > depth) |
376 | popParent(); |
377 | } |
378 | |
379 | static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement) |
380 | { |
381 | if (!pseudoElement) |
382 | return false; |
383 | return pseudoElement->needsStyleRecalc(); |
384 | } |
385 | |
386 | static bool shouldResolveElement(const Element& element, DescendantsToResolve parentDescendantsToResolve) |
387 | { |
388 | if (element.styleValidity() != Validity::Valid) |
389 | return true; |
390 | if (shouldResolvePseudoElement(element.beforePseudoElement())) |
391 | return true; |
392 | if (shouldResolvePseudoElement(element.afterPseudoElement())) |
393 | return true; |
394 | |
395 | switch (parentDescendantsToResolve) { |
396 | case DescendantsToResolve::None: |
397 | return false; |
398 | case DescendantsToResolve::Children: |
399 | case DescendantsToResolve::All: |
400 | return true; |
401 | case DescendantsToResolve::ChildrenWithExplicitInherit: |
402 | auto* existingStyle = element.renderOrDisplayContentsStyle(); |
403 | return existingStyle && existingStyle->hasExplicitlyInheritedProperties(); |
404 | }; |
405 | ASSERT_NOT_REACHED(); |
406 | return false; |
407 | } |
408 | |
409 | static void clearNeedsStyleResolution(Element& element) |
410 | { |
411 | element.setHasValidStyle(); |
412 | if (auto* before = element.beforePseudoElement()) |
413 | before->setHasValidStyle(); |
414 | if (auto* after = element.afterPseudoElement()) |
415 | after->setHasValidStyle(); |
416 | } |
417 | |
418 | static bool hasLoadingStylesheet(const Style::Scope& styleScope, const Element& element, bool checkDescendants) |
419 | { |
420 | if (!styleScope.hasPendingSheetsInBody()) |
421 | return false; |
422 | if (styleScope.hasPendingSheetInBody(element)) |
423 | return true; |
424 | if (!checkDescendants) |
425 | return false; |
426 | for (auto& descendant : descendantsOfType<Element>(element)) { |
427 | if (styleScope.hasPendingSheetInBody(descendant)) |
428 | return true; |
429 | }; |
430 | return false; |
431 | } |
432 | |
433 | static std::unique_ptr<RenderStyle> createInheritedDisplayContentsStyleIfNeeded(const RenderStyle& parentElementStyle, const RenderStyle* parentBoxStyle) |
434 | { |
435 | if (parentElementStyle.display() != DisplayType::Contents) |
436 | return nullptr; |
437 | if (parentBoxStyle && !parentBoxStyle->inheritedNotEqual(&parentElementStyle)) |
438 | return nullptr; |
439 | // Compute style for imaginary unstyled <span> around the text node. |
440 | auto style = RenderStyle::createPtr(); |
441 | style->inheritFrom(parentElementStyle); |
442 | return style; |
443 | } |
444 | |
445 | void TreeResolver::resolveComposedTree() |
446 | { |
447 | ASSERT(m_parentStack.size() == 1); |
448 | ASSERT(m_scopeStack.size() == 1); |
449 | |
450 | auto descendants = composedTreeDescendants(m_document); |
451 | auto it = descendants.begin(); |
452 | auto end = descendants.end(); |
453 | |
454 | while (it != end) { |
455 | popParentsToDepth(it.depth()); |
456 | |
457 | auto& node = *it; |
458 | auto& parent = this->parent(); |
459 | |
460 | ASSERT(node.isConnected()); |
461 | ASSERT(node.containingShadowRoot() == scope().shadowRoot); |
462 | ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot()); |
463 | |
464 | if (is<Text>(node)) { |
465 | auto& text = downcast<Text>(node); |
466 | |
467 | if ((text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach) || parent.style.display() == DisplayType::Contents) { |
468 | TextUpdate textUpdate; |
469 | textUpdate.inheritedDisplayContentsStyle = createInheritedDisplayContentsStyleIfNeeded(parent.style, parentBoxStyle()); |
470 | |
471 | m_update->addText(text, parent.element, WTFMove(textUpdate)); |
472 | } |
473 | |
474 | text.setHasValidStyle(); |
475 | it.traverseNextSkippingChildren(); |
476 | continue; |
477 | } |
478 | |
479 | auto& element = downcast<Element>(node); |
480 | |
481 | if (it.depth() > Settings::defaultMaximumRenderTreeDepth) { |
482 | resetStyleForNonRenderedDescendants(element); |
483 | it.traverseNextSkippingChildren(); |
484 | continue; |
485 | } |
486 | |
487 | auto* style = element.renderOrDisplayContentsStyle(); |
488 | auto change = NoChange; |
489 | auto descendantsToResolve = DescendantsToResolve::None; |
490 | |
491 | bool shouldResolve = shouldResolveElement(element, parent.descendantsToResolve); |
492 | if (shouldResolve) { |
493 | if (!element.hasDisplayContents()) |
494 | element.resetComputedStyle(); |
495 | element.resetStyleRelations(); |
496 | |
497 | if (element.hasCustomStyleResolveCallbacks()) |
498 | element.willRecalcStyle(parent.change); |
499 | |
500 | auto elementUpdates = resolveElement(element); |
501 | |
502 | if (element.hasCustomStyleResolveCallbacks()) |
503 | element.didRecalcStyle(elementUpdates.update.change); |
504 | |
505 | style = elementUpdates.update.style.get(); |
506 | change = elementUpdates.update.change; |
507 | descendantsToResolve = elementUpdates.descendantsToResolve; |
508 | |
509 | if (elementUpdates.update.style) |
510 | m_update->addElement(element, parent.element, WTFMove(elementUpdates)); |
511 | |
512 | clearNeedsStyleResolution(element); |
513 | } |
514 | |
515 | if (!style) |
516 | resetStyleForNonRenderedDescendants(element); |
517 | |
518 | bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || descendantsToResolve != DescendantsToResolve::None); |
519 | |
520 | if (!m_didSeePendingStylesheet) |
521 | m_didSeePendingStylesheet = hasLoadingStylesheet(m_document.styleScope(), element, !shouldIterateChildren); |
522 | |
523 | if (!shouldIterateChildren) { |
524 | it.traverseNextSkippingChildren(); |
525 | continue; |
526 | } |
527 | |
528 | pushParent(element, *style, change, descendantsToResolve); |
529 | |
530 | it.traverseNext(); |
531 | } |
532 | |
533 | popParentsToDepth(1); |
534 | } |
535 | |
536 | std::unique_ptr<Update> TreeResolver::resolve() |
537 | { |
538 | auto& renderView = *m_document.renderView(); |
539 | |
540 | Element* documentElement = m_document.documentElement(); |
541 | if (!documentElement) { |
542 | m_document.styleScope().resolver(); |
543 | return nullptr; |
544 | } |
545 | if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc()) |
546 | return nullptr; |
547 | |
548 | m_didSeePendingStylesheet = m_document.styleScope().hasPendingSheetsBeforeBody(); |
549 | |
550 | m_update = std::make_unique<Update>(m_document); |
551 | m_scopeStack.append(adoptRef(*new Scope(m_document))); |
552 | m_parentStack.append(Parent(m_document)); |
553 | |
554 | // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc. |
555 | renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules()); |
556 | renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules()); |
557 | |
558 | resolveComposedTree(); |
559 | |
560 | renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules()); |
561 | renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules()); |
562 | |
563 | ASSERT(m_scopeStack.size() == 1); |
564 | ASSERT(m_parentStack.size() == 1); |
565 | m_parentStack.clear(); |
566 | popScope(); |
567 | |
568 | if (m_update->roots().isEmpty()) |
569 | return { }; |
570 | |
571 | return WTFMove(m_update); |
572 | } |
573 | |
574 | static Vector<Function<void ()>>& postResolutionCallbackQueue() |
575 | { |
576 | static NeverDestroyed<Vector<Function<void ()>>> vector; |
577 | return vector; |
578 | } |
579 | |
580 | static Vector<RefPtr<Frame>>& memoryCacheClientCallsResumeQueue() |
581 | { |
582 | static NeverDestroyed<Vector<RefPtr<Frame>>> vector; |
583 | return vector; |
584 | } |
585 | |
586 | void queuePostResolutionCallback(Function<void ()>&& callback) |
587 | { |
588 | postResolutionCallbackQueue().append(WTFMove(callback)); |
589 | } |
590 | |
591 | static void suspendMemoryCacheClientCalls(Document& document) |
592 | { |
593 | Page* page = document.page(); |
594 | if (!page || !page->areMemoryCacheClientCallsEnabled()) |
595 | return; |
596 | |
597 | page->setMemoryCacheClientCallsEnabled(false); |
598 | |
599 | memoryCacheClientCallsResumeQueue().append(&page->mainFrame()); |
600 | } |
601 | |
602 | static unsigned resolutionNestingDepth; |
603 | |
604 | PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document, DrainCallbacks drainCallbacks) |
605 | : m_drainCallbacks(drainCallbacks) |
606 | { |
607 | ++resolutionNestingDepth; |
608 | |
609 | if (resolutionNestingDepth == 1) |
610 | platformStrategies()->loaderStrategy()->suspendPendingRequests(); |
611 | |
612 | // FIXME: It's strange to build this into the disabler. |
613 | suspendMemoryCacheClientCalls(document); |
614 | } |
615 | |
616 | PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler() |
617 | { |
618 | if (resolutionNestingDepth == 1) { |
619 | if (m_drainCallbacks == DrainCallbacks::Yes) { |
620 | // Get size each time through the loop because a callback can add more callbacks to the end of the queue. |
621 | auto& queue = postResolutionCallbackQueue(); |
622 | for (size_t i = 0; i < queue.size(); ++i) |
623 | queue[i](); |
624 | queue.clear(); |
625 | } |
626 | |
627 | auto& queue = memoryCacheClientCallsResumeQueue(); |
628 | for (size_t i = 0; i < queue.size(); ++i) { |
629 | if (auto* page = queue[i]->page()) |
630 | page->setMemoryCacheClientCallsEnabled(true); |
631 | } |
632 | queue.clear(); |
633 | |
634 | platformStrategies()->loaderStrategy()->resumePendingRequests(); |
635 | } |
636 | |
637 | --resolutionNestingDepth; |
638 | } |
639 | |
640 | bool postResolutionCallbacksAreSuspended() |
641 | { |
642 | return resolutionNestingDepth; |
643 | } |
644 | |
645 | } |
646 | } |
647 | |