Rename setNeedsStyleRecalc to invalidateStyle
[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 "CSSFontSelector.h"
30 #include "ComposedTreeAncestorIterator.h"
31 #include "ComposedTreeIterator.h"
32 #include "ElementIterator.h"
33 #include "HTMLBodyElement.h"
34 #include "HTMLMeterElement.h"
35 #include "HTMLNames.h"
36 #include "HTMLProgressElement.h"
37 #include "HTMLSlotElement.h"
38 #include "LoaderStrategy.h"
39 #include "MainFrame.h"
40 #include "NodeRenderStyle.h"
41 #include "Page.h"
42 #include "PlatformStrategies.h"
43 #include "Settings.h"
44 #include "ShadowRoot.h"
45 #include "StyleFontSizeFunctions.h"
46 #include "StyleResolver.h"
47 #include "StyleScope.h"
48 #include "Text.h"
49
50 namespace WebCore {
51
52 namespace Style {
53
54 static RenderStyle* placeholderStyle;
55
56 static void ensurePlaceholderStyle(Document& document)
57 {
58     if (placeholderStyle)
59         return;
60     placeholderStyle = RenderStyle::createPtr().release();
61     placeholderStyle->setDisplay(NONE);
62     placeholderStyle->setIsPlaceholderStyle();
63
64     FontCascadeDescription fontDescription;
65     fontDescription.setOneFamily(standardFamily);
66     fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);
67     float size = Style::fontSizeForKeyword(CSSValueMedium, false, document);
68     fontDescription.setSpecifiedSize(size);
69     fontDescription.setComputedSize(size);
70     placeholderStyle->setFontDescription(fontDescription);
71
72     placeholderStyle->fontCascade().update(&document.fontSelector());
73 }
74
75 TreeResolver::TreeResolver(Document& document)
76     : m_document(document)
77 {
78     ensurePlaceholderStyle(document);
79 }
80
81 TreeResolver::~TreeResolver()
82 {
83 }
84
85 TreeResolver::Scope::Scope(Document& document)
86     : styleResolver(document.styleScope().resolver())
87     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
88 {
89 }
90
91 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
92     : styleResolver(shadowRoot.styleScope().resolver())
93     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
94     , shadowRoot(&shadowRoot)
95     , enclosingScope(&enclosingScope)
96 {
97 }
98
99 TreeResolver::Parent::Parent(Document& document, Change change)
100     : element(nullptr)
101     , style(*document.renderStyle())
102     , change(change)
103 {
104 }
105
106 TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change)
107     : element(&element)
108     , style(style)
109     , change(change)
110 {
111 }
112
113 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
114 {
115     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
116     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
117 }
118
119 void TreeResolver::pushEnclosingScope()
120 {
121     ASSERT(scope().enclosingScope);
122     m_scopeStack.append(*scope().enclosingScope);
123     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
124 }
125
126 void TreeResolver::popScope()
127 {
128     scope().styleResolver.setOverrideDocumentElementStyle(nullptr);
129     return m_scopeStack.removeLast();
130 }
131
132 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle)
133 {
134     if (!m_document.haveStylesheetsLoaded() && !element.renderer()) {
135         m_document.setHasNodesWithPlaceholderStyle();
136         return RenderStyle::clonePtr(*placeholderStyle);
137     }
138
139     if (element.hasCustomStyleResolveCallbacks()) {
140         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
141         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
142             if (customStyle->relations)
143                 commitRelations(WTFMove(customStyle->relations), *m_update);
144
145             return WTFMove(customStyle->renderStyle);
146         }
147     }
148
149     if (auto style = scope().sharingResolver.resolve(element, *m_update))
150         return style;
151
152     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, MatchAllRules, nullptr, &scope().selectorFilter);
153
154     if (elementStyle.relations)
155         commitRelations(WTFMove(elementStyle.relations), *m_update);
156
157     return WTFMove(elementStyle.renderStyle);
158 }
159
160 static void resetStyleForNonRenderedDescendants(Element& current)
161 {
162     // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator.
163     bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
164     for (auto& child : childrenOfType<Element>(current)) {
165         bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
166         if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
167             elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
168
169         if (child.needsStyleRecalc() || affectedByPreviousSibling) {
170             child.resetComputedStyle();
171             child.setHasValidStyle();
172         }
173
174         if (child.childNeedsStyleRecalc()) {
175             resetStyleForNonRenderedDescendants(child);
176             child.clearChildNeedsStyleRecalc();
177         }
178     }
179 }
180
181 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
182 {
183     if (element.renderer())
184         return true;
185     if (newStyle.display() != NONE)
186         return true;
187     if (element.rendererIsNeeded(newStyle))
188         return true;
189 #if ENABLE(CSS_REGIONS)
190     if (element.shouldMoveToFlowThread(newStyle))
191         return true;
192 #endif
193     return false;
194 }
195
196 ElementUpdate TreeResolver::resolveElement(Element& element)
197 {
198     auto newStyle = styleForElement(element, parent().style);
199
200     if (!affectsRenderedSubtree(element, *newStyle))
201         return { };
202
203     bool shouldReconstructRenderTree = element.styleValidity() >= Validity::SubtreeAndRenderersInvalid || parent().change == Detach;
204     auto* rendererToUpdate = shouldReconstructRenderTree ? nullptr : element.renderer();
205
206     auto update = createAnimatedElementUpdate(WTFMove(newStyle), rendererToUpdate, m_document);
207
208     if (element.styleResolutionShouldRecompositeLayer())
209         update.recompositeLayer = true;
210
211     auto* existingStyle = element.renderStyle();
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->fontSize() != update.style->fontSize()) {
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             update.change = Force;
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     }
235
236     if (update.change != Detach && (parent().change == Force || element.styleValidity() >= Validity::SubtreeInvalid))
237         update.change = Force;
238
239     return update;
240 }
241
242 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, RenderElement* rendererToUpdate, Document& document)
243 {
244     ElementUpdate update;
245
246     std::unique_ptr<RenderStyle> animatedStyle;
247     if (rendererToUpdate && document.frame()->animation().updateAnimations(*rendererToUpdate, *newStyle, animatedStyle))
248         update.recompositeLayer = true;
249
250     if (animatedStyle) {
251         update.change = determineChange(rendererToUpdate->style(), *animatedStyle);
252         // If animation forces render tree reconstruction pass the original style. The animation will be applied on renderer construction.
253         // FIXME: We should always use the animated style here.
254         update.style = update.change == Detach ? WTFMove(newStyle) : WTFMove(animatedStyle);
255     } else {
256         update.change = rendererToUpdate ? determineChange(rendererToUpdate->style(), *newStyle) : Detach;
257         update.style = WTFMove(newStyle);
258     }
259
260     return update;
261 }
262
263 void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change)
264 {
265     scope().selectorFilter.pushParent(&element);
266
267     Parent parent(element, style, change);
268
269     if (auto* shadowRoot = element.shadowRoot()) {
270         pushScope(*shadowRoot);
271         parent.didPushScope = true;
272     }
273     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
274         pushEnclosingScope();
275         parent.didPushScope = true;
276     }
277
278     m_parentStack.append(WTFMove(parent));
279 }
280
281 void TreeResolver::popParent()
282 {
283     auto& parentElement = *parent().element;
284
285     parentElement.setHasValidStyle();
286     parentElement.clearChildNeedsStyleRecalc();
287
288     if (parent().didPushScope)
289         popScope();
290
291     scope().selectorFilter.popParent();
292
293     m_parentStack.removeLast();
294 }
295
296 void TreeResolver::popParentsToDepth(unsigned depth)
297 {
298     ASSERT(depth);
299     ASSERT(m_parentStack.size() >= depth);
300
301     while (m_parentStack.size() > depth)
302         popParent();
303 }
304
305 static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement)
306 {
307     if (!pseudoElement)
308         return false;
309     return pseudoElement->needsStyleRecalc();
310 }
311
312 static bool shouldResolveElement(const Element& element, Style::Change parentChange)
313 {
314     if (parentChange >= Inherit)
315         return true;
316     if (parentChange == NoInherit) {
317         auto* existingStyle = element.renderStyle();
318         if (existingStyle && existingStyle->hasExplicitlyInheritedProperties())
319             return true;
320     }
321     if (element.needsStyleRecalc())
322         return true;
323     if (element.hasDisplayContents())
324         return true;
325     if (shouldResolvePseudoElement(element.beforePseudoElement()))
326         return true;
327     if (shouldResolvePseudoElement(element.afterPseudoElement()))
328         return true;
329
330     return false;
331 }
332
333 static void clearNeedsStyleResolution(Element& element)
334 {
335     element.setHasValidStyle();
336     if (auto* before = element.beforePseudoElement())
337         before->setHasValidStyle();
338     if (auto* after = element.afterPseudoElement())
339         after->setHasValidStyle();
340 }
341
342 void TreeResolver::resolveComposedTree()
343 {
344     ASSERT(m_parentStack.size() == 1);
345     ASSERT(m_scopeStack.size() == 1);
346
347     auto descendants = composedTreeDescendants(m_document);
348     auto it = descendants.begin();
349     auto end = descendants.end();
350
351     // FIXME: SVG <use> element may cause tree mutations during style recalc.
352     it.dropAssertions();
353
354     while (it != end) {
355         popParentsToDepth(it.depth());
356
357         auto& node = *it;
358         auto& parent = this->parent();
359
360         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
361         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
362
363         if (is<Text>(node)) {
364             auto& text = downcast<Text>(node);
365             if (text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach)
366                 m_update->addText(text, parent.element);
367
368             text.setHasValidStyle();
369             it.traverseNextSkippingChildren();
370             continue;
371         }
372
373         auto& element = downcast<Element>(node);
374
375         if (it.depth() > Settings::defaultMaximumRenderTreeDepth) {
376             resetStyleForNonRenderedDescendants(element);
377             element.clearChildNeedsStyleRecalc();
378             it.traverseNextSkippingChildren();
379             continue;
380         }
381
382         // FIXME: We should deal with this during style invalidation.
383         bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
384         if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
385             parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle();
386
387         auto* style = element.renderStyle();
388         auto change = NoChange;
389
390         bool shouldResolve = shouldResolveElement(element, parent.change) || affectedByPreviousSibling;
391         if (shouldResolve) {
392             element.resetComputedStyle();
393
394             if (element.hasCustomStyleResolveCallbacks()) {
395                 if (!element.willRecalcStyle(parent.change)) {
396                     it.traverseNextSkippingChildren();
397                     continue;
398                 }
399             }
400
401             auto elementUpdate = resolveElement(element);
402
403             if (element.hasCustomStyleResolveCallbacks())
404                 element.didRecalcStyle(elementUpdate.change);
405
406             style = elementUpdate.style.get();
407             change = elementUpdate.change;
408
409             if (affectedByPreviousSibling && change != Detach)
410                 change = Force;
411
412             if (elementUpdate.style)
413                 m_update->addElement(element, parent.element, WTFMove(elementUpdate));
414
415             clearNeedsStyleResolution(element);
416         }
417
418         if (!style) {
419             resetStyleForNonRenderedDescendants(element);
420             element.clearChildNeedsStyleRecalc();
421         }
422
423         bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || change != NoChange);
424         if (!shouldIterateChildren) {
425             it.traverseNextSkippingChildren();
426             continue;
427         }
428
429         pushParent(element, *style, change);
430
431         it.traverseNext();
432     }
433
434     popParentsToDepth(1);
435 }
436
437 std::unique_ptr<Update> TreeResolver::resolve(Change change)
438 {
439     auto& renderView = *m_document.renderView();
440
441     Element* documentElement = m_document.documentElement();
442     if (!documentElement) {
443         m_document.styleScope().resolver();
444         return nullptr;
445     }
446     if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
447         return nullptr;
448
449     m_update = std::make_unique<Update>(m_document);
450     m_scopeStack.append(adoptRef(*new Scope(m_document)));
451     m_parentStack.append(Parent(m_document, change));
452
453     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
454     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
455     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
456
457     resolveComposedTree();
458
459     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
460     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
461
462     ASSERT(m_scopeStack.size() == 1);
463     ASSERT(m_parentStack.size() == 1);
464     m_parentStack.clear();
465     popScope();
466
467     if (m_update->roots().isEmpty())
468         return { };
469
470     return WTFMove(m_update);
471 }
472
473 static Vector<Function<void ()>>& postResolutionCallbackQueue()
474 {
475     static NeverDestroyed<Vector<Function<void ()>>> vector;
476     return vector;
477 }
478
479 void queuePostResolutionCallback(Function<void ()>&& callback)
480 {
481     postResolutionCallbackQueue().append(WTFMove(callback));
482 }
483
484 static void suspendMemoryCacheClientCalls(Document& document)
485 {
486     Page* page = document.page();
487     if (!page || !page->areMemoryCacheClientCallsEnabled())
488         return;
489
490     page->setMemoryCacheClientCallsEnabled(false);
491
492     postResolutionCallbackQueue().append([protectedMainFrame = Ref<MainFrame>(page->mainFrame())] {
493         if (Page* page = protectedMainFrame->page())
494             page->setMemoryCacheClientCallsEnabled(true);
495     });
496 }
497
498 static unsigned resolutionNestingDepth;
499
500 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document)
501 {
502     ++resolutionNestingDepth;
503
504     if (resolutionNestingDepth == 1)
505         platformStrategies()->loaderStrategy()->suspendPendingRequests();
506
507     // FIXME: It's strange to build this into the disabler.
508     suspendMemoryCacheClientCalls(document);
509 }
510
511 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
512 {
513     if (resolutionNestingDepth == 1) {
514         // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
515         auto& queue = postResolutionCallbackQueue();
516         for (size_t i = 0; i < queue.size(); ++i)
517             queue[i]();
518         queue.clear();
519
520         platformStrategies()->loaderStrategy()->resumePendingRequests();
521     }
522
523     --resolutionNestingDepth;
524 }
525
526 bool postResolutionCallbacksAreSuspended()
527 {
528     return resolutionNestingDepth;
529 }
530
531 bool isPlaceholderStyle(const RenderStyle& style)
532 {
533     return &style == placeholderStyle;
534 }
535
536 }
537 }