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-2014 Apple Inc. All rights reserved.
8 * (C) 2007 Eric Seidel (eric@webkit.org)
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.
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.
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.
27 #include "StyleResolveTree.h"
29 #include "AXObjectCache.h"
30 #include "AnimationController.h"
31 #include "CSSFontSelector.h"
32 #include "ElementIterator.h"
33 #include "ElementRareData.h"
34 #include "FlowThreadController.h"
35 #include "InsertionPoint.h"
36 #include "LoaderStrategy.h"
37 #include "MainFrame.h"
38 #include "NodeRenderStyle.h"
39 #include "NodeRenderingTraversal.h"
40 #include "NodeTraversal.h"
41 #include "PlatformStrategies.h"
42 #include "RenderFullScreen.h"
43 #include "RenderNamedFlowThread.h"
44 #include "RenderText.h"
45 #include "RenderTreePosition.h"
46 #include "RenderWidget.h"
47 #include "ResourceLoadScheduler.h"
49 #include "ShadowRoot.h"
50 #include "StyleResolveForDocument.h"
51 #include "StyleResolver.h"
55 #include "CSSFontSelector.h"
56 #include "WKContentObservation.h"
63 enum DetachType { NormalDetach, ReattachDetach };
65 static void attachRenderTree(Element&, RenderStyle& inheritedStyle, RenderTreePosition&, PassRefPtr<RenderStyle>);
66 static void attachTextRenderer(Text&, RenderTreePosition&);
67 static void detachRenderTree(Element&, DetachType);
68 static void resolveTextNode(Text&, RenderTreePosition&);
69 static void resolveTree(Element&, RenderStyle& inheritedStyle, RenderTreePosition&, Change);
71 Change determineChange(const RenderStyle& s1, const RenderStyle& s2)
73 if (s1.display() != s2.display())
75 if (s1.hasPseudoStyle(FIRST_LETTER) != s2.hasPseudoStyle(FIRST_LETTER))
77 // We just detach if a renderer acquires or loses a column-span, since spanning elements
78 // typically won't contain much content.
79 if (s1.columnSpan() != s2.columnSpan())
81 if (!s1.contentDataEquivalent(&s2))
83 // When text-combine property has been changed, we need to prepare a separate renderer object.
84 // When text-combine is on, we use RenderCombineText, otherwise RenderText.
85 // https://bugs.webkit.org/show_bug.cgi?id=55069
86 if (s1.hasTextCombine() != s2.hasTextCombine())
88 // We need to reattach the node, so that it is moved to the correct RenderFlowThread.
89 if (s1.flowThread() != s2.flowThread())
91 // When the region thread has changed, we need to prepare a separate render region object.
92 if (s1.regionThread() != s2.regionThread())
94 // FIXME: Multicolumn regions not yet supported (http://dev.w3.org/csswg/css-regions/#multi-column-regions)
95 // When the node has region style and changed its multicol style, we have to prepare
96 // a separate render region object.
97 if (s1.hasFlowFrom() && (s1.specifiesColumns() != s2.specifiesColumns()))
99 if (s1.alignItems() != s2.alignItems())
103 if (s1.inheritedNotEqual(&s2))
105 if (s1.hasExplicitlyInheritedProperties() || s2.hasExplicitlyInheritedProperties())
110 // If the pseudoStyles have changed, we want any StyleChange that is not NoChange
111 // because setStyle will do the right thing with anything else.
112 if (s1.hasAnyPublicPseudoStyles()) {
113 for (PseudoId pseudoId = FIRST_PUBLIC_PSEUDOID; pseudoId < FIRST_INTERNAL_PSEUDOID; pseudoId = static_cast<PseudoId>(pseudoId + 1)) {
114 if (s1.hasPseudoStyle(pseudoId)) {
115 RenderStyle* ps2 = s2.getCachedPseudoStyle(pseudoId);
118 RenderStyle* ps1 = s1.getCachedPseudoStyle(pseudoId);
119 if (!ps1 || *ps1 != *ps2)
128 static bool shouldCreateRenderer(const Element& element, const RenderElement& parentRenderer)
130 if (!element.document().shouldCreateRenderers())
132 if (!parentRenderer.canHaveChildren() && !(element.isPseudoElement() && parentRenderer.canHaveGeneratedChildren()))
134 if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(element))
139 static Ref<RenderStyle> styleForElement(Element& element, RenderStyle& inheritedStyle)
141 if (element.hasCustomStyleResolveCallbacks()) {
142 if (RefPtr<RenderStyle> style = element.customStyleForRenderer(inheritedStyle))
143 return style.releaseNonNull();
145 return element.document().ensureStyleResolver().styleForElement(&element, &inheritedStyle);
148 #if ENABLE(CSS_REGIONS)
149 static RenderNamedFlowThread* moveToFlowThreadIfNeeded(Element& element, const RenderStyle& style)
151 if (!element.shouldMoveToFlowThread(style))
154 FlowThreadController& flowThreadController = element.document().renderView()->flowThreadController();
155 RenderNamedFlowThread& parentFlowRenderer = flowThreadController.ensureRenderFlowThreadWithName(style.flowThread());
156 flowThreadController.registerNamedFlowContentElement(element, parentFlowRenderer);
157 return &parentFlowRenderer;
161 static void createRendererIfNeeded(Element& element, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
163 ASSERT(!element.renderer());
165 RefPtr<RenderStyle> style = resolvedStyle;
167 if (!shouldCreateRenderer(element, renderTreePosition.parent()))
171 style = styleForElement(element, inheritedStyle);
173 RenderNamedFlowThread* parentFlowRenderer = 0;
174 #if ENABLE(CSS_REGIONS)
175 parentFlowRenderer = moveToFlowThreadIfNeeded(element, *style);
178 if (!element.rendererIsNeeded(*style))
181 renderTreePosition.computeNextSibling(element);
183 RenderTreePosition insertionPosition = parentFlowRenderer
184 ? RenderTreePosition(*parentFlowRenderer, parentFlowRenderer->nextRendererForElement(element))
185 : renderTreePosition;
187 RenderElement* newRenderer = element.createElementRenderer(style.releaseNonNull(), insertionPosition).leakPtr();
190 if (!insertionPosition.canInsert(*newRenderer)) {
191 newRenderer->destroy();
195 // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
196 // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
197 newRenderer->setFlowThreadState(insertionPosition.parent().flowThreadState());
199 // Code below updateAnimations() can depend on Element::renderer() already being set.
200 element.setRenderer(newRenderer);
202 // FIXME: There's probably a better way to factor this.
203 // This just does what setAnimatedStyle() does, except with setStyleInternal() instead of setStyle().
204 Ref<RenderStyle> animatedStyle = newRenderer->style();
205 newRenderer->animation().updateAnimations(*newRenderer, animatedStyle, animatedStyle);
206 newRenderer->setStyleInternal(WTF::move(animatedStyle));
208 newRenderer->initializeStyle();
210 #if ENABLE(FULLSCREEN_API)
211 Document& document = element.document();
212 if (document.webkitIsFullScreen() && document.webkitCurrentFullScreenElement() == &element) {
213 newRenderer = RenderFullScreen::wrapRenderer(newRenderer, &insertionPosition.parent(), document);
218 // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer.
219 insertionPosition.insert(*newRenderer);
222 static void invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node& current)
224 if (is<InsertionPoint>(current))
226 // This function finds sibling text renderers where the results of textRendererIsNeeded may have changed as a result of
227 // the current node gaining or losing the renderer. This can only affect white space text nodes.
228 for (Node* sibling = current.nextSibling(); sibling; sibling = sibling->nextSibling()) {
229 if (sibling->needsStyleRecalc())
231 if (is<Element>(*sibling)) {
232 // Text renderers beyond rendered elements can't be affected.
233 if (!sibling->renderer() || RenderTreePosition::isRendererReparented(*sibling->renderer()))
237 if (!is<Text>(*sibling))
239 Text& textSibling = downcast<Text>(*sibling);
240 if (!textSibling.containsOnlyWhitespace())
242 textSibling.setNeedsStyleRecalc();
246 static bool textRendererIsNeeded(const Text& textNode, const RenderTreePosition& renderTreePosition)
248 const RenderElement& parentRenderer = renderTreePosition.parent();
249 if (!parentRenderer.canHaveChildren())
251 if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(textNode))
253 if (textNode.isEditingText())
255 if (!textNode.length())
257 if (!textNode.containsOnlyWhitespace())
259 // This text node has nothing but white space. We may still need a renderer in some cases.
260 if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet())
262 if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
265 RenderObject* previousRenderer = RenderTreePosition::previousSiblingRenderer(textNode);
266 if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span>
269 if (parentRenderer.isRenderInline()) {
270 // <span><div/> <div/></span>
271 if (previousRenderer && !previousRenderer->isInline())
274 if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline()))
277 RenderObject* first = parentRenderer.firstChild();
278 while (first && first->isFloatingOrOutOfFlowPositioned())
279 first = first->nextSibling();
280 RenderObject* nextRenderer = RenderTreePosition::nextSiblingRenderer(textNode, parentRenderer);
281 if (!first || nextRenderer == first) {
282 // Whitespace at the start of a block just goes away. Don't even make a render object for this text.
289 static void createTextRendererIfNeeded(Text& textNode, RenderTreePosition& renderTreePosition)
291 ASSERT(!textNode.renderer());
293 if (!textRendererIsNeeded(textNode, renderTreePosition))
296 auto newRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
299 renderTreePosition.computeNextSibling(textNode);
301 if (!renderTreePosition.canInsert(*newRenderer))
304 // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
305 // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
306 newRenderer->setFlowThreadState(renderTreePosition.parent().flowThreadState());
308 textNode.setRenderer(newRenderer.get());
309 // Parent takes care of the animations, no need to call setAnimatableStyle.
310 renderTreePosition.insert(*newRenderer.leakPtr());
313 void attachTextRenderer(Text& textNode, RenderTreePosition& renderTreePosition)
315 createTextRendererIfNeeded(textNode, renderTreePosition);
317 textNode.clearNeedsStyleRecalc();
320 void detachTextRenderer(Text& textNode)
322 if (textNode.renderer())
323 textNode.renderer()->destroyAndCleanupAnonymousWrappers();
324 textNode.setRenderer(0);
327 void updateTextRendererAfterContentChange(Text& textNode, unsigned offsetOfReplacedData, unsigned lengthOfReplacedData)
329 ContainerNode* renderingParentNode = NodeRenderingTraversal::parent(&textNode);
330 if (!renderingParentNode || !renderingParentNode->renderer())
333 bool hadRenderer = textNode.renderer();
335 RenderTreePosition renderTreePosition(*renderingParentNode->renderer());
336 resolveTextNode(textNode, renderTreePosition);
338 if (hadRenderer && textNode.renderer())
339 textNode.renderer()->setTextWithOffset(textNode.data(), offsetOfReplacedData, lengthOfReplacedData);
342 static void attachChildren(ContainerNode& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition)
344 for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
345 ASSERT((!child->renderer() || child->isNamedFlowContentNode()) || current.shadowRoot());
346 if (child->renderer()) {
347 renderTreePosition.invalidateNextSibling(*child->renderer());
350 if (is<Text>(*child)) {
351 attachTextRenderer(downcast<Text>(*child), renderTreePosition);
354 if (is<Element>(*child))
355 attachRenderTree(downcast<Element>(*child), inheritedStyle, renderTreePosition, nullptr);
359 static void attachDistributedChildren(InsertionPoint& insertionPoint, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition)
361 if (ShadowRoot* shadowRoot = insertionPoint.containingShadowRoot())
362 ContentDistributor::ensureDistribution(shadowRoot);
364 for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) {
365 if (current->renderer())
366 renderTreePosition.invalidateNextSibling(*current->renderer());
367 if (is<Text>(*current)) {
368 if (current->renderer())
370 attachTextRenderer(downcast<Text>(*current), renderTreePosition);
373 if (is<Element>(*current)) {
374 Element& currentElement = downcast<Element>(*current);
375 if (currentElement.renderer())
376 detachRenderTree(currentElement);
377 attachRenderTree(currentElement, inheritedStyle, renderTreePosition, nullptr);
380 // Use actual children as fallback content.
381 if (!insertionPoint.hasDistribution())
382 attachChildren(insertionPoint, inheritedStyle, renderTreePosition);
385 static void attachShadowRoot(ShadowRoot& shadowRoot)
387 ASSERT(shadowRoot.hostElement());
388 ASSERT(shadowRoot.hostElement()->renderer());
390 auto& renderer = *shadowRoot.hostElement()->renderer();
391 RenderTreePosition renderTreePosition(renderer);
392 attachChildren(shadowRoot, renderer.style(), renderTreePosition);
394 shadowRoot.clearNeedsStyleRecalc();
395 shadowRoot.clearChildNeedsStyleRecalc();
398 static PseudoElement* beforeOrAfterPseudoElement(Element& current, PseudoId pseudoId)
400 ASSERT(pseudoId == BEFORE || pseudoId == AFTER);
401 if (pseudoId == BEFORE)
402 return current.beforePseudoElement();
403 return current.afterPseudoElement();
406 static void setBeforeOrAfterPseudoElement(Element& current, Ref<PseudoElement>&& pseudoElement, PseudoId pseudoId)
408 ASSERT(pseudoId == BEFORE || pseudoId == AFTER);
409 if (pseudoId == BEFORE) {
410 current.setBeforePseudoElement(WTF::move(pseudoElement));
413 current.setAfterPseudoElement(WTF::move(pseudoElement));
416 static void clearBeforeOrAfterPseudoElement(Element& current, PseudoId pseudoId)
418 ASSERT(pseudoId == BEFORE || pseudoId == AFTER);
419 if (pseudoId == BEFORE) {
420 current.clearBeforePseudoElement();
423 current.clearAfterPseudoElement();
426 static void resetStyleForNonRenderedDescendants(Element& current)
428 ASSERT(!current.renderer());
429 bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
430 for (auto& child : childrenOfType<Element>(current)) {
431 ASSERT(!child.renderer());
432 if (elementNeedingStyleRecalcAffectsNextSiblingElementStyle) {
433 if (child.styleIsAffectedByPreviousSibling())
434 child.setNeedsStyleRecalc();
435 elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
438 if (child.needsStyleRecalc()) {
439 child.resetComputedStyle();
440 child.clearNeedsStyleRecalc();
441 elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
444 if (child.childNeedsStyleRecalc()) {
445 resetStyleForNonRenderedDescendants(child);
446 child.clearChildNeedsStyleRecalc();
451 static bool needsPseudoElement(Element& current, PseudoId pseudoId)
453 if (!current.renderer() || !current.renderer()->canHaveGeneratedChildren())
455 if (current.isPseudoElement())
457 if (!pseudoElementRendererIsNeeded(current.renderer()->getCachedPseudoStyle(pseudoId)))
462 static void attachBeforeOrAfterPseudoElementIfNeeded(Element& current, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
464 if (!needsPseudoElement(current, pseudoId))
466 Ref<PseudoElement> pseudoElement = PseudoElement::create(current, pseudoId);
467 setBeforeOrAfterPseudoElement(current, pseudoElement.copyRef(), pseudoId);
468 attachRenderTree(pseudoElement.get(), *current.renderStyle(), renderTreePosition, nullptr);
471 static void attachRenderTree(Element& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, PassRefPtr<RenderStyle> resolvedStyle)
473 PostResolutionCallbackDisabler callbackDisabler(current.document());
474 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
476 if (is<InsertionPoint>(current)) {
477 attachDistributedChildren(downcast<InsertionPoint>(current), inheritedStyle, renderTreePosition);
478 current.clearNeedsStyleRecalc();
479 current.clearChildNeedsStyleRecalc();
483 if (current.hasCustomStyleResolveCallbacks())
484 current.willAttachRenderers();
486 createRendererIfNeeded(current, inheritedStyle, renderTreePosition, resolvedStyle);
488 if (auto* renderer = current.renderer()) {
489 StyleResolverParentPusher parentPusher(¤t);
491 RenderTreePosition childRenderTreePosition(*renderer);
492 attachBeforeOrAfterPseudoElementIfNeeded(current, BEFORE, childRenderTreePosition);
494 if (ShadowRoot* shadowRoot = current.shadowRoot()) {
496 attachShadowRoot(*shadowRoot);
497 } else if (current.firstChild())
500 attachChildren(current, renderer->style(), childRenderTreePosition);
502 if (AXObjectCache* cache = current.document().axObjectCache())
503 cache->updateCacheAfterNodeIsAttached(¤t);
505 attachBeforeOrAfterPseudoElementIfNeeded(current, AFTER, childRenderTreePosition);
507 current.updateFocusAppearanceAfterAttachIfNeeded();
509 resetStyleForNonRenderedDescendants(current);
511 current.clearNeedsStyleRecalc();
512 current.clearChildNeedsStyleRecalc();
514 if (current.hasCustomStyleResolveCallbacks())
515 current.didAttachRenderers();
518 static void detachDistributedChildren(InsertionPoint& insertionPoint)
520 for (Node* current = insertionPoint.firstDistributed(); current; current = insertionPoint.nextDistributedTo(current)) {
521 if (is<Text>(*current)) {
522 detachTextRenderer(downcast<Text>(*current));
525 if (is<Element>(*current))
526 detachRenderTree(downcast<Element>(*current));
530 static void detachChildren(ContainerNode& current, DetachType detachType)
532 if (is<InsertionPoint>(current))
533 detachDistributedChildren(downcast<InsertionPoint>(current));
535 for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
536 if (is<Text>(*child)) {
537 Style::detachTextRenderer(downcast<Text>(*child));
540 if (is<Element>(*child))
541 detachRenderTree(downcast<Element>(*child), detachType);
543 current.clearChildNeedsStyleRecalc();
546 static void detachShadowRoot(ShadowRoot& shadowRoot, DetachType detachType)
548 detachChildren(shadowRoot, detachType);
551 static void detachRenderTree(Element& current, DetachType detachType)
553 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
555 if (current.hasCustomStyleResolveCallbacks())
556 current.willDetachRenderers();
558 current.clearStyleDerivedDataBeforeDetachingRenderer();
560 // Do not remove the element's hovered and active status
561 // if performing a reattach.
562 if (detachType != ReattachDetach)
563 current.clearHoverAndActiveStatusBeforeDetachingRenderer();
565 if (ShadowRoot* shadowRoot = current.shadowRoot())
566 detachShadowRoot(*shadowRoot, detachType);
568 detachChildren(current, detachType);
570 if (current.renderer())
571 current.renderer()->destroyAndCleanupAnonymousWrappers();
572 current.setRenderer(0);
574 if (current.hasCustomStyleResolveCallbacks())
575 current.didDetachRenderers();
578 static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newStyle)
580 const RenderStyle& currentStyle = renderer->style();
582 const PseudoStyleCache* pseudoStyleCache = currentStyle.cachedPseudoStyles();
583 if (!pseudoStyleCache)
586 size_t cacheSize = pseudoStyleCache->size();
587 for (size_t i = 0; i < cacheSize; ++i) {
588 RefPtr<RenderStyle> newPseudoStyle;
589 PseudoId pseudoId = pseudoStyleCache->at(i)->styleType();
590 if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED)
591 newPseudoStyle = renderer->uncachedFirstLineStyle(newStyle);
593 newPseudoStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle);
596 if (*newPseudoStyle != *pseudoStyleCache->at(i)) {
597 if (pseudoId < FIRST_INTERNAL_PSEUDOID)
598 newStyle->setHasPseudoStyle(pseudoId);
599 newStyle->addCachedPseudoStyle(newPseudoStyle);
600 if (pseudoId == FIRST_LINE || pseudoId == FIRST_LINE_INHERITED) {
601 // FIXME: We should do an actual diff to determine whether a repaint vs. layout
602 // is needed, but for now just assume a layout will be required. The diff code
603 // in RenderObject::setStyle would need to be factored out so that it could be reused.
604 renderer->setNeedsLayoutAndPrefWidthsRecalc();
612 static Change resolveLocal(Element& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Change inheritedChange)
614 Change localChange = Detach;
615 RefPtr<RenderStyle> newStyle;
616 RefPtr<RenderStyle> currentStyle = current.renderStyle();
618 Document& document = current.document();
619 if (currentStyle && current.styleChangeType() != ReconstructRenderTree) {
620 Ref<RenderStyle> style(styleForElement(current, inheritedStyle));
621 newStyle = style.ptr();
622 localChange = determineChange(*currentStyle, style);
624 if (localChange == Detach) {
625 if (current.renderer() || current.isNamedFlowContentNode())
626 detachRenderTree(current, ReattachDetach);
627 attachRenderTree(current, inheritedStyle, renderTreePosition, newStyle.release());
628 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(current);
633 if (RenderElement* renderer = current.renderer()) {
634 if (localChange != NoChange || pseudoStyleCacheIsInvalid(renderer, newStyle.get()) || (inheritedChange == Force && renderer->requiresForcedStyleRecalcPropagation()) || current.styleChangeType() == SyntheticStyleChange)
635 renderer->setAnimatableStyle(*newStyle, current.styleChangeType() == SyntheticStyleChange ? StyleDifferenceRecompositeLayer : StyleDifferenceEqual);
636 else if (current.needsStyleRecalc()) {
637 // Although no change occurred, we use the new style so that the cousin style sharing code won't get
638 // fooled into believing this style is the same.
639 renderer->setStyleInternal(*newStyle);
643 // If "rem" units are used anywhere in the document, and if the document element's font size changes, then force font updating
644 // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway).
645 if (document.styleSheetCollection().usesRemUnits() && document.documentElement() == ¤t && localChange != NoChange && currentStyle && newStyle && currentStyle->fontSize() != newStyle->fontSize()) {
646 // Cached RenderStyles may depend on the re units.
647 if (StyleResolver* styleResolver = document.styleResolverIfExists())
648 styleResolver->invalidateMatchedPropertiesCache();
651 if (inheritedChange == Force)
653 if (current.styleChangeType() >= FullStyleChange)
659 void resolveTextNode(Text& text, RenderTreePosition& renderTreePosition)
661 text.clearNeedsStyleRecalc();
663 bool hasRenderer = text.renderer();
664 bool needsRenderer = textRendererIsNeeded(text, renderTreePosition);
668 detachTextRenderer(text);
669 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
674 attachTextRenderer(text, renderTreePosition);
675 invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
678 static void resolveShadowTree(ShadowRoot& shadowRoot, Element& host, Style::Change change)
680 ASSERT(shadowRoot.hostElement() == &host);
681 ASSERT(host.renderer());
682 RenderTreePosition renderTreePosition(*host.renderer());
683 for (Node* child = shadowRoot.firstChild(); child; child = child->nextSibling()) {
684 if (child->renderer())
685 renderTreePosition.invalidateNextSibling(*child->renderer());
686 if (is<Text>(*child) && child->needsStyleRecalc()) {
687 resolveTextNode(downcast<Text>(*child), renderTreePosition);
690 if (is<Element>(*child))
691 resolveTree(downcast<Element>(*child), host.renderer()->style(), renderTreePosition, change);
694 shadowRoot.clearNeedsStyleRecalc();
695 shadowRoot.clearChildNeedsStyleRecalc();
698 static void updateBeforeOrAfterPseudoElement(Element& current, Change change, PseudoId pseudoId, RenderTreePosition& renderTreePosition)
700 ASSERT(current.renderer());
701 if (PseudoElement* existingPseudoElement = beforeOrAfterPseudoElement(current, pseudoId)) {
702 if (existingPseudoElement->renderer())
703 renderTreePosition.invalidateNextSibling(*existingPseudoElement->renderer());
705 if (needsPseudoElement(current, pseudoId))
706 resolveTree(*existingPseudoElement, current.renderer()->style(), renderTreePosition, current.needsStyleRecalc() ? Force : change);
708 clearBeforeOrAfterPseudoElement(current, pseudoId);
711 attachBeforeOrAfterPseudoElementIfNeeded(current, pseudoId, renderTreePosition);
715 static EVisibility elementImplicitVisibility(const Element* element)
717 RenderObject* renderer = element->renderer();
721 RenderStyle& style = renderer->style();
723 Length width(style.width());
724 Length height(style.height());
725 if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
728 Length top(style.top());
729 Length left(style.left());
730 if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
733 if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
738 class CheckForVisibilityChangeOnRecalcStyle {
740 CheckForVisibilityChangeOnRecalcStyle(Element* element, RenderStyle* currentStyle)
742 , m_previousDisplay(currentStyle ? currentStyle->display() : NONE)
743 , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN)
744 , m_previousImplicitVisibility(WKObservingContentChanges() && WKContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
747 ~CheckForVisibilityChangeOnRecalcStyle()
749 if (!WKObservingContentChanges())
751 RenderStyle* style = m_element->renderStyle();
754 if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
755 || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE))
756 WKSetObservedContentChange(WKContentVisibilityChange);
759 RefPtr<Element> m_element;
760 EDisplay m_previousDisplay;
761 EVisibility m_previousVisibility;
762 EVisibility m_previousImplicitVisibility;
764 #endif // PLATFORM(IOS)
766 void resolveTree(Element& current, RenderStyle& inheritedStyle, RenderTreePosition& renderTreePosition, Change change)
768 ASSERT(change != Detach);
770 if (is<InsertionPoint>(current)) {
771 current.clearNeedsStyleRecalc();
772 current.clearChildNeedsStyleRecalc();
776 if (current.hasCustomStyleResolveCallbacks()) {
777 if (!current.willRecalcStyle(change))
782 CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(¤t, current.renderStyle());
785 if (change > NoChange || current.needsStyleRecalc())
786 current.resetComputedStyle();
788 if (change >= Inherit || current.needsStyleRecalc())
789 change = resolveLocal(current, inheritedStyle, renderTreePosition, change);
791 auto* renderer = current.renderer();
793 if (change != Detach && renderer) {
794 StyleResolverParentPusher parentPusher(¤t);
796 if (ShadowRoot* shadowRoot = current.shadowRoot()) {
797 if (change >= Inherit || shadowRoot->childNeedsStyleRecalc() || shadowRoot->needsStyleRecalc()) {
799 resolveShadowTree(*shadowRoot, current, change);
803 RenderTreePosition childRenderTreePosition(*renderer);
804 updateBeforeOrAfterPseudoElement(current, change, BEFORE, childRenderTreePosition);
806 bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
807 for (Node* child = current.firstChild(); child; child = child->nextSibling()) {
808 if (RenderObject* childRenderer = child->renderer())
809 childRenderTreePosition.invalidateNextSibling(*childRenderer);
810 if (is<Text>(*child) && child->needsStyleRecalc()) {
811 resolveTextNode(downcast<Text>(*child), childRenderTreePosition);
814 if (!is<Element>(*child))
817 Element& childElement = downcast<Element>(*child);
818 if (elementNeedingStyleRecalcAffectsNextSiblingElementStyle) {
819 if (childElement.styleIsAffectedByPreviousSibling())
820 childElement.setNeedsStyleRecalc();
821 elementNeedingStyleRecalcAffectsNextSiblingElementStyle = childElement.affectsNextSiblingElementStyle();
822 } else if (childElement.styleChangeType() >= FullStyleChange)
823 elementNeedingStyleRecalcAffectsNextSiblingElementStyle = childElement.affectsNextSiblingElementStyle();
824 if (change >= Inherit || childElement.childNeedsStyleRecalc() || childElement.needsStyleRecalc()) {
826 resolveTree(childElement, renderer->style(), childRenderTreePosition, change);
830 updateBeforeOrAfterPseudoElement(current, change, AFTER, childRenderTreePosition);
832 if (change != Detach && !renderer)
833 resetStyleForNonRenderedDescendants(current);
835 current.clearNeedsStyleRecalc();
836 current.clearChildNeedsStyleRecalc();
838 if (current.hasCustomStyleResolveCallbacks())
839 current.didRecalcStyle(change);
842 void resolveTree(Document& document, Change change)
844 if (change == Force) {
845 auto documentStyle = resolveForDocument(document);
847 // Inserting the pictograph font at the end of the font fallback list is done by the
848 // font selector, so set a font selector if needed.
849 if (Settings* settings = document.settings()) {
850 if (settings->fontFallbackPrefersPictographs())
851 documentStyle.get().fontCascade().update(&document.fontSelector());
854 Style::Change documentChange = determineChange(documentStyle.get(), document.renderView()->style());
855 if (documentChange != NoChange)
856 document.renderView()->setStyle(WTF::move(documentStyle));
859 Element* documentElement = document.documentElement();
860 if (!documentElement)
862 if (change < Inherit && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
864 RenderTreePosition renderTreePosition(*document.renderView());
865 resolveTree(*documentElement, *document.renderStyle(), renderTreePosition, change);
868 void detachRenderTree(Element& element)
870 detachRenderTree(element, NormalDetach);
873 static Vector<std::function<void ()>>& postResolutionCallbackQueue()
875 static NeverDestroyed<Vector<std::function<void ()>>> vector;
879 void queuePostResolutionCallback(std::function<void ()> callback)
881 postResolutionCallbackQueue().append(callback);
884 static void suspendMemoryCacheClientCalls(Document& document)
886 Page* page = document.page();
887 if (!page || !page->areMemoryCacheClientCallsEnabled())
890 page->setMemoryCacheClientCallsEnabled(false);
892 RefPtr<MainFrame> protectedMainFrame = &page->mainFrame();
893 postResolutionCallbackQueue().append([protectedMainFrame]{
894 if (Page* page = protectedMainFrame->page())
895 page->setMemoryCacheClientCallsEnabled(true);
899 static unsigned resolutionNestingDepth;
901 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document)
903 ++resolutionNestingDepth;
905 if (resolutionNestingDepth == 1)
906 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->suspendPendingRequests();
908 // FIXME: It's strange to build this into the disabler.
909 suspendMemoryCacheClientCalls(document);
912 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
914 if (resolutionNestingDepth == 1) {
915 // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
916 auto& queue = postResolutionCallbackQueue();
917 for (size_t i = 0; i < queue.size(); ++i)
921 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->resumePendingRequests();
924 --resolutionNestingDepth;
927 bool postResolutionCallbacksAreSuspended()
929 return resolutionNestingDepth;