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