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