/* * Copyright (C) 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h" #include "services/metrics/public/cpp/ukm_builders.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.h" #include "third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h" #include "third_party/blink/renderer/core/html/canvas/canvas_image_source.h" #include "third_party/blink/renderer/core/workers/worker_global_scope.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { CanvasRenderingContext::CanvasRenderingContext( CanvasRenderingContextHost* host, const CanvasContextCreationAttributesCore& attrs) : host_(host), color_params_(attrs.color_space, attrs.pixel_format, attrs.alpha), creation_attributes_(attrs) {} void CanvasRenderingContext::Dispose() { StopListeningForDidProcessTask(); // HTMLCanvasElement and CanvasRenderingContext have a circular reference. // When the pair is no longer reachable, their destruction order is non- // deterministic, so the first of the two to be destroyed needs to notify // the other in order to break the circular reference. This is to avoid // an error when CanvasRenderingContext::DidProcessTask() is invoked // after the HTMLCanvasElement is destroyed. if (Host()) { Host()->DetachContext(); host_ = nullptr; } } void CanvasRenderingContext::DidDraw(const SkIRect& dirty_rect) { Host()->DidDraw(SkRect::Make(dirty_rect)); StartListeningForDidProcessTask(); } void CanvasRenderingContext::DidDraw() { Host()->DidDraw(); StartListeningForDidProcessTask(); } void CanvasRenderingContext::DidProcessTask( const base::PendingTask& /* pending_task */) { StopListeningForDidProcessTask(); // The end of a script task that drew content to the canvas is the point // at which the current frame may be considered complete. if (Host()) Host()->PreFinalizeFrame(); FinalizeFrame(); if (Host()) Host()->PostFinalizeFrame(); } void CanvasRenderingContext::RecordUKMCanvasRenderingAPI( CanvasRenderingAPI canvasRenderingAPI) { DCHECK(Host()); const auto& ukm_params = Host()->GetUkmParameters(); if (Host()->IsOffscreenCanvas()) { ukm::builders::ClientRenderingAPI(ukm_params.source_id) .SetOffscreenCanvas_RenderingContext( static_cast(canvasRenderingAPI)) .Record(ukm_params.ukm_recorder); } else { ukm::builders::ClientRenderingAPI(ukm_params.source_id) .SetCanvas_RenderingContext(static_cast(canvasRenderingAPI)) .Record(ukm_params.ukm_recorder); } } void CanvasRenderingContext::RecordUKMCanvasDrawnToRenderingAPI( CanvasRenderingAPI canvasRenderingAPI) { DCHECK(Host()); const auto& ukm_params = Host()->GetUkmParameters(); if (Host()->IsOffscreenCanvas()) { ukm::builders::ClientRenderingAPI(ukm_params.source_id) .SetOffscreenCanvas_RenderingContextDrawnTo( static_cast(canvasRenderingAPI)) .Record(ukm_params.ukm_recorder); } else { ukm::builders::ClientRenderingAPI(ukm_params.source_id) .SetCanvas_RenderingContextDrawnTo(static_cast(canvasRenderingAPI)) .Record(ukm_params.ukm_recorder); } } CanvasRenderingContext::ContextType CanvasRenderingContext::ContextTypeFromId( const String& id) { if (id == "2d") return kContext2D; if (id == "experimental-webgl") return kContextExperimentalWebgl; if (id == "webgl") return kContextWebgl; if (id == "webgl2") return kContextWebgl2; if (id == "bitmaprenderer") return kContextImageBitmap; if (id == "gpupresent" && RuntimeEnabledFeatures::WebGPUEnabled()) return kContextGPUPresent; return kContextTypeUnknown; } CanvasRenderingContext::ContextType CanvasRenderingContext::ResolveContextTypeAliases( CanvasRenderingContext::ContextType type) { if (type == kContextExperimentalWebgl) return kContextWebgl; return type; } bool CanvasRenderingContext::WouldTaintOrigin(CanvasImageSource* image_source) { // Don't taint the canvas on data URLs. This special case is needed here // because CanvasImageSource::WouldTaintOrigin() can return false for data // URLs due to restrictions on SVG foreignObject nodes as described in // https://crbug.com/294129. // TODO(crbug.com/294129): Remove the restriction on foreignObject nodes, then // this logic isn't needed, CanvasImageSource::SourceURL() isn't needed, and // this function can just be image_source->WouldTaintOrigin(). const KURL& source_url = image_source->SourceURL(); const bool has_url = (source_url.IsValid() && !source_url.IsAboutBlankURL()); if (has_url && source_url.ProtocolIsData()) return false; return image_source->WouldTaintOrigin(); } void CanvasRenderingContext::Trace(Visitor* visitor) const { visitor->Trace(host_); ScriptWrappable::Trace(visitor); } void CanvasRenderingContext::StartListeningForDidProcessTask() { if (listening_for_did_process_task_) return; listening_for_did_process_task_ = true; Thread::Current()->AddTaskObserver(this); } void CanvasRenderingContext::StopListeningForDidProcessTask() { if (!listening_for_did_process_task_) return; Thread::Current()->RemoveTaskObserver(this); listening_for_did_process_task_ = false; } } // namespace blink