Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(959)

Side by Side Diff: sky/engine/core/rendering/RenderLayer.cpp

Issue 945693002: Move hit testing out of RenderLayer into RenderBox. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: add to test case Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « sky/engine/core/rendering/RenderLayer.h ('k') | sky/engine/core/rendering/RenderLayerModelObject.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698