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 |