1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
29 
30 #include <math.h>
31 
32 #include <limits>
33 #include <memory>
34 #include <utility>
35 
36 #include "base/callback_helpers.h"
37 #include "base/location.h"
38 #include "base/metrics/histogram_macros.h"
39 #include "base/numerics/checked_math.h"
40 #include "build/build_config.h"
41 #include "services/metrics/public/cpp/ukm_recorder.h"
42 #include "services/metrics/public/cpp/ukm_source_id.h"
43 #include "third_party/blink/public/common/features.h"
44 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
45 #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
46 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
47 #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
48 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
49 #include "third_party/blink/public/mojom/gpu/gpu.mojom-blink.h"
50 #include "third_party/blink/public/platform/platform.h"
51 #include "third_party/blink/public/platform/task_type.h"
52 #include "third_party/blink/public/resources/grit/blink_image_resources.h"
53 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
54 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
55 #include "third_party/blink/renderer/bindings/core/v8/v8_image_encode_options.h"
56 #include "third_party/blink/renderer/core/css/css_font_selector.h"
57 #include "third_party/blink/renderer/core/css/style_engine.h"
58 #include "third_party/blink/renderer/core/dom/document.h"
59 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
60 #include "third_party/blink/renderer/core/dom/element.h"
61 #include "third_party/blink/renderer/core/dom/element_traversal.h"
62 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
63 #include "third_party/blink/renderer/core/fileapi/file.h"
64 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
65 #include "third_party/blink/renderer/core/frame/local_frame.h"
66 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
67 #include "third_party/blink/renderer/core/frame/settings.h"
68 #include "third_party/blink/renderer/core/frame/web_feature.h"
69 #include "third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h"
70 #include "third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h"
71 #include "third_party/blink/renderer/core/html/canvas/canvas_draw_listener.h"
72 #include "third_party/blink/renderer/core/html/canvas/canvas_font_cache.h"
73 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
74 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
75 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
76 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
77 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
78 #include "third_party/blink/renderer/core/html/html_image_element.h"
79 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
80 #include "third_party/blink/renderer/core/html_names.h"
81 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
82 #include "third_party/blink/renderer/core/input_type_names.h"
83 #include "third_party/blink/renderer/core/layout/hit_test_canvas_result.h"
84 #include "third_party/blink/renderer/core/layout/layout_html_canvas.h"
85 #include "third_party/blink/renderer/core/layout/layout_view.h"
86 #include "third_party/blink/renderer/core/page/chrome_client.h"
87 #include "third_party/blink/renderer/core/page/page.h"
88 #include "third_party/blink/renderer/core/paint/paint_layer.h"
89 #include "third_party/blink/renderer/core/probe/core_probes.h"
90 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
91 #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h"
92 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
93 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
94 #include "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
95 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
96 #include "third_party/blink/renderer/platform/image-encoders/image_encoder_utils.h"
97 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
98 #include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
99 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
100 #include "ui/base/resource/scale_factor.h"
101 #include "v8/include/v8.h"
102 
103 namespace blink {
104 
105 namespace {
106 
107 // These values come from the WhatWG spec.
108 constexpr int kDefaultCanvasWidth = 300;
109 constexpr int kDefaultCanvasHeight = 150;
110 
111 // A default value of quality argument for toDataURL and toBlob
112 // It is in an invalid range (outside 0.0 - 1.0) so that it will not be
113 // misinterpreted as a user-input value
114 constexpr int kUndefinedQualityValue = -1.0;
115 constexpr int kMinimumAccelerated2dCanvasSize = 128 * 129;
116 
117 }  // namespace
118 
HTMLCanvasElement(Document & document)119 HTMLCanvasElement::HTMLCanvasElement(Document& document)
120     : HTMLElement(html_names::kCanvasTag, document),
121       ExecutionContextLifecycleObserver(GetExecutionContext()),
122       PageVisibilityObserver(document.GetPage()),
123       CanvasRenderingContextHost(
124           CanvasRenderingContextHost::HostType::kCanvasHost),
125       size_(kDefaultCanvasWidth, kDefaultCanvasHeight),
126       context_creation_was_blocked_(false),
127       ignore_reset_(false),
128       origin_clean_(true),
129       surface_layer_bridge_(nullptr),
130       externally_allocated_memory_(0) {
131   UseCounter::Count(document, WebFeature::kHTMLCanvasElement);
132   GetDocument().IncrementNumberOfCanvases();
133 }
134 
~HTMLCanvasElement()135 HTMLCanvasElement::~HTMLCanvasElement() {
136   v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
137       -externally_allocated_memory_);
138 }
139 
Dispose()140 void HTMLCanvasElement::Dispose() {
141   if (OffscreenCanvasFrame()) {
142     ReleaseOffscreenCanvasFrame();
143   }
144   // It's possible that the placeholder frame has been disposed but its ID still
145   // exists. Make sure that it gets unregistered here
146   UnregisterPlaceholderCanvas();
147 
148   // We need to drop frame dispatcher, to prevent mojo calls from completing.
149   frame_dispatcher_ = nullptr;
150   DiscardResourceProvider();
151 
152   if (context_) {
153     UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.HasRendered", bool(ResourceProvider()));
154     if (context_->Host()) {
155       UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.IsComposited",
156                             context_->IsComposited());
157       context_->DetachHost();
158     }
159     context_ = nullptr;
160   }
161 
162   if (canvas2d_bridge_) {
163     canvas2d_bridge_->SetCanvasResourceHost(nullptr);
164     canvas2d_bridge_ = nullptr;
165   }
166 
167   if (surface_layer_bridge_) {
168     // Observer has to be cleared out at this point. Otherwise the
169     // SurfaceLayerBridge may call back into the observer which is undefined
170     // behavior. In the worst case, the dead canvas element re-adds itself into
171     // a data structure which may crash at a later point in time. See
172     // https://crbug.com/976577.
173     surface_layer_bridge_->ClearObserver();
174   }
175 }
176 
ParseAttribute(const AttributeModificationParams & params)177 void HTMLCanvasElement::ParseAttribute(
178     const AttributeModificationParams& params) {
179   if (params.name == html_names::kWidthAttr ||
180       params.name == html_names::kHeightAttr)
181     Reset();
182   HTMLElement::ParseAttribute(params);
183 }
184 
CreateLayoutObject(const ComputedStyle & style,LegacyLayout legacy)185 LayoutObject* HTMLCanvasElement::CreateLayoutObject(const ComputedStyle& style,
186                                                     LegacyLayout legacy) {
187   if (GetExecutionContext() &&
188       GetExecutionContext()->CanExecuteScripts(kNotAboutToExecuteScript)) {
189     // Allocation of a layout object indicates that the canvas doesn't
190     // have display:none set, so is conceptually being displayed.
191     if (context_) {
192       context_->SetIsBeingDisplayed(style.Visibility() ==
193                                     EVisibility::kVisible);
194     }
195     return new LayoutHTMLCanvas(this);
196   }
197   return HTMLElement::CreateLayoutObject(style, legacy);
198 }
199 
InsertedInto(ContainerNode & node)200 Node::InsertionNotificationRequest HTMLCanvasElement::InsertedInto(
201     ContainerNode& node) {
202   SetIsInCanvasSubtree(true);
203   return HTMLElement::InsertedInto(node);
204 }
205 
setHeight(unsigned value,ExceptionState & exception_state)206 void HTMLCanvasElement::setHeight(unsigned value,
207                                   ExceptionState& exception_state) {
208   if (IsOffscreenCanvasRegistered()) {
209     exception_state.ThrowDOMException(
210         DOMExceptionCode::kInvalidStateError,
211         "Cannot resize canvas after call to transferControlToOffscreen().");
212     return;
213   }
214   SetUnsignedIntegralAttribute(html_names::kHeightAttr, value,
215                                kDefaultCanvasHeight);
216 }
217 
setWidth(unsigned value,ExceptionState & exception_state)218 void HTMLCanvasElement::setWidth(unsigned value,
219                                  ExceptionState& exception_state) {
220   if (IsOffscreenCanvasRegistered()) {
221     exception_state.ThrowDOMException(
222         DOMExceptionCode::kInvalidStateError,
223         "Cannot resize canvas after call to transferControlToOffscreen().");
224     return;
225   }
226   SetUnsignedIntegralAttribute(html_names::kWidthAttr, value,
227                                kDefaultCanvasWidth);
228 }
229 
SetSize(const IntSize & new_size)230 void HTMLCanvasElement::SetSize(const IntSize& new_size) {
231   if (new_size == Size())
232     return;
233   ignore_reset_ = true;
234   SetIntegralAttribute(html_names::kWidthAttr, new_size.Width());
235   SetIntegralAttribute(html_names::kHeightAttr, new_size.Height());
236   ignore_reset_ = false;
237   Reset();
238 }
239 
240 HTMLCanvasElement::ContextFactoryVector&
RenderingContextFactories()241 HTMLCanvasElement::RenderingContextFactories() {
242   DCHECK(IsMainThread());
243   DEFINE_STATIC_LOCAL(ContextFactoryVector, context_factories,
244                       (CanvasRenderingContext::kMaxValue));
245   return context_factories;
246 }
247 
GetRenderingContextFactory(int type)248 CanvasRenderingContextFactory* HTMLCanvasElement::GetRenderingContextFactory(
249     int type) {
250   DCHECK_LE(type, CanvasRenderingContext::kMaxValue);
251   return RenderingContextFactories()[type].get();
252 }
253 
RegisterRenderingContextFactory(std::unique_ptr<CanvasRenderingContextFactory> rendering_context_factory)254 void HTMLCanvasElement::RegisterRenderingContextFactory(
255     std::unique_ptr<CanvasRenderingContextFactory> rendering_context_factory) {
256   CanvasRenderingContext::ContextType type =
257       rendering_context_factory->GetContextType();
258   DCHECK_LE(type, CanvasRenderingContext::kMaxValue);
259   DCHECK(!RenderingContextFactories()[type]);
260   RenderingContextFactories()[type] = std::move(rendering_context_factory);
261 }
262 
RecordIdentifiabilityMetric(IdentifiableSurface surface,IdentifiableToken value) const263 void HTMLCanvasElement::RecordIdentifiabilityMetric(
264     IdentifiableSurface surface,
265     IdentifiableToken value) const {
266   blink::IdentifiabilityMetricBuilder(GetDocument().UkmSourceID())
267       .Set(surface, value)
268       .Record(GetDocument().UkmRecorder());
269 }
270 
IdentifiabilityReportWithDigest(IdentifiableToken canvas_contents_token) const271 void HTMLCanvasElement::IdentifiabilityReportWithDigest(
272     IdentifiableToken canvas_contents_token) const {
273   if (IdentifiabilityStudySettings::Get()->ShouldSample(
274           blink::IdentifiableSurface::Type::kCanvasReadback)) {
275     RecordIdentifiabilityMetric(
276         blink::IdentifiableSurface::FromTypeAndToken(
277             blink::IdentifiableSurface::Type::kCanvasReadback,
278             IdentifiabilityInputDigest(context_)),
279         canvas_contents_token.ToUkmMetricValue());
280   }
281 }
282 
GetCanvasRenderingContext(const String & type,const CanvasContextCreationAttributesCore & attributes)283 CanvasRenderingContext* HTMLCanvasElement::GetCanvasRenderingContext(
284     const String& type,
285     const CanvasContextCreationAttributesCore& attributes) {
286   auto* old_contents_cc_layer = ContentsCcLayer();
287   auto* result = GetCanvasRenderingContextInternal(type, attributes);
288 
289   if (IdentifiabilityStudySettings::Get()->ShouldSample(
290           IdentifiableSurface::Type::kCanvasRenderingContext)) {
291     Document& doc = GetDocument();
292     IdentifiabilityMetricBuilder(doc.UkmSourceID())
293         .Set(IdentifiableSurface::FromTypeAndToken(
294                  IdentifiableSurface::Type::kCanvasRenderingContext,
295                  CanvasRenderingContext::ContextTypeFromId(type)),
296              !!result)
297         .Record(doc.UkmRecorder());
298   }
299 
300   if (ContentsCcLayer() != old_contents_cc_layer)
301     OnContentsCcLayerChanged();
302   return result;
303 }
304 
GetCanvasRenderingContextInternal(const String & type,const CanvasContextCreationAttributesCore & attributes)305 CanvasRenderingContext* HTMLCanvasElement::GetCanvasRenderingContextInternal(
306     const String& type,
307     const CanvasContextCreationAttributesCore& attributes) {
308   CanvasRenderingContext::ContextType context_type =
309       CanvasRenderingContext::ContextTypeFromId(type);
310 
311   // Unknown type.
312   if (context_type == CanvasRenderingContext::kContextTypeUnknown) {
313     return nullptr;
314   }
315 
316   // Log the aliased context type used.
317   if (!context_) {
318     UMA_HISTOGRAM_ENUMERATION("Blink.Canvas.ContextType", context_type);
319   }
320 
321   context_type =
322       CanvasRenderingContext::ResolveContextTypeAliases(context_type);
323 
324   CanvasRenderingContextFactory* factory =
325       GetRenderingContextFactory(context_type);
326   if (!factory)
327     return nullptr;
328 
329   // FIXME - The code depends on the context not going away once created, to
330   // prevent JS from seeing a dangling pointer. So for now we will disallow the
331   // context from being changed once it is created.
332   if (context_) {
333     if (context_->GetContextType() == context_type)
334       return context_.Get();
335 
336     factory->OnError(this,
337                      "Canvas has an existing context of a different type");
338     return nullptr;
339   }
340 
341   // If this context is cross-origin, it should prefer to use the low-power GPU
342   LocalFrame* frame = GetDocument().GetFrame();
343   CanvasContextCreationAttributesCore recomputed_attributes = attributes;
344   if (frame && frame->IsCrossOriginToMainFrame())
345     recomputed_attributes.power_preference = "low-power";
346 
347   context_ = factory->Create(this, recomputed_attributes);
348   if (!context_)
349     return nullptr;
350 
351   // Since the |context_| is created, free the transparent image,
352   // |transparent_image_| created for this canvas if it exists.
353   if (transparent_image_.get()) {
354     transparent_image_.reset();
355   }
356 
357   context_creation_was_blocked_ = false;
358 
359   probe::DidCreateCanvasContext(&GetDocument());
360 
361   if (Is3d())
362     UpdateMemoryUsage();
363 
364   LayoutObject* layout_object = GetLayoutObject();
365   if (layout_object) {
366     const ComputedStyle* style = GetComputedStyle();
367     if (style) {
368       context_->SetIsBeingDisplayed(style->Visibility() ==
369                                     EVisibility::kVisible);
370     }
371 
372     if (IsRenderingContext2D() && !context_->CreationAttributes().alpha) {
373       // In the alpha false case, canvas is initially opaque, so we need to
374       // trigger an invalidation.
375       DidDraw();
376     }
377   }
378 
379   if (context_->CreationAttributes().desynchronized) {
380     CreateLayer();
381     SetNeedsUnbufferedInputEvents(true);
382     frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>(
383         nullptr, surface_layer_bridge_->GetFrameSinkId().client_id(),
384         surface_layer_bridge_->GetFrameSinkId().sink_id(),
385         CanvasResourceDispatcher::kInvalidPlaceholderCanvasId, size_);
386     // We don't actually need the begin frame signal when in low latency mode,
387     // but we need to subscribe to it or else dispatching frames will not work.
388     frame_dispatcher_->SetNeedsBeginFrame(GetPage()->IsPageVisible());
389 
390     UseCounter::Count(GetDocument(), WebFeature::kHTMLCanvasElementLowLatency);
391   }
392 
393   // A 2D context does not know before lazy creation whether or not it is
394   // direct composited. The Canvas2DLayerBridge will handle this
395   if (!IsRenderingContext2D())
396     SetNeedsCompositingUpdate();
397 
398   return context_.Get();
399 }
400 
convertToBlob(ScriptState * script_state,const ImageEncodeOptions * options,ExceptionState & exception_state)401 ScriptPromise HTMLCanvasElement::convertToBlob(
402     ScriptState* script_state,
403     const ImageEncodeOptions* options,
404     ExceptionState& exception_state) {
405   return CanvasRenderingContextHost::convertToBlob(script_state, options,
406                                                    exception_state, context_);
407 }
408 
ShouldBeDirectComposited() const409 bool HTMLCanvasElement::ShouldBeDirectComposited() const {
410   return (context_ && context_->IsComposited()) || (!!surface_layer_bridge_);
411 }
412 
IsAccelerated() const413 bool HTMLCanvasElement::IsAccelerated() const {
414   return context_ && context_->IsAccelerated();
415 }
416 
GetSettings() const417 Settings* HTMLCanvasElement::GetSettings() const {
418   auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext());
419   if (window && window->GetFrame())
420     return window->GetFrame()->GetSettings();
421   return nullptr;
422 }
423 
IsWebGL1Enabled() const424 bool HTMLCanvasElement::IsWebGL1Enabled() const {
425   Settings* settings = GetSettings();
426   return settings && settings->GetWebGL1Enabled();
427 }
428 
IsWebGL2Enabled() const429 bool HTMLCanvasElement::IsWebGL2Enabled() const {
430   Settings* settings = GetSettings();
431   return settings && settings->GetWebGL2Enabled();
432 }
433 
IsWebGLBlocked() const434 bool HTMLCanvasElement::IsWebGLBlocked() const {
435   Document& document = GetDocument();
436   bool blocked = false;
437   mojo::Remote<mojom::blink::GpuDataManager> gpu_data_manager;
438   Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
439       gpu_data_manager.BindNewPipeAndPassReceiver());
440   gpu_data_manager->Are3DAPIsBlockedForUrl(document.Url(), &blocked);
441   return blocked;
442 }
443 
DidDraw(const FloatRect & rect)444 void HTMLCanvasElement::DidDraw(const FloatRect& rect) {
445   if (rect.IsEmpty())
446     return;
447   if (GetLayoutObject() && GetLayoutObject()->PreviousVisibilityVisible() &&
448       GetDocument().GetPage())
449     GetDocument().GetPage()->Animator().SetHasCanvasInvalidation();
450   canvas_is_clear_ = false;
451   if (GetLayoutObject() && !LowLatencyEnabled())
452     GetLayoutObject()->SetShouldCheckForPaintInvalidation();
453   if (IsRenderingContext2D() && context_->ShouldAntialias() && GetPage() &&
454       GetPage()->DeviceScaleFactorDeprecated() > 1.0f) {
455     FloatRect inflated_rect = rect;
456     inflated_rect.Inflate(1);
457     dirty_rect_.Unite(inflated_rect);
458   } else {
459     dirty_rect_.Unite(rect);
460   }
461   if (IsRenderingContext2D() && canvas2d_bridge_)
462     canvas2d_bridge_->DidDraw(rect);
463 }
464 
DidDraw()465 void HTMLCanvasElement::DidDraw() {
466   DidDraw(FloatRect(0, 0, Size().Width(), Size().Height()));
467 }
468 
PreFinalizeFrame()469 void HTMLCanvasElement::PreFinalizeFrame() {
470   RecordCanvasSizeToUMA(size_);
471 
472   // Low-latency 2d canvases produce their frames after the resource gets single
473   // buffered.
474   if (LowLatencyEnabled() && !dirty_rect_.IsEmpty() &&
475       GetOrCreateCanvasResourceProvider(RasterModeHint::kPreferGPU)) {
476     // TryEnableSingleBuffering() the first time we FinalizeFrame().  This is
477     // a nop if already single buffered or if single buffering is unsupported.
478     ResourceProvider()->TryEnableSingleBuffering();
479   }
480 }
481 
PostFinalizeFrame()482 void HTMLCanvasElement::PostFinalizeFrame() {
483   if (LowLatencyEnabled() && !dirty_rect_.IsEmpty() &&
484       GetOrCreateCanvasResourceProvider(RasterModeHint::kPreferGPU)) {
485     const base::TimeTicks start_time = base::TimeTicks::Now();
486     const scoped_refptr<CanvasResource> canvas_resource =
487         ResourceProvider()->ProduceCanvasResource();
488     const FloatRect src_rect(0, 0, Size().Width(), Size().Height());
489     dirty_rect_.Intersect(src_rect);
490     const IntRect int_dirty = EnclosingIntRect(dirty_rect_);
491     const SkIRect damage_rect = SkIRect::MakeXYWH(
492         int_dirty.X(), int_dirty.Y(), int_dirty.Width(), int_dirty.Height());
493     const bool needs_vertical_flip = !RenderingContext()->IsOriginTopLeft();
494     frame_dispatcher_->DispatchFrame(std::move(canvas_resource), start_time,
495                                      damage_rect, needs_vertical_flip,
496                                      IsOpaque());
497     dirty_rect_ = FloatRect();
498   }
499 
500   // If the canvas is visible, notifying listeners is taken care of in
501   // DoDeferredPaintInvalidation(), which allows the frame to be grabbed prior
502   // to compositing, which is critically important because compositing may clear
503   // the canvas's image. (e.g. WebGL context with preserveDrawingBuffer=false).
504   // If the canvas is not visible, DoDeferredPaintInvalidation will not get
505   // called, so we need to take care of business here.
506   if (!did_notify_listeners_for_current_frame_)
507     NotifyListenersCanvasChanged();
508   did_notify_listeners_for_current_frame_ = false;
509 }
510 
DisableAcceleration(std::unique_ptr<Canvas2DLayerBridge> unaccelerated_bridge_used_for_testing)511 void HTMLCanvasElement::DisableAcceleration(
512     std::unique_ptr<Canvas2DLayerBridge>
513         unaccelerated_bridge_used_for_testing) {
514   // Create and configure an unaccelerated Canvas2DLayerBridge.
515   std::unique_ptr<Canvas2DLayerBridge> bridge;
516   if (unaccelerated_bridge_used_for_testing)
517     bridge = std::move(unaccelerated_bridge_used_for_testing);
518   else
519     bridge = Create2DLayerBridge(RasterMode::kCPU);
520 
521   if (bridge && canvas2d_bridge_)
522     ReplaceExisting2dLayerBridge(std::move(bridge));
523 
524   // We must force a paint invalidation on the canvas even if it's
525   // content did not change because it layer was destroyed.
526   DidDraw();
527   SetNeedsCompositingUpdate();
528 }
529 
SetNeedsCompositingUpdate()530 void HTMLCanvasElement::SetNeedsCompositingUpdate() {
531   Element::SetNeedsCompositingUpdate();
532 }
533 
DoDeferredPaintInvalidation()534 void HTMLCanvasElement::DoDeferredPaintInvalidation() {
535   DCHECK(!dirty_rect_.IsEmpty());
536   if (LowLatencyEnabled()) {
537     // Low latency canvas handles dirty propagation in FinalizeFrame();
538     return;
539   }
540   LayoutBox* layout_box = GetLayoutBox();
541 
542   FloatRect content_rect;
543   if (layout_box) {
544     if (auto* replaced = DynamicTo<LayoutReplaced>(layout_box))
545       content_rect = FloatRect(replaced->ReplacedContentRect());
546     else
547       content_rect = FloatRect(layout_box->PhysicalContentBoxRect());
548   }
549 
550   if (IsRenderingContext2D()) {
551     FloatRect src_rect(0, 0, Size().Width(), Size().Height());
552     dirty_rect_.Intersect(src_rect);
553 
554     FloatRect invalidation_rect;
555     if (layout_box) {
556       FloatRect mapped_dirty_rect =
557           MapRect(dirty_rect_, src_rect, content_rect);
558       if (context_->IsComposited()) {
559         // Composited 2D canvases need the dirty rect to be expressed relative
560         // to the content box, as opposed to the layout box.
561         mapped_dirty_rect.MoveBy(-content_rect.Location());
562       }
563       invalidation_rect = mapped_dirty_rect;
564     } else {
565       invalidation_rect = dirty_rect_;
566     }
567 
568     if (dirty_rect_.IsEmpty())
569       return;
570 
571     if (canvas2d_bridge_)
572       canvas2d_bridge_->DoPaintInvalidation(invalidation_rect);
573   }
574 
575   if (context_ && HasImageBitmapContext() && context_->CcLayer())
576     context_->CcLayer()->SetNeedsDisplay();
577 
578   NotifyListenersCanvasChanged();
579   did_notify_listeners_for_current_frame_ = true;
580 
581   // Propagate the |dirty_rect_| accumulated so far to the compositor
582   // before restarting with a blank dirty rect.
583   // Canvas content updates do not need to be propagated as
584   // paint invalidations if the canvas is composited separately, since
585   // the canvas contents are sent separately through a texture layer.
586   if (layout_box && (!context_ || !context_->IsComposited())) {
587     // If the content box is larger than |src_rect|, the canvas's image is
588     // being stretched, so we need to account for color bleeding caused by the
589     // interpolation filter.
590     FloatRect src_rect(0, 0, Size().Width(), Size().Height());
591     if (content_rect.Width() > src_rect.Width() ||
592         content_rect.Height() > src_rect.Height()) {
593       dirty_rect_.Inflate(0.5);
594     }
595 
596     dirty_rect_.Intersect(src_rect);
597     PhysicalRect mapped_dirty_rect(
598         EnclosingIntRect(MapRect(dirty_rect_, src_rect, content_rect)));
599     layout_box->InvalidatePaintRectangle(mapped_dirty_rect);
600   }
601   dirty_rect_ = FloatRect();
602 
603   DCHECK(dirty_rect_.IsEmpty());
604 }
605 
Reset()606 void HTMLCanvasElement::Reset() {
607   if (ignore_reset_)
608     return;
609 
610   dirty_rect_ = FloatRect();
611 
612   bool had_resource_provider = HasResourceProvider();
613 
614   unsigned w = 0;
615   AtomicString value = FastGetAttribute(html_names::kWidthAttr);
616   if (value.IsEmpty() || !ParseHTMLNonNegativeInteger(value, w) ||
617       w > 0x7fffffffu) {
618     w = kDefaultCanvasWidth;
619   }
620 
621   unsigned h = 0;
622   value = FastGetAttribute(html_names::kHeightAttr);
623   if (value.IsEmpty() || !ParseHTMLNonNegativeInteger(value, h) ||
624       h > 0x7fffffffu) {
625     h = kDefaultCanvasHeight;
626   }
627 
628   if (IsRenderingContext2D()) {
629     context_->Reset();
630     origin_clean_ = true;
631   }
632 
633   IntSize old_size = Size();
634   IntSize new_size(w, h);
635 
636   // If the size of an existing buffer matches, we can just clear it instead of
637   // reallocating.  This optimization is only done for 2D canvases for now.
638   if (had_resource_provider && old_size == new_size && IsRenderingContext2D()) {
639     if (!canvas_is_clear_) {
640       canvas_is_clear_ = true;
641       if (canvas2d_bridge_)
642         canvas2d_bridge_->ClearFrame();
643       context_->ClearRect(0, 0, width(), height());
644     }
645     return;
646   }
647 
648   SetSurfaceSize(new_size);
649 
650   if (Is3d() && old_size != Size())
651     context_->Reshape(width(), height());
652 
653   if (LayoutObject* layout_object = GetLayoutObject()) {
654     if (layout_object->IsCanvas()) {
655       if (old_size != Size()) {
656         To<LayoutHTMLCanvas>(layout_object)->CanvasSizeChanged();
657         if (GetDocument().GetSettings()->GetAcceleratedCompositingEnabled())
658           GetLayoutBox()->ContentChanged(kCanvasChanged);
659       }
660       if (had_resource_provider)
661         layout_object->SetShouldDoFullPaintInvalidation();
662     }
663   }
664 }
665 
PaintsIntoCanvasBuffer() const666 bool HTMLCanvasElement::PaintsIntoCanvasBuffer() const {
667   if (OffscreenCanvasFrame())
668     return false;
669   DCHECK(context_);
670   if (!context_->IsComposited())
671     return true;
672   auto* settings = GetDocument().GetSettings();
673   if (settings && settings->GetAcceleratedCompositingEnabled())
674     return false;
675 
676   return true;
677 }
678 
NotifyListenersCanvasChanged()679 void HTMLCanvasElement::NotifyListenersCanvasChanged() {
680   if (listeners_.size() == 0)
681     return;
682 
683   if (!OriginClean()) {
684     listeners_.clear();
685     return;
686   }
687 
688   bool listener_needs_new_frame_capture = false;
689   for (const CanvasDrawListener* listener : listeners_) {
690     if (listener->NeedsNewFrame())
691       listener_needs_new_frame_capture = true;
692   }
693 
694   if (listener_needs_new_frame_capture) {
695     SourceImageStatus status;
696     scoped_refptr<StaticBitmapImage> source_image =
697         GetSourceImageForCanvasInternal(&status);
698     if (status != kNormalSourceImageStatus)
699       return;
700     for (CanvasDrawListener* listener : listeners_) {
701       if (listener->NeedsNewFrame()) {
702         // Here we need to use the SharedGpuContext as some of the images may
703         // have been originated with other contextProvider, but we internally
704         // need a context_provider that has a RasterInterface available.
705         listener->SendNewFrame(source_image,
706                                SharedGpuContext::ContextProviderWrapper());
707       }
708     }
709   }
710 }
711 
712 // Returns an image and the image's resolution scale factor.
BrokenCanvas(float device_scale_factor)713 static std::pair<blink::Image*, float> BrokenCanvas(float device_scale_factor) {
714   if (device_scale_factor >= 2) {
715     DEFINE_STATIC_REF(blink::Image, broken_canvas_hi_res,
716                       (blink::Image::LoadPlatformResource(
717                           IDR_BROKENCANVAS, ui::SCALE_FACTOR_200P)));
718     return std::make_pair(broken_canvas_hi_res, 2);
719   }
720 
721   DEFINE_STATIC_REF(blink::Image, broken_canvas_lo_res,
722                     (blink::Image::LoadPlatformResource(IDR_BROKENCANVAS)));
723   return std::make_pair(broken_canvas_lo_res, 1);
724 }
725 
FilterQualityFromStyle(const ComputedStyle * style)726 static SkFilterQuality FilterQualityFromStyle(const ComputedStyle* style) {
727   if (style && style->ImageRendering() == EImageRendering::kPixelated)
728     return kNone_SkFilterQuality;
729   return kLow_SkFilterQuality;
730 }
731 
FilterQuality() const732 SkFilterQuality HTMLCanvasElement::FilterQuality() const {
733   if (!isConnected())
734     return kLow_SkFilterQuality;
735 
736   const ComputedStyle* style = GetComputedStyle();
737   if (!style) {
738     GetDocument().UpdateStyleAndLayoutTreeForNode(this);
739     HTMLCanvasElement* non_const_this = const_cast<HTMLCanvasElement*>(this);
740     style = non_const_this->EnsureComputedStyle();
741   }
742   return FilterQualityFromStyle(style);
743 }
744 
LowLatencyEnabled() const745 bool HTMLCanvasElement::LowLatencyEnabled() const {
746   return !!frame_dispatcher_;
747 }
748 
UpdateFilterQuality(SkFilterQuality filter_quality)749 void HTMLCanvasElement::UpdateFilterQuality(SkFilterQuality filter_quality) {
750   if (IsOffscreenCanvasRegistered())
751     UpdateOffscreenCanvasFilterQuality(filter_quality);
752 
753   if (context_ && Is3d())
754     context_->SetFilterQuality(filter_quality);
755   else if (canvas2d_bridge_)
756     canvas2d_bridge_->SetFilterQuality(filter_quality);
757 }
758 
759 // In some instances we don't actually want to paint to the parent layer
760 // We still might want to set filter quality and MarkFirstContentfulPaint though
Paint(GraphicsContext & context,const PhysicalRect & r,bool flatten_composited_layers)761 void HTMLCanvasElement::Paint(GraphicsContext& context,
762                               const PhysicalRect& r,
763                               bool flatten_composited_layers) {
764   if (context_creation_was_blocked_ ||
765       (context_ && context_->isContextLost())) {
766     float device_scale_factor =
767         blink::DeviceScaleFactorDeprecated(GetDocument().GetFrame());
768     std::pair<Image*, float> broken_canvas_and_image_scale_factor =
769         BrokenCanvas(device_scale_factor);
770     Image* broken_canvas = broken_canvas_and_image_scale_factor.first;
771     context.Save();
772     context.FillRect(FloatRect(r), Color(), SkBlendMode::kClear);
773     // Place the icon near the upper left, like the missing image icon
774     // for image elements. Offset it a bit from the upper corner.
775     FloatSize icon_size(broken_canvas->Size());
776     FloatPoint upper_left =
777         FloatPoint(r.PixelSnappedOffset()) + icon_size.ScaledBy(0.5f);
778     context.DrawImage(broken_canvas, Image::kSyncDecode,
779                       FloatRect(upper_left, icon_size));
780     context.Restore();
781     return;
782   }
783 
784   // FIXME: crbug.com/438240; there is a bug with the new CSS blending and
785   // compositing feature.
786   if (!context_ && !OffscreenCanvasFrame())
787     return;
788 
789   // If the canvas is gpu composited, it has another way of getting to screen
790   if (!PaintsIntoCanvasBuffer()) {
791     // For click-and-drag or printing we still want to draw
792     if (!(flatten_composited_layers || GetDocument().Printing()))
793       return;
794   }
795 
796   if (OffscreenCanvasFrame()) {
797     DCHECK(GetDocument().Printing());
798     scoped_refptr<StaticBitmapImage> image_for_printing =
799         OffscreenCanvasFrame()->Bitmap()->MakeUnaccelerated();
800     context.DrawImage(image_for_printing.get(), Image::kSyncDecode,
801                       FloatRect(PixelSnappedIntRect(r)));
802     return;
803   }
804 
805   PaintInternal(context, r);
806 }
807 
PaintInternal(GraphicsContext & context,const PhysicalRect & r)808 void HTMLCanvasElement::PaintInternal(GraphicsContext& context,
809                                       const PhysicalRect& r) {
810   context_->PaintRenderingResultsToCanvas(kFrontBuffer);
811   if (HasResourceProvider()) {
812     const ComputedStyle* style = GetComputedStyle();
813     // For 2D Canvas, there are two ways of render Canvas for printing:
814     // display list or image snapshot. Display list allows better PDF printing
815     // and we prefer this method.
816     // Here are the requirements for display list to be used:
817     //    1. We must have had a full repaint of the Canvas after beginprint
818     //       event has been fired. Otherwise, we don't have a PaintRecord.
819     //    2. CSS property 'image-rendering' must not be 'pixelated'.
820 
821     // display list rendering: we replay the last full PaintRecord, if Canvas
822     // has been redraw since beginprint happened.
823     if (IsPrinting() && !Is3d() && canvas2d_bridge_) {
824       canvas2d_bridge_->FlushRecording();
825       if (canvas2d_bridge_->getLastRecord()) {
826         if (style && style->ImageRendering() != EImageRendering::kPixelated) {
827           context.Canvas()->save();
828           context.Canvas()->translate(r.X(), r.Y());
829           context.Canvas()->scale(r.Width() / Size().Width(),
830                                   r.Height() / Size().Height());
831           context.Canvas()->drawPicture(canvas2d_bridge_->getLastRecord());
832           context.Canvas()->restore();
833           UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.2DPrintingAsVector", true);
834           return;
835         }
836       }
837       UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.2DPrintingAsVector", false);
838     }
839     // or image snapshot rendering: grab a snapshot and raster it.
840     SkBlendMode composite_operator =
841         !context_ || context_->CreationAttributes().alpha
842             ? SkBlendMode::kSrcOver
843             : SkBlendMode::kSrc;
844     FloatRect src_rect = FloatRect(FloatPoint(), FloatSize(Size()));
845     scoped_refptr<StaticBitmapImage> snapshot =
846         canvas2d_bridge_
847             ? canvas2d_bridge_->NewImageSnapshot()
848             : (ResourceProvider() ? ResourceProvider()->Snapshot() : nullptr);
849     if (snapshot) {
850       // GraphicsContext cannot handle gpu resource serialization.
851       snapshot = snapshot->MakeUnaccelerated();
852       DCHECK(!snapshot->IsTextureBacked());
853       context.DrawImage(snapshot.get(), Image::kSyncDecode,
854                         FloatRect(PixelSnappedIntRect(r)), &src_rect,
855                         style && style->HasFilterInducingProperty(),
856                         composite_operator);
857     }
858   } else {
859     // When alpha is false, we should draw to opaque black.
860     if (!context_->CreationAttributes().alpha)
861       context.FillRect(FloatRect(r), Color(0, 0, 0));
862   }
863 
864   if (Is3d() && PaintsIntoCanvasBuffer())
865     context_->MarkLayerComposited();
866 }
867 
IsPrinting() const868 bool HTMLCanvasElement::IsPrinting() const {
869   return GetDocument().BeforePrintingOrPrinting();
870 }
871 
GetUkmParameters()872 UkmParameters HTMLCanvasElement::GetUkmParameters() {
873   return {GetDocument().UkmRecorder(), GetDocument().UkmSourceID()};
874 }
875 
SetSurfaceSize(const IntSize & size)876 void HTMLCanvasElement::SetSurfaceSize(const IntSize& size) {
877   size_ = size;
878   did_fail_to_create_resource_provider_ = false;
879   DiscardResourceProvider();
880   if (IsRenderingContext2D() && context_->isContextLost())
881     context_->DidSetSurfaceSize();
882   if (frame_dispatcher_)
883     frame_dispatcher_->Reshape(size_);
884 }
885 
ImageSourceURL() const886 const AtomicString HTMLCanvasElement::ImageSourceURL() const {
887   return AtomicString(ToDataURLInternal(
888       ImageEncoderUtils::kDefaultRequestedMimeType, 0, kFrontBuffer));
889 }
890 
Snapshot(SourceDrawingBuffer source_buffer) const891 scoped_refptr<StaticBitmapImage> HTMLCanvasElement::Snapshot(
892     SourceDrawingBuffer source_buffer) const {
893   if (size_.IsEmpty())
894     return nullptr;
895 
896   scoped_refptr<StaticBitmapImage> image_bitmap = nullptr;
897   if (OffscreenCanvasFrame()) {  // Offscreen Canvas
898     DCHECK(OffscreenCanvasFrame()->OriginClean());
899     image_bitmap = OffscreenCanvasFrame()->Bitmap();
900   } else if (Is3d()) {  // WebGL or WebGL2 canvas
901     if (context_->CreationAttributes().premultiplied_alpha) {
902       context_->PaintRenderingResultsToCanvas(source_buffer);
903       if (ResourceProvider())
904         image_bitmap = ResourceProvider()->Snapshot();
905     } else {
906       sk_sp<SkData> pixel_data =
907           context_->PaintRenderingResultsToDataArray(source_buffer);
908       if (pixel_data) {
909         // If the accelerated canvas is too big, there is a logic in WebGL code
910         // path that scales down the drawing buffer to the maximum supported
911         // size. Hence, we need to query the adjusted size of DrawingBuffer.
912         IntSize adjusted_size = context_->DrawingBufferSize();
913         SkImageInfo info =
914             SkImageInfo::Make(adjusted_size.Width(), adjusted_size.Height(),
915                               kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
916         info = info.makeColorSpace(ColorParams().GetSkColorSpace());
917         if (ColorParams().GetSkColorType() != kN32_SkColorType)
918           info = info.makeColorType(kRGBA_F16_SkColorType);
919         image_bitmap = StaticBitmapImage::Create(std::move(pixel_data), info);
920       }
921     }
922   } else if (canvas2d_bridge_) {
923     DCHECK(IsRenderingContext2D());
924     image_bitmap = canvas2d_bridge_->NewImageSnapshot();
925   } else if (context_) {  // Bitmap renderer canvas
926     image_bitmap = context_->GetImage();
927   }
928 
929   if (!image_bitmap)
930     image_bitmap = CreateTransparentImage(size_);
931   return image_bitmap;
932 }
933 
ToDataURLInternal(const String & mime_type,const double & quality,SourceDrawingBuffer source_buffer) const934 String HTMLCanvasElement::ToDataURLInternal(
935     const String& mime_type,
936     const double& quality,
937     SourceDrawingBuffer source_buffer) const {
938   base::TimeTicks start_time = base::TimeTicks::Now();
939   if (!IsPaintable())
940     return String("data:,");
941 
942   ImageEncodingMimeType encoding_mime_type =
943       ImageEncoderUtils::ToEncodingMimeType(
944           mime_type, ImageEncoderUtils::kEncodeReasonToDataURL);
945 
946   scoped_refptr<StaticBitmapImage> image_bitmap = Snapshot(source_buffer);
947   if (image_bitmap) {
948     std::unique_ptr<ImageDataBuffer> data_buffer =
949         ImageDataBuffer::Create(image_bitmap);
950     if (!data_buffer)
951       return String("data:,");
952 
953     String data_url = data_buffer->ToDataURL(encoding_mime_type, quality);
954     base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time;
955     float sqrt_pixels =
956         std::sqrt(image_bitmap->width()) * std::sqrt(image_bitmap->height());
957     float scaled_time_float = elapsed_time.InMicrosecondsF() /
958                               (sqrt_pixels == 0 ? 1.0f : sqrt_pixels);
959 
960     // If scaled_time_float overflows as integer, CheckedNumeric will store it
961     // as invalid, then ValueOrDefault will return the maximum int.
962     base::CheckedNumeric<int> checked_scaled_time = scaled_time_float;
963     int scaled_time_int =
964         checked_scaled_time.ValueOrDefault(std::numeric_limits<int>::max());
965 
966     if (encoding_mime_type == kMimeTypePng) {
967       UMA_HISTOGRAM_COUNTS_100000("Blink.Canvas.ToDataURLScaledDuration.PNG",
968                                   scaled_time_int);
969     } else if (encoding_mime_type == kMimeTypeJpeg) {
970       UMA_HISTOGRAM_COUNTS_100000("Blink.Canvas.ToDataURLScaledDuration.JPEG",
971                                   scaled_time_int);
972     } else if (encoding_mime_type == kMimeTypeWebp) {
973       UMA_HISTOGRAM_COUNTS_100000("Blink.Canvas.ToDataURLScaledDuration.WEBP",
974                                   scaled_time_int);
975     } else {
976       // Currently we only support three encoding types.
977       NOTREACHED();
978     }
979     IdentifiabilityReportWithDigest(IdentifiabilityBenignStringToken(data_url));
980     return data_url;
981   }
982 
983   return String("data:,");
984 }
985 
toDataURL(const String & mime_type,const ScriptValue & quality_argument,ExceptionState & exception_state) const986 String HTMLCanvasElement::toDataURL(const String& mime_type,
987                                     const ScriptValue& quality_argument,
988                                     ExceptionState& exception_state) const {
989   if (!OriginClean()) {
990     exception_state.ThrowSecurityError("Tainted canvases may not be exported.");
991     return String();
992   }
993 
994   double quality = kUndefinedQualityValue;
995   if (!quality_argument.IsEmpty()) {
996     v8::Local<v8::Value> v8_value = quality_argument.V8Value();
997     if (v8_value->IsNumber())
998       quality = v8_value.As<v8::Number>()->Value();
999   }
1000   return ToDataURLInternal(mime_type, quality, kBackBuffer);
1001 }
1002 
toBlob(V8BlobCallback * callback,const String & mime_type,const ScriptValue & quality_argument,ExceptionState & exception_state)1003 void HTMLCanvasElement::toBlob(V8BlobCallback* callback,
1004                                const String& mime_type,
1005                                const ScriptValue& quality_argument,
1006                                ExceptionState& exception_state) {
1007   if (!OriginClean()) {
1008     exception_state.ThrowSecurityError("Tainted canvases may not be exported.");
1009     return;
1010   }
1011 
1012   if (!GetExecutionContext())
1013     return;
1014 
1015   if (!IsPaintable()) {
1016     // If the canvas element's bitmap has no pixels
1017     GetDocument()
1018         .GetTaskRunner(TaskType::kCanvasBlobSerialization)
1019         ->PostTask(FROM_HERE,
1020                    WTF::Bind(&V8BlobCallback::InvokeAndReportException,
1021                              WrapPersistent(callback), nullptr, nullptr));
1022     return;
1023   }
1024 
1025   base::TimeTicks start_time = base::TimeTicks::Now();
1026   double quality = kUndefinedQualityValue;
1027   if (!quality_argument.IsEmpty()) {
1028     v8::Local<v8::Value> v8_value = quality_argument.V8Value();
1029     if (v8_value->IsNumber())
1030       quality = v8_value.As<v8::Number>()->Value();
1031   }
1032 
1033   ImageEncodingMimeType encoding_mime_type =
1034       ImageEncoderUtils::ToEncodingMimeType(
1035           mime_type, ImageEncoderUtils::kEncodeReasonToBlobCallback);
1036 
1037   CanvasAsyncBlobCreator* async_creator = nullptr;
1038   scoped_refptr<StaticBitmapImage> image_bitmap = Snapshot(kBackBuffer);
1039   if (image_bitmap) {
1040     auto* options = ImageEncodeOptions::Create();
1041     options->setType(ImageEncodingMimeTypeName(encoding_mime_type));
1042     async_creator = MakeGarbageCollected<CanvasAsyncBlobCreator>(
1043         image_bitmap, options,
1044         CanvasAsyncBlobCreator::kHTMLCanvasToBlobCallback, callback, start_time,
1045         GetExecutionContext(),
1046         IdentifiabilityStudySettings::Get()->IsTypeAllowed(
1047             IdentifiableSurface::Type::kCanvasReadback)
1048             ? IdentifiabilityInputDigest(context_)
1049             : 0);
1050   }
1051 
1052   if (async_creator) {
1053     async_creator->ScheduleAsyncBlobCreation(quality);
1054   } else {
1055     GetDocument()
1056         .GetTaskRunner(TaskType::kCanvasBlobSerialization)
1057         ->PostTask(FROM_HERE,
1058                    WTF::Bind(&V8BlobCallback::InvokeAndReportException,
1059                              WrapPersistent(callback), nullptr, nullptr));
1060   }
1061 }
1062 
AddListener(CanvasDrawListener * listener)1063 void HTMLCanvasElement::AddListener(CanvasDrawListener* listener) {
1064   listeners_.insert(listener);
1065 }
1066 
RemoveListener(CanvasDrawListener * listener)1067 void HTMLCanvasElement::RemoveListener(CanvasDrawListener* listener) {
1068   listeners_.erase(listener);
1069 }
1070 
OriginClean() const1071 bool HTMLCanvasElement::OriginClean() const {
1072   if (GetDocument().GetSettings() &&
1073       GetDocument().GetSettings()->GetDisableReadingFromCanvas()) {
1074     return false;
1075   }
1076   if (OffscreenCanvasFrame())
1077     return OffscreenCanvasFrame()->OriginClean();
1078   return origin_clean_;
1079 }
1080 
ShouldAccelerate2dContext() const1081 bool HTMLCanvasElement::ShouldAccelerate2dContext() const {
1082   return ShouldAccelerate();
1083 }
1084 
GetOrCreateResourceDispatcher()1085 CanvasResourceDispatcher* HTMLCanvasElement::GetOrCreateResourceDispatcher() {
1086   // The HTMLCanvasElement override of this method never needs to 'create'
1087   // because the frame_dispatcher is only used in low latency mode, in which
1088   // case the dispatcher is created upfront.
1089   return frame_dispatcher_.get();
1090 }
1091 
PushFrame(scoped_refptr<CanvasResource> image,const SkIRect & damage_rect)1092 bool HTMLCanvasElement::PushFrame(scoped_refptr<CanvasResource> image,
1093                                   const SkIRect& damage_rect) {
1094   NOTIMPLEMENTED();
1095   return false;
1096 }
1097 
ShouldAccelerate() const1098 bool HTMLCanvasElement::ShouldAccelerate() const {
1099   if (context_ && !IsRenderingContext2D())
1100     return false;
1101 
1102   // The command line flag --disable-accelerated-2d-canvas toggles this option
1103   if (!RuntimeEnabledFeatures::Accelerated2dCanvasEnabled()) {
1104     return false;
1105   }
1106 
1107   // Webview crashes with accelerated small canvases (crbug.com/1004304)
1108   // Experimenting to see if this still causes crashes (crbug.com/1136603)
1109   if (!RuntimeEnabledFeatures::AcceleratedSmallCanvasesEnabled() &&
1110       !base::FeatureList::IsEnabled(
1111           features::kWebviewAccelerateSmallCanvases)) {
1112     base::CheckedNumeric<int> checked_canvas_pixel_count =
1113         Size().Width() * Size().Height();
1114     if (!checked_canvas_pixel_count.IsValid())
1115       return false;
1116     int canvas_pixel_count = checked_canvas_pixel_count.ValueOrDie();
1117 
1118     if (canvas_pixel_count < kMinimumAccelerated2dCanvasSize)
1119       return false;
1120   }
1121 
1122   // The following is necessary for handling the special case of canvases in
1123   // the dev tools overlay, which run in a process that supports accelerated
1124   // 2d canvas but in a special compositing context that does not.
1125   auto* settings = GetDocument().GetSettings();
1126   if (settings && !settings->GetAcceleratedCompositingEnabled())
1127     return false;
1128 
1129   // Avoid creating |contextProvider| until we're sure we want to try use it,
1130   // since it costs us GPU memory.
1131   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
1132       SharedGpuContext::ContextProviderWrapper();
1133   if (!context_provider_wrapper)
1134     return false;
1135 
1136   return context_provider_wrapper->Utils()->Accelerated2DCanvasFeatureEnabled();
1137 }
1138 
Create2DLayerBridge(RasterMode raster_mode)1139 std::unique_ptr<Canvas2DLayerBridge> HTMLCanvasElement::Create2DLayerBridge(
1140     RasterMode raster_mode) {
1141   auto surface =
1142       std::make_unique<Canvas2DLayerBridge>(Size(), raster_mode, ColorParams());
1143   if (!surface->IsValid())
1144     return nullptr;
1145 
1146   return surface;
1147 }
1148 
SetCanvas2DLayerBridgeInternal(std::unique_ptr<Canvas2DLayerBridge> external_canvas2d_bridge)1149 void HTMLCanvasElement::SetCanvas2DLayerBridgeInternal(
1150     std::unique_ptr<Canvas2DLayerBridge> external_canvas2d_bridge) {
1151   DCHECK(IsRenderingContext2D() && !canvas2d_bridge_);
1152   did_fail_to_create_resource_provider_ = true;
1153 
1154   if (!IsValidImageSize(Size()))
1155     return;
1156 
1157   if (external_canvas2d_bridge) {
1158     if (external_canvas2d_bridge->IsValid())
1159       canvas2d_bridge_ = std::move(external_canvas2d_bridge);
1160   } else {
1161     // If the canvas meets the criteria to use accelerated-GPU rendering, and
1162     // the user signals that the canvas will not be read frequently through
1163     // getImageData, which is a slow operation with GPU, the canvas will try to
1164     // use accelerated-GPU rendering.
1165     // If any of the two conditions fails, or if the creation of accelerated
1166     // resource provider fails, the canvas will fallback to CPU rendering.
1167     UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.WillReadFrequently",
1168                           context_->CreationAttributes().will_read_frequently);
1169 
1170     if (ShouldAccelerate() &&
1171         !context_->CreationAttributes().will_read_frequently) {
1172       canvas2d_bridge_ = Create2DLayerBridge(RasterMode::kGPU);
1173     }
1174     if (!canvas2d_bridge_) {
1175       canvas2d_bridge_ = Create2DLayerBridge(RasterMode::kCPU);
1176     }
1177   }
1178 
1179   if (!canvas2d_bridge_)
1180     return;
1181 
1182   canvas2d_bridge_->SetCanvasResourceHost(this);
1183   bool is_being_displayed =
1184       GetLayoutObject() && GetComputedStyle() &&
1185       GetComputedStyle()->Visibility() == EVisibility::kVisible;
1186   canvas2d_bridge_->SetIsBeingDisplayed(is_being_displayed);
1187 
1188   did_fail_to_create_resource_provider_ = false;
1189   UpdateMemoryUsage();
1190 
1191   if (context_)
1192     SetNeedsCompositingUpdate();
1193 }
1194 
NotifyGpuContextLost()1195 void HTMLCanvasElement::NotifyGpuContextLost() {
1196   if (IsRenderingContext2D())
1197     context_->LoseContext(CanvasRenderingContext::kRealLostContext);
1198 }
1199 
Trace(Visitor * visitor) const1200 void HTMLCanvasElement::Trace(Visitor* visitor) const {
1201   visitor->Trace(listeners_);
1202   visitor->Trace(context_);
1203   ExecutionContextLifecycleObserver::Trace(visitor);
1204   PageVisibilityObserver::Trace(visitor);
1205   HTMLElement::Trace(visitor);
1206 }
1207 
GetOrCreateCanvas2DLayerBridge()1208 Canvas2DLayerBridge* HTMLCanvasElement::GetOrCreateCanvas2DLayerBridge() {
1209   DCHECK(IsRenderingContext2D());
1210   if (!canvas2d_bridge_ && !did_fail_to_create_resource_provider_) {
1211     SetCanvas2DLayerBridgeInternal(nullptr);
1212     if (did_fail_to_create_resource_provider_ && !Size().IsEmpty())
1213       context_->LoseContext(CanvasRenderingContext::kSyntheticLostContext);
1214   }
1215   return canvas2d_bridge_.get();
1216 }
1217 
SetResourceProviderForTesting(std::unique_ptr<CanvasResourceProvider> resource_provider,std::unique_ptr<Canvas2DLayerBridge> bridge,const IntSize & size)1218 void HTMLCanvasElement::SetResourceProviderForTesting(
1219     std::unique_ptr<CanvasResourceProvider> resource_provider,
1220     std::unique_ptr<Canvas2DLayerBridge> bridge,
1221     const IntSize& size) {
1222   DiscardResourceProvider();
1223   SetIntegralAttribute(html_names::kWidthAttr, size.Width());
1224   SetIntegralAttribute(html_names::kHeightAttr, size.Height());
1225   SetCanvas2DLayerBridgeInternal(std::move(bridge));
1226   ReplaceResourceProvider(std::move(resource_provider));
1227 }
1228 
DiscardResourceProvider()1229 void HTMLCanvasElement::DiscardResourceProvider() {
1230   canvas2d_bridge_.reset();
1231   CanvasResourceHost::DiscardResourceProvider();
1232   dirty_rect_ = FloatRect();
1233 }
1234 
PageVisibilityChanged()1235 void HTMLCanvasElement::PageVisibilityChanged() {
1236   bool hidden = !GetPage()->IsPageVisible();
1237   SetSuspendOffscreenCanvasAnimation(hidden);
1238 
1239   if (!context_)
1240     return;
1241 
1242   context_->SetIsInHiddenPage(hidden);
1243   if (hidden && Is3d())
1244     DiscardResourceProvider();
1245 }
1246 
ContextDestroyed()1247 void HTMLCanvasElement::ContextDestroyed() {
1248   if (context_)
1249     context_->Stop();
1250 }
1251 
StyleChangeNeedsDidDraw(const ComputedStyle * old_style,const ComputedStyle & new_style)1252 bool HTMLCanvasElement::StyleChangeNeedsDidDraw(
1253     const ComputedStyle* old_style,
1254     const ComputedStyle& new_style) {
1255   // It will only need to redraw for a style change, if the new imageRendering
1256   // is different than the previous one, and only if one of the two ir
1257   // pixelated.
1258   return old_style &&
1259          old_style->ImageRendering() != new_style.ImageRendering() &&
1260          (old_style->ImageRendering() == EImageRendering::kPixelated ||
1261           new_style.ImageRendering() == EImageRendering::kPixelated);
1262 }
1263 
StyleDidChange(const ComputedStyle * old_style,const ComputedStyle & new_style)1264 void HTMLCanvasElement::StyleDidChange(const ComputedStyle* old_style,
1265                                        const ComputedStyle& new_style) {
1266   UpdateFilterQuality(FilterQualityFromStyle(&new_style));
1267   if (context_)
1268     context_->StyleDidChange(old_style, new_style);
1269   if (StyleChangeNeedsDidDraw(old_style, new_style))
1270     DidDraw();
1271 }
1272 
LayoutObjectDestroyed()1273 void HTMLCanvasElement::LayoutObjectDestroyed() {
1274   // If the canvas has no layout object then it definitely isn't being
1275   // displayed any more.
1276   if (context_)
1277     context_->SetIsBeingDisplayed(false);
1278 }
1279 
DidMoveToNewDocument(Document & old_document)1280 void HTMLCanvasElement::DidMoveToNewDocument(Document& old_document) {
1281   SetExecutionContext(GetExecutionContext());
1282   SetPage(GetDocument().GetPage());
1283   HTMLElement::DidMoveToNewDocument(old_document);
1284 }
1285 
WillDrawImageTo2DContext(CanvasImageSource * source)1286 void HTMLCanvasElement::WillDrawImageTo2DContext(CanvasImageSource* source) {
1287   if (SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade() &&
1288       source->IsAccelerated() && GetOrCreateCanvas2DLayerBridge() &&
1289       !canvas2d_bridge_->IsAccelerated() && ShouldAccelerate()) {
1290     std::unique_ptr<Canvas2DLayerBridge> surface =
1291         Create2DLayerBridge(RasterMode::kGPU);
1292     if (surface) {
1293       ReplaceExisting2dLayerBridge(std::move(surface));
1294       SetNeedsCompositingUpdate();
1295     }
1296   }
1297 }
1298 
GetSourceImageForCanvas(SourceImageStatus * status,const FloatSize &)1299 scoped_refptr<Image> HTMLCanvasElement::GetSourceImageForCanvas(
1300     SourceImageStatus* status,
1301     const FloatSize&) {
1302   return GetSourceImageForCanvasInternal(status);
1303 }
1304 
1305 scoped_refptr<StaticBitmapImage>
GetSourceImageForCanvasInternal(SourceImageStatus * status)1306 HTMLCanvasElement::GetSourceImageForCanvasInternal(SourceImageStatus* status) {
1307   if (!width() || !height()) {
1308     *status = kZeroSizeCanvasSourceImageStatus;
1309     return nullptr;
1310   }
1311 
1312   if (!IsPaintable()) {
1313     *status = kInvalidSourceImageStatus;
1314     return nullptr;
1315   }
1316 
1317   if (OffscreenCanvasFrame()) {
1318     *status = kNormalSourceImageStatus;
1319     return OffscreenCanvasFrame()->Bitmap();
1320   }
1321 
1322   if (!context_) {
1323     scoped_refptr<StaticBitmapImage> result = GetTransparentImage();
1324     *status = result ? kNormalSourceImageStatus : kInvalidSourceImageStatus;
1325     return result;
1326   }
1327 
1328   if (HasImageBitmapContext()) {
1329     *status = kNormalSourceImageStatus;
1330     scoped_refptr<StaticBitmapImage> result = context_->GetImage();
1331     if (!result)
1332       result = GetTransparentImage();
1333     *status = result ? kNormalSourceImageStatus : kInvalidSourceImageStatus;
1334     return result;
1335   }
1336 
1337   scoped_refptr<StaticBitmapImage> image;
1338   // TODO(ccameron): Canvas should produce sRGB images.
1339   // https://crbug.com/672299
1340   if (Is3d()) {
1341     // Because WebGL sources always require making a copy of the back buffer, we
1342     // use paintRenderingResultsToCanvas instead of getImage in order to keep a
1343     // cached copy of the backing in the canvas's resource provider.
1344     RenderingContext()->PaintRenderingResultsToCanvas(kBackBuffer);
1345     if (ResourceProvider())
1346       image = ResourceProvider()->Snapshot();
1347     else
1348       image = GetTransparentImage();
1349   } else {
1350     image = RenderingContext()->GetImage();
1351     if (!image)
1352       image = GetTransparentImage();
1353   }
1354 
1355   if (image)
1356     *status = kNormalSourceImageStatus;
1357   else
1358     *status = kInvalidSourceImageStatus;
1359   return image;
1360 }
1361 
WouldTaintOrigin() const1362 bool HTMLCanvasElement::WouldTaintOrigin() const {
1363   return !OriginClean();
1364 }
1365 
ElementSize(const FloatSize &,const RespectImageOrientationEnum) const1366 FloatSize HTMLCanvasElement::ElementSize(
1367     const FloatSize&,
1368     const RespectImageOrientationEnum) const {
1369   if (context_ && HasImageBitmapContext()) {
1370     scoped_refptr<Image> image = context_->GetImage();
1371     if (image)
1372       return FloatSize(image->width(), image->height());
1373     return FloatSize(0, 0);
1374   }
1375   if (OffscreenCanvasFrame())
1376     return FloatSize(OffscreenCanvasFrame()->Size());
1377   return FloatSize(width(), height());
1378 }
1379 
BitmapSourceSize() const1380 IntSize HTMLCanvasElement::BitmapSourceSize() const {
1381   return IntSize(width(), height());
1382 }
1383 
CreateImageBitmap(ScriptState * script_state,base::Optional<IntRect> crop_rect,const ImageBitmapOptions * options,ExceptionState & exception_state)1384 ScriptPromise HTMLCanvasElement::CreateImageBitmap(
1385     ScriptState* script_state,
1386     base::Optional<IntRect> crop_rect,
1387     const ImageBitmapOptions* options,
1388     ExceptionState& exception_state) {
1389   return ImageBitmapSource::FulfillImageBitmap(
1390       script_state, MakeGarbageCollected<ImageBitmap>(this, crop_rect, options),
1391       exception_state);
1392 }
1393 
SetOffscreenCanvasResource(scoped_refptr<CanvasResource> image,unsigned resource_id)1394 void HTMLCanvasElement::SetOffscreenCanvasResource(
1395     scoped_refptr<CanvasResource> image,
1396     unsigned resource_id) {
1397   OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(std::move(image),
1398                                                          resource_id);
1399   SetSize(OffscreenCanvasFrame()->Size());
1400   NotifyListenersCanvasChanged();
1401 }
1402 
IsOpaque() const1403 bool HTMLCanvasElement::IsOpaque() const {
1404   return context_ && !context_->CreationAttributes().alpha;
1405 }
1406 
IsSupportedInteractiveCanvasFallback(const Element & element)1407 bool HTMLCanvasElement::IsSupportedInteractiveCanvasFallback(
1408     const Element& element) {
1409   if (!element.IsDescendantOf(this))
1410     return false;
1411 
1412   // An element is a supported interactive canvas fallback element if it is one
1413   // of the following:
1414   // https://html.spec.whatwg.org/C/#supported-interactive-canvas-fallback-element
1415 
1416   // An a element that represents a hyperlink and that does not have any img
1417   // descendants.
1418   if (IsA<HTMLAnchorElement>(element))
1419     return !Traversal<HTMLImageElement>::FirstWithin(element);
1420 
1421   // A button element
1422   if (IsA<HTMLButtonElement>(element))
1423     return true;
1424 
1425   // An input element whose type attribute is in one of the Checkbox or Radio
1426   // Button states.  An input element that is a button but its type attribute is
1427   // not in the Image Button state.
1428   if (auto* input_element = DynamicTo<HTMLInputElement>(element)) {
1429     if (input_element->type() == input_type_names::kCheckbox ||
1430         input_element->type() == input_type_names::kRadio ||
1431         input_element->IsTextButton()) {
1432       return true;
1433     }
1434   }
1435 
1436   // A select element with a "multiple" attribute or with a display size greater
1437   // than 1.
1438   if (auto* select_element = DynamicTo<HTMLSelectElement>(element)) {
1439     if (select_element->IsMultiple() || select_element->size() > 1)
1440       return true;
1441   }
1442 
1443   // An option element that is in a list of options of a select element with a
1444   // "multiple" attribute or with a display size greater than 1.
1445   const auto* parent_select =
1446       IsA<HTMLOptionElement>(element)
1447           ? DynamicTo<HTMLSelectElement>(element.parentNode())
1448           : nullptr;
1449 
1450   if (parent_select &&
1451       (parent_select->IsMultiple() || parent_select->size() > 1))
1452     return true;
1453 
1454   // An element that would not be interactive content except for having the
1455   // tabindex attribute specified.
1456   if (element.FastHasAttribute(html_names::kTabindexAttr))
1457     return true;
1458 
1459   // A non-interactive table, caption, thead, tbody, tfoot, tr, td, or th
1460   // element.
1461   if (IsA<HTMLTableElement>(element) ||
1462       element.HasTagName(html_names::kCaptionTag) ||
1463       element.HasTagName(html_names::kTheadTag) ||
1464       element.HasTagName(html_names::kTbodyTag) ||
1465       element.HasTagName(html_names::kTfootTag) ||
1466       element.HasTagName(html_names::kTrTag) ||
1467       element.HasTagName(html_names::kTdTag) ||
1468       element.HasTagName(html_names::kThTag))
1469     return true;
1470 
1471   return false;
1472 }
1473 
GetControlAndIdIfHitRegionExists(const PhysicalOffset & location)1474 HitTestCanvasResult* HTMLCanvasElement::GetControlAndIdIfHitRegionExists(
1475     const PhysicalOffset& location) {
1476   if (IsRenderingContext2D())
1477     return context_->GetControlAndIdIfHitRegionExists(location);
1478   return MakeGarbageCollected<HitTestCanvasResult>(String(), nullptr);
1479 }
1480 
GetIdFromControl(const Element * element)1481 String HTMLCanvasElement::GetIdFromControl(const Element* element) {
1482   if (context_)
1483     return context_->GetIdFromControl(element);
1484   return String();
1485 }
1486 
CreateLayer()1487 void HTMLCanvasElement::CreateLayer() {
1488   DCHECK(!surface_layer_bridge_);
1489   LocalFrame* frame = GetDocument().GetFrame();
1490   // We do not design transferControlToOffscreen() for frame-less HTML canvas.
1491   if (frame) {
1492     surface_layer_bridge_ = std::make_unique<::blink::SurfaceLayerBridge>(
1493         frame->GetPage()->GetChromeClient().GetFrameSinkId(frame),
1494         ::blink::SurfaceLayerBridge::ContainsVideo::kNo, this,
1495         base::NullCallback());
1496     // Creates a placeholder layer first before Surface is created.
1497     surface_layer_bridge_->CreateSolidColorLayer();
1498     // This may cause the canvas to be composited.
1499     SetNeedsCompositingUpdate();
1500   }
1501 }
1502 
OnWebLayerUpdated()1503 void HTMLCanvasElement::OnWebLayerUpdated() {
1504   SetNeedsCompositingUpdate();
1505 }
1506 
RegisterContentsLayer(cc::Layer * layer)1507 void HTMLCanvasElement::RegisterContentsLayer(cc::Layer* layer) {
1508   OnContentsCcLayerChanged();
1509 }
1510 
UnregisterContentsLayer(cc::Layer * layer)1511 void HTMLCanvasElement::UnregisterContentsLayer(cc::Layer* layer) {
1512   OnContentsCcLayerChanged();
1513 }
1514 
GetFontSelector()1515 FontSelector* HTMLCanvasElement::GetFontSelector() {
1516   return GetDocument().GetStyleEngine().GetFontSelector();
1517 }
1518 
UpdateMemoryUsage()1519 void HTMLCanvasElement::UpdateMemoryUsage() {
1520   int non_gpu_buffer_count = 0;
1521   int gpu_buffer_count = 0;
1522 
1523   if (!IsRenderingContext2D() && !Is3d())
1524     return;
1525   if (ResourceProvider()) {
1526     non_gpu_buffer_count++;
1527     if (IsAccelerated()) {
1528       // The number of internal GPU buffers vary between one (stable
1529       // non-displayed state) and three (triple-buffered animations).
1530       // Adding 2 is a pessimistic but relevant estimate.
1531       // Note: These buffers might be allocated in GPU memory.
1532       gpu_buffer_count += 2;
1533     }
1534   }
1535 
1536   if (Is3d())
1537     non_gpu_buffer_count += context_->ExternallyAllocatedBufferCountPerPixel();
1538 
1539   const int bytes_per_pixel = ColorParams().BytesPerPixel();
1540 
1541   intptr_t gpu_memory_usage = 0;
1542   if (gpu_buffer_count) {
1543     // Switch from cpu mode to gpu mode
1544     base::CheckedNumeric<intptr_t> checked_usage =
1545         gpu_buffer_count * bytes_per_pixel;
1546     checked_usage *= width();
1547     checked_usage *= height();
1548     gpu_memory_usage =
1549         checked_usage.ValueOrDefault(std::numeric_limits<intptr_t>::max());
1550   }
1551 
1552   // Recomputation of externally memory usage computation is carried out
1553   // in all cases.
1554   base::CheckedNumeric<intptr_t> checked_usage =
1555       non_gpu_buffer_count * bytes_per_pixel;
1556   checked_usage *= width();
1557   checked_usage *= height();
1558   checked_usage += gpu_memory_usage;
1559   intptr_t externally_allocated_memory =
1560       checked_usage.ValueOrDefault(std::numeric_limits<intptr_t>::max());
1561   // Subtracting two intptr_t that are known to be positive will never
1562   // underflow.
1563   v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(
1564       externally_allocated_memory - externally_allocated_memory_);
1565   externally_allocated_memory_ = externally_allocated_memory;
1566 }
1567 
ReplaceExisting2dLayerBridge(std::unique_ptr<Canvas2DLayerBridge> new_layer_bridge)1568 void HTMLCanvasElement::ReplaceExisting2dLayerBridge(
1569     std::unique_ptr<Canvas2DLayerBridge> new_layer_bridge) {
1570   scoped_refptr<StaticBitmapImage> image;
1571   std::unique_ptr<Canvas2DLayerBridge> old_layer_bridge;
1572   if (canvas2d_bridge_) {
1573     image = canvas2d_bridge_->NewImageSnapshot();
1574     // image can be null if allocation failed in which case we should just
1575     // abort the surface switch to retain the old surface which is still
1576     // functional.
1577     if (!image)
1578       return;
1579     old_layer_bridge = std::move(canvas2d_bridge_);
1580     // Removing connection between old_layer_bridge and CanvasResourceHost;
1581     // otherwise, the CanvasResourceHost checks for the old one before
1582     // assigning the new canvas layer bridge.
1583     old_layer_bridge->SetCanvasResourceHost(nullptr);
1584   }
1585   ReplaceResourceProvider(nullptr);
1586   canvas2d_bridge_ = std::move(new_layer_bridge);
1587   canvas2d_bridge_->SetCanvasResourceHost(this);
1588 
1589   // If PaintCanvas cannot be get from the new layer bridge, revert the
1590   // replacement.
1591   cc::PaintCanvas* canvas = canvas2d_bridge_->GetPaintCanvas();
1592   if (!canvas) {
1593     if (old_layer_bridge) {
1594       canvas2d_bridge_ = std::move(old_layer_bridge);
1595       canvas2d_bridge_->SetCanvasResourceHost(this);
1596     }
1597     return;
1598   }
1599 
1600   // After validating paint canvas on new layer bridge, removes the clip from
1601   // the canvas. Since clips is automatically applied to paint canvas, the image
1602   // already contains clip and the image needs to be drawn before the clip stack
1603   // is re-applied, it needs to remove clip from canvas and restore it after the
1604   // image is drawn.
1605   canvas->restoreToCount(1);
1606   canvas->save();
1607 
1608   // TODO(jochin): Consider using ResourceProvider()->RestoreBackBuffer() here
1609   // to avoid all of this clip stack manipulation.
1610   if (image)
1611     canvas2d_bridge_->DrawFullImage(image->PaintImageForCurrentFrame());
1612 
1613   RestoreCanvasMatrixClipStack(canvas);
1614   canvas2d_bridge_->DidRestoreCanvasMatrixClipStack(canvas);
1615 
1616   UpdateMemoryUsage();
1617 }
1618 
GetOrCreateCanvasResourceProvider(RasterModeHint hint)1619 CanvasResourceProvider* HTMLCanvasElement::GetOrCreateCanvasResourceProvider(
1620     RasterModeHint hint) {
1621   if (IsRenderingContext2D())
1622     return GetOrCreateCanvas2DLayerBridge()->GetOrCreateResourceProvider();
1623 
1624   return CanvasRenderingContextHost::GetOrCreateCanvasResourceProvider(hint);
1625 }
1626 
HasImageBitmapContext() const1627 bool HTMLCanvasElement::HasImageBitmapContext() const {
1628   if (!context_)
1629     return false;
1630   CanvasRenderingContext::ContextType type = context_->GetContextType();
1631   return (type == CanvasRenderingContext::kContextImageBitmap);
1632 }
1633 
GetTransparentImage()1634 scoped_refptr<StaticBitmapImage> HTMLCanvasElement::GetTransparentImage() {
1635   if (!transparent_image_ || transparent_image_.get()->Size() != Size())
1636     transparent_image_ = CreateTransparentImage(Size());
1637   return transparent_image_;
1638 }
1639 
ContentsCcLayer() const1640 cc::Layer* HTMLCanvasElement::ContentsCcLayer() const {
1641   if (surface_layer_bridge_)
1642     return surface_layer_bridge_->GetCcLayer();
1643   if (context_ && context_->IsComposited())
1644     return context_->CcLayer();
1645   return nullptr;
1646 }
1647 
OnContentsCcLayerChanged()1648 void HTMLCanvasElement::OnContentsCcLayerChanged() {
1649   // We need to repaint the layer because the foreign layer display item may
1650   // appear, disappear or change.
1651   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
1652       GetLayoutObject() && GetLayoutObject()->HasLayer())
1653     GetLayoutBoxModelObject()->Layer()->SetNeedsRepaint();
1654 }
1655 
RespectImageOrientation() const1656 RespectImageOrientationEnum HTMLCanvasElement::RespectImageOrientation() const {
1657   return LayoutObject::ShouldRespectImageOrientation(GetLayoutObject());
1658 }
1659 
1660 }  // namespace blink
1661