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