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 |