Render tree teardown should be iterative
[WebKit-https.git] / Source / WebCore / style / StyleTreeResolver.cpp
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 "AuthorStyleSheets.h"
30 #include "CSSFontSelector.h"
31 #include "ComposedTreeAncestorIterator.h"
32 #include "ComposedTreeIterator.h"
33 #include "ElementIterator.h"
34 #include "HTMLBodyElement.h"
35 #include "HTMLSlotElement.h"
36 #include "LoaderStrategy.h"
37 #include "MainFrame.h"
38 #include "NodeRenderStyle.h"
39 #include "PlatformStrategies.h"
40 #include "Settings.h"
41 #include "ShadowRoot.h"
42 #include "StyleResolver.h"
43 #include "Text.h"
44
45 #if PLATFORM(IOS)
46 #include "WKContentObservation.h"
47 #endif
48
49 namespace WebCore {
50
51 namespace Style {
52
53 static RenderStyle* placeholderStyle;
54
55 static void ensurePlaceholderStyle(Document& document)
56 {
57     if (placeholderStyle)
58         return;
59     placeholderStyle = &RenderStyle::create().leakRef();
60     placeholderStyle->setDisplay(NONE);
61     placeholderStyle->fontCascade().update(&document.fontSelector());
62 }
63
64 TreeResolver::TreeResolver(Document& document)
65     : m_document(document)
66 {
67     ensurePlaceholderStyle(document);
68 }
69
70 TreeResolver::~TreeResolver()
71 {
72 }
73
74 TreeResolver::Scope::Scope(Document& document)
75     : styleResolver(document.ensureStyleResolver())
76     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
77 {
78 }
79
80 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
81     : styleResolver(shadowRoot.styleResolver())
82     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
83     , shadowRoot(&shadowRoot)
84     , enclosingScope(&enclosingScope)
85 {
86 }
87
88 TreeResolver::Parent::Parent(Document& document, Change change)
89     : element(nullptr)
90     , style(*document.renderStyle())
91     , change(change)
92 {
93 }
94
95 TreeResolver::Parent::Parent(Element& element, ElementUpdate& update)
96     : element(&element)
97     , style(*update.style)
98     , change(update.change)
99 {
100 }
101
102 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
103 {
104     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
105 }
106
107 void TreeResolver::pushEnclosingScope()
108 {
109     ASSERT(scope().enclosingScope);
110     m_scopeStack.append(*scope().enclosingScope);
111 }
112
113 void TreeResolver::popScope()
114 {
115     return m_scopeStack.removeLast();
116 }
117
118 Ref<RenderStyle> TreeResolver::styleForElement(Element& element, RenderStyle& inheritedStyle)
119 {
120     if (!m_document.haveStylesheetsLoaded() && !element.renderer()) {
121         m_document.setHasNodesWithPlaceholderStyle();
122         return *placeholderStyle;
123     }
124
125     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
126
127     if (element.hasCustomStyleResolveCallbacks()) {
128         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
129         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
130             if (customStyle->relations)
131                 commitRelations(WTFMove(customStyle->relations), *m_update);
132
133             return WTFMove(customStyle->renderStyle);
134         }
135     }
136
137     if (auto style = scope().sharingResolver.resolve(element, *m_update))
138         return *style;
139
140     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, MatchAllRules, nullptr, &scope().selectorFilter);
141
142     if (elementStyle.relations)
143         commitRelations(WTFMove(elementStyle.relations), *m_update);
144
145     return WTFMove(elementStyle.renderStyle);
146 }
147
148 static void resetStyleForNonRenderedDescendants(Element& current)
149 {
150     // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator.
151     bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
152     for (auto& child : childrenOfType<Element>(current)) {
153         bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
154         if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
155             elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
156
157         if (child.needsStyleRecalc() || affectedByPreviousSibling) {
158             child.resetComputedStyle();
159             child.clearNeedsStyleRecalc();
160         }
161
162         if (child.childNeedsStyleRecalc()) {
163             resetStyleForNonRenderedDescendants(child);
164             child.clearChildNeedsStyleRecalc();
165         }
166     }
167 }
168
169 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
170 {
171     if (element.renderer())
172         return true;
173     if (newStyle.display() != NONE)
174         return true;
175     // FIXME: Make 'contents' an actual display property value.
176     if (hasImplicitDisplayContents(element))
177         return true;
178     if (element.rendererIsNeeded(newStyle))
179         return true;
180     if (element.shouldMoveToFlowThread(newStyle))
181         return true;
182     return false;
183 }
184
185 ElementUpdate TreeResolver::resolveElement(Element& element)
186 {
187     auto newStyle = styleForElement(element, parent().style);
188
189     auto* renderer = element.renderer();
190
191     if (!affectsRenderedSubtree(element, newStyle.get()))
192         return { };
193
194     ElementUpdate update;
195
196     bool needsNewRenderer = !renderer || element.styleChangeType() == ReconstructRenderTree || parent().change == Detach;
197     if (!needsNewRenderer && m_document.frame()->animation().updateAnimations(*renderer, newStyle, newStyle))
198         update.isSynthetic = true;
199
200     update.change = needsNewRenderer ? Detach : determineChange(renderer->style(), newStyle);
201     update.style = WTFMove(newStyle);
202
203     if (element.styleChangeType() == SyntheticStyleChange)
204         update.isSynthetic = true;
205
206     if (&element == m_document.documentElement()) {
207         m_documentElementStyle = update.style;
208
209         // If "rem" units are used anywhere in the document, and if the document element's font size changes, then force font updating
210         // 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).
211         if (m_document.authorStyleSheets().usesRemUnits() && update.change != NoChange && renderer && renderer->style().fontSize() != update.style->fontSize()) {
212             // Cached RenderStyles may depend on the rem units.
213             scope().styleResolver.invalidateMatchedPropertiesCache();
214             update.change = Force;
215         }
216     }
217
218     // This is needed for resolving color:-webkit-text for subsequent elements.
219     // FIXME: We shouldn't mutate document when resolving style.
220     if (&element == m_document.body())
221         m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor));
222
223     if (update.change != Detach && (parent().change == Force || element.styleChangeType() >= FullStyleChange))
224         update.change = Force;
225
226     return update;
227 }
228
229 #if PLATFORM(IOS)
230 static EVisibility elementImplicitVisibility(const Element* element)
231 {
232     RenderObject* renderer = element->renderer();
233     if (!renderer)
234         return VISIBLE;
235
236     RenderStyle& style = renderer->style();
237
238     Length width(style.width());
239     Length height(style.height());
240     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
241         return HIDDEN;
242
243     Length top(style.top());
244     Length left(style.left());
245     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
246         return HIDDEN;
247
248     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
249         return HIDDEN;
250     return VISIBLE;
251 }
252
253 class CheckForVisibilityChangeOnRecalcStyle {
254 public:
255     CheckForVisibilityChangeOnRecalcStyle(Element* element, RenderStyle* currentStyle)
256         : m_element(element)
257         , m_previousDisplay(currentStyle ? currentStyle->display() : NONE)
258         , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN)
259         , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
260     {
261     }
262     ~CheckForVisibilityChangeOnRecalcStyle()
263     {
264         if (!WKObservingContentChanges())
265             return;
266         if (m_element->isInUserAgentShadowTree())
267             return;
268         RenderStyle* style = m_element->renderStyle();
269         if (!style)
270             return;
271         if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
272             || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE))
273             WKSetObservedContentChange(WKContentVisibilityChange);
274     }
275 private:
276     RefPtr<Element> m_element;
277     EDisplay m_previousDisplay;
278     EVisibility m_previousVisibility;
279     EVisibility m_previousImplicitVisibility;
280 };
281 #endif // PLATFORM(IOS)
282
283 void TreeResolver::pushParent(Element& element, ElementUpdate& update)
284 {
285     scope().selectorFilter.pushParent(&element);
286
287     Parent parent(element, update);
288
289     if (auto* shadowRoot = element.shadowRoot()) {
290         pushScope(*shadowRoot);
291         parent.didPushScope = true;
292     }
293 #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
294     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
295         pushEnclosingScope();
296         parent.didPushScope = true;
297     }
298 #endif
299
300     m_parentStack.append(WTFMove(parent));
301 }
302
303 void TreeResolver::popParent()
304 {
305     auto& parentElement = *parent().element;
306
307     parentElement.clearNeedsStyleRecalc();
308     parentElement.clearChildNeedsStyleRecalc();
309
310     if (parent().didPushScope)
311         popScope();
312
313     scope().selectorFilter.popParent();
314
315     m_parentStack.removeLast();
316 }
317
318 void TreeResolver::popParentsToDepth(unsigned depth)
319 {
320     ASSERT(depth);
321     ASSERT(m_parentStack.size() >= depth);
322
323     while (m_parentStack.size() > depth)
324         popParent();
325 }
326
327 static bool shouldResolvePseudoElement(PseudoElement* pseudoElement)
328 {
329     if (!pseudoElement)
330         return false;
331     bool needsStyleRecalc = pseudoElement->needsStyleRecalc();
332     pseudoElement->clearNeedsStyleRecalc();
333     return needsStyleRecalc;
334 }
335
336 void TreeResolver::resolveComposedTree()
337 {
338     ASSERT(m_parentStack.size() == 1);
339     ASSERT(m_scopeStack.size() == 1);
340
341     auto descendants = composedTreeDescendants(m_document);
342     auto it = descendants.begin();
343     auto end = descendants.end();
344
345     // FIXME: SVG <use> element may cause tree mutations during style recalc.
346     it.dropAssertions();
347
348     while (it != end) {
349         popParentsToDepth(it.depth());
350
351         auto& node = *it;
352         auto& parent = this->parent();
353
354         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
355         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
356
357         if (is<Text>(node)) {
358             auto& text = downcast<Text>(node);
359             if (text.styleChangeType() == ReconstructRenderTree && parent.change != Detach)
360                 m_update->addText(text, parent.element);
361
362             text.clearNeedsStyleRecalc();
363             it.traverseNextSkippingChildren();
364             continue;
365         }
366
367         auto& element = downcast<Element>(node);
368
369         // FIXME: We should deal with this during style invalidation.
370         bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
371         if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
372             parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle();
373
374         bool shouldResolveForPseudoElement = shouldResolvePseudoElement(element.beforePseudoElement()) || shouldResolvePseudoElement(element.afterPseudoElement());
375
376         ElementUpdate update;
377         update.style = element.renderStyle();
378
379         bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || shouldResolveForPseudoElement || affectedByPreviousSibling || hasImplicitDisplayContents(element);
380         if (shouldResolve) {
381 #if PLATFORM(IOS)
382             CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle());
383 #endif
384             element.resetComputedStyle();
385
386             if (element.hasCustomStyleResolveCallbacks()) {
387                 if (!element.willRecalcStyle(parent.change)) {
388                     it.traverseNextSkippingChildren();
389                     continue;
390                 }
391             }
392
393             update = resolveElement(element);
394
395             if (element.hasCustomStyleResolveCallbacks())
396                 element.didRecalcStyle(update.change);
397
398             if (affectedByPreviousSibling && update.change != Detach)
399                 update.change = Force;
400
401             if (update.style)
402                 m_update->addElement(element, parent.element, update);
403
404             element.clearNeedsStyleRecalc();
405         }
406
407         if (!update.style) {
408             resetStyleForNonRenderedDescendants(element);
409             element.clearChildNeedsStyleRecalc();
410         }
411
412         bool shouldIterateChildren = update.style && (element.childNeedsStyleRecalc() || update.change != NoChange);
413         if (!shouldIterateChildren) {
414             it.traverseNextSkippingChildren();
415             continue;
416         }
417
418         pushParent(element, update);
419
420         it.traverseNext();
421     }
422
423     popParentsToDepth(1);
424 }
425
426 std::unique_ptr<const Update> TreeResolver::resolve(Change change)
427 {
428     auto& renderView = *m_document.renderView();
429
430     Element* documentElement = m_document.documentElement();
431     if (!documentElement)
432         return nullptr;
433     if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
434         return nullptr;
435
436     m_update = std::make_unique<Update>(m_document);
437     m_scopeStack.append(adoptRef(*new Scope(m_document)));
438     m_parentStack.append(Parent(m_document, change));
439
440     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
441     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
442     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
443
444     resolveComposedTree();
445
446     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
447     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
448
449     m_parentStack.clear();
450     m_scopeStack.clear();
451
452     if (m_update->roots().isEmpty())
453         return { };
454
455     return WTFMove(m_update);
456 }
457
458 static Vector<std::function<void ()>>& postResolutionCallbackQueue()
459 {
460     static NeverDestroyed<Vector<std::function<void ()>>> vector;
461     return vector;
462 }
463
464 void queuePostResolutionCallback(std::function<void ()> callback)
465 {
466     postResolutionCallbackQueue().append(callback);
467 }
468
469 static void suspendMemoryCacheClientCalls(Document& document)
470 {
471     Page* page = document.page();
472     if (!page || !page->areMemoryCacheClientCallsEnabled())
473         return;
474
475     page->setMemoryCacheClientCallsEnabled(false);
476
477     RefPtr<MainFrame> protectedMainFrame = &page->mainFrame();
478     postResolutionCallbackQueue().append([protectedMainFrame]{
479         if (Page* page = protectedMainFrame->page())
480             page->setMemoryCacheClientCallsEnabled(true);
481     });
482 }
483
484 static unsigned resolutionNestingDepth;
485
486 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document)
487 {
488     ++resolutionNestingDepth;
489
490     if (resolutionNestingDepth == 1)
491         platformStrategies()->loaderStrategy()->suspendPendingRequests();
492
493     // FIXME: It's strange to build this into the disabler.
494     suspendMemoryCacheClientCalls(document);
495 }
496
497 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
498 {
499     if (resolutionNestingDepth == 1) {
500         // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
501         auto& queue = postResolutionCallbackQueue();
502         for (size_t i = 0; i < queue.size(); ++i)
503             queue[i]();
504         queue.clear();
505
506         platformStrategies()->loaderStrategy()->resumePendingRequests();
507     }
508
509     --resolutionNestingDepth;
510 }
511
512 bool postResolutionCallbacksAreSuspended()
513 {
514     return resolutionNestingDepth;
515 }
516
517 bool isPlaceholderStyle(const RenderStyle& style)
518 {
519     return &style == placeholderStyle;
520 }
521
522 }
523 }