| 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 | |