| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights
reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights
reserved. |
| 3 * | 3 * |
| 4 * Portions are Copyright (C) 1998 Netscape Communications Corporation. | 4 * Portions are Copyright (C) 1998 Netscape Communications Corporation. |
| 5 * | 5 * |
| 6 * Other contributors: | 6 * Other contributors: |
| 7 * Robert O'Callahan <roc+@cs.cmu.edu> | 7 * Robert O'Callahan <roc+@cs.cmu.edu> |
| 8 * David Baron <dbaron@fas.harvard.edu> | 8 * David Baron <dbaron@fas.harvard.edu> |
| 9 * Christian Biesinger <cbiesinger@web.de> | 9 * Christian Biesinger <cbiesinger@web.de> |
| 10 * Randall Jesup <rjesup@wgate.com> | 10 * Randall Jesup <rjesup@wgate.com> |
| (...skipping 791 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 802 ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); | 802 ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); |
| 803 | 803 |
| 804 // RenderView should make sure to update layout before entering hit testing | 804 // RenderView should make sure to update layout before entering hit testing |
| 805 ASSERT(!renderer()->frame()->view()->layoutPending()); | 805 ASSERT(!renderer()->frame()->view()->layoutPending()); |
| 806 ASSERT(!renderer()->document().renderView()->needsLayout()); | 806 ASSERT(!renderer()->document().renderView()->needsLayout()); |
| 807 | 807 |
| 808 LayoutRect hitTestArea = renderer()->view()->documentRect(); | 808 LayoutRect hitTestArea = renderer()->view()->documentRect(); |
| 809 if (!request.ignoreClipping()) | 809 if (!request.ignoreClipping()) |
| 810 hitTestArea.intersect(frameVisibleRect(renderer())); | 810 hitTestArea.intersect(frameVisibleRect(renderer())); |
| 811 | 811 |
| 812 RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestAre
a, hitTestLocation, false); | 812 RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestAre
a, hitTestLocation); |
| 813 if (!insideLayer) { | 813 if (!insideLayer) { |
| 814 // We didn't hit any layer. If we are the root layer and the mouse is --
or just was -- down, | 814 // We didn't hit any layer. If we are the root layer and the mouse is --
or just was -- down, |
| 815 // return ourselves. We do this so mouse events continue getting deliver
ed after a drag has | 815 // return ourselves. We do this so mouse events continue getting deliver
ed after a drag has |
| 816 // exited the WebView, and so hit testing over a scrollbar hits the cont
ent document. | 816 // exited the WebView, and so hit testing over a scrollbar hits the cont
ent document. |
| 817 if ((request.active() || request.release()) && isRootLayer()) { | 817 if ((request.active() || request.release()) && isRootLayer()) { |
| 818 renderer()->updateHitTestResult(result, hitTestLocation.point()); | 818 renderer()->updateHitTestResult(result, hitTestLocation.point()); |
| 819 insideLayer = this; | 819 insideLayer = this; |
| 820 } | 820 } |
| 821 } | 821 } |
| 822 return insideLayer; | 822 return insideLayer; |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 907 | 907 |
| 908 // hitTestLocation and hitTestRect are relative to rootLayer. | 908 // hitTestLocation and hitTestRect are relative to rootLayer. |
| 909 // A 'flattening' layer is one preserves3D() == false. | 909 // A 'flattening' layer is one preserves3D() == false. |
| 910 // transformState.m_accumulatedTransform holds the transform from the containing
flattening layer. | 910 // transformState.m_accumulatedTransform holds the transform from the containing
flattening layer. |
| 911 // transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the c
ontaining flattening layer. | 911 // transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the c
ontaining flattening layer. |
| 912 // transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of
the containing flattening layer. | 912 // transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of
the containing flattening layer. |
| 913 // | 913 // |
| 914 // If zOffset is non-null (which indicates that the caller wants z offset inform
ation), | 914 // If zOffset is non-null (which indicates that the caller wants z offset inform
ation), |
| 915 // *zOffset on return is the z offset of the hit point relative to the containi
ng flattening layer. | 915 // *zOffset on return is the z offset of the hit point relative to the containi
ng flattening layer. |
| 916 RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
ainerLayer, const HitTestRequest& request, HitTestResult& result, | 916 RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
ainerLayer, const HitTestRequest& request, HitTestResult& result, |
| 917 const LayoutRect& hitTestRect, const HitT
estLocation& hitTestLocation, bool appliedTransform, | 917 const LayoutRect& hitTestRect, const HitT
estLocation& hitTestLocation, |
| 918 const HitTestingTransformState* transform
State, double* zOffset) | 918 const HitTestingTransformState* transform
State, double* zOffset) |
| 919 { | 919 { |
| 920 if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) | 920 if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) |
| 921 return 0; | 921 return 0; |
| 922 | 922 |
| 923 // The natural thing would be to keep HitTestingTransformState on the stack,
but it's big, so we heap-allocate. | 923 // The natural thing would be to keep HitTestingTransformState on the stack,
but it's big, so we heap-allocate. |
| 924 RefPtr<HitTestingTransformState> localTransformState; |
| 925 |
| 926 LayoutRect localHitTestRect = hitTestRect; |
| 927 HitTestLocation localHitTestLocation = hitTestLocation; |
| 928 |
| 929 // We need transform state for the first time, or to offset the container st
ate, or to accumulate the new transform. |
| 930 if (transform() || transformState || m_has3DTransformedDescendant || preserv
es3D()) |
| 931 localTransformState = createLocalTransformState(rootLayer, containerLaye
r, localHitTestRect, localHitTestLocation, transformState); |
| 924 | 932 |
| 925 // Apply a transform if we have one. | 933 // Apply a transform if we have one. |
| 926 if (transform() && !appliedTransform) { | 934 if (transform()) { |
| 927 // Make sure the parent's clip rects have been calculated. | 935 // Make sure the parent's clip rects have been calculated. |
| 928 if (parent()) { | 936 if (parent()) { |
| 929 ClipRect clipRect = clipper().backgroundClipRect(ClipRectsContext(ro
otLayer, RootRelativeClipRects)); | 937 ClipRect clipRect = clipper().backgroundClipRect(ClipRectsContext(ro
otLayer, RootRelativeClipRects)); |
| 930 // Go ahead and test the enclosing clip now. | 938 // Go ahead and test the enclosing clip now. |
| 931 if (!clipRect.intersects(hitTestLocation)) | 939 if (!clipRect.intersects(localHitTestLocation)) |
| 932 return 0; | 940 return 0; |
| 933 } | 941 } |
| 934 | 942 |
| 935 return hitTestLayerByApplyingTransform(rootLayer, containerLayer, reques
t, result, hitTestRect, hitTestLocation, transformState, zOffset); | 943 // If the transform can't be inverted, then don't hit test this layer at
all. |
| 944 if (!localTransformState->m_accumulatedTransform.isInvertible()) |
| 945 return 0; |
| 946 |
| 947 // Compute the point and the hit test rect in the coords of this layer b
y using the values |
| 948 // from the transformState, which store the point and quad in the coords
of the last flattened |
| 949 // layer, and the accumulated transform which lets up map through preser
ve-3d layers. |
| 950 // |
| 951 // We can't just map hitTestLocation and hitTestRect because they may ha
ve been flattened (losing z) |
| 952 // by our container. |
| 953 FloatPoint localPoint = localTransformState->mappedPoint(); |
| 954 FloatQuad localPointQuad = localTransformState->mappedQuad(); |
| 955 localHitTestRect = localTransformState->boundsOfMappedArea(); |
| 956 if (localHitTestLocation.isRectBasedTest()) |
| 957 localHitTestLocation = HitTestLocation(localPoint, localPointQuad); |
| 958 else |
| 959 localHitTestLocation = HitTestLocation(localPoint); |
| 960 |
| 961 // Now do a hit test with the root layer shifted to be us. |
| 962 rootLayer = this; |
| 936 } | 963 } |
| 937 | 964 |
| 938 // Ensure our lists and 3d status are up-to-date. | 965 // Ensure our lists and 3d status are up-to-date. |
| 939 m_stackingNode->updateLayerListsIfNeeded(); | 966 m_stackingNode->updateLayerListsIfNeeded(); |
| 940 update3DTransformedDescendantStatus(); | 967 update3DTransformedDescendantStatus(); |
| 941 | 968 |
| 942 RefPtr<HitTestingTransformState> localTransformState; | |
| 943 if (appliedTransform) { | |
| 944 // We computed the correct state in the caller (above code), so just ref
erence it. | |
| 945 ASSERT(transformState); | |
| 946 localTransformState = const_cast<HitTestingTransformState*>(transformSta
te); | |
| 947 } else if (transformState || m_has3DTransformedDescendant || preserves3D())
{ | |
| 948 // We need transform state for the first time, or to offset the containe
r state, so create it here. | |
| 949 localTransformState = createLocalTransformState(rootLayer, containerLaye
r, hitTestRect, hitTestLocation, transformState); | |
| 950 } | |
| 951 | |
| 952 // Check for hit test on backface if backface-visibility is 'hidden' | 969 // Check for hit test on backface if backface-visibility is 'hidden' |
| 953 if (localTransformState && renderer()->style()->backfaceVisibility() == Back
faceVisibilityHidden) { | 970 if (localTransformState && renderer()->style()->backfaceVisibility() == Back
faceVisibilityHidden) { |
| 954 TransformationMatrix invertedMatrix = localTransformState->m_accumulated
Transform.inverse(); | 971 TransformationMatrix invertedMatrix = localTransformState->m_accumulated
Transform.inverse(); |
| 955 // If the z-vector of the matrix is negative, the back is facing towards
the viewer. | 972 // If the z-vector of the matrix is negative, the back is facing towards
the viewer. |
| 956 if (invertedMatrix.m33() < 0) | 973 if (invertedMatrix.m33() < 0) |
| 957 return 0; | 974 return 0; |
| 958 } | 975 } |
| 959 | 976 |
| 960 RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformS
tate; | 977 RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformS
tate; |
| 961 if (localTransformState && !preserves3D()) { | 978 if (localTransformState && !preserves3D()) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 980 } else if (zOffset) { | 997 } else if (zOffset) { |
| 981 zOffsetForDescendantsPtr = 0; | 998 zOffsetForDescendantsPtr = 0; |
| 982 // Container needs us to give back a z offset for the hit layer. | 999 // Container needs us to give back a z offset for the hit layer. |
| 983 zOffsetForContentsPtr = zOffset; | 1000 zOffsetForContentsPtr = zOffset; |
| 984 } | 1001 } |
| 985 | 1002 |
| 986 // This variable tracks which layer the mouse ends up being inside. | 1003 // This variable tracks which layer the mouse ends up being inside. |
| 987 RenderLayer* candidateLayer = 0; | 1004 RenderLayer* candidateLayer = 0; |
| 988 | 1005 |
| 989 // Begin by walking our list of positive layers from highest z-index down to
the lowest z-index. | 1006 // Begin by walking our list of positive layers from highest z-index down to
the lowest z-index. |
| 990 RenderLayer* hitLayer = hitTestChildren(PositiveZOrderChildren, rootLayer, r
equest, result, hitTestRect, hitTestLocation, | 1007 RenderLayer* hitLayer = hitTestChildren(PositiveZOrderChildren, rootLayer, r
equest, result, localHitTestRect, localHitTestLocation, |
| 991 localTransformState.get(), zOffsetForDes
cendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); | 1008 localTransformState.get(), zOffsetForDes
cendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); |
| 992 if (hitLayer) { | 1009 if (hitLayer) { |
| 993 if (!depthSortDescendants) | 1010 if (!depthSortDescendants) |
| 994 return hitLayer; | 1011 return hitLayer; |
| 995 candidateLayer = hitLayer; | 1012 candidateLayer = hitLayer; |
| 996 } | 1013 } |
| 997 | 1014 |
| 998 // Now check our overflow objects. | 1015 // Now check our overflow objects. |
| 999 hitLayer = hitTestChildren(NormalFlowChildren, rootLayer, request, result, h
itTestRect, hitTestLocation, | 1016 hitLayer = hitTestChildren(NormalFlowChildren, rootLayer, request, result, l
ocalHitTestRect, localHitTestLocation, |
| 1000 localTransformState.get(), zOffsetForDescendantsPtr,
zOffset, unflattenedTransformState.get(), depthSortDescendants); | 1017 localTransformState.get(), zOffsetForDescendantsPtr,
zOffset, unflattenedTransformState.get(), depthSortDescendants); |
| 1001 if (hitLayer) { | 1018 if (hitLayer) { |
| 1002 if (!depthSortDescendants) | 1019 if (!depthSortDescendants) |
| 1003 return hitLayer; | 1020 return hitLayer; |
| 1004 candidateLayer = hitLayer; | 1021 candidateLayer = hitLayer; |
| 1005 } | 1022 } |
| 1006 | 1023 |
| 1007 LayoutRect layerBounds; | 1024 LayoutRect layerBounds; |
| 1008 // FIXME(sky): Remove foregroundRect. It's unused. | 1025 // FIXME(sky): Remove foregroundRect. It's unused. |
| 1009 ClipRect contentRect, foregroundRect; | 1026 ClipRect contentRect, foregroundRect; |
| 1010 ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects); | 1027 ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects); |
| 1011 clipper().calculateRects(clipRectsContext, hitTestRect, layerBounds, content
Rect, foregroundRect); | 1028 clipper().calculateRects(clipRectsContext, localHitTestRect, layerBounds, co
ntentRect, foregroundRect); |
| 1012 | 1029 |
| 1013 // Next we want to see if the mouse pos is inside the child RenderObjects of
the layer. | 1030 // Next we want to see if the mouse pos is inside the child RenderObjects of
the layer. |
| 1014 if (isSelfPaintingLayer() && contentRect.intersects(hitTestLocation)) { | 1031 if (isSelfPaintingLayer() && contentRect.intersects(localHitTestLocation)) { |
| 1015 // Hit test with a temporary HitTestResult, because we only want to comm
it to 'result' if we know we're frontmost. | 1032 // Hit test with a temporary HitTestResult, because we only want to comm
it to 'result' if we know we're frontmost. |
| 1016 HitTestResult tempResult(result.hitTestLocation()); | 1033 HitTestResult tempResult(result.hitTestLocation()); |
| 1017 if (hitTestContents(request, tempResult, layerBounds, hitTestLocation) | 1034 if (hitTestContents(request, tempResult, layerBounds, localHitTestLocati
on) |
| 1018 && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTra
nsformState.get())) { | 1035 && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTra
nsformState.get())) { |
| 1019 if (result.isRectBasedTest()) | 1036 if (result.isRectBasedTest()) |
| 1020 result.append(tempResult); | 1037 result.append(tempResult); |
| 1021 else | 1038 else |
| 1022 result = tempResult; | 1039 result = tempResult; |
| 1023 if (!depthSortDescendants) | 1040 if (!depthSortDescendants) |
| 1024 return this; | 1041 return this; |
| 1025 // Foreground can depth-sort with descendant layers, so keep this as
a candidate. | 1042 // Foreground can depth-sort with descendant layers, so keep this as
a candidate. |
| 1026 candidateLayer = this; | 1043 candidateLayer = this; |
| 1027 } else if (result.isRectBasedTest()) { | 1044 } else if (result.isRectBasedTest()) { |
| 1028 result.append(tempResult); | 1045 result.append(tempResult); |
| 1029 } | 1046 } |
| 1030 } | 1047 } |
| 1031 | 1048 |
| 1032 return candidateLayer; | 1049 return candidateLayer; |
| 1033 } | 1050 } |
| 1034 | 1051 |
| 1035 RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer
, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& res
ult, | |
| 1036 const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const
HitTestingTransformState* transformState, double* zOffset, | |
| 1037 const LayoutPoint& translationOffset) | |
| 1038 { | |
| 1039 // Create a transform state to accumulate this transform. | |
| 1040 RefPtr<HitTestingTransformState> newTransformState = createLocalTransformSta
te(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, tran
slationOffset); | |
| 1041 | |
| 1042 // If the transform can't be inverted, then don't hit test this layer at all
. | |
| 1043 if (!newTransformState->m_accumulatedTransform.isInvertible()) | |
| 1044 return 0; | |
| 1045 | |
| 1046 // Compute the point and the hit test rect in the coords of this layer by us
ing the values | |
| 1047 // from the transformState, which store the point and quad in the coords of
the last flattened | |
| 1048 // layer, and the accumulated transform which lets up map through preserve-3
d layers. | |
| 1049 // | |
| 1050 // We can't just map hitTestLocation and hitTestRect because they may have b
een flattened (losing z) | |
| 1051 // by our container. | |
| 1052 FloatPoint localPoint = newTransformState->mappedPoint(); | |
| 1053 FloatQuad localPointQuad = newTransformState->mappedQuad(); | |
| 1054 LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea(); | |
| 1055 HitTestLocation newHitTestLocation; | |
| 1056 if (hitTestLocation.isRectBasedTest()) | |
| 1057 newHitTestLocation = HitTestLocation(localPoint, localPointQuad); | |
| 1058 else | |
| 1059 newHitTestLocation = HitTestLocation(localPoint); | |
| 1060 | |
| 1061 // Now do a hit test with the root layer shifted to be us. | |
| 1062 return hitTestLayer(this, containerLayer, request, result, localHitTestRect,
newHitTestLocation, true, newTransformState.get(), zOffset); | |
| 1063 } | |
| 1064 | |
| 1065 bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult&
result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation) c
onst | 1052 bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult&
result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation) c
onst |
| 1066 { | 1053 { |
| 1067 ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); | 1054 ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); |
| 1068 | 1055 |
| 1069 if (!renderer()->hitTest(request, result, hitTestLocation, toLayoutPoint(lay
erBounds.location() - renderBoxLocation()))) { | 1056 if (!renderer()->hitTest(request, result, hitTestLocation, toLayoutPoint(lay
erBounds.location() - renderBoxLocation()))) { |
| 1070 // It's wrong to set innerNode, but then claim that you didn't hit anyth
ing, unless it is | 1057 // It's wrong to set innerNode, but then claim that you didn't hit anyth
ing, unless it is |
| 1071 // a rect-based test. | 1058 // a rect-based test. |
| 1072 ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBa
sedTestResult().size())); | 1059 ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBa
sedTestResult().size())); |
| 1073 return false; | 1060 return false; |
| 1074 } | 1061 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1098 { | 1085 { |
| 1099 if (!hasSelfPaintingLayerDescendant()) | 1086 if (!hasSelfPaintingLayerDescendant()) |
| 1100 return 0; | 1087 return 0; |
| 1101 | 1088 |
| 1102 RenderLayer* resultLayer = 0; | 1089 RenderLayer* resultLayer = 0; |
| 1103 RenderLayerStackingNodeReverseIterator iterator(*m_stackingNode, childrentoV
isit); | 1090 RenderLayerStackingNodeReverseIterator iterator(*m_stackingNode, childrentoV
isit); |
| 1104 while (RenderLayerStackingNode* child = iterator.next()) { | 1091 while (RenderLayerStackingNode* child = iterator.next()) { |
| 1105 RenderLayer* childLayer = child->layer(); | 1092 RenderLayer* childLayer = child->layer(); |
| 1106 RenderLayer* hitLayer = 0; | 1093 RenderLayer* hitLayer = 0; |
| 1107 HitTestResult tempResult(result.hitTestLocation()); | 1094 HitTestResult tempResult(result.hitTestLocation()); |
| 1108 hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult
, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants); | 1095 hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult
, hitTestRect, hitTestLocation, transformState, zOffsetForDescendants); |
| 1109 | 1096 |
| 1110 // If it a rect-based test, we can safely append the temporary result si
nce it might had hit | 1097 // If it a rect-based test, we can safely append the temporary result si
nce it might had hit |
| 1111 // nodes but not necesserily had hitLayer set. | 1098 // nodes but not necesserily had hitLayer set. |
| 1112 if (result.isRectBasedTest()) | 1099 if (result.isRectBasedTest()) |
| 1113 result.append(tempResult); | 1100 result.append(tempResult); |
| 1114 | 1101 |
| 1115 if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedT
ransformState)) { | 1102 if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedT
ransformState)) { |
| 1116 resultLayer = hitLayer; | 1103 resultLayer = hitLayer; |
| 1117 if (!result.isRectBasedTest()) | 1104 if (!result.isRectBasedTest()) |
| 1118 result = tempResult; | 1105 result = tempResult; |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1404 } | 1391 } |
| 1405 } | 1392 } |
| 1406 | 1393 |
| 1407 void showLayerTree(const blink::RenderObject* renderer) | 1394 void showLayerTree(const blink::RenderObject* renderer) |
| 1408 { | 1395 { |
| 1409 if (!renderer) | 1396 if (!renderer) |
| 1410 return; | 1397 return; |
| 1411 showLayerTree(renderer->enclosingLayer()); | 1398 showLayerTree(renderer->enclosingLayer()); |
| 1412 } | 1399 } |
| 1413 #endif | 1400 #endif |
| OLD | NEW |