blob: 4f67b5c84dcb977f3b311b7f6c12b16cfca34987 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/css_mask_painter.h"
#include "third_party/blink/renderer/core/layout/layout_block.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/svg_mask_painter.h"
#include "third_party/blink/renderer/core/style/style_mask_source_image.h"
namespace blink {
namespace {
bool HasSingleInvalidSVGMaskReferenceMaskLayer(const LayoutObject& object,
const FillLayer& first_layer) {
if (first_layer.Next()) {
return false;
}
const auto* mask_source =
DynamicTo<StyleMaskSourceImage>(first_layer.GetImage());
if (!mask_source || !mask_source->HasSVGMask()) {
return false;
}
return !SVGMaskPainter::MaskIsValid(*mask_source, object);
}
} // namespace
std::optional<gfx::RectF> CSSMaskPainter::MaskBoundingBox(
const LayoutObject& object,
const PhysicalOffset& paint_offset) {
if (!object.IsBoxModelObject() && !object.IsSVGChild())
return std::nullopt;
const ComputedStyle& style = object.StyleRef();
if (!style.HasMask())
return std::nullopt;
if (object.IsSVGChild()) {
// This is a kludge. The spec[1] says that a non-existent <mask>
// reference should yield an image layer of transparent black.
//
// [1] https://drafts.fxtf.org/css-masking/#the-mask-image
if (HasSingleInvalidSVGMaskReferenceMaskLayer(object, style.MaskLayers())) {
return std::nullopt;
}
// foreignObject handled by the regular box code.
if (!object.IsSVGForeignObject()) {
return SVGMaskPainter::ResourceBoundsForSVGChild(object);
}
}
PhysicalRect maximum_mask_region;
EFillBox maximum_mask_clip = style.MaskLayers().LayersClipMax();
if (object.IsBox()) {
if (maximum_mask_clip == EFillBox::kNoClip) {
maximum_mask_region =
To<LayoutBox>(object)
.Layer()
->LocalBoundingBoxIncludingSelfPaintingDescendants();
} else {
// We could use a tighter rect for padding-box/content-box.
maximum_mask_region = To<LayoutBox>(object).PhysicalBorderBoxRect();
}
} else {
// For inline elements, depends on the value of box-decoration-break
// there could be one box in multiple fragments or multiple boxes.
// Either way here we are only interested in the bounding box of them.
if (maximum_mask_clip == EFillBox::kNoClip) {
maximum_mask_region =
To<LayoutInline>(object)
.Layer()
->LocalBoundingBoxIncludingSelfPaintingDescendants();
} else {
// We could use a tighter rect for padding-box/content-box.
maximum_mask_region = To<LayoutInline>(object).PhysicalLinesBoundingBox();
}
}
if (style.HasMaskBoxImageOutsets())
maximum_mask_region.Expand(style.MaskBoxImageOutsets());
maximum_mask_region.offset += paint_offset;
return gfx::RectF(maximum_mask_region);
}
} // namespace blink