wtf/Optional.h: move-constructor and move-assignment operator should disengage the...
[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 "CSSAnimationController.h"
30 #include "CSSFontSelector.h"
31 #include "ComposedTreeAncestorIterator.h"
32 #include "ComposedTreeIterator.h"
33 #include "DocumentTimeline.h"
34 #include "ElementIterator.h"
35 #include "Frame.h"
36 #include "HTMLBodyElement.h"
37 #include "HTMLMeterElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLProgressElement.h"
40 #include "HTMLSlotElement.h"
41 #include "LoaderStrategy.h"
42 #include "NodeRenderStyle.h"
43 #include "Page.h"
44 #include "PlatformStrategies.h"
45 #include "RenderElement.h"
46 #include "RenderView.h"
47 #include "RuntimeEnabledFeatures.h"
48 #include "Settings.h"
49 #include "ShadowRoot.h"
50 #include "StyleFontSizeFunctions.h"
51 #include "StyleResolver.h"
52 #include "StyleScope.h"
53 #include "Text.h"
54
55 namespace WebCore {
56
57 namespace Style {
58
59 TreeResolver::TreeResolver(Document& document)
60     : m_document(document)
61 {
62 }
63
64 TreeResolver::~TreeResolver() = default;
65
66 TreeResolver::Scope::Scope(Document& document)
67     : styleResolver(document.styleScope().resolver())
68     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
69 {
70 }
71
72 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
73     : styleResolver(shadowRoot.styleScope().resolver())
74     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
75     , shadowRoot(&shadowRoot)
76     , enclosingScope(&enclosingScope)
77 {
78     styleResolver.setOverrideDocumentElementStyle(enclosingScope.styleResolver.overrideDocumentElementStyle());
79 }
80
81 TreeResolver::Scope::~Scope()
82 {
83     styleResolver.setOverrideDocumentElementStyle(nullptr);
84 }
85
86 TreeResolver::Parent::Parent(Document& document)
87     : element(nullptr)
88     , style(*document.renderStyle())
89 {
90 }
91
92 TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve)
93     : element(&element)
94     , style(style)
95     , change(change)
96     , descendantsToResolve(descendantsToResolve)
97 {
98 }
99
100 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
101 {
102     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
103 }
104
105 void TreeResolver::pushEnclosingScope()
106 {
107     ASSERT(scope().enclosingScope);
108     m_scopeStack.append(*scope().enclosingScope);
109 }
110
111 void TreeResolver::popScope()
112 {
113     return m_scopeStack.removeLast();
114 }
115
116 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle)
117 {
118     if (element.hasCustomStyleResolveCallbacks()) {
119         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
120         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
121             if (customStyle->relations)
122                 commitRelations(WTFMove(customStyle->relations), *m_update);
123
124             return WTFMove(customStyle->renderStyle);
125         }
126     }
127
128     if (auto style = scope().sharingResolver.resolve(element, *m_update))
129         return style;
130
131     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, parentBoxStyle(), RuleMatchingBehavior::MatchAllRules, &scope().selectorFilter);
132
133     if (elementStyle.relations)
134         commitRelations(WTFMove(elementStyle.relations), *m_update);
135
136     return WTFMove(elementStyle.renderStyle);
137 }
138
139 static void resetStyleForNonRenderedDescendants(Element& current)
140 {
141     for (auto& child : childrenOfType<Element>(current)) {
142         if (child.needsStyleRecalc()) {
143             child.resetComputedStyle();
144             child.resetStyleRelations();
145             child.setHasValidStyle();
146         }
147
148         if (child.childNeedsStyleRecalc())
149             resetStyleForNonRenderedDescendants(child);
150     }
151     current.clearChildNeedsStyleRecalc();
152 }
153
154 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
155 {
156     if (element.renderer())
157         return true;
158     if (newStyle.display() != DisplayType::None)
159         return true;
160     if (element.rendererIsNeeded(newStyle))
161         return true;
162     return false;
163 }
164
165 static DescendantsToResolve computeDescendantsToResolve(Change change, Validity validity, DescendantsToResolve parentDescendantsToResolve)
166 {
167     if (parentDescendantsToResolve == DescendantsToResolve::All)
168         return DescendantsToResolve::All;
169     if (validity >= Validity::SubtreeInvalid)
170         return DescendantsToResolve::All;
171     switch (change) {
172     case NoChange:
173         return DescendantsToResolve::None;
174     case NoInherit:
175         return DescendantsToResolve::ChildrenWithExplicitInherit;
176     case Inherit:
177         return DescendantsToResolve::Children;
178     case Detach:
179         return DescendantsToResolve::All;
180     };
181     ASSERT_NOT_REACHED();
182     return DescendantsToResolve::None;
183 };
184
185 ElementUpdates TreeResolver::resolveElement(Element& element)
186 {
187     if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {
188         m_document.setHasNodesWithMissingStyle();
189         return { };
190     }
191
192     auto newStyle = styleForElement(element, parent().style);
193
194     if (!affectsRenderedSubtree(element, *newStyle))
195         return { };
196
197     auto* existingStyle = element.renderOrDisplayContentsStyle();
198
199     if (m_didSeePendingStylesheet && (!existingStyle || existingStyle->isNotFinal())) {
200         newStyle->setIsNotFinal();
201         m_document.setHasNodesWithNonFinalStyle();
202     }
203
204     auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change);
205     auto descendantsToResolve = computeDescendantsToResolve(update.change, element.styleValidity(), parent().descendantsToResolve);
206
207     if (&element == m_document.documentElement()) {
208         m_documentElementStyle = RenderStyle::clonePtr(*update.style);
209         scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
210
211         if (update.change != NoChange && existingStyle && existingStyle->computedFontPixelSize() != update.style->computedFontPixelSize()) {
212             // "rem" units are relative to the document element's font size so we need to recompute everything.
213             // In practice this is rare.
214             scope().styleResolver.invalidateMatchedPropertiesCache();
215             descendantsToResolve = DescendantsToResolve::All;
216         }
217     }
218
219     // This is needed for resolving color:-webkit-text for subsequent elements.
220     // FIXME: We shouldn't mutate document when resolving style.
221     if (&element == m_document.body())
222         m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor));
223
224     // FIXME: These elements should not change renderer based on appearance property.
225     if (element.hasTagName(HTMLNames::meterTag) || is<HTMLProgressElement>(element)) {
226         if (existingStyle && update.style->appearance() != existingStyle->appearance()) {
227             update.change = Detach;
228             descendantsToResolve = DescendantsToResolve::All;
229         }
230     }
231
232     auto beforeUpdate = resolvePseudoStyle(element, update, PseudoId::Before);
233     auto afterUpdate = resolvePseudoStyle(element, update, PseudoId::After);
234
235     return { WTFMove(update), descendantsToResolve, WTFMove(beforeUpdate), WTFMove(afterUpdate) };
236 }
237
238 ElementUpdate TreeResolver::resolvePseudoStyle(Element& element, const ElementUpdate& elementUpdate, PseudoId pseudoId)
239 {
240     if (elementUpdate.style->display() == DisplayType::None)
241         return { };
242     if (!elementUpdate.style->hasPseudoStyle(pseudoId))
243         return { };
244
245     auto pseudoStyle = scope().styleResolver.pseudoStyleForElement(element, { pseudoId }, *elementUpdate.style, &scope().selectorFilter);
246     if (!pseudoStyle)
247         return { };
248
249     PseudoElement* pseudoElement = pseudoId == PseudoId::Before ? element.beforePseudoElement() : element.afterPseudoElement();
250     if (!pseudoElement) {
251         auto newPseudoElement = PseudoElement::create(element, pseudoId);
252         pseudoElement = newPseudoElement.ptr();
253         if (pseudoId == PseudoId::Before)
254             element.setBeforePseudoElement(WTFMove(newPseudoElement));
255         else
256             element.setAfterPseudoElement(WTFMove(newPseudoElement));
257     }
258
259     return createAnimatedElementUpdate(WTFMove(pseudoStyle), *pseudoElement, elementUpdate.change);
260 }
261
262 const RenderStyle* TreeResolver::parentBoxStyle() const
263 {
264     // 'display: contents' doesn't generate boxes.
265     for (unsigned i = m_parentStack.size(); i; --i) {
266         auto& parent = m_parentStack[i - 1];
267         if (parent.style.display() == DisplayType::None)
268             return nullptr;
269         if (parent.style.display() != DisplayType::Contents)
270             return &parent.style;
271     }
272     ASSERT_NOT_REACHED();
273     return nullptr;
274 }
275
276 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange)
277 {
278     auto* oldStyle = element.renderOrDisplayContentsStyle();
279
280     bool shouldRecompositeLayer = false;
281
282     // New code path for CSS Animations and CSS Transitions.
283     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
284         // First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation
285         // on the document timeline. Note that we get timeline() on the Document here because we need a timeline created
286         // in case no Web Animations have been created through the JS API.
287         if (element.document().pageCacheState() == Document::NotInPageCache && !element.document().renderView()->printing()) {
288             if (oldStyle && (oldStyle->hasTransitions() || newStyle->hasTransitions()))
289                 m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle);
290
291             if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
292                 m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle);
293         }
294     }
295
296     if (auto timeline = m_document.existingTimeline()) {
297         // Now we can update all Web animations, which will include CSS Animations as well
298         // as animations created via the JS API.
299         auto animatedStyle = RenderStyle::clonePtr(*newStyle);
300         shouldRecompositeLayer = timeline->resolveAnimationsForElement(element, *animatedStyle);
301         newStyle = WTFMove(animatedStyle);
302     }
303
304     // Old code path for CSS Animations and CSS Transitions.
305     if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
306         auto& animationController = m_document.frame()->animation();
307
308         auto animationUpdate = animationController.updateAnimations(element, *newStyle, oldStyle);
309         shouldRecompositeLayer = animationUpdate.stateChanged;
310
311         if (animationUpdate.style)
312             newStyle = WTFMove(animationUpdate.style);
313     }
314
315     auto change = oldStyle ? determineChange(*oldStyle, *newStyle) : Detach;
316
317     auto validity = element.styleValidity();
318     if (validity >= Validity::SubtreeAndRenderersInvalid || parentChange == Detach)
319         change = Detach;
320
321     shouldRecompositeLayer |= element.styleResolutionShouldRecompositeLayer();
322
323     return { WTFMove(newStyle), change, shouldRecompositeLayer };
324 }
325
326 void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve)
327 {
328     scope().selectorFilter.pushParent(&element);
329
330     Parent parent(element, style, change, descendantsToResolve);
331
332     if (auto* shadowRoot = element.shadowRoot()) {
333         pushScope(*shadowRoot);
334         parent.didPushScope = true;
335     }
336     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
337         pushEnclosingScope();
338         parent.didPushScope = true;
339     }
340
341     m_parentStack.append(WTFMove(parent));
342 }
343
344 void TreeResolver::popParent()
345 {
346     auto& parentElement = *parent().element;
347
348     parentElement.setHasValidStyle();
349     parentElement.clearChildNeedsStyleRecalc();
350
351     if (parent().didPushScope)
352         popScope();
353
354     scope().selectorFilter.popParent();
355
356     m_parentStack.removeLast();
357 }
358
359 void TreeResolver::popParentsToDepth(unsigned depth)
360 {
361     ASSERT(depth);
362     ASSERT(m_parentStack.size() >= depth);
363
364     while (m_parentStack.size() > depth)
365         popParent();
366 }
367
368 static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement)
369 {
370     if (!pseudoElement)
371         return false;
372     return pseudoElement->needsStyleRecalc();
373 }
374
375 static bool shouldResolveElement(const Element& element, DescendantsToResolve parentDescendantsToResolve)
376 {
377     if (element.styleValidity() != Validity::Valid)
378         return true;
379     if (shouldResolvePseudoElement(element.beforePseudoElement()))
380         return true;
381     if (shouldResolvePseudoElement(element.afterPseudoElement()))
382         return true;
383
384     switch (parentDescendantsToResolve) {
385     case DescendantsToResolve::None:
386         return false;
387     case DescendantsToResolve::Children:
388     case DescendantsToResolve::All:
389         return true;
390     case DescendantsToResolve::ChildrenWithExplicitInherit:
391         auto* existingStyle = element.renderOrDisplayContentsStyle();
392         return existingStyle && existingStyle->hasExplicitlyInheritedProperties();
393     };
394     ASSERT_NOT_REACHED();
395     return false;
396 }
397
398 static void clearNeedsStyleResolution(Element& element)
399 {
400     element.setHasValidStyle();
401     if (auto* before = element.beforePseudoElement())
402         before->setHasValidStyle();
403     if (auto* after = element.afterPseudoElement())
404         after->setHasValidStyle();
405 }
406
407 static bool hasLoadingStylesheet(const Style::Scope& styleScope, const Element& element, bool checkDescendants)
408 {
409     if (!styleScope.hasPendingSheetsInBody())
410         return false;
411     if (styleScope.hasPendingSheetInBody(element))
412         return true;
413     if (!checkDescendants)
414         return false;
415     for (auto& descendant : descendantsOfType<Element>(element)) {
416         if (styleScope.hasPendingSheetInBody(descendant))
417             return true;
418     };
419     return false;
420 }
421
422 static std::unique_ptr<RenderStyle> createInheritedDisplayContentsStyleIfNeeded(const RenderStyle& parentElementStyle, const RenderStyle* parentBoxStyle)
423 {
424     if (parentElementStyle.display() != DisplayType::Contents)
425         return nullptr;
426     if (parentBoxStyle && !parentBoxStyle->inheritedNotEqual(&parentElementStyle))
427         return nullptr;
428     // Compute style for imaginary unstyled <span> around the text node.
429     auto style = RenderStyle::createPtr();
430     style->inheritFrom(parentElementStyle);
431     return style;
432 }
433
434 void TreeResolver::resolveComposedTree()
435 {
436     ASSERT(m_parentStack.size() == 1);
437     ASSERT(m_scopeStack.size() == 1);
438
439     auto descendants = composedTreeDescendants(m_document);
440     auto it = descendants.begin();
441     auto end = descendants.end();
442
443     while (it != end) {
444         popParentsToDepth(it.depth());
445
446         auto& node = *it;
447         auto& parent = this->parent();
448
449         ASSERT(node.isConnected());
450         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
451         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
452
453         if (is<Text>(node)) {
454             auto& text = downcast<Text>(node);
455             
456             if ((text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach) || parent.style.display() == DisplayType::Contents) {
457                 TextUpdate textUpdate;
458                 textUpdate.inheritedDisplayContentsStyle = createInheritedDisplayContentsStyleIfNeeded(parent.style, parentBoxStyle());
459
460                 m_update->addText(text, parent.element, WTFMove(textUpdate));
461             }
462
463             text.setHasValidStyle();
464             it.traverseNextSkippingChildren();
465             continue;
466         }
467
468         auto& element = downcast<Element>(node);
469
470         if (it.depth() > Settings::defaultMaximumRenderTreeDepth) {
471             resetStyleForNonRenderedDescendants(element);
472             it.traverseNextSkippingChildren();
473             continue;
474         }
475
476         auto* style = element.renderOrDisplayContentsStyle();
477         auto change = NoChange;
478         auto descendantsToResolve = DescendantsToResolve::None;
479
480         bool shouldResolve = shouldResolveElement(element, parent.descendantsToResolve);
481         if (shouldResolve) {
482             if (!element.hasDisplayContents())
483                 element.resetComputedStyle();
484             element.resetStyleRelations();
485
486             if (element.hasCustomStyleResolveCallbacks())
487                 element.willRecalcStyle(parent.change);
488
489             auto elementUpdates = resolveElement(element);
490
491             if (element.hasCustomStyleResolveCallbacks())
492                 element.didRecalcStyle(elementUpdates.update.change);
493
494             style = elementUpdates.update.style.get();
495             change = elementUpdates.update.change;
496             descendantsToResolve = elementUpdates.descendantsToResolve;
497
498             if (elementUpdates.update.style)
499                 m_update->addElement(element, parent.element, WTFMove(elementUpdates));
500
501             clearNeedsStyleResolution(element);
502         }
503
504         if (!style)
505             resetStyleForNonRenderedDescendants(element);
506
507         bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || descendantsToResolve != DescendantsToResolve::None);
508
509         if (!m_didSeePendingStylesheet)
510             m_didSeePendingStylesheet = hasLoadingStylesheet(m_document.styleScope(), element, !shouldIterateChildren);
511
512         if (!shouldIterateChildren) {
513             it.traverseNextSkippingChildren();
514             continue;
515         }
516
517         pushParent(element, *style, change, descendantsToResolve);
518
519         it.traverseNext();
520     }
521
522     popParentsToDepth(1);
523 }
524
525 std::unique_ptr<Update> TreeResolver::resolve()
526 {
527     auto& renderView = *m_document.renderView();
528
529     Element* documentElement = m_document.documentElement();
530     if (!documentElement) {
531         m_document.styleScope().resolver();
532         return nullptr;
533     }
534     if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
535         return nullptr;
536
537     m_didSeePendingStylesheet = m_document.styleScope().hasPendingSheetsBeforeBody();
538
539     m_update = std::make_unique<Update>(m_document);
540     m_scopeStack.append(adoptRef(*new Scope(m_document)));
541     m_parentStack.append(Parent(m_document));
542
543     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
544     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
545     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
546
547     resolveComposedTree();
548
549     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
550     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
551
552     ASSERT(m_scopeStack.size() == 1);
553     ASSERT(m_parentStack.size() == 1);
554     m_parentStack.clear();
555     popScope();
556
557     if (m_update->roots().isEmpty())
558         return { };
559
560     return WTFMove(m_update);
561 }
562
563 static Vector<Function<void ()>>& postResolutionCallbackQueue()
564 {
565     static NeverDestroyed<Vector<Function<void ()>>> vector;
566     return vector;
567 }
568
569 static Vector<RefPtr<Frame>>& memoryCacheClientCallsResumeQueue()
570 {
571     static NeverDestroyed<Vector<RefPtr<Frame>>> vector;
572     return vector;
573 }
574
575 void queuePostResolutionCallback(Function<void ()>&& callback)
576 {
577     postResolutionCallbackQueue().append(WTFMove(callback));
578 }
579
580 static void suspendMemoryCacheClientCalls(Document& document)
581 {
582     Page* page = document.page();
583     if (!page || !page->areMemoryCacheClientCallsEnabled())
584         return;
585
586     page->setMemoryCacheClientCallsEnabled(false);
587
588     memoryCacheClientCallsResumeQueue().append(&page->mainFrame());
589 }
590
591 static unsigned resolutionNestingDepth;
592
593 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document, DrainCallbacks drainCallbacks)
594     : m_drainCallbacks(drainCallbacks)
595 {
596     ++resolutionNestingDepth;
597
598     if (resolutionNestingDepth == 1)
599         platformStrategies()->loaderStrategy()->suspendPendingRequests();
600
601     // FIXME: It's strange to build this into the disabler.
602     suspendMemoryCacheClientCalls(document);
603 }
604
605 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
606 {
607     if (resolutionNestingDepth == 1) {
608         if (m_drainCallbacks == DrainCallbacks::Yes) {
609             // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
610             auto& queue = postResolutionCallbackQueue();
611             for (size_t i = 0; i < queue.size(); ++i)
612                 queue[i]();
613             queue.clear();
614         }
615
616         auto& queue = memoryCacheClientCallsResumeQueue();
617         for (size_t i = 0; i < queue.size(); ++i) {
618             if (auto* page = queue[i]->page())
619                 page->setMemoryCacheClientCallsEnabled(true);
620         }
621         queue.clear();
622
623         platformStrategies()->loaderStrategy()->resumePendingRequests();
624     }
625
626     --resolutionNestingDepth;
627 }
628
629 bool postResolutionCallbacksAreSuspended()
630 {
631     return resolutionNestingDepth;
632 }
633
634 }
635 }