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