Source/WebCore:
[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 "HTMLSlotElement.h"
36 #include "LoaderStrategy.h"
37 #include "MainFrame.h"
38 #include "NodeRenderStyle.h"
39 #include "PlatformStrategies.h"
40 #include "Settings.h"
41 #include "ShadowRoot.h"
42 #include "StyleResolver.h"
43 #include "Text.h"
44
45 #if PLATFORM(IOS)
46 #include "WKContentObservation.h"
47 #endif
48
49 namespace WebCore {
50
51 namespace Style {
52
53 static RenderStyle* placeholderStyle;
54
55 static void ensurePlaceholderStyle(Document& document)
56 {
57     if (placeholderStyle)
58         return;
59     placeholderStyle = &RenderStyle::create().leakRef();
60     placeholderStyle->setDisplay(NONE);
61     placeholderStyle->fontCascade().update(&document.fontSelector());
62 }
63
64 TreeResolver::TreeResolver(Document& document)
65     : m_document(document)
66 {
67     ensurePlaceholderStyle(document);
68 }
69
70 TreeResolver::~TreeResolver()
71 {
72 }
73
74 TreeResolver::Scope::Scope(Document& document)
75     : styleResolver(document.ensureStyleResolver())
76     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
77 {
78 }
79
80 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
81     : styleResolver(shadowRoot.styleResolver())
82     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
83     , shadowRoot(&shadowRoot)
84     , enclosingScope(&enclosingScope)
85 {
86 }
87
88 TreeResolver::Parent::Parent(Document& document, Change change)
89     : element(nullptr)
90     , style(*document.renderStyle())
91     , change(change)
92 {
93 }
94
95 TreeResolver::Parent::Parent(Element& element, ElementUpdate& update)
96     : element(&element)
97     , style(*update.style)
98     , change(update.change)
99 {
100 }
101
102 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
103 {
104     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
105 }
106
107 void TreeResolver::pushEnclosingScope()
108 {
109     ASSERT(scope().enclosingScope);
110     m_scopeStack.append(*scope().enclosingScope);
111 }
112
113 void TreeResolver::popScope()
114 {
115     return m_scopeStack.removeLast();
116 }
117
118 Ref<RenderStyle> TreeResolver::styleForElement(Element& element, RenderStyle& inheritedStyle)
119 {
120     if (!m_document.haveStylesheetsLoaded() && !element.renderer()) {
121         m_document.setHasNodesWithPlaceholderStyle();
122         return *placeholderStyle;
123     }
124
125     scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
126
127     if (element.hasCustomStyleResolveCallbacks()) {
128         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
129         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
130             if (customStyle->relations)
131                 commitRelations(WTFMove(customStyle->relations), *m_update);
132
133             return WTFMove(customStyle->renderStyle);
134         }
135     }
136
137     if (auto style = scope().sharingResolver.resolve(element, *m_update))
138         return *style;
139
140     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, MatchAllRules, nullptr, &scope().selectorFilter);
141
142     if (elementStyle.relations)
143         commitRelations(WTFMove(elementStyle.relations), *m_update);
144
145     return WTFMove(elementStyle.renderStyle);
146 }
147
148 static void resetStyleForNonRenderedDescendants(Element& current)
149 {
150     // FIXME: This is not correct with shadow trees. This should be done with ComposedTreeIterator.
151     bool elementNeedingStyleRecalcAffectsNextSiblingElementStyle = false;
152     for (auto& child : childrenOfType<Element>(current)) {
153         bool affectedByPreviousSibling = child.styleIsAffectedByPreviousSibling() && elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
154         if (child.needsStyleRecalc() || elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
155             elementNeedingStyleRecalcAffectsNextSiblingElementStyle = child.affectsNextSiblingElementStyle();
156
157         if (child.needsStyleRecalc() || affectedByPreviousSibling) {
158             child.resetComputedStyle();
159             child.clearNeedsStyleRecalc();
160         }
161
162         if (child.childNeedsStyleRecalc()) {
163             resetStyleForNonRenderedDescendants(child);
164             child.clearChildNeedsStyleRecalc();
165         }
166     }
167 }
168
169 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
170 {
171     if (element.renderer())
172         return true;
173     if (newStyle.display() != NONE)
174         return true;
175     if (element.rendererIsNeeded(newStyle))
176         return true;
177     if (element.shouldMoveToFlowThread(newStyle))
178         return true;
179     return false;
180 }
181
182 ElementUpdate TreeResolver::resolveElement(Element& element)
183 {
184     auto newStyle = styleForElement(element, parent().style);
185
186     auto* renderer = element.renderer();
187
188     if (!affectsRenderedSubtree(element, newStyle.get()))
189         return { };
190
191     ElementUpdate update;
192
193     bool needsNewRenderer = !renderer || element.styleChangeType() == ReconstructRenderTree || parent().change == Detach;
194     if (!needsNewRenderer && m_document.frame()->animation().updateAnimations(*renderer, newStyle, newStyle))
195         update.isSynthetic = true;
196
197     update.change = needsNewRenderer ? Detach : determineChange(renderer->style(), newStyle);
198     update.style = WTFMove(newStyle);
199
200     if (element.styleChangeType() == SyntheticStyleChange)
201         update.isSynthetic = true;
202
203     if (&element == m_document.documentElement()) {
204         m_documentElementStyle = update.style;
205
206         // If "rem" units are used anywhere in the document, and if the document element's font size changes, then force font updating
207         // 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).
208         if (m_document.authorStyleSheets().usesRemUnits() && update.change != NoChange && renderer && renderer->style().fontSize() != update.style->fontSize()) {
209             // Cached RenderStyles may depend on the rem units.
210             scope().styleResolver.invalidateMatchedPropertiesCache();
211             update.change = Force;
212         }
213     }
214
215     // This is needed for resolving color:-webkit-text for subsequent elements.
216     // FIXME: We shouldn't mutate document when resolving style.
217     if (&element == m_document.body())
218         m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor));
219
220     if (update.change != Detach && (parent().change == Force || element.styleChangeType() >= FullStyleChange))
221         update.change = Force;
222
223     return update;
224 }
225
226 #if PLATFORM(IOS)
227 static EVisibility elementImplicitVisibility(const Element* element)
228 {
229     RenderObject* renderer = element->renderer();
230     if (!renderer)
231         return VISIBLE;
232
233     RenderStyle& style = renderer->style();
234
235     Length width(style.width());
236     Length height(style.height());
237     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
238         return HIDDEN;
239
240     Length top(style.top());
241     Length left(style.left());
242     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
243         return HIDDEN;
244
245     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
246         return HIDDEN;
247     return VISIBLE;
248 }
249
250 class CheckForVisibilityChangeOnRecalcStyle {
251 public:
252     CheckForVisibilityChangeOnRecalcStyle(Element* element, RenderStyle* currentStyle)
253         : m_element(element)
254         , m_previousDisplay(currentStyle ? currentStyle->display() : NONE)
255         , m_previousVisibility(currentStyle ? currentStyle->visibility() : HIDDEN)
256         , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
257     {
258     }
259     ~CheckForVisibilityChangeOnRecalcStyle()
260     {
261         if (!WKObservingContentChanges())
262             return;
263         if (m_element->isInUserAgentShadowTree())
264             return;
265         RenderStyle* style = m_element->renderStyle();
266         if (!style)
267             return;
268         if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
269             || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element.get()) == VISIBLE))
270             WKSetObservedContentChange(WKContentVisibilityChange);
271     }
272 private:
273     RefPtr<Element> m_element;
274     EDisplay m_previousDisplay;
275     EVisibility m_previousVisibility;
276     EVisibility m_previousImplicitVisibility;
277 };
278 #endif // PLATFORM(IOS)
279
280 void TreeResolver::pushParent(Element& element, ElementUpdate& update)
281 {
282     scope().selectorFilter.pushParent(&element);
283
284     Parent parent(element, update);
285
286     if (auto* shadowRoot = element.shadowRoot()) {
287         pushScope(*shadowRoot);
288         parent.didPushScope = true;
289     }
290 #if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
291     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
292         pushEnclosingScope();
293         parent.didPushScope = true;
294     }
295 #endif
296
297     m_parentStack.append(WTFMove(parent));
298 }
299
300 void TreeResolver::popParent()
301 {
302     auto& parentElement = *parent().element;
303
304     parentElement.clearNeedsStyleRecalc();
305     parentElement.clearChildNeedsStyleRecalc();
306
307     if (parent().didPushScope)
308         popScope();
309
310     scope().selectorFilter.popParent();
311
312     m_parentStack.removeLast();
313 }
314
315 void TreeResolver::popParentsToDepth(unsigned depth)
316 {
317     ASSERT(depth);
318     ASSERT(m_parentStack.size() >= depth);
319
320     while (m_parentStack.size() > depth)
321         popParent();
322 }
323
324 static bool shouldResolvePseudoElement(PseudoElement* pseudoElement)
325 {
326     if (!pseudoElement)
327         return false;
328     bool needsStyleRecalc = pseudoElement->needsStyleRecalc();
329     pseudoElement->clearNeedsStyleRecalc();
330     return needsStyleRecalc;
331 }
332
333 void TreeResolver::resolveComposedTree()
334 {
335     ASSERT(m_parentStack.size() == 1);
336     ASSERT(m_scopeStack.size() == 1);
337
338     auto descendants = composedTreeDescendants(m_document);
339     auto it = descendants.begin();
340     auto end = descendants.end();
341
342     // FIXME: SVG <use> element may cause tree mutations during style recalc.
343     it.dropAssertions();
344
345     while (it != end) {
346         popParentsToDepth(it.depth());
347
348         auto& node = *it;
349         auto& parent = this->parent();
350
351         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
352         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
353
354         if (is<Text>(node)) {
355             auto& text = downcast<Text>(node);
356             if (text.styleChangeType() == ReconstructRenderTree && parent.change != Detach)
357                 m_update->addText(text, parent.element);
358
359             text.clearNeedsStyleRecalc();
360             it.traverseNextSkippingChildren();
361             continue;
362         }
363
364         auto& element = downcast<Element>(node);
365
366         // FIXME: We should deal with this during style invalidation.
367         bool affectedByPreviousSibling = element.styleIsAffectedByPreviousSibling() && parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle;
368         if (element.needsStyleRecalc() || parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle)
369             parent.elementNeedingStyleRecalcAffectsNextSiblingElementStyle = element.affectsNextSiblingElementStyle();
370
371         bool shouldResolveForPseudoElement = shouldResolvePseudoElement(element.beforePseudoElement()) || shouldResolvePseudoElement(element.afterPseudoElement());
372
373         ElementUpdate update;
374         update.style = element.renderStyle();
375
376         bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || shouldResolveForPseudoElement || affectedByPreviousSibling || element.hasDisplayContents();
377         if (shouldResolve) {
378 #if PLATFORM(IOS)
379             CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle());
380 #endif
381             element.resetComputedStyle();
382
383             if (element.hasCustomStyleResolveCallbacks()) {
384                 if (!element.willRecalcStyle(parent.change)) {
385                     it.traverseNextSkippingChildren();
386                     continue;
387                 }
388             }
389
390             update = resolveElement(element);
391
392             if (element.hasCustomStyleResolveCallbacks())
393                 element.didRecalcStyle(update.change);
394
395             if (affectedByPreviousSibling && update.change != Detach)
396                 update.change = Force;
397
398             if (update.style)
399                 m_update->addElement(element, parent.element, update);
400
401             element.clearNeedsStyleRecalc();
402         }
403
404         if (!update.style) {
405             resetStyleForNonRenderedDescendants(element);
406             element.clearChildNeedsStyleRecalc();
407         }
408
409         bool shouldIterateChildren = update.style && (element.childNeedsStyleRecalc() || update.change != NoChange);
410         if (!shouldIterateChildren) {
411             it.traverseNextSkippingChildren();
412             continue;
413         }
414
415         pushParent(element, update);
416
417         it.traverseNext();
418     }
419
420     popParentsToDepth(1);
421 }
422
423 std::unique_ptr<const Update> TreeResolver::resolve(Change change)
424 {
425     auto& renderView = *m_document.renderView();
426
427     Element* documentElement = m_document.documentElement();
428     if (!documentElement)
429         return nullptr;
430     if (change != Force && !documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
431         return nullptr;
432
433     m_update = std::make_unique<Update>(m_document);
434     m_scopeStack.append(adoptRef(*new Scope(m_document)));
435     m_parentStack.append(Parent(m_document, change));
436
437     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
438     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
439     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
440
441     resolveComposedTree();
442
443     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
444     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
445
446     m_parentStack.clear();
447     m_scopeStack.clear();
448
449     if (m_update->roots().isEmpty())
450         return { };
451
452     return WTFMove(m_update);
453 }
454
455 static Vector<std::function<void ()>>& postResolutionCallbackQueue()
456 {
457     static NeverDestroyed<Vector<std::function<void ()>>> vector;
458     return vector;
459 }
460
461 void queuePostResolutionCallback(std::function<void ()> callback)
462 {
463     postResolutionCallbackQueue().append(callback);
464 }
465
466 static void suspendMemoryCacheClientCalls(Document& document)
467 {
468     Page* page = document.page();
469     if (!page || !page->areMemoryCacheClientCallsEnabled())
470         return;
471
472     page->setMemoryCacheClientCallsEnabled(false);
473
474     RefPtr<MainFrame> protectedMainFrame = &page->mainFrame();
475     postResolutionCallbackQueue().append([protectedMainFrame]{
476         if (Page* page = protectedMainFrame->page())
477             page->setMemoryCacheClientCallsEnabled(true);
478     });
479 }
480
481 static unsigned resolutionNestingDepth;
482
483 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document)
484 {
485     ++resolutionNestingDepth;
486
487     if (resolutionNestingDepth == 1)
488         platformStrategies()->loaderStrategy()->suspendPendingRequests();
489
490     // FIXME: It's strange to build this into the disabler.
491     suspendMemoryCacheClientCalls(document);
492 }
493
494 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
495 {
496     if (resolutionNestingDepth == 1) {
497         // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
498         auto& queue = postResolutionCallbackQueue();
499         for (size_t i = 0; i < queue.size(); ++i)
500             queue[i]();
501         queue.clear();
502
503         platformStrategies()->loaderStrategy()->resumePendingRequests();
504     }
505
506     --resolutionNestingDepth;
507 }
508
509 bool postResolutionCallbacksAreSuspended()
510 {
511     return resolutionNestingDepth;
512 }
513
514 bool isPlaceholderStyle(const RenderStyle& style)
515 {
516     return &style == placeholderStyle;
517 }
518
519 }
520 }