Assert that no script is executed during LayoutPhase::InRenderTreeLayout
[WebKit-https.git] / Source / WebCore / page / LayoutContext.cpp
1 /*
2  * Copyright (C) 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "LayoutContext.h"
28
29 #include "CSSAnimationController.h"
30 #include "DebugPageOverlays.h"
31 #include "Document.h"
32 #include "FrameView.h"
33 #include "InspectorInstrumentation.h"
34 #include "Logging.h"
35 #include "NoEventDispatchAssertion.h"
36 #include "RenderElement.h"
37 #include "RenderView.h"
38 #include "Settings.h"
39
40 #include <wtf/SetForScope.h>
41 #include <wtf/SystemTracing.h>
42
43 namespace WebCore {
44
45 static bool isObjectAncestorContainerOf(RenderElement& ancestor, RenderElement& descendant)
46 {
47     for (auto* renderer = &descendant; renderer; renderer = renderer->container()) {
48         if (renderer == &ancestor)
49             return true;
50     }
51     return false;
52 }
53
54 class SubtreeLayoutStateMaintainer {
55 public:
56     SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
57         : m_subtreeLayoutRoot(subtreeLayoutRoot)
58     {
59         if (m_subtreeLayoutRoot) {
60             RenderView& view = m_subtreeLayoutRoot->view();
61             view.pushLayoutState(*m_subtreeLayoutRoot);
62             if (shouldDisableLayoutStateForSubtree()) {
63                 view.disableLayoutState();
64                 m_didDisableLayoutState = true;
65             }
66         }
67     }
68
69     ~SubtreeLayoutStateMaintainer()
70     {
71         if (m_subtreeLayoutRoot) {
72             RenderView& view = m_subtreeLayoutRoot->view();
73             view.popLayoutState(*m_subtreeLayoutRoot);
74             if (m_didDisableLayoutState)
75                 view.enableLayoutState();
76         }
77     }
78
79     bool shouldDisableLayoutStateForSubtree()
80     {
81         for (auto* renderer = m_subtreeLayoutRoot; renderer; renderer = renderer->container()) {
82             if (renderer->hasTransform() || renderer->hasReflection())
83                 return true;
84         }
85         return false;
86     }
87     
88 private:
89     RenderElement* m_subtreeLayoutRoot { nullptr };
90     bool m_didDisableLayoutState { false };
91 };
92
93 #ifndef NDEBUG
94 class RenderTreeNeedsLayoutChecker {
95 public :
96     RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot)
97         : m_layoutRoot(layoutRoot)
98     {
99     }
100
101     ~RenderTreeNeedsLayoutChecker()
102     {
103         auto reportNeedsLayoutError = [] (const RenderObject& renderer) {
104             WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)");
105             renderer.showRenderTreeForThis();
106             ASSERT_NOT_REACHED();
107         };
108
109         if (m_layoutRoot.needsLayout()) {
110             reportNeedsLayoutError(m_layoutRoot);
111             return;
112         }
113
114         for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) {
115             if (!descendant->needsLayout())
116                 continue;
117             
118             reportNeedsLayoutError(*descendant);
119             return;
120         }
121     }
122
123 private:
124     const RenderElement& m_layoutRoot;
125 };
126 #endif
127
128 class LayoutScope {
129 public:
130     LayoutScope(LayoutContext& layoutContext)
131         : m_view(layoutContext.view())
132         , m_nestedState(layoutContext.m_layoutNestedState, layoutContext.m_layoutNestedState == LayoutContext::LayoutNestedState::NotInLayout ? LayoutContext::LayoutNestedState::NotNested : LayoutContext::LayoutNestedState::Nested)
133         , m_schedulingIsEnabled(layoutContext.m_layoutSchedulingIsEnabled, false)
134         , m_inProgrammaticScroll(layoutContext.view().inProgrammaticScroll())
135     {
136         m_view.setInProgrammaticScroll(true);
137     }
138         
139     ~LayoutScope()
140     {
141         m_view.setInProgrammaticScroll(m_inProgrammaticScroll);
142     }
143         
144 private:
145     FrameView& m_view;
146     SetForScope<LayoutContext::LayoutNestedState> m_nestedState;
147     SetForScope<bool> m_schedulingIsEnabled;
148     bool m_inProgrammaticScroll { false };
149 };
150
151 LayoutContext::LayoutContext(FrameView& frameView)
152     : m_frameView(frameView)
153     , m_layoutTimer(*this, &LayoutContext::layoutTimerFired)
154     , m_asynchronousTasksTimer(*this, &LayoutContext::runAsynchronousTasks)
155 {
156 }
157
158 void LayoutContext::layout()
159 {
160     RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate());
161     ASSERT(!view().isPainting());
162     ASSERT(frame().view() == &view());
163     ASSERT(frame().document());
164     ASSERT(frame().document()->pageCacheState() == Document::NotInPageCache);
165     if (!canPerformLayout()) {
166         LOG(Layout, "  is not allowed, bailing");
167         return;
168     }
169
170     Ref<FrameView> protectView(view());
171     LayoutScope layoutScope(*this);
172     TraceScope tracingScope(LayoutStart, LayoutEnd);
173     InspectorInstrumentationCookie inspectorLayoutScope(InspectorInstrumentation::willLayout(view().frame()));
174     AnimationUpdateBlock animationUpdateBlock(&view().frame().animation());
175     WeakPtr<RenderElement> layoutRoot;
176     
177     m_layoutTimer.stop();
178     m_delayedLayout = false;
179     m_setNeedsLayoutWasDeferred = false;
180
181 #if !LOG_DISABLED
182     if (m_firstLayout && !frame().ownerElement())
183         LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs\n", this, document()->timeSinceDocumentCreation().value());
184 #endif
185 #if PLATFORM(IOS)
186     if (view().updateFixedPositionLayoutRect() && subtreeLayoutRoot())
187         convertSubtreeLayoutToFullLayout();
188 #endif
189     if (handleLayoutWithFrameFlatteningIfNeeded())
190         return;
191
192     {
193         SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPreLayout);
194
195         // If this is a new top-level layout and there are any remaining tasks from the previous layout, finish them now.
196         if (!isLayoutNested() && m_asynchronousTasksTimer.isActive() && !view().isInChildFrameWithFrameFlattening())
197             runAsynchronousTasks();
198
199         updateStyleForLayout();
200         if (view().hasOneRef())
201             return;
202
203         view().autoSizeIfEnabled();
204         if (!renderView())
205             return;
206
207         layoutRoot = makeWeakPtr(subtreeLayoutRoot() ? subtreeLayoutRoot() : renderView());
208         m_needsFullRepaint = is<RenderView>(layoutRoot.get()) && (m_firstLayout || renderView()->printing());
209         view().willDoLayout(layoutRoot);
210         m_firstLayout = false;
211     }
212     {
213         SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InRenderTreeLayout);
214         NoEventDispatchAssertion noEventDispatchAssertion;
215         SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(subtreeLayoutRoot());
216         RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView());
217 #ifndef NDEBUG
218         RenderTreeNeedsLayoutChecker checker(*layoutRoot);
219 #endif
220         layoutRoot->layout();
221         ++m_layoutCount;
222 #if ENABLE(TEXT_AUTOSIZING)
223         applyTextSizingIfNeeded(*layoutRoot.get());
224 #endif
225         clearSubtreeLayoutRoot();
226     }
227     {
228         SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InViewSizeAdjust);
229         if (is<RenderView>(layoutRoot.get()) && !renderView()->printing()) {
230             // This is to protect m_needsFullRepaint's value when layout() is getting re-entered through adjustViewSize().
231             SetForScope<bool> needsFullRepaint(m_needsFullRepaint);
232             view().adjustViewSize();
233             // FIXME: Firing media query callbacks synchronously on nested frames could produced a detached FrameView here by
234             // navigating away from the current document (see webkit.org/b/173329).
235             if (view().hasOneRef())
236                 return;
237         }
238     }
239     {
240         SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPostLayout);
241         if (m_needsFullRepaint)
242             renderView()->repaintRootContents();
243         ASSERT(!layoutRoot->needsLayout());
244         view().didLayout(layoutRoot);
245         runOrScheduleAsynchronousTasks();
246     }
247     InspectorInstrumentation::didLayout(inspectorLayoutScope, *layoutRoot);
248     DebugPageOverlays::didLayout(view().frame());
249 }
250
251 void LayoutContext::runOrScheduleAsynchronousTasks()
252 {
253     if (m_asynchronousTasksTimer.isActive())
254         return;
255
256     if (view().isInChildFrameWithFrameFlattening()) {
257         // While flattening frames, we defer post layout tasks to avoid getting stuck in a cycle,
258         // except updateWidgetPositions() which is required to kick off subframe layout in certain cases.
259         if (!m_inAsynchronousTasks)
260             view().updateWidgetPositions();
261         m_asynchronousTasksTimer.startOneShot(0_s);
262         return;
263     }
264
265     // If we are already in performPostLayoutTasks(), defer post layout tasks until after we return
266     // to avoid re-entrancy.
267     if (m_inAsynchronousTasks) {
268         m_asynchronousTasksTimer.startOneShot(0_s);
269         return;
270     }
271
272     runAsynchronousTasks();
273     if (needsLayout()) {
274         // If runAsynchronousTasks() made us layout again, let's defer the tasks until after we return.
275         m_asynchronousTasksTimer.startOneShot(0_s);
276         layout();
277     }
278 }
279
280 void LayoutContext::runAsynchronousTasks()
281 {
282     m_asynchronousTasksTimer.stop();
283     if (m_inAsynchronousTasks)
284         return;
285     SetForScope<bool> inAsynchronousTasks(m_inAsynchronousTasks, true);
286     view().performPostLayoutTasks();
287 }
288
289 void LayoutContext::flushAsynchronousTasks()
290 {
291     if (!m_asynchronousTasksTimer.isActive())
292         return;
293     runAsynchronousTasks();
294 }
295
296 void LayoutContext::reset()
297 {
298     m_layoutPhase = LayoutPhase::OutsideLayout;
299     clearSubtreeLayoutRoot();
300     m_layoutCount = 0;
301     m_layoutSchedulingIsEnabled = true;
302     m_delayedLayout = false;
303     m_layoutTimer.stop();
304     m_firstLayout = true;
305     m_asynchronousTasksTimer.stop();
306     m_needsFullRepaint = true;
307 }
308
309 bool LayoutContext::needsLayout() const
310 {
311     // This can return true in cases where the document does not have a body yet.
312     // Document::shouldScheduleLayout takes care of preventing us from scheduling
313     // layout in that case.
314     auto* renderView = this->renderView();
315     return isLayoutPending()
316         || (renderView && renderView->needsLayout())
317         || subtreeLayoutRoot()
318         || (m_disableSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred);
319 }
320
321 void LayoutContext::setNeedsLayout()
322 {
323     if (m_disableSetNeedsLayoutCount) {
324         m_setNeedsLayoutWasDeferred = true;
325         return;
326     }
327
328     if (auto* renderView = this->renderView()) {
329         ASSERT(!renderView->inHitTesting());
330         renderView->setNeedsLayout();
331     }
332 }
333
334 void LayoutContext::enableSetNeedsLayout()
335 {
336     ASSERT(m_disableSetNeedsLayoutCount);
337     if (!--m_disableSetNeedsLayoutCount)
338         m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen.
339 }
340
341 void LayoutContext::disableSetNeedsLayout()
342 {
343     ++m_disableSetNeedsLayoutCount;
344 }
345
346 void LayoutContext::scheduleLayout()
347 {
348     // FIXME: We should assert the page is not in the page cache, but that is causing
349     // too many false assertions. See <rdar://problem/7218118>.
350     ASSERT(frame().view() == &view());
351
352     if (subtreeLayoutRoot())
353         convertSubtreeLayoutToFullLayout();
354     if (!isLayoutSchedulingEnabled())
355         return;
356     if (!needsLayout())
357         return;
358     if (!frame().document()->shouldScheduleLayout())
359         return;
360     InspectorInstrumentation::didInvalidateLayout(frame());
361     // When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames.
362     // Also invalidate parent frame starting from the owner element of this frame.
363     if (frame().ownerRenderer() && view().isInChildFrameWithFrameFlattening())
364         frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain);
365
366     Seconds delay = frame().document()->minimumLayoutDelay();
367     if (m_layoutTimer.isActive() && m_delayedLayout && !delay)
368         unscheduleLayout();
369
370     if (m_layoutTimer.isActive())
371         return;
372
373     m_delayedLayout = delay.value();
374
375 #if !LOG_DISABLED
376     if (!frame().document()->ownerElement())
377         LOG(Layout, "FrameView %p scheduling layout for %.3fs", this, delay.value());
378 #endif
379
380     m_layoutTimer.startOneShot(delay);
381 }
382
383 void LayoutContext::unscheduleLayout()
384 {
385     if (m_asynchronousTasksTimer.isActive())
386         m_asynchronousTasksTimer.stop();
387
388     if (!m_layoutTimer.isActive())
389         return;
390
391 #if !LOG_DISABLED
392     if (!frame().document()->ownerElement())
393         LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
394 #endif
395
396     m_layoutTimer.stop();
397     m_delayedLayout = false;
398 }
399
400 void LayoutContext::scheduleSubtreeLayout(RenderElement& layoutRoot)
401 {
402     ASSERT(renderView());
403     auto& renderView = *this->renderView();
404
405     // Try to catch unnecessary work during render tree teardown.
406     ASSERT(!renderView.renderTreeBeingDestroyed());
407     ASSERT(frame().view() == &view());
408
409     if (renderView.needsLayout() && !subtreeLayoutRoot()) {
410         layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
411         return;
412     }
413
414     if (!isLayoutPending() && isLayoutSchedulingEnabled()) {
415         Seconds delay = renderView.document().minimumLayoutDelay();
416         ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
417         setSubtreeLayoutRoot(layoutRoot);
418         InspectorInstrumentation::didInvalidateLayout(frame());
419         m_delayedLayout = delay.value();
420         m_layoutTimer.startOneShot(delay);
421         return;
422     }
423
424     auto* subtreeLayoutRoot = this->subtreeLayoutRoot();
425     if (subtreeLayoutRoot == &layoutRoot)
426         return;
427
428     if (!subtreeLayoutRoot) {
429         // We already have a pending (full) layout. Just mark the subtree for layout.
430         layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
431         InspectorInstrumentation::didInvalidateLayout(frame());
432         return;
433     }
434
435     if (isObjectAncestorContainerOf(*subtreeLayoutRoot, layoutRoot)) {
436         // Keep the current root.
437         layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, subtreeLayoutRoot);
438         ASSERT(!subtreeLayoutRoot->container() || is<RenderView>(subtreeLayoutRoot->container()) || !subtreeLayoutRoot->container()->needsLayout());
439         return;
440     }
441
442     if (isObjectAncestorContainerOf(layoutRoot, *subtreeLayoutRoot)) {
443         // Re-root at newRelayoutRoot.
444         subtreeLayoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &layoutRoot);
445         setSubtreeLayoutRoot(layoutRoot);
446         ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout());
447         InspectorInstrumentation::didInvalidateLayout(frame());
448         return;
449     }
450     // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead.
451     convertSubtreeLayoutToFullLayout();
452     layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No);
453     InspectorInstrumentation::didInvalidateLayout(frame());
454 }
455
456 void LayoutContext::layoutTimerFired()
457 {
458 #if !LOG_DISABLED
459     if (!frame().document()->ownerElement())
460         LOG(Layout, "FrameView %p layout timer fired at %.3fs", this, frame().document()->timeSinceDocumentCreation().value());
461 #endif
462     layout();
463 }
464
465 void LayoutContext::convertSubtreeLayoutToFullLayout()
466 {
467     ASSERT(subtreeLayoutRoot());
468     subtreeLayoutRoot()->markContainingBlocksForLayout(ScheduleRelayout::No);
469     clearSubtreeLayoutRoot();
470 }
471
472 void LayoutContext::setSubtreeLayoutRoot(RenderElement& layoutRoot)
473 {
474     m_subtreeLayoutRoot = makeWeakPtr(layoutRoot);
475 }
476
477 bool LayoutContext::canPerformLayout() const
478 {
479     if (isInRenderTreeLayout())
480         return false;
481
482     if (layoutDisallowed())
483         return false;
484
485     if (view().isPainting())
486         return false;
487
488     if (!subtreeLayoutRoot() && !frame().document()->renderView())
489         return false;
490
491     return true;
492 }
493
494 #if ENABLE(TEXT_AUTOSIZING)
495 void LayoutContext::applyTextSizingIfNeeded(RenderElement& layoutRoot)
496 {
497     auto& settings = layoutRoot.settings();
498     if (!settings.textAutosizingEnabled() || renderView()->printing())
499         return;
500     auto minimumZoomFontSize = settings.minimumZoomFontSize();
501     if (!minimumZoomFontSize)
502         return;
503     auto textAutosizingWidth = layoutRoot.page().textAutosizingWidth();
504     if (auto overrideWidth = settings.textAutosizingWindowSizeOverride().width())
505         textAutosizingWidth = overrideWidth;
506     if (!textAutosizingWidth)
507         return;
508     layoutRoot.adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth);
509     if (!layoutRoot.needsLayout())
510         return;
511     LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth);
512     layoutRoot.layout();
513 }
514 #endif
515
516 void LayoutContext::updateStyleForLayout()
517 {
518     Document& document = *frame().document();
519     // Viewport-dependent media queries may cause us to need completely different style information.
520     auto* styleResolver = document.styleScope().resolverIfExists();
521     if (!styleResolver || styleResolver->hasMediaQueriesAffectedByViewportChange()) {
522         LOG(Layout, "  hasMediaQueriesAffectedByViewportChange, enqueueing style recalc");
523         document.styleScope().didChangeStyleSheetEnvironment();
524         // FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds.
525         InspectorInstrumentation::mediaQueryResultChanged(document);
526     }
527     document.evaluateMediaQueryList();
528     // If there is any pagination to apply, it will affect the RenderView's style, so we should
529     // take care of that now.
530     view().applyPaginationToViewport();
531     // Always ensure our style info is up-to-date. This can happen in situations where
532     // the layout beats any sort of style recalc update that needs to occur.
533     document.updateStyleIfNeeded();
534 }
535
536 bool LayoutContext::handleLayoutWithFrameFlatteningIfNeeded()
537 {
538     if (!view().isInChildFrameWithFrameFlattening())
539         return false;
540     
541     if (!view().frameFlatteningViewSizeForMediaQueryIsSet()) {
542         LOG_WITH_STREAM(MediaQueries, stream << "FrameView " << this << " snapshotting size " <<  view().layoutSize() << " for media queries");
543         view().setFrameFlatteningViewSizeForMediaQuery();
544     }
545     startLayoutAtMainFrameViewIfNeeded();
546     auto* layoutRoot = subtreeLayoutRoot() ? subtreeLayoutRoot() : frame().document()->renderView();
547     return !layoutRoot || !layoutRoot->needsLayout();
548 }
549
550 void LayoutContext::startLayoutAtMainFrameViewIfNeeded()
551 {
552     // When we start a layout at the child level as opposed to the topmost frame view and this child
553     // frame requires flattening, we need to re-initiate the layout at the topmost view. Layout
554     // will hit this view eventually.
555     auto* parentView = view().parentFrameView();
556     if (!parentView)
557         return;
558
559     // In the middle of parent layout, no need to restart from topmost.
560     if (parentView->layoutContext().isInLayout())
561         return;
562
563     // Parent tree is clean. Starting layout from it would have no effect.
564     if (!parentView->needsLayout())
565         return;
566
567     while (parentView->parentFrameView())
568         parentView = parentView->parentFrameView();
569
570     LOG(Layout, "  frame flattening, starting from root");
571     parentView->layoutContext().layout();
572 }
573
574 Frame& LayoutContext::frame() const
575 {
576     return view().frame();
577 }
578
579 FrameView& LayoutContext::view() const
580 {
581     return m_frameView;
582 }
583
584 RenderView* LayoutContext::renderView() const
585 {
586     return view().renderView();
587 }
588
589 Document* LayoutContext::document() const
590 {
591     return frame().document();
592 }
593
594 } // namespace WebCore