| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/paint/PaintPropertyTreeBuilder.h" | 5 #include "core/paint/PaintPropertyTreeBuilder.h" |
| 6 | 6 |
| 7 #include "core/dom/DOMNodeIds.h" | 7 #include "core/dom/DOMNodeIds.h" |
| 8 #include "core/frame/FrameView.h" | 8 #include "core/frame/FrameView.h" |
| 9 #include "core/frame/LocalFrame.h" | 9 #include "core/frame/LocalFrame.h" |
| 10 #include "core/frame/Settings.h" | 10 #include "core/frame/Settings.h" |
| (...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 context.current.renderingContextId = | 403 context.current.renderingContextId = |
| 404 properties->transform()->renderingContextId(); | 404 properties->transform()->renderingContextId(); |
| 405 context.current.shouldFlattenInheritedTransform = false; | 405 context.current.shouldFlattenInheritedTransform = false; |
| 406 } else { | 406 } else { |
| 407 context.current.renderingContextId = 0; | 407 context.current.renderingContextId = 0; |
| 408 context.current.shouldFlattenInheritedTransform = true; | 408 context.current.shouldFlattenInheritedTransform = true; |
| 409 } | 409 } |
| 410 } | 410 } |
| 411 } | 411 } |
| 412 | 412 |
| 413 static bool computeMaskParameters(IntRect& maskClip, |
| 414 const LayoutObject& object, |
| 415 const LayoutPoint& paintOffset) { |
| 416 DCHECK(object.isBoxModelObject() || object.isSVGChild()); |
| 417 const ComputedStyle& style = object.styleRef(); |
| 418 |
| 419 if (object.isSVGChild()) { |
| 420 // TODO(trchen): Implement SVG masks. |
| 421 return false; |
| 422 } |
| 423 if (!style.hasMask()) |
| 424 return false; |
| 425 |
| 426 LayoutRect maximumMaskRegion; |
| 427 // For HTML/CSS objects, the extent of the mask is known as "mask |
| 428 // painting area", which is determined by CSS mask-clip property. |
| 429 // We don't implement mask-clip:margin-box or no-clip currently, |
| 430 // so the maximum we can get is border-box. |
| 431 if (object.isBox()) { |
| 432 maximumMaskRegion = toLayoutBox(object).borderBoxRect(); |
| 433 } else { |
| 434 // For inline elements, depends on the value of box-decoration-break |
| 435 // there could be one box in multiple fragments or multiple boxes. |
| 436 // Either way here we are only interested in the bounding box of them. |
| 437 DCHECK(object.isLayoutInline()); |
| 438 maximumMaskRegion = toLayoutInline(object).linesBoundingBox(); |
| 439 } |
| 440 maximumMaskRegion.moveBy(paintOffset); |
| 441 maskClip = enclosingIntRect(maximumMaskRegion); |
| 442 return true; |
| 443 } |
| 444 |
| 413 void PaintPropertyTreeBuilder::updateEffect( | 445 void PaintPropertyTreeBuilder::updateEffect( |
| 414 const LayoutObject& object, | 446 const LayoutObject& object, |
| 415 PaintPropertyTreeBuilderContext& context) { | 447 PaintPropertyTreeBuilderContext& context) { |
| 416 const ComputedStyle& style = object.styleRef(); | 448 const ComputedStyle& style = object.styleRef(); |
| 417 | 449 |
| 418 const bool isCSSIsolatedGroup = | 450 const bool isCSSIsolatedGroup = |
| 419 object.isBoxModelObject() && style.isStackingContext(); | 451 object.isBoxModelObject() && style.isStackingContext(); |
| 420 if (!isCSSIsolatedGroup && !object.isSVGChild()) { | 452 if (!isCSSIsolatedGroup && !object.isSVGChild()) { |
| 421 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { | 453 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { |
| 422 if (auto* properties = object.getMutableForPainting().paintProperties()) | 454 if (auto* properties = object.getMutableForPainting().paintProperties()) { |
| 423 context.forceSubtreeUpdate |= properties->clearEffect(); | 455 context.forceSubtreeUpdate |= properties->clearEffect(); |
| 456 context.forceSubtreeUpdate |= properties->clearMask(); |
| 457 context.forceSubtreeUpdate |= properties->clearMaskClip(); |
| 458 } |
| 424 } | 459 } |
| 425 return; | 460 return; |
| 426 } | 461 } |
| 427 | 462 |
| 428 // TODO(trchen): Can't omit effect node if we have 3D children. | 463 // TODO(trchen): Can't omit effect node if we have 3D children. |
| 429 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { | 464 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { |
| 465 const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect; |
| 466 |
| 430 bool effectNodeNeeded = false; | 467 bool effectNodeNeeded = false; |
| 431 | 468 |
| 432 // Can't omit effect node if we have paint children with exotic blending. | 469 // Can't omit effect node if we have paint children with exotic blending. |
| 433 if (object.isSVG()) { | 470 if (object.isSVG()) { |
| 434 // Yes, including LayoutSVGRoot, because SVG layout objects don't create | 471 // Yes, including LayoutSVGRoot, because SVG layout objects don't create |
| 435 // PaintLayer so PaintLayer::hasNonIsolatedDescendantWithBlendMode() | 472 // PaintLayer so PaintLayer::hasNonIsolatedDescendantWithBlendMode() |
| 436 // doesn't catch SVG descendants. | 473 // doesn't catch SVG descendants. |
| 437 if (SVGLayoutSupport::isIsolationRequired(&object)) | 474 if (SVGLayoutSupport::isIsolationRequired(&object)) |
| 438 effectNodeNeeded = true; | 475 effectNodeNeeded = true; |
| 439 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { | 476 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { |
| 440 if (layer->hasNonIsolatedDescendantWithBlendMode()) | 477 if (layer->hasNonIsolatedDescendantWithBlendMode()) |
| 441 effectNodeNeeded = true; | 478 effectNodeNeeded = true; |
| 442 } | 479 } |
| 443 | 480 |
| 444 SkBlendMode blendMode = object.isBlendingAllowed() | 481 SkBlendMode blendMode = object.isBlendingAllowed() |
| 445 ? WebCoreCompositeToSkiaComposite( | 482 ? WebCoreCompositeToSkiaComposite( |
| 446 CompositeSourceOver, style.blendMode()) | 483 CompositeSourceOver, style.blendMode()) |
| 447 : SkBlendMode::kSrcOver; | 484 : SkBlendMode::kSrcOver; |
| 448 if (blendMode != SkBlendMode::kSrcOver) | 485 if (blendMode != SkBlendMode::kSrcOver) |
| 449 effectNodeNeeded = true; | 486 effectNodeNeeded = true; |
| 450 | 487 |
| 451 float opacity = style.opacity(); | 488 float opacity = style.opacity(); |
| 452 if (opacity != 1.0f) | 489 if (opacity != 1.0f) |
| 453 effectNodeNeeded = true; | 490 effectNodeNeeded = true; |
| 454 | 491 |
| 492 // We may begin to composite our subtree prior to an animation starts, |
| 493 // but a compositor element ID is only needed when an animation is current. |
| 494 CompositorElementId compositorElementId = |
| 495 style.hasCurrentOpacityAnimation() |
| 496 ? createDomNodeBasedCompositorElementId(object) |
| 497 : CompositorElementId(); |
| 498 CompositingReasons compositingReasons = CompositingReasonNone; |
| 499 if (CompositingReasonFinder::requiresCompositingForOpacityAnimation( |
| 500 style)) { |
| 501 compositingReasons = CompositingReasonActiveAnimation; |
| 502 effectNodeNeeded = true; |
| 503 } |
| 504 DCHECK(!style.hasCurrentOpacityAnimation() || |
| 505 compositingReasons != CompositingReasonNone); |
| 506 |
| 507 IntRect maskClip; |
| 508 bool hasMask = |
| 509 computeMaskParameters(maskClip, object, context.current.paintOffset); |
| 510 if (hasMask) { |
| 511 effectNodeNeeded = true; |
| 512 |
| 513 auto& properties = object.getMutableForPainting().ensurePaintProperties(); |
| 514 context.forceSubtreeUpdate |= properties.updateMaskClip( |
| 515 context.current.clip, context.current.transform, |
| 516 FloatRoundedRect(maskClip)); |
| 517 outputClip = properties.maskClip(); |
| 518 |
| 519 // TODO(crbug.com/683425): PaintArtifactCompositor does not handle |
| 520 // grouping (i.e. descendant-dependent compositing reason) properly yet. |
| 521 // This forces masked subtree always create a layer for now. |
| 522 compositingReasons |= CompositingReasonIsolateCompositedDescendants; |
| 523 } else { |
| 524 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 525 context.forceSubtreeUpdate |= properties->clearMaskClip(); |
| 526 } |
| 527 |
| 528 if (effectNodeNeeded) { |
| 529 auto& properties = object.getMutableForPainting().ensurePaintProperties(); |
| 530 context.forceSubtreeUpdate |= properties.updateEffect( |
| 531 context.currentEffect, context.current.transform, outputClip, |
| 532 CompositorFilterOperations(), opacity, blendMode, compositingReasons, |
| 533 compositorElementId); |
| 534 if (hasMask) { |
| 535 // TODO(crbug.com/683425): PaintArtifactCompositor does not handle |
| 536 // grouping (i.e. descendant-dependent compositing reason) properly yet. |
| 537 // Adding CompositingReasonSquashingDisallowed forces mask not getting |
| 538 // squashed into a child effect. Have no compositing reason otherwise. |
| 539 context.forceSubtreeUpdate |= properties.updateMask( |
| 540 properties.effect(), context.current.transform, outputClip, |
| 541 CompositorFilterOperations(), 1.f, SkBlendMode::kDstIn, |
| 542 CompositingReasonSquashingDisallowed, CompositorElementId()); |
| 543 } else { |
| 544 context.forceSubtreeUpdate |= properties.clearMask(); |
| 545 } |
| 546 } else { |
| 547 if (auto* properties = object.getMutableForPainting().paintProperties()) { |
| 548 context.forceSubtreeUpdate |= properties->clearEffect(); |
| 549 context.forceSubtreeUpdate |= properties->clearMask(); |
| 550 } |
| 551 } |
| 552 } |
| 553 |
| 554 const auto* properties = object.paintProperties(); |
| 555 if (properties && properties->effect()) { |
| 556 context.currentEffect = properties->effect(); |
| 557 if (properties->maskClip()) { |
| 558 context.inputClipOfCurrentEffect = context.current.clip = |
| 559 context.absolutePosition.clip = context.fixedPosition.clip = |
| 560 properties->maskClip(); |
| 561 } |
| 562 } |
| 563 } |
| 564 |
| 565 void PaintPropertyTreeBuilder::updateFilter( |
| 566 const LayoutObject& object, |
| 567 PaintPropertyTreeBuilderContext& context) { |
| 568 const ComputedStyle& style = object.styleRef(); |
| 569 |
| 570 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { |
| 455 CompositorFilterOperations filter; | 571 CompositorFilterOperations filter; |
| 456 if (object.isSVGChild()) { | 572 if (object.isSVGChild()) { |
| 457 // TODO(trchen): SVG caches filters in SVGResources. Implement it. | 573 // TODO(trchen): SVG caches filters in SVGResources. Implement it. |
| 458 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { | 574 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { |
| 459 // TODO(trchen): Eliminate PaintLayer dependency. | 575 // TODO(trchen): Eliminate PaintLayer dependency. |
| 460 filter = layer->createCompositorFilterOperationsForFilter(style); | 576 filter = layer->createCompositorFilterOperationsForFilter(style); |
| 461 } | 577 } |
| 462 | 578 |
| 463 const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect; | |
| 464 // The CSS filter spec didn't specify how filters interact with overflow | 579 // The CSS filter spec didn't specify how filters interact with overflow |
| 465 // clips. The implementation here mimics the old Blink/WebKit behavior for | 580 // clips. The implementation here mimics the old Blink/WebKit behavior for |
| 466 // backward compatibility. | 581 // backward compatibility. |
| 467 // Basically the output of the filter will be affected by clips that applies | 582 // Basically the output of the filter will be affected by clips that applies |
| 468 // to the current element. The descendants that paints into the input of the | 583 // to the current element. The descendants that paints into the input of the |
| 469 // filter ignores any clips collected so far. For example: | 584 // filter ignores any clips collected so far. For example: |
| 470 // <div style="overflow:scroll"> | 585 // <div style="overflow:scroll"> |
| 471 // <div style="filter:blur(1px);"> | 586 // <div style="filter:blur(1px);"> |
| 472 // <div>A</div> | 587 // <div>A</div> |
| 473 // <div style="position:absolute;">B</div> | 588 // <div style="position:absolute;">B</div> |
| 474 // </div> | 589 // </div> |
| 475 // </div> | 590 // </div> |
| 476 // In this example "A" should be clipped if the filter was not present. | 591 // In this example "A" should be clipped if the filter was not present. |
| 477 // With the filter, "A" will be rastered without clipping, but instead | 592 // With the filter, "A" will be rastered without clipping, but instead |
| 478 // the blurred result will be clipped. | 593 // the blurred result will be clipped. |
| 479 // On the other hand, "B" should not be clipped because the overflow clip is | 594 // On the other hand, "B" should not be clipped because the overflow clip is |
| 480 // not in its containing block chain, but as the filter output will be | 595 // not in its containing block chain, but as the filter output will be |
| 481 // clipped, so a blurred "B" may still be invisible. | 596 // clipped, so a blurred "B" may still be invisible. |
| 482 if (!filter.isEmpty()) { | 597 const ClipPaintPropertyNode* outputClip = context.current.clip; |
| 483 effectNodeNeeded = true; | |
| 484 outputClip = context.current.clip; | |
| 485 | 598 |
| 486 // TODO(trchen): A filter may contain spatial operations such that an | 599 // TODO(trchen): A filter may contain spatial operations such that an |
| 487 // output pixel may depend on an input pixel outside of the output clip. | 600 // output pixel may depend on an input pixel outside of the output clip. |
| 488 // We should generate a special clip node to represent this expansion. | 601 // We should generate a special clip node to represent this expansion. |
| 489 } | |
| 490 | 602 |
| 491 CompositingReasons compositingReasons = CompositingReasonNone; | 603 // We may begin to composite our subtree prior to an animation starts, |
| 492 if (CompositingReasonFinder::requiresCompositingForEffectAnimation(style)) { | 604 // but a compositor element ID is only needed when an animation is current. |
| 493 compositingReasons = CompositingReasonActiveAnimation; | |
| 494 effectNodeNeeded = true; | |
| 495 } | |
| 496 | |
| 497 CompositorElementId compositorElementId = | 605 CompositorElementId compositorElementId = |
| 498 (style.hasCurrentOpacityAnimation() || | 606 style.hasCurrentFilterAnimation() |
| 499 style.hasCurrentFilterAnimation() || | |
| 500 style.hasCurrentBackdropFilterAnimation()) | |
| 501 ? createDomNodeBasedCompositorElementId(object) | 607 ? createDomNodeBasedCompositorElementId(object) |
| 502 : CompositorElementId(); | 608 : CompositorElementId(); |
| 609 CompositingReasons compositingReasons = |
| 610 CompositingReasonFinder::requiresCompositingForFilterAnimation(style) |
| 611 ? CompositingReasonActiveAnimation |
| 612 : CompositingReasonNone; |
| 613 DCHECK(!style.hasCurrentFilterAnimation() || |
| 614 compositingReasons != CompositingReasonNone); |
| 503 | 615 |
| 504 if (effectNodeNeeded) { | 616 if (compositingReasons == CompositingReasonNone && filter.isEmpty()) { |
| 617 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 618 context.forceSubtreeUpdate |= properties->clearFilter(); |
| 619 } else { |
| 505 auto& properties = object.getMutableForPainting().ensurePaintProperties(); | 620 auto& properties = object.getMutableForPainting().ensurePaintProperties(); |
| 506 context.forceSubtreeUpdate |= properties.updateEffect( | 621 context.forceSubtreeUpdate |= properties.updateFilter( |
| 507 context.currentEffect, context.current.transform, outputClip, | 622 context.currentEffect, context.current.transform, outputClip, |
| 508 std::move(filter), opacity, blendMode, compositingReasons, | 623 std::move(filter), 1.f, SkBlendMode::kSrcOver, compositingReasons, |
| 509 compositorElementId); | 624 compositorElementId); |
| 510 } else { | |
| 511 if (auto* properties = object.getMutableForPainting().paintProperties()) | |
| 512 context.forceSubtreeUpdate |= properties->clearEffect(); | |
| 513 } | 625 } |
| 514 } | 626 } |
| 515 | 627 |
| 516 const auto* properties = object.paintProperties(); | 628 const auto* properties = object.paintProperties(); |
| 517 if (properties && properties->effect()) { | 629 if (properties && properties->filter()) { |
| 518 context.currentEffect = properties->effect(); | 630 context.currentEffect = properties->filter(); |
| 519 if (!properties->effect()->filter().isEmpty()) { | 631 // TODO(trchen): Change input clip to expansion hint once implemented. |
| 520 // TODO(trchen): Change input clip to expansion hint once implemented. | 632 const ClipPaintPropertyNode* inputClip = properties->filter()->outputClip(); |
| 521 const ClipPaintPropertyNode* inputClip = | 633 context.inputClipOfCurrentEffect = context.current.clip = |
| 522 properties->effect()->outputClip(); | 634 context.absolutePosition.clip = context.fixedPosition.clip = inputClip; |
| 523 context.inputClipOfCurrentEffect = context.current.clip = | |
| 524 context.absolutePosition.clip = context.fixedPosition.clip = | |
| 525 inputClip; | |
| 526 } | |
| 527 } | 635 } |
| 528 } | 636 } |
| 529 | 637 |
| 530 void PaintPropertyTreeBuilder::updateCssClip( | 638 void PaintPropertyTreeBuilder::updateCssClip( |
| 531 const LayoutObject& object, | 639 const LayoutObject& object, |
| 532 PaintPropertyTreeBuilderContext& context) { | 640 PaintPropertyTreeBuilderContext& context) { |
| 533 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { | 641 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { |
| 534 if (object.hasClip()) { | 642 if (object.hasClip()) { |
| 535 // Create clip node for descendants that are not fixed position. | 643 // Create clip node for descendants that are not fixed position. |
| 536 // We don't have to setup context.absolutePosition.clip here because this | 644 // We don't have to setup context.absolutePosition.clip here because this |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 946 // This is not in FindObjectPropertiesNeedingUpdateScope because paint offset | 1054 // This is not in FindObjectPropertiesNeedingUpdateScope because paint offset |
| 947 // can change without needsPaintPropertyUpdate. | 1055 // can change without needsPaintPropertyUpdate. |
| 948 updateForObjectLocation(object, context); | 1056 updateForObjectLocation(object, context); |
| 949 | 1057 |
| 950 #if DCHECK_IS_ON() | 1058 #if DCHECK_IS_ON() |
| 951 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context); | 1059 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context); |
| 952 #endif | 1060 #endif |
| 953 | 1061 |
| 954 if (object.isBoxModelObject() || object.isSVG()) { | 1062 if (object.isBoxModelObject() || object.isSVG()) { |
| 955 updateTransform(object, context); | 1063 updateTransform(object, context); |
| 956 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | 1064 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| 957 updateEffect(object, context); | 1065 updateEffect(object, context); |
| 1066 updateFilter(object, context); |
| 1067 } |
| 958 updateCssClip(object, context); | 1068 updateCssClip(object, context); |
| 959 updateLocalBorderBoxContext(object, context); | 1069 updateLocalBorderBoxContext(object, context); |
| 960 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | 1070 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| 961 updateScrollbarPaintOffset(object, context); | 1071 updateScrollbarPaintOffset(object, context); |
| 962 } | 1072 } |
| 963 } | 1073 } |
| 964 | 1074 |
| 965 void PaintPropertyTreeBuilder::updatePropertiesForChildren( | 1075 void PaintPropertyTreeBuilder::updatePropertiesForChildren( |
| 966 const LayoutObject& object, | 1076 const LayoutObject& object, |
| 967 PaintPropertyTreeBuilderContext& context) { | 1077 PaintPropertyTreeBuilderContext& context) { |
| 968 #if DCHECK_IS_ON() | 1078 #if DCHECK_IS_ON() |
| 969 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context); | 1079 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context); |
| 970 #endif | 1080 #endif |
| 971 | 1081 |
| 972 if (!object.isBoxModelObject() && !object.isSVG()) | 1082 if (!object.isBoxModelObject() && !object.isSVG()) |
| 973 return; | 1083 return; |
| 974 | 1084 |
| 975 updateOverflowClip(object, context); | 1085 updateOverflowClip(object, context); |
| 976 updatePerspective(object, context); | 1086 updatePerspective(object, context); |
| 977 updateSvgLocalToBorderBoxTransform(object, context); | 1087 updateSvgLocalToBorderBoxTransform(object, context); |
| 978 updateScrollAndScrollTranslation(object, context); | 1088 updateScrollAndScrollTranslation(object, context); |
| 979 updateOutOfFlowContext(object, context); | 1089 updateOutOfFlowContext(object, context); |
| 980 | 1090 |
| 981 context.forceSubtreeUpdate |= object.subtreeNeedsPaintPropertyUpdate(); | 1091 context.forceSubtreeUpdate |= object.subtreeNeedsPaintPropertyUpdate(); |
| 982 } | 1092 } |
| 983 | 1093 |
| 984 } // namespace blink | 1094 } // namespace blink |
| OLD | NEW |