| 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 770 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 } | 781 } |
| 782 } | 782 } |
| 783 | 783 |
| 784 void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintD
irtyRect, const ClipRect& clipRect) | 784 void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintD
irtyRect, const ClipRect& clipRect) |
| 785 { | 785 { |
| 786 if (clipRect.rect() == paintDirtyRect && !clipRect.hasRadius()) | 786 if (clipRect.rect() == paintDirtyRect && !clipRect.hasRadius()) |
| 787 return; | 787 return; |
| 788 context->restore(); | 788 context->restore(); |
| 789 } | 789 } |
| 790 | 790 |
| 791 Node* RenderLayer::enclosingElement() const | |
| 792 { | |
| 793 for (RenderObject* r = renderer(); r; r = r->parent()) { | |
| 794 if (Node* e = r->node()) | |
| 795 return e; | |
| 796 } | |
| 797 ASSERT_NOT_REACHED(); | |
| 798 return 0; | |
| 799 } | |
| 800 | |
| 801 // Compute the z-offset of the point in the transformState. | |
| 802 // This is effectively projecting a ray normal to the plane of ancestor, finding
where that | |
| 803 // ray intersects target, and computing the z delta between those two points. | |
| 804 static double computeZOffset(const HitTestingTransformState& transformState) | |
| 805 { | |
| 806 // We got an affine transform, so no z-offset | |
| 807 if (transformState.m_accumulatedTransform.isAffine()) | |
| 808 return 0; | |
| 809 | |
| 810 // Flatten the point into the target plane | |
| 811 FloatPoint targetPoint = transformState.mappedPoint(); | |
| 812 | |
| 813 // Now map the point back through the transform, which computes Z. | |
| 814 FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoin
t(FloatPoint3D(targetPoint)); | |
| 815 return backmappedPoint.z(); | |
| 816 } | |
| 817 | |
| 818 PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(Rend
erLayer* rootLayer, RenderLayer* containerLayer, | |
| 819 const LayoutRect& hitTestRect, const Hit
TestLocation& hitTestLocation, | |
| 820 const HitTestingTransformState* containe
rTransformState, | |
| 821 const LayoutPoint& translationOffset) co
nst | |
| 822 { | |
| 823 RefPtr<HitTestingTransformState> transformState; | |
| 824 LayoutPoint offset; | |
| 825 if (containerTransformState) { | |
| 826 // If we're already computing transform state, then it's relative to the
container (which we know is non-null). | |
| 827 transformState = HitTestingTransformState::create(*containerTransformSta
te); | |
| 828 convertToLayerCoords(containerLayer, offset); | |
| 829 } else { | |
| 830 // If this is the first time we need to make transform state, then base
it off of hitTestLocation, | |
| 831 // which is relative to rootLayer. | |
| 832 transformState = HitTestingTransformState::create(hitTestLocation.transf
ormedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect)); | |
| 833 convertToLayerCoords(rootLayer, offset); | |
| 834 } | |
| 835 offset.moveBy(translationOffset); | |
| 836 | |
| 837 RenderObject* containerRenderer = containerLayer ? containerLayer->renderer(
) : 0; | |
| 838 if (renderer()->shouldUseTransformFromContainer(containerRenderer)) { | |
| 839 TransformationMatrix containerTransform; | |
| 840 renderer()->getTransformFromContainer(containerRenderer, toLayoutSize(of
fset), containerTransform); | |
| 841 transformState->applyTransform(containerTransform, HitTestingTransformSt
ate::AccumulateTransform); | |
| 842 } else { | |
| 843 transformState->translate(offset.x(), offset.y(), HitTestingTransformSta
te::AccumulateTransform); | |
| 844 } | |
| 845 | |
| 846 return transformState; | |
| 847 } | |
| 848 | |
| 849 | |
| 850 static bool isHitCandidate(bool canDepthSort, double* zOffset, const HitTestingT
ransformState* transformState) | |
| 851 { | |
| 852 // The hit layer is depth-sorting with other layers, so just say that it was
hit. | |
| 853 if (canDepthSort) | |
| 854 return true; | |
| 855 | |
| 856 // We need to look at z-depth to decide if this layer was hit. | |
| 857 if (zOffset) { | |
| 858 ASSERT(transformState); | |
| 859 // This is actually computing our z, but that's OK because the hitLayer
is coplanar with us. | |
| 860 double childZOffset = computeZOffset(*transformState); | |
| 861 if (childZOffset > *zOffset) { | |
| 862 *zOffset = childZOffset; | |
| 863 return true; | |
| 864 } | |
| 865 return false; | |
| 866 } | |
| 867 | |
| 868 return true; | |
| 869 } | |
| 870 | |
| 871 // hitTestLocation and hitTestRect are relative to rootLayer. | |
| 872 // A 'flattening' layer is one preserves3D() == false. | |
| 873 // transformState.m_accumulatedTransform holds the transform from the containing
flattening layer. | |
| 874 // transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the c
ontaining flattening layer. | |
| 875 // transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of
the containing flattening layer. | |
| 876 // | |
| 877 // If zOffset is non-null (which indicates that the caller wants z offset inform
ation), | |
| 878 // *zOffset on return is the z offset of the hit point relative to the containi
ng flattening layer. | |
| 879 bool RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLay
er, const HitTestRequest& request, HitTestResult& result, | |
| 880 const LayoutRect& hitTestRect, const HitT
estLocation& hitTestLocation, | |
| 881 const HitTestingTransformState* transform
State, double* zOffset) | |
| 882 { | |
| 883 if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) | |
| 884 return 0; | |
| 885 | |
| 886 // The natural thing would be to keep HitTestingTransformState on the stack,
but it's big, so we heap-allocate. | |
| 887 RefPtr<HitTestingTransformState> localTransformState; | |
| 888 | |
| 889 LayoutRect localHitTestRect = hitTestRect; | |
| 890 HitTestLocation localHitTestLocation = hitTestLocation; | |
| 891 | |
| 892 // We need transform state for the first time, or to offset the container st
ate, or to accumulate the new transform. | |
| 893 if (transform() || transformState || m_has3DTransformedDescendant || preserv
es3D()) | |
| 894 localTransformState = createLocalTransformState(rootLayer, containerLaye
r, localHitTestRect, localHitTestLocation, transformState); | |
| 895 | |
| 896 // Apply a transform if we have one. | |
| 897 if (transform()) { | |
| 898 // Make sure the parent's clip rects have been calculated. | |
| 899 if (parent()) { | |
| 900 ClipRect clipRect = clipper().backgroundClipRect(ClipRectsContext(ro
otLayer, RootRelativeClipRects)); | |
| 901 // Go ahead and test the enclosing clip now. | |
| 902 if (!clipRect.intersects(localHitTestLocation)) | |
| 903 return 0; | |
| 904 } | |
| 905 | |
| 906 // If the transform can't be inverted, then don't hit test this layer at
all. | |
| 907 if (!localTransformState->m_accumulatedTransform.isInvertible()) | |
| 908 return 0; | |
| 909 | |
| 910 // Compute the point and the hit test rect in the coords of this layer b
y using the values | |
| 911 // from the transformState, which store the point and quad in the coords
of the last flattened | |
| 912 // layer, and the accumulated transform which lets up map through preser
ve-3d layers. | |
| 913 // | |
| 914 // We can't just map hitTestLocation and hitTestRect because they may ha
ve been flattened (losing z) | |
| 915 // by our container. | |
| 916 FloatPoint localPoint = localTransformState->mappedPoint(); | |
| 917 FloatQuad localPointQuad = localTransformState->mappedQuad(); | |
| 918 localHitTestRect = localTransformState->boundsOfMappedArea(); | |
| 919 if (localHitTestLocation.isRectBasedTest()) | |
| 920 localHitTestLocation = HitTestLocation(localPoint, localPointQuad); | |
| 921 else | |
| 922 localHitTestLocation = HitTestLocation(localPoint); | |
| 923 | |
| 924 // Now do a hit test with the root layer shifted to be us. | |
| 925 rootLayer = this; | |
| 926 } | |
| 927 | |
| 928 // Ensure our lists and 3d status are up-to-date. | |
| 929 m_stackingNode->updateLayerListsIfNeeded(); | |
| 930 update3DTransformedDescendantStatus(); | |
| 931 | |
| 932 // Check for hit test on backface if backface-visibility is 'hidden' | |
| 933 if (localTransformState && renderer()->style()->backfaceVisibility() == Back
faceVisibilityHidden) { | |
| 934 TransformationMatrix invertedMatrix = localTransformState->m_accumulated
Transform.inverse(); | |
| 935 // If the z-vector of the matrix is negative, the back is facing towards
the viewer. | |
| 936 if (invertedMatrix.m33() < 0) | |
| 937 return 0; | |
| 938 } | |
| 939 | |
| 940 RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformS
tate; | |
| 941 if (localTransformState && !preserves3D()) { | |
| 942 // Keep a copy of the pre-flattening state, for computing z-offsets for
the container | |
| 943 unflattenedTransformState = HitTestingTransformState::create(*localTrans
formState); | |
| 944 // This layer is flattening, so flatten the state passed to descendants. | |
| 945 localTransformState->flatten(); | |
| 946 } | |
| 947 | |
| 948 // The following are used for keeping track of the z-depth of the hit point
of 3d-transformed | |
| 949 // descendants. | |
| 950 double localZOffset = -std::numeric_limits<double>::infinity(); | |
| 951 double* zOffsetForDescendantsPtr = 0; | |
| 952 double* zOffsetForContentsPtr = 0; | |
| 953 | |
| 954 bool depthSortDescendants = false; | |
| 955 if (preserves3D()) { | |
| 956 depthSortDescendants = true; | |
| 957 // Our layers can depth-test with our container, so share the z depth po
inter with the container, if it passed one down. | |
| 958 zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; | |
| 959 zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; | |
| 960 } else if (zOffset) { | |
| 961 zOffsetForDescendantsPtr = 0; | |
| 962 // Container needs us to give back a z offset for the hit layer. | |
| 963 zOffsetForContentsPtr = zOffset; | |
| 964 } | |
| 965 | |
| 966 // Begin by walking our list of positive layers from highest z-index down to
the lowest z-index. | |
| 967 bool hitLayer = hitTestChildren(PositiveZOrderChildren, rootLayer, request,
result, localHitTestRect, localHitTestLocation, | |
| 968 localTransformState.get(), zOffsetForDescend
antsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); | |
| 969 if (hitLayer && !depthSortDescendants) | |
| 970 return true; | |
| 971 | |
| 972 // Now check our overflow objects. | |
| 973 hitLayer = hitTestChildren(NormalFlowChildren, rootLayer, request, result, l
ocalHitTestRect, localHitTestLocation, | |
| 974 localTransformState.get(), zOffsetForDescendantsP
tr, zOffset, unflattenedTransformState.get(), depthSortDescendants); | |
| 975 if (hitLayer && !depthSortDescendants) | |
| 976 return true; | |
| 977 | |
| 978 LayoutRect layerBounds; | |
| 979 // FIXME(sky): Remove foregroundRect. It's unused. | |
| 980 ClipRect contentRect, foregroundRect; | |
| 981 ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects); | |
| 982 clipper().calculateRects(clipRectsContext, localHitTestRect, layerBounds, co
ntentRect, foregroundRect); | |
| 983 | |
| 984 // Next we want to see if the mouse pos is inside the child RenderObjects of
the layer. | |
| 985 if (isSelfPaintingLayer() && contentRect.intersects(localHitTestLocation)) { | |
| 986 // Hit test with a temporary HitTestResult, because we only want to comm
it to 'result' if we know we're frontmost. | |
| 987 HitTestResult tempResult(result.hitTestLocation()); | |
| 988 if (hitTestContents(request, tempResult, layerBounds, localHitTestLocati
on) | |
| 989 && isHitCandidate(false, zOffsetForContentsPtr, unflattenedTransform
State.get())) { | |
| 990 if (result.isRectBasedTest()) | |
| 991 result.append(tempResult); | |
| 992 else | |
| 993 result = tempResult; | |
| 994 if (!depthSortDescendants) | |
| 995 return true; | |
| 996 // Foreground can depth-sort with descendant layers, so keep this as
a candidate. | |
| 997 hitLayer = true; | |
| 998 } else if (result.isRectBasedTest()) { | |
| 999 result.append(tempResult); | |
| 1000 } | |
| 1001 } | |
| 1002 | |
| 1003 return hitLayer; | |
| 1004 } | |
| 1005 | |
| 1006 bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult&
result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation) c
onst | |
| 1007 { | |
| 1008 ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); | |
| 1009 | |
| 1010 if (!renderer()->hitTest(request, result, hitTestLocation, toLayoutPoint(lay
erBounds.location() - renderBoxLocation()))) { | |
| 1011 // It's wrong to set innerNode, but then claim that you didn't hit anyth
ing, unless it is | |
| 1012 // a rect-based test. | |
| 1013 ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBa
sedTestResult().size())); | |
| 1014 return false; | |
| 1015 } | |
| 1016 | |
| 1017 // For positioned generated content, we might still not have a | |
| 1018 // node by the time we get to the layer level, since none of | |
| 1019 // the content in the layer has an element. So just walk up | |
| 1020 // the tree. | |
| 1021 if (!result.innerNode() || !result.innerNonSharedNode()) { | |
| 1022 Node* e = enclosingElement(); | |
| 1023 if (!result.innerNode()) | |
| 1024 result.setInnerNode(e); | |
| 1025 if (!result.innerNonSharedNode()) | |
| 1026 result.setInnerNonSharedNode(e); | |
| 1027 } | |
| 1028 | |
| 1029 return true; | |
| 1030 } | |
| 1031 | |
| 1032 bool RenderLayer::hitTestChildren(ChildrenIteration childrentoVisit, RenderLayer
* rootLayer, | |
| 1033 const HitTestRequest& request, HitTestResult& result, | |
| 1034 const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, | |
| 1035 const HitTestingTransformState* transformState, | |
| 1036 double* zOffsetForDescendants, double* zOffset, | |
| 1037 const HitTestingTransformState* unflattenedTransformState, | |
| 1038 bool depthSortDescendants) | |
| 1039 { | |
| 1040 if (!hasSelfPaintingLayerDescendant()) | |
| 1041 return 0; | |
| 1042 | |
| 1043 bool hitLayer = false; | |
| 1044 RenderLayerStackingNodeReverseIterator iterator(*m_stackingNode, childrentoV
isit); | |
| 1045 while (RenderLayerStackingNode* child = iterator.next()) { | |
| 1046 RenderLayer* childLayer = child->layer(); | |
| 1047 HitTestResult tempResult(result.hitTestLocation()); | |
| 1048 hitLayer |= childLayer->hitTestLayer(rootLayer, this, request, tempResul
t, hitTestRect, hitTestLocation, transformState, zOffsetForDescendants); | |
| 1049 | |
| 1050 // If it a rect-based test, we can safely append the temporary result si
nce it might had hit | |
| 1051 // nodes but not necesserily had hitLayer set. | |
| 1052 if (result.isRectBasedTest()) | |
| 1053 result.append(tempResult); | |
| 1054 | |
| 1055 if (hitLayer && isHitCandidate(depthSortDescendants, zOffset, unflattene
dTransformState)) { | |
| 1056 if (!result.isRectBasedTest()) | |
| 1057 result = tempResult; | |
| 1058 if (!depthSortDescendants) | |
| 1059 break; | |
| 1060 } | |
| 1061 } | |
| 1062 | |
| 1063 return hitLayer; | |
| 1064 } | |
| 1065 | |
| 1066 bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const Layo
utRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromR
oot) const | 791 bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const Layo
utRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromR
oot) const |
| 1067 { | 792 { |
| 1068 // Always examine the canvas and the root. | 793 // Always examine the canvas and the root. |
| 1069 if (isRootLayer()) | 794 if (isRootLayer()) |
| 1070 return true; | 795 return true; |
| 1071 | 796 |
| 1072 // If we aren't an inline flow, and our layer bounds do intersect the damage
rect, then we | 797 // If we aren't an inline flow, and our layer bounds do intersect the damage
rect, then we |
| 1073 // can go ahead and return true. | 798 // can go ahead and return true. |
| 1074 RenderView* view = renderer()->view(); | 799 RenderView* view = renderer()->view(); |
| 1075 ASSERT(view); | 800 ASSERT(view); |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1343 } | 1068 } |
| 1344 } | 1069 } |
| 1345 | 1070 |
| 1346 void showLayerTree(const blink::RenderObject* renderer) | 1071 void showLayerTree(const blink::RenderObject* renderer) |
| 1347 { | 1072 { |
| 1348 if (!renderer) | 1073 if (!renderer) |
| 1349 return; | 1074 return; |
| 1350 showLayerTree(renderer->enclosingLayer()); | 1075 showLayerTree(renderer->enclosingLayer()); |
| 1351 } | 1076 } |
| 1352 #endif | 1077 #endif |
| OLD | NEW |