670648a15f224c7c5cf242b1a137fa526da08866
[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::updateElementRenderer(Element& element, const Style::ElementUpdate& update)
298 {
299 #if PLATFORM(IOS)
300     CheckForVisibilityChange checkForVisibilityChange(element);
301 #endif
302
303     bool shouldTearDownRenderers = update.change == Style::Detach && (element.renderer() || element.hasDisplayContents());
304     if (shouldTearDownRenderers) {
305         if (!element.renderer()) {
306             // We may be tearing down a descendant renderer cached in renderTreePosition.
307             renderTreePosition().invalidateNextSibling();
308         }
309
310         // display:none cancels animations.
311         auto teardownType = update.style->display() == NONE ? TeardownType::RendererUpdateCancelingAnimations : TeardownType::RendererUpdate;
312         tearDownRenderers(element, teardownType);
313
314         renderingParent().didCreateOrDestroyChildRenderer = true;
315     }
316
317     bool hasDisplayContents = update.style->display() == CONTENTS;
318     if (hasDisplayContents)
319         element.storeDisplayContentsStyle(RenderStyle::clonePtr(*update.style));
320     else
321         element.resetComputedStyle();
322
323     bool shouldCreateNewRenderer = !element.renderer() && !hasDisplayContents;
324     if (shouldCreateNewRenderer) {
325         if (element.hasCustomStyleResolveCallbacks())
326             element.willAttachRenderers();
327         createRenderer(element, RenderStyle::clone(*update.style));
328
329         renderingParent().didCreateOrDestroyChildRenderer = true;
330         return;
331     }
332
333     if (!element.renderer())
334         return;
335     auto& renderer = *element.renderer();
336
337     if (update.recompositeLayer) {
338         renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceRecompositeLayer);
339         return;
340     }
341
342     if (update.change == Style::NoChange) {
343         if (pseudoStyleCacheIsInvalid(&renderer, update.style.get())) {
344             renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual);
345             return;
346         }
347         return;
348     }
349
350     renderer.setStyle(RenderStyle::clone(*update.style), StyleDifferenceEqual);
351 }
352
353 void RenderTreeUpdater::createRenderer(Element& element, RenderStyle&& style)
354 {
355     auto computeInsertionPosition = [this, &element] () {
356         renderTreePosition().computeNextSibling(element);
357         return renderTreePosition();
358     };
359     
360     if (!shouldCreateRenderer(element, renderTreePosition().parent()))
361         return;
362
363     if (!element.rendererIsNeeded(style))
364         return;
365
366     RenderTreePosition insertionPosition = computeInsertionPosition();
367     auto newRenderer = element.createElementRenderer(WTFMove(style), insertionPosition);
368     if (!newRenderer)
369         return;
370
371     if (!insertionPosition.parent().isChildAllowed(*newRenderer, newRenderer->style()))
372         return;
373
374     element.setRenderer(newRenderer.get());
375
376     newRenderer->initializeStyle();
377
378 #if ENABLE(FULLSCREEN_API)
379     if (m_document.webkitIsFullScreen() && m_document.webkitCurrentFullScreenElement() == &element) {
380         newRenderer = RenderFullScreen::wrapNewRenderer(m_builder, WTFMove(newRenderer), insertionPosition.parent(), m_document);
381         if (!newRenderer)
382             return;
383     }
384 #endif
385
386     m_builder.attach(insertionPosition, WTFMove(newRenderer));
387
388     if (AXObjectCache* cache = m_document.axObjectCache())
389         cache->updateCacheAfterNodeIsAttached(&element);
390 }
391
392 bool RenderTreeUpdater::textRendererIsNeeded(const Text& textNode)
393 {
394     auto& renderingParent = this->renderingParent();
395     auto& parentRenderer = renderingParent.renderTreePosition->parent();
396     if (!parentRenderer.canHaveChildren())
397         return false;
398     if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(textNode))
399         return false;
400     if (textNode.isEditingText())
401         return true;
402     if (!textNode.length())
403         return false;
404     if (!textNode.data().isAllSpecialCharacters<isHTMLSpace>())
405         return true;
406     if (is<RenderText>(renderingParent.previousChildRenderer))
407         return true;
408     // This text node has nothing but white space. We may still need a renderer in some cases.
409     if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet())
410         return false;
411     if (parentRenderer.isFlexibleBox() && !parentRenderer.isRenderButton())
412         return false;
413     if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
414         return true;
415
416     auto* previousRenderer = renderingParent.previousChildRenderer;
417     if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span>
418         return false;
419
420     if (parentRenderer.isRenderInline()) {
421         // <span><div/> <div/></span>
422         if (previousRenderer && !previousRenderer->isInline())
423             return false;
424     } else {
425         if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline()))
426             return false;
427         
428         RenderObject* first = parentRenderer.firstChild();
429         while (first && first->isFloatingOrOutOfFlowPositioned())
430             first = first->nextSibling();
431         RenderObject* nextRenderer = textNode.renderer() ? textNode.renderer() :  renderTreePosition().nextSiblingRenderer(textNode);
432         if (!first || nextRenderer == first) {
433             // Whitespace at the start of a block just goes away. Don't even make a render object for this text.
434             return false;
435         }
436     }
437     return true;
438 }
439
440 void RenderTreeUpdater::createTextRenderer(Text& textNode, const Style::TextUpdate* textUpdate)
441 {
442     ASSERT(!textNode.renderer());
443
444     auto& renderTreePosition = this->renderTreePosition();
445     auto textRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
446
447     renderTreePosition.computeNextSibling(textNode);
448
449     if (!renderTreePosition.parent().isChildAllowed(*textRenderer, renderTreePosition.parent().style()))
450         return;
451
452     textNode.setRenderer(textRenderer.get());
453
454     if (textUpdate && textUpdate->inheritedDisplayContentsStyle && *textUpdate->inheritedDisplayContentsStyle) {
455         // Wrap text renderer into anonymous inline so we can give it a style.
456         // This is to support "<div style='display:contents;color:green'>text</div>" type cases
457         auto newDisplayContentsAnonymousWrapper = WebCore::createRenderer<RenderInline>(textNode.document(), RenderStyle::clone(**textUpdate->inheritedDisplayContentsStyle));
458         newDisplayContentsAnonymousWrapper->initializeStyle();
459         auto& displayContentsAnonymousWrapper = *newDisplayContentsAnonymousWrapper;
460         m_builder.attach(renderTreePosition, WTFMove(newDisplayContentsAnonymousWrapper));
461
462         textRenderer->setInlineWrapperForDisplayContents(&displayContentsAnonymousWrapper);
463         m_builder.attach(displayContentsAnonymousWrapper, WTFMove(textRenderer));
464         return;
465     }
466
467     m_builder.attach(renderTreePosition, WTFMove(textRenderer));
468 }
469
470 void RenderTreeUpdater::updateTextRenderer(Text& text, const Style::TextUpdate* textUpdate)
471 {
472     auto* existingRenderer = text.renderer();
473     bool needsRenderer = textRendererIsNeeded(text);
474
475     if (existingRenderer && textUpdate && textUpdate->inheritedDisplayContentsStyle) {
476         if (existingRenderer->inlineWrapperForDisplayContents() || *textUpdate->inheritedDisplayContentsStyle) {
477             // FIXME: We could update without teardown.
478             tearDownTextRenderer(text);
479             existingRenderer = nullptr;
480         }
481     }
482
483     if (existingRenderer) {
484         if (needsRenderer) {
485             if (textUpdate)
486                 existingRenderer->setTextWithOffset(text.data(), textUpdate->offset, textUpdate->length);
487             return;
488         }
489         tearDownTextRenderer(text);
490         renderingParent().didCreateOrDestroyChildRenderer = true;
491         return;
492     }
493     if (!needsRenderer)
494         return;
495     createTextRenderer(text, textUpdate);
496     renderingParent().didCreateOrDestroyChildRenderer = true;
497 }
498
499 void RenderTreeUpdater::storePreviousRenderer(Node& node)
500 {
501     auto* renderer = node.renderer();
502     if (!renderer)
503         return;
504     ASSERT(renderingParent().previousChildRenderer != renderer);
505     renderingParent().previousChildRenderer = renderer;
506 }
507
508 void RenderTreeUpdater::tearDownRenderers(Element& root)
509 {
510     auto* view = root.document().renderView();
511     if (!view)
512         return;
513     RenderTreeBuilder builder(*view);
514     tearDownRenderers(root, TeardownType::Full);
515 }
516
517 void RenderTreeUpdater::tearDownRenderer(Text& text)
518 {
519     auto* view = text.document().renderView();
520     if (!view)
521         return;
522     RenderTreeBuilder builder(*view);
523     tearDownTextRenderer(text);
524 }
525
526 void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownType)
527 {
528     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
529
530     Vector<Element*, 30> teardownStack;
531
532     auto push = [&] (Element& element) {
533         if (element.hasCustomStyleResolveCallbacks())
534             element.willDetachRenderers();
535         teardownStack.append(&element);
536     };
537
538     auto& animationController = root.document().frame()->animation();
539
540     auto pop = [&] (unsigned depth) {
541         while (teardownStack.size() > depth) {
542             auto& element = *teardownStack.takeLast();
543
544             if (teardownType == TeardownType::Full || teardownType == TeardownType::RendererUpdateCancelingAnimations)
545                 animationController.cancelAnimations(element);
546
547             if (teardownType == TeardownType::Full)
548                 element.clearHoverAndActiveStatusBeforeDetachingRenderer();
549
550             GeneratedContent::removeBeforePseudoElement(element);
551             GeneratedContent::removeAfterPseudoElement(element);
552
553             if (auto* renderer = element.renderer()) {
554                 RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*renderer);
555                 element.setRenderer(nullptr);
556             }
557
558             // Make sure we don't leave any renderers behind in nodes outside the composed tree.
559             if (element.shadowRoot())
560                 tearDownLeftoverShadowHostChildren(element);
561
562             if (element.hasCustomStyleResolveCallbacks())
563                 element.didDetachRenderers();
564         }
565     };
566
567     push(root);
568
569     auto descendants = composedTreeDescendants(root);
570     for (auto it = descendants.begin(), end = descendants.end(); it != end; ++it) {
571         pop(it.depth());
572
573         if (is<Text>(*it)) {
574             tearDownTextRenderer(downcast<Text>(*it));
575             continue;
576         }
577
578         push(downcast<Element>(*it));
579     }
580
581     pop(0);
582
583     tearDownLeftoverPaginationRenderersIfNeeded(root);
584 }
585
586 void RenderTreeUpdater::tearDownTextRenderer(Text& text)
587 {
588     auto* renderer = text.renderer();
589     if (!renderer)
590         return;
591     RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*renderer);
592     text.setRenderer(nullptr);
593 }
594
595 void RenderTreeUpdater::tearDownLeftoverPaginationRenderersIfNeeded(Element& root)
596 {
597     if (&root != root.document().documentElement())
598         return;
599     for (auto* child = root.document().renderView()->firstChild(); child;) {
600         auto* nextSibling = child->nextSibling();
601         if (is<RenderMultiColumnFlow>(*child) || is<RenderMultiColumnSet>(*child))
602             RenderTreeBuilder::current()->destroyAndCleanUpAnonymousWrappers(*child);
603         child = nextSibling;
604     }
605 }
606
607 void RenderTreeUpdater::tearDownLeftoverShadowHostChildren(Element& host)
608 {
609     for (auto* hostChild = host.firstChild(); hostChild; hostChild = hostChild->nextSibling()) {
610         if (!hostChild->renderer())
611             continue;
612         if (is<Text>(*hostChild)) {
613             tearDownTextRenderer(downcast<Text>(*hostChild));
614             continue;
615         }
616         if (is<Element>(*hostChild))
617             tearDownRenderers(downcast<Element>(*hostChild), TeardownType::Full);
618     }
619 }
620
621 RenderView& RenderTreeUpdater::renderView()
622 {
623     return *m_document.renderView();
624 }
625
626 #if PLATFORM(IOS)
627 static EVisibility elementImplicitVisibility(const Element& element)
628 {
629     auto* renderer = element.renderer();
630     if (!renderer)
631         return VISIBLE;
632
633     auto& style = renderer->style();
634
635     auto width = style.width();
636     auto height = style.height();
637     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
638         return HIDDEN;
639
640     auto top = style.top();
641     auto left = style.left();
642     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
643         return HIDDEN;
644
645     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
646         return HIDDEN;
647     return VISIBLE;
648 }
649
650 CheckForVisibilityChange::CheckForVisibilityChange(const Element& element)
651     : m_element(element)
652     , m_previousDisplay(element.renderStyle() ? element.renderStyle()->display() : NONE)
653     , m_previousVisibility(element.renderStyle() ? element.renderStyle()->visibility() : HIDDEN)
654     , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
655 {
656 }
657
658 CheckForVisibilityChange::~CheckForVisibilityChange()
659 {
660     if (!WKObservingContentChanges())
661         return;
662     if (m_element.isInUserAgentShadowTree())
663         return;
664     auto* style = m_element.renderStyle();
665     if (!style)
666         return;
667     if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
668         || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element) == VISIBLE))
669         WKSetObservedContentChange(WKContentVisibilityChange);
670 }
671 #endif
672
673 }