[RenderTreeBuilder] Move styleDidChange mutation logic to RenderTreeUpdater
[WebKit-https.git] / Source / WebCore / rendering / updating / RenderTreeUpdater.cpp
1 /*
2  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "RenderTreeUpdater.h"
28
29 #include "AXObjectCache.h"
30 #include "CSSAnimationController.h"
31 #include "ComposedTreeAncestorIterator.h"
32 #include "ComposedTreeIterator.h"
33 #include "Document.h"
34 #include "Element.h"
35 #include "HTMLParserIdioms.h"
36 #include "HTMLSlotElement.h"
37 #include "InspectorInstrumentation.h"
38 #include "NodeRenderStyle.h"
39 #include "PseudoElement.h"
40 #include "RenderDescendantIterator.h"
41 #include "RenderFullScreen.h"
42 #include "RenderInline.h"
43 #include "RenderTreeUpdaterGeneratedContent.h"
44 #include "StyleResolver.h"
45 #include "StyleTreeResolver.h"
46 #include <wtf/SystemTracing.h>
47
48 #if PLATFORM(IOS)
49 #include "WKContentObservation.h"
50 #include "WKContentObservationInternal.h"
51 #endif
52
53 namespace WebCore {
54
55 #if PLATFORM(IOS)
56 class CheckForVisibilityChange {
57 public:
58     CheckForVisibilityChange(const Element&);
59     ~CheckForVisibilityChange();
60
61 private:
62     const Element& m_element;
63     EDisplay m_previousDisplay;
64     EVisibility m_previousVisibility;
65     EVisibility m_previousImplicitVisibility;
66 };
67 #endif // PLATFORM(IOS)
68
69 RenderTreeUpdater::Parent::Parent(ContainerNode& root)
70     : element(is<Document>(root) ? nullptr : downcast<Element>(&root))
71     , renderTreePosition(RenderTreePosition(*root.renderer()))
72 {
73 }
74
75 RenderTreeUpdater::Parent::Parent(Element& element, const Style::ElementUpdates* updates)
76     : element(&element)
77     , updates(updates)
78     , renderTreePosition(element.renderer() ? std::make_optional(RenderTreePosition(*element.renderer())) : std::nullopt)
79 {
80 }
81
82 RenderTreeUpdater::RenderTreeUpdater(Document& document)
83     : m_document(document)
84     , m_generatedContent(std::make_unique<GeneratedContent>(*this))
85     , m_builder(renderView())
86 {
87 }
88
89 RenderTreeUpdater::~RenderTreeUpdater() = default;
90
91 static ContainerNode* findRenderingRoot(ContainerNode& node)
92 {
93     if (node.renderer())
94         return &node;
95     for (auto& ancestor : composedTreeAncestors(node)) {
96         if (ancestor.renderer())
97             return &ancestor;
98         if (!ancestor.hasDisplayContents())
99             return nullptr;
100     }
101     return &node.document();
102 }
103
104 static ListHashSet<ContainerNode*> findRenderingRoots(const Style::Update& update)
105 {
106     ListHashSet<ContainerNode*> renderingRoots;
107     for (auto* root : update.roots()) {
108         auto* renderingRoot = findRenderingRoot(*root);
109         if (!renderingRoot)
110             continue;
111         renderingRoots.add(renderingRoot);
112     }
113     return renderingRoots;
114 }
115
116 void RenderTreeUpdater::commit(std::unique_ptr<const Style::Update> styleUpdate)
117 {
118     ASSERT(&m_document == &styleUpdate->document());
119
120     if (!m_document.shouldCreateRenderers() || !m_document.renderView())
121         return;
122     
123     TraceScope scope(RenderTreeBuildStart, RenderTreeBuildEnd);
124
125     Style::PostResolutionCallbackDisabler callbackDisabler(m_document);
126
127     m_styleUpdate = WTFMove(styleUpdate);
128
129     for (auto* root : findRenderingRoots(*m_styleUpdate))
130         updateRenderTree(*root);
131
132     generatedContent().updateRemainingQuotes();
133
134     m_builder.updateAfterDescendants(renderView());
135
136     m_styleUpdate = nullptr;
137 }
138
139 static bool shouldCreateRenderer(const Element& element, const RenderElement& parentRenderer)
140 {
141     if (!parentRenderer.canHaveChildren() && !(element.isPseudoElement() && parentRenderer.canHaveGeneratedChildren()))
142         return false;
143     if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(element))
144         return false;
145     return true;
146 }
147
148 void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
149 {
150     ASSERT(root.renderer());
151     ASSERT(m_parentStack.isEmpty());
152
153     m_parentStack.append(Parent(root));
154
155     auto descendants = composedTreeDescendants(root);
156     auto it = descendants.begin();
157     auto end = descendants.end();
158
159     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156172
160     it.dropAssertions();
161
162     while (it != end) {
163         popParentsToDepth(it.depth());
164
165         auto& node = *it;
166
167         if (auto* renderer = node.renderer())
168             renderTreePosition().invalidateNextSibling(*renderer);
169         else if (is<Element>(node) && downcast<Element>(node).hasDisplayContents())
170             renderTreePosition().invalidateNextSibling();
171
172         if (is<Text>(node)) {
173             auto& text = downcast<Text>(node);
174             auto* textUpdate = m_styleUpdate->textUpdate(text);
175             bool didCreateParent = parent().updates && parent().updates->update.change == Style::Detach;
176             bool mayNeedUpdateWhitespaceOnlyRenderer = renderingParent().didCreateOrDestroyChildRenderer && text.data().isAllSpecialCharacters<isHTMLSpace>();
177             if (didCreateParent || textUpdate || mayNeedUpdateWhitespaceOnlyRenderer)
178                 updateTextRenderer(text, textUpdate);
179
180             storePreviousRenderer(text);
181             it.traverseNextSkippingChildren();
182             continue;
183         }
184
185         auto& element = downcast<Element>(node);
186
187         auto* elementUpdates = m_styleUpdate->elementUpdates(element);
188
189         // We hop through display: contents elements in findRenderingRoot, so
190         // there may be other updates down the tree.
191         if (!elementUpdates && !element.hasDisplayContents()) {
192             storePreviousRenderer(element);
193             it.traverseNextSkippingChildren();
194             continue;
195         }
196
197         if (elementUpdates)
198             updateElementRenderer(element, elementUpdates->update);
199
200         storePreviousRenderer(element);
201
202         bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent()));
203         if (!mayHaveRenderedDescendants) {
204             it.traverseNextSkippingChildren();
205             continue;
206         }
207
208         pushParent(element, elementUpdates);
209
210         it.traverseNext();
211     }
212
213     popParentsToDepth(0);
214 }
215
216 auto RenderTreeUpdater::renderingParent() -> Parent&
217 {
218     for (unsigned i = m_parentStack.size(); i--;) {
219         if (m_parentStack[i].renderTreePosition)
220             return m_parentStack[i];
221     }
222     ASSERT_NOT_REACHED();
223     return m_parentStack.last();
224 }
225
226 RenderTreePosition& RenderTreeUpdater::renderTreePosition()
227 {
228     return *renderingParent().renderTreePosition;
229 }
230
231 void RenderTreeUpdater::pushParent(Element& element, const Style::ElementUpdates* updates)
232 {
233     m_parentStack.append(Parent(element, updates));
234
235     updateBeforeDescendants(element, updates);
236 }
237
238 void RenderTreeUpdater::popParent()
239 {
240     auto& parent = m_parentStack.last();
241     if (parent.element)
242         updateAfterDescendants(*parent.element, parent.updates);
243
244     m_parentStack.removeLast();
245 }
246
247 void RenderTreeUpdater::popParentsToDepth(unsigned depth)
248 {
249     ASSERT(m_parentStack.size() >= depth);
250
251     while (m_parentStack.size() > depth)
252         popParent();
253 }
254
255 void RenderTreeUpdater::updateBeforeDescendants(Element& element, const Style::ElementUpdates* updates)
256 {
257     if (updates)
258         generatedContent().updatePseudoElement(element, updates->beforePseudoElementUpdate, BEFORE);
259 }
260
261 void RenderTreeUpdater::updateAfterDescendants(Element& element, const Style::ElementUpdates* updates)
262 {
263     if (updates)
264         generatedContent().updatePseudoElement(element, updates->afterPseudoElementUpdate, AFTER);
265
266     auto* renderer = element.renderer();
267     if (!renderer)
268         return;
269
270     m_builder.updateAfterDescendants(*renderer);
271
272     if (element.hasCustomStyleResolveCallbacks() && updates && updates->update.change == Style::Detach)
273         element.didAttachRenderers();
274 }
275
276 static bool pseudoStyleCacheIsInvalid(RenderElement* renderer, RenderStyle* newStyle)
277 {
278     const RenderStyle& currentStyle = renderer->style();
279
280     const PseudoStyleCache* pseudoStyleCache = currentStyle.cachedPseudoStyles();
281     if (!pseudoStyleCache)
282         return false;
283
284     for (auto& cache : *pseudoStyleCache) {
285         PseudoId pseudoId = cache->styleType();
286         std::unique_ptr<RenderStyle> newPseudoStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId), newStyle, newStyle);
287         if (!newPseudoStyle)
288             return true;
289         if (*newPseudoStyle != *cache) {
290             newStyle->addCachedPseudoStyle(WTFMove(newPseudoStyle));
291             return true;
292         }
293     }
294     return false;
295 }
296
297 void RenderTreeUpdater::updateRendererStyle(RenderElement& renderer, RenderStyle&& newStyle, StyleDifference minimalStyleDifference)
298 {
299     auto oldStyle = RenderStyle::clone(renderer.style());
300     renderer.setStyle(WTFMove(newStyle), minimalStyleDifference);
301     m_builder.normalizeTreeAfterStyleChange(renderer, oldStyle);
302 }
303
304 void RenderTreeUpdater::updateElementRenderer(Element& element, const Style::ElementUpdate& update)
305 {
306 #if PLATFORM(IOS)
307     CheckForVisibilityChange checkForVisibilityChange(element);
308 #endif
309
310     bool shouldTearDownRenderers = update.change == Style::Detach && (element.renderer() || element.hasDisplayContents());
311     if (shouldTearDownRenderers) {
312         if (!element.renderer()) {
313             // We may be tearing down a descendant renderer cached in renderTreePosition.
314             renderTreePosition().invalidateNextSibling();
315         }
316
317         // display:none cancels animations.
318         auto teardownType = update.style->display() == NONE ? TeardownType::RendererUpdateCancelingAnimations : TeardownType::RendererUpdate;
319         tearDownRenderers(element, teardownType);
320
321         renderingParent().didCreateOrDestroyChildRenderer = true;
322     }
323
324     bool hasDisplayContents = update.style->display() == CONTENTS;
325     if (hasDisplayContents)
326         element.storeDisplayContentsStyle(RenderStyle::clonePtr(*update.style));
327     else
328         element.resetComputedStyle();
329
330     bool shouldCreateNewRenderer = !element.renderer() && !hasDisplayContents;
331     if (shouldCreateNewRenderer) {
332         if (element.hasCustomStyleResolveCallbacks())
333             element.willAttachRenderers();
334         createRenderer(element, RenderStyle::clone(*update.style));
335
336         renderingParent().didCreateOrDestroyChildRenderer = true;
337         return;
338     }
339
340     if (!element.renderer())
341         return;
342     auto& renderer = *element.renderer();
343
344     if (update.recompositeLayer) {
345         updateRendererStyle(renderer, RenderStyle::clone(*update.style), StyleDifferenceRecompositeLayer);
346         return;
347     }
348
349     if (update.change == Style::NoChange) {
350         if (pseudoStyleCacheIsInvalid(&renderer, update.style.get())) {
351             updateRendererStyle(renderer, RenderStyle::clone(*update.style), StyleDifferenceEqual);
352             return;
353         }
354         return;
355     }
356
357     updateRendererStyle(renderer, RenderStyle::clone(*update.style), StyleDifferenceEqual);
358 }
359
360 void RenderTreeUpdater::createRenderer(Element& element, RenderStyle&& style)
361 {
362     auto computeInsertionPosition = [this, &element] () {
363         renderTreePosition().computeNextSibling(element);
364         return renderTreePosition();
365     };
366     
367     if (!shouldCreateRenderer(element, renderTreePosition().parent()))
368         return;
369
370     if (!element.rendererIsNeeded(style))
371         return;
372
373     RenderTreePosition insertionPosition = computeInsertionPosition();
374     auto newRenderer = element.createElementRenderer(WTFMove(style), insertionPosition);
375     if (!newRenderer)
376         return;
377
378     if (!insertionPosition.parent().isChildAllowed(*newRenderer, newRenderer->style()))
379         return;
380
381     element.setRenderer(newRenderer.get());
382
383     newRenderer->initializeStyle();
384
385 #if ENABLE(FULLSCREEN_API)
386     if (m_document.webkitIsFullScreen() && m_document.webkitCurrentFullScreenElement() == &element) {
387         newRenderer = RenderFullScreen::wrapNewRenderer(m_builder, WTFMove(newRenderer), insertionPosition.parent(), m_document);
388         if (!newRenderer)
389             return;
390     }
391 #endif
392
393     m_builder.attach(insertionPosition, WTFMove(newRenderer));
394
395     if (AXObjectCache* cache = m_document.axObjectCache())
396         cache->updateCacheAfterNodeIsAttached(&element);
397 }
398
399 bool RenderTreeUpdater::textRendererIsNeeded(const Text& textNode)
400 {
401     auto& renderingParent = this->renderingParent();
402     auto& parentRenderer = renderingParent.renderTreePosition->parent();
403     if (!parentRenderer.canHaveChildren())
404         return false;
405     if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(textNode))
406         return false;
407     if (textNode.isEditingText())
408         return true;
409     if (!textNode.length())
410         return false;
411     if (!textNode.data().isAllSpecialCharacters<isHTMLSpace>())
412         return true;
413     if (is<RenderText>(renderingParent.previousChildRenderer))
414         return true;
415     // This text node has nothing but white space. We may still need a renderer in some cases.
416     if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet())
417         return false;
418     if (parentRenderer.isFlexibleBox() && !parentRenderer.isRenderButton())
419         return false;
420     if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
421         return true;
422
423     auto* previousRenderer = renderingParent.previousChildRenderer;
424     if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span>
425         return false;
426
427     if (parentRenderer.isRenderInline()) {
428         // <span><div/> <div/></span>
429         if (previousRenderer && !previousRenderer->isInline())
430             return false;
431     } else {
432         if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline()))
433             return false;
434         
435         RenderObject* first = parentRenderer.firstChild();
436         while (first && first->isFloatingOrOutOfFlowPositioned())
437             first = first->nextSibling();
438         RenderObject* nextRenderer = textNode.renderer() ? textNode.renderer() :  renderTreePosition().nextSiblingRenderer(textNode);
439         if (!first || nextRenderer == first) {
440             // Whitespace at the start of a block just goes away. Don't even make a render object for this text.
441             return false;
442         }
443     }
444     return true;
445 }
446
447 void RenderTreeUpdater::createTextRenderer(Text& textNode, const Style::TextUpdate* textUpdate)
448 {
449     ASSERT(!textNode.renderer());
450
451     auto& renderTreePosition = this->renderTreePosition();
452     auto textRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
453
454     renderTreePosition.computeNextSibling(textNode);
455
456     if (!renderTreePosition.parent().isChildAllowed(*textRenderer, renderTreePosition.parent().style()))
457         return;
458
459     textNode.setRenderer(textRenderer.get());
460
461     if (textUpdate && textUpdate->inheritedDisplayContentsStyle && *textUpdate->inheritedDisplayContentsStyle) {
462         // Wrap text renderer into anonymous inline so we can give it a style.
463         // This is to support "<div style='display:contents;color:green'>text</div>" type cases
464         auto newDisplayContentsAnonymousWrapper = WebCore::createRenderer<RenderInline>(textNode.document(), RenderStyle::clone(**textUpdate->inheritedDisplayContentsStyle));
465         newDisplayContentsAnonymousWrapper->initializeStyle();
466         auto& displayContentsAnonymousWrapper = *newDisplayContentsAnonymousWrapper;
467         m_builder.attach(renderTreePosition, WTFMove(newDisplayContentsAnonymousWrapper));
468
469         textRenderer->setInlineWrapperForDisplayContents(&displayContentsAnonymousWrapper);
470         m_builder.attach(displayContentsAnonymousWrapper, WTFMove(textRenderer));
471         return;
472     }
473
474     m_builder.attach(renderTreePosition, WTFMove(textRenderer));
475 }
476
477 void RenderTreeUpdater::updateTextRenderer(Text& text, const Style::TextUpdate* textUpdate)
478 {
479     auto* existingRenderer = text.renderer();
480     bool needsRenderer = textRendererIsNeeded(text);
481
482     if (existingRenderer && textUpdate && textUpdate->inheritedDisplayContentsStyle) {
483         if (existingRenderer->inlineWrapperForDisplayContents() || *textUpdate->inheritedDisplayContentsStyle) {
484             // FIXME: We could update without teardown.
485             tearDownTextRenderer(text);
486             existingRenderer = nullptr;
487         }
488     }
489
490     if (existingRenderer) {
491         if (needsRenderer) {
492             if (textUpdate)
493                 existingRenderer->setTextWithOffset(text.data(), textUpdate->offset, textUpdate->length);
494             return;
495         }
496         tearDownTextRenderer(text);
497         renderingParent().didCreateOrDestroyChildRenderer = true;
498         return;
499     }
500     if (!needsRenderer)
501         return;
502     createTextRenderer(text, textUpdate);
503     renderingParent().didCreateOrDestroyChildRenderer = true;
504 }
505
506 void RenderTreeUpdater::storePreviousRenderer(Node& node)
507 {
508     auto* renderer = node.renderer();
509     if (!renderer)
510         return;
511     ASSERT(renderingParent().previousChildRenderer != renderer);
512     renderingParent().previousChildRenderer = renderer;
513 }
514
515 void RenderTreeUpdater::tearDownRenderers(Element& root)
516 {
517     auto* view = root.document().renderView();
518     if (!view)
519         return;
520     RenderTreeBuilder builder(*view);
521     tearDownRenderers(root, TeardownType::Full);
522 }
523
524 void RenderTreeUpdater::tearDownRenderer(Text& text)
525 {
526     auto* view = text.document().renderView();
527     if (!view)
528         return;
529     RenderTreeBuilder builder(*view);
530     tearDownTextRenderer(text);
531 }
532
533 void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownType)
534 {
535     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
536
537     Vector<Element*, 30> teardownStack;
538
539     auto push = [&] (Element& element) {
540         if (element.hasCustomStyleResolveCallbacks())
541             element.willDetachRenderers();
542         teardownStack.append(&element);
543     };
544
545     auto& animationController = root.document().frame()->animation();
546
547     auto pop = [&] (unsigned depth) {
548         while (teardownStack.size() > depth) {
549             auto& element = *teardownStack.takeLast();
550
551             if (teardownType == TeardownType::Full || teardownType == TeardownType::RendererUpdateCancelingAnimations)
552                 animationController.cancelAnimations(element);
553
554             if (teardownType == TeardownType::Full)
555                 element.clearHoverAndActiveStatusBeforeDetachingRenderer();
556
557             GeneratedContent::removeBeforePseudoElement(element);
558             GeneratedContent::removeAfterPseudoElement(element);
559
560             if (auto* renderer = element.renderer()) {
561                 RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*renderer);
562                 element.setRenderer(nullptr);
563             }
564
565             // Make sure we don't leave any renderers behind in nodes outside the composed tree.
566             if (element.shadowRoot())
567                 tearDownLeftoverShadowHostChildren(element);
568
569             if (element.hasCustomStyleResolveCallbacks())
570                 element.didDetachRenderers();
571         }
572     };
573
574     push(root);
575
576     auto descendants = composedTreeDescendants(root);
577     for (auto it = descendants.begin(), end = descendants.end(); it != end; ++it) {
578         pop(it.depth());
579
580         if (is<Text>(*it)) {
581             tearDownTextRenderer(downcast<Text>(*it));
582             continue;
583         }
584
585         push(downcast<Element>(*it));
586     }
587
588     pop(0);
589
590     tearDownLeftoverPaginationRenderersIfNeeded(root);
591 }
592
593 void RenderTreeUpdater::tearDownTextRenderer(Text& text)
594 {
595     auto* renderer = text.renderer();
596     if (!renderer)
597         return;
598     RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*renderer);
599     text.setRenderer(nullptr);
600 }
601
602 void RenderTreeUpdater::tearDownLeftoverPaginationRenderersIfNeeded(Element& root)
603 {
604     if (&root != root.document().documentElement())
605         return;
606     for (auto* child = root.document().renderView()->firstChild(); child;) {
607         auto* nextSibling = child->nextSibling();
608         if (is<RenderMultiColumnFlow>(*child) || is<RenderMultiColumnSet>(*child))
609             RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*child);
610         child = nextSibling;
611     }
612 }
613
614 void RenderTreeUpdater::tearDownLeftoverShadowHostChildren(Element& host)
615 {
616     for (auto* hostChild = host.firstChild(); hostChild; hostChild = hostChild->nextSibling()) {
617         if (!hostChild->renderer())
618             continue;
619         if (is<Text>(*hostChild)) {
620             tearDownTextRenderer(downcast<Text>(*hostChild));
621             continue;
622         }
623         if (is<Element>(*hostChild))
624             tearDownRenderers(downcast<Element>(*hostChild), TeardownType::Full);
625     }
626 }
627
628 RenderView& RenderTreeUpdater::renderView()
629 {
630     return *m_document.renderView();
631 }
632
633 #if PLATFORM(IOS)
634 static EVisibility elementImplicitVisibility(const Element& element)
635 {
636     auto* renderer = element.renderer();
637     if (!renderer)
638         return VISIBLE;
639
640     auto& style = renderer->style();
641
642     auto width = style.width();
643     auto height = style.height();
644     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
645         return HIDDEN;
646
647     auto top = style.top();
648     auto left = style.left();
649     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
650         return HIDDEN;
651
652     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
653         return HIDDEN;
654     return VISIBLE;
655 }
656
657 CheckForVisibilityChange::CheckForVisibilityChange(const Element& element)
658     : m_element(element)
659     , m_previousDisplay(element.renderStyle() ? element.renderStyle()->display() : NONE)
660     , m_previousVisibility(element.renderStyle() ? element.renderStyle()->visibility() : HIDDEN)
661     , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
662 {
663 }
664
665 CheckForVisibilityChange::~CheckForVisibilityChange()
666 {
667     if (!WKObservingContentChanges())
668         return;
669     if (m_element.isInUserAgentShadowTree())
670         return;
671     auto* style = m_element.renderStyle();
672     if (!style)
673         return;
674     if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
675         || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element) == VISIBLE))
676         WKSetObservedContentChange(WKContentVisibilityChange);
677 }
678 #endif
679
680 }