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 "FrameViewLayoutContext.h" |
28 | |
29 | #include "CSSAnimationController.h" |
30 | #include "DebugPageOverlays.h" |
31 | #include "Document.h" |
32 | #include "FrameView.h" |
33 | #include "InspectorInstrumentation.h" |
34 | #include "LayoutDisallowedScope.h" |
35 | #include "Logging.h" |
36 | #include "RenderElement.h" |
37 | #include "RenderLayoutState.h" |
38 | #include "RenderView.h" |
39 | #include "RuntimeEnabledFeatures.h" |
40 | #include "ScriptDisallowedScope.h" |
41 | #include "Settings.h" |
42 | |
43 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
44 | #include "FormattingState.h" |
45 | #include "LayoutContainer.h" |
46 | #include "LayoutState.h" |
47 | #include "LayoutTreeBuilder.h" |
48 | #endif |
49 | |
50 | #include <wtf/SetForScope.h> |
51 | #include <wtf/SystemTracing.h> |
52 | #include <wtf/text/TextStream.h> |
53 | |
54 | namespace WebCore { |
55 | |
56 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
57 | static void layoutUsingFormattingContext(const RenderView& renderView) |
58 | { |
59 | if (!RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextEnabled()) |
60 | return; |
61 | auto initialContainingBlock = Layout::TreeBuilder::createLayoutTree(renderView); |
62 | auto layoutState = std::make_unique<Layout::LayoutState>(*initialContainingBlock); |
63 | layoutState->setInQuirksMode(renderView.document().inQuirksMode()); |
64 | layoutState->updateLayout(); |
65 | layoutState->verifyAndOutputMismatchingLayoutTree(renderView); |
66 | } |
67 | #endif |
68 | |
69 | static bool isObjectAncestorContainerOf(RenderElement& ancestor, RenderElement& descendant) |
70 | { |
71 | for (auto* renderer = &descendant; renderer; renderer = renderer->container()) { |
72 | if (renderer == &ancestor) |
73 | return true; |
74 | } |
75 | return false; |
76 | } |
77 | |
78 | #ifndef NDEBUG |
79 | class RenderTreeNeedsLayoutChecker { |
80 | public : |
81 | RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot) |
82 | : m_layoutRoot(layoutRoot) |
83 | { |
84 | } |
85 | |
86 | ~RenderTreeNeedsLayoutChecker() |
87 | { |
88 | auto reportNeedsLayoutError = [] (const RenderObject& renderer) { |
89 | WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)" ); |
90 | renderer.showRenderTreeForThis(); |
91 | ASSERT_NOT_REACHED(); |
92 | }; |
93 | |
94 | if (m_layoutRoot.needsLayout()) { |
95 | reportNeedsLayoutError(m_layoutRoot); |
96 | return; |
97 | } |
98 | |
99 | for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) { |
100 | if (!descendant->needsLayout()) |
101 | continue; |
102 | |
103 | reportNeedsLayoutError(*descendant); |
104 | return; |
105 | } |
106 | } |
107 | |
108 | private: |
109 | const RenderElement& m_layoutRoot; |
110 | }; |
111 | #endif |
112 | |
113 | class LayoutScope { |
114 | public: |
115 | LayoutScope(FrameViewLayoutContext& layoutContext) |
116 | : m_view(layoutContext.view()) |
117 | , m_nestedState(layoutContext.m_layoutNestedState, layoutContext.m_layoutNestedState == FrameViewLayoutContext::LayoutNestedState::NotInLayout ? FrameViewLayoutContext::LayoutNestedState::NotNested : FrameViewLayoutContext::LayoutNestedState::Nested) |
118 | , m_schedulingIsEnabled(layoutContext.m_layoutSchedulingIsEnabled, false) |
119 | , m_previousScrollType(layoutContext.view().currentScrollType()) |
120 | { |
121 | m_view.setCurrentScrollType(ScrollType::Programmatic); |
122 | } |
123 | |
124 | ~LayoutScope() |
125 | { |
126 | m_view.setCurrentScrollType(m_previousScrollType); |
127 | } |
128 | |
129 | private: |
130 | FrameView& m_view; |
131 | SetForScope<FrameViewLayoutContext::LayoutNestedState> m_nestedState; |
132 | SetForScope<bool> m_schedulingIsEnabled; |
133 | ScrollType m_previousScrollType; |
134 | }; |
135 | |
136 | FrameViewLayoutContext::FrameViewLayoutContext(FrameView& frameView) |
137 | : m_frameView(frameView) |
138 | , m_layoutTimer(*this, &FrameViewLayoutContext::layoutTimerFired) |
139 | , m_asynchronousTasksTimer(*this, &FrameViewLayoutContext::runAsynchronousTasks) |
140 | { |
141 | } |
142 | |
143 | FrameViewLayoutContext::~FrameViewLayoutContext() |
144 | { |
145 | } |
146 | |
147 | void FrameViewLayoutContext::layout() |
148 | { |
149 | LOG_WITH_STREAM(Layout, stream << "FrameView " << &view() << " FrameViewLayoutContext::layout() with size " << view().layoutSize()); |
150 | |
151 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate()); |
152 | ASSERT(LayoutDisallowedScope::isLayoutAllowed()); |
153 | ASSERT(!view().isPainting()); |
154 | ASSERT(frame().view() == &view()); |
155 | ASSERT(frame().document()); |
156 | ASSERT(frame().document()->pageCacheState() == Document::NotInPageCache); |
157 | if (!canPerformLayout()) { |
158 | LOG(Layout, " is not allowed, bailing" ); |
159 | return; |
160 | } |
161 | |
162 | Ref<FrameView> protectView(view()); |
163 | LayoutScope layoutScope(*this); |
164 | TraceScope tracingScope(LayoutStart, LayoutEnd); |
165 | InspectorInstrumentationCookie inspectorLayoutScope(InspectorInstrumentation::willLayout(view().frame())); |
166 | AnimationUpdateBlock animationUpdateBlock(&view().frame().animation()); |
167 | WeakPtr<RenderElement> layoutRoot; |
168 | |
169 | m_layoutTimer.stop(); |
170 | m_delayedLayout = false; |
171 | m_setNeedsLayoutWasDeferred = false; |
172 | |
173 | #if !LOG_DISABLED |
174 | if (m_firstLayout && !frame().ownerElement()) |
175 | LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs" , this, document()->timeSinceDocumentCreation().value()); |
176 | #endif |
177 | #if PLATFORM(IOS_FAMILY) |
178 | if (view().updateFixedPositionLayoutRect() && subtreeLayoutRoot()) |
179 | convertSubtreeLayoutToFullLayout(); |
180 | #endif |
181 | if (handleLayoutWithFrameFlatteningIfNeeded()) |
182 | return; |
183 | |
184 | { |
185 | SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPreLayout); |
186 | |
187 | // If this is a new top-level layout and there are any remaining tasks from the previous layout, finish them now. |
188 | if (!isLayoutNested() && m_asynchronousTasksTimer.isActive() && !view().isInChildFrameWithFrameFlattening()) |
189 | runAsynchronousTasks(); |
190 | |
191 | updateStyleForLayout(); |
192 | if (view().hasOneRef()) |
193 | return; |
194 | |
195 | view().autoSizeIfEnabled(); |
196 | if (!renderView()) |
197 | return; |
198 | |
199 | layoutRoot = makeWeakPtr(subtreeLayoutRoot() ? subtreeLayoutRoot() : renderView()); |
200 | m_needsFullRepaint = is<RenderView>(layoutRoot.get()) && (m_firstLayout || renderView()->printing()); |
201 | view().willDoLayout(layoutRoot); |
202 | m_firstLayout = false; |
203 | } |
204 | { |
205 | SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InRenderTreeLayout); |
206 | ScriptDisallowedScope::InMainThread scriptDisallowedScope; |
207 | SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(subtreeLayoutRoot()); |
208 | RenderView::RepaintRegionAccumulator repaintRegionAccumulator(renderView()); |
209 | #ifndef NDEBUG |
210 | RenderTreeNeedsLayoutChecker checker(*layoutRoot); |
211 | #endif |
212 | layoutRoot->layout(); |
213 | #if ENABLE(LAYOUT_FORMATTING_CONTEXT) |
214 | layoutUsingFormattingContext(*renderView()); |
215 | #endif |
216 | ++m_layoutCount; |
217 | #if ENABLE(TEXT_AUTOSIZING) |
218 | applyTextSizingIfNeeded(*layoutRoot.get()); |
219 | #endif |
220 | clearSubtreeLayoutRoot(); |
221 | } |
222 | { |
223 | SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InViewSizeAdjust); |
224 | if (is<RenderView>(layoutRoot.get()) && !renderView()->printing()) { |
225 | // This is to protect m_needsFullRepaint's value when layout() is getting re-entered through adjustViewSize(). |
226 | SetForScope<bool> needsFullRepaint(m_needsFullRepaint); |
227 | view().adjustViewSize(); |
228 | // FIXME: Firing media query callbacks synchronously on nested frames could produced a detached FrameView here by |
229 | // navigating away from the current document (see webkit.org/b/173329). |
230 | if (view().hasOneRef()) |
231 | return; |
232 | } |
233 | } |
234 | { |
235 | SetForScope<LayoutPhase> layoutPhase(m_layoutPhase, LayoutPhase::InPostLayout); |
236 | if (m_needsFullRepaint) |
237 | renderView()->repaintRootContents(); |
238 | ASSERT(!layoutRoot->needsLayout()); |
239 | view().didLayout(layoutRoot); |
240 | runOrScheduleAsynchronousTasks(); |
241 | } |
242 | InspectorInstrumentation::didLayout(inspectorLayoutScope, *layoutRoot); |
243 | DebugPageOverlays::didLayout(view().frame()); |
244 | } |
245 | |
246 | void FrameViewLayoutContext::runOrScheduleAsynchronousTasks() |
247 | { |
248 | if (m_asynchronousTasksTimer.isActive()) |
249 | return; |
250 | |
251 | if (view().isInChildFrameWithFrameFlattening()) { |
252 | // While flattening frames, we defer post layout tasks to avoid getting stuck in a cycle, |
253 | // except updateWidgetPositions() which is required to kick off subframe layout in certain cases. |
254 | if (!m_inAsynchronousTasks) |
255 | view().updateWidgetPositions(); |
256 | m_asynchronousTasksTimer.startOneShot(0_s); |
257 | return; |
258 | } |
259 | |
260 | // If we are already in performPostLayoutTasks(), defer post layout tasks until after we return |
261 | // to avoid re-entrancy. |
262 | if (m_inAsynchronousTasks) { |
263 | m_asynchronousTasksTimer.startOneShot(0_s); |
264 | return; |
265 | } |
266 | |
267 | runAsynchronousTasks(); |
268 | if (needsLayout()) { |
269 | // If runAsynchronousTasks() made us layout again, let's defer the tasks until after we return. |
270 | m_asynchronousTasksTimer.startOneShot(0_s); |
271 | layout(); |
272 | } |
273 | } |
274 | |
275 | void FrameViewLayoutContext::runAsynchronousTasks() |
276 | { |
277 | m_asynchronousTasksTimer.stop(); |
278 | if (m_inAsynchronousTasks) |
279 | return; |
280 | SetForScope<bool> inAsynchronousTasks(m_inAsynchronousTasks, true); |
281 | view().performPostLayoutTasks(); |
282 | } |
283 | |
284 | void FrameViewLayoutContext::flushAsynchronousTasks() |
285 | { |
286 | if (!m_asynchronousTasksTimer.isActive()) |
287 | return; |
288 | runAsynchronousTasks(); |
289 | } |
290 | |
291 | void FrameViewLayoutContext::reset() |
292 | { |
293 | m_layoutPhase = LayoutPhase::OutsideLayout; |
294 | clearSubtreeLayoutRoot(); |
295 | m_layoutCount = 0; |
296 | m_layoutSchedulingIsEnabled = true; |
297 | m_delayedLayout = false; |
298 | m_layoutTimer.stop(); |
299 | m_firstLayout = true; |
300 | m_asynchronousTasksTimer.stop(); |
301 | m_needsFullRepaint = true; |
302 | } |
303 | |
304 | bool FrameViewLayoutContext::needsLayout() const |
305 | { |
306 | // This can return true in cases where the document does not have a body yet. |
307 | // Document::shouldScheduleLayout takes care of preventing us from scheduling |
308 | // layout in that case. |
309 | auto* renderView = this->renderView(); |
310 | return isLayoutPending() |
311 | || (renderView && renderView->needsLayout()) |
312 | || subtreeLayoutRoot() |
313 | || (m_disableSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred); |
314 | } |
315 | |
316 | void FrameViewLayoutContext::setNeedsLayoutAfterViewConfigurationChange() |
317 | { |
318 | if (m_disableSetNeedsLayoutCount) { |
319 | m_setNeedsLayoutWasDeferred = true; |
320 | return; |
321 | } |
322 | |
323 | if (auto* renderView = this->renderView()) { |
324 | ASSERT(!renderView->inHitTesting()); |
325 | renderView->setNeedsLayout(); |
326 | scheduleLayout(); |
327 | } |
328 | } |
329 | |
330 | void FrameViewLayoutContext::enableSetNeedsLayout() |
331 | { |
332 | ASSERT(m_disableSetNeedsLayoutCount); |
333 | if (!--m_disableSetNeedsLayoutCount) |
334 | m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen. |
335 | } |
336 | |
337 | void FrameViewLayoutContext::disableSetNeedsLayout() |
338 | { |
339 | ++m_disableSetNeedsLayoutCount; |
340 | } |
341 | |
342 | void FrameViewLayoutContext::scheduleLayout() |
343 | { |
344 | // FIXME: We should assert the page is not in the page cache, but that is causing |
345 | // too many false assertions. See <rdar://problem/7218118>. |
346 | ASSERT(frame().view() == &view()); |
347 | |
348 | if (subtreeLayoutRoot()) |
349 | convertSubtreeLayoutToFullLayout(); |
350 | if (!isLayoutSchedulingEnabled()) |
351 | return; |
352 | if (!needsLayout()) |
353 | return; |
354 | if (!frame().document()->shouldScheduleLayout()) |
355 | return; |
356 | InspectorInstrumentation::didInvalidateLayout(frame()); |
357 | // When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames. |
358 | // Also invalidate parent frame starting from the owner element of this frame. |
359 | if (frame().ownerRenderer() && view().isInChildFrameWithFrameFlattening()) |
360 | frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain); |
361 | |
362 | Seconds delay = frame().document()->minimumLayoutDelay(); |
363 | if (m_layoutTimer.isActive() && m_delayedLayout && !delay) |
364 | unscheduleLayout(); |
365 | |
366 | if (m_layoutTimer.isActive()) |
367 | return; |
368 | |
369 | m_delayedLayout = delay.value(); |
370 | |
371 | #if !LOG_DISABLED |
372 | if (!frame().document()->ownerElement()) |
373 | LOG(Layout, "FrameView %p scheduling layout for %.3fs" , this, delay.value()); |
374 | #endif |
375 | |
376 | m_layoutTimer.startOneShot(delay); |
377 | } |
378 | |
379 | void FrameViewLayoutContext::unscheduleLayout() |
380 | { |
381 | if (m_asynchronousTasksTimer.isActive()) |
382 | m_asynchronousTasksTimer.stop(); |
383 | |
384 | if (!m_layoutTimer.isActive()) |
385 | return; |
386 | |
387 | #if !LOG_DISABLED |
388 | if (!frame().document()->ownerElement()) |
389 | LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs" , this, frame().document()->timeSinceDocumentCreation().value()); |
390 | #endif |
391 | |
392 | m_layoutTimer.stop(); |
393 | m_delayedLayout = false; |
394 | } |
395 | |
396 | void FrameViewLayoutContext::scheduleSubtreeLayout(RenderElement& layoutRoot) |
397 | { |
398 | ASSERT(renderView()); |
399 | auto& renderView = *this->renderView(); |
400 | |
401 | // Try to catch unnecessary work during render tree teardown. |
402 | ASSERT(!renderView.renderTreeBeingDestroyed()); |
403 | ASSERT(frame().view() == &view()); |
404 | |
405 | if (renderView.needsLayout() && !subtreeLayoutRoot()) { |
406 | layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No); |
407 | return; |
408 | } |
409 | |
410 | if (!isLayoutPending() && isLayoutSchedulingEnabled()) { |
411 | Seconds delay = renderView.document().minimumLayoutDelay(); |
412 | ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout()); |
413 | setSubtreeLayoutRoot(layoutRoot); |
414 | InspectorInstrumentation::didInvalidateLayout(frame()); |
415 | m_delayedLayout = delay.value(); |
416 | m_layoutTimer.startOneShot(delay); |
417 | return; |
418 | } |
419 | |
420 | auto* subtreeLayoutRoot = this->subtreeLayoutRoot(); |
421 | if (subtreeLayoutRoot == &layoutRoot) |
422 | return; |
423 | |
424 | if (!subtreeLayoutRoot) { |
425 | // We already have a pending (full) layout. Just mark the subtree for layout. |
426 | layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No); |
427 | InspectorInstrumentation::didInvalidateLayout(frame()); |
428 | return; |
429 | } |
430 | |
431 | if (isObjectAncestorContainerOf(*subtreeLayoutRoot, layoutRoot)) { |
432 | // Keep the current root. |
433 | layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, subtreeLayoutRoot); |
434 | ASSERT(!subtreeLayoutRoot->container() || is<RenderView>(subtreeLayoutRoot->container()) || !subtreeLayoutRoot->container()->needsLayout()); |
435 | return; |
436 | } |
437 | |
438 | if (isObjectAncestorContainerOf(layoutRoot, *subtreeLayoutRoot)) { |
439 | // Re-root at newRelayoutRoot. |
440 | subtreeLayoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &layoutRoot); |
441 | setSubtreeLayoutRoot(layoutRoot); |
442 | ASSERT(!layoutRoot.container() || is<RenderView>(layoutRoot.container()) || !layoutRoot.container()->needsLayout()); |
443 | InspectorInstrumentation::didInvalidateLayout(frame()); |
444 | return; |
445 | } |
446 | // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead. |
447 | convertSubtreeLayoutToFullLayout(); |
448 | layoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No); |
449 | InspectorInstrumentation::didInvalidateLayout(frame()); |
450 | } |
451 | |
452 | void FrameViewLayoutContext::layoutTimerFired() |
453 | { |
454 | #if !LOG_DISABLED |
455 | if (!frame().document()->ownerElement()) |
456 | LOG(Layout, "FrameView %p layout timer fired at %.3fs" , this, frame().document()->timeSinceDocumentCreation().value()); |
457 | #endif |
458 | layout(); |
459 | } |
460 | |
461 | void FrameViewLayoutContext::convertSubtreeLayoutToFullLayout() |
462 | { |
463 | ASSERT(subtreeLayoutRoot()); |
464 | subtreeLayoutRoot()->markContainingBlocksForLayout(ScheduleRelayout::No); |
465 | clearSubtreeLayoutRoot(); |
466 | } |
467 | |
468 | void FrameViewLayoutContext::setSubtreeLayoutRoot(RenderElement& layoutRoot) |
469 | { |
470 | m_subtreeLayoutRoot = makeWeakPtr(layoutRoot); |
471 | } |
472 | |
473 | bool FrameViewLayoutContext::canPerformLayout() const |
474 | { |
475 | if (isInRenderTreeLayout()) |
476 | return false; |
477 | |
478 | if (layoutDisallowed()) |
479 | return false; |
480 | |
481 | if (view().isPainting()) |
482 | return false; |
483 | |
484 | if (!subtreeLayoutRoot() && !frame().document()->renderView()) |
485 | return false; |
486 | |
487 | return true; |
488 | } |
489 | |
490 | #if ENABLE(TEXT_AUTOSIZING) |
491 | void FrameViewLayoutContext::applyTextSizingIfNeeded(RenderElement& layoutRoot) |
492 | { |
493 | auto& settings = layoutRoot.settings(); |
494 | if (!settings.textAutosizingEnabled() || renderView()->printing()) |
495 | return; |
496 | bool idempotentMode = settings.textAutosizingUsesIdempotentMode(); |
497 | auto minimumZoomFontSize = settings.minimumZoomFontSize(); |
498 | if (!idempotentMode && !minimumZoomFontSize) |
499 | return; |
500 | auto textAutosizingWidth = layoutRoot.page().textAutosizingWidth(); |
501 | if (auto overrideWidth = settings.textAutosizingWindowSizeOverride().width()) |
502 | textAutosizingWidth = overrideWidth; |
503 | if (!idempotentMode && !textAutosizingWidth) |
504 | return; |
505 | layoutRoot.adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth); |
506 | if (!layoutRoot.needsLayout()) |
507 | return; |
508 | LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f" , minimumZoomFontSize, textAutosizingWidth); |
509 | layoutRoot.layout(); |
510 | } |
511 | #endif |
512 | |
513 | void FrameViewLayoutContext::updateStyleForLayout() |
514 | { |
515 | Document& document = *frame().document(); |
516 | |
517 | // FIXME: This shouldn't be necessary, but see rdar://problem/36670246. |
518 | if (!document.styleScope().resolverIfExists()) |
519 | document.styleScope().didChangeStyleSheetEnvironment(); |
520 | |
521 | // Viewport-dependent media queries may cause us to need completely different style information. |
522 | document.styleScope().evaluateMediaQueriesForViewportChange(); |
523 | |
524 | document.evaluateMediaQueryList(); |
525 | // If there is any pagination to apply, it will affect the RenderView's style, so we should |
526 | // take care of that now. |
527 | view().applyPaginationToViewport(); |
528 | // Always ensure our style info is up-to-date. This can happen in situations where |
529 | // the layout beats any sort of style recalc update that needs to occur. |
530 | document.updateStyleIfNeeded(); |
531 | } |
532 | |
533 | bool FrameViewLayoutContext::handleLayoutWithFrameFlatteningIfNeeded() |
534 | { |
535 | if (!view().isInChildFrameWithFrameFlattening()) |
536 | return false; |
537 | |
538 | startLayoutAtMainFrameViewIfNeeded(); |
539 | auto* layoutRoot = subtreeLayoutRoot() ? subtreeLayoutRoot() : frame().document()->renderView(); |
540 | return !layoutRoot || !layoutRoot->needsLayout(); |
541 | } |
542 | |
543 | void FrameViewLayoutContext::startLayoutAtMainFrameViewIfNeeded() |
544 | { |
545 | // When we start a layout at the child level as opposed to the topmost frame view and this child |
546 | // frame requires flattening, we need to re-initiate the layout at the topmost view. Layout |
547 | // will hit this view eventually. |
548 | auto* parentView = view().parentFrameView(); |
549 | if (!parentView) |
550 | return; |
551 | |
552 | // In the middle of parent layout, no need to restart from topmost. |
553 | if (parentView->layoutContext().isInLayout()) |
554 | return; |
555 | |
556 | // Parent tree is clean. Starting layout from it would have no effect. |
557 | if (!parentView->needsLayout()) |
558 | return; |
559 | |
560 | while (parentView->parentFrameView()) |
561 | parentView = parentView->parentFrameView(); |
562 | |
563 | LOG(Layout, " frame flattening, starting from root" ); |
564 | parentView->layoutContext().layout(); |
565 | } |
566 | |
567 | LayoutSize FrameViewLayoutContext::layoutDelta() const |
568 | { |
569 | if (auto* layoutState = this->layoutState()) |
570 | return layoutState->layoutDelta(); |
571 | return { }; |
572 | } |
573 | |
574 | void FrameViewLayoutContext::addLayoutDelta(const LayoutSize& delta) |
575 | { |
576 | if (auto* layoutState = this->layoutState()) |
577 | layoutState->addLayoutDelta(delta); |
578 | } |
579 | |
580 | #if !ASSERT_DISABLED |
581 | bool FrameViewLayoutContext::layoutDeltaMatches(const LayoutSize& delta) |
582 | { |
583 | if (auto* layoutState = this->layoutState()) |
584 | return layoutState->layoutDeltaMatches(delta); |
585 | return false; |
586 | } |
587 | #endif |
588 | |
589 | RenderLayoutState* FrameViewLayoutContext::layoutState() const |
590 | { |
591 | if (m_layoutStateStack.isEmpty()) |
592 | return nullptr; |
593 | return m_layoutStateStack.last().get(); |
594 | } |
595 | |
596 | void FrameViewLayoutContext::pushLayoutState(RenderElement& root) |
597 | { |
598 | ASSERT(!m_paintOffsetCacheDisableCount); |
599 | ASSERT(!layoutState()); |
600 | |
601 | m_layoutStateStack.append(std::make_unique<RenderLayoutState>(root)); |
602 | } |
603 | |
604 | bool FrameViewLayoutContext::(RenderBlockFlow& layoutRoot) |
605 | { |
606 | if (layoutState()) |
607 | return false; |
608 | m_layoutStateStack.append(std::make_unique<RenderLayoutState>(layoutRoot, RenderLayoutState::IsPaginated::Yes)); |
609 | return true; |
610 | } |
611 | |
612 | bool FrameViewLayoutContext::pushLayoutState(RenderBox& renderer, const LayoutSize& offset, LayoutUnit pageHeight, bool pageHeightChanged) |
613 | { |
614 | // We push LayoutState even if layoutState is disabled because it stores layoutDelta too. |
615 | auto* layoutState = this->layoutState(); |
616 | if (!layoutState || !needsFullRepaint() || layoutState->isPaginated() || renderer.enclosingFragmentedFlow() |
617 | || layoutState->lineGrid() || (renderer.style().lineGrid() != RenderStyle::initialLineGrid() && renderer.isRenderBlockFlow())) { |
618 | m_layoutStateStack.append(std::make_unique<RenderLayoutState>(m_layoutStateStack, renderer, offset, pageHeight, pageHeightChanged)); |
619 | return true; |
620 | } |
621 | return false; |
622 | } |
623 | |
624 | void FrameViewLayoutContext::popLayoutState() |
625 | { |
626 | m_layoutStateStack.removeLast(); |
627 | } |
628 | |
629 | #ifndef NDEBUG |
630 | void FrameViewLayoutContext::checkLayoutState() |
631 | { |
632 | ASSERT(layoutDeltaMatches(LayoutSize())); |
633 | ASSERT(!m_paintOffsetCacheDisableCount); |
634 | } |
635 | #endif |
636 | |
637 | Frame& FrameViewLayoutContext::frame() const |
638 | { |
639 | return view().frame(); |
640 | } |
641 | |
642 | FrameView& FrameViewLayoutContext::view() const |
643 | { |
644 | return m_frameView; |
645 | } |
646 | |
647 | RenderView* FrameViewLayoutContext::renderView() const |
648 | { |
649 | return view().renderView(); |
650 | } |
651 | |
652 | Document* FrameViewLayoutContext::document() const |
653 | { |
654 | return frame().document(); |
655 | } |
656 | |
657 | } // namespace WebCore |
658 | |