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

Side by Side Diff: sky/engine/core/rendering/RenderBox.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
« no previous file with comments | « sky/engine/core/rendering/RenderBox.h ('k') | sky/engine/core/rendering/RenderLayer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserv ed. 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserv ed.
7 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. 7 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
8 * 8 *
9 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public 10 * modify it under the terms of the GNU Library General Public
(...skipping 21 matching lines...) Expand all
32 #include "sky/engine/core/editing/htmlediting.h" 32 #include "sky/engine/core/editing/htmlediting.h"
33 #include "sky/engine/core/frame/FrameHost.h" 33 #include "sky/engine/core/frame/FrameHost.h"
34 #include "sky/engine/core/frame/FrameView.h" 34 #include "sky/engine/core/frame/FrameView.h"
35 #include "sky/engine/core/frame/LocalFrame.h" 35 #include "sky/engine/core/frame/LocalFrame.h"
36 #include "sky/engine/core/frame/Settings.h" 36 #include "sky/engine/core/frame/Settings.h"
37 #include "sky/engine/core/html/HTMLElement.h" 37 #include "sky/engine/core/html/HTMLElement.h"
38 #include "sky/engine/core/page/EventHandler.h" 38 #include "sky/engine/core/page/EventHandler.h"
39 #include "sky/engine/core/page/Page.h" 39 #include "sky/engine/core/page/Page.h"
40 #include "sky/engine/core/rendering/FilterEffectRenderer.h" 40 #include "sky/engine/core/rendering/FilterEffectRenderer.h"
41 #include "sky/engine/core/rendering/HitTestResult.h" 41 #include "sky/engine/core/rendering/HitTestResult.h"
42 #include "sky/engine/core/rendering/HitTestingTransformState.h"
42 #include "sky/engine/core/rendering/PaintInfo.h" 43 #include "sky/engine/core/rendering/PaintInfo.h"
43 #include "sky/engine/core/rendering/RenderFlexibleBox.h" 44 #include "sky/engine/core/rendering/RenderFlexibleBox.h"
44 #include "sky/engine/core/rendering/RenderGeometryMap.h" 45 #include "sky/engine/core/rendering/RenderGeometryMap.h"
45 #include "sky/engine/core/rendering/RenderInline.h" 46 #include "sky/engine/core/rendering/RenderInline.h"
46 #include "sky/engine/core/rendering/RenderLayer.h" 47 #include "sky/engine/core/rendering/RenderLayer.h"
47 #include "sky/engine/core/rendering/RenderView.h" 48 #include "sky/engine/core/rendering/RenderView.h"
48 #include "sky/engine/platform/LengthFunctions.h" 49 #include "sky/engine/platform/LengthFunctions.h"
49 #include "sky/engine/platform/geometry/FloatQuad.h" 50 #include "sky/engine/platform/geometry/FloatQuad.h"
50 #include "sky/engine/platform/geometry/TransformState.h" 51 #include "sky/engine/platform/geometry/TransformState.h"
51 #include "sky/engine/platform/graphics/GraphicsContextStateSaver.h" 52 #include "sky/engine/platform/graphics/GraphicsContextStateSaver.h"
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
376 boundsRect.moveBy(adjustedLocation); 377 boundsRect.moveBy(adjustedLocation);
377 if (visibleToHitTestRequest(request) && locationInContainer.intersects(bound sRect)) { 378 if (visibleToHitTestRequest(request) && locationInContainer.intersects(bound sRect)) {
378 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(a djustedLocation)); 379 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(a djustedLocation));
379 if (!result.addNodeToRectBasedTestResult(node(), request, locationInCont ainer, boundsRect)) 380 if (!result.addNodeToRectBasedTestResult(node(), request, locationInCont ainer, boundsRect))
380 return true; 381 return true;
381 } 382 }
382 383
383 return false; 384 return false;
384 } 385 }
385 386
387 PassRefPtr<HitTestingTransformState> RenderBox::createLocalTransformState(
388 RenderLayer* rootLayer, RenderLayer* containerLayer,
389 const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
390 const HitTestingTransformState* containerTransformState) const
391 {
392 RefPtr<HitTestingTransformState> transformState;
393 LayoutPoint offset;
394 if (containerTransformState) {
395 // If we're already computing transform state, then it's relative to the container (which we know is non-null).
396 transformState = HitTestingTransformState::create(*containerTransformSta te);
397 layer()->convertToLayerCoords(containerLayer, offset);
398 } else {
399 // If this is the first time we need to make transform state, then base it off of hitTestLocation,
400 // which is relative to rootLayer.
401 transformState = HitTestingTransformState::create(hitTestLocation.transf ormedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect));
402 layer()->convertToLayerCoords(rootLayer, offset);
403 }
404
405 RenderObject* containerRenderer = containerLayer ? containerLayer->renderer( ) : 0;
406 if (shouldUseTransformFromContainer(containerRenderer)) {
407 TransformationMatrix containerTransform;
408 getTransformFromContainer(containerRenderer, toLayoutSize(offset), conta inerTransform);
409 transformState->applyTransform(containerTransform, HitTestingTransformSt ate::AccumulateTransform);
410 } else {
411 transformState->translate(offset.x(), offset.y(), HitTestingTransformSta te::AccumulateTransform);
412 }
413
414 return transformState;
415 }
416
417 // Compute the z-offset of the point in the transformState.
418 // This is effectively projecting a ray normal to the plane of ancestor, finding where that
419 // ray intersects target, and computing the z delta between those two points.
420 static double computeZOffset(const HitTestingTransformState& transformState)
421 {
422 // We got an affine transform, so no z-offset
423 if (transformState.m_accumulatedTransform.isAffine())
424 return 0;
425
426 // Flatten the point into the target plane
427 FloatPoint targetPoint = transformState.mappedPoint();
428
429 // Now map the point back through the transform, which computes Z.
430 FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoin t(FloatPoint3D(targetPoint));
431 return backmappedPoint.z();
432 }
433
434 static bool isHitCandidate(bool canDepthSort, double* zOffset, const HitTestingT ransformState* transformState)
435 {
436 // The hit layer is depth-sorting with other layers, so just say that it was hit.
437 if (canDepthSort)
438 return true;
439
440 // We need to look at z-depth to decide if this layer was hit.
441 if (zOffset) {
442 ASSERT(transformState);
443 // This is actually computing our z, but that's OK because the hitLayer is coplanar with us.
444 double childZOffset = computeZOffset(*transformState);
445 if (childZOffset > *zOffset) {
446 *zOffset = childZOffset;
447 return true;
448 }
449 return false;
450 }
451
452 return true;
453 }
454
455 static inline bool reverseCompareZIndex(RenderLayerModelObject* first, RenderLay erModelObject* second)
456 {
457 return first->style()->zIndex() > second->style()->zIndex();
458 }
459
460 // hitTestLocation and hitTestRect are relative to rootLayer.
461 // A 'flattening' layer is one preserves3D() == false.
462 // transformState.m_accumulatedTransform holds the transform from the containing flattening layer.
463 // transformState.m_lastPlanarPoint is the hitTestLocation in the plane of the c ontaining flattening layer.
464 // transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer.
465 //
466 // If zOffset is non-null (which indicates that the caller wants z offset inform ation),
467 // *zOffset on return is the z offset of the hit point relative to the containi ng flattening layer.
468 bool RenderBox::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer , const HitTestRequest& request, HitTestResult& result,
469 const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
470 const HitTestingTransformState* transformState, double* zOffset)
471 {
472 ASSERT(layer()->isSelfPaintingLayer());
473
474 // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate.
475 RefPtr<HitTestingTransformState> localTransformState;
476
477 LayoutRect localHitTestRect = hitTestRect;
478 HitTestLocation localHitTestLocation = hitTestLocation;
479
480 // We need transform state for the first time, or to offset the container st ate, or to accumulate the new transform.
481 if (layer()->transform() || transformState || layer()->has3DTransformedDesce ndant() || layer()->preserves3D())
482 localTransformState = createLocalTransformState(rootLayer, containerLaye r, localHitTestRect, localHitTestLocation, transformState);
483
484 // Apply a transform if we have one.
485 if (layer()->transform()) {
486 // Make sure the parent's clip rects have been calculated.
487 if (parent()) {
488 ClipRect clipRect = layer()->clipper().backgroundClipRect(ClipRectsC ontext(rootLayer, RootRelativeClipRects));
489 // Go ahead and test the enclosing clip now.
490 if (!clipRect.intersects(localHitTestLocation))
491 return 0;
492 }
493
494 // If the transform can't be inverted, then don't hit test this layer at all.
495 if (!localTransformState->m_accumulatedTransform.isInvertible())
496 return 0;
497
498 // Compute the point and the hit test rect in the coords of this layer b y using the values
499 // from the transformState, which store the point and quad in the coords of the last flattened
500 // layer, and the accumulated transform which lets up map through preser ve-3d layers.
501 //
502 // We can't just map hitTestLocation and hitTestRect because they may ha ve been flattened (losing z)
503 // by our container.
504 FloatPoint localPoint = localTransformState->mappedPoint();
505 FloatQuad localPointQuad = localTransformState->mappedQuad();
506 localHitTestRect = localTransformState->boundsOfMappedArea();
507 if (localHitTestLocation.isRectBasedTest())
508 localHitTestLocation = HitTestLocation(localPoint, localPointQuad);
509 else
510 localHitTestLocation = HitTestLocation(localPoint);
511
512 // Now do a hit test with the root layer shifted to be us.
513 rootLayer = layer();
514 }
515
516 // Ensure our lists and 3d status are up-to-date.
517 layer()->stackingNode()->updateLayerListsIfNeeded();
518 layer()->update3DTransformedDescendantStatus();
519
520 // Check for hit test on backface if backface-visibility is 'hidden'
521 if (localTransformState && style()->backfaceVisibility() == BackfaceVisibili tyHidden) {
522 TransformationMatrix invertedMatrix = localTransformState->m_accumulated Transform.inverse();
523 // If the z-vector of the matrix is negative, the back is facing towards the viewer.
524 if (invertedMatrix.m33() < 0)
525 return 0;
526 }
527
528 RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformS tate;
529 if (localTransformState && !layer()->preserves3D()) {
530 // Keep a copy of the pre-flattening state, for computing z-offsets for the container
531 unflattenedTransformState = HitTestingTransformState::create(*localTrans formState);
532 // This layer is flattening, so flatten the state passed to descendants.
533 localTransformState->flatten();
534 }
535
536 // The following are used for keeping track of the z-depth of the hit point of 3d-transformed
537 // descendants.
538 double localZOffset = -std::numeric_limits<double>::infinity();
539 double* zOffsetForDescendantsPtr = 0;
540 double* zOffsetForContentsPtr = 0;
541
542 bool depthSortDescendants = false;
543 if (layer()->preserves3D()) {
544 depthSortDescendants = true;
545 // Our layers can depth-test with our container, so share the z depth po inter with the container, if it passed one down.
546 zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset;
547 zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset;
548 } else if (zOffset) {
549 zOffsetForDescendantsPtr = 0;
550 // Container needs us to give back a z offset for the hit layer.
551 zOffsetForContentsPtr = zOffset;
552 }
553
554 Vector<RenderBox*> layers;
555 collectSelfPaintingLayers(layers);
556 std::stable_sort(layers.begin(), layers.end(), reverseCompareZIndex);
557
558 bool hitLayer = false;
559 for (auto& currentLayer : layers) {
560 HitTestResult tempResult(result.hitTestLocation());
561 bool localHitLayer = currentLayer->hitTestLayer(rootLayer, layer(), requ est, tempResult,
562 hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForD escendantsPtr);
563
564 // If it a rect-based test, we can safely append the temporary result si nce it might had hit
565 // nodes but not necesserily had hitLayer set.
566 if (result.isRectBasedTest())
567 result.append(tempResult);
568
569 if (localHitLayer && isHitCandidate(depthSortDescendants, zOffset, unfla ttenedTransformState.get())) {
570 hitLayer = localHitLayer;
571 if (!result.isRectBasedTest())
572 result = tempResult;
573 if (!depthSortDescendants)
574 return true;
575 }
576 }
577
578 LayoutRect layerBounds;
579 // FIXME(sky): Remove foregroundRect. It's unused.
580 ClipRect contentRect, foregroundRect;
581 ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects);
582 layer()->clipper().calculateRects(clipRectsContext, localHitTestRect, layerB ounds, contentRect, foregroundRect);
583
584 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
585 if (contentRect.intersects(localHitTestLocation)) {
586 // Hit test with a temporary HitTestResult, because we only want to comm it to 'result' if we know we're frontmost.
587 HitTestResult tempResult(result.hitTestLocation());
588 if (hitTestNonLayerDescendants(request, tempResult, layerBounds, localHi tTestLocation)
589 && isHitCandidate(false, zOffsetForContentsPtr, unflattenedTransform State.get())) {
590 if (result.isRectBasedTest())
591 result.append(tempResult);
592 else
593 result = tempResult;
594 if (!depthSortDescendants)
595 return true;
596 // Foreground can depth-sort with descendant layers, so keep this as a candidate.
597 hitLayer = true;
598 } else if (result.isRectBasedTest()) {
599 result.append(tempResult);
600 }
601 }
602
603 return hitLayer;
604 }
605
606 bool RenderBox::hitTestNonLayerDescendants(const HitTestRequest& request, HitTes tResult& result,
607 const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation)
608 {
609 if (!hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.loc ation() - location()))) {
610 // It's wrong to set innerNode, but then claim that you didn't hit anyth ing, unless it is
611 // a rect-based test.
612 ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBa sedTestResult().size()));
613 return false;
614 }
615
616 // For positioned generated content, we might still not have a
617 // node by the time we get to the layer level, since none of
618 // the content in the layer has an element. So just walk up
619 // the tree.
620 if (!result.innerNode() || !result.innerNonSharedNode()) {
621 Node* enclosingElement = 0;
622 for (RenderObject* r = this; r; r = r->parent()) {
623 if (Node* element = r->node()) {
624 enclosingElement = element;
625 break;
626 }
627 }
628 ASSERT(enclosingElement);
629
630 if (!result.innerNode())
631 result.setInnerNode(enclosingElement);
632 if (!result.innerNonSharedNode())
633 result.setInnerNonSharedNode(enclosingElement);
634 }
635
636 return true;
637 }
638
386 // --------------------- painting stuff ------------------------------- 639 // --------------------- painting stuff -------------------------------
387 640
388 static inline bool compareZIndex(RenderBox* first, RenderBox* second) 641 static inline bool forwardCompareZIndex(RenderBox* first, RenderBox* second)
389 { 642 {
390 return first->style()->zIndex() < second->style()->zIndex(); 643 return first->style()->zIndex() < second->style()->zIndex();
391 } 644 }
392 645
393 void RenderBox::paintLayer(GraphicsContext* context, RenderLayer* rootLayer, con st IntRect& rect) 646 void RenderBox::paintLayer(GraphicsContext* context, RenderLayer* rootLayer, con st IntRect& rect)
394 { 647 {
395 // If this layer is totally invisible then there is nothing to paint. 648 // If this layer is totally invisible then there is nothing to paint.
396 // TODO(ojan): Return false from isSelfPainting and then ASSERT(!opacity()) here. 649 // TODO(ojan): Return false from isSelfPainting and then ASSERT(!opacity()) here.
397 if (!opacity()) 650 if (!opacity())
398 return; 651 return;
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
557 if (shouldClip) 810 if (shouldClip)
558 layer()->clipToRect(localPaintingInfo, context, contentRect); 811 layer()->clipToRect(localPaintingInfo, context, contentRect);
559 812
560 // TODO(ojan): We probably should have already set shouldPaintContent to false if the rect is empty. 813 // TODO(ojan): We probably should have already set shouldPaintContent to false if the rect is empty.
561 if (!contentRectIsEmpty) { 814 if (!contentRectIsEmpty) {
562 Vector<RenderBox*> layers; 815 Vector<RenderBox*> layers;
563 816
564 PaintInfo paintInfo(context, pixelSnappedIntRect(contentRect.rect()) , localPaintingInfo.rootLayer->renderer()); 817 PaintInfo paintInfo(context, pixelSnappedIntRect(contentRect.rect()) , localPaintingInfo.rootLayer->renderer());
565 paint(paintInfo, layerLocation, layers); 818 paint(paintInfo, layerLocation, layers);
566 819
567 std::stable_sort(layers.begin(), layers.end(), compareZIndex); 820 std::stable_sort(layers.begin(), layers.end(), forwardCompareZIndex) ;
568 for (auto& box : layers) { 821 for (auto& box : layers) {
569 box->paintLayer(context, paintingInfo.rootLayer, rect); 822 box->paintLayer(context, paintingInfo.rootLayer, rect);
570 } 823 }
571 } 824 }
572 825
573 if (shouldClip) 826 if (shouldClip)
574 layer()->restoreClip(context, localPaintingInfo.paintDirtyRect, cont entRect); 827 layer()->restoreClip(context, localPaintingInfo.paintDirtyRect, cont entRect);
575 } 828 }
576 829
577 if (filterPainter.hasStartedFilterEffect()) { 830 if (filterPainter.hasStartedFilterEffect()) {
(...skipping 2292 matching lines...) Expand 10 before | Expand all | Expand 10 after
2870 3123
2871 RenderBox::BoxDecorationData::BoxDecorationData(const RenderStyle& style) 3124 RenderBox::BoxDecorationData::BoxDecorationData(const RenderStyle& style)
2872 { 3125 {
2873 backgroundColor = style.colorIncludingFallback(CSSPropertyBackgroundColor); 3126 backgroundColor = style.colorIncludingFallback(CSSPropertyBackgroundColor);
2874 hasBackground = backgroundColor.alpha() || style.hasBackgroundImage(); 3127 hasBackground = backgroundColor.alpha() || style.hasBackgroundImage();
2875 ASSERT(hasBackground == style.hasBackground()); 3128 ASSERT(hasBackground == style.hasBackground());
2876 hasBorder = style.hasBorder(); 3129 hasBorder = style.hasBorder();
2877 } 3130 }
2878 3131
2879 } // namespace blink 3132 } // namespace blink
OLDNEW
« no previous file with comments | « sky/engine/core/rendering/RenderBox.h ('k') | sky/engine/core/rendering/RenderLayer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698