blob: b574e0351a3b23df01f1fe1ace70cef5c8377dc7 [file] [log] [blame]
// 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/modules/v8/v8_ink_trail_style.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.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/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/geometry/dom_rect.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
DelegatedInkTrailPresenter* DelegatedInkTrailPresenter::CreatePresenter(
Element* element,
LocalFrame* frame) {
DCHECK(!element || element->GetDocument() == frame->GetDocument());
return MakeGarbageCollected<DelegatedInkTrailPresenter>(element, frame);
}
DelegatedInkTrailPresenter::DelegatedInkTrailPresenter(Element* element,
LocalFrame* frame)
: presentation_area_(element), local_frame_(frame) {}
void ThrowException(v8::Isolate* isolate,
ExceptionCode code,
const String& error_message) {
ExceptionState exception_state(isolate, ExceptionState::kExecutionContext,
"DelegatedInkTrailPresenter",
"updateInkTrailStatePoint");
exception_state.ThrowException(code, error_message);
}
void DelegatedInkTrailPresenter::updateInkTrailStartPoint(
ScriptState* state,
PointerEvent* evt,
InkTrailStyle* style) {
DCHECK(RuntimeEnabledFeatures::DelegatedInkTrailsEnabled());
if (!state->ContextIsValid()) {
ThrowException(state->GetIsolate(),
ToExceptionCode(DOMExceptionCode::kInvalidStateError),
"The object is no longer associated with a window.");
return;
}
if (!evt->isTrusted()) {
ThrowException(state->GetIsolate(),
ToExceptionCode(DOMExceptionCode::kNotAllowedError),
"Only trusted pointerevents are accepted.");
return;
}
// If diameter is less than or equal to 0, then nothing is going to be
// displayed anyway, so just bail early and save the effort.
if (style->diameter() <= 0) {
ThrowException(state->GetIsolate(),
ToExceptionCode(DOMExceptionCode::kNotSupportedError),
"Delegated ink trail diameter must be greater than 0.");
return;
}
Color color;
if (!CSSParser::ParseColor(color, style->color(), true /*strict*/)) {
ThrowException(state->GetIsolate(),
ToExceptionCode(ESErrorType::kTypeError), "Unknown color.");
return;
}
LayoutView* layout_view = local_frame_->ContentLayoutObject();
DCHECK(layout_view);
const float effective_zoom = layout_view->StyleRef().EffectiveZoom();
PhysicalOffset physical_point(LayoutUnit(evt->x()), LayoutUnit(evt->y()));
physical_point.Scale(effective_zoom);
physical_point = layout_view->LocalToAbsolutePoint(
physical_point, kTraverseDocumentBoundaries);
gfx::PointF point(physical_point.left.ToFloat(),
physical_point.top.ToFloat());
LayoutBox* layout_box = nullptr;
if (presentation_area_) {
layout_box = presentation_area_->GetLayoutBox();
DCHECK(layout_box);
} else {
// If presentation_area_ wasn't provided, then default to the layout
// viewport.
layout_box = layout_view;
}
// Intersect with the visible viewport so that the presentation area can't
// extend beyond the edges of the window or over the scrollbars. The frame
// visual viewport loop accounts for all iframe viewports, and the page visual
// viewport accounts for the full window. Convert everything to root frame
// coordinates in order to make sure offsets aren't lost along the way.
//
// TODO(1052145): Overflow and clip-path clips are ignored here, which results
// in delegated ink trails ignoring the clips and appearing incorrectly in
// some situations. This could also occur due to transformations, as the
// |presenation_area| is currently always a rectilinear bounding box. Ideally
// both of these situations are handled correctly, or the trail doesn't appear
// if we are unable to accurately render it.
PhysicalRect border_box_rect_absolute = layout_box->LocalToAbsoluteRect(
layout_box->PhysicalBorderBoxRect(), kTraverseDocumentBoundaries);
while (layout_view->GetFrame()->OwnerLayoutObject()) {
PhysicalRect frame_visual_viewport_absolute =
layout_view->LocalToAbsoluteRect(
PhysicalRect(
layout_view->GetScrollableArea()->VisibleContentRect()),
kTraverseDocumentBoundaries);
border_box_rect_absolute.Intersect(frame_visual_viewport_absolute);
layout_view = layout_view->GetFrame()->OwnerLayoutObject()->View();
}
border_box_rect_absolute.Intersect(PhysicalRect(
local_frame_->GetPage()->GetVisualViewport().VisibleContentRect()));
gfx::RectF area(border_box_rect_absolute.X().ToFloat(),
border_box_rect_absolute.Y().ToFloat(),
border_box_rect_absolute.Width().ToFloat(),
border_box_rect_absolute.Height().ToFloat());
// This is used to know if the user starts inking with the pointer down or
// not, so that we can stop drawing delegated ink trails as quickly as
// possible if the left button state changes, as presumably that indicates the
// the end of inking.
// Touch events do not need to be special cased here. When something is
// physically touching the screen to trigger a touch event, it is converted to
// a pointerevent with kLeftButtonDown, and if a stylus with hovering
// capabilities sent the touch event, then the resulting pointerevent will not
// have the kLeftButtonDown modifier. In either case, it will match the
// expectations of a normal mouse event, so it doesn't need to be handled
// separately.
const bool is_hovering =
!(evt->GetModifiers() & WebInputEvent::Modifiers::kLeftButtonDown);
const double diameter_in_physical_pixels = style->diameter() * effective_zoom;
std::unique_ptr<viz::DelegatedInkMetadata> metadata =
std::make_unique<viz::DelegatedInkMetadata>(
point, diameter_in_physical_pixels, color.Rgb(),
evt->PlatformTimeStamp(), area, is_hovering);
TRACE_EVENT_INSTANT1(
"blink", "DelegatedInkTrailPresenter::updateInkTrailStartPoint",
TRACE_EVENT_SCOPE_THREAD, "ink metadata", metadata->ToString());
Page* page = local_frame_->GetPage();
page->GetChromeClient().SetDelegatedInkMetadata(local_frame_,
std::move(metadata));
}
void DelegatedInkTrailPresenter::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
visitor->Trace(presentation_area_);
visitor->Trace(local_frame_);
}
} // namespace blink