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
57namespace WebCore {
58
59namespace Style {
60
61TreeResolver::TreeResolver(Document& document)
62 : m_document(document)
63{
64}
65
66TreeResolver::~TreeResolver() = default;
67
68TreeResolver::Scope::Scope(Document& document)
69 : styleResolver(document.styleScope().resolver())
70 , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
71{
72 document.setIsResolvingTreeStyle(true);
73}
74
75TreeResolver::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
84TreeResolver::Scope::~Scope()
85{
86 if (!shadowRoot)
87 styleResolver.document().setIsResolvingTreeStyle(false);
88
89 styleResolver.setOverrideDocumentElementStyle(nullptr);
90}
91
92TreeResolver::Parent::Parent(Document& document)
93 : element(nullptr)
94 , style(*document.renderStyle())
95{
96}
97
98TreeResolver::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
106void TreeResolver::pushScope(ShadowRoot& shadowRoot)
107{
108 m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
109}
110
111void TreeResolver::pushEnclosingScope()
112{
113 ASSERT(scope().enclosingScope);
114 m_scopeStack.append(*scope().enclosingScope);
115}
116
117void TreeResolver::popScope()
118{
119 return m_scopeStack.removeLast();
120}
121
122std::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
145static 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
160static 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
171static 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
191ElementUpdates 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
249ElementUpdate 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
273const 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
287ElementUpdate 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
337void 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
355void 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
370void 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
379static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement)
380{
381 if (!pseudoElement)
382 return false;
383 return pseudoElement->needsStyleRecalc();
384}
385
386static 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
409static 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
418static 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
433static 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
445void 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
536std::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
574static Vector<Function<void ()>>& postResolutionCallbackQueue()
575{
576 static NeverDestroyed<Vector<Function<void ()>>> vector;
577 return vector;
578}
579
580static Vector<RefPtr<Frame>>& memoryCacheClientCallsResumeQueue()
581{
582 static NeverDestroyed<Vector<RefPtr<Frame>>> vector;
583 return vector;
584}
585
586void queuePostResolutionCallback(Function<void ()>&& callback)
587{
588 postResolutionCallbackQueue().append(WTFMove(callback));
589}
590
591static 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
602static unsigned resolutionNestingDepth;
603
604PostResolutionCallbackDisabler::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
616PostResolutionCallbackDisabler::~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
640bool postResolutionCallbacksAreSuspended()
641{
642 return resolutionNestingDepth;
643}
644
645}
646}
647