1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc.
3 * All rights reserved.
4 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
5 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
6 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
7 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
8 * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
9 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved.
10 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h"
35
36 #include "base/metrics/histogram_functions.h"
37 #include "base/rand_util.h"
38 #include "third_party/blink/public/common/features.h"
39 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
40 #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
41 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
42 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
43 #include "third_party/blink/public/platform/platform.h"
44 #include "third_party/blink/public/platform/task_type.h"
45 #include "third_party/blink/renderer/bindings/modules/v8/rendering_context.h"
46 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
47 #include "third_party/blink/renderer/core/css/css_font_selector.h"
48 #include "third_party/blink/renderer/core/css/css_property_names.h"
49 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
50 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
51 #include "third_party/blink/renderer/core/css/style_engine.h"
52 #include "third_party/blink/renderer/core/dom/events/event.h"
53 #include "third_party/blink/renderer/core/events/mouse_event.h"
54 #include "third_party/blink/renderer/core/frame/settings.h"
55 #include "third_party/blink/renderer/core/frame/web_feature.h"
56 #include "third_party/blink/renderer/core/html/canvas/canvas_font_cache.h"
57 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
58 #include "third_party/blink/renderer/core/layout/hit_test_canvas_result.h"
59 #include "third_party/blink/renderer/core/layout/layout_box.h"
60 #include "third_party/blink/renderer/core/layout/layout_theme.h"
61 #include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
62 #include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
63 #include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h"
64 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h"
65 #include "third_party/blink/renderer/modules/canvas/canvas2d/hit_region.h"
66 #include "third_party/blink/renderer/modules/canvas/canvas2d/path_2d.h"
67 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
68 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
69 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
70 #include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
71 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
72 #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
73 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
74 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
75 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
76 #include "third_party/blink/renderer/platform/heap/heap.h"
77 #include "third_party/blink/renderer/platform/text/bidi_text_run.h"
78 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
79 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
80
81 namespace blink {
82
83 static const base::TimeDelta kTryRestoreContextInterval =
84 base::TimeDelta::FromMilliseconds(500);
85 static const unsigned kMaxTryRestoreContextAttempts = 4;
86
ContextLostRestoredEventsEnabled()87 static bool ContextLostRestoredEventsEnabled() {
88 return RuntimeEnabledFeatures::Canvas2dContextLostRestoredEnabled();
89 }
90
91 // Drawing methods need to use this instead of SkAutoCanvasRestore in case
92 // overdraw detection substitutes the recording canvas (to discard overdrawn
93 // draw calls).
94 class CanvasRenderingContext2DAutoRestoreSkCanvas {
95 STACK_ALLOCATED();
96
97 public:
CanvasRenderingContext2DAutoRestoreSkCanvas(CanvasRenderingContext2D * context)98 explicit CanvasRenderingContext2DAutoRestoreSkCanvas(
99 CanvasRenderingContext2D* context)
100 : context_(context), save_count_(0) {
101 DCHECK(context_);
102 cc::PaintCanvas* c = context_->GetOrCreatePaintCanvas();
103 if (c) {
104 save_count_ = c->getSaveCount();
105 }
106 }
107
~CanvasRenderingContext2DAutoRestoreSkCanvas()108 ~CanvasRenderingContext2DAutoRestoreSkCanvas() {
109 cc::PaintCanvas* c = context_->GetOrCreatePaintCanvas();
110 if (c)
111 c->restoreToCount(save_count_);
112 context_->ValidateStateStack();
113 }
114
115 private:
116 CanvasRenderingContext2D* context_;
117 int save_count_;
118 };
119
Create(CanvasRenderingContextHost * host,const CanvasContextCreationAttributesCore & attrs)120 CanvasRenderingContext* CanvasRenderingContext2D::Factory::Create(
121 CanvasRenderingContextHost* host,
122 const CanvasContextCreationAttributesCore& attrs) {
123 DCHECK(!host->IsOffscreenCanvas());
124 CanvasRenderingContext* rendering_context =
125 MakeGarbageCollected<CanvasRenderingContext2D>(
126 static_cast<HTMLCanvasElement*>(host), attrs);
127 DCHECK(rendering_context);
128 rendering_context->RecordUKMCanvasRenderingAPI(
129 CanvasRenderingContext::CanvasRenderingAPI::k2D);
130 return rendering_context;
131 }
132
CanvasRenderingContext2D(HTMLCanvasElement * canvas,const CanvasContextCreationAttributesCore & attrs)133 CanvasRenderingContext2D::CanvasRenderingContext2D(
134 HTMLCanvasElement* canvas,
135 const CanvasContextCreationAttributesCore& attrs)
136 : CanvasRenderingContext(canvas, attrs),
137 context_lost_mode_(kNotLostContext),
138 context_restorable_(true),
139 try_restore_context_attempt_count_(0),
140 dispatch_context_lost_event_timer_(
141 canvas->GetDocument().GetTaskRunner(TaskType::kMiscPlatformAPI),
142 this,
143 &CanvasRenderingContext2D::DispatchContextLostEvent),
144 dispatch_context_restored_event_timer_(
145 canvas->GetDocument().GetTaskRunner(TaskType::kMiscPlatformAPI),
146 this,
147 &CanvasRenderingContext2D::DispatchContextRestoredEvent),
148 try_restore_context_event_timer_(
149 canvas->GetDocument().GetTaskRunner(TaskType::kMiscPlatformAPI),
150 this,
151 &CanvasRenderingContext2D::TryRestoreContextEvent),
152 should_prune_local_font_cache_(false),
153 random_generator_((uint32_t)base::RandUint64()),
154 bernoulli_distribution_(kRasterMetricProbability),
155 ukm_recorder_(canvas->GetDocument().UkmRecorder()),
156 ukm_source_id_(canvas->GetDocument().UkmSourceID()) {
157 if (canvas->GetDocument().GetSettings() &&
158 canvas->GetDocument().GetSettings()->GetAntialiasedClips2dCanvasEnabled())
159 clip_antialiasing_ = kAntiAliased;
160 SetShouldAntialias(true);
161 ValidateStateStack();
162 }
163
SetCanvasGetContextResult(RenderingContext & result)164 void CanvasRenderingContext2D::SetCanvasGetContextResult(
165 RenderingContext& result) {
166 result.SetCanvasRenderingContext2D(this);
167 }
168
169 CanvasRenderingContext2D::~CanvasRenderingContext2D() = default;
170
ValidateStateStackWithCanvas(const cc::PaintCanvas * canvas) const171 void CanvasRenderingContext2D::ValidateStateStackWithCanvas(
172 const cc::PaintCanvas* canvas) const {
173 #if DCHECK_IS_ON()
174 if (canvas) {
175 // The canvas should always have an initial save frame, to support
176 // resetting the top level matrix and clip.
177 DCHECK_GT(canvas->getSaveCount(), 1);
178
179 if (context_lost_mode_ == kNotLostContext) {
180 DCHECK_EQ(static_cast<size_t>(canvas->getSaveCount()),
181 state_stack_.size() + 1);
182 }
183 }
184 #endif
185 CHECK(state_stack_.front()
186 .Get()); // Temporary for investigating crbug.com/648510
187 }
188
IsAccelerated() const189 bool CanvasRenderingContext2D::IsAccelerated() const {
190 Canvas2DLayerBridge* layer_bridge = canvas()->GetCanvas2DLayerBridge();
191 if (!layer_bridge)
192 return false;
193 return layer_bridge->IsAccelerated();
194 }
195
IsOriginTopLeft() const196 bool CanvasRenderingContext2D::IsOriginTopLeft() const {
197 // Accelerated 2D contexts have the origin of coordinates on the bottom left,
198 // except if they are used for low latency mode (front buffer rendering).
199 return !IsAccelerated() || canvas()->LowLatencyEnabled();
200 }
201
IsComposited() const202 bool CanvasRenderingContext2D::IsComposited() const {
203 return IsAccelerated();
204 }
205
Stop()206 void CanvasRenderingContext2D::Stop() {
207 if (!isContextLost()) {
208 // Never attempt to restore the context because the page is being torn down.
209 LoseContext(kSyntheticLostContext);
210 }
211 }
212
isContextLost() const213 bool CanvasRenderingContext2D::isContextLost() const {
214 return context_lost_mode_ != kNotLostContext;
215 }
216
LoseContext(LostContextMode lost_mode)217 void CanvasRenderingContext2D::LoseContext(LostContextMode lost_mode) {
218 if (context_lost_mode_ != kNotLostContext)
219 return;
220 context_lost_mode_ = lost_mode;
221 if (context_lost_mode_ == kSyntheticLostContext && Host()) {
222 Host()->DiscardResourceProvider();
223 }
224 dispatch_context_lost_event_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
225 }
226
DidSetSurfaceSize()227 void CanvasRenderingContext2D::DidSetSurfaceSize() {
228 if (!context_restorable_)
229 return;
230 // This code path is for restoring from an eviction
231 // Restoring from surface failure is handled internally
232 DCHECK(context_lost_mode_ != kNotLostContext && !IsPaintable());
233
234 if (CanCreateCanvas2dResourceProvider()) {
235 if (ContextLostRestoredEventsEnabled()) {
236 dispatch_context_restored_event_timer_.StartOneShot(base::TimeDelta(),
237 FROM_HERE);
238 } else {
239 // legacy synchronous context restoration.
240 Reset();
241 context_lost_mode_ = kNotLostContext;
242 }
243 }
244 }
245
Trace(Visitor * visitor) const246 void CanvasRenderingContext2D::Trace(Visitor* visitor) const {
247 visitor->Trace(hit_region_manager_);
248 visitor->Trace(filter_operations_);
249 CanvasRenderingContext::Trace(visitor);
250 BaseRenderingContext2D::Trace(visitor);
251 SVGResourceClient::Trace(visitor);
252 }
253
DispatchContextLostEvent(TimerBase *)254 void CanvasRenderingContext2D::DispatchContextLostEvent(TimerBase*) {
255 if (canvas() && ContextLostRestoredEventsEnabled()) {
256 Event* event = Event::CreateCancelable(event_type_names::kContextlost);
257 canvas()->DispatchEvent(*event);
258 if (event->defaultPrevented()) {
259 context_restorable_ = false;
260 }
261 }
262
263 // If RealLostContext, it means the context was not lost due to surface
264 // failure but rather due to a an eviction, which means image buffer exists.
265 if (context_restorable_ && context_lost_mode_ == kRealLostContext) {
266 try_restore_context_attempt_count_ = 0;
267 try_restore_context_event_timer_.StartRepeating(kTryRestoreContextInterval,
268 FROM_HERE);
269 }
270 }
271
TryRestoreContextEvent(TimerBase * timer)272 void CanvasRenderingContext2D::TryRestoreContextEvent(TimerBase* timer) {
273 if (context_lost_mode_ == kNotLostContext) {
274 // Canvas was already restored (possibly thanks to a resize), so stop
275 // trying.
276 try_restore_context_event_timer_.Stop();
277 return;
278 }
279
280 DCHECK(context_lost_mode_ == kRealLostContext);
281 if (IsPaintable() && canvas()->GetCanvas2DLayerBridge()->Restore()) {
282 try_restore_context_event_timer_.Stop();
283 DispatchContextRestoredEvent(nullptr);
284 }
285
286 if (++try_restore_context_attempt_count_ > kMaxTryRestoreContextAttempts) {
287 // final attempt: allocate a brand new image buffer instead of restoring
288 Host()->DiscardResourceProvider();
289 try_restore_context_event_timer_.Stop();
290 if (CanCreateCanvas2dResourceProvider())
291 DispatchContextRestoredEvent(nullptr);
292 }
293 }
294
DispatchContextRestoredEvent(TimerBase *)295 void CanvasRenderingContext2D::DispatchContextRestoredEvent(TimerBase*) {
296 if (context_lost_mode_ == kNotLostContext)
297 return;
298 Reset();
299 context_lost_mode_ = kNotLostContext;
300 if (ContextLostRestoredEventsEnabled()) {
301 Event* event(Event::Create(event_type_names::kContextrestored));
302 canvas()->DispatchEvent(*event);
303 }
304 }
305
WillDrawImage(CanvasImageSource * source) const306 void CanvasRenderingContext2D::WillDrawImage(CanvasImageSource* source) const {
307 canvas()->WillDrawImageTo2DContext(source);
308 }
309
ColorSpaceAsString() const310 String CanvasRenderingContext2D::ColorSpaceAsString() const {
311 return CanvasRenderingContext::ColorSpaceAsString();
312 }
313
ColorParams() const314 CanvasColorParams CanvasRenderingContext2D::ColorParams() const {
315 return CanvasRenderingContext::ColorParams();
316 }
317
WritePixels(const SkImageInfo & orig_info,const void * pixels,size_t row_bytes,int x,int y)318 bool CanvasRenderingContext2D::WritePixels(const SkImageInfo& orig_info,
319 const void* pixels,
320 size_t row_bytes,
321 int x,
322 int y) {
323 DCHECK(IsPaintable());
324 return canvas()->GetCanvas2DLayerBridge()->WritePixels(orig_info, pixels,
325 row_bytes, x, y);
326 }
327
WillOverwriteCanvas()328 void CanvasRenderingContext2D::WillOverwriteCanvas() {
329 if (IsPaintable())
330 canvas()->GetCanvas2DLayerBridge()->WillOverwriteCanvas();
331 }
332
PixelFormat() const333 CanvasPixelFormat CanvasRenderingContext2D::PixelFormat() const {
334 return ColorParams().PixelFormat();
335 }
336
Reset()337 void CanvasRenderingContext2D::Reset() {
338 // This is a multiple inheritance bootstrap
339 BaseRenderingContext2D::reset();
340 }
341
RestoreCanvasMatrixClipStack(cc::PaintCanvas * c) const342 void CanvasRenderingContext2D::RestoreCanvasMatrixClipStack(
343 cc::PaintCanvas* c) const {
344 RestoreMatrixClipStack(c);
345 }
346
ShouldAntialias() const347 bool CanvasRenderingContext2D::ShouldAntialias() const {
348 return GetState().ShouldAntialias();
349 }
350
SetShouldAntialias(bool do_aa)351 void CanvasRenderingContext2D::SetShouldAntialias(bool do_aa) {
352 ModifiableState().SetShouldAntialias(do_aa);
353 }
354
scrollPathIntoView()355 void CanvasRenderingContext2D::scrollPathIntoView() {
356 ScrollPathIntoViewInternal(path_);
357 }
358
scrollPathIntoView(Path2D * path2d)359 void CanvasRenderingContext2D::scrollPathIntoView(Path2D* path2d) {
360 ScrollPathIntoViewInternal(path2d->GetPath());
361 }
362
ScrollPathIntoViewInternal(const Path & path)363 void CanvasRenderingContext2D::ScrollPathIntoViewInternal(const Path& path) {
364 if (!GetState().IsTransformInvertible() || path.IsEmpty())
365 return;
366
367 canvas()->GetDocument().UpdateStyleAndLayout(
368 DocumentUpdateReason::kJavaScript);
369
370 LayoutObject* renderer = canvas()->GetLayoutObject();
371 LayoutBox* layout_box = canvas()->GetLayoutBox();
372 if (!renderer || !layout_box)
373 return;
374
375 if (Width() == 0 || Height() == 0)
376 return;
377
378 // Apply transformation and get the bounding rect
379 Path transformed_path = path;
380 transformed_path.Transform(GetState().GetAffineTransform());
381 FloatRect bounding_rect = transformed_path.BoundingRect();
382
383 // We first map canvas coordinates to layout coordinates.
384 PhysicalRect path_rect = PhysicalRect::EnclosingRect(bounding_rect);
385 PhysicalRect canvas_rect = layout_box->PhysicalContentBoxRect();
386 // TODO(fserb): Is this kIgnoreTransforms correct?
387 canvas_rect.Move(
388 layout_box->LocalToAbsolutePoint(PhysicalOffset(), kIgnoreTransforms));
389 path_rect.SetX(
390 (canvas_rect.X() + path_rect.X() * canvas_rect.Width() / Width()));
391 path_rect.SetY(
392 (canvas_rect.Y() + path_rect.Y() * canvas_rect.Height() / Height()));
393 path_rect.SetWidth((path_rect.Width() * canvas_rect.Width() / Width()));
394 path_rect.SetHeight((path_rect.Height() * canvas_rect.Height() / Height()));
395
396 // Then we clip the bounding box to the canvas visible range.
397 path_rect.Intersect(canvas_rect);
398
399 // Horizontal text is aligned at the top of the screen
400 mojom::blink::ScrollAlignment horizontal_scroll_mode =
401 ScrollAlignment::ToEdgeIfNeeded();
402 mojom::blink::ScrollAlignment vertical_scroll_mode =
403 ScrollAlignment::TopAlways();
404
405 // Vertical text needs be aligned horizontally on the screen
406 bool is_horizontal_writing_mode =
407 canvas()->EnsureComputedStyle()->IsHorizontalWritingMode();
408 if (!is_horizontal_writing_mode) {
409 bool is_right_to_left =
410 canvas()->EnsureComputedStyle()->IsFlippedBlocksWritingMode();
411 horizontal_scroll_mode = (is_right_to_left ? ScrollAlignment::RightAlways()
412 : ScrollAlignment::LeftAlways());
413 vertical_scroll_mode = ScrollAlignment::ToEdgeIfNeeded();
414 }
415 renderer->ScrollRectToVisible(
416 path_rect, ScrollAlignment::CreateScrollIntoViewParams(
417 horizontal_scroll_mode, vertical_scroll_mode,
418 mojom::blink::ScrollType::kProgrammatic, false,
419 mojom::blink::ScrollBehavior::kAuto));
420 }
421
clearRect(double x,double y,double width,double height)422 void CanvasRenderingContext2D::clearRect(double x,
423 double y,
424 double width,
425 double height) {
426 BaseRenderingContext2D::clearRect(x, y, width, height);
427
428 if (hit_region_manager_ && std::isfinite(x) && std::isfinite(y) &&
429 std::isfinite(width) && std::isfinite(height)) {
430 FloatRect rect(clampTo<float>(x), clampTo<float>(y), clampTo<float>(width),
431 clampTo<float>(height));
432 hit_region_manager_->RemoveHitRegionsInRect(
433 rect, GetState().GetAffineTransform());
434 }
435 }
436
DidDraw(const SkIRect & dirty_rect)437 void CanvasRenderingContext2D::DidDraw(const SkIRect& dirty_rect) {
438 if (dirty_rect.isEmpty())
439 return;
440
441 CanvasRenderingContext::DidDraw(dirty_rect);
442 }
443
StateHasFilter()444 bool CanvasRenderingContext2D::StateHasFilter() {
445 return GetState().HasFilter(canvas(), canvas()->Size(), this);
446 }
447
StateGetFilter()448 sk_sp<PaintFilter> CanvasRenderingContext2D::StateGetFilter() {
449 return GetState().GetFilter(canvas(), canvas()->Size(), this);
450 }
451
SnapshotStateForFilter()452 void CanvasRenderingContext2D::SnapshotStateForFilter() {
453 // The style resolution required for fonts is not available in frame-less
454 // documents.
455 if (!canvas()->GetDocument().GetFrame())
456 return;
457
458 ModifiableState().SetFontForFilter(AccessFont());
459 }
460
GetOrCreatePaintCanvas()461 cc::PaintCanvas* CanvasRenderingContext2D::GetOrCreatePaintCanvas() {
462 if (isContextLost())
463 return nullptr;
464 if (canvas()->GetOrCreateCanvas2DLayerBridge())
465 return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas();
466 return nullptr;
467 }
468
GetPaintCanvas() const469 cc::PaintCanvas* CanvasRenderingContext2D::GetPaintCanvas() const {
470 if (isContextLost() || !canvas()->GetCanvas2DLayerBridge())
471 return nullptr;
472 if (canvas() && canvas()->GetCanvas2DLayerBridge()->ResourceProvider())
473 return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas();
474 return nullptr;
475 }
476
font() const477 String CanvasRenderingContext2D::font() const {
478 if (!GetState().HasRealizedFont())
479 return kDefaultFont;
480
481 canvas()->GetDocument().GetCanvasFontCache()->WillUseCurrentFont();
482 StringBuilder serialized_font;
483 const FontDescription& font_description = GetState().GetFontDescription();
484
485 if (font_description.Style() == ItalicSlopeValue())
486 serialized_font.Append("italic ");
487 if (font_description.Weight() == BoldWeightValue())
488 serialized_font.Append("bold ");
489 if (font_description.VariantCaps() == FontDescription::kSmallCaps)
490 serialized_font.Append("small-caps ");
491
492 serialized_font.AppendNumber(font_description.ComputedSize());
493 serialized_font.Append("px");
494
495 const FontFamily& first_font_family = font_description.Family();
496 for (const FontFamily* font_family = &first_font_family; font_family;
497 font_family = font_family->Next()) {
498 if (font_family != &first_font_family)
499 serialized_font.Append(',');
500
501 // FIXME: We should append family directly to serializedFont rather than
502 // building a temporary string.
503 String family = font_family->Family();
504 if (family.StartsWith("-webkit-"))
505 family = family.Substring(8);
506 if (family.Contains(' '))
507 family = "\"" + family + "\"";
508
509 serialized_font.Append(' ');
510 serialized_font.Append(family);
511 }
512
513 return serialized_font.ToString();
514 }
515
setFont(const String & new_font)516 void CanvasRenderingContext2D::setFont(const String& new_font) {
517 // The style resolution required for fonts is not available in frame-less
518 // documents.
519 if (!canvas()->GetDocument().GetFrame())
520 return;
521 identifiability_study_helper_.MaybeUpdateBuilder(
522 CanvasOps::kSetFont, IdentifiabilityBenignStringToken(new_font));
523
524 base::TimeTicks start_time = base::TimeTicks::Now();
525 canvas()->GetDocument().UpdateStyleAndLayoutTreeForNode(canvas());
526
527 // The following early exit is dependent on the cache not being empty
528 // because an empty cache may indicate that a style change has occured
529 // which would require that the font be re-resolved. This check has to
530 // come after the layout tree update to flush pending style changes.
531 if (new_font == GetState().UnparsedFont() && GetState().HasRealizedFont() &&
532 fonts_resolved_using_current_style_.size() > 0)
533 return;
534
535 CanvasFontCache* canvas_font_cache =
536 canvas()->GetDocument().GetCanvasFontCache();
537
538 // Map the <canvas> font into the text style. If the font uses keywords like
539 // larger/smaller, these will work relative to the canvas.
540 scoped_refptr<ComputedStyle> font_style;
541 const ComputedStyle* computed_style = canvas()->EnsureComputedStyle();
542 if (computed_style) {
543 auto i = fonts_resolved_using_current_style_.find(new_font);
544 if (i != fonts_resolved_using_current_style_.end()) {
545 auto add_result = font_lru_list_.PrependOrMoveToFirst(new_font);
546 DCHECK(!add_result.is_new_entry);
547 ModifiableState().SetFont(i->value, Host()->GetFontSelector());
548 } else {
549 MutableCSSPropertyValueSet* parsed_style =
550 canvas_font_cache->ParseFont(new_font);
551 if (!parsed_style)
552 return;
553 font_style = ComputedStyle::Create();
554 FontDescription element_font_description(
555 computed_style->GetFontDescription());
556 // Reset the computed size to avoid inheriting the zoom factor from the
557 // <canvas> element.
558 element_font_description.SetComputedSize(
559 element_font_description.SpecifiedSize());
560 element_font_description.SetAdjustedSize(
561 element_font_description.SpecifiedSize());
562
563 font_style->SetFontDescription(element_font_description);
564 canvas()->GetDocument().GetStyleEngine().ComputeFont(
565 *canvas(), font_style.get(), *parsed_style);
566
567 // We need to reset Computed and Adjusted size so we skip zoom and
568 // minimum font size.
569 FontDescription final_description(
570 font_style->GetFont().GetFontDescription());
571 final_description.SetComputedSize(final_description.SpecifiedSize());
572 final_description.SetAdjustedSize(final_description.SpecifiedSize());
573
574 fonts_resolved_using_current_style_.insert(new_font, final_description);
575 auto add_result = font_lru_list_.PrependOrMoveToFirst(new_font);
576 DCHECK(add_result.is_new_entry);
577 PruneLocalFontCache(canvas_font_cache->HardMaxFonts()); // hard limit
578 should_prune_local_font_cache_ = true; // apply soft limit
579 ModifiableState().SetFont(final_description, Host()->GetFontSelector());
580 }
581 } else {
582 Font resolved_font;
583 if (!canvas_font_cache->GetFontUsingDefaultStyle(*canvas(), new_font,
584 resolved_font))
585 return;
586
587 // We need to reset Computed and Adjusted size so we skip zoom and
588 // minimum font size for detached canvas.
589 FontDescription final_description(resolved_font.GetFontDescription());
590 final_description.SetComputedSize(final_description.SpecifiedSize());
591 final_description.SetAdjustedSize(final_description.SpecifiedSize());
592 ModifiableState().SetFont(final_description, Host()->GetFontSelector());
593 }
594
595 // The parse succeeded.
596 String new_font_safe_copy(new_font); // Create a string copy since newFont
597 // can be deleted inside realizeSaves.
598 ModifiableState().SetUnparsedFont(new_font_safe_copy);
599 if (bernoulli_distribution_(random_generator_)) {
600 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time;
601 base::UmaHistogramMicrosecondsTimesUnderTenMilliseconds(
602 "Canvas.TextMetrics.SetFont", elapsed);
603 }
604 }
605
DidProcessTask(const base::PendingTask & pending_task)606 void CanvasRenderingContext2D::DidProcessTask(
607 const base::PendingTask& pending_task) {
608 CanvasRenderingContext::DidProcessTask(pending_task);
609 // This should be the only place where canvas() needs to be checked for
610 // nullness because the circular refence with HTMLCanvasElement means the
611 // canvas and the context keep each other alive. As long as the pair is
612 // referenced, the task observer is the only persistent refernce to this
613 // object
614 // that is not traced, so didProcessTask() may be called at a time when the
615 // canvas has been garbage collected but not the context.
616 if (should_prune_local_font_cache_ && canvas()) {
617 should_prune_local_font_cache_ = false;
618 PruneLocalFontCache(
619 canvas()->GetDocument().GetCanvasFontCache()->MaxFonts());
620 }
621 }
622
PruneLocalFontCache(size_t target_size)623 void CanvasRenderingContext2D::PruneLocalFontCache(size_t target_size) {
624 if (target_size == 0) {
625 // Short cut: LRU does not matter when evicting everything
626 font_lru_list_.clear();
627 fonts_resolved_using_current_style_.clear();
628 return;
629 }
630 while (font_lru_list_.size() > target_size) {
631 fonts_resolved_using_current_style_.erase(font_lru_list_.back());
632 font_lru_list_.pop_back();
633 }
634 }
635
StyleDidChange(const ComputedStyle * old_style,const ComputedStyle & new_style)636 void CanvasRenderingContext2D::StyleDidChange(const ComputedStyle* old_style,
637 const ComputedStyle& new_style) {
638 // Only the visibility flag is considered here. display:none is
639 // handled via creation and destruction of the LayoutObject.
640 SetIsBeingDisplayed(new_style.Visibility() == EVisibility::kVisible);
641 if (old_style && old_style->GetFont() == new_style.GetFont())
642 return;
643 PruneLocalFontCache(0);
644 }
645
ClearFilterReferences()646 void CanvasRenderingContext2D::ClearFilterReferences() {
647 filter_operations_.RemoveClient(*this);
648 filter_operations_.clear();
649 }
650
UpdateFilterReferences(const FilterOperations & filters)651 void CanvasRenderingContext2D::UpdateFilterReferences(
652 const FilterOperations& filters) {
653 filters.AddClient(*this);
654 ClearFilterReferences();
655 filter_operations_ = filters;
656 }
657
ResourceContentChanged(InvalidationModeMask)658 void CanvasRenderingContext2D::ResourceContentChanged(InvalidationModeMask) {
659 ResourceElementChanged();
660 }
661
ResourceElementChanged()662 void CanvasRenderingContext2D::ResourceElementChanged() {
663 ClearFilterReferences();
664 GetState().ClearResolvedFilter();
665 }
666
OriginClean() const667 bool CanvasRenderingContext2D::OriginClean() const {
668 return Host()->OriginClean();
669 }
670
SetOriginTainted()671 void CanvasRenderingContext2D::SetOriginTainted() {
672 Host()->SetOriginTainted();
673 }
674
Width() const675 int CanvasRenderingContext2D::Width() const {
676 return Host()->Size().Width();
677 }
678
Height() const679 int CanvasRenderingContext2D::Height() const {
680 return Host()->Size().Height();
681 }
682
CanCreateCanvas2dResourceProvider() const683 bool CanvasRenderingContext2D::CanCreateCanvas2dResourceProvider() const {
684 return canvas()->GetOrCreateCanvas2DLayerBridge();
685 }
686
GetImage()687 scoped_refptr<StaticBitmapImage> blink::CanvasRenderingContext2D::GetImage() {
688 if (!IsPaintable())
689 return nullptr;
690 return canvas()->GetCanvas2DLayerBridge()->NewImageSnapshot();
691 }
692
getImageData(int sx,int sy,int sw,int sh,ExceptionState & exception_state)693 ImageData* CanvasRenderingContext2D::getImageData(
694 int sx,
695 int sy,
696 int sw,
697 int sh,
698 ExceptionState& exception_state) {
699 const IdentifiableSurface surface = IdentifiableSurface::FromTypeAndToken(
700 IdentifiableSurface::Type::kCanvasReadback, GetContextType());
701 if (IdentifiabilityStudySettings::Get()->ShouldSample(surface)) {
702 blink::IdentifiabilityMetricBuilder(ukm_source_id_)
703 .Set(surface, 0)
704 .Record(ukm_recorder_);
705 }
706 return BaseRenderingContext2D::getImageData(sx, sy, sw, sh, exception_state);
707 }
708
FinalizeFrame()709 void CanvasRenderingContext2D::FinalizeFrame() {
710 TRACE_EVENT0("blink", "CanvasRenderingContext2D::FinalizeFrame");
711 if (IsPaintable())
712 canvas()->GetCanvas2DLayerBridge()->FinalizeFrame();
713 }
714
ParseColorOrCurrentColor(Color & color,const String & color_string) const715 bool CanvasRenderingContext2D::ParseColorOrCurrentColor(
716 Color& color,
717 const String& color_string) const {
718 return ::blink::ParseColorOrCurrentColor(color, color_string, canvas());
719 }
720
GetControlAndIdIfHitRegionExists(const PhysicalOffset & location)721 HitTestCanvasResult* CanvasRenderingContext2D::GetControlAndIdIfHitRegionExists(
722 const PhysicalOffset& location) {
723 if (HitRegionsCount() <= 0)
724 return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
725
726 LayoutBox* box = canvas()->GetLayoutBox();
727 FloatPoint local_pos(box->AbsoluteToLocalPoint(location));
728 if (box->StyleRef().HasBorder() || box->StyleRef().MayHavePadding())
729 local_pos.Move(FloatSize(-box->PhysicalContentBoxOffset()));
730 float scaleWidth = box->ContentWidth().ToFloat() == 0.0f
731 ? 1.0f
732 : canvas()->width() / box->ContentWidth();
733 float scaleHeight = box->ContentHeight().ToFloat() == 0.0f
734 ? 1.0f
735 : canvas()->height() / box->ContentHeight();
736 local_pos.Scale(scaleWidth, scaleHeight);
737
738 HitRegion* hit_region = HitRegionAtPoint(local_pos);
739 if (hit_region) {
740 Element* control = hit_region->Control();
741 if (control && canvas()->IsSupportedInteractiveCanvasFallback(*control)) {
742 return MakeGarbageCollected<HitTestCanvasResult>(hit_region->Id(),
743 hit_region->Control());
744 }
745 return MakeGarbageCollected<HitTestCanvasResult>(hit_region->Id(), nullptr);
746 }
747 return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
748 }
749
GetIdFromControl(const Element * element)750 String CanvasRenderingContext2D::GetIdFromControl(const Element* element) {
751 if (HitRegionsCount() <= 0)
752 return String();
753
754 if (HitRegion* hit_region =
755 hit_region_manager_->GetHitRegionByControl(element))
756 return hit_region->Id();
757 return String();
758 }
759
ToTextDirection(CanvasRenderingContext2DState::Direction direction,HTMLCanvasElement * canvas,const ComputedStyle ** computed_style=nullptr)760 static inline TextDirection ToTextDirection(
761 CanvasRenderingContext2DState::Direction direction,
762 HTMLCanvasElement* canvas,
763 const ComputedStyle** computed_style = nullptr) {
764 const ComputedStyle* style =
765 (computed_style ||
766 direction == CanvasRenderingContext2DState::kDirectionInherit)
767 ? canvas->EnsureComputedStyle()
768 : nullptr;
769 if (computed_style)
770 *computed_style = style;
771 switch (direction) {
772 case CanvasRenderingContext2DState::kDirectionInherit:
773 return style ? style->Direction() : TextDirection::kLtr;
774 case CanvasRenderingContext2DState::kDirectionRTL:
775 return TextDirection::kRtl;
776 case CanvasRenderingContext2DState::kDirectionLTR:
777 return TextDirection::kLtr;
778 }
779 NOTREACHED();
780 return TextDirection::kLtr;
781 }
782
direction() const783 String CanvasRenderingContext2D::direction() const {
784 if (GetState().GetDirection() ==
785 CanvasRenderingContext2DState::kDirectionInherit)
786 canvas()->GetDocument().UpdateStyleAndLayoutTreeForNode(canvas());
787 return ToTextDirection(GetState().GetDirection(), canvas()) ==
788 TextDirection::kRtl
789 ? kRtlDirectionString
790 : kLtrDirectionString;
791 }
792
setDirection(const String & direction_string)793 void CanvasRenderingContext2D::setDirection(const String& direction_string) {
794 CanvasRenderingContext2DState::Direction direction;
795 if (direction_string == kInheritDirectionString)
796 direction = CanvasRenderingContext2DState::kDirectionInherit;
797 else if (direction_string == kRtlDirectionString)
798 direction = CanvasRenderingContext2DState::kDirectionRTL;
799 else if (direction_string == kLtrDirectionString)
800 direction = CanvasRenderingContext2DState::kDirectionLTR;
801 else
802 return;
803
804 if (GetState().GetDirection() == direction)
805 return;
806
807 ModifiableState().SetDirection(direction);
808 }
809
setTextLetterSpacing(const double letter_spacing)810 void CanvasRenderingContext2D::setTextLetterSpacing(
811 const double letter_spacing) {
812 if (!GetState().HasRealizedFont())
813 setFont(font());
814
815 float letter_spacing_float = clampTo<float>(letter_spacing);
816 ModifiableState().SetTextLetterSpacing(letter_spacing_float,
817 Host()->GetFontSelector());
818 }
819
setTextWordSpacing(const double word_spacing)820 void CanvasRenderingContext2D::setTextWordSpacing(const double word_spacing) {
821 if (!GetState().HasRealizedFont())
822 setFont(font());
823
824 float word_spacing_float = clampTo<float>(word_spacing);
825 ModifiableState().SetTextWordSpacing(word_spacing_float,
826 Host()->GetFontSelector());
827 }
828
setTextRendering(const String & text_rendering_string)829 void CanvasRenderingContext2D::setTextRendering(
830 const String& text_rendering_string) {
831 if (!GetState().HasRealizedFont())
832 setFont(font());
833
834 TextRenderingMode text_rendering_mode;
835 String text_rendering = text_rendering_string.LowerASCII();
836
837 if (text_rendering == kAutoRendering)
838 text_rendering_mode = TextRenderingMode::kAutoTextRendering;
839 else if (text_rendering == kOptimizeSpeedRendering)
840 text_rendering_mode = TextRenderingMode::kOptimizeSpeed;
841 else if (text_rendering == kOptimizeLegibilityRendering)
842 text_rendering_mode = TextRenderingMode::kOptimizeLegibility;
843 else if (text_rendering == kGeometricPrecisionRendering)
844 text_rendering_mode = TextRenderingMode::kGeometricPrecision;
845 else
846 return;
847
848 if (GetState().GetTextRendering() == text_rendering_mode)
849 return;
850
851 ModifiableState().SetTextRendering(text_rendering_mode,
852 Host()->GetFontSelector());
853 }
854
setFontKerning(const String & font_kerning_string)855 void CanvasRenderingContext2D::setFontKerning(
856 const String& font_kerning_string) {
857 if (!GetState().HasRealizedFont())
858 setFont(font());
859 FontDescription::Kerning kerning;
860 String font_kerning = font_kerning_string.LowerASCII();
861 if (font_kerning == kAutoKerningString)
862 kerning = FontDescription::kAutoKerning;
863 else if (font_kerning == kNoneKerningString)
864 kerning = FontDescription::kNoneKerning;
865 else if (font_kerning == kNormalKerningString)
866 kerning = FontDescription::kNormalKerning;
867 else
868 return;
869
870 if (GetState().GetFontKerning() == kerning)
871 return;
872
873 ModifiableState().SetFontKerning(kerning, Host()->GetFontSelector());
874 }
875
setFontVariantCaps(const String & font_variant_caps_string)876 void CanvasRenderingContext2D::setFontVariantCaps(
877 const String& font_variant_caps_string) {
878 if (!GetState().HasRealizedFont())
879 setFont(font());
880 FontDescription::FontVariantCaps variant_caps;
881 String variant_caps_lower = font_variant_caps_string.LowerASCII();
882 if (variant_caps_lower == kNormalVariantString)
883 variant_caps = FontDescription::kCapsNormal;
884 else if (variant_caps_lower == kSmallCapsVariantString)
885 variant_caps = FontDescription::kSmallCaps;
886 else if (variant_caps_lower == kAllSmallCapsVariantString)
887 variant_caps = FontDescription::kAllSmallCaps;
888 else if (variant_caps_lower == kPetiteVariantString)
889 variant_caps = FontDescription::kPetiteCaps;
890 else if (variant_caps_lower == kAllPetiteVariantString)
891 variant_caps = FontDescription::kAllPetiteCaps;
892 else if (variant_caps_lower == kUnicaseVariantString)
893 variant_caps = FontDescription::kUnicase;
894 else if (variant_caps_lower == kTitlingCapsVariantString)
895 variant_caps = FontDescription::kTitlingCaps;
896 else
897 return;
898
899 if (GetState().GetFontVariantCaps() == variant_caps)
900 return;
901
902 ModifiableState().SetFontVariantCaps(variant_caps, Host()->GetFontSelector());
903 }
904
fillText(const String & text,double x,double y)905 void CanvasRenderingContext2D::fillText(const String& text,
906 double x,
907 double y) {
908 DrawTextInternal(text, x, y, CanvasRenderingContext2DState::kFillPaintType);
909 }
910
fillText(const String & text,double x,double y,double max_width)911 void CanvasRenderingContext2D::fillText(const String& text,
912 double x,
913 double y,
914 double max_width) {
915 DrawTextInternal(text, x, y, CanvasRenderingContext2DState::kFillPaintType,
916 &max_width);
917 }
918
strokeText(const String & text,double x,double y)919 void CanvasRenderingContext2D::strokeText(const String& text,
920 double x,
921 double y) {
922 DrawTextInternal(text, x, y, CanvasRenderingContext2DState::kStrokePaintType);
923 }
924
strokeText(const String & text,double x,double y,double max_width)925 void CanvasRenderingContext2D::strokeText(const String& text,
926 double x,
927 double y,
928 double max_width) {
929 DrawTextInternal(text, x, y, CanvasRenderingContext2DState::kStrokePaintType,
930 &max_width);
931 }
932
measureText(const String & text)933 TextMetrics* CanvasRenderingContext2D::measureText(const String& text) {
934 // The style resolution required for fonts is not available in frame-less
935 // documents.
936 if (!canvas()->GetDocument().GetFrame())
937 return MakeGarbageCollected<TextMetrics>();
938
939 canvas()->GetDocument().UpdateStyleAndLayoutTreeForNode(canvas());
940
941 const Font& font = AccessFont();
942
943 TextDirection direction;
944 if (GetState().GetDirection() ==
945 CanvasRenderingContext2DState::kDirectionInherit)
946 direction = DetermineDirectionality(text);
947 else
948 direction = ToTextDirection(GetState().GetDirection(), canvas());
949
950 return MakeGarbageCollected<TextMetrics>(font, direction,
951 GetState().GetTextBaseline(),
952 GetState().GetTextAlign(), text);
953 }
954
DrawTextInternal(const String & text,double x,double y,CanvasRenderingContext2DState::PaintType paint_type,double * max_width)955 void CanvasRenderingContext2D::DrawTextInternal(
956 const String& text,
957 double x,
958 double y,
959 CanvasRenderingContext2DState::PaintType paint_type,
960 double* max_width) {
961 // The style resolution required for fonts is not available in frame-less
962 // documents.
963 if (!canvas()->GetDocument().GetFrame())
964 return;
965
966 // accessFont needs the style to be up to date, but updating style can cause
967 // script to run, (e.g. due to autofocus) which can free the canvas (set size
968 // to 0, for example), so update style before grabbing the PaintCanvas.
969 canvas()->GetDocument().UpdateStyleAndLayoutTreeForNode(canvas());
970
971 cc::PaintCanvas* c = GetOrCreatePaintCanvas();
972 if (!c)
973 return;
974
975 if (!std::isfinite(x) || !std::isfinite(y))
976 return;
977 if (max_width && (!std::isfinite(*max_width) || *max_width <= 0))
978 return;
979
980 identifiability_study_helper_.MaybeUpdateBuilder(
981 paint_type == CanvasRenderingContext2DState::kFillPaintType
982 ? CanvasOps::kFillText
983 : CanvasOps::kStrokeText,
984 IdentifiabilitySensitiveStringToken(text), x, y,
985 max_width ? *max_width : -1);
986 identifiability_study_helper_.set_encountered_sensitive_ops();
987
988 const Font& font = AccessFont();
989 const SimpleFontData* font_data = font.PrimaryFont();
990 DCHECK(font_data);
991 if (!font_data)
992 return;
993 const FontMetrics& font_metrics = font_data->GetFontMetrics();
994
995 // FIXME: Need to turn off font smoothing.
996
997 const ComputedStyle* computed_style = nullptr;
998 TextDirection direction =
999 ToTextDirection(GetState().GetDirection(), canvas(), &computed_style);
1000 bool is_rtl = direction == TextDirection::kRtl;
1001 bool override =
1002 computed_style ? IsOverride(computed_style->GetUnicodeBidi()) : false;
1003
1004 TextRun text_run(text, 0, 0, TextRun::kAllowTrailingExpansion, direction,
1005 override);
1006 text_run.SetNormalizeSpace(true);
1007 // Draw the item text at the correct point.
1008 FloatPoint location(clampTo<float>(x),
1009 clampTo<float>(y + GetFontBaseline(*font_data)));
1010 double font_width = font.Width(text_run);
1011
1012 bool use_max_width = (max_width && *max_width < font_width);
1013 double width = use_max_width ? *max_width : font_width;
1014
1015 TextAlign align = GetState().GetTextAlign();
1016 if (align == kStartTextAlign)
1017 align = is_rtl ? kRightTextAlign : kLeftTextAlign;
1018 else if (align == kEndTextAlign)
1019 align = is_rtl ? kLeftTextAlign : kRightTextAlign;
1020
1021 switch (align) {
1022 case kCenterTextAlign:
1023 location.SetX(location.X() - width / 2);
1024 break;
1025 case kRightTextAlign:
1026 location.SetX(location.X() - width);
1027 break;
1028 default:
1029 break;
1030 }
1031
1032 TextRunPaintInfo text_run_paint_info(text_run);
1033 FloatRect bounds(
1034 location.X() - font_metrics.Height() / 2,
1035 location.Y() - font_metrics.Ascent() - font_metrics.LineGap(),
1036 clampTo<float>(width + font_metrics.Height()),
1037 font_metrics.LineSpacing());
1038 if (paint_type == CanvasRenderingContext2DState::kStrokePaintType)
1039 InflateStrokeRect(bounds);
1040
1041 CanvasRenderingContext2DAutoRestoreSkCanvas state_restorer(this);
1042 if (use_max_width) {
1043 c->save();
1044 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op)
1045 // still work. As the width of canvas is scaled, so text can be scaled to
1046 // match the given maxwidth, update text location so it appears on desired
1047 // place.
1048 c->scale(clampTo<float>(width / font_width), 1);
1049 location.SetX(location.X() / clampTo<float>(width / font_width));
1050 }
1051
1052 Draw(
1053 [&font, &text_run_paint_info, &location](
1054 cc::PaintCanvas* c, const PaintFlags* flags) // draw lambda
1055 {
1056 font.DrawBidiText(c, text_run_paint_info, location,
1057 Font::kUseFallbackIfFontNotReady, kCDeviceScaleFactor,
1058 *flags);
1059 },
1060 [](const SkIRect& rect) // overdraw test lambda
1061 { return false; },
1062 bounds, paint_type, CanvasRenderingContext2DState::kNoImage);
1063 }
1064
AccessFont()1065 const Font& CanvasRenderingContext2D::AccessFont() {
1066 if (!GetState().HasRealizedFont())
1067 setFont(GetState().UnparsedFont());
1068 canvas()->GetDocument().GetCanvasFontCache()->WillUseCurrentFont();
1069 return GetState().GetFont();
1070 }
1071
SetIsInHiddenPage(bool hidden)1072 void CanvasRenderingContext2D::SetIsInHiddenPage(bool hidden) {
1073 if (IsPaintable())
1074 canvas()->GetCanvas2DLayerBridge()->SetIsInHiddenPage(hidden);
1075 if (hidden)
1076 PruneLocalFontCache(0);
1077 }
1078
SetIsBeingDisplayed(bool displayed)1079 void CanvasRenderingContext2D::SetIsBeingDisplayed(bool displayed) {
1080 if (IsPaintable())
1081 canvas()->GetCanvas2DLayerBridge()->SetIsBeingDisplayed(displayed);
1082 }
1083
IsTransformInvertible() const1084 bool CanvasRenderingContext2D::IsTransformInvertible() const {
1085 return GetState().IsTransformInvertible();
1086 }
1087
GetTransform() const1088 TransformationMatrix CanvasRenderingContext2D::GetTransform() const {
1089 return GetState().GetTransform();
1090 }
1091
CcLayer() const1092 cc::Layer* CanvasRenderingContext2D::CcLayer() const {
1093 return IsPaintable() ? canvas()->GetCanvas2DLayerBridge()->Layer() : nullptr;
1094 }
1095
1096 CanvasRenderingContext2DSettings*
getContextAttributes() const1097 CanvasRenderingContext2D::getContextAttributes() const {
1098 CanvasRenderingContext2DSettings* settings =
1099 CanvasRenderingContext2DSettings::Create();
1100 settings->setAlpha(CreationAttributes().alpha);
1101 if (RuntimeEnabledFeatures::CanvasColorManagementEnabled()) {
1102 settings->setColorSpace(ColorSpaceAsString());
1103 settings->setPixelFormat(PixelFormatAsString());
1104 }
1105 settings->setDesynchronized(Host()->LowLatencyEnabled());
1106 if (RuntimeEnabledFeatures::NewCanvas2DAPIEnabled())
1107 settings->setWillReadFrequently(CreationAttributes().will_read_frequently);
1108 return settings;
1109 }
1110
drawFocusIfNeeded(Element * element)1111 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element) {
1112 DrawFocusIfNeededInternal(path_, element);
1113 }
1114
drawFocusIfNeeded(Path2D * path2d,Element * element)1115 void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d,
1116 Element* element) {
1117 DrawFocusIfNeededInternal(path2d->GetPath(), element);
1118 }
1119
DrawFocusIfNeededInternal(const Path & path,Element * element)1120 void CanvasRenderingContext2D::DrawFocusIfNeededInternal(const Path& path,
1121 Element* element) {
1122 if (!FocusRingCallIsValid(path, element))
1123 return;
1124
1125 // Note: we need to check document->focusedElement() rather than just calling
1126 // element->focused(), because element->focused() isn't updated until after
1127 // focus events fire.
1128 if (element->GetDocument().FocusedElement() == element) {
1129 ScrollPathIntoViewInternal(path);
1130 DrawFocusRing(path);
1131 }
1132
1133 // Update its accessible bounds whether it's focused or not.
1134 UpdateElementAccessibility(path, element);
1135 }
1136
FocusRingCallIsValid(const Path & path,Element * element)1137 bool CanvasRenderingContext2D::FocusRingCallIsValid(const Path& path,
1138 Element* element) {
1139 DCHECK(element);
1140 if (!GetState().IsTransformInvertible())
1141 return false;
1142 if (path.IsEmpty())
1143 return false;
1144 if (!element->IsDescendantOf(canvas()))
1145 return false;
1146
1147 return true;
1148 }
1149
DrawFocusRing(const Path & path)1150 void CanvasRenderingContext2D::DrawFocusRing(const Path& path) {
1151 if (!GetOrCreatePaintCanvas())
1152 return;
1153
1154 SkColor color = LayoutTheme::GetTheme().FocusRingColor().Rgb();
1155 const int kFocusRingWidth = 5;
1156 DrawPlatformFocusRing(path.GetSkPath(), GetPaintCanvas(), color,
1157 /*width=*/kFocusRingWidth, /*radius=*/kFocusRingWidth);
1158
1159 // We need to add focusRingWidth to dirtyRect.
1160 StrokeData stroke_data;
1161 stroke_data.SetThickness(kFocusRingWidth);
1162
1163 SkIRect dirty_rect;
1164 if (!ComputeDirtyRect(path.StrokeBoundingRect(stroke_data), &dirty_rect))
1165 return;
1166
1167 DidDraw(dirty_rect);
1168 }
1169
UpdateElementAccessibility(const Path & path,Element * element)1170 void CanvasRenderingContext2D::UpdateElementAccessibility(const Path& path,
1171 Element* element) {
1172 element->GetDocument().UpdateStyleAndLayout(
1173 DocumentUpdateReason::kAccessibility);
1174 AXObjectCache* ax_object_cache =
1175 element->GetDocument().ExistingAXObjectCache();
1176 LayoutBoxModelObject* lbmo = canvas()->GetLayoutBoxModelObject();
1177 LayoutObject* renderer = canvas()->GetLayoutObject();
1178 if (!ax_object_cache || !lbmo || !renderer)
1179 return;
1180
1181 // Get the transformed path.
1182 Path transformed_path = path;
1183 transformed_path.Transform(GetState().GetAffineTransform());
1184
1185 // Add border and padding to the bounding rect.
1186 LayoutRect element_rect =
1187 EnclosingLayoutRect(transformed_path.BoundingRect());
1188 element_rect.Move(lbmo->BorderLeft() + lbmo->PaddingLeft(),
1189 lbmo->BorderTop() + lbmo->PaddingTop());
1190
1191 // Update the accessible object.
1192 ax_object_cache->SetCanvasObjectBounds(canvas(), element, element_rect);
1193 }
1194
addHitRegion(const HitRegionOptions * options,ExceptionState & exception_state)1195 void CanvasRenderingContext2D::addHitRegion(const HitRegionOptions* options,
1196 ExceptionState& exception_state) {
1197 if (options->id().IsEmpty() && !options->control()) {
1198 exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
1199 "Both id and control are null.");
1200 return;
1201 }
1202
1203 if (options->control() &&
1204 !canvas()->IsSupportedInteractiveCanvasFallback(*options->control())) {
1205 exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
1206 "The control is neither null nor a "
1207 "supported interactive canvas fallback "
1208 "element.");
1209 return;
1210 }
1211
1212 Path hit_region_path = options->path() ? options->path()->GetPath() : path_;
1213
1214 cc::PaintCanvas* c = GetOrCreatePaintCanvas();
1215
1216 if (hit_region_path.IsEmpty() || !c || !GetState().IsTransformInvertible() ||
1217 c->isClipEmpty()) {
1218 exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
1219 "The specified path has no pixels.");
1220 return;
1221 }
1222
1223 hit_region_path.Transform(GetState().GetAffineTransform());
1224
1225 if (GetState().HasClip()) {
1226 hit_region_path.IntersectPath(GetState().GetCurrentClipPath());
1227 if (hit_region_path.IsEmpty()) {
1228 exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
1229 "The specified path has no pixels.");
1230 }
1231 }
1232
1233 if (!hit_region_manager_)
1234 hit_region_manager_ = MakeGarbageCollected<HitRegionManager>();
1235
1236 // Remove previous region (with id or control)
1237 hit_region_manager_->RemoveHitRegionById(options->id());
1238 hit_region_manager_->RemoveHitRegionByControl(options->control());
1239
1240 auto* hit_region = MakeGarbageCollected<HitRegion>(hit_region_path, options);
1241 Element* element = hit_region->Control();
1242 if (element && element->IsDescendantOf(canvas()))
1243 UpdateElementAccessibility(hit_region->GetPath(), hit_region->Control());
1244 hit_region_manager_->AddHitRegion(hit_region);
1245 }
1246
removeHitRegion(const String & id)1247 void CanvasRenderingContext2D::removeHitRegion(const String& id) {
1248 if (hit_region_manager_)
1249 hit_region_manager_->RemoveHitRegionById(id);
1250 }
1251
clearHitRegions()1252 void CanvasRenderingContext2D::clearHitRegions() {
1253 if (hit_region_manager_)
1254 hit_region_manager_->RemoveAllHitRegions();
1255 }
1256
HitRegionAtPoint(const FloatPoint & point)1257 HitRegion* CanvasRenderingContext2D::HitRegionAtPoint(const FloatPoint& point) {
1258 if (hit_region_manager_)
1259 return hit_region_manager_->GetHitRegionAtPoint(point);
1260
1261 return nullptr;
1262 }
1263
HitRegionsCount() const1264 unsigned CanvasRenderingContext2D::HitRegionsCount() const {
1265 if (hit_region_manager_)
1266 return hit_region_manager_->GetHitRegionsCount();
1267
1268 return 0;
1269 }
1270
1271 // TODO(aaronhk) This is only used for the size heuristic. Delete this function
1272 // once always accelerate fully lands.
DisableAcceleration()1273 void CanvasRenderingContext2D::DisableAcceleration() {
1274 canvas()->DisableAcceleration();
1275 }
1276
IsCanvas2DBufferValid() const1277 bool CanvasRenderingContext2D::IsCanvas2DBufferValid() const {
1278 if (IsPaintable()) {
1279 return canvas()->GetCanvas2DLayerBridge()->IsValid();
1280 }
1281 return false;
1282 }
1283
RespectImageOrientation() const1284 RespectImageOrientationEnum CanvasRenderingContext2D::RespectImageOrientation()
1285 const {
1286 if (canvas()->RespectImageOrientation() != kRespectImageOrientation) {
1287 return kDoNotRespectImageOrientation;
1288 }
1289 return kRespectImageOrientation;
1290 }
1291
1292 } // namespace blink
1293