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