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