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