0c19c620f14a5420cd956bcbae25e7f4d4c7a9d0
[WebKit-https.git] / Source / WebCore / style / StyleTreeResolver.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  *           (C) 2007 David Smith (catfish.man@gmail.com)
7  * Copyright (C) 2004-2010, 2012-2016 Apple Inc. All rights reserved.
8  *           (C) 2007 Eric Seidel (eric@webkit.org)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "StyleTreeResolver.h"
28
29 #include "AuthorStyleSheets.h"
30 #include "CSSFontSelector.h"
31 #include "ComposedTreeAncestorIterator.h"
32 #include "ComposedTreeIterator.h"
33 #include "ElementIterator.h"
34 #include "HTMLBodyElement.h"
35 #include "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 "Settings.h"
45 #include "ShadowRoot.h"
46 #include "StyleFontSizeFunctions.h"
47 #include "StyleResolver.h"
48 #include "Text.h"
49
50 #if PLATFORM(IOS)
51 #include "WKContentObservation.h"
52 #endif
53
54 namespace WebCore {
55
56 namespace Style {
57
58 static RenderStyle* placeholderStyle;
59
60 static void ensurePlaceholderStyle(Document& document)
61 {
62     if (placeholderStyle)
63         return;
64     placeholderStyle = RenderStyle::createPtr().release();
65     placeholderStyle->setDisplay(NONE);
66     placeholderStyle->setIsPlaceholderStyle();
67
68     FontCascadeDescription fontDescription;
69     fontDescription.setOneFamily(standardFamily);
70     fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);
71     float size = Style::fontSizeForKeyword(CSSValueMedium, false, document);
72     fontDescription.setSpecifiedSize(size);
73     fontDescription.setComputedSize(size);
74     placeholderStyle->setFontDescription(fontDescription);
75
76     placeholderStyle->fontCascade().update(&document.fontSelector());
77 }
78
79 TreeResolver::TreeResolver(Document& document)
80     : m_document(document)
81 {
82     ensurePlaceholderStyle(document);
83 }
84
85 TreeResolver::~TreeResolver()
86 {
87 }
88
89 TreeResolver::Scope::Scope(Document& document)
90     : styleResolver(document.ensureStyleResolver())
91     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
92 {
93 }
94
95 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
96     : styleResolver(shadowRoot.styleResolver())
97     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
98     , shadowRoot(&shadowRoot)
99     , enclosingScope(&enclosingScope)
100 {
101 }
102
103 TreeResolver::Parent::Parent(Document& document, Change change)
104     : element(nullptr)
105     , style(*document.renderStyle())
106     , change(change)
107 {
108 }
109
110 TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change)
111     : element(&element)
112     , style(style)
113     , change(change)
114 {
115 }
116
117 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
118 {
119     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
120     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
121 }
122
123 void TreeResolver::pushEnclosingScope()
124 {
125     ASSERT(scope().enclosingScope);
126     m_scopeStack.append(*scope().enclosingScope);
127     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
128 }
129
130 void TreeResolver::popScope()
131 {
132     scope().styleResolver.setOverrideDocumentElementStyle(nullptr);
133     return m_scopeStack.removeLast();
134 }
135
136 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle)
137 {
138     if (!m_document.haveStylesheetsLoaded() && !element.renderer()) {
139         m_document.setHasNodesWithPlaceholderStyle();
140         return RenderStyle::clonePtr(*placeholderStyle);
141     }
142
143     if (element.hasCustomStyleResolveCallbacks()) {
144         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
145         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
146             if (customStyle->relations)
147                 commitRelations(WTFMove(customStyle->relations), *m_update);
148
149             return WTFMove(customStyle->renderStyle);
150         }
151     }
152
153     if (auto style = scope().sharingResolver.resolve(element, *m_update))
154         return style;
155
156     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, MatchAllRules, nullptr, &scope().selectorFilter);
157
158     if (elementStyle.relations)
159         commitRelations(WTFMove(elementStyle.relations), *m_update);
160
161     return WTFMove(elementStyle.renderStyle);
162 }
163
164 static void resetStyleForNonRenderedDescendants(Element& current)
165 {
166     // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator.
167     bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
168     for (auto& child : childrenOfType<Element>(current)) {
169         bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
170         if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
171             elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
172
173         if (child.needsStyleRecalc() || affectedByPreviousSibling) {
174             child.resetComputedStyle();
175             child.clearNeedsStyleRecalc();
176         }
177
178         if (child.childNeedsStyleRecalc()) {
179             resetStyleForNonRenderedDescendants(child);
180             child.clearChildNeedsStyleRecalc();
181         }
182     }
183 }
184
185 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
186 {
187     if (element.renderer())
188         return true;
189     if (newStyle.display() != NONE)
190         return true;
191     if (element.rendererIsNeeded(newStyle))
192         return true;
193 #if ENABLE(CSS_REGIONS)
194     if (element.shouldMoveToFlowThread(newStyle))
195         return true;
196 #endif
197     return false;
198 }
199
200 ElementUpdate TreeResolver::resolveElement(Element& element)
201 {
202     auto newStyle = styleForElement(element, parent().style);
203
204     if (!affectsRenderedSubtree(element, *newStyle))
205         return { };
206
207     bool shouldReconstructRenderTree = element.styleChangeType() == ReconstructRenderTree || parent().change == Detach;
208     auto* rendererToUpdate = shouldReconstructRenderTree ? nullptr : element.renderer();
209
210     auto update = createAnimatedElementUpdate(WTFMove(newStyle), rendererToUpdate, m_document);
211
212     if (element.styleChangeType() == SyntheticStyleChange)
213         update.isSynthetic = true;
214
215     auto* existingStyle = element.renderStyle();
216
217     if (&element == m_document.documentElement()) {
218         m_documentElementStyle = RenderStyle::clonePtr(*update.style);
219         scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
220
221         // If "rem" units are used anywhere in the document, and if the document element's font size changes, then force font updating
222         // all the way down the tree. This is simpler than having to maintain a cache of objects (and such font size changes should be rare anyway).
223         if (m_document.authorStyleSheets().usesRemUnits() && update.change != NoChange && existingStyle && existingStyle->fontSize() != update.style->fontSize()) {
224             // Cached RenderStyles may depend on the rem units.
225             scope().styleResolver.invalidateMatchedPropertiesCache();
226             update.change = Force;
227         }
228     }
229
230     // This is needed for resolving color:-webkit-text for subsequent elements.
231     // FIXME: We shouldn't mutate document when resolving style.
232     if (&element == m_document.body())
233         m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor));
234
235     // FIXME: These elements should not change renderer based on appearance property.
236     if (element.hasTagName(HTMLNames::meterTag) || is<HTMLProgressElement>(element)) {
237         if (existingStyle && update.style->appearance() != existingStyle->appearance())
238             update.change = Detach;
239     }
240
241     if (update.change != Detach && (parent().change == Force || element.styleChangeType() >= FullStyleChange))
242         update.change = Force;
243
244     return update;
245 }
246
247 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, RenderElement* rendererToUpdate, Document& document)
248 {
249     ElementUpdate update;
250
251     std::unique_ptr<RenderStyle> animatedStyle;
252     if (rendererToUpdate && document.frame()->animation().updateAnimations(*rendererToUpdate, *newStyle, animatedStyle))
253         update.isSynthetic = true;
254
255     if (animatedStyle) {
256         update.change = determineChange(rendererToUpdate->style(), *animatedStyle);
257         // If animation forces render tree reconstruction pass the original style. The animation will be applied on renderer construction.
258         // FIXME: We should always use the animated style here.
259         update.style = update.change == Detach ? WTFMove(newStyle) : WTFMove(animatedStyle);
260     } else {
261         update.change = rendererToUpdate ? determineChange(rendererToUpdate->style(), *newStyle) : Detach;
262         update.style = WTFMove(newStyle);
263     }
264
265     return update;
266 }
267
268 #if PLATFORM(IOS)
269 static EVisibility elementImplicitVisibility(const Element* element)
270 {
271     RenderObject* renderer = element->renderer();
272     if (!renderer)
273         return VISIBLE;
274
275     auto& style = renderer->style();
276
277     Length width(style.width());
278     Length height(style.height());
279     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
280         return HIDDEN;
281
282     Length top(style.top());
283     Length left(style.left());
284     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
285         return HIDDEN;
286
287     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
288         return HIDDEN;
289     return VISIBLE;
290 }
291
292 class CheckForVisibilityChangeOnRecalcStyle {
293 public:
294     CheckForVisibilityChangeOnRecalcStyle(Element* element, const RenderStyle* currentStyle)
295         : m_element(element)
296         , m_previousDisplay(currentStyle ? currentStyle->display() : NONE)
297         , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN)
298         , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
299     {
300     }
301     ~CheckForVisibilityChangeOnRecalcStyle()
302     {
303         if (!WKObservingContentChanges())
304             return;
305         if (m_element->isInUserAgentShadowTree())
306             return;
307         auto* style = m_element->renderStyle();
308         if (!style)
309             return;
310         if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
311             || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE))
312             WKSetObservedContentChange(WKContentVisibilityChange);
313     }
314 private:
315     RefPtr<Element> m_element;
316     EDisplay m_previousDisplay;
317     EVisibility m_previousVisibility;
318     EVisibility m_previousImplicitVisibility;
319 };
320 #endif // PLATFORM(IOS)
321
322 void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change)
323 {
324     scope().selectorFilter.pushParent(&element);
325
326     Parent parent(element, style, change);
327
328     if (auto* shadowRoot = element.shadowRoot()) {
329         pushScope(*shadowRoot);
330         parent.didPushScope = true;
331     }
332     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
333         pushEnclosingScope();
334         parent.didPushScope = true;
335     }
336
337     m_parentStack.append(WTFMove(parent));
338 }
339
340 void TreeResolver::popParent()
341 {
342     auto& parentElement = *parent().element;
343
344     parentElement.clearNeedsStyleRecalc();
345     parentElement.clearChildNeedsStyleRecalc();
346
347     if (parent().didPushScope)
348         popScope();
349
350     scope().selectorFilter.popParent();
351
352     m_parentStack.removeLast();
353 }
354
355 void TreeResolver::popParentsToDepth(unsigned depth)
356 {
357     ASSERT(depth);
358     ASSERT(m_parentStack.size() >= depth);
359
360     while (m_parentStack.size() > depth)
361         popParent();
362 }
363
364 static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement)
365 {
366     if (!pseudoElement)
367         return false;
368     return pseudoElement->needsStyleRecalc();
369 }
370
371 static bool shouldResolveElement(const Element& element, Style::Change parentChange)
372 {
373     if (parentChange >= Inherit)
374         return true;
375     if (parentChange == NoInherit) {
376         auto* existingStyle = element.renderStyle();
377         if (existingStyle && existingStyle->hasExplicitlyInheritedProperties())
378             return true;
379     }
380     if (element.needsStyleRecalc())
381         return true;
382     if (element.hasDisplayContents())
383         return true;
384     if (shouldResolvePseudoElement(element.beforePseudoElement()))
385         return true;
386     if (shouldResolvePseudoElement(element.afterPseudoElement()))
387         return true;
388
389     return false;
390 }
391
392 static void clearNeedsStyleResolution(Element& element)
393 {
394     element.clearNeedsStyleRecalc();
395     if (auto* before = element.beforePseudoElement())
396         before->clearNeedsStyleRecalc();
397     if (auto* after = element.afterPseudoElement())
398         after->clearNeedsStyleRecalc();
399 }
400
401 void TreeResolver::resolveComposedTree()
402 {
403     ASSERT(m_parentStack.size() == 1);
404     ASSERT(m_scopeStack.size() == 1);
405
406     auto descendants = composedTreeDescendants(m_document);
407     auto it = descendants.begin();
408     auto end = descendants.end();
409
410     // FIXME: SVG <use> element may cause tree mutations during style recalc.
411     it.dropAssertions();
412
413     while (it != end) {
414         popParentsToDepth(it.depth());
415
416         auto& node = *it;
417         auto& parent = this->parent();
418
419         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
420         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
421
422         if (is<Text>(node)) {
423             auto& text = downcast<Text>(node);
424             if (text.styleChangeType() == ReconstructRenderTree && parent.change != Detach)
425                 m_update->addText(text, parent.element);
426
427             text.clearNeedsStyleRecalc();
428             it.traverseNextSkippingChildren();
429             continue;
430         }
431
432         auto& element = downcast<Element>(node);
433
434         if (it.depth() > Settings::defaultMaximumRenderTreeDepth) {
435             resetStyleForNonRenderedDescendants(element);
436             element.clearChildNeedsStyleRecalc();
437             it.traverseNextSkippingChildren();
438             continue;
439         }
440
441         // FIXME: We should deal with this during style invalidation.
442         bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
443         if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
444             parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle();
445
446         auto* style = element.renderStyle();
447         auto change = NoChange;
448
449         bool shouldResolve = shouldResolveElement(element, parent.change) || affectedByPreviousSibling;
450         if (shouldResolve) {
451 #if PLATFORM(IOS)
452             CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle());
453 #endif
454             element.resetComputedStyle();
455
456             if (element.hasCustomStyleResolveCallbacks()) {
457                 if (!element.willRecalcStyle(parent.change)) {
458                     it.traverseNextSkippingChildren();
459                     continue;
460                 }
461             }
462
463             auto elementUpdate = resolveElement(element);
464
465             if (element.hasCustomStyleResolveCallbacks())
466                 element.didRecalcStyle(elementUpdate.change);
467
468             style = elementUpdate.style.get();
469             change = elementUpdate.change;
470
471             if (affectedByPreviousSibling && change != Detach)
472                 change = Force;
473
474             if (elementUpdate.style)
475                 m_update->addElement(element, parent.element, WTFMove(elementUpdate));
476
477             clearNeedsStyleResolution(element);
478         }
479
480         if (!style) {
481             resetStyleForNonRenderedDescendants(element);
482             element.clearChildNeedsStyleRecalc();
483         }
484
485         bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || change != NoChange);
486         if (!shouldIterateChildren) {
487             it.traverseNextSkippingChildren();
488             continue;
489         }
490
491         pushParent(element, *style, change);
492
493         it.traverseNext();
494     }
495
496     popParentsToDepth(1);
497 }
498
499 std::unique_ptr<Update> TreeResolver::resolve(Change change)
500 {
501     auto& renderView = *m_document.renderView();
502
503     Element* documentElement = m_document.documentElement();
504     if (!documentElement) {
505         m_document.ensureStyleResolver();
506         return nullptr;
507     }
508     if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
509         return nullptr;
510
511     m_update = std::make_unique<Update>(m_document);
512     m_scopeStack.append(adoptRef(*new Scope(m_document)));
513     m_parentStack.append(Parent(m_document, change));
514
515     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
516     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
517     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
518
519     resolveComposedTree();
520
521     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
522     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
523
524     ASSERT(m_scopeStack.size() == 1);
525     ASSERT(m_parentStack.size() == 1);
526     m_parentStack.clear();
527     popScope();
528
529     if (m_update->roots().isEmpty())
530         return { };
531
532     return WTFMove(m_update);
533 }
534
535 static Vector<Function<void ()>>& postResolutionCallbackQueue()
536 {
537     static NeverDestroyed<Vector<Function<void ()>>> vector;
538     return vector;
539 }
540
541 void queuePostResolutionCallback(Function<void ()>&& callback)
542 {
543     postResolutionCallbackQueue().append(WTFMove(callback));
544 }
545
546 static void suspendMemoryCacheClientCalls(Document& document)
547 {
548     Page* page = document.page();
549     if (!page || !page->areMemoryCacheClientCallsEnabled())
550         return;
551
552     page->setMemoryCacheClientCallsEnabled(false);
553
554     postResolutionCallbackQueue().append([protectedMainFrame = Ref<MainFrame>(page->mainFrame())] {
555         if (Page* page = protectedMainFrame->page())
556             page->setMemoryCacheClientCallsEnabled(true);
557     });
558 }
559
560 static unsigned resolutionNestingDepth;
561
562 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document)
563 {
564     ++resolutionNestingDepth;
565
566     if (resolutionNestingDepth == 1)
567         platformStrategies()->loaderStrategy()->suspendPendingRequests();
568
569     // FIXME: It's strange to build this into the disabler.
570     suspendMemoryCacheClientCalls(document);
571 }
572
573 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
574 {
575     if (resolutionNestingDepth == 1) {
576         // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
577         auto& queue = postResolutionCallbackQueue();
578         for (size_t i = 0; i < queue.size(); ++i)
579             queue[i]();
580         queue.clear();
581
582         platformStrategies()->loaderStrategy()->resumePendingRequests();
583     }
584
585     --resolutionNestingDepth;
586 }
587
588 bool postResolutionCallbacksAreSuspended()
589 {
590     return resolutionNestingDepth;
591 }
592
593 bool isPlaceholderStyle(const RenderStyle& style)
594 {
595     return &style == placeholderStyle;
596 }
597
598 }
599 }