summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
blob: b574e0351a3b23df01f1fe1ace70cef5c8377dc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// 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