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 | |
60 | namespace WebCore { |
61 | |
62 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderView); |
63 | |
64 | struct 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 | |
79 | private: |
80 | FrameView& m_frameView; |
81 | bool m_disallowLayout { false }; |
82 | }; |
83 | |
84 | RenderView::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 | |
106 | RenderView::~RenderView() |
107 | { |
108 | ASSERT_WITH_MESSAGE(m_rendererCount == 1, "All other renderers in this render tree should have been destroyed" ); |
109 | } |
110 | |
111 | void 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 | |
121 | void 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 | |
131 | void RenderView::lazyRepaintTimerFired() |
132 | { |
133 | for (auto& renderer : m_renderersNeedingLazyRepaint) { |
134 | renderer->repaint(); |
135 | renderer->setRenderBoxNeedsLazyRepaint(false); |
136 | } |
137 | m_renderersNeedingLazyRepaint.clear(); |
138 | } |
139 | |
140 | bool RenderView::hitTest(const HitTestRequest& request, HitTestResult& result) |
141 | { |
142 | return hitTest(request, result.hitTestLocation(), result); |
143 | } |
144 | |
145 | bool 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 | |
174 | RenderBox::LogicalExtentComputedValues RenderView::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit) const |
175 | { |
176 | return { !shouldUsePrintingLayout() ? LayoutUnit(viewLogicalHeight()) : logicalHeight, 0_lu, ComputedMarginValues() }; |
177 | } |
178 | |
179 | void RenderView::updateLogicalWidth() |
180 | { |
181 | setLogicalWidth(shouldUsePrintingLayout() ? m_pageLogicalSize->width() : LayoutUnit(viewLogicalWidth())); |
182 | } |
183 | |
184 | LayoutUnit 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 | |
199 | bool RenderView::isChildAllowed(const RenderObject& child, const RenderStyle&) const |
200 | { |
201 | return child.isBox(); |
202 | } |
203 | |
204 | void 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 | |
249 | LayoutUnit 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 | |
262 | LayoutUnit 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 | |
279 | LayoutUnit 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 | |
296 | void 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 | |
313 | const 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 | |
331 | void 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 | |
343 | bool RenderView::requiresColumns(int) const |
344 | { |
345 | return frameView().pagination().mode != Pagination::Unpaginated; |
346 | } |
347 | |
348 | void 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 | |
358 | void 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 | |
372 | RenderElement* 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 | |
394 | static 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 | |
416 | void 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 | |
487 | bool RenderView::shouldRepaint(const LayoutRect& rect) const |
488 | { |
489 | return !printing() && !rect.isEmpty(); |
490 | } |
491 | |
492 | void 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 | |
505 | void 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 | |
553 | void 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 | |
563 | void RenderView::repaintViewAndCompositedLayers() |
564 | { |
565 | repaintRootContents(); |
566 | |
567 | RenderLayerCompositor& compositor = this->compositor(); |
568 | if (compositor.usesCompositing()) |
569 | compositor.repaintCompositedLayers(); |
570 | } |
571 | |
572 | LayoutRect RenderView::visualOverflowRect() const |
573 | { |
574 | if (frameView().paintsEntireContents()) |
575 | return layoutOverflowRect(); |
576 | |
577 | return RenderBlockFlow::visualOverflowRect(); |
578 | } |
579 | |
580 | Optional<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 | |
608 | bool 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 | |
616 | void RenderView::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const |
617 | { |
618 | rects.append(snappedIntRect(accumulatedOffset, layer()->size())); |
619 | } |
620 | |
621 | void RenderView::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const |
622 | { |
623 | if (wasFixed) |
624 | *wasFixed = false; |
625 | quads.append(FloatRect(FloatPoint(), layer()->size())); |
626 | } |
627 | |
628 | bool RenderView::printing() const |
629 | { |
630 | return document().printing(); |
631 | } |
632 | |
633 | bool RenderView::shouldUsePrintingLayout() const |
634 | { |
635 | if (!printing()) |
636 | return false; |
637 | return frameView().frame().shouldUsePrintingLayout(); |
638 | } |
639 | |
640 | LayoutRect RenderView::viewRect() const |
641 | { |
642 | if (shouldUsePrintingLayout()) |
643 | return LayoutRect(LayoutPoint(), size()); |
644 | return frameView().visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); |
645 | } |
646 | |
647 | IntRect RenderView::unscaledDocumentRect() const |
648 | { |
649 | LayoutRect overflowRect(layoutOverflowRect()); |
650 | flipForWritingMode(overflowRect); |
651 | return snappedIntRect(overflowRect); |
652 | } |
653 | |
654 | bool RenderView::rootBackgroundIsEntirelyFixed() const |
655 | { |
656 | if (auto* rootBackgroundRenderer = rendererForRootBackground()) |
657 | return rootBackgroundRenderer->style().hasEntirelyFixedBackground(); |
658 | return false; |
659 | } |
660 | |
661 | LayoutRect RenderView::unextendedBackgroundRect() const |
662 | { |
663 | // FIXME: What is this? Need to patch for new columns? |
664 | return unscaledDocumentRect(); |
665 | } |
666 | |
667 | LayoutRect 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 | |
676 | IntRect RenderView::documentRect() const |
677 | { |
678 | FloatRect overflowRect(unscaledDocumentRect()); |
679 | if (hasTransform()) |
680 | overflowRect = layer()->currentTransform().mapRect(overflowRect); |
681 | return IntRect(overflowRect); |
682 | } |
683 | |
684 | int 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 | |
694 | int 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 | |
704 | int RenderView::viewLogicalHeight() const |
705 | { |
706 | int height = style().isHorizontalWritingMode() ? viewHeight() : viewWidth(); |
707 | return height; |
708 | } |
709 | |
710 | void 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 | |
718 | float RenderView::zoomFactor() const |
719 | { |
720 | return frameView().frame().pageZoomFactor(); |
721 | } |
722 | |
723 | IntSize RenderView::viewportSizeForCSSViewportUnits() const |
724 | { |
725 | return frameView().viewportSizeForCSSViewportUnits(); |
726 | } |
727 | |
728 | void 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. |
753 | void 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 | |
774 | bool RenderView::usesCompositing() const |
775 | { |
776 | return m_compositor && m_compositor->usesCompositing(); |
777 | } |
778 | |
779 | RenderLayerCompositor& RenderView::compositor() |
780 | { |
781 | if (!m_compositor) |
782 | m_compositor = std::make_unique<RenderLayerCompositor>(*this); |
783 | |
784 | return *m_compositor; |
785 | } |
786 | |
787 | void RenderView::setIsInWindow(bool isInWindow) |
788 | { |
789 | if (m_compositor) |
790 | m_compositor->setIsInWindow(isInWindow); |
791 | } |
792 | |
793 | void RenderView::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
794 | { |
795 | RenderBlockFlow::styleDidChange(diff, oldStyle); |
796 | |
797 | frameView().styleDidChange(); |
798 | } |
799 | |
800 | ImageQualityController& RenderView::imageQualityController() |
801 | { |
802 | if (!m_imageQualityController) |
803 | m_imageQualityController = std::make_unique<ImageQualityController>(*this); |
804 | return *m_imageQualityController; |
805 | } |
806 | |
807 | void RenderView::registerForVisibleInViewportCallback(RenderElement& renderer) |
808 | { |
809 | ASSERT(!m_visibleInViewportRenderers.contains(&renderer)); |
810 | m_visibleInViewportRenderers.add(&renderer); |
811 | } |
812 | |
813 | void RenderView::unregisterForVisibleInViewportCallback(RenderElement& renderer) |
814 | { |
815 | ASSERT(m_visibleInViewportRenderers.contains(&renderer)); |
816 | m_visibleInViewportRenderers.remove(&renderer); |
817 | } |
818 | |
819 | void 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 | |
829 | void 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 | |
841 | void 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 | |
850 | void 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 | |
867 | void 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 | |
881 | RenderView::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 | |
896 | RenderView::RepaintRegionAccumulator::~RepaintRegionAccumulator() |
897 | { |
898 | if (m_wasAccumulatingRepaintRegion) |
899 | return; |
900 | if (!m_rootView) |
901 | return; |
902 | m_rootView.get()->flushAccumulatedRepaintRegion(); |
903 | } |
904 | |
905 | unsigned RenderView::pageNumberForBlockProgressionOffset(int offset) const |
906 | { |
907 | int columnNumber = 0; |
908 | const 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 | |
931 | unsigned RenderView::pageCount() const |
932 | { |
933 | const 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) |
944 | void RenderView::registerBoxWithScrollSnapPositions(const RenderBox& box) |
945 | { |
946 | m_boxesWithScrollSnapPositions.add(&box); |
947 | } |
948 | |
949 | void RenderView::unregisterBoxWithScrollSnapPositions(const RenderBox& box) |
950 | { |
951 | m_boxesWithScrollSnapPositions.remove(&box); |
952 | } |
953 | #endif |
954 | |
955 | } // namespace WebCore |
956 | |