OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |