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