| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> |
| 3 * Copyright (C) 2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org> |
| 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> | 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
| 5 * Copyright (C) 2009 Google, Inc. | 5 * Copyright (C) 2009 Google, Inc. |
| 6 * Copyright (C) Research In Motion Limited 2011. All rights reserved. | 6 * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| 7 * | 7 * |
| 8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
| 9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
| 10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 } | 92 } |
| 93 | 93 |
| 94 bool LayoutSVGRoot::isEmbeddedThroughFrameContainingSVGDocument() const { | 94 bool LayoutSVGRoot::isEmbeddedThroughFrameContainingSVGDocument() const { |
| 95 if (!node()) | 95 if (!node()) |
| 96 return false; | 96 return false; |
| 97 | 97 |
| 98 LocalFrame* frame = node()->document().frame(); | 98 LocalFrame* frame = node()->document().frame(); |
| 99 if (!frame) | 99 if (!frame) |
| 100 return false; | 100 return false; |
| 101 | 101 |
| 102 // If our frame has an owner layoutObject, we're embedded through eg. object/e
mbed/iframe, | 102 // If our frame has an owner layoutObject, we're embedded through eg. |
| 103 // but we only negotiate if we're in an SVG document inside a embedded object
(object/embed). | 103 // object/embed/iframe, but we only negotiate if we're in an SVG document |
| 104 // inside a embedded object (object/embed). |
| 104 if (frame->ownerLayoutItem().isNull() || | 105 if (frame->ownerLayoutItem().isNull() || |
| 105 !frame->ownerLayoutItem().isEmbeddedObject()) | 106 !frame->ownerLayoutItem().isEmbeddedObject()) |
| 106 return false; | 107 return false; |
| 107 return frame->document()->isSVGDocument(); | 108 return frame->document()->isSVGDocument(); |
| 108 } | 109 } |
| 109 | 110 |
| 110 LayoutUnit LayoutSVGRoot::computeReplacedLogicalWidth( | 111 LayoutUnit LayoutSVGRoot::computeReplacedLogicalWidth( |
| 111 ShouldComputePreferred shouldComputePreferred) const { | 112 ShouldComputePreferred shouldComputePreferred) const { |
| 112 // When we're embedded through SVGImage (border-image/background-image/<html:i
mg>/...) we're forced to resize to a specific size. | 113 // When we're embedded through SVGImage |
| 114 // (border-image/background-image/<html:img>/...) we're forced to resize to a |
| 115 // specific size. |
| 113 if (!m_containerSize.isEmpty()) | 116 if (!m_containerSize.isEmpty()) |
| 114 return LayoutUnit(m_containerSize.width()); | 117 return LayoutUnit(m_containerSize.width()); |
| 115 | 118 |
| 116 if (isEmbeddedThroughFrameContainingSVGDocument()) | 119 if (isEmbeddedThroughFrameContainingSVGDocument()) |
| 117 return containingBlock()->availableLogicalWidth(); | 120 return containingBlock()->availableLogicalWidth(); |
| 118 | 121 |
| 119 return LayoutReplaced::computeReplacedLogicalWidth(shouldComputePreferred); | 122 return LayoutReplaced::computeReplacedLogicalWidth(shouldComputePreferred); |
| 120 } | 123 } |
| 121 | 124 |
| 122 LayoutUnit LayoutSVGRoot::computeReplacedLogicalHeight( | 125 LayoutUnit LayoutSVGRoot::computeReplacedLogicalHeight( |
| 123 LayoutUnit estimatedUsedWidth) const { | 126 LayoutUnit estimatedUsedWidth) const { |
| 124 // When we're embedded through SVGImage (border-image/background-image/<html:i
mg>/...) we're forced to resize to a specific size. | 127 // When we're embedded through SVGImage |
| 128 // (border-image/background-image/<html:img>/...) we're forced to resize to a |
| 129 // specific size. |
| 125 if (!m_containerSize.isEmpty()) | 130 if (!m_containerSize.isEmpty()) |
| 126 return LayoutUnit(m_containerSize.height()); | 131 return LayoutUnit(m_containerSize.height()); |
| 127 | 132 |
| 128 if (isEmbeddedThroughFrameContainingSVGDocument()) | 133 if (isEmbeddedThroughFrameContainingSVGDocument()) |
| 129 return containingBlock()->availableLogicalHeight( | 134 return containingBlock()->availableLogicalHeight( |
| 130 IncludeMarginBorderPadding); | 135 IncludeMarginBorderPadding); |
| 131 | 136 |
| 132 return LayoutReplaced::computeReplacedLogicalHeight(estimatedUsedWidth); | 137 return LayoutReplaced::computeReplacedLogicalHeight(estimatedUsedWidth); |
| 133 } | 138 } |
| 134 | 139 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 updateLayerTransformAfterLayout(); | 183 updateLayerTransformAfterLayout(); |
| 179 m_hasBoxDecorationBackground = isDocumentElement() | 184 m_hasBoxDecorationBackground = isDocumentElement() |
| 180 ? styleRef().hasBoxDecorationBackground() | 185 ? styleRef().hasBoxDecorationBackground() |
| 181 : hasBoxDecorationBackground(); | 186 : hasBoxDecorationBackground(); |
| 182 invalidateBackgroundObscurationStatus(); | 187 invalidateBackgroundObscurationStatus(); |
| 183 | 188 |
| 184 clearNeedsLayout(); | 189 clearNeedsLayout(); |
| 185 } | 190 } |
| 186 | 191 |
| 187 bool LayoutSVGRoot::shouldApplyViewportClip() const { | 192 bool LayoutSVGRoot::shouldApplyViewportClip() const { |
| 188 // the outermost svg is clipped if auto, and svg document roots are always cli
pped | 193 // the outermost svg is clipped if auto, and svg document roots are always |
| 189 // When the svg is stand-alone (isDocumentElement() == true) the viewport clip
ping should always | 194 // clipped. When the svg is stand-alone (isDocumentElement() == true) the |
| 190 // be applied, noting that the window scrollbars should be hidden if overflow=
hidden. | 195 // viewport clipping should always be applied, noting that the window |
| 196 // scrollbars should be hidden if overflow=hidden. |
| 191 return style()->overflowX() == OverflowHidden || | 197 return style()->overflowX() == OverflowHidden || |
| 192 style()->overflowX() == OverflowAuto || | 198 style()->overflowX() == OverflowAuto || |
| 193 style()->overflowX() == OverflowScroll || this->isDocumentElement(); | 199 style()->overflowX() == OverflowScroll || this->isDocumentElement(); |
| 194 } | 200 } |
| 195 | 201 |
| 196 LayoutRect LayoutSVGRoot::visualOverflowRect() const { | 202 LayoutRect LayoutSVGRoot::visualOverflowRect() const { |
| 197 LayoutRect rect = LayoutReplaced::selfVisualOverflowRect(); | 203 LayoutRect rect = LayoutReplaced::selfVisualOverflowRect(); |
| 198 if (!shouldApplyViewportClip()) | 204 if (!shouldApplyViewportClip()) |
| 199 rect.unite(contentsVisualOverflowRect()); | 205 rect.unite(contentsVisualOverflowRect()); |
| 200 return rect; | 206 return rect; |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 contentWidth() / scale, contentHeight() / scale); | 334 contentWidth() / scale, contentHeight() / scale); |
| 329 | 335 |
| 330 AffineTransform viewToBorderBoxTransform( | 336 AffineTransform viewToBorderBoxTransform( |
| 331 scale, 0, 0, scale, borderAndPadding.width() + translate.x(), | 337 scale, 0, 0, scale, borderAndPadding.width() + translate.x(), |
| 332 borderAndPadding.height() + translate.y()); | 338 borderAndPadding.height() + translate.y()); |
| 333 viewToBorderBoxTransform.scale(svg->currentScale()); | 339 viewToBorderBoxTransform.scale(svg->currentScale()); |
| 334 m_localToBorderBoxTransform.preMultiply(viewToBorderBoxTransform); | 340 m_localToBorderBoxTransform.preMultiply(viewToBorderBoxTransform); |
| 335 } | 341 } |
| 336 | 342 |
| 337 const AffineTransform& LayoutSVGRoot::localToSVGParentTransform() const { | 343 const AffineTransform& LayoutSVGRoot::localToSVGParentTransform() const { |
| 338 // Slightly optimized version of m_localToParentTransform = AffineTransform::t
ranslation(x(), y()) * m_localToBorderBoxTransform; | 344 // Slightly optimized version of m_localToParentTransform = |
| 345 // AffineTransform::translation(x(), y()) * m_localToBorderBoxTransform; |
| 339 m_localToParentTransform = m_localToBorderBoxTransform; | 346 m_localToParentTransform = m_localToBorderBoxTransform; |
| 340 if (location().x()) | 347 if (location().x()) |
| 341 m_localToParentTransform.setE(m_localToParentTransform.e() + | 348 m_localToParentTransform.setE(m_localToParentTransform.e() + |
| 342 roundToInt(location().x())); | 349 roundToInt(location().x())); |
| 343 if (location().y()) | 350 if (location().y()) |
| 344 m_localToParentTransform.setF(m_localToParentTransform.f() + | 351 m_localToParentTransform.setF(m_localToParentTransform.f() + |
| 345 roundToInt(location().y())); | 352 roundToInt(location().y())); |
| 346 return m_localToParentTransform; | 353 return m_localToParentTransform; |
| 347 } | 354 } |
| 348 | 355 |
| 349 LayoutRect LayoutSVGRoot::localOverflowRectForPaintInvalidation() const { | 356 LayoutRect LayoutSVGRoot::localOverflowRectForPaintInvalidation() const { |
| 350 // This is an open-coded aggregate of SVGLayoutSupport::localOverflowRectForPa
intInvalidation, | 357 // This is an open-coded aggregate of SVGLayoutSupport:: |
| 351 // and LayoutReplaced::localOverflowRectForPaintInvalidation. | 358 // localOverflowRectForPaintInvalidation, and LayoutReplaced:: |
| 352 // The reason for this is to optimize/minimize the paint invalidation rect whe
n the box is not "decorated" | 359 // localOverflowRectForPaintInvalidation. |
| 353 // (does not have background/border/etc., see LayoutSVGRootTest.OverflowRectMa
ppingWithViewportClipWithoutBorder). | 360 // The reason for this is to optimize/minimize the paint invalidation rect |
| 361 // when the box is not "decorated" (does not have background/border/etc., see |
| 362 // LayoutSVGRootTest.OverflowRectMappingWithViewportClipWithoutBorder). |
| 354 | 363 |
| 355 // Return early for any cases where we don't actually paint. | 364 // Return early for any cases where we don't actually paint. |
| 356 if (style()->visibility() != EVisibility::Visible && | 365 if (style()->visibility() != EVisibility::Visible && |
| 357 !enclosingLayer()->hasVisibleContent()) | 366 !enclosingLayer()->hasVisibleContent()) |
| 358 return LayoutRect(); | 367 return LayoutRect(); |
| 359 | 368 |
| 360 // Compute the paint invalidation rect of the content of the SVG in the border
-box coordinate space. | 369 // Compute the paint invalidation rect of the content of the SVG in the |
| 370 // border-box coordinate space. |
| 361 FloatRect contentPaintInvalidationRect = | 371 FloatRect contentPaintInvalidationRect = |
| 362 paintInvalidationRectInLocalSVGCoordinates(); | 372 paintInvalidationRectInLocalSVGCoordinates(); |
| 363 contentPaintInvalidationRect = | 373 contentPaintInvalidationRect = |
| 364 m_localToBorderBoxTransform.mapRect(contentPaintInvalidationRect); | 374 m_localToBorderBoxTransform.mapRect(contentPaintInvalidationRect); |
| 365 | 375 |
| 366 // Apply initial viewport clip, overflow:visible content is added to visualOve
rflow | 376 // Apply initial viewport clip, overflow:visible content is added to |
| 367 // but the most common case is that overflow is hidden, so always intersect. | 377 // visualOverflow but the most common case is that overflow is hidden, so |
| 378 // always intersect. |
| 368 contentPaintInvalidationRect.intersect(pixelSnappedBorderBoxRect()); | 379 contentPaintInvalidationRect.intersect(pixelSnappedBorderBoxRect()); |
| 369 | 380 |
| 370 LayoutRect paintInvalidationRect = | 381 LayoutRect paintInvalidationRect = |
| 371 enclosingLayoutRect(contentPaintInvalidationRect); | 382 enclosingLayoutRect(contentPaintInvalidationRect); |
| 372 // If the box is decorated or is overflowing, extend it to include the border-
box and overflow. | 383 // If the box is decorated or is overflowing, extend it to include the |
| 384 // border-box and overflow. |
| 373 if (m_hasBoxDecorationBackground || hasOverflowModel()) { | 385 if (m_hasBoxDecorationBackground || hasOverflowModel()) { |
| 374 // The selectionRect can project outside of the overflowRect, so take their
union | 386 // The selectionRect can project outside of the overflowRect, so take their |
| 375 // for paint invalidation to avoid selection painting glitches. | 387 // union for paint invalidation to avoid selection painting glitches. |
| 376 LayoutRect decoratedPaintInvalidationRect = | 388 LayoutRect decoratedPaintInvalidationRect = |
| 377 unionRect(localSelectionRect(), visualOverflowRect()); | 389 unionRect(localSelectionRect(), visualOverflowRect()); |
| 378 paintInvalidationRect.unite(decoratedPaintInvalidationRect); | 390 paintInvalidationRect.unite(decoratedPaintInvalidationRect); |
| 379 } | 391 } |
| 380 | 392 |
| 381 return LayoutRect(enclosingIntRect(paintInvalidationRect)); | 393 return LayoutRect(enclosingIntRect(paintInvalidationRect)); |
| 382 } | 394 } |
| 383 | 395 |
| 384 // This method expects local CSS box coordinates. | 396 // This method expects local CSS box coordinates. |
| 385 // Callers with local SVG viewport coordinates should first apply the localToBor
derBoxTransform | 397 // Callers with local SVG viewport coordinates should first apply the |
| 386 // to convert from SVG viewport coordinates to local CSS box coordinates. | 398 // localToBorderBoxTransform to convert from SVG viewport coordinates to local |
| 399 // CSS box coordinates. |
| 387 void LayoutSVGRoot::mapLocalToAncestor(const LayoutBoxModelObject* ancestor, | 400 void LayoutSVGRoot::mapLocalToAncestor(const LayoutBoxModelObject* ancestor, |
| 388 TransformState& transformState, | 401 TransformState& transformState, |
| 389 MapCoordinatesFlags mode) const { | 402 MapCoordinatesFlags mode) const { |
| 390 LayoutReplaced::mapLocalToAncestor(ancestor, transformState, | 403 LayoutReplaced::mapLocalToAncestor(ancestor, transformState, |
| 391 mode | ApplyContainerFlip); | 404 mode | ApplyContainerFlip); |
| 392 } | 405 } |
| 393 | 406 |
| 394 const LayoutObject* LayoutSVGRoot::pushMappingToContainer( | 407 const LayoutObject* LayoutSVGRoot::pushMappingToContainer( |
| 395 const LayoutBoxModelObject* ancestorToStopAt, | 408 const LayoutBoxModelObject* ancestorToStopAt, |
| 396 LayoutGeometryMap& geometryMap) const { | 409 LayoutGeometryMap& geometryMap) const { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 408 bool LayoutSVGRoot::nodeAtPoint(HitTestResult& result, | 421 bool LayoutSVGRoot::nodeAtPoint(HitTestResult& result, |
| 409 const HitTestLocation& locationInContainer, | 422 const HitTestLocation& locationInContainer, |
| 410 const LayoutPoint& accumulatedOffset, | 423 const LayoutPoint& accumulatedOffset, |
| 411 HitTestAction hitTestAction) { | 424 HitTestAction hitTestAction) { |
| 412 LayoutPoint pointInParent = | 425 LayoutPoint pointInParent = |
| 413 locationInContainer.point() - toLayoutSize(accumulatedOffset); | 426 locationInContainer.point() - toLayoutSize(accumulatedOffset); |
| 414 LayoutPoint pointInBorderBox = pointInParent - toLayoutSize(location()); | 427 LayoutPoint pointInBorderBox = pointInParent - toLayoutSize(location()); |
| 415 | 428 |
| 416 // Only test SVG content if the point is in our content box, or in case we | 429 // Only test SVG content if the point is in our content box, or in case we |
| 417 // don't clip to the viewport, the visual overflow rect. | 430 // don't clip to the viewport, the visual overflow rect. |
| 418 // FIXME: This should be an intersection when rect-based hit tests are support
ed by nodeAtFloatPoint. | 431 // FIXME: This should be an intersection when rect-based hit tests are |
| 432 // supported by nodeAtFloatPoint. |
| 419 if (contentBoxRect().contains(pointInBorderBox) || | 433 if (contentBoxRect().contains(pointInBorderBox) || |
| 420 (!shouldApplyViewportClip() && | 434 (!shouldApplyViewportClip() && |
| 421 visualOverflowRect().contains(pointInBorderBox))) { | 435 visualOverflowRect().contains(pointInBorderBox))) { |
| 422 const AffineTransform& localToParentTransform = | 436 const AffineTransform& localToParentTransform = |
| 423 this->localToSVGParentTransform(); | 437 this->localToSVGParentTransform(); |
| 424 if (localToParentTransform.isInvertible()) { | 438 if (localToParentTransform.isInvertible()) { |
| 425 FloatPoint localPoint = | 439 FloatPoint localPoint = |
| 426 localToParentTransform.inverse().mapPoint(FloatPoint(pointInParent)); | 440 localToParentTransform.inverse().mapPoint(FloatPoint(pointInParent)); |
| 427 | 441 |
| 428 for (LayoutObject* child = lastChild(); child; | 442 for (LayoutObject* child = lastChild(); child; |
| 429 child = child->previousSibling()) { | 443 child = child->previousSibling()) { |
| 430 // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. | 444 // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. |
| 431 if (child->nodeAtFloatPoint(result, localPoint, hitTestAction)) { | 445 if (child->nodeAtFloatPoint(result, localPoint, hitTestAction)) { |
| 432 updateHitTestResult(result, pointInBorderBox); | 446 updateHitTestResult(result, pointInBorderBox); |
| 433 if (result.addNodeToListBasedTestResult( | 447 if (result.addNodeToListBasedTestResult( |
| 434 child->node(), locationInContainer) == StopHitTesting) | 448 child->node(), locationInContainer) == StopHitTesting) |
| 435 return true; | 449 return true; |
| 436 } | 450 } |
| 437 } | 451 } |
| 438 } | 452 } |
| 439 } | 453 } |
| 440 | 454 |
| 441 // If we didn't early exit above, we've just hit the container <svg> element.
Unlike SVG 1.1, 2nd Edition allows container elements to be hit. | 455 // If we didn't early exit above, we've just hit the container <svg> element. |
| 456 // Unlike SVG 1.1, 2nd Edition allows container elements to be hit. |
| 442 if ((hitTestAction == HitTestBlockBackground || | 457 if ((hitTestAction == HitTestBlockBackground || |
| 443 hitTestAction == HitTestChildBlockBackground) && | 458 hitTestAction == HitTestChildBlockBackground) && |
| 444 visibleToHitTestRequest(result.hitTestRequest())) { | 459 visibleToHitTestRequest(result.hitTestRequest())) { |
| 445 // Only return true here, if the last hit testing phase 'BlockBackground' (o
r 'ChildBlockBackground' - depending on context) is executed. | 460 // Only return true here, if the last hit testing phase 'BlockBackground' |
| 446 // If we'd return true in the 'Foreground' phase, hit testing would stop imm
ediately. For SVG only trees this doesn't matter. | 461 // (or 'ChildBlockBackground' - depending on context) is executed. |
| 447 // Though when we have a <foreignObject> subtree we need to be able to detec
t hits on the background of a <div> element. | 462 // If we'd return true in the 'Foreground' phase, hit testing would stop |
| 448 // If we'd return true here in the 'Foreground' phase, we are not able to de
tect these hits anymore. | 463 // immediately. For SVG only trees this doesn't matter. |
| 464 // Though when we have a <foreignObject> subtree we need to be able to |
| 465 // detect hits on the background of a <div> element. |
| 466 // If we'd return true here in the 'Foreground' phase, we are not able to |
| 467 // detect these hits anymore. |
| 449 LayoutRect boundsRect(accumulatedOffset + location(), size()); | 468 LayoutRect boundsRect(accumulatedOffset + location(), size()); |
| 450 if (locationInContainer.intersects(boundsRect)) { | 469 if (locationInContainer.intersects(boundsRect)) { |
| 451 updateHitTestResult(result, pointInBorderBox); | 470 updateHitTestResult(result, pointInBorderBox); |
| 452 if (result.addNodeToListBasedTestResult(node(), locationInContainer, | 471 if (result.addNodeToListBasedTestResult(node(), locationInContainer, |
| 453 boundsRect) == StopHitTesting) | 472 boundsRect) == StopHitTesting) |
| 454 return true; | 473 return true; |
| 455 } | 474 } |
| 456 } | 475 } |
| 457 | 476 |
| 458 return false; | 477 return false; |
| 459 } | 478 } |
| 460 | 479 |
| 461 } // namespace blink | 480 } // namespace blink |
| OLD | NEW |