Skip to content

Commit

Permalink
SET: Compute object-view-box for incoming/outgoing images.
Browse files Browse the repository at this point in the history
This patch makes style tracker compute the object-view-box based on
the border box size and the overflow rect.

This is currently ignoring clip-path since it's using a function for
overflow that ignores this as well.

R=khushalsagar@chromium.org

Change-Id: I2a4329adc3b19da309522666e843fd1058d1a3ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3615255
Reviewed-by: Khushal Sagar <khushalsagar@chromium.org>
Commit-Queue: Vladimir Levin <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1002841}
  • Loading branch information
vmpstr authored and Chromium LUCI CQ committed May 12, 2022
1 parent 1a82a3c commit 2307001
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "third_party/blink/renderer/platform/geometry/layout_size.h"
#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "ui/gfx/geometry/rect_conversions.h"

namespace blink {
namespace {
Expand Down Expand Up @@ -59,6 +60,53 @@ const String& AnimationUAStyles() {
return kAnimationUAStyles;
}

absl::optional<String> ComputeInsetDifference(PhysicalRect reference_rect,
const gfx::Rect& target_rect,
float device_pixel_ratio) {
if (reference_rect.IsEmpty()) {
DCHECK(target_rect.IsEmpty());
return absl::nullopt;
}

// Reference rect is given to us in layout space, but target_rect is in css
// space. Note that this currently relies on the fact that object-view-box
// scales its parameters from CSS to layout space. However, that's a bug.
// TODO(crbug.com/1324618): Fix this when the object-view-box bug is fixed.
gfx::Rect reference_bounding_rect = gfx::ToEnclosingRect(gfx::ScaleRect(
static_cast<gfx::RectF>(reference_rect), 1.0 / device_pixel_ratio));

if (reference_bounding_rect == target_rect)
return absl::nullopt;

int top_offset = target_rect.y() - reference_bounding_rect.y();
int right_offset = reference_bounding_rect.right() - target_rect.right();
int bottom_offset = reference_bounding_rect.bottom() - target_rect.bottom();
int left_offset = target_rect.x() - reference_bounding_rect.x();

DCHECK_GE(top_offset, 0);
DCHECK_GE(right_offset, 0);
DCHECK_GE(bottom_offset, 0);
DCHECK_GE(left_offset, 0);

return String::Format("inset(%dpx %dpx %dpx %dpx)", top_offset, right_offset,
bottom_offset, left_offset);
}

// TODO(vmpstr): This could be optimized by caching values for individual layout
// boxes. However, it's unclear when the cache should be cleared.
PhysicalRect ComputeVisualOverflowRect(LayoutBox* box) {
PhysicalRect result;
for (auto* child = box->Layer()->FirstChild(); child;
child = child->NextSibling()) {
auto* child_box = child->GetLayoutBox();
PhysicalRect overflow_rect = ComputeVisualOverflowRect(child_box);
child_box->MapToVisualRectInAncestorSpace(box, overflow_rect);
result.Unite(overflow_rect);
}
result.Unite(box->PhysicalVisualOverflowRectIncludingFilters());
return result;
}

} // namespace

class DocumentTransitionStyleTracker::ImageWrapperPseudoElement
Expand Down Expand Up @@ -334,9 +382,12 @@ void DocumentTransitionStyleTracker::CaptureResolved() {
for (auto& entry : element_data_map_) {
auto& element_data = entry.value;
element_data->target_element = nullptr;
element_data->cached_border_box_size = element_data->border_box_size;
element_data->cached_border_box_size_in_css_space =
element_data->border_box_size_in_css_space;
element_data->cached_viewport_matrix = element_data->viewport_matrix;
element_data->cached_device_pixel_ratio = element_data->device_pixel_ratio;
element_data->cached_visual_overflow_rect_in_layout_space =
element_data->visual_overflow_rect_in_layout_space;
element_data->effect_node = nullptr;
}
root_effect_node_ = nullptr;
Expand Down Expand Up @@ -577,22 +628,31 @@ void DocumentTransitionStyleTracker::RunPostPrePaintSteps() {
auto* resize_observer_entry =
MakeGarbageCollected<ResizeObserverEntry>(element_data->target_element);
auto entry_size = resize_observer_entry->borderBoxSize()[0];
LayoutSize border_box_size =
LayoutSize border_box_size_in_css_space =
layout_object->IsHorizontalWritingMode()
? LayoutSize(LayoutUnit(entry_size->inlineSize()),
LayoutUnit(entry_size->blockSize()))
: LayoutSize(LayoutUnit(entry_size->blockSize()),
LayoutUnit(entry_size->inlineSize()));

PhysicalRect visual_overflow_rect_in_layout_space;
if (auto* box = DynamicTo<LayoutBox>(layout_object))
visual_overflow_rect_in_layout_space = ComputeVisualOverflowRect(box);

if (viewport_matrix == element_data->viewport_matrix &&
border_box_size == element_data->border_box_size &&
device_pixel_ratio == element_data->device_pixel_ratio) {
border_box_size_in_css_space ==
element_data->border_box_size_in_css_space &&
device_pixel_ratio == element_data->device_pixel_ratio &&
visual_overflow_rect_in_layout_space ==
element_data->visual_overflow_rect_in_layout_space) {
continue;
}

element_data->viewport_matrix = viewport_matrix;
element_data->border_box_size = border_box_size;
element_data->border_box_size_in_css_space = border_box_size_in_css_space;
element_data->device_pixel_ratio = device_pixel_ratio;
element_data->visual_overflow_rect_in_layout_space =
visual_overflow_rect_in_layout_space;

PseudoId live_content_element = HasLiveNewContent()
? kPseudoIdPageTransitionIncomingImage
Expand Down Expand Up @@ -797,6 +857,13 @@ const String& DocumentTransitionStyleTracker::UAStyleSheet() {
auto document_transition_tag = entry.key.GetString().Utf8();
auto& element_data = entry.value;

gfx::Rect border_box_in_css_space = gfx::Rect(
gfx::Size(element_data->border_box_size_in_css_space.Width().ToInt(),
element_data->border_box_size_in_css_space.Height().ToInt()));
gfx::Rect cached_border_box_in_css_space = gfx::Rect(gfx::Size(
element_data->cached_border_box_size_in_css_space.Width().ToInt(),
element_data->cached_border_box_size_in_css_space.Height().ToInt()));

// ::page-transition-container styles using computed properties for each
// element.
builder.AppendFormat(
Expand All @@ -807,15 +874,41 @@ const String& DocumentTransitionStyleTracker::UAStyleSheet() {
transform: %s;
}
)CSS",
document_transition_tag.c_str(),
element_data->border_box_size.Width().ToInt(),
element_data->border_box_size.Height().ToInt(),
document_transition_tag.c_str(), border_box_in_css_space.width(),
border_box_in_css_space.height(),
ComputedStyleUtils::ValueForTransformationMatrix(
element_data->viewport_matrix, 1, false)
->CssText()
.Utf8()
.c_str());

float device_pixel_ratio = document_->DevicePixelRatio();
absl::optional<String> incoming_inset = ComputeInsetDifference(
element_data->visual_overflow_rect_in_layout_space,
border_box_in_css_space, device_pixel_ratio);
if (incoming_inset) {
builder.AppendFormat(
R"CSS(
html::page-transition-incoming-image(%s) {
object-view-box: %s;
}
)CSS",
document_transition_tag.c_str(), incoming_inset->Utf8().c_str());
}

absl::optional<String> outgoing_inset = ComputeInsetDifference(
element_data->cached_visual_overflow_rect_in_layout_space,
cached_border_box_in_css_space, device_pixel_ratio);
if (outgoing_inset) {
builder.AppendFormat(
R"CSS(
html::page-transition-outgoing-image(%s) {
object-view-box: %s;
}
)CSS",
document_transition_tag.c_str(), outgoing_inset->Utf8().c_str());
}

// TODO(khushalsagar) : We'll need to retarget the animation if the final
// value changes during the start phase.
if (add_animations && element_data->old_snapshot_id.IsValid() &&
Expand All @@ -836,8 +929,8 @@ const String& DocumentTransitionStyleTracker::UAStyleSheet() {
->CssText()
.Utf8()
.c_str(),
element_data->cached_border_box_size.Width().ToInt(),
element_data->cached_border_box_size.Height().ToInt());
element_data->cached_border_box_size_in_css_space.Width().ToInt(),
element_data->cached_border_box_size_in_css_space.Height().ToInt());

// TODO(khushalsagar) : The duration/delay in the UA stylesheet will need
// to be the duration from TransitionConfig. See crbug.com/1275727.
Expand Down Expand Up @@ -870,14 +963,15 @@ void DocumentTransitionStyleTracker::ElementData::Trace(
visitor->Trace(target_element);
}

// TODO(vmpstr): We need to write tests for the following:
// * A local transform on the shared element.
// * A transform on an ancestor which changes its screen space transform.
LayoutSize DocumentTransitionStyleTracker::ElementData::GetIntrinsicSize(
bool use_cached_data) {
LayoutSize box_size =
use_cached_data ? cached_border_box_size : border_box_size;
float ratio =
use_cached_data ? cached_device_pixel_ratio : device_pixel_ratio;

box_size.Scale(ratio);
use_cached_data
? cached_visual_overflow_rect_in_layout_space.size.ToLayoutSize()
: visual_overflow_rect_in_layout_space.size.ToLayoutSize();
return box_size;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "components/viz/common/shared_element_resource_id.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/platform/geometry/layout_size.h"
#include "third_party/blink/renderer/platform/graphics/document_transition_shared_element_id.h"
#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
Expand Down Expand Up @@ -136,12 +137,12 @@ class DocumentTransitionStyleTracker

// Computed info for each element participating in the transition for the
// |target_element|. This information is mirrored into the UA stylesheet.
LayoutSize border_box_size;
LayoutSize border_box_size_in_css_space;
TransformationMatrix viewport_matrix;
float device_pixel_ratio = 1.f;

// Computed info cached before the DOM switches to the new state.
LayoutSize cached_border_box_size;
LayoutSize cached_border_box_size_in_css_space;
TransformationMatrix cached_viewport_matrix;
float cached_device_pixel_ratio = 1.f;

Expand All @@ -157,6 +158,12 @@ class DocumentTransitionStyleTracker

// Index to add to the document transition shared element id.
int element_index;

// The visual overflow rect for this element. This is used to compute
// object-view-box if needed.
// This rect is in layout space.
PhysicalRect visual_overflow_rect_in_layout_space;
PhysicalRect cached_visual_overflow_rect_in_layout_space;
};

void InvalidateStyle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ struct CORE_EXPORT PhysicalRect {
return LayoutRect::InfiniteIntRect();
}

void Scale(float s) {
offset.Scale(s);
size.Scale(s);
}

String ToString() const;
};

Expand Down
3 changes: 2 additions & 1 deletion third_party/blink/renderer/core/layout/layout_box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7389,8 +7389,9 @@ LayoutRect LayoutBox::VisualOverflowRect() const {

const LayoutRect& self_visual_overflow_rect =
overflow_->visual_overflow->SelfVisualOverflowRect();
if (HasMask())
if (HasMask()) {
return self_visual_overflow_rect;
}

const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
if (ShouldApplyOverflowClipMargin()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<title>Shared transitions: object-view-box with larger overflow (ref)</title>
<link rel="help" href="https://github.com/WICG/shared-element-transitions">
<link rel="author" href="mailto:vmpstr@chromium.org">
<style>
.target {
color: red;
width: 100px;
height: 100px;
contain: paint;
position: relative;
top: 50px;
left: 50px;
}
.child {
width: 123px;
height: 150px;
background: lightblue;
position: relative;
top: -10px;
left: -20px;
}
.grandchild {
width: 25px;
height: 25px;
position: relative;
top: 20px;
left: 40px;
background: green;
}
body { background: lightpink; }
</style>

<div class=target>
<div class=child>
<div class=grandchild></div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html class=reftest-wait>
<title>Shared transitions: object-view-box with larger overflow</title>
<link rel="help" href="https://github.com/WICG/shared-element-transitions">
<link rel="author" href="mailto:vmpstr@chromium.org">
<link rel="match" href="old-content-object-view-box-overflow-ref.html">
<script src="/common/reftest-wait.js"></script>
<style>
.target {
color: red;
width: 100px;
height: 100px;
contain: paint;
overflow-clip-margin: 1000px;
position: relative;
top: 50px;
left: 50px;
page-transition-tag: target;
}
.child {
width: 123px;
height: 150px;
background: lightblue;
position: relative;
top: -10px;
left: -20px;
}
.grandchild {
width: 25px;
height: 25px;
position: relative;
top: 20px;
left: 40px;
background: green;
}

html::page-transition-container(target) { animation-duration: 300s; }
html::page-transition-incoming-image(target) {
animation: unset;
opacity: 1;

/* clip overflow, and verify inner contents only */
overflow: hidden;
}
html::page-transition-outgoing-image(target) { animation: unset; opacity: 0; }

html::page-transition-container(root) { animation: unset; opacity: 0; }
html::page-transition { background: lightpink; }
</style>

<div class=target>
<div class=child>
<div class=grandchild></div>
</div>
</div>

<script>
async function runTest() {
document.createDocumentTransition().start(() =>
requestAnimationFrame(() => requestAnimationFrame(takeScreenshot)));
}
onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
</script>

0 comments on commit 2307001

Please sign in to comment.