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