| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // 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/modules/delegated_ink/delegated_ink_trail_presenter.h" |
| |
| #include "components/viz/common/delegated_ink_metadata.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_pointer_event_init.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/v8_ink_trail_style.h" |
| #include "third_party/blink/renderer/core/dom/element.h" |
| #include "third_party/blink/renderer/core/events/pointer_event.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/html/html_iframe_element.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" |
| #include "third_party/blink/renderer/core/testing/sim/sim_request.h" |
| #include "third_party/blink/renderer/core/testing/sim/sim_test.h" |
| |
| namespace blink { |
| namespace { |
| |
| class TestDelegatedInkMetadata { |
| public: |
| explicit TestDelegatedInkMetadata(viz::DelegatedInkMetadata* metadata) |
| : point_(metadata->point()), |
| color_(metadata->color()), |
| diameter_(metadata->diameter()), |
| area_(metadata->presentation_area()), |
| is_hovering_(metadata->is_hovering()) {} |
| explicit TestDelegatedInkMetadata(gfx::RectF area, |
| float device_pixel_ratio = 1.0) |
| : area_(area) { |
| area_.Scale(device_pixel_ratio); |
| } |
| TestDelegatedInkMetadata() = default; |
| |
| void ExpectEqual(TestDelegatedInkMetadata actual) const { |
| // LayoutUnits cast floats to ints, causing the actual point and area to be |
| // off a small amount from what is expected. |
| EXPECT_NEAR(point_.x(), actual.point_.x(), LayoutUnit::Epsilon()); |
| EXPECT_NEAR(point_.y(), actual.point_.y(), LayoutUnit::Epsilon()); |
| EXPECT_EQ(color_, actual.color_); |
| EXPECT_EQ(diameter_, actual.diameter_); |
| EXPECT_NEAR(area_.x(), actual.area_.x(), LayoutUnit::Epsilon()); |
| EXPECT_NEAR(area_.y(), actual.area_.y(), LayoutUnit::Epsilon()); |
| EXPECT_NEAR(area_.width(), actual.area_.width(), LayoutUnit::Epsilon()); |
| EXPECT_NEAR(area_.height(), actual.area_.height(), LayoutUnit::Epsilon()); |
| EXPECT_EQ(is_hovering_, actual.is_hovering_); |
| } |
| |
| void SetPoint(gfx::PointF pt) { point_ = pt; } |
| void SetColor(SkColor color) { color_ = color; } |
| void SetDiameter(double diameter) { diameter_ = diameter; } |
| void SetArea(gfx::RectF area) { area_ = area; } |
| void SetHovering(bool hovering) { is_hovering_ = hovering; } |
| |
| private: |
| gfx::PointF point_; |
| SkColor color_; |
| double diameter_; |
| gfx::RectF area_; |
| bool is_hovering_; |
| }; |
| |
| DelegatedInkTrailPresenter* CreatePresenter(Element* element, |
| LocalFrame* frame) { |
| return DelegatedInkTrailPresenter::CreatePresenter(element, frame); |
| } |
| |
| } // namespace |
| |
| class DelegatedInkTrailPresenterUnitTest : public SimTest { |
| public: |
| void SetWebViewSize(float width, float height) { |
| WebView().MainFrameViewWidget()->Resize(gfx::Size(width, height)); |
| } |
| |
| void SetWebViewSizeGreaterThanCanvas(float width, float height) { |
| // The presentation area is intersected with the visible content rect, so |
| // make sure that the page size is larger than the canvas to ensure it |
| // isn't clipped. Adding 1 to the height and width is enough to ensure that |
| // doesn't happen. |
| SetWebViewSize(width + 1, height + 1); |
| } |
| |
| PointerEvent* CreatePointerMoveEvent(gfx::PointF pt, bool hovering) { |
| PointerEventInit* init = PointerEventInit::Create(); |
| init->setClientX(pt.x()); |
| init->setClientY(pt.y()); |
| if (!hovering) { |
| init->setButtons(MouseEvent::WebInputEventModifiersToButtons( |
| WebInputEvent::Modifiers::kLeftButtonDown)); |
| } |
| PointerEvent* event = PointerEvent::Create("pointermove", init); |
| event->SetTrusted(true); |
| return event; |
| } |
| |
| TestDelegatedInkMetadata GetActualMetadata() { |
| return TestDelegatedInkMetadata(WebView() |
| .MainFrameViewWidget() |
| ->LayerTreeHostForTesting() |
| ->DelegatedInkMetadataForTesting()); |
| } |
| |
| void SetPageZoomFactor(const float zoom) { |
| GetDocument().GetFrame()->SetPageZoomFactor(zoom); |
| } |
| }; |
| |
| // Test scenarios with the presentation area extending beyond the edges of the |
| // window to confirm it gets clipped correctly. |
| class DelegatedInkTrailPresenterCanvasBeyondViewport |
| : public DelegatedInkTrailPresenterUnitTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| bool CanvasShouldBePastViewport() { return GetParam(); } |
| float GetViewportWidth() const { return kViewportWidth; } |
| float GetViewportHeight() const { return kViewportHeight; } |
| void SetWebViewSize() { |
| DelegatedInkTrailPresenterUnitTest::SetWebViewSize(kViewportWidth, |
| kViewportHeight); |
| } |
| |
| private: |
| const float kViewportWidth = 175.f; |
| const float kViewportHeight = 180.f; |
| }; |
| |
| // Confirm that all the information is collected and transformed correctly, if |
| // necessary. Numbers and color used were chosen arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, |
| CollectAndPropagateMetadata) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 191px; |
| height: 234px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| const float kCanvasWidth = 191.f; |
| const float kCanvasHeight = 234.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kCanvasWidth, kCanvasHeight); |
| expected_metadata.SetArea(gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight)); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea( |
| gfx::RectF(0, 0, GetViewportWidth(), GetViewportHeight())); |
| } |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| GetDocument().getElementById("canvas"), GetDocument().GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(5); |
| style.setColor("blue"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorBLUE); |
| |
| gfx::PointF pt(100, 100); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(GetDocument().GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ true), &style); |
| expected_metadata.SetHovering(true); |
| expected_metadata.SetPoint(pt); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that everything is still calculated correctly when the |
| // DevicePixelRatio is not 1. Numbers and color used were chosen arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, |
| NotDefaultDevicePixelRatio) { |
| const float kZoom = 1.7; |
| SetPageZoomFactor(kZoom); |
| |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 281px; |
| height: 190px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| const float kCanvasWidth = 281.f; |
| const float kCanvasHeight = 190.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kCanvasWidth * kZoom, |
| kCanvasHeight * kZoom); |
| expected_metadata = TestDelegatedInkMetadata( |
| gfx::RectF(0, 0, kCanvasWidth, kCanvasHeight), kZoom); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea( |
| gfx::RectF(0, 0, GetViewportWidth(), GetViewportHeight())); |
| } |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| GetDocument().getElementById("canvas"), GetDocument().GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(101.5); |
| style.setColor("magenta"); |
| expected_metadata.SetDiameter(style.diameter() * kZoom); |
| expected_metadata.SetColor(SK_ColorMAGENTA); |
| |
| gfx::PointF pt(87, 113); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(GetDocument().GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ true), &style); |
| expected_metadata.SetHovering(true); |
| pt.Scale(kZoom); |
| expected_metadata.SetPoint(pt); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that the offset is correct. Numbers and color used were chosen |
| // arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, CanvasNotAtOrigin) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 250px; |
| height: 350px; |
| position: fixed; |
| top: 59px; |
| left: 16px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| const float kCanvasWidth = 250.f; |
| const float kCanvasHeight = 350.f; |
| const float kCanvasTopOffset = 59.f; |
| const float kCanvasLeftOffset = 16.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kCanvasWidth + kCanvasLeftOffset, |
| kCanvasHeight + kCanvasTopOffset); |
| expected_metadata.SetArea(gfx::RectF(kCanvasLeftOffset, kCanvasTopOffset, |
| kCanvasWidth, kCanvasHeight)); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea( |
| gfx::RectF(kCanvasLeftOffset, kCanvasTopOffset, |
| GetViewportWidth() - kCanvasLeftOffset, |
| GetViewportHeight() - kCanvasTopOffset)); |
| } |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| GetDocument().getElementById("canvas"), GetDocument().GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(8.6); |
| style.setColor("red"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorRED); |
| |
| gfx::PointF pt(380, 175); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(GetDocument().GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ false), &style); |
| expected_metadata.SetHovering(false); |
| expected_metadata.SetPoint(pt); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that values, specifically offsets, are transformed correctly when |
| // the canvas is in an iframe. Numbers and color used were chosen arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, CanvasInIFrame) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #iframe { |
| width: 500px; |
| height: 500px; |
| position: fixed; |
| top: 26px; |
| left: 57px; |
| } |
| </style> |
| <iframe id='iframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 250px; |
| height: 250px; |
| position: fixed; |
| top: 33px; |
| left: 16px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe that the canvas is in, and the 2px |
| // border around the iframe. |
| const float kIframeBorder = 2.f; |
| const float kIframeLeftOffset = 57.f + kIframeBorder; |
| const float kIframeTopOffset = 26.f + kIframeBorder; |
| const float kIframeHeight = 500.f; |
| const float kIframeWidth = 500.f; |
| const float kCanvasLeftOffset = 16.f; |
| const float kCanvasTopOffset = 33.f; |
| const float kCanvasHeight = 250.f; |
| const float kCanvasWidth = 250.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kIframeWidth + kIframeLeftOffset, |
| kIframeHeight + kIframeTopOffset); |
| expected_metadata.SetArea(gfx::RectF(kIframeLeftOffset + kCanvasLeftOffset, |
| kIframeTopOffset + kCanvasTopOffset, |
| kCanvasWidth, kCanvasHeight)); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea(gfx::RectF( |
| kIframeLeftOffset + kCanvasLeftOffset, |
| kIframeTopOffset + kCanvasTopOffset, |
| GetViewportWidth() - (kIframeLeftOffset + kCanvasLeftOffset), |
| GetViewportHeight() - (kIframeTopOffset + kCanvasTopOffset))); |
| } |
| |
| auto* iframe_element = |
| To<HTMLIFrameElement>(GetDocument().getElementById("iframe")); |
| auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame()); |
| Document* iframe_document = iframe_element->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| iframe_localframe->GetDocument()->getElementById("canvas"), |
| iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(0.3); |
| style.setColor("cyan"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorCYAN); |
| |
| gfx::PointF pt(380, 375); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ false), &style); |
| expected_metadata.SetHovering(false); |
| expected_metadata.SetPoint( |
| gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that values, specifically offsets, are transformed correctly when |
| // the canvas is in a nested iframe. Numbers and color used were chosen |
| // arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, NestedIframe) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| SimRequest frame2_resource("https://example.com/iframe2.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #OuterIframe { |
| width: 500px; |
| height: 500px; |
| position: fixed; |
| top: 26px; |
| left: 57px; |
| } |
| </style> |
| <iframe id='OuterIframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #InnerIframe { |
| width: 400px; |
| height: 400px; |
| position: fixed; |
| top: 11px; |
| left: 18px; |
| } |
| </style> |
| <iframe id='InnerIframe' src='https://example.com/iframe2.html'> |
| </iframe> |
| )HTML"); |
| |
| frame2_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 250px; |
| height: 250px; |
| position: fixed; |
| top: 28px; |
| left: 6px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe that the canvas is in, and the 2px |
| // border around the iframe. |
| const float kIframeBorder = 2.f; |
| const float kOuterIframeLeftOffset = 57.f + kIframeBorder; |
| const float kOuterIframeTopOffset = 26.f + kIframeBorder; |
| const float kOuterIframeHeight = 500.f; |
| const float kOuterIframeWidth = 500.f; |
| const float kInnerIframeLeftOffset = |
| kOuterIframeLeftOffset + 18.f + kIframeBorder; |
| const float kInnerIframeTopOffset = |
| kOuterIframeTopOffset + 11.f + kIframeBorder; |
| const float kCanvasLeftOffset = 6.f; |
| const float kCanvasTopOffset = 28.f; |
| const float kCanvasHeight = 250.f; |
| const float kCanvasWidth = 250.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kOuterIframeWidth + kOuterIframeLeftOffset, |
| kOuterIframeHeight + kOuterIframeTopOffset); |
| expected_metadata.SetArea(gfx::RectF( |
| kInnerIframeLeftOffset + kCanvasLeftOffset, |
| kInnerIframeTopOffset + kCanvasTopOffset, kCanvasWidth, kCanvasHeight)); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea(gfx::RectF( |
| kInnerIframeLeftOffset + kCanvasLeftOffset, |
| kInnerIframeTopOffset + kCanvasTopOffset, |
| GetViewportWidth() - (kInnerIframeLeftOffset + kCanvasLeftOffset), |
| GetViewportHeight() - (kInnerIframeTopOffset + kCanvasTopOffset))); |
| } |
| |
| auto* outer_iframe_element = |
| To<HTMLIFrameElement>(GetDocument().getElementById("OuterIframe")); |
| auto* inner_iframe_element = To<HTMLIFrameElement>( |
| outer_iframe_element->contentDocument()->getElementById("InnerIframe")); |
| auto* iframe_localframe = |
| To<LocalFrame>(inner_iframe_element->ContentFrame()); |
| Document* iframe_document = inner_iframe_element->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| iframe_localframe->GetDocument()->getElementById("canvas"), |
| iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(100000.3); |
| style.setColor("yellow"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorYELLOW); |
| |
| gfx::PointF pt(350, 375); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ true), &style); |
| expected_metadata.SetHovering(true); |
| expected_metadata.SetPoint(gfx::PointF(pt.x() + kInnerIframeLeftOffset, |
| pt.y() + kInnerIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that values are correct when an iframe is used and presentation area |
| // isn't provided. Numbers and color used were chosen arbitrarily. |
| TEST_P(DelegatedInkTrailPresenterCanvasBeyondViewport, |
| IFrameNoPresentationArea) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #iframe { |
| width: 500px; |
| height: 500px; |
| position: fixed; |
| top: 56px; |
| left: 72px; |
| } |
| </style> |
| <iframe id='iframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| </style> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe, and the 2px border. |
| const float kIframeBorder = 2.f; |
| const float kIframeLeftOffset = 72.f + kIframeBorder; |
| const float kIframeTopOffset = 56.f + kIframeBorder; |
| const float kIframeHeight = 500.f; |
| const float kIframeWidth = 500.f; |
| |
| TestDelegatedInkMetadata expected_metadata; |
| |
| if (!CanvasShouldBePastViewport()) { |
| SetWebViewSizeGreaterThanCanvas(kIframeWidth + kIframeLeftOffset, |
| kIframeHeight + kIframeTopOffset); |
| expected_metadata.SetArea(gfx::RectF(kIframeLeftOffset, kIframeTopOffset, |
| kIframeWidth, kIframeHeight)); |
| } else { |
| SetWebViewSize(); |
| expected_metadata.SetArea( |
| gfx::RectF(kIframeLeftOffset, kIframeTopOffset, |
| GetViewportWidth() - kIframeLeftOffset, |
| GetViewportHeight() - kIframeTopOffset)); |
| } |
| |
| Document* iframe_document = |
| To<HTMLIFrameElement>(GetDocument().getElementById("iframe")) |
| ->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = |
| CreatePresenter(nullptr, iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(0.01); |
| style.setColor("white"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorWHITE); |
| |
| gfx::PointF pt(380, 375); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ true), &style); |
| expected_metadata.SetHovering(true); |
| expected_metadata.SetPoint( |
| gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| DelegatedInkTrailPresenterCanvasBeyondViewport, |
| testing::Bool()); |
| |
| // Confirm that presentation area defaults to the size of the viewport. |
| // Numbers and color used were chosen arbitrarily. |
| TEST_F(DelegatedInkTrailPresenterUnitTest, PresentationAreaNotProvided) { |
| const int kViewportHeight = 555; |
| const int kViewportWidth = 333; |
| SetWebViewSize(kViewportWidth, kViewportHeight); |
| |
| DelegatedInkTrailPresenter* presenter = |
| CreatePresenter(nullptr, GetDocument().GetFrame()); |
| DCHECK(presenter); |
| |
| TestDelegatedInkMetadata expected_metadata( |
| gfx::RectF(0, 0, kViewportWidth, kViewportHeight)); |
| |
| InkTrailStyle style; |
| style.setDiameter(3.6); |
| style.setColor("yellow"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorYELLOW); |
| |
| gfx::PointF pt(70, 109); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(GetDocument().GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ false), &style); |
| expected_metadata.SetHovering(false); |
| expected_metadata.SetPoint(pt); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Test that the presentation area is clipped correctly by the dimensions of |
| // the iframe, even when the iframe and canvas each fit entirely within the |
| // visual viewport. |
| TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasExtendsOutsideOfIframe) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #iframe { |
| width: 150px; |
| height: 150px; |
| position: fixed; |
| top: 13px; |
| left: 19px; |
| } |
| </style> |
| <iframe id='iframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 199px; |
| height: 202px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe that the canvas is in, and the 2px |
| // border around the iframe. |
| const float kIframeBorder = 2.f; |
| const float kIframeLeftOffset = 19.f + kIframeBorder; |
| const float kIframeTopOffset = 13.f + kIframeBorder; |
| const float kIframeHeight = 150.f; |
| const float kIframeWidth = 150.f; |
| const float kCanvasHeight = 202.f; |
| const float kCanvasWidth = 199.f; |
| |
| // Ensure that the webpage is larger than the iframe and canvas. |
| SetWebViewSize(kCanvasWidth + kIframeLeftOffset + 1, |
| kCanvasHeight + kIframeTopOffset + 1); |
| |
| TestDelegatedInkMetadata expected_metadata(gfx::RectF( |
| kIframeLeftOffset, kIframeTopOffset, kIframeWidth, kIframeHeight)); |
| |
| auto* iframe_element = |
| To<HTMLIFrameElement>(GetDocument().getElementById("iframe")); |
| auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame()); |
| Document* iframe_document = iframe_element->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| iframe_localframe->GetDocument()->getElementById("canvas"), |
| iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(99.999); |
| style.setColor("lime"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorGREEN); |
| |
| gfx::PointF pt(102, 67); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ false), &style); |
| expected_metadata.SetHovering(false); |
| expected_metadata.SetPoint( |
| gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Test that the presentation area is clipped correctly when it is offset left |
| // and above the iframe boundaries. |
| TEST_F(DelegatedInkTrailPresenterUnitTest, CanvasLeftAndAboveIframeBoundaries) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #iframe { |
| width: 300px; |
| height: 301px; |
| position: fixed; |
| top: 13px; |
| left: 19px; |
| } |
| </style> |
| <iframe id='iframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 189px; |
| height: 145px; |
| position: fixed; |
| top: -70px; |
| left: -99px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe that the canvas is in, and the 2px |
| // border around the iframe. |
| const float kIframeBorder = 2.f; |
| const float kIframeLeftOffset = 19.f + kIframeBorder; |
| const float kIframeTopOffset = 13.f + kIframeBorder; |
| const float kIframeHeight = 301.f; |
| const float kIframeWidth = 300.f; |
| const float kCanvasHeight = 145.f; |
| const float kCanvasWidth = 189.f; |
| const float kCanvasLeftOffset = -99.f; |
| const float kCanvasTopOffset = -70.f; |
| |
| // Ensure that the webpage is larger than the iframe. |
| SetWebViewSize(kIframeHeight + kIframeLeftOffset + 1, |
| kIframeWidth + kIframeTopOffset + 1); |
| |
| TestDelegatedInkMetadata expected_metadata(gfx::RectF( |
| kIframeLeftOffset, kIframeTopOffset, kCanvasWidth + kCanvasLeftOffset, |
| kCanvasHeight + kCanvasTopOffset)); |
| |
| auto* iframe_element = |
| To<HTMLIFrameElement>(GetDocument().getElementById("iframe")); |
| auto* iframe_localframe = To<LocalFrame>(iframe_element->ContentFrame()); |
| Document* iframe_document = iframe_element->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| iframe_localframe->GetDocument()->getElementById("canvas"), |
| iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(99.999); |
| style.setColor("lime"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorGREEN); |
| |
| gfx::PointF pt(102, 67); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ true), &style); |
| expected_metadata.SetHovering(true); |
| expected_metadata.SetPoint( |
| gfx::PointF(pt.x() + kIframeLeftOffset, pt.y() + kIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| // Confirm that values, specifically presentation area, are transformed |
| // correctly when the iframe that the canvas is in is clipped by its parent |
| // iframe. Numbers and color used were chosen arbitrarily. |
| TEST_F(DelegatedInkTrailPresenterUnitTest, OuterIframeClipsInnerIframe) { |
| SimRequest main_resource("https://example.com/test.html", "text/html"); |
| SimRequest frame_resource("https://example.com/iframe.html", "text/html"); |
| SimRequest frame2_resource("https://example.com/iframe2.html", "text/html"); |
| LoadURL("https://example.com/test.html"); |
| main_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #OuterIframe { |
| width: 500px; |
| height: 500px; |
| position: fixed; |
| top: 26px; |
| left: 57px; |
| } |
| </style> |
| <iframe id='OuterIframe' src='https://example.com/iframe.html'> |
| </iframe> |
| )HTML"); |
| |
| frame_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| #InnerIframe { |
| width: 400px; |
| height: 400px; |
| position: fixed; |
| top: 311px; |
| left: 334px; |
| } |
| </style> |
| <iframe id='InnerIframe' src='https://example.com/iframe2.html'> |
| </iframe> |
| )HTML"); |
| |
| frame2_resource.Complete(R"HTML( |
| <!DOCTYPE html> |
| <style> |
| body { |
| margin: 0; |
| } |
| canvas { |
| width: 250px; |
| height: 250px; |
| position: fixed; |
| top: 1px; |
| left: 2px; |
| } |
| </style> |
| <canvas id='canvas'></canvas> |
| )HTML"); |
| |
| Compositor().BeginFrame(); |
| |
| // When creating the expected metadata, we have to take into account the |
| // offsets that are applied to the iframe that the canvas is in, and the 2px |
| // border around the iframe. |
| const float kIframeBorder = 2.f; |
| const float kOuterIframeLeftOffset = 57.f + kIframeBorder; |
| const float kOuterIframeTopOffset = 26.f + kIframeBorder; |
| const float kOuterIframeHeight = 500.f; |
| const float kOuterIframeWidth = 500.f; |
| const float kInnerIframeLeftOffset = |
| kOuterIframeLeftOffset + 334.f + kIframeBorder; |
| const float kInnerIframeTopOffset = |
| kOuterIframeTopOffset + 311.f + kIframeBorder; |
| const float kCanvasLeftOffset = 2.f; |
| const float kCanvasTopOffset = 1.f; |
| |
| // Ensure that the webpage is larger than the iframe and canvas. |
| const float kViewportWidth = kOuterIframeWidth + kOuterIframeLeftOffset + 1.f; |
| const float kViewportHeight = |
| kOuterIframeHeight + kOuterIframeTopOffset + 1.f; |
| SetWebViewSize(kViewportWidth, kViewportHeight); |
| |
| TestDelegatedInkMetadata expected_metadata( |
| gfx::RectF(kInnerIframeLeftOffset + kCanvasLeftOffset, |
| kInnerIframeTopOffset + kCanvasTopOffset, |
| kOuterIframeWidth + kOuterIframeLeftOffset - |
| kInnerIframeLeftOffset - kCanvasLeftOffset, |
| kOuterIframeHeight + kOuterIframeTopOffset - |
| kInnerIframeTopOffset - kCanvasTopOffset)); |
| |
| auto* outer_iframe_element = |
| To<HTMLIFrameElement>(GetDocument().getElementById("OuterIframe")); |
| auto* inner_iframe_element = To<HTMLIFrameElement>( |
| outer_iframe_element->contentDocument()->getElementById("InnerIframe")); |
| auto* iframe_localframe = |
| To<LocalFrame>(inner_iframe_element->ContentFrame()); |
| Document* iframe_document = inner_iframe_element->contentDocument(); |
| |
| DelegatedInkTrailPresenter* presenter = CreatePresenter( |
| iframe_localframe->GetDocument()->getElementById("canvas"), |
| iframe_document->GetFrame()); |
| DCHECK(presenter); |
| |
| InkTrailStyle style; |
| style.setDiameter(19); |
| style.setColor("red"); |
| expected_metadata.SetDiameter(style.diameter()); |
| expected_metadata.SetColor(SK_ColorRED); |
| |
| gfx::PointF pt(357, 401); |
| presenter->updateInkTrailStartPoint( |
| ToScriptStateForMainWorld(iframe_document->GetFrame()), |
| CreatePointerMoveEvent(pt, /*hovering*/ false), &style); |
| expected_metadata.SetHovering(false); |
| expected_metadata.SetPoint(gfx::PointF(pt.x() + kInnerIframeLeftOffset, |
| pt.y() + kInnerIframeTopOffset)); |
| |
| expected_metadata.ExpectEqual(GetActualMetadata()); |
| } |
| |
| } // namespace blink |