Computing animated style should not require renderers
[WebKit-https.git] / Source / WebCore / style / RenderTreeUpdater.cpp
1 /*
2  * Copyright (C) 2016 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 "FlowThreadController.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 "RenderListItem.h"
43 #include "RenderNamedFlowThread.h"
44 #include "RenderTreeUpdaterFirstLetter.h"
45 #include "RenderTreeUpdaterGeneratedContent.h"
46 #include "RenderTreeUpdaterListItem.h"
47 #include "RenderTreeUpdaterMultiColumn.h"
48 #include "StyleResolver.h"
49 #include "StyleTreeResolver.h"
50 #include <wtf/SystemTracing.h>
51
52 #if PLATFORM(IOS)
53 #include "WKContentObservation.h"
54 #include "WKContentObservationInternal.h"
55 #endif
56
57 namespace WebCore {
58
59 #if PLATFORM(IOS)
60 class CheckForVisibilityChange {
61 public:
62     CheckForVisibilityChange(const Element&);
63     ~CheckForVisibilityChange();
64
65 private:
66     const Element& m_element;
67     EDisplay m_previousDisplay;
68     EVisibility m_previousVisibility;
69     EVisibility m_previousImplicitVisibility;
70 };
71 #endif // PLATFORM(IOS)
72
73 RenderTreeUpdater::Parent::Parent(ContainerNode& root)
74     : element(is<Document>(root) ? nullptr : downcast<Element>(&root))
75     , renderTreePosition(RenderTreePosition(*root.renderer()))
76 {
77 }
78
79 RenderTreeUpdater::Parent::Parent(Element& element, Style::Change styleChange)
80     : element(&element)
81     , styleChange(styleChange)
82     , renderTreePosition(element.renderer() ? std::make_optional(RenderTreePosition(*element.renderer())) : std::nullopt)
83 {
84 }
85
86 RenderTreeUpdater::RenderTreeUpdater(Document& document)
87     : m_document(document)
88     , m_generatedContent(std::make_unique<GeneratedContent>(*this))
89 {
90 }
91
92 RenderTreeUpdater::~RenderTreeUpdater()
93 {
94 }
95
96 static ContainerNode* findRenderingRoot(ContainerNode& node)
97 {
98     if (node.renderer())
99         return &node;
100     for (auto& ancestor : composedTreeAncestors(node)) {
101         if (ancestor.renderer())
102             return &ancestor;
103         if (!ancestor.hasDisplayContents())
104             return nullptr;
105     }
106     return &node.document();
107 }
108
109 static ListHashSet<ContainerNode*> findRenderingRoots(const Style::Update& update)
110 {
111     ListHashSet<ContainerNode*> renderingRoots;
112     for (auto* root : update.roots()) {
113         auto* renderingRoot = findRenderingRoot(*root);
114         if (!renderingRoot)
115             continue;
116         renderingRoots.add(renderingRoot);
117     }
118     return renderingRoots;
119 }
120
121 void RenderTreeUpdater::commit(std::unique_ptr<const Style::Update> styleUpdate)
122 {
123     ASSERT(&m_document == &styleUpdate->document());
124
125     if (!m_document.shouldCreateRenderers() || !m_document.renderView())
126         return;
127     
128     TraceScope scope(RenderTreeBuildStart, RenderTreeBuildEnd);
129
130     Style::PostResolutionCallbackDisabler callbackDisabler(m_document);
131
132     m_styleUpdate = WTFMove(styleUpdate);
133
134     for (auto* root : findRenderingRoots(*m_styleUpdate))
135         updateRenderTree(*root);
136
137     generatedContent().updateRemainingQuotes();
138
139     MultiColumn::update(renderView());
140
141     m_styleUpdate = nullptr;
142 }
143
144 static bool shouldCreateRenderer(const Element& element, const RenderElement& parentRenderer)
145 {
146     if (!parentRenderer.canHaveChildren() && !(element.isPseudoElement() && parentRenderer.canHaveGeneratedChildren()))
147         return false;
148     if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(element))
149         return false;
150     return true;
151 }
152
153 void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
154 {
155     ASSERT(root.renderer());
156     ASSERT(m_parentStack.isEmpty());
157
158     m_parentStack.append(Parent(root));
159
160     auto descendants = composedTreeDescendants(root);
161     auto it = descendants.begin();
162     auto end = descendants.end();
163
164     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156172
165     it.dropAssertions();
166
167     while (it != end) {
168         popParentsToDepth(it.depth());
169
170         auto& node = *it;
171
172         if (auto* renderer = node.renderer())
173             renderTreePosition().invalidateNextSibling(*renderer);
174         else if (is<Element>(node) && downcast<Element>(node).hasDisplayContents())
175             renderTreePosition().invalidateNextSibling();
176
177         if (is<Text>(node)) {
178             auto& text = downcast<Text>(node);
179             auto* textUpdate = m_styleUpdate->textUpdate(text);
180             if (parent().styleChange == Style::Detach || textUpdate || m_invalidatedWhitespaceOnlyTextSiblings.contains(&text))
181                 updateTextRenderer(text, textUpdate);
182
183             it.traverseNextSkippingChildren();
184             continue;
185         }
186
187         auto& element = downcast<Element>(node);
188
189         auto* elementUpdate = m_styleUpdate->elementUpdate(element);
190
191         // We hop through display: contents elements in findRenderingRoot, so
192         // there may be other updates down the tree.
193         if (!elementUpdate && !element.hasDisplayContents()) {
194             it.traverseNextSkippingChildren();
195             continue;
196         }
197
198         if (elementUpdate)
199             updateElementRenderer(element, *elementUpdate);
200
201         bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent()));
202         if (!mayHaveRenderedDescendants) {
203             it.traverseNextSkippingChildren();
204             continue;
205         }
206
207         pushParent(element, elementUpdate ? elementUpdate->change : Style::NoChange);
208
209         it.traverseNext();
210     }
211
212     popParentsToDepth(0);
213
214     m_invalidatedWhitespaceOnlyTextSiblings.clear();
215 }
216
217 RenderTreePosition& RenderTreeUpdater::renderTreePosition()
218 {
219     for (unsigned i = m_parentStack.size(); i; --i) {
220         if (auto& position = m_parentStack[i - 1].renderTreePosition)
221             return *position;
222     }
223     ASSERT_NOT_REACHED();
224     return *m_parentStack.last().renderTreePosition;
225 }
226
227 void RenderTreeUpdater::pushParent(Element& element, Style::Change changeType)
228 {
229     m_parentStack.append(Parent(element, changeType));
230
231     updateBeforeDescendants(element);
232 }
233
234 void RenderTreeUpdater::popParent()
235 {
236     auto& parent = m_parentStack.last();
237     if (parent.element)
238         updateAfterDescendants(*parent.element, parent.styleChange);
239
240     m_parentStack.removeLast();
241 }
242
243 void RenderTreeUpdater::popParentsToDepth(unsigned depth)
244 {
245     ASSERT(m_parentStack.size() >= depth);
246
247     while (m_parentStack.size() > depth)
248         popParent();
249 }
250
251 void RenderTreeUpdater::updateBeforeDescendants(Element& element)
252 {
253     generatedContent().updateBeforePseudoElement(element);
254 }
255
256 void RenderTreeUpdater::updateAfterDescendants(Element& element, Style::Change styleChange)
257 {
258     generatedContent().updateAfterPseudoElement(element);
259
260     auto* renderer = element.renderer();
261     if (!renderer)
262         return;
263
264     // These functions do render tree mutations that require descendant renderers.
265     if (is<RenderBlock>(*renderer))
266         FirstLetter::update(downcast<RenderBlock>(*renderer));
267     if (is<RenderListItem>(*renderer))
268         ListItem::updateMarker(downcast<RenderListItem>(*renderer));
269     if (is<RenderBlockFlow>(*renderer))
270         MultiColumn::update(downcast<RenderBlockFlow>(*renderer));
271
272     if (element.hasCustomStyleResolveCallbacks() && styleChange == 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
304         && (element.renderer() || element.isNamedFlowContentElement() || element.hasDisplayContents());
305     if (shouldTearDownRenderers) {
306         if (!element.renderer()) {
307             // We may be tearing down a descendant renderer cached in renderTreePosition.
308             renderTreePosition().invalidateNextSibling();
309         }
310
311         // display:none cancels animations.
312         auto teardownType = update.style->display() == NONE ? TeardownType::RendererUpdateCancelingAnimations : TeardownType::RendererUpdate;
313         tearDownRenderers(element, teardownType);
314     }
315
316     bool hasDisplayContents = update.style->display() == CONTENTS;
317     if (hasDisplayContents != element.hasDisplayContents()) {
318         if (!hasDisplayContents)
319             element.resetComputedStyle();
320         else
321             element.storeDisplayContentsStyle(RenderStyle::clonePtr(*update.style));
322     }
323
324     bool shouldCreateNewRenderer = !element.renderer() && !hasDisplayContents;
325     if (shouldCreateNewRenderer) {
326         if (element.hasCustomStyleResolveCallbacks())
327             element.willAttachRenderers();
328         createRenderer(element, RenderStyle::clone(*update.style));
329         invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(element);
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 #if ENABLE(CSS_REGIONS)
354 static void registerElementForFlowThreadIfNeeded(Element& element, const RenderStyle& style)
355 {
356     if (!element.shouldMoveToFlowThread(style))
357         return;
358     FlowThreadController& flowThreadController = renderView().flowThreadController();
359     flowThreadController.registerNamedFlowContentElement(element, flowThreadController.ensureRenderFlowThreadWithName(style.flowThread()));
360 }
361 #endif
362
363 void RenderTreeUpdater::createRenderer(Element& element, RenderStyle&& style)
364 {
365     auto computeInsertionPosition = [this, &element, &style] () {
366 #if ENABLE(CSS_REGIONS)
367         if (element.shouldMoveToFlowThread(style))
368             return RenderTreePosition::insertionPositionForFlowThread(renderTreePosition().parent().element(), element, style);
369 #else
370         UNUSED_PARAM(style);
371 #endif
372         renderTreePosition().computeNextSibling(element);
373         return renderTreePosition();
374     };
375     
376     if (!shouldCreateRenderer(element, renderTreePosition().parent()))
377         return;
378
379 #if ENABLE(CSS_REGIONS)
380     // Even display: none elements need to be registered in FlowThreadController.
381     registerElementForFlowThreadIfNeeded(element, style);
382 #endif
383
384     if (!element.rendererIsNeeded(style))
385         return;
386
387     RenderTreePosition insertionPosition = computeInsertionPosition();
388     RenderElement* newRenderer = element.createElementRenderer(WTFMove(style), insertionPosition).leakPtr();
389     if (!newRenderer)
390         return;
391     if (!insertionPosition.canInsert(*newRenderer)) {
392         newRenderer->destroy();
393         return;
394     }
395
396     // Make sure the RenderObject already knows it is going to be added to a RenderFlowThread before we set the style
397     // for the first time. Otherwise code using inRenderFlowThread() in the styleWillChange and styleDidChange will fail.
398     newRenderer->setFlowThreadState(insertionPosition.parent().flowThreadState());
399
400     element.setRenderer(newRenderer);
401
402     newRenderer->initializeStyle();
403
404 #if ENABLE(FULLSCREEN_API)
405     if (m_document.webkitIsFullScreen() && m_document.webkitCurrentFullScreenElement() == &element) {
406         newRenderer = RenderFullScreen::wrapRenderer(newRenderer, &insertionPosition.parent(), m_document);
407         if (!newRenderer)
408             return;
409     }
410 #endif
411     // Note: Adding newRenderer instead of renderer(). renderer() may be a child of newRenderer.
412     insertionPosition.insert(*newRenderer);
413
414     if (AXObjectCache* cache = m_document.axObjectCache())
415         cache->updateCacheAfterNodeIsAttached(&element);
416 }
417
418 static bool textRendererIsNeeded(const Text& textNode, const RenderTreePosition& renderTreePosition)
419 {
420     const RenderElement& parentRenderer = renderTreePosition.parent();
421     if (!parentRenderer.canHaveChildren())
422         return false;
423     if (parentRenderer.element() && !parentRenderer.element()->childShouldCreateRenderer(textNode))
424         return false;
425     if (textNode.isEditingText())
426         return true;
427     if (!textNode.length())
428         return false;
429     if (!textNode.containsOnlyWhitespace())
430         return true;
431     // This text node has nothing but white space. We may still need a renderer in some cases.
432     if (parentRenderer.isTable() || parentRenderer.isTableRow() || parentRenderer.isTableSection() || parentRenderer.isRenderTableCol() || parentRenderer.isFrameSet() || (parentRenderer.isFlexibleBox() && !parentRenderer.isRenderButton()))
433         return false;
434     if (parentRenderer.style().preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
435         return true;
436
437     RenderObject* previousRenderer = renderTreePosition.previousSiblingRenderer(textNode);
438     if (previousRenderer && previousRenderer->isBR()) // <span><br/> <br/></span>
439         return false;
440
441     if (parentRenderer.isRenderInline()) {
442         // <span><div/> <div/></span>
443         if (previousRenderer && !previousRenderer->isInline())
444             return false;
445     } else {
446         if (parentRenderer.isRenderBlock() && !parentRenderer.childrenInline() && (!previousRenderer || !previousRenderer->isInline()))
447             return false;
448         
449         RenderObject* first = parentRenderer.firstChild();
450         while (first && first->isFloatingOrOutOfFlowPositioned())
451             first = first->nextSibling();
452         RenderObject* nextRenderer = renderTreePosition.nextSiblingRenderer(textNode);
453         if (!first || nextRenderer == first) {
454             // Whitespace at the start of a block just goes away. Don't even make a render object for this text.
455             return false;
456         }
457     }
458     return true;
459 }
460
461 static void createTextRenderer(Text& textNode, RenderTreePosition& renderTreePosition)
462 {
463     ASSERT(!textNode.renderer());
464
465     auto newRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
466     ASSERT(newRenderer);
467
468     renderTreePosition.computeNextSibling(textNode);
469
470     if (!renderTreePosition.canInsert(*newRenderer))
471         return;
472
473     textNode.setRenderer(newRenderer.get());
474     renderTreePosition.insert(*newRenderer.leakPtr());
475 }
476
477 void RenderTreeUpdater::updateTextRenderer(Text& text, const Style::TextUpdate* textUpdate)
478 {
479     auto* existingRenderer = text.renderer();
480     bool needsRenderer = textRendererIsNeeded(text, renderTreePosition());
481     if (existingRenderer) {
482         if (needsRenderer) {
483             if (textUpdate)
484                 existingRenderer->setTextWithOffset(text.data(), textUpdate->offset, textUpdate->length);
485             return;
486         }
487         tearDownRenderer(text);
488         invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
489         return;
490     }
491     if (!needsRenderer)
492         return;
493     createTextRenderer(text, renderTreePosition());
494     invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
495 }
496
497 void RenderTreeUpdater::invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(Node& current)
498 {
499     // FIXME: This needs to traverse in composed tree order.
500
501     // This function finds sibling text renderers where the results of textRendererIsNeeded may have changed as a result of
502     // the current node gaining or losing the renderer. This can only affect white space text nodes.
503     for (Node* sibling = current.nextSibling(); sibling; sibling = sibling->nextSibling()) {
504         if (is<Element>(*sibling)) {
505             if (m_styleUpdate->elementUpdate(downcast<Element>(*sibling)))
506                 return;
507             // Text renderers beyond rendered elements can't be affected.
508             if (!sibling->renderer() || RenderTreePosition::isRendererReparented(*sibling->renderer()))
509                 continue;
510             return;
511         }
512         if (!is<Text>(*sibling))
513             continue;
514         Text& textSibling = downcast<Text>(*sibling);
515         if (m_styleUpdate->textUpdate(textSibling))
516             return;
517         if (!textSibling.containsOnlyWhitespace())
518             continue;
519         m_invalidatedWhitespaceOnlyTextSiblings.add(&textSibling);
520     }
521 }
522
523 void RenderTreeUpdater::tearDownRenderers(Element& root)
524 {
525     tearDownRenderers(root, TeardownType::Full);
526 }
527
528 void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownType)
529 {
530     WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
531
532     Vector<Element*, 30> teardownStack;
533
534     auto push = [&] (Element& element) {
535         if (element.hasCustomStyleResolveCallbacks())
536             element.willDetachRenderers();
537         teardownStack.append(&element);
538     };
539
540     auto& animationController = root.document().frame()->animation();
541
542     auto pop = [&] (unsigned depth) {
543         while (teardownStack.size() > depth) {
544             auto& element = *teardownStack.takeLast();
545
546             if (teardownType == TeardownType::Full || teardownType == TeardownType::RendererUpdateCancelingAnimations)
547                 animationController.cancelAnimations(element);
548
549             if (teardownType == TeardownType::Full)
550                 element.clearHoverAndActiveStatusBeforeDetachingRenderer();
551
552             element.clearStyleDerivedDataBeforeDetachingRenderer();
553
554             if (auto* renderer = element.renderer()) {
555                 renderer->destroyAndCleanupAnonymousWrappers();
556                 element.setRenderer(nullptr);
557             }
558             if (element.hasCustomStyleResolveCallbacks())
559                 element.didDetachRenderers();
560         }
561     };
562
563     push(root);
564
565     auto descendants = composedTreeDescendants(root);
566     for (auto it = descendants.begin(), end = descendants.end(); it != end; ++it) {
567         pop(it.depth());
568
569         if (is<Text>(*it)) {
570             tearDownRenderer(downcast<Text>(*it));
571             continue;
572         }
573
574         push(downcast<Element>(*it));
575     }
576
577     pop(0);
578 }
579
580 void RenderTreeUpdater::tearDownRenderer(Text& text)
581 {
582     auto* renderer = text.renderer();
583     if (!renderer)
584         return;
585     renderer->destroyAndCleanupAnonymousWrappers();
586     text.setRenderer(nullptr);
587 }
588
589 RenderView& RenderTreeUpdater::renderView()
590 {
591     return *m_document.renderView();
592 }
593
594 #if PLATFORM(IOS)
595 static EVisibility elementImplicitVisibility(const Element& element)
596 {
597     auto* renderer = element.renderer();
598     if (!renderer)
599         return VISIBLE;
600
601     auto& style = renderer->style();
602
603     auto width = style.width();
604     auto height = style.height();
605     if ((width.isFixed() && width.value() <= 0) || (height.isFixed() && height.value() <= 0))
606         return HIDDEN;
607
608     auto top = style.top();
609     auto left = style.left();
610     if (left.isFixed() && width.isFixed() && -left.value() >= width.value())
611         return HIDDEN;
612
613     if (top.isFixed() && height.isFixed() && -top.value() >= height.value())
614         return HIDDEN;
615     return VISIBLE;
616 }
617
618 CheckForVisibilityChange::CheckForVisibilityChange(const Element& element)
619     : m_element(element)
620     , m_previousDisplay(element.renderStyle() ? element.renderStyle()->display() : NONE)
621     , m_previousVisibility(element.renderStyle() ? element.renderStyle()->visibility() : HIDDEN)
622     , m_previousImplicitVisibility(WKObservingContentChanges() && WKObservedContentChange() != WKContentVisibilityChange ? elementImplicitVisibility(element) : VISIBLE)
623 {
624 }
625
626 CheckForVisibilityChange::~CheckForVisibilityChange()
627 {
628     if (!WKObservingContentChanges())
629         return;
630     if (m_element.isInUserAgentShadowTree())
631         return;
632     auto* style = m_element.renderStyle();
633     if (!style)
634         return;
635     if ((m_previousDisplay == NONE && style->display() != NONE) || (m_previousVisibility == HIDDEN && style->visibility() != HIDDEN)
636         || (m_previousImplicitVisibility == HIDDEN && elementImplicitVisibility(m_element) == VISIBLE))
637         WKSetObservedContentChange(WKContentVisibilityChange);
638 }
639 #endif
640
641 }