1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "RenderView.h"
23
24#include "Document.h"
25#include "Element.h"
26#include "FloatQuad.h"
27#include "FloatingObjects.h"
28#include "Frame.h"
29#include "FrameView.h"
30#include "GraphicsContext.h"
31#include "HTMLBodyElement.h"
32#include "HTMLFrameOwnerElement.h"
33#include "HTMLHtmlElement.h"
34#include "HTMLIFrameElement.h"
35#include "HitTestResult.h"
36#include "ImageQualityController.h"
37#include "NodeTraversal.h"
38#include "Page.h"
39#include "RenderDescendantIterator.h"
40#include "RenderGeometryMap.h"
41#include "RenderIterator.h"
42#include "RenderLayer.h"
43#include "RenderLayerBacking.h"
44#include "RenderLayerCompositor.h"
45#include "RenderLayoutState.h"
46#include "RenderMultiColumnFlow.h"
47#include "RenderMultiColumnSet.h"
48#include "RenderMultiColumnSpannerPlaceholder.h"
49#include "RenderQuote.h"
50#include "RenderTreeBuilder.h"
51#include "RenderWidget.h"
52#include "ScrollbarTheme.h"
53#include "Settings.h"
54#include "StyleInheritedData.h"
55#include "TransformState.h"
56#include <wtf/IsoMallocInlines.h>
57#include <wtf/SetForScope.h>
58#include <wtf/StackStats.h>
59
60namespace WebCore {
61
62WTF_MAKE_ISO_ALLOCATED_IMPL(RenderView);
63
64struct FrameFlatteningLayoutDisallower {
65 FrameFlatteningLayoutDisallower(FrameView& frameView)
66 : m_frameView(frameView)
67 , m_disallowLayout(frameView.effectiveFrameFlattening() != FrameFlattening::Disabled)
68 {
69 if (m_disallowLayout)
70 m_frameView.startDisallowingLayout();
71 }
72
73 ~FrameFlatteningLayoutDisallower()
74 {
75 if (m_disallowLayout)
76 m_frameView.endDisallowingLayout();
77 }
78
79private:
80 FrameView& m_frameView;
81 bool m_disallowLayout { false };
82};
83
84RenderView::RenderView(Document& document, RenderStyle&& style)
85 : RenderBlockFlow(document, WTFMove(style))
86 , m_frameView(*document.view())
87 , m_selection(*this)
88 , m_lazyRepaintTimer(*this, &RenderView::lazyRepaintTimerFired)
89{
90 setIsRenderView();
91
92 // FIXME: We should find a way to enforce this at compile time.
93 ASSERT(document.view());
94
95 // init RenderObject attributes
96 setInline(false);
97
98 m_minPreferredLogicalWidth = 0;
99 m_maxPreferredLogicalWidth = 0;
100
101 setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
102
103 setPositionState(PositionType::Absolute); // to 0,0 :)
104}
105
106RenderView::~RenderView()
107{
108 ASSERT_WITH_MESSAGE(m_rendererCount == 1, "All other renderers in this render tree should have been destroyed");
109}
110
111void RenderView::scheduleLazyRepaint(RenderBox& renderer)
112{
113 if (renderer.renderBoxNeedsLazyRepaint())
114 return;
115 renderer.setRenderBoxNeedsLazyRepaint(true);
116 m_renderersNeedingLazyRepaint.add(&renderer);
117 if (!m_lazyRepaintTimer.isActive())
118 m_lazyRepaintTimer.startOneShot(0_s);
119}
120
121void RenderView::unscheduleLazyRepaint(RenderBox& renderer)
122{
123 if (!renderer.renderBoxNeedsLazyRepaint())
124 return;
125 renderer.setRenderBoxNeedsLazyRepaint(false);
126 m_renderersNeedingLazyRepaint.remove(&renderer);
127 if (m_renderersNeedingLazyRepaint.isEmpty())
128 m_lazyRepaintTimer.stop();
129}
130
131void RenderView::lazyRepaintTimerFired()
132{
133 for (auto& renderer : m_renderersNeedingLazyRepaint) {
134 renderer->repaint();
135 renderer->setRenderBoxNeedsLazyRepaint(false);
136 }
137 m_renderersNeedingLazyRepaint.clear();
138}
139
140bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result)
141{
142 return hitTest(request, result.hitTestLocation(), result);
143}
144
145bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result)
146{
147 document().updateLayout();
148
149#if !ASSERT_DISABLED
150 SetForScope<bool> hitTestRestorer { m_inHitTesting, true };
151#endif
152
153 FrameFlatteningLayoutDisallower disallower(frameView());
154
155 bool resultLayer = layer()->hitTest(request, location, result);
156
157 // ScrollView scrollbars are not the same as RenderLayer scrollbars tested by RenderLayer::hitTestOverflowControls,
158 // so we need to test ScrollView scrollbars separately here. In case of using overlay scrollbars, the layer hit test
159 // will always work so we need to check the ScrollView scrollbars in that case too.
160 if (!resultLayer || ScrollbarTheme::theme().usesOverlayScrollbars()) {
161 // FIXME: Consider if this test should be done unconditionally.
162 if (request.allowsFrameScrollbars()) {
163 IntPoint windowPoint = frameView().contentsToWindow(location.roundedPoint());
164 if (Scrollbar* frameScrollbar = frameView().scrollbarAtPoint(windowPoint)) {
165 result.setScrollbar(frameScrollbar);
166 return true;
167 }
168 }
169 }
170
171 return resultLayer;
172}
173
174RenderBox::LogicalExtentComputedValues RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit) const
175{
176 return { !shouldUsePrintingLayout() ? LayoutUnit(viewLogicalHeight()) : logicalHeight, 0_lu, ComputedMarginValues() };
177}
178
179void RenderView::updateLogicalWidth()
180{
181 setLogicalWidth(shouldUsePrintingLayout() ? m_pageLogicalSize->width() : LayoutUnit(viewLogicalWidth()));
182}
183
184LayoutUnit RenderView::availableLogicalHeight(AvailableLogicalHeightType) const
185{
186 // Make sure block progression pagination for percentages uses the column extent and
187 // not the view's extent. See https://bugs.webkit.org/show_bug.cgi?id=135204.
188 if (multiColumnFlow() && multiColumnFlow()->firstMultiColumnSet())
189 return multiColumnFlow()->firstMultiColumnSet()->computedColumnHeight();
190
191#if PLATFORM(IOS_FAMILY)
192 // Workaround for <rdar://problem/7166808>.
193 if (document().isPluginDocument() && frameView().useFixedLayout())
194 return frameView().fixedLayoutSize().height();
195#endif
196 return isHorizontalWritingMode() ? frameView().visibleHeight() : frameView().visibleWidth();
197}
198
199bool RenderView::isChildAllowed(const RenderObject& child, const RenderStyle&) const
200{
201 return child.isBox();
202}
203
204void RenderView::layout()
205{
206 StackStats::LayoutCheckPoint layoutCheckPoint;
207 if (!document().paginated())
208 m_pageLogicalSize = { };
209
210 if (shouldUsePrintingLayout()) {
211 if (!m_pageLogicalSize)
212 m_pageLogicalSize = LayoutSize(logicalWidth(), 0_lu);
213 m_minPreferredLogicalWidth = m_pageLogicalSize->width();
214 m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth;
215 }
216
217 // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account.
218 bool relayoutChildren = !shouldUsePrintingLayout() && (width() != viewWidth() || height() != viewHeight());
219 if (relayoutChildren) {
220 setChildNeedsLayout(MarkOnlyThis);
221
222 for (auto& box : childrenOfType<RenderBox>(*this)) {
223 if (box.hasRelativeLogicalHeight()
224 || box.style().logicalHeight().isPercentOrCalculated()
225 || box.style().logicalMinHeight().isPercentOrCalculated()
226 || box.style().logicalMaxHeight().isPercentOrCalculated()
227 || box.isSVGRoot()
228 )
229 box.setChildNeedsLayout(MarkOnlyThis);
230 }
231 }
232
233 ASSERT(!frameView().layoutContext().layoutState());
234 if (!needsLayout())
235 return;
236
237 LayoutStateMaintainer statePusher(*this, { }, false, m_pageLogicalSize.valueOr(LayoutSize()).height(), m_pageLogicalHeightChanged);
238
239 m_pageLogicalHeightChanged = false;
240
241 RenderBlockFlow::layout();
242
243#ifndef NDEBUG
244 frameView().layoutContext().checkLayoutState();
245#endif
246 clearNeedsLayout();
247}
248
249LayoutUnit RenderView::pageOrViewLogicalHeight() const
250{
251 if (shouldUsePrintingLayout())
252 return m_pageLogicalSize->height();
253
254 if (multiColumnFlow() && !style().hasInlineColumnAxis()) {
255 if (int pageLength = frameView().pagination().pageLength)
256 return pageLength;
257 }
258
259 return viewLogicalHeight();
260}
261
262LayoutUnit RenderView::clientLogicalWidthForFixedPosition() const
263{
264 // FIXME: If the FrameView's fixedVisibleContentRect() is not empty, perhaps it should be consulted here too?
265 if (frameView().fixedElementsLayoutRelativeToFrame())
266 return (isHorizontalWritingMode() ? frameView().visibleWidth() : frameView().visibleHeight()) / frameView().frame().frameScaleFactor();
267
268#if PLATFORM(IOS_FAMILY)
269 if (frameView().useCustomFixedPositionLayoutRect())
270 return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().width() : frameView().customFixedPositionLayoutRect().height();
271#endif
272
273 if (settings().visualViewportEnabled())
274 return isHorizontalWritingMode() ? frameView().layoutViewportRect().width() : frameView().layoutViewportRect().height();
275
276 return clientLogicalWidth();
277}
278
279LayoutUnit RenderView::clientLogicalHeightForFixedPosition() const
280{
281 // FIXME: If the FrameView's fixedVisibleContentRect() is not empty, perhaps it should be consulted here too?
282 if (frameView().fixedElementsLayoutRelativeToFrame())
283 return (isHorizontalWritingMode() ? frameView().visibleHeight() : frameView().visibleWidth()) / frameView().frame().frameScaleFactor();
284
285#if PLATFORM(IOS_FAMILY)
286 if (frameView().useCustomFixedPositionLayoutRect())
287 return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().height() : frameView().customFixedPositionLayoutRect().width();
288#endif
289
290 if (settings().visualViewportEnabled())
291 return isHorizontalWritingMode() ? frameView().layoutViewportRect().height() : frameView().layoutViewportRect().width();
292
293 return clientLogicalHeight();
294}
295
296void RenderView::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
297{
298 // If a container was specified, and was not nullptr or the RenderView,
299 // then we should have found it by now.
300 ASSERT_ARG(repaintContainer, !repaintContainer || repaintContainer == this);
301 ASSERT_UNUSED(wasFixed, !wasFixed || *wasFixed == (mode & IsFixed));
302
303 if (mode & IsFixed)
304 transformState.move(toLayoutSize(frameView().scrollPositionRespectingCustomFixedPosition()));
305
306 if (!repaintContainer && mode & UseTransforms && shouldUseTransformFromContainer(nullptr)) {
307 TransformationMatrix t;
308 getTransformFromContainer(nullptr, LayoutSize(), t);
309 transformState.applyTransform(t);
310 }
311}
312
313const RenderObject* RenderView::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
314{
315 // If a container was specified, and was not nullptr or the RenderView,
316 // then we should have found it by now.
317 ASSERT_ARG(ancestorToStopAt, !ancestorToStopAt || ancestorToStopAt == this);
318
319 LayoutPoint scrollPosition = frameView().scrollPositionRespectingCustomFixedPosition();
320
321 if (!ancestorToStopAt && shouldUseTransformFromContainer(nullptr)) {
322 TransformationMatrix t;
323 getTransformFromContainer(nullptr, LayoutSize(), t);
324 geometryMap.pushView(this, toLayoutSize(scrollPosition), &t);
325 } else
326 geometryMap.pushView(this, toLayoutSize(scrollPosition));
327
328 return nullptr;
329}
330
331void RenderView::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
332{
333 if (mode & UseTransforms && shouldUseTransformFromContainer(nullptr)) {
334 TransformationMatrix t;
335 getTransformFromContainer(nullptr, LayoutSize(), t);
336 transformState.applyTransform(t);
337 }
338
339 if (mode & IsFixed)
340 transformState.move(toLayoutSize(frameView().scrollPositionRespectingCustomFixedPosition()));
341}
342
343bool RenderView::requiresColumns(int) const
344{
345 return frameView().pagination().mode != Pagination::Unpaginated;
346}
347
348void RenderView::computeColumnCountAndWidth()
349{
350 int columnWidth = contentLogicalWidth();
351 if (style().hasInlineColumnAxis()) {
352 if (int pageLength = frameView().pagination().pageLength)
353 columnWidth = pageLength;
354 }
355 setComputedColumnCountAndWidth(1, columnWidth);
356}
357
358void RenderView::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
359{
360 // If we ever require layout but receive a paint anyway, something has gone horribly wrong.
361 ASSERT(!needsLayout());
362 // RenderViews should never be called to paint with an offset not on device pixels.
363 ASSERT(LayoutPoint(IntPoint(paintOffset.x(), paintOffset.y())) == paintOffset);
364
365 // This avoids painting garbage between columns if there is a column gap.
366 if (frameView().pagination().mode != Pagination::Unpaginated && paintInfo.shouldPaintWithinRoot(*this))
367 paintInfo.context().fillRect(paintInfo.rect, frameView().baseBackgroundColor());
368
369 paintObject(paintInfo, paintOffset);
370}
371
372RenderElement* RenderView::rendererForRootBackground() const
373{
374 auto* firstChild = this->firstChild();
375 if (!firstChild)
376 return nullptr;
377 ASSERT(is<RenderElement>(*firstChild));
378 auto& documentRenderer = downcast<RenderElement>(*firstChild);
379
380 if (documentRenderer.hasBackground())
381 return &documentRenderer;
382
383 // We propagate the background only for HTML content.
384 if (!is<HTMLHtmlElement>(documentRenderer.element()))
385 return &documentRenderer;
386
387 if (auto* body = document().body()) {
388 if (auto* renderer = body->renderer())
389 return renderer;
390 }
391 return &documentRenderer;
392}
393
394static inline bool rendererObscuresBackground(const RenderElement& rootElement)
395{
396 auto& style = rootElement.style();
397 if (style.visibility() != Visibility::Visible || style.opacity() != 1 || style.hasTransform())
398 return false;
399
400 if (style.hasBorderRadius())
401 return false;
402
403 if (rootElement.isComposited())
404 return false;
405
406 auto* rendererForBackground = rootElement.view().rendererForRootBackground();
407 if (!rendererForBackground)
408 return false;
409
410 if (rendererForBackground->style().backgroundClip() == FillBox::Text)
411 return false;
412
413 return true;
414}
415
416void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&)
417{
418 if (!paintInfo.shouldPaintWithinRoot(*this))
419 return;
420
421 // Check to see if we are enclosed by a layer that requires complex painting rules. If so, we cannot blit
422 // when scrolling, and we need to use slow repaints. Examples of layers that require this are transparent layers,
423 // layers with reflections, or transformed layers.
424 // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being inside
425 // a transform, transparency layer, etc.
426 for (HTMLFrameOwnerElement* element = document().ownerElement(); element && element->renderer(); element = element->document().ownerElement()) {
427 RenderLayer* layer = element->renderer()->enclosingLayer();
428 if (layer->cannotBlitToWindow()) {
429 frameView().setCannotBlitToWindow();
430 break;
431 }
432
433 if (RenderLayer* compositingLayer = layer->enclosingCompositingLayerForRepaint()) {
434 if (!compositingLayer->backing()->paintsIntoWindow()) {
435 frameView().setCannotBlitToWindow();
436 break;
437 }
438 }
439 }
440
441 if (document().ownerElement())
442 return;
443
444 if (paintInfo.skipRootBackground())
445 return;
446
447 bool rootFillsViewport = false;
448 bool rootObscuresBackground = false;
449 Element* documentElement = document().documentElement();
450 if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr) {
451 // The document element's renderer is currently forced to be a block, but may not always be.
452 RenderBox* rootBox = is<RenderBox>(*rootRenderer) ? downcast<RenderBox>(rootRenderer) : nullptr;
453 rootFillsViewport = rootBox && !rootBox->x() && !rootBox->y() && rootBox->width() >= width() && rootBox->height() >= height();
454 rootObscuresBackground = rendererObscuresBackground(*rootRenderer);
455 }
456
457 compositor().rootBackgroundColorOrTransparencyChanged();
458
459 Page* page = document().page();
460 float pageScaleFactor = page ? page->pageScaleFactor() : 1;
461
462 // If painting will entirely fill the view, no need to fill the background.
463 if (rootFillsViewport && rootObscuresBackground && pageScaleFactor >= 1)
464 return;
465
466 // This code typically only executes if the root element's visibility has been set to hidden,
467 // if there is a transform on the <html>, or if there is a page scale factor less than 1.
468 // Only fill with a background color (typically white) if we're the root document,
469 // since iframes/frames with no background in the child document should show the parent's background.
470 // We use the base background color unless the backgroundShouldExtendBeyondPage setting is set,
471 // in which case we use the document's background color.
472 if (frameView().isTransparent()) // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent.
473 frameView().setCannotBlitToWindow(); // The parent must show behind the child.
474 else {
475 const Color& documentBackgroundColor = frameView().documentBackgroundColor();
476 const Color& backgroundColor = (settings().backgroundShouldExtendBeyondPage() && documentBackgroundColor.isValid()) ? documentBackgroundColor : frameView().baseBackgroundColor();
477 if (backgroundColor.isVisible()) {
478 CompositeOperator previousOperator = paintInfo.context().compositeOperation();
479 paintInfo.context().setCompositeOperation(CompositeCopy);
480 paintInfo.context().fillRect(paintInfo.rect, backgroundColor);
481 paintInfo.context().setCompositeOperation(previousOperator);
482 } else
483 paintInfo.context().clearRect(paintInfo.rect);
484 }
485}
486
487bool RenderView::shouldRepaint(const LayoutRect& rect) const
488{
489 return !printing() && !rect.isEmpty();
490}
491
492void RenderView::repaintRootContents()
493{
494 if (layer()->isComposited()) {
495 layer()->setBackingNeedsRepaint(GraphicsLayer::DoNotClipToLayer);
496 return;
497 }
498
499 // Always use layoutOverflowRect() to fix rdar://problem/27182267.
500 // This should be cleaned up via webkit.org/b/159913 and webkit.org/b/159914.
501 RenderLayerModelObject* repaintContainer = containerForRepaint();
502 repaintUsingContainer(repaintContainer, computeRectForRepaint(layoutOverflowRect(), repaintContainer));
503}
504
505void RenderView::repaintViewRectangle(const LayoutRect& repaintRect) const
506{
507 if (!shouldRepaint(repaintRect))
508 return;
509
510 // FIXME: enclosingRect is needed as long as we integral snap ScrollView/FrameView/RenderWidget size/position.
511 IntRect enclosingRect = enclosingIntRect(repaintRect);
512 if (auto ownerElement = document().ownerElement()) {
513 RenderBox* ownerBox = ownerElement->renderBox();
514 if (!ownerBox)
515 return;
516 LayoutRect viewRect = this->viewRect();
517#if PLATFORM(IOS_FAMILY)
518 // Don't clip using the visible rect since clipping is handled at a higher level on iPhone.
519 LayoutRect adjustedRect = enclosingRect;
520#else
521 LayoutRect adjustedRect = intersection(enclosingRect, viewRect);
522#endif
523 adjustedRect.moveBy(-viewRect.location());
524 adjustedRect.moveBy(ownerBox->contentBoxRect().location());
525
526 // A dirty rect in an iframe is relative to the contents of that iframe.
527 // When we traverse between parent frames and child frames, we need to make sure
528 // that the coordinate system is mapped appropriately between the iframe's contents
529 // and the Renderer that contains the iframe. This transformation must account for a
530 // left scrollbar (if one exists).
531 FrameView& frameView = this->frameView();
532 if (frameView.shouldPlaceBlockDirectionScrollbarOnLeft() && frameView.verticalScrollbar())
533 adjustedRect.move(LayoutSize(frameView.verticalScrollbar()->occupiedWidth(), 0));
534
535 ownerBox->repaintRectangle(adjustedRect);
536 return;
537 }
538
539 frameView().addTrackedRepaintRect(snapRectToDevicePixels(repaintRect, document().deviceScaleFactor()));
540 if (!m_accumulatedRepaintRegion) {
541 frameView().repaintContentRectangle(enclosingRect);
542 return;
543 }
544 m_accumulatedRepaintRegion->unite(enclosingRect);
545
546 // Region will get slow if it gets too complex. Merge all rects so far to bounds if this happens.
547 // FIXME: Maybe there should be a region type that does this automatically.
548 static const unsigned maximumRepaintRegionGridSize = 16 * 16;
549 if (m_accumulatedRepaintRegion->gridSize() > maximumRepaintRegionGridSize)
550 m_accumulatedRepaintRegion = std::make_unique<Region>(m_accumulatedRepaintRegion->bounds());
551}
552
553void RenderView::flushAccumulatedRepaintRegion() const
554{
555 ASSERT(!document().ownerElement());
556 ASSERT(m_accumulatedRepaintRegion);
557 auto repaintRects = m_accumulatedRepaintRegion->rects();
558 for (auto& rect : repaintRects)
559 frameView().repaintContentRectangle(rect);
560 m_accumulatedRepaintRegion = nullptr;
561}
562
563void RenderView::repaintViewAndCompositedLayers()
564{
565 repaintRootContents();
566
567 RenderLayerCompositor& compositor = this->compositor();
568 if (compositor.usesCompositing())
569 compositor.repaintCompositedLayers();
570}
571
572LayoutRect RenderView::visualOverflowRect() const
573{
574 if (frameView().paintsEntireContents())
575 return layoutOverflowRect();
576
577 return RenderBlockFlow::visualOverflowRect();
578}
579
580Optional<LayoutRect> RenderView::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
581{
582 // If a container was specified, and was not nullptr or the RenderView,
583 // then we should have found it by now.
584 ASSERT_ARG(container, !container || container == this);
585
586 if (printing())
587 return rect;
588
589 LayoutRect adjustedRect = rect;
590 if (style().isFlippedBlocksWritingMode()) {
591 // We have to flip by hand since the view's logical height has not been determined. We
592 // can use the viewport width and height.
593 if (style().isHorizontalWritingMode())
594 adjustedRect.setY(viewHeight() - adjustedRect.maxY());
595 else
596 adjustedRect.setX(viewWidth() - adjustedRect.maxX());
597 }
598
599 if (context.m_hasPositionFixedDescendant)
600 adjustedRect.moveBy(frameView().scrollPositionRespectingCustomFixedPosition());
601
602 // Apply our transform if we have one (because of full page zooming).
603 if (!container && layer() && layer()->transform())
604 adjustedRect = LayoutRect(layer()->transform()->mapRect(snapRectToDevicePixels(adjustedRect, document().deviceScaleFactor())));
605 return adjustedRect;
606}
607
608bool RenderView::isScrollableOrRubberbandableBox() const
609{
610 // The main frame might be allowed to rubber-band even if there is no content to scroll to. This is unique to
611 // the main frame; subframes and overflow areas have to have content that can be scrolled to in order to rubber-band.
612 FrameView::Scrollability defineScrollable = frame().ownerElement() ? FrameView::Scrollability::Scrollable : FrameView::Scrollability::ScrollableOrRubberbandable;
613 return frameView().isScrollable(defineScrollable);
614}
615
616void RenderView::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
617{
618 rects.append(snappedIntRect(accumulatedOffset, layer()->size()));
619}
620
621void RenderView::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
622{
623 if (wasFixed)
624 *wasFixed = false;
625 quads.append(FloatRect(FloatPoint(), layer()->size()));
626}
627
628bool RenderView::printing() const
629{
630 return document().printing();
631}
632
633bool RenderView::shouldUsePrintingLayout() const
634{
635 if (!printing())
636 return false;
637 return frameView().frame().shouldUsePrintingLayout();
638}
639
640LayoutRect RenderView::viewRect() const
641{
642 if (shouldUsePrintingLayout())
643 return LayoutRect(LayoutPoint(), size());
644 return frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
645}
646
647IntRect RenderView::unscaledDocumentRect() const
648{
649 LayoutRect overflowRect(layoutOverflowRect());
650 flipForWritingMode(overflowRect);
651 return snappedIntRect(overflowRect);
652}
653
654bool RenderView::rootBackgroundIsEntirelyFixed() const
655{
656 if (auto* rootBackgroundRenderer = rendererForRootBackground())
657 return rootBackgroundRenderer->style().hasEntirelyFixedBackground();
658 return false;
659}
660
661LayoutRect RenderView::unextendedBackgroundRect() const
662{
663 // FIXME: What is this? Need to patch for new columns?
664 return unscaledDocumentRect();
665}
666
667LayoutRect RenderView::backgroundRect() const
668{
669 // FIXME: New columns care about this?
670 if (frameView().hasExtendedBackgroundRectForPainting())
671 return frameView().extendedBackgroundRectForPainting();
672
673 return unextendedBackgroundRect();
674}
675
676IntRect RenderView::documentRect() const
677{
678 FloatRect overflowRect(unscaledDocumentRect());
679 if (hasTransform())
680 overflowRect = layer()->currentTransform().mapRect(overflowRect);
681 return IntRect(overflowRect);
682}
683
684int RenderView::viewHeight() const
685{
686 int height = 0;
687 if (!shouldUsePrintingLayout()) {
688 height = frameView().layoutHeight();
689 height = frameView().useFixedLayout() ? ceilf(style().effectiveZoom() * float(height)) : height;
690 }
691 return height;
692}
693
694int RenderView::viewWidth() const
695{
696 int width = 0;
697 if (!shouldUsePrintingLayout()) {
698 width = frameView().layoutWidth();
699 width = frameView().useFixedLayout() ? ceilf(style().effectiveZoom() * float(width)) : width;
700 }
701 return width;
702}
703
704int RenderView::viewLogicalHeight() const
705{
706 int height = style().isHorizontalWritingMode() ? viewHeight() : viewWidth();
707 return height;
708}
709
710void RenderView::setPageLogicalSize(LayoutSize size)
711{
712 if (!m_pageLogicalSize || m_pageLogicalSize->height() != size.height())
713 m_pageLogicalHeightChanged = true;
714
715 m_pageLogicalSize = size;
716}
717
718float RenderView::zoomFactor() const
719{
720 return frameView().frame().pageZoomFactor();
721}
722
723IntSize RenderView::viewportSizeForCSSViewportUnits() const
724{
725 return frameView().viewportSizeForCSSViewportUnits();
726}
727
728void RenderView::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
729{
730 if (result.innerNode())
731 return;
732
733 if (multiColumnFlow() && multiColumnFlow()->firstMultiColumnSet())
734 return multiColumnFlow()->firstMultiColumnSet()->updateHitTestResult(result, point);
735
736 Node* node = document().documentElement();
737 if (node) {
738 result.setInnerNode(node);
739 if (!result.innerNonSharedNode())
740 result.setInnerNonSharedNode(node);
741
742 LayoutPoint adjustedPoint = point;
743 offsetForContents(adjustedPoint);
744
745 result.setLocalPoint(adjustedPoint);
746 }
747}
748
749// FIXME: This function is obsolete and only used by embedded WebViews inside AppKit NSViews.
750// Do not add callers of this function!
751// The idea here is to take into account what object is moving the pagination point, and
752// thus choose the best place to chop it.
753void RenderView::setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak)
754{
755 // Nobody else can set a page break once we have a forced break.
756 if (m_legacyPrinting.m_forcedPageBreak)
757 return;
758
759 // Forced breaks always win over unforced breaks.
760 if (forcedBreak) {
761 m_legacyPrinting.m_forcedPageBreak = true;
762 m_legacyPrinting.m_bestTruncatedAt = y;
763 return;
764 }
765
766 // Prefer the widest object that tries to move the pagination point
767 LayoutRect boundingBox = forRenderer->borderBoundingBox();
768 if (boundingBox.width() > m_legacyPrinting.m_truncatorWidth) {
769 m_legacyPrinting.m_truncatorWidth = boundingBox.width();
770 m_legacyPrinting.m_bestTruncatedAt = y;
771 }
772}
773
774bool RenderView::usesCompositing() const
775{
776 return m_compositor && m_compositor->usesCompositing();
777}
778
779RenderLayerCompositor& RenderView::compositor()
780{
781 if (!m_compositor)
782 m_compositor = std::make_unique<RenderLayerCompositor>(*this);
783
784 return *m_compositor;
785}
786
787void RenderView::setIsInWindow(bool isInWindow)
788{
789 if (m_compositor)
790 m_compositor->setIsInWindow(isInWindow);
791}
792
793void RenderView::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
794{
795 RenderBlockFlow::styleDidChange(diff, oldStyle);
796
797 frameView().styleDidChange();
798}
799
800ImageQualityController& RenderView::imageQualityController()
801{
802 if (!m_imageQualityController)
803 m_imageQualityController = std::make_unique<ImageQualityController>(*this);
804 return *m_imageQualityController;
805}
806
807void RenderView::registerForVisibleInViewportCallback(RenderElement& renderer)
808{
809 ASSERT(!m_visibleInViewportRenderers.contains(&renderer));
810 m_visibleInViewportRenderers.add(&renderer);
811}
812
813void RenderView::unregisterForVisibleInViewportCallback(RenderElement& renderer)
814{
815 ASSERT(m_visibleInViewportRenderers.contains(&renderer));
816 m_visibleInViewportRenderers.remove(&renderer);
817}
818
819void RenderView::updateVisibleViewportRect(const IntRect& visibleRect)
820{
821 resumePausedImageAnimationsIfNeeded(visibleRect);
822
823 for (auto* renderer : m_visibleInViewportRenderers) {
824 auto state = visibleRect.intersects(enclosingIntRect(renderer->absoluteClippedOverflowRect())) ? VisibleInViewportState::Yes : VisibleInViewportState::No;
825 renderer->setVisibleInViewportState(state);
826 }
827}
828
829void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image)
830{
831 ASSERT(!renderer.hasPausedImageAnimations() || m_renderersWithPausedImageAnimation.contains(&renderer));
832
833 renderer.setHasPausedImageAnimations(true);
834 auto& images = m_renderersWithPausedImageAnimation.ensure(&renderer, [] {
835 return Vector<CachedImage*>();
836 }).iterator->value;
837 if (!images.contains(&image))
838 images.append(&image);
839}
840
841void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer)
842{
843 ASSERT(renderer.hasPausedImageAnimations());
844 ASSERT(m_renderersWithPausedImageAnimation.contains(&renderer));
845
846 renderer.setHasPausedImageAnimations(false);
847 m_renderersWithPausedImageAnimation.remove(&renderer);
848}
849
850void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image)
851{
852 ASSERT(renderer.hasPausedImageAnimations());
853
854 auto it = m_renderersWithPausedImageAnimation.find(&renderer);
855 ASSERT(it != m_renderersWithPausedImageAnimation.end());
856
857 auto& images = it->value;
858 if (!images.contains(&image))
859 return;
860
861 if (images.size() == 1)
862 removeRendererWithPausedImageAnimations(renderer);
863 else
864 images.removeFirst(&image);
865}
866
867void RenderView::resumePausedImageAnimationsIfNeeded(const IntRect& visibleRect)
868{
869 Vector<std::pair<RenderElement*, CachedImage*>, 10> toRemove;
870 for (auto& it : m_renderersWithPausedImageAnimation) {
871 auto* renderer = it.key;
872 for (auto* image : it.value) {
873 if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect, *image))
874 toRemove.append(std::make_pair(renderer, image));
875 }
876 }
877 for (auto& pair : toRemove)
878 removeRendererWithPausedImageAnimations(*pair.first, *pair.second);
879}
880
881RenderView::RepaintRegionAccumulator::RepaintRegionAccumulator(RenderView* view)
882{
883 if (!view)
884 return;
885
886 auto* rootRenderView = view->document().topDocument().renderView();
887 if (!rootRenderView)
888 return;
889
890 m_wasAccumulatingRepaintRegion = !!rootRenderView->m_accumulatedRepaintRegion;
891 if (!m_wasAccumulatingRepaintRegion)
892 rootRenderView->m_accumulatedRepaintRegion = std::make_unique<Region>();
893 m_rootView = makeWeakPtr(*rootRenderView);
894}
895
896RenderView::RepaintRegionAccumulator::~RepaintRegionAccumulator()
897{
898 if (m_wasAccumulatingRepaintRegion)
899 return;
900 if (!m_rootView)
901 return;
902 m_rootView.get()->flushAccumulatedRepaintRegion();
903}
904
905unsigned RenderView::pageNumberForBlockProgressionOffset(int offset) const
906{
907 int columnNumber = 0;
908 const Pagination& pagination = page().pagination();
909 if (pagination.mode == Pagination::Unpaginated)
910 return columnNumber;
911
912 bool progressionIsInline = false;
913 bool progressionIsReversed = false;
914
915 if (multiColumnFlow()) {
916 progressionIsInline = multiColumnFlow()->progressionIsInline();
917 progressionIsReversed = multiColumnFlow()->progressionIsReversed();
918 } else
919 return columnNumber;
920
921 if (!progressionIsInline) {
922 if (!progressionIsReversed)
923 columnNumber = (pagination.pageLength + pagination.gap - offset) / (pagination.pageLength + pagination.gap);
924 else
925 columnNumber = offset / (pagination.pageLength + pagination.gap);
926 }
927
928 return columnNumber;
929}
930
931unsigned RenderView::pageCount() const
932{
933 const Pagination& pagination = page().pagination();
934 if (pagination.mode == Pagination::Unpaginated)
935 return 0;
936
937 if (multiColumnFlow() && multiColumnFlow()->firstMultiColumnSet())
938 return multiColumnFlow()->firstMultiColumnSet()->columnCount();
939
940 return 0;
941}
942
943#if ENABLE(CSS_SCROLL_SNAP)
944void RenderView::registerBoxWithScrollSnapPositions(const RenderBox& box)
945{
946 m_boxesWithScrollSnapPositions.add(&box);
947}
948
949void RenderView::unregisterBoxWithScrollSnapPositions(const RenderBox& box)
950{
951 m_boxesWithScrollSnapPositions.remove(&box);
952}
953#endif
954
955} // namespace WebCore
956