Add CanvasRenderingContext2DBase class and OffscreenCanvasRenderingContext2D
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 23:35:51 +0000 (23:35 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 23:35:51 +0000 (23:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180718
<rdar://problem/36004015>

Reviewed by Sam Weinig.

Add an OffscreenCanvasRenderingContext2D class, and in the process create a shared
base class for it and CanvasRenderingContext2D, called CanvasRenderingContext2DBase.
The base class has nearly all the functionality, with the exception of the text
and focus rendering APIs, which are only exposed on CanvasRenderingContext2D.

At the moment CanvasRenderingContext2DBase's implementation still expects the attached
canvas to be an HTMLCanvasElement, but that's ok since you can't yet create an
OffscreenCanvasRenderingContext2D. A subsequent patch will do the right thing.

No change in functionality at the moment, so covered by the existing tests.

* DerivedSources.make: Add the new IDL file.

* Sources.txt: Add all the new files to compile.
* WebCore.xcodeproj/project.pbxproj:

* bindings/js/JSCanvasRenderingContext2DCustom.cpp: Removed some unnecessary headers, and
added JSC:: where appropriate.
(WebCore::JSCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots):
(WebCore::JSCanvasRenderingContext2D::visitAdditionalChildren):

* bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp: Copied from Source/WebCore/bindings/js/JSCanvasRenderingContext2DCustom.cpp.
(WebCore::root): New root function that just returns the address of the OffscreenCanvas.
(WebCore::JSOffscreenCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots):
(WebCore::JSOffscreenCanvasRenderingContext2D::visitAdditionalChildren):

* bindings/js/JSWorkerNavigatorCustom.cpp: Add JSC:: to fix a compilation error.
(WebCore::JSWorkerNavigator::visitAdditionalChildren):

* bindings/js/WebCoreBuiltinNames.h: New IDL types.

* html/OffscreenCanvas.idl: Explicitly generates an IsReachable.

* html/canvas/CanvasRenderingContext.h:
(WebCore::CanvasRenderingContext::isOffscreen2d const): Helper for is<> trait.

* html/canvas/CanvasRenderingContext2D.cpp: Nearly everything has been moved to the Base class.
* html/canvas/CanvasRenderingContext2D.h:
* html/canvas/CanvasRenderingContext2DBase.cpp: Copied from Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp.
* html/canvas/CanvasRenderingContext2DBase.h: Copied from Source/WebCore/html/canvas/CanvasRenderingContext2D.h.

* html/canvas/OffscreenCanvasRenderingContext2D.cpp: Added. Basic implementation that
just uses the Base class.
(WebCore::OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D):
* html/canvas/OffscreenCanvasRenderingContext2D.h: Added.
* html/canvas/OffscreenCanvasRenderingContext2D.idl: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@225816 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSCanvasRenderingContext2DCustom.cpp
Source/WebCore/bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp [new file with mode: 0644]
Source/WebCore/bindings/js/JSWorkerNavigatorCustom.cpp
Source/WebCore/bindings/js/WebCoreBuiltinNames.h
Source/WebCore/html/OffscreenCanvas.idl
Source/WebCore/html/canvas/CanvasRenderingContext.h
Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp
Source/WebCore/html/canvas/CanvasRenderingContext2D.h
Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp [new file with mode: 0644]
Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h [new file with mode: 0644]
Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.cpp [new file with mode: 0644]
Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.h [new file with mode: 0644]
Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.idl [new file with mode: 0644]

index 3973f59..fd56468 100644 (file)
@@ -729,6 +729,7 @@ set(WebCore_NON_SVG_IDL_FILES
     html/canvas/CanvasUserInterface.idl
     html/canvas/ImageBitmapRenderingContext.idl
     html/canvas/ImageSmoothingQuality.idl
+    html/canvas/OffscreenCanvasRenderingContext2D.idl
     html/canvas/Path2D.idl
     html/canvas/WebGPUBuffer.idl
     html/canvas/WebGPUCommandBuffer.idl
index b891774..f3c7a8b 100644 (file)
@@ -1,3 +1,58 @@
+2017-12-12  Dean Jackson  <dino@apple.com>
+
+        Add CanvasRenderingContext2DBase class and OffscreenCanvasRenderingContext2D
+        https://bugs.webkit.org/show_bug.cgi?id=180718
+        <rdar://problem/36004015>
+
+        Reviewed by Sam Weinig.
+
+        Add an OffscreenCanvasRenderingContext2D class, and in the process create a shared
+        base class for it and CanvasRenderingContext2D, called CanvasRenderingContext2DBase.
+        The base class has nearly all the functionality, with the exception of the text
+        and focus rendering APIs, which are only exposed on CanvasRenderingContext2D.
+
+        At the moment CanvasRenderingContext2DBase's implementation still expects the attached
+        canvas to be an HTMLCanvasElement, but that's ok since you can't yet create an
+        OffscreenCanvasRenderingContext2D. A subsequent patch will do the right thing.
+
+        No change in functionality at the moment, so covered by the existing tests.
+
+        * DerivedSources.make: Add the new IDL file.
+
+        * Sources.txt: Add all the new files to compile.
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * bindings/js/JSCanvasRenderingContext2DCustom.cpp: Removed some unnecessary headers, and
+        added JSC:: where appropriate.
+        (WebCore::JSCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots):
+        (WebCore::JSCanvasRenderingContext2D::visitAdditionalChildren):
+
+        * bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp: Copied from Source/WebCore/bindings/js/JSCanvasRenderingContext2DCustom.cpp.
+        (WebCore::root): New root function that just returns the address of the OffscreenCanvas.
+        (WebCore::JSOffscreenCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots):
+        (WebCore::JSOffscreenCanvasRenderingContext2D::visitAdditionalChildren):
+
+        * bindings/js/JSWorkerNavigatorCustom.cpp: Add JSC:: to fix a compilation error.
+        (WebCore::JSWorkerNavigator::visitAdditionalChildren):
+
+        * bindings/js/WebCoreBuiltinNames.h: New IDL types.
+
+        * html/OffscreenCanvas.idl: Explicitly generates an IsReachable.
+
+        * html/canvas/CanvasRenderingContext.h:
+        (WebCore::CanvasRenderingContext::isOffscreen2d const): Helper for is<> trait.
+
+        * html/canvas/CanvasRenderingContext2D.cpp: Nearly everything has been moved to the Base class.
+        * html/canvas/CanvasRenderingContext2D.h:
+        * html/canvas/CanvasRenderingContext2DBase.cpp: Copied from Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp.
+        * html/canvas/CanvasRenderingContext2DBase.h: Copied from Source/WebCore/html/canvas/CanvasRenderingContext2D.h.
+
+        * html/canvas/OffscreenCanvasRenderingContext2D.cpp: Added. Basic implementation that
+        just uses the Base class.
+        (WebCore::OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D):
+        * html/canvas/OffscreenCanvasRenderingContext2D.h: Added.
+        * html/canvas/OffscreenCanvasRenderingContext2D.idl: Added.
+
 2017-12-12  Jer Noble  <jer.noble@apple.com>
 
         [EME] Support FPS-over-HLS in the Modern EME API
index 0f2961f..d7fd041 100644 (file)
@@ -659,6 +659,7 @@ JS_BINDING_IDLS = \
     $(WebCore)/html/canvas/OESTextureHalfFloat.idl \
     $(WebCore)/html/canvas/OESTextureHalfFloatLinear.idl \
     $(WebCore)/html/canvas/OESVertexArrayObject.idl \
+    $(WebCore)/html/canvas/OffscreenCanvasRenderingContext2D.idl \
     $(WebCore)/html/canvas/Path2D.idl \
     $(WebCore)/html/canvas/WebGL2RenderingContext.idl \
     $(WebCore)/html/canvas/WebGLActiveInfo.idl \
index 4ac8def..5a0ae99 100644 (file)
@@ -408,6 +408,7 @@ bindings/js/JSNavigatorCustom.cpp
 bindings/js/JSNodeCustom.cpp
 bindings/js/JSNodeIteratorCustom.cpp
 bindings/js/JSNodeListCustom.cpp
+bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp
 bindings/js/JSPerformanceEntryCustom.cpp
 bindings/js/JSPluginElementFunctions.cpp
 bindings/js/JSPopStateEventCustom.cpp
@@ -1058,8 +1059,10 @@ html/canvas/CanvasPath.cpp
 html/canvas/CanvasPattern.cpp
 html/canvas/CanvasRenderingContext.cpp
 html/canvas/CanvasRenderingContext2D.cpp
+html/canvas/CanvasRenderingContext2DBase.cpp
 html/canvas/CanvasStyle.cpp
 html/canvas/ImageBitmapRenderingContext.cpp
+html/canvas/OffscreenCanvasRenderingContext2D.cpp
 html/canvas/Path2D.cpp
 html/canvas/PlaceholderRenderingContext.cpp
 html/canvas/WebGPUBuffer.cpp
@@ -2666,6 +2669,7 @@ JSNotificationPermissionCallback.cpp
 JSOfflineAudioCompletionEvent.cpp
 JSOfflineAudioContext.cpp
 JSOffscreenCanvas.cpp
+JSOffscreenCanvasRenderingContext2D.cpp
 JSOscillatorNode.cpp
 JSOverconstrainedError.cpp
 JSOverconstrainedErrorEvent.cpp
index f8dedc4..833c045 100644 (file)
                3135910B1E7DDC7300F30630 /* RTCSignalingState.h in Headers */ = {isa = PBXBuildFile; fileRef = 313591051E7DDC6000F30630 /* RTCSignalingState.h */; };
                3140379B124BEA7F00AF40E4 /* WebCoreMotionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 31403797124BEA7F00AF40E4 /* WebCoreMotionManager.h */; };
                3140379D124BEA7F00AF40E4 /* DeviceOrientationClientIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 31403799124BEA7F00AF40E4 /* DeviceOrientationClientIOS.h */; };
+               3140C5201FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.h in Headers */ = {isa = PBXBuildFile; fileRef = 3140C51E1FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.h */; };
+               3140C5241FDF318700D2A873 /* CanvasRenderingContext2DBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 3140C5221FDF318600D2A873 /* CanvasRenderingContext2DBase.h */; };
+               3140C5271FDF558200D2A873 /* JSOffscreenCanvasRenderingContext2D.h in Headers */ = {isa = PBXBuildFile; fileRef = 3140C5251FDF557F00D2A873 /* JSOffscreenCanvasRenderingContext2D.h */; };
                3146FE6E184420A8001A937C /* OESTextureFloatLinear.h in Headers */ = {isa = PBXBuildFile; fileRef = 3146FE6618442087001A937C /* OESTextureFloatLinear.h */; };
                3146FE6F184420AA001A937C /* OESTextureFloatLinear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3146FE6518442087001A937C /* OESTextureFloatLinear.cpp */; };
                3146FE7518442370001A937C /* JSOESTextureFloatLinear.h in Headers */ = {isa = PBXBuildFile; fileRef = 3146FE7118442367001A937C /* JSOESTextureFloatLinear.h */; };
                31403798124BEA7F00AF40E4 /* WebCoreMotionManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCoreMotionManager.mm; sourceTree = "<group>"; };
                31403799124BEA7F00AF40E4 /* DeviceOrientationClientIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeviceOrientationClientIOS.h; sourceTree = "<group>"; };
                3140379A124BEA7F00AF40E4 /* DeviceOrientationClientIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DeviceOrientationClientIOS.mm; sourceTree = "<group>"; };
+               3140C51B1FDF13D200D2A873 /* OffscreenCanvasRenderingContext2D.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = OffscreenCanvasRenderingContext2D.idl; sourceTree = "<group>"; };
+               3140C51D1FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OffscreenCanvasRenderingContext2D.cpp; sourceTree = "<group>"; };
+               3140C51E1FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OffscreenCanvasRenderingContext2D.h; sourceTree = "<group>"; };
+               3140C5211FDF318400D2A873 /* CanvasRenderingContext2DBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CanvasRenderingContext2DBase.cpp; sourceTree = "<group>"; };
+               3140C5221FDF318600D2A873 /* CanvasRenderingContext2DBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CanvasRenderingContext2DBase.h; sourceTree = "<group>"; };
+               3140C5251FDF557F00D2A873 /* JSOffscreenCanvasRenderingContext2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSOffscreenCanvasRenderingContext2D.h; sourceTree = "<group>"; };
+               3140C5261FDF558100D2A873 /* JSOffscreenCanvasRenderingContext2D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSOffscreenCanvasRenderingContext2D.cpp; sourceTree = "<group>"; };
+               3140C52B1FE06B4900D2A873 /* JSOffscreenCanvasRenderingContext2DCustom.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSOffscreenCanvasRenderingContext2DCustom.cpp; sourceTree = "<group>"; };
                3146FE6518442087001A937C /* OESTextureFloatLinear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OESTextureFloatLinear.cpp; sourceTree = "<group>"; };
                3146FE6618442087001A937C /* OESTextureFloatLinear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OESTextureFloatLinear.h; sourceTree = "<group>"; };
                3146FE6718442087001A937C /* OESTextureFloatLinear.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OESTextureFloatLinear.idl; sourceTree = "<group>"; };
                                49484FBC102CF23C00187DD3 /* CanvasRenderingContext2D.cpp */,
                                49484FBD102CF23C00187DD3 /* CanvasRenderingContext2D.h */,
                                49484FBE102CF23C00187DD3 /* CanvasRenderingContext2D.idl */,
+                               3140C5211FDF318400D2A873 /* CanvasRenderingContext2DBase.cpp */,
+                               3140C5221FDF318600D2A873 /* CanvasRenderingContext2DBase.h */,
                                7C193BAD1F5E0EB30088F3E6 /* CanvasShadowStyles.idl */,
                                7C193BB21F5E0EB60088F3E6 /* CanvasState.idl */,
                                49484FBF102CF23C00187DD3 /* CanvasStyle.cpp */,
                                77A17A6E12F28182004E02F6 /* OESVertexArrayObject.cpp */,
                                77A17A6F12F28182004E02F6 /* OESVertexArrayObject.h */,
                                77A17A7012F28182004E02F6 /* OESVertexArrayObject.idl */,
+                               3140C51D1FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.cpp */,
+                               3140C51E1FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.h */,
+                               3140C51B1FDF13D200D2A873 /* OffscreenCanvasRenderingContext2D.idl */,
                                7C193BA71F5E0EAF0088F3E6 /* Path2D.cpp */,
                                7C193BAB1F5E0EB10088F3E6 /* Path2D.h */,
                                7C193BB41F5E0EB70088F3E6 /* Path2D.idl */,
                                1A750DD30A90E729000FF215 /* JSNodeIteratorCustom.cpp */,
                                BCD9C2610C17AA67005C90A2 /* JSNodeListCustom.cpp */,
                                AD20B18C18E9D216005A8083 /* JSNodeListCustom.h */,
+                               3140C52B1FE06B4900D2A873 /* JSOffscreenCanvasRenderingContext2DCustom.cpp */,
                                CB38FD551CD21D5B00592A3F /* JSPerformanceEntryCustom.cpp */,
                                83F572941FA1066F003837BE /* JSServiceWorkerClientCustom.cpp */,
                                460D19441FCE21DD00C3DB85 /* JSServiceWorkerGlobalScopeCustom.cpp */,
                                77A17AA312F28B2A004E02F6 /* JSOESVertexArrayObject.h */,
                                314877E51FAAB02400C05759 /* JSOffscreenCanvas.cpp */,
                                314877E41FAAB02200C05759 /* JSOffscreenCanvas.h */,
+                               3140C5261FDF558100D2A873 /* JSOffscreenCanvasRenderingContext2D.cpp */,
+                               3140C5251FDF557F00D2A873 /* JSOffscreenCanvasRenderingContext2D.h */,
                                7C193BFD1F5E10D60088F3E6 /* JSPath2D.cpp */,
                                7C193BFE1F5E10D70088F3E6 /* JSPath2D.h */,
                                B658FF9F1522EF3A00DD5595 /* JSRadioNodeList.cpp */,
                                49484FC5102CF23C00187DD3 /* CanvasPattern.h in Headers */,
                                49C7B9DD1042D32F0009D447 /* CanvasRenderingContext.h in Headers */,
                                49484FCB102CF23C00187DD3 /* CanvasRenderingContext2D.h in Headers */,
+                               3140C5241FDF318700D2A873 /* CanvasRenderingContext2DBase.h in Headers */,
                                49484FCE102CF23C00187DD3 /* CanvasStyle.h in Headers */,
                                7C193BBF1F5E0EED0088F3E6 /* CanvasTextAlign.h in Headers */,
                                7C193BC01F5E0EED0088F3E6 /* CanvasTextBaseline.h in Headers */,
                                FDF6BAF9134A4C9800822920 /* JSOfflineAudioCompletionEvent.h in Headers */,
                                FDA9326716703BA9008982DC /* JSOfflineAudioContext.h in Headers */,
                                314877E61FAAB02500C05759 /* JSOffscreenCanvas.h in Headers */,
+                               3140C5271FDF558200D2A873 /* JSOffscreenCanvasRenderingContext2D.h in Headers */,
                                57E233651DC7DB1F00F28D01 /* JsonWebKey.h in Headers */,
                                FDEA6243152102E200479DF0 /* JSOscillatorNode.h in Headers */,
                                0704A40C1D6DFC690086DCDB /* JSOverconstrainedError.h in Headers */,
                                FDA9325E16703B2A008982DC /* OfflineAudioContext.h in Headers */,
                                FDA3E95C134A49EF008D4B5A /* OfflineAudioDestinationNode.h in Headers */,
                                314877E31FAA8FE900C05759 /* OffscreenCanvas.h in Headers */,
+                               3140C5201FDF151A00D2A873 /* OffscreenCanvasRenderingContext2D.h in Headers */,
                                B2D3DA650D006CD600EF6F3A /* OpenTypeCG.h in Headers */,
                                B2D3DA650D006CD600EF6F27 /* OpenTypeMathData.h in Headers */,
                                B2D3EA650D006CD600EF6F28 /* OpenTypeTypes.h in Headers */,
index 451def1..f1018f4 100644 (file)
 #include "config.h"
 #include "JSCanvasRenderingContext2D.h"
 
-#include "CanvasGradient.h"
-#include "CanvasPattern.h"
-#include "CanvasRenderingContext2D.h"
-#include "CanvasStyle.h"
-#include "HTMLCanvasElement.h"
-#include "HTMLImageElement.h"
-#include "ImageData.h"
-#include "JSCanvasGradient.h"
-#include "JSCanvasPattern.h"
-#include "JSHTMLCanvasElement.h"
-#include "JSHTMLImageElement.h"
-#include "JSImageData.h"
-
-
 namespace WebCore {
-using namespace JSC;
 
-bool JSCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
+bool JSCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, JSC::SlotVisitor& visitor)
 {
     JSCanvasRenderingContext2D* jsCanvasRenderingContext = jsCast<JSCanvasRenderingContext2D*>(handle.slot()->asCell());
     void* root = WebCore::root(jsCanvasRenderingContext->wrapped().canvas());
     return visitor.containsOpaqueRoot(root);
 }
 
-void JSCanvasRenderingContext2D::visitAdditionalChildren(SlotVisitor& visitor)
+void JSCanvasRenderingContext2D::visitAdditionalChildren(JSC::SlotVisitor& visitor)
 {
     visitor.addOpaqueRoot(root(wrapped().canvas()));
 }
diff --git a/Source/WebCore/bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp b/Source/WebCore/bindings/js/JSOffscreenCanvasRenderingContext2DCustom.cpp
new file mode 100644 (file)
index 0000000..33b08e7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "JSOffscreenCanvasRenderingContext2D.h"
+
+namespace WebCore {
+using namespace JSC;
+
+inline void* root(OffscreenCanvas* canvas)
+{
+    return canvas;
+}
+
+bool JSOffscreenCanvasRenderingContext2DOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
+{
+    JSOffscreenCanvasRenderingContext2D* jsOffscreenCanvasRenderingContext = jsCast<JSOffscreenCanvasRenderingContext2D*>(handle.slot()->asCell());
+    void* root = WebCore::root(&jsOffscreenCanvasRenderingContext->wrapped().canvas());
+    return visitor.containsOpaqueRoot(root);
+}
+
+void JSOffscreenCanvasRenderingContext2D::visitAdditionalChildren(SlotVisitor& visitor)
+{
+    visitor.addOpaqueRoot(root(&wrapped().canvas()));
+}
+
+} // namespace WebCore
+
index 9829cdd..0a1363c 100644 (file)
@@ -28,7 +28,7 @@
 
 namespace WebCore {
 
-void JSWorkerNavigator::visitAdditionalChildren(SlotVisitor& visitor)
+void JSWorkerNavigator::visitAdditionalChildren(JSC::SlotVisitor& visitor)
 {
 #if ENABLE(SERVICE_WORKER)
     visitor.addOpaqueRoot(&wrapped().serviceWorker());
index 100e593..17dfe70 100644 (file)
@@ -98,6 +98,7 @@ namespace WebCore {
     macro(NavigatorMediaDevices) \
     macro(NavigatorUserMedia) \
     macro(OffscreenCanvas) \
+    macro(OffscreenCanvasRenderingContext2D) \
     macro(PasswordCredential) \
     macro(PaymentAddress) \
     macro(PaymentRequest) \
index 577c328..9b6f8d1 100644 (file)
@@ -40,6 +40,7 @@ enum OffscreenRenderingContextType
 [
     ConstructorCallWith=ScriptExecutionContext,
     Constructor([EnforceRange] unsigned long width, [EnforceRange] unsigned long height),
+    GenerateIsReachable=Impl,
     EnabledAtRuntime=ImageBitmapOffscreenCanvas,
     Exposed=(Window)
 ] interface OffscreenCanvas : EventTarget {
index e7d7ae3..80cd95e 100644 (file)
@@ -62,6 +62,7 @@ public:
     virtual bool isAccelerated() const { return false; }
     virtual bool isBitmapRenderer() const { return false; }
     virtual bool isPlaceholder() const { return false; }
+    virtual bool isOffscreen2d() const { return false; }
 
     virtual void paintRenderingResultsToCanvas() {}
     virtual PlatformLayer* platformLayer() const { return 0; }
index 44e6109..aecf56b 100644 (file)
 #include "config.h"
 #include "CanvasRenderingContext2D.h"
 
-#include "BitmapImage.h"
 #include "CSSFontSelector.h"
 #include "CSSParser.h"
 #include "CSSPropertyNames.h"
-#include "CachedImage.h"
-#include "CanvasGradient.h"
-#include "CanvasPattern.h"
-#include "DOMMatrix.h"
-#include "DOMMatrix2DInit.h"
-#include "DisplayListRecorder.h"
-#include "DisplayListReplayer.h"
 #include "FloatQuad.h"
 #include "HTMLImageElement.h"
 #include "HTMLVideoElement.h"
@@ -75,2110 +67,12 @@ namespace WebCore {
 
 using namespace HTMLNames;
 
-#if USE(CG)
-const ImageSmoothingQuality defaultSmoothingQuality = ImageSmoothingQuality::Low;
-#else
-const ImageSmoothingQuality defaultSmoothingQuality = ImageSmoothingQuality::Medium;
-#endif
-
-static const int defaultFontSize = 10;
-static const char* const defaultFontFamily = "sans-serif";
-static const char* const defaultFont = "10px sans-serif";
-
-struct DisplayListDrawingContext {
-    WTF_MAKE_FAST_ALLOCATED;
-public:
-    GraphicsContext context;
-    DisplayList::DisplayList displayList;
-    
-    DisplayListDrawingContext(const FloatRect& clip)
-        : context([&](GraphicsContext& context) {
-            return std::make_unique<DisplayList::Recorder>(context, displayList, clip, AffineTransform());
-        })
-    {
-    }
-};
-
-typedef HashMap<const CanvasRenderingContext2D*, std::unique_ptr<DisplayList::DisplayList>> ContextDisplayListHashMap;
-
-static ContextDisplayListHashMap& contextDisplayListMap()
-{
-    static NeverDestroyed<ContextDisplayListHashMap> sharedHashMap;
-    return sharedHashMap;
-}
-
-class CanvasStrokeStyleApplier : public StrokeStyleApplier {
-public:
-    CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
-        : m_canvasContext(canvasContext)
-    {
-    }
-
-    void strokeStyle(GraphicsContext* c) override
-    {
-        c->setStrokeThickness(m_canvasContext->lineWidth());
-        c->setLineCap(m_canvasContext->getLineCap());
-        c->setLineJoin(m_canvasContext->getLineJoin());
-        c->setMiterLimit(m_canvasContext->miterLimit());
-        const Vector<float>& lineDash = m_canvasContext->getLineDash();
-        DashArray convertedLineDash(lineDash.size());
-        for (size_t i = 0; i < lineDash.size(); ++i)
-            convertedLineDash[i] = static_cast<DashArrayElement>(lineDash[i]);
-        c->setLineDash(convertedLineDash, m_canvasContext->lineDashOffset());
-    }
-
-private:
-    CanvasRenderingContext2D* m_canvasContext;
-};
-
-CanvasRenderingContext2D::CanvasRenderingContext2D(CanvasBase& canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
-    : CanvasRenderingContext(canvas)
-    , m_stateStack(1)
-    , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
-#if ENABLE(DASHBOARD_SUPPORT)
-    , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
-#endif
-{
-#if !ENABLE(DASHBOARD_SUPPORT)
-    ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode);
-#endif
-}
-
-void CanvasRenderingContext2D::unwindStateStack()
-{
-    // Ensure that the state stack in the ImageBuffer's context
-    // is cleared before destruction, to avoid assertions in the
-    // GraphicsContext dtor.
-    if (size_t stackSize = m_stateStack.size()) {
-        if (GraphicsContext* context = canvas().existingDrawingContext()) {
-            while (--stackSize)
-                context->restore();
-        }
-    }
-}
-
-CanvasRenderingContext2D::~CanvasRenderingContext2D()
-{
-#if !ASSERT_DISABLED
-    unwindStateStack();
-#endif
-
-    if (UNLIKELY(tracksDisplayListReplay()))
-        contextDisplayListMap().remove(this);
-}
-
-bool CanvasRenderingContext2D::isAccelerated() const
-{
-#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
-    if (!canvas().hasCreatedImageBuffer())
-        return false;
-    auto* context = drawingContext();
-    return context && context->isAcceleratedContext();
-#else
-    return false;
-#endif
-}
-
-void CanvasRenderingContext2D::reset()
-{
-    unwindStateStack();
-    m_stateStack.resize(1);
-    m_stateStack.first() = State();
-    m_path.clear();
-    m_unrealizedSaveCount = 0;
-    
-    m_recordingContext = nullptr;
-}
-
-CanvasRenderingContext2D::State::State()
-    : strokeStyle(Color::black)
-    , fillStyle(Color::black)
-    , lineWidth(1)
-    , lineCap(ButtCap)
-    , lineJoin(MiterJoin)
-    , miterLimit(10)
-    , shadowBlur(0)
-    , shadowColor(Color::transparent)
-    , globalAlpha(1)
-    , globalComposite(CompositeSourceOver)
-    , globalBlend(BlendModeNormal)
-    , hasInvertibleTransform(true)
-    , lineDashOffset(0)
-    , imageSmoothingEnabled(true)
-    , imageSmoothingQuality(defaultSmoothingQuality)
-    , textAlign(StartTextAlign)
-    , textBaseline(AlphabeticTextBaseline)
-    , direction(Direction::Inherit)
-    , unparsedFont(defaultFont)
-{
-}
-
-CanvasRenderingContext2D::State::State(const State& other)
-    : unparsedStrokeColor(other.unparsedStrokeColor)
-    , unparsedFillColor(other.unparsedFillColor)
-    , strokeStyle(other.strokeStyle)
-    , fillStyle(other.fillStyle)
-    , lineWidth(other.lineWidth)
-    , lineCap(other.lineCap)
-    , lineJoin(other.lineJoin)
-    , miterLimit(other.miterLimit)
-    , shadowOffset(other.shadowOffset)
-    , shadowBlur(other.shadowBlur)
-    , shadowColor(other.shadowColor)
-    , globalAlpha(other.globalAlpha)
-    , globalComposite(other.globalComposite)
-    , globalBlend(other.globalBlend)
-    , transform(other.transform)
-    , hasInvertibleTransform(other.hasInvertibleTransform)
-    , lineDashOffset(other.lineDashOffset)
-    , imageSmoothingEnabled(other.imageSmoothingEnabled)
-    , imageSmoothingQuality(other.imageSmoothingQuality)
-    , textAlign(other.textAlign)
-    , textBaseline(other.textBaseline)
-    , direction(other.direction)
-    , unparsedFont(other.unparsedFont)
-    , font(other.font)
-{
-}
-
-CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
-{
-    if (this == &other)
-        return *this;
-
-    unparsedStrokeColor = other.unparsedStrokeColor;
-    unparsedFillColor = other.unparsedFillColor;
-    strokeStyle = other.strokeStyle;
-    fillStyle = other.fillStyle;
-    lineWidth = other.lineWidth;
-    lineCap = other.lineCap;
-    lineJoin = other.lineJoin;
-    miterLimit = other.miterLimit;
-    shadowOffset = other.shadowOffset;
-    shadowBlur = other.shadowBlur;
-    shadowColor = other.shadowColor;
-    globalAlpha = other.globalAlpha;
-    globalComposite = other.globalComposite;
-    globalBlend = other.globalBlend;
-    transform = other.transform;
-    hasInvertibleTransform = other.hasInvertibleTransform;
-    imageSmoothingEnabled = other.imageSmoothingEnabled;
-    imageSmoothingQuality = other.imageSmoothingQuality;
-    textAlign = other.textAlign;
-    textBaseline = other.textBaseline;
-    direction = other.direction;
-    unparsedFont = other.unparsedFont;
-    font = other.font;
-
-    return *this;
-}
-
-CanvasRenderingContext2D::FontProxy::~FontProxy()
-{
-    if (realized())
-        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
-}
-
-CanvasRenderingContext2D::FontProxy::FontProxy(const FontProxy& other)
-    : m_font(other.m_font)
-{
-    if (realized())
-        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
-}
-
-auto CanvasRenderingContext2D::FontProxy::operator=(const FontProxy& other) -> FontProxy&
-{
-    if (realized())
-        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
-
-    m_font = other.m_font;
-
-    if (realized())
-        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
-
-    return *this;
-}
-
-inline void CanvasRenderingContext2D::FontProxy::update(FontSelector& selector)
-{
-    ASSERT(&selector == m_font.fontSelector()); // This is an invariant. We should only ever be registered for callbacks on m_font.m_fonts.m_fontSelector.
-    if (realized())
-        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
-    m_font.update(&selector);
-    if (realized())
-        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
-    ASSERT(&selector == m_font.fontSelector());
-}
-
-void CanvasRenderingContext2D::FontProxy::fontsNeedUpdate(FontSelector& selector)
-{
-    ASSERT_ARG(selector, &selector == m_font.fontSelector());
-    ASSERT(realized());
-
-    update(selector);
-}
-
-inline void CanvasRenderingContext2D::FontProxy::initialize(FontSelector& fontSelector, const RenderStyle& newStyle)
-{
-    // Beware! m_font.fontSelector() might not point to document.fontSelector()!
-    ASSERT(newStyle.fontCascade().fontSelector() == &fontSelector);
-    if (realized())
-        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
-    m_font = newStyle.fontCascade();
-    m_font.update(&fontSelector);
-    ASSERT(&fontSelector == m_font.fontSelector());
-    m_font.fontSelector()->registerForInvalidationCallbacks(*this);
-}
-
-inline const FontMetrics& CanvasRenderingContext2D::FontProxy::fontMetrics() const
-{
-    return m_font.fontMetrics();
-}
-
-inline const FontCascadeDescription& CanvasRenderingContext2D::FontProxy::fontDescription() const
-{
-    return m_font.fontDescription();
-}
-
-inline float CanvasRenderingContext2D::FontProxy::width(const TextRun& textRun, GlyphOverflow* overflow) const
-{
-    return m_font.width(textRun, 0, overflow);
-}
-
-inline void CanvasRenderingContext2D::FontProxy::drawBidiText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction action) const
-{
-    context.drawBidiText(m_font, run, point, action);
-}
-
-void CanvasRenderingContext2D::realizeSaves()
-{
-    if (m_unrealizedSaveCount)
-        realizeSavesLoop();
-
-    if (m_unrealizedSaveCount) {
-        static NeverDestroyed<String> consoleMessage(MAKE_STATIC_STRING_IMPL("CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save()."));
-        canvas().document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage);
-    }
-}
-
-void CanvasRenderingContext2D::realizeSavesLoop()
-{
-    ASSERT(m_unrealizedSaveCount);
-    ASSERT(m_stateStack.size() >= 1);
-    GraphicsContext* context = drawingContext();
-    do {
-        if (m_stateStack.size() > MaxSaveCount)
-            break;
-        m_stateStack.append(state());
-        if (context)
-            context->save();
-    } while (--m_unrealizedSaveCount);
-}
-
-void CanvasRenderingContext2D::restore()
-{
-    if (m_unrealizedSaveCount) {
-        --m_unrealizedSaveCount;
-        return;
-    }
-    ASSERT(m_stateStack.size() >= 1);
-    if (m_stateStack.size() <= 1)
-        return;
-    m_path.transform(state().transform);
-    m_stateStack.removeLast();
-    if (std::optional<AffineTransform> inverse = state().transform.inverse())
-        m_path.transform(inverse.value());
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->restore();
-}
-
-void CanvasRenderingContext2D::setStrokeStyle(CanvasStyle style)
-{
-    if (!style.isValid())
-        return;
-
-    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentColor(style))
-        return;
-
-    if (style.isCurrentColor()) {
-        if (style.hasOverrideAlpha()) {
-            // FIXME: Should not use RGBA32 here.
-            style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas()).rgb(), style.overrideAlpha()));
-        } else
-            style = CanvasStyle(currentColor(&canvas()));
-    } else
-        checkOrigin(style.canvasPattern().get());
-
-    realizeSaves();
-    State& state = modifiableState();
-    state.strokeStyle = style;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    state.strokeStyle.applyStrokeColor(*c);
-    state.unparsedStrokeColor = String();
-}
-
-void CanvasRenderingContext2D::setFillStyle(CanvasStyle style)
-{
-    if (!style.isValid())
-        return;
-
-    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentColor(style))
-        return;
-
-    if (style.isCurrentColor()) {
-        if (style.hasOverrideAlpha()) {
-            // FIXME: Should not use RGBA32 here.
-            style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas()).rgb(), style.overrideAlpha()));
-        } else
-            style = CanvasStyle(currentColor(&canvas()));
-    } else
-        checkOrigin(style.canvasPattern().get());
-
-    realizeSaves();
-    State& state = modifiableState();
-    state.fillStyle = style;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    state.fillStyle.applyFillColor(*c);
-    state.unparsedFillColor = String();
-}
-
-float CanvasRenderingContext2D::lineWidth() const
-{
-    return state().lineWidth;
-}
-
-void CanvasRenderingContext2D::setLineWidth(float width)
-{
-    if (!(std::isfinite(width) && width > 0))
-        return;
-    if (state().lineWidth == width)
-        return;
-    realizeSaves();
-    modifiableState().lineWidth = width;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setStrokeThickness(width);
-}
-
-static CanvasLineCap toCanvasLineCap(LineCap lineCap)
-{
-    switch (lineCap) {
-    case ButtCap:
-        return CanvasLineCap::Butt;
-    case RoundCap:
-        return CanvasLineCap::Round;
-    case SquareCap:
-        return CanvasLineCap::Square;
-    }
-
-    ASSERT_NOT_REACHED();
-    return CanvasLineCap::Butt;
-}
-
-static LineCap fromCanvasLineCap(CanvasLineCap canvasLineCap)
-{
-    switch (canvasLineCap) {
-    case CanvasLineCap::Butt:
-        return ButtCap;
-    case CanvasLineCap::Round:
-        return RoundCap;
-    case CanvasLineCap::Square:
-        return SquareCap;
-    }
-    
-    ASSERT_NOT_REACHED();
-    return ButtCap;
-}
-
-CanvasLineCap CanvasRenderingContext2D::lineCap() const
-{
-    return toCanvasLineCap(state().lineCap);
-}
-
-void CanvasRenderingContext2D::setLineCap(CanvasLineCap canvasLineCap)
-{
-    auto lineCap = fromCanvasLineCap(canvasLineCap);
-    if (state().lineCap == lineCap)
-        return;
-    realizeSaves();
-    modifiableState().lineCap = lineCap;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setLineCap(lineCap);
-}
-
-void CanvasRenderingContext2D::setLineCap(const String& stringValue)
-{
-    CanvasLineCap cap;
-    if (stringValue == "butt")
-        cap = CanvasLineCap::Butt;
-    else if (stringValue == "round")
-        cap = CanvasLineCap::Round;
-    else if (stringValue == "square")
-        cap = CanvasLineCap::Square;
-    else
-        return;
-    
-    setLineCap(cap);
-}
-
-static CanvasLineJoin toCanvasLineJoin(LineJoin lineJoin)
-{
-    switch (lineJoin) {
-    case RoundJoin:
-        return CanvasLineJoin::Round;
-    case BevelJoin:
-        return CanvasLineJoin::Bevel;
-    case MiterJoin:
-        return CanvasLineJoin::Miter;
-    }
-
-    ASSERT_NOT_REACHED();
-    return CanvasLineJoin::Round;
-}
-
-static LineJoin fromCanvasLineJoin(CanvasLineJoin canvasLineJoin)
-{
-    switch (canvasLineJoin) {
-    case CanvasLineJoin::Round:
-        return RoundJoin;
-    case CanvasLineJoin::Bevel:
-        return BevelJoin;
-    case CanvasLineJoin::Miter:
-        return MiterJoin;
-    }
-    
-    ASSERT_NOT_REACHED();
-    return RoundJoin;
-}
-
-CanvasLineJoin CanvasRenderingContext2D::lineJoin() const
-{
-    return toCanvasLineJoin(state().lineJoin);
-}
-
-void CanvasRenderingContext2D::setLineJoin(CanvasLineJoin canvasLineJoin)
-{
-    auto lineJoin = fromCanvasLineJoin(canvasLineJoin);
-    if (state().lineJoin == lineJoin)
-        return;
-    realizeSaves();
-    modifiableState().lineJoin = lineJoin;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setLineJoin(lineJoin);
-}
-
-void CanvasRenderingContext2D::setLineJoin(const String& stringValue)
-{
-    CanvasLineJoin join;
-    if (stringValue == "round")
-        join = CanvasLineJoin::Round;
-    else if (stringValue == "bevel")
-        join = CanvasLineJoin::Bevel;
-    else if (stringValue == "miter")
-        join = CanvasLineJoin::Miter;
-    else
-        return;
-
-    setLineJoin(join);
-}
-
-float CanvasRenderingContext2D::miterLimit() const
-{
-    return state().miterLimit;
-}
-
-void CanvasRenderingContext2D::setMiterLimit(float limit)
-{
-    if (!(std::isfinite(limit) && limit > 0))
-        return;
-    if (state().miterLimit == limit)
-        return;
-    realizeSaves();
-    modifiableState().miterLimit = limit;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setMiterLimit(limit);
-}
-
-float CanvasRenderingContext2D::shadowOffsetX() const
-{
-    return state().shadowOffset.width();
-}
-
-void CanvasRenderingContext2D::setShadowOffsetX(float x)
-{
-    if (!std::isfinite(x))
-        return;
-    if (state().shadowOffset.width() == x)
-        return;
-    realizeSaves();
-    modifiableState().shadowOffset.setWidth(x);
-    applyShadow();
-}
-
-float CanvasRenderingContext2D::shadowOffsetY() const
-{
-    return state().shadowOffset.height();
-}
-
-void CanvasRenderingContext2D::setShadowOffsetY(float y)
-{
-    if (!std::isfinite(y))
-        return;
-    if (state().shadowOffset.height() == y)
-        return;
-    realizeSaves();
-    modifiableState().shadowOffset.setHeight(y);
-    applyShadow();
-}
-
-float CanvasRenderingContext2D::shadowBlur() const
-{
-    return state().shadowBlur;
-}
-
-void CanvasRenderingContext2D::setShadowBlur(float blur)
-{
-    if (!(std::isfinite(blur) && blur >= 0))
-        return;
-    if (state().shadowBlur == blur)
-        return;
-    realizeSaves();
-    modifiableState().shadowBlur = blur;
-    applyShadow();
-}
-
-String CanvasRenderingContext2D::shadowColor() const
-{
-    return Color(state().shadowColor).serialized();
-}
-
-void CanvasRenderingContext2D::setShadowColor(const String& colorString)
-{
-    Color color = parseColorOrCurrentColor(colorString, &canvas());
-    if (!color.isValid())
-        return;
-    if (state().shadowColor == color)
-        return;
-    realizeSaves();
-    modifiableState().shadowColor = color;
-    applyShadow();
-}
-
-const Vector<float>& CanvasRenderingContext2D::getLineDash() const
-{
-    return state().lineDash;
-}
-
-static bool lineDashSequenceIsValid(const Vector<float>& dash)
-{
-    for (size_t i = 0; i < dash.size(); i++) {
-        if (!std::isfinite(dash[i]) || dash[i] < 0)
-            return false;
-    }
-    return true;
-}
-
-void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
-{
-    if (!lineDashSequenceIsValid(dash))
-        return;
-
-    realizeSaves();
-    modifiableState().lineDash = dash;
-    // Spec requires the concatenation of two copies the dash list when the
-    // number of elements is odd
-    if (dash.size() % 2)
-        modifiableState().lineDash.appendVector(dash);
-
-    applyLineDash();
-}
-
-void CanvasRenderingContext2D::setWebkitLineDash(const Vector<float>& dash)
-{
-    if (!lineDashSequenceIsValid(dash))
-        return;
-
-    realizeSaves();
-    modifiableState().lineDash = dash;
-
-    applyLineDash();
-}
-
-float CanvasRenderingContext2D::lineDashOffset() const
-{
-    return state().lineDashOffset;
-}
-
-void CanvasRenderingContext2D::setLineDashOffset(float offset)
-{
-    if (!std::isfinite(offset) || state().lineDashOffset == offset)
-        return;
-
-    realizeSaves();
-    modifiableState().lineDashOffset = offset;
-    applyLineDash();
-}
-
-void CanvasRenderingContext2D::applyLineDash() const
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    DashArray convertedLineDash(state().lineDash.size());
-    for (size_t i = 0; i < state().lineDash.size(); ++i)
-        convertedLineDash[i] = static_cast<DashArrayElement>(state().lineDash[i]);
-    c->setLineDash(convertedLineDash, state().lineDashOffset);
-}
-
-float CanvasRenderingContext2D::globalAlpha() const
-{
-    return state().globalAlpha;
-}
-
-void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
-{
-    if (!(alpha >= 0 && alpha <= 1))
-        return;
-    if (state().globalAlpha == alpha)
-        return;
-    realizeSaves();
-    modifiableState().globalAlpha = alpha;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setAlpha(alpha);
-}
-
-String CanvasRenderingContext2D::globalCompositeOperation() const
-{
-    return compositeOperatorName(state().globalComposite, state().globalBlend);
-}
-
-void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
-{
-    CompositeOperator op = CompositeSourceOver;
-    BlendMode blendMode = BlendModeNormal;
-    if (!parseCompositeAndBlendOperator(operation, op, blendMode))
-        return;
-    if ((state().globalComposite == op) && (state().globalBlend == blendMode))
-        return;
-    realizeSaves();
-    modifiableState().globalComposite = op;
-    modifiableState().globalBlend = blendMode;
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    c->setCompositeOperation(op, blendMode);
-}
-
-void CanvasRenderingContext2D::scale(float sx, float sy)
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    if (!std::isfinite(sx) || !std::isfinite(sy))
-        return;
-
-    AffineTransform newTransform = state().transform;
-    newTransform.scaleNonUniform(sx, sy);
-    if (state().transform == newTransform)
-        return;
-
-    realizeSaves();
-
-    if (!sx || !sy) {
-        modifiableState().hasInvertibleTransform = false;
-        return;
-    }
-
-    modifiableState().transform = newTransform;
-    c->scale(FloatSize(sx, sy));
-    m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
-}
-
-void CanvasRenderingContext2D::rotate(float angleInRadians)
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    if (!std::isfinite(angleInRadians))
-        return;
-
-    AffineTransform newTransform = state().transform;
-    newTransform.rotate(angleInRadians / piDouble * 180.0);
-    if (state().transform == newTransform)
-        return;
-
-    realizeSaves();
-
-    modifiableState().transform = newTransform;
-    c->rotate(angleInRadians);
-    m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
-}
-
-void CanvasRenderingContext2D::translate(float tx, float ty)
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    if (!std::isfinite(tx) | !std::isfinite(ty))
-        return;
-
-    AffineTransform newTransform = state().transform;
-    newTransform.translate(tx, ty);
-    if (state().transform == newTransform)
-        return;
-
-    realizeSaves();
-
-    modifiableState().transform = newTransform;
-    c->translate(tx, ty);
-    m_path.transform(AffineTransform().translate(-tx, -ty));
-}
-
-void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
-        return;
-
-    AffineTransform transform(m11, m12, m21, m22, dx, dy);
-    AffineTransform newTransform = state().transform * transform;
-    if (state().transform == newTransform)
-        return;
-
-    realizeSaves();
-
-    if (auto inverse = transform.inverse()) {
-        modifiableState().transform = newTransform;
-        c->concatCTM(transform);
-        m_path.transform(inverse.value());
-        return;
-    }
-    modifiableState().hasInvertibleTransform = false;
-}
-
-Ref<DOMMatrix> CanvasRenderingContext2D::getTransform() const
-{
-    return DOMMatrix::create(state().transform.toTransformationMatrix(), DOMMatrixReadOnly::Is2D::Yes);
-}
-
-void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-
-    if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
-        return;
-
-    resetTransform();
-    transform(m11, m12, m21, m22, dx, dy);
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::setTransform(DOMMatrix2DInit&& matrixInit)
-{
-    auto checkValid = DOMMatrixReadOnly::validateAndFixup(matrixInit);
-    if (checkValid.hasException())
-        return checkValid.releaseException();
-
-    setTransform(matrixInit.a.value_or(1), matrixInit.b.value_or(0), matrixInit.c.value_or(0), matrixInit.d.value_or(1), matrixInit.e.value_or(0), matrixInit.f.value_or(0));
-    return { };
-}
-
-void CanvasRenderingContext2D::resetTransform()
-{
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-
-    AffineTransform ctm = state().transform;
-    bool hasInvertibleTransform = state().hasInvertibleTransform;
-
-    realizeSaves();
-
-    c->setCTM(canvas().baseTransform());
-    modifiableState().transform = AffineTransform();
-
-    if (hasInvertibleTransform)
-        m_path.transform(ctm);
-
-    modifiableState().hasInvertibleTransform = true;
-}
-
-void CanvasRenderingContext2D::setStrokeColor(const String& color, std::optional<float> alpha)
-{
-    if (alpha) {
-        setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
-        return;
-    }
-
-    if (color == state().unparsedStrokeColor)
-        return;
-
-    realizeSaves();
-    setStrokeStyle(CanvasStyle::createFromString(color));
-    modifiableState().unparsedStrokeColor = color;
-}
-
-void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
-{
-    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
-        return;
-    setStrokeStyle(CanvasStyle(grayLevel, alpha));
-}
-
-void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
-{
-    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(r, g, b, a))
-        return;
-    setStrokeStyle(CanvasStyle(r, g, b, a));
-}
-
-void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
-{
-    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentCMYKA(c, m, y, k, a))
-        return;
-    setStrokeStyle(CanvasStyle(c, m, y, k, a));
-}
-
-void CanvasRenderingContext2D::setFillColor(const String& color, std::optional<float> alpha)
-{
-    if (alpha) {
-        setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
-        return;
-    }
-
-    if (color == state().unparsedFillColor)
-        return;
-
-    realizeSaves();
-    setFillStyle(CanvasStyle::createFromString(color));
-    modifiableState().unparsedFillColor = color;
-}
-
-void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
-{
-    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
-        return;
-    setFillStyle(CanvasStyle(grayLevel, alpha));
-}
-
-void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
-{
-    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(r, g, b, a))
-        return;
-    setFillStyle(CanvasStyle(r, g, b, a));
-}
-
-void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
-{
-    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentCMYKA(c, m, y, k, a))
-        return;
-    setFillStyle(CanvasStyle(c, m, y, k, a));
-}
-
-void CanvasRenderingContext2D::beginPath()
-{
-    m_path.clear();
-}
-
-static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
-{
-    if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
-        return false;
-
-    if (!width && !height)
-        return false;
-
-    if (width < 0) {
-        width = -width;
-        x -= width;
-    }
-
-    if (height < 0) {
-        height = -height;
-        y -= height;
-    }
-
-    return true;
-}
-
-inline void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
-{
-#if ENABLE(DASHBOARD_SUPPORT)
-    if (m_usesDashboardCompatibilityMode)
-        m_path.clear();
-#endif
-}
-
-static bool isFullCanvasCompositeMode(CompositeOperator op)
-{
-    // See 4.8.11.1.3 Compositing
-    // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
-    // implement the specification's behavior.
-    return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
-}
-
-static WindRule toWindRule(CanvasFillRule rule)
-{
-    return rule == CanvasFillRule::Nonzero ? RULE_NONZERO : RULE_EVENODD;
-}
-
-void CanvasRenderingContext2D::fill(CanvasFillRule windingRule)
-{
-    fillInternal(m_path, windingRule);
-    clearPathForDashboardBackwardCompatibilityMode();
-}
-
-void CanvasRenderingContext2D::stroke()
-{
-    strokeInternal(m_path);
-    clearPathForDashboardBackwardCompatibilityMode();
-}
-
-void CanvasRenderingContext2D::clip(CanvasFillRule windingRule)
-{
-    clipInternal(m_path, windingRule);
-    clearPathForDashboardBackwardCompatibilityMode();
-}
-
-void CanvasRenderingContext2D::fill(Path2D& path, CanvasFillRule windingRule)
-{
-    fillInternal(path.path(), windingRule);
-}
-
-void CanvasRenderingContext2D::stroke(Path2D& path)
-{
-    strokeInternal(path.path());
-}
-
-void CanvasRenderingContext2D::clip(Path2D& path, CanvasFillRule windingRule)
-{
-    clipInternal(path.path(), windingRule);
-}
-
-void CanvasRenderingContext2D::fillInternal(const Path& path, CanvasFillRule windingRule)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    // If gradient size is zero, then paint nothing.
-    auto gradient = c->fillGradient();
-    if (gradient && gradient->isZeroSize())
-        return;
-
-    if (!path.isEmpty()) {
-        auto savedFillRule = c->fillRule();
-        c->setFillRule(toWindRule(windingRule));
-
-        if (isFullCanvasCompositeMode(state().globalComposite)) {
-            beginCompositeLayer();
-            c->fillPath(path);
-            endCompositeLayer();
-            didDrawEntireCanvas();
-        } else if (state().globalComposite == CompositeCopy) {
-            clearCanvas();
-            c->fillPath(path);
-            didDrawEntireCanvas();
-        } else {
-            c->fillPath(path);
-            didDraw(path.fastBoundingRect());
-        }
-        
-        c->setFillRule(savedFillRule);
-    }
-}
-
-void CanvasRenderingContext2D::strokeInternal(const Path& path)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    // If gradient size is zero, then paint nothing.
-    auto gradient = c->strokeGradient();
-    if (gradient && gradient->isZeroSize())
-        return;
-
-    if (!path.isEmpty()) {
-        if (isFullCanvasCompositeMode(state().globalComposite)) {
-            beginCompositeLayer();
-            c->strokePath(path);
-            endCompositeLayer();
-            didDrawEntireCanvas();
-        } else if (state().globalComposite == CompositeCopy) {
-            clearCanvas();
-            c->strokePath(path);
-            didDrawEntireCanvas();
-        } else {
-            FloatRect dirtyRect = path.fastBoundingRect();
-            inflateStrokeRect(dirtyRect);
-            c->strokePath(path);
-            didDraw(dirtyRect);
-        }
-    }
-}
-
-void CanvasRenderingContext2D::clipInternal(const Path& path, CanvasFillRule windingRule)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    realizeSaves();
-    c->canvasClip(path, toWindRule(windingRule));
-}
-
-inline void CanvasRenderingContext2D::beginCompositeLayer()
-{
-#if !USE(CAIRO)
-    drawingContext()->beginTransparencyLayer(1);
-#endif
-}
-
-inline void CanvasRenderingContext2D::endCompositeLayer()
-{
-#if !USE(CAIRO)
-    drawingContext()->endTransparencyLayer();    
-#endif
-}
-
-bool CanvasRenderingContext2D::isPointInPath(float x, float y, CanvasFillRule windingRule)
-{
-    return isPointInPathInternal(m_path, x, y, windingRule);
-}
-
-bool CanvasRenderingContext2D::isPointInStroke(float x, float y)
-{
-    return isPointInStrokeInternal(m_path, x, y);
-}
-
-bool CanvasRenderingContext2D::isPointInPath(Path2D& path, float x, float y, CanvasFillRule windingRule)
-{
-    return isPointInPathInternal(path.path(), x, y, windingRule);
-}
-
-bool CanvasRenderingContext2D::isPointInStroke(Path2D& path, float x, float y)
-{
-    return isPointInStrokeInternal(path.path(), x, y);
-}
-
-bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, float x, float y, CanvasFillRule windingRule)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return false;
-    if (!state().hasInvertibleTransform)
-        return false;
-
-    auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
-
-    if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
-        return false;
-
-    return path.contains(transformedPoint, toWindRule(windingRule));
-}
-
-bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, float x, float y)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return false;
-    if (!state().hasInvertibleTransform)
-        return false;
-
-    auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
-    if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
-        return false;
-
-    CanvasStrokeStyleApplier applier(this);
-    return path.strokeContains(&applier, transformedPoint);
-}
-
-void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
-{
-    if (!validateRectForCanvas(x, y, width, height))
-        return;
-    auto* context = drawingContext();
-    if (!context)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-    FloatRect rect(x, y, width, height);
-
-    bool saved = false;
-    if (shouldDrawShadows()) {
-        context->save();
-        saved = true;
-        context->setLegacyShadow(FloatSize(), 0, Color::transparent);
-    }
-    if (state().globalAlpha != 1) {
-        if (!saved) {
-            context->save();
-            saved = true;
-        }
-        context->setAlpha(1);
-    }
-    if (state().globalComposite != CompositeSourceOver) {
-        if (!saved) {
-            context->save();
-            saved = true;
-        }
-        context->setCompositeOperation(CompositeSourceOver);
-    }
-    context->clearRect(rect);
-    if (saved)
-        context->restore();
-    didDraw(rect);
-}
-
-void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
-{
-    if (!validateRectForCanvas(x, y, width, height))
-        return;
-
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-    // from the HTML5 Canvas spec:
-    // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
-    // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
-    auto gradient = c->fillGradient();
-    if (gradient && gradient->isZeroSize())
-        return;
-
-    FloatRect rect(x, y, width, height);
-
-    if (rectContainsCanvas(rect)) {
-        c->fillRect(rect);
-        didDrawEntireCanvas();
-    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
-        beginCompositeLayer();
-        c->fillRect(rect);
-        endCompositeLayer();
-        didDrawEntireCanvas();
-    } else if (state().globalComposite == CompositeCopy) {
-        clearCanvas();
-        c->fillRect(rect);
-        didDrawEntireCanvas();
-    } else {
-        c->fillRect(rect);
-        didDraw(rect);
-    }
-}
-
-void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
-{
-    if (!validateRectForCanvas(x, y, width, height))
-        return;
-
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-    if (!(state().lineWidth >= 0))
-        return;
-
-    // If gradient size is zero, then paint nothing.
-    auto gradient = c->strokeGradient();
-    if (gradient && gradient->isZeroSize())
-        return;
-
-    FloatRect rect(x, y, width, height);
-    if (isFullCanvasCompositeMode(state().globalComposite)) {
-        beginCompositeLayer();
-        c->strokeRect(rect, state().lineWidth);
-        endCompositeLayer();
-        didDrawEntireCanvas();
-    } else if (state().globalComposite == CompositeCopy) {
-        clearCanvas();
-        c->strokeRect(rect, state().lineWidth);
-        didDrawEntireCanvas();
-    } else {
-        FloatRect boundingRect = rect;
-        boundingRect.inflate(state().lineWidth / 2);
-        c->strokeRect(rect, state().lineWidth);
-        didDraw(boundingRect);
-    }
-}
-
-void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& colorString, std::optional<float> alpha)
-{
-    Color color = Color::transparent;
-    if (!colorString.isNull()) {
-        color = parseColorOrCurrentColor(colorString, &canvas());
-        if (!color.isValid())
-            return;
-    }
-    // FIXME: Should not use RGBA32 here.
-    setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(color.rgb(), alpha));
-}
-
-void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
-{
-    setShadow(FloatSize(width, height), blur, Color(grayLevel, grayLevel, grayLevel, alpha));
-}
-
-void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
-{
-    setShadow(FloatSize(width, height), blur, Color(r, g, b, a));
-}
-
-void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
-{
-    setShadow(FloatSize(width, height), blur, Color(c, m, y, k, a));
-}
-
-void CanvasRenderingContext2D::clearShadow()
-{
-    setShadow(FloatSize(), 0, Color::transparent);
-}
-
-void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, const Color& color)
-{
-    if (state().shadowOffset == offset && state().shadowBlur == blur && state().shadowColor == color)
-        return;
-    bool wasDrawingShadows = shouldDrawShadows();
-    realizeSaves();
-    modifiableState().shadowOffset = offset;
-    modifiableState().shadowBlur = blur;
-    modifiableState().shadowColor = color;
-    if (!wasDrawingShadows && !shouldDrawShadows())
-        return;
-    applyShadow();
-}
-
-void CanvasRenderingContext2D::applyShadow()
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-
-    if (shouldDrawShadows()) {
-        float width = state().shadowOffset.width();
-        float height = state().shadowOffset.height();
-        c->setLegacyShadow(FloatSize(width, -height), state().shadowBlur, state().shadowColor);
-    } else
-        c->setLegacyShadow(FloatSize(), 0, Color::transparent);
-}
-
-bool CanvasRenderingContext2D::shouldDrawShadows() const
-{
-    return state().shadowColor.isVisible() && (state().shadowBlur || !state().shadowOffset.isZero());
-}
-
-enum class ImageSizeType { AfterDevicePixelRatio, BeforeDevicePixelRatio };
-static LayoutSize size(HTMLImageElement& element, ImageSizeType sizeType = ImageSizeType::BeforeDevicePixelRatio)
-{
-    LayoutSize size;
-    if (auto* cachedImage = element.cachedImage()) {
-        size = cachedImage->imageSizeForRenderer(element.renderer(), 1.0f); // FIXME: Not sure about this.
-        if (sizeType == ImageSizeType::AfterDevicePixelRatio && is<RenderImage>(element.renderer()) && cachedImage->image() && !cachedImage->image()->hasRelativeWidth())
-            size.scale(downcast<RenderImage>(*element.renderer()).imageDevicePixelRatio());
-    }
-    return size;
-}
-
-static inline FloatSize size(HTMLCanvasElement& canvasElement)
-{
-    return canvasElement.size();
-}
-
-static inline FloatSize size(ImageBitmap& imageBitmap)
-{
-    return FloatSize { static_cast<float>(imageBitmap.width()), static_cast<float>(imageBitmap.height()) };
-}
-
-#if ENABLE(VIDEO)
-
-static inline FloatSize size(HTMLVideoElement& video)
-{
-    auto player = video.player();
-    if (!player)
-        return { };
-    return player->naturalSize();
-}
-
-#endif
-
-static inline FloatRect normalizeRect(const FloatRect& rect)
-{
-    return FloatRect(std::min(rect.x(), rect.maxX()),
-        std::min(rect.y(), rect.maxY()),
-        std::max(rect.width(), -rect.width()),
-        std::max(rect.height(), -rect.height()));
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float dx, float dy)
-{
-    return WTF::switchOn(image,
-        [&] (RefPtr<HTMLImageElement>& imageElement) -> ExceptionOr<void> {
-            LayoutSize destRectSize = size(*imageElement, ImageSizeType::AfterDevicePixelRatio);
-            LayoutSize sourceRectSize = size(*imageElement, ImageSizeType::BeforeDevicePixelRatio);
-            return this->drawImage(*imageElement, FloatRect { 0, 0, sourceRectSize.width(), sourceRectSize.height() }, FloatRect { dx, dy, destRectSize.width(), destRectSize.height() });
-        },
-        [&] (auto& element) -> ExceptionOr<void> {
-            FloatSize elementSize = size(*element);
-            return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, elementSize.width(), elementSize.height() });
-        }
-    );
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float dx, float dy, float dw, float dh)
-{
-    return WTF::switchOn(image,
-        [&] (auto& element) -> ExceptionOr<void> {
-            FloatSize elementSize = size(*element);
-            return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, dw, dh });
-        }
-    );
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh)
-{
-    return WTF::switchOn(image,
-        [&] (auto& element) -> ExceptionOr<void> {
-            return this->drawImage(*element, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh });
-        }
-    );
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect)
-{
-    return drawImage(imageElement, srcRect, dstRect, state().globalComposite, state().globalBlend);
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode)
-{
-    if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
-        || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
-        return { };
-
-    if (!dstRect.width() || !dstRect.height())
-        return { };
-
-    if (!imageElement.complete())
-        return { };
-
-    FloatRect normalizedSrcRect = normalizeRect(srcRect);
-    FloatRect normalizedDstRect = normalizeRect(dstRect);
-
-    FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));
-    if (!srcRect.width() || !srcRect.height())
-        return Exception { IndexSizeError };
-
-    // When the source rectangle is outside the source image, the source rectangle must be clipped
-    // to the source image and the destination rectangle must be clipped in the same proportion.
-    FloatRect originalNormalizedSrcRect = normalizedSrcRect;
-    normalizedSrcRect.intersect(imageRect);
-    if (normalizedSrcRect.isEmpty())
-        return { };
-
-    if (normalizedSrcRect != originalNormalizedSrcRect) {
-        normalizedDstRect.setWidth(normalizedDstRect.width() * normalizedSrcRect.width() / originalNormalizedSrcRect.width());
-        normalizedDstRect.setHeight(normalizedDstRect.height() * normalizedSrcRect.height() / originalNormalizedSrcRect.height());
-        if (normalizedDstRect.isEmpty())
-            return { };
-    }
-
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return { };
-    if (!state().hasInvertibleTransform)
-        return { };
-
-    CachedImage* cachedImage = imageElement.cachedImage();
-    if (!cachedImage)
-        return { };
-
-    RefPtr<Image> image = cachedImage->imageForRenderer(imageElement.renderer());
-    if (!image)
-        return { };
-
-    ImageObserver* observer = image->imageObserver();
-
-    if (image->isSVGImage()) {
-        image->setImageObserver(nullptr);
-        image->setContainerSize(imageRect.size());
-    }
-
-    if (image->isBitmapImage())
-        downcast<BitmapImage>(*image).updateFromSettings(imageElement.document().settings());
-
-    if (rectContainsCanvas(normalizedDstRect)) {
-        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
-        didDrawEntireCanvas();
-    } else if (isFullCanvasCompositeMode(op)) {
-        fullCanvasCompositedDrawImage(*image, normalizedDstRect, normalizedSrcRect, op);
-        didDrawEntireCanvas();
-    } else if (op == CompositeCopy) {
-        clearCanvas();
-        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
-        didDrawEntireCanvas();
-    } else {
-        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
-        didDraw(normalizedDstRect);
-    }
-    
-    if (image->isSVGImage())
-        image->setImageObserver(observer);
-
-    checkOrigin(&imageElement);
-
-    return { };
-}
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas, const FloatRect& srcRect, const FloatRect& dstRect)
-{
-    FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas.size());
-
-    if (!srcCanvasRect.width() || !srcCanvasRect.height())
-        return Exception { InvalidStateError };
-
-    if (!srcRect.width() || !srcRect.height())
-        return Exception { IndexSizeError };
-
-    if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
-        return { };
-
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return { };
-    if (!state().hasInvertibleTransform)
-        return { };
-
-    // FIXME: Do this through platform-independent GraphicsContext API.
-    ImageBuffer* buffer = sourceCanvas.buffer();
-    if (!buffer)
-        return { };
-
-    checkOrigin(&sourceCanvas);
-
-#if ENABLE(ACCELERATED_2D_CANVAS)
-    // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas.makeRenderingResultsAvailable()
-    // as that will do a readback to software.
-    RefPtr<CanvasRenderingContext> sourceContext = sourceCanvas.renderingContext();
-    // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
-    if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d())
-        sourceCanvas.makeRenderingResultsAvailable();
-#else
-    sourceCanvas.makeRenderingResultsAvailable();
-#endif
-
-    if (rectContainsCanvas(dstRect)) {
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDrawEntireCanvas();
-    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
-        fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
-        didDrawEntireCanvas();
-    } else if (state().globalComposite == CompositeCopy) {
-        clearCanvas();
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDrawEntireCanvas();
-    } else {
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDraw(dstRect);
-    }
-
-    return { };
-}
-
-#if ENABLE(VIDEO)
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLVideoElement& video, const FloatRect& srcRect, const FloatRect& dstRect)
-{
-    if (video.readyState() == HTMLMediaElement::HAVE_NOTHING || video.readyState() == HTMLMediaElement::HAVE_METADATA)
-        return { };
-
-    FloatRect videoRect = FloatRect(FloatPoint(), size(video));
-    if (!srcRect.width() || !srcRect.height())
-        return Exception { IndexSizeError };
-
-    if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
-        return { };
-
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return { };
-    if (!state().hasInvertibleTransform)
-        return { };
-
-    checkOrigin(&video);
-
-#if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
-    if (NativeImagePtr image = video.nativeImageForCurrentTime()) {
-        c->drawNativeImage(image, FloatSize(video.videoWidth(), video.videoHeight()), dstRect, srcRect);
-        if (rectContainsCanvas(dstRect))
-            didDrawEntireCanvas();
-        else
-            didDraw(dstRect);
-
-        return { };
-    }
-#endif
-
-    GraphicsContextStateSaver stateSaver(*c);
-    c->clip(dstRect);
-    c->translate(dstRect.location());
-    c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
-    c->translate(-srcRect.location());
-    video.paintCurrentFrameInContext(*c, FloatRect(FloatPoint(), size(video)));
-    stateSaver.restore();
-    didDraw(dstRect);
-
-    return { };
-}
-
-#endif
-
-ExceptionOr<void> CanvasRenderingContext2D::drawImage(ImageBitmap& imageBitmap, const FloatRect& srcRect, const FloatRect& dstRect)
-{
-    if (!imageBitmap.width() || !imageBitmap.height())
-        return Exception { InvalidStateError };
-
-    if (!srcRect.width() || !srcRect.height())
-        return Exception { IndexSizeError };
-
-    FloatRect srcBitmapRect = FloatRect(FloatPoint(), FloatSize(imageBitmap.width(), imageBitmap.height()));
-
-    if (!srcBitmapRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
-        return { };
-
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return { };
-    if (!state().hasInvertibleTransform)
-        return { };
-
-    ImageBuffer* buffer = imageBitmap.buffer();
-    if (!buffer)
-        return { };
-
-    checkOrigin(&imageBitmap);
-
-    if (rectContainsCanvas(dstRect)) {
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDrawEntireCanvas();
-    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
-        fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
-        didDrawEntireCanvas();
-    } else if (state().globalComposite == CompositeCopy) {
-        clearCanvas();
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDrawEntireCanvas();
-    } else {
-        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
-        didDraw(dstRect);
-    }
-
-    return { };
-}
-
-void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement& imageElement, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, const String& compositeOperation)
-{
-    CompositeOperator op;
-    auto blendOp = BlendModeNormal;
-    if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != BlendModeNormal)
-        op = CompositeSourceOver;
-    drawImage(imageElement, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh }, op, BlendModeNormal);
-}
-
-void CanvasRenderingContext2D::clearCanvas()
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-
-    c->save();
-    c->setCTM(canvas().baseTransform());
-    c->clearRect(FloatRect(0, 0, canvas().width(), canvas().height()));
-    c->restore();
-}
-
-Path CanvasRenderingContext2D::transformAreaToDevice(const Path& path) const
-{
-    Path transformed(path);
-    transformed.transform(state().transform);
-    transformed.transform(canvas().baseTransform());
-    return transformed;
-}
-
-Path CanvasRenderingContext2D::transformAreaToDevice(const FloatRect& rect) const
-{
-    Path path;
-    path.addRect(rect);
-    return transformAreaToDevice(path);
-}
-
-bool CanvasRenderingContext2D::rectContainsCanvas(const FloatRect& rect) const
-{
-    FloatQuad quad(rect);
-    FloatQuad canvasQuad(FloatRect(0, 0, canvas().width(), canvas().height()));
-    return state().transform.mapQuad(quad).containsQuad(canvasQuad);
-}
-
-template<class T> IntRect CanvasRenderingContext2D::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset)
-{
-    IntRect canvasRect(0, 0, canvas().width(), canvas().height());
-    canvasRect = canvas().baseTransform().mapRect(canvasRect);
-    Path path = transformAreaToDevice(area);
-    IntRect bufferRect = enclosingIntRect(path.fastBoundingRect());
-    IntPoint originalLocation = bufferRect.location();
-    bufferRect.intersect(canvasRect);
-    if (croppedOffset)
-        *croppedOffset = originalLocation - bufferRect.location();
-    return bufferRect;
-}
-
-std::unique_ptr<ImageBuffer> CanvasRenderingContext2D::createCompositingBuffer(const IntRect& bufferRect)
-{
-    return ImageBuffer::create(bufferRect.size(), isAccelerated() ? Accelerated : Unaccelerated);
-}
-
-void CanvasRenderingContext2D::compositeBuffer(ImageBuffer& buffer, const IntRect& bufferRect, CompositeOperator op)
-{
-    IntRect canvasRect(0, 0, canvas().width(), canvas().height());
-    canvasRect = canvas().baseTransform().mapRect(canvasRect);
-
-    auto* c = drawingContext();
-    if (!c)
-        return;
-
-    c->save();
-    c->setCTM(AffineTransform());
-    c->setCompositeOperation(op);
-
-    c->save();
-    c->clipOut(bufferRect);
-    c->clearRect(canvasRect);
-    c->restore();
-    c->drawImageBuffer(buffer, bufferRect.location(), state().globalComposite);
-    c->restore();
-}
-
-static void drawImageToContext(Image& image, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
-{
-    context.drawImage(image, dest, src, op);
-}
-
-static void drawImageToContext(ImageBuffer& imageBuffer, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
-{
-    context.drawImageBuffer(imageBuffer, dest, src, op);
-}
-
-template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T& image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
-{
-    ASSERT(isFullCanvasCompositeMode(op));
-
-    IntSize croppedOffset;
-    auto bufferRect = calculateCompositingBufferRect(dest, &croppedOffset);
-    if (bufferRect.isEmpty()) {
-        clearCanvas();
-        return;
-    }
-
-    auto buffer = createCompositingBuffer(bufferRect);
-    if (!buffer)
-        return;
-
-    auto* c = drawingContext();
-    if (!c)
-        return;
-
-    FloatRect adjustedDest = dest;
-    adjustedDest.setLocation(FloatPoint(0, 0));
-    AffineTransform effectiveTransform = c->getCTM();
-    IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest));
-    buffer->context().translate(-transformedAdjustedRect.location());
-    buffer->context().translate(croppedOffset);
-    buffer->context().concatCTM(effectiveTransform);
-    drawImageToContext(image, buffer->context(), adjustedDest, src, CompositeSourceOver);
-
-    compositeBuffer(*buffer, bufferRect, op);
-}
-
-void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient& gradient) const
-{
-#if ENABLE(DASHBOARD_SUPPORT)
-    if (m_usesDashboardCompatibilityMode)
-        gradient.setDashboardCompatibilityMode();
-#else
-    UNUSED_PARAM(gradient);
-#endif
-}
-
-static CanvasRenderingContext2D::Style toStyle(const CanvasStyle& style)
-{
-    if (auto gradient = style.canvasGradient())
-        return gradient;
-    if (auto pattern = style.canvasPattern())
-        return pattern;
-    return style.color();
-}
-
-CanvasRenderingContext2D::Style CanvasRenderingContext2D::strokeStyle() const
-{
-    return toStyle(state().strokeStyle);
-}
-
-void CanvasRenderingContext2D::setStrokeStyle(CanvasRenderingContext2D::Style&& style)
-{
-    WTF::switchOn(style,
-        [this] (const String& string) { this->setStrokeColor(string); },
-        [this] (const RefPtr<CanvasGradient>& gradient) { this->setStrokeStyle(CanvasStyle(*gradient)); },
-        [this] (const RefPtr<CanvasPattern>& pattern) { this->setStrokeStyle(CanvasStyle(*pattern)); }
-    );
-}
-
-CanvasRenderingContext2D::Style CanvasRenderingContext2D::fillStyle() const
-{
-    return toStyle(state().fillStyle);
-}
-
-void CanvasRenderingContext2D::setFillStyle(CanvasRenderingContext2D::Style&& style)
-{
-    WTF::switchOn(style,
-        [this] (const String& string) { this->setFillColor(string); },
-        [this] (const RefPtr<CanvasGradient>& gradient) { this->setFillStyle(CanvasStyle(*gradient)); },
-        [this] (const RefPtr<CanvasPattern>& pattern) { this->setFillStyle(CanvasStyle(*pattern)); }
-    );
-}
-
-ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
-{
-    if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1))
-        return Exception { NotSupportedError };
-
-    auto gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
-    prepareGradientForDashboard(gradient.get());
-    return WTFMove(gradient);
-}
-
-ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
-{
-    if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1))
-        return Exception { NotSupportedError };
-
-    if (r0 < 0 || r1 < 0)
-        return Exception { IndexSizeError };
-
-    auto gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
-    prepareGradientForDashboard(gradient.get());
-    return WTFMove(gradient);
-}
-
-ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(CanvasImageSource&& image, const String& repetition)
-{
-    bool repeatX, repeatY;
-    if (!CanvasPattern::parseRepetitionType(repetition, repeatX, repeatY))
-        return Exception { SyntaxError };
-
-    return WTF::switchOn(image,
-        [&] (auto& element) -> ExceptionOr<RefPtr<CanvasPattern>> { return this->createPattern(*element, repeatX, repeatY); }
-    );
-}
-
-ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLImageElement& imageElement, bool repeatX, bool repeatY)
-{
-    auto* cachedImage = imageElement.cachedImage();
-
-    // If the image loading hasn't started or the image is not complete, it is not fully decodable.
-    if (!cachedImage || !imageElement.complete())
-        return nullptr;
-
-    if (cachedImage->status() == CachedResource::LoadError)
-        return Exception { InvalidStateError };
-
-    bool originClean = cachedImage->isOriginClean(canvas().securityOrigin());
-
-    // FIXME: SVG images with animations can switch between clean and dirty (leaking cross-origin
-    // data). We should either:
-    //   1) Take a fixed snapshot of an SVG image when creating a pattern and determine then whether
-    //      the origin is clean.
-    //   2) Dynamically verify the origin checks at draw time, and dirty the canvas accordingly.
-    // To be on the safe side, taint the origin for all patterns containing SVG images for now.
-    if (cachedImage->image()->isSVGImage())
-        originClean = false;
-
-    return RefPtr<CanvasPattern> { CanvasPattern::create(*cachedImage->imageForRenderer(imageElement.renderer()), repeatX, repeatY, originClean) };
-}
-
-ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLCanvasElement& canvas, bool repeatX, bool repeatY)
-{
-    if (!canvas.width() || !canvas.height() || !canvas.buffer())
-        return Exception { InvalidStateError };
-
-    return RefPtr<CanvasPattern> { CanvasPattern::create(*canvas.copiedImage(), repeatX, repeatY, canvas.originClean()) };
-}
-    
-#if ENABLE(VIDEO)
-
-ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLVideoElement& videoElement, bool repeatX, bool repeatY)
-{
-    if (videoElement.readyState() < HTMLMediaElement::HAVE_CURRENT_DATA)
-        return nullptr;
-    
-    checkOrigin(&videoElement);
-    bool originClean = canvas().originClean();
-
-#if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
-    if (auto nativeImage = videoElement.nativeImageForCurrentTime())
-        return RefPtr<CanvasPattern> { CanvasPattern::create(BitmapImage::create(WTFMove(nativeImage)), repeatX, repeatY, originClean) };
-#endif
-
-    auto imageBuffer = ImageBuffer::create(size(videoElement), drawingContext() ? drawingContext()->renderingMode() : Accelerated);
-    videoElement.paintCurrentFrameInContext(imageBuffer->context(), FloatRect(FloatPoint(), size(videoElement)));
-    
-    return RefPtr<CanvasPattern> { CanvasPattern::create(ImageBuffer::sinkIntoImage(WTFMove(imageBuffer), Unscaled).releaseNonNull(), repeatX, repeatY, originClean) };
-}
-
-#endif
-
-ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(ImageBitmap&, bool, bool)
-{
-    // FIXME: Implement.
-    return Exception { TypeError };
-}
-
-void CanvasRenderingContext2D::didDrawEntireCanvas()
-{
-    didDraw(FloatRect(FloatPoint::zero(), canvas().size()), CanvasDidDrawApplyClip);
-}
-
-void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options)
-{
-    auto* c = drawingContext();
-    if (!c)
-        return;
-    if (!state().hasInvertibleTransform)
-        return;
-
-#if ENABLE(ACCELERATED_2D_CANVAS)
-    // If we are drawing to hardware and we have a composited layer, just call contentChanged().
-    if (isAccelerated()) {
-        RenderBox* renderBox = canvas().renderBox();
-        if (renderBox && renderBox->hasAcceleratedCompositing()) {
-            renderBox->contentChanged(CanvasPixelsChanged);
-            canvas().clearCopiedImage();
-            canvas().notifyObserversCanvasChanged(r);
-            return;
-        }
-    }
-#endif
-
-    FloatRect dirtyRect = r;
-    if (options & CanvasDidDrawApplyTransform) {
-        AffineTransform ctm = state().transform;
-        dirtyRect = ctm.mapRect(r);
-    }
-
-    if (options & CanvasDidDrawApplyShadow && state().shadowColor.isVisible()) {
-        // The shadow gets applied after transformation
-        FloatRect shadowRect(dirtyRect);
-        shadowRect.move(state().shadowOffset);
-        shadowRect.inflate(state().shadowBlur);
-        dirtyRect.unite(shadowRect);
-    }
-
-    if (options & CanvasDidDrawApplyClip) {
-        // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
-        // back out of the GraphicsContext, so to take clip into account for incremental painting,
-        // we'd have to keep the clip path around.
-    }
-
-    canvas().didDraw(dirtyRect);
-}
-
-void CanvasRenderingContext2D::setTracksDisplayListReplay(bool tracksDisplayListReplay)
-{
-    if (tracksDisplayListReplay == m_tracksDisplayListReplay)
-        return;
-
-    m_tracksDisplayListReplay = tracksDisplayListReplay;
-    if (!m_tracksDisplayListReplay)
-        contextDisplayListMap().remove(this);
-}
-
-String CanvasRenderingContext2D::displayListAsText(DisplayList::AsTextFlags flags) const
-{
-    if (!m_recordingContext)
-        return { };
-    return m_recordingContext->displayList.asText(flags);
-}
-
-String CanvasRenderingContext2D::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
-{
-    auto* displayList = contextDisplayListMap().get(this);
-    if (!displayList)
-        return { };
-    return displayList->asText(flags);
-}
-
-void CanvasRenderingContext2D::paintRenderingResultsToCanvas()
-{
-    if (UNLIKELY(m_usesDisplayListDrawing)) {
-        if (!m_recordingContext)
-            return;
-
-        FloatRect clip(FloatPoint::zero(), canvas().size());
-        DisplayList::Replayer replayer(*canvas().drawingContext(), m_recordingContext->displayList);
-
-        if (UNLIKELY(m_tracksDisplayListReplay)) {
-            auto replayList = replayer.replay(clip, m_tracksDisplayListReplay);
-            contextDisplayListMap().add(this, WTFMove(replayList));
-        } else
-            replayer.replay(clip);
-
-        m_recordingContext->displayList.clear();
-    }
-}
-
-GraphicsContext* CanvasRenderingContext2D::drawingContext() const
-{
-    if (UNLIKELY(m_usesDisplayListDrawing)) {
-        if (!m_recordingContext)
-            m_recordingContext = std::make_unique<DisplayListDrawingContext>(FloatRect(FloatPoint::zero(), canvas().size()));
-        return &m_recordingContext->context;
-    }
-
-    return canvas().drawingContext();
-}
-
-static RefPtr<ImageData> createEmptyImageData(const IntSize& size)
-{
-    auto data = ImageData::create(size);
-    if (data)
-        data->data()->zeroFill();
-    return data;
-}
-
-ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::createImageData(ImageData* imageData) const
-{
-    if (!imageData)
-        return Exception { NotSupportedError };
-
-    return createEmptyImageData(imageData->size());
-}
-
-ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::createImageData(float sw, float sh) const
-{
-    if (!sw || !sh)
-        return Exception { IndexSizeError };
-
-    FloatSize logicalSize(std::abs(sw), std::abs(sh));
-    if (!logicalSize.isExpressibleAsIntSize())
-        return nullptr;
-
-    IntSize size = expandedIntSize(logicalSize);
-    if (size.width() < 1)
-        size.setWidth(1);
-    if (size.height() < 1)
-        size.setHeight(1);
-
-    return createEmptyImageData(size);
-}
-
-ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh) const
-{
-    return getImageData(ImageBuffer::LogicalCoordinateSystem, sx, sy, sw, sh);
-}
-
-ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::getImageData(ImageBuffer::CoordinateSystem coordinateSystem, float sx, float sy, float sw, float sh) const
-{
-    if (!canvas().originClean()) {
-        static NeverDestroyed<String> consoleMessage(MAKE_STATIC_STRING_IMPL("Unable to get image data from canvas because the canvas has been tainted by cross-origin data."));
-        canvas().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
-        return Exception { SecurityError };
-    }
-
-    if (!sw || !sh)
-        return Exception { IndexSizeError };
-
-    if (sw < 0) {
-        sx += sw;
-        sw = -sw;
-    }    
-    if (sh < 0) {
-        sy += sh;
-        sh = -sh;
-    }
-
-    FloatRect logicalRect(sx, sy, sw, sh);
-    if (logicalRect.width() < 1)
-        logicalRect.setWidth(1);
-    if (logicalRect.height() < 1)
-        logicalRect.setHeight(1);
-    if (!logicalRect.isExpressibleAsIntRect())
-        return nullptr;
-
-    IntRect imageDataRect = enclosingIntRect(logicalRect);
-    ImageBuffer* buffer = canvas().buffer();
-    if (!buffer)
-        return createEmptyImageData(imageDataRect.size());
-
-    auto byteArray = buffer->getUnmultipliedImageData(imageDataRect, nullptr, coordinateSystem);
-    if (!byteArray) {
-        StringBuilder consoleMessage;
-        consoleMessage.appendLiteral("Unable to get image data from canvas. Requested size was ");
-        consoleMessage.appendNumber(imageDataRect.width());
-        consoleMessage.appendLiteral(" x ");
-        consoleMessage.appendNumber(imageDataRect.height());
-
-        canvas().document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage.toString());
-        return Exception { InvalidStateError };
-    }
-
-    return ImageData::create(imageDataRect.size(), byteArray.releaseNonNull());
-}
-
-void CanvasRenderingContext2D::putImageData(ImageData& data, float dx, float dy)
+CanvasRenderingContext2D::CanvasRenderingContext2D(CanvasBase& canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
+    : CanvasRenderingContext2DBase(canvas, usesCSSCompatibilityParseMode, usesDashboardCompatibilityMode)
 {
-    putImageData(data, dx, dy, 0, 0, data.width(), data.height());
 }
 
-void CanvasRenderingContext2D::putImageData(ImageData& data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
-{
-    putImageData(data, ImageBuffer::LogicalCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
-}
+CanvasRenderingContext2D::~CanvasRenderingContext2D() = default;
 
 void CanvasRenderingContext2D::drawFocusIfNeeded(Element& element)
 {
@@ -2198,47 +92,10 @@ void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Eleme
     context->drawFocusRing(path, 1, 1, RenderTheme::focusRingColor());
 }
 
-void CanvasRenderingContext2D::putImageData(ImageData& data, ImageBuffer::CoordinateSystem coordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
-{
-    ImageBuffer* buffer = canvas().buffer();
-    if (!buffer)
-        return;
-
-    if (!data.data())
-        return;
-
-    if (dirtyWidth < 0) {
-        dirtyX += dirtyWidth;
-        dirtyWidth = -dirtyWidth;
-    }
-
-    if (dirtyHeight < 0) {
-        dirtyY += dirtyHeight;
-        dirtyHeight = -dirtyHeight;
-    }
-
-    FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
-    clipRect.intersect(IntRect(0, 0, data.width(), data.height()));
-    IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
-    IntRect destRect = enclosingIntRect(clipRect);
-    destRect.move(destOffset);
-    destRect.intersect(IntRect(IntPoint(), coordinateSystem == ImageBuffer::LogicalCoordinateSystem ? buffer->logicalSize() : buffer->internalSize()));
-    if (destRect.isEmpty())
-        return;
-    IntRect sourceRect(destRect);
-    sourceRect.move(-destOffset);
-    sourceRect.intersect(IntRect(0, 0, data.width(), data.height()));
-
-    if (!sourceRect.isEmpty())
-        buffer->putByteArray(*data.data(), AlphaPremultiplication::Unpremultiplied, IntSize(data.width(), data.height()), sourceRect, IntPoint(destOffset), coordinateSystem);
-
-    didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip
-}
-
 String CanvasRenderingContext2D::font() const
 {
     if (!state().font.realized())
-        return defaultFont;
+        return DefaultFont;
 
     StringBuilder serializedFont;
     const auto& fontDescription = state().font.fontDescription();
@@ -2301,9 +158,9 @@ void CanvasRenderingContext2D::setFont(const String& newFont)
         newStyle->setFontDescription(computedStyle->fontDescription());
     else {
         FontCascadeDescription defaultFontDescription;
-        defaultFontDescription.setOneFamily(defaultFontFamily);
-        defaultFontDescription.setSpecifiedSize(defaultFontSize);
-        defaultFontDescription.setComputedSize(defaultFontSize);
+        defaultFontDescription.setOneFamily(DefaultFontFamily);
+        defaultFontDescription.setSpecifiedSize(DefaultFontSize);
+        defaultFontDescription.setComputedSize(DefaultFontSize);
 
         newStyle->setFontDescription(defaultFontDescription);
     }
@@ -2547,6 +404,14 @@ Ref<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
     return metrics;
 }
 
+auto CanvasRenderingContext2D::fontProxy() -> const FontProxy& {
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    canvas.document().updateStyleIfNeeded();
+    if (!state().font.realized())
+        setFont(state().unparsedFont);
+    return state().font;
+}
+
 FloatPoint CanvasRenderingContext2D::textOffset(float width, TextDirection direction)
 {
     auto& fontMetrics = fontProxy().fontMetrics();
@@ -2728,97 +593,4 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo
     }
 }
 
-void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
-{
-    // Fast approximation of the stroke's bounding rect.
-    // This yields a slightly oversized rect but is very fast
-    // compared to Path::strokeBoundingRect().
-    static const float root2 = sqrtf(2);
-    float delta = state().lineWidth / 2;
-    if (state().lineJoin == MiterJoin)
-        delta *= state().miterLimit;
-    else if (state().lineCap == SquareCap)
-        delta *= root2;
-    rect.inflate(delta);
-}
-
-auto CanvasRenderingContext2D::fontProxy() -> const FontProxy&
-{
-    canvas().document().updateStyleIfNeeded();
-    if (!state().font.realized())
-        setFont(state().unparsedFont);
-    return state().font;
-}
-
-#if ENABLE(ACCELERATED_2D_CANVAS)
-
-PlatformLayer* CanvasRenderingContext2D::platformLayer() const
-{
-    return canvas().buffer() ? canvas().buffer()->platformLayer() : nullptr;
-}
-
-#endif
-
-static inline InterpolationQuality smoothingToInterpolationQuality(ImageSmoothingQuality quality)
-{
-    switch (quality) {
-    case ImageSmoothingQuality::Low:
-        return InterpolationLow;
-    case ImageSmoothingQuality::Medium:
-        return InterpolationMedium;
-    case ImageSmoothingQuality::High:
-        return InterpolationHigh;
-    }
-
-    ASSERT_NOT_REACHED();
-    return InterpolationLow;
-};
-
-auto CanvasRenderingContext2D::imageSmoothingQuality() const -> ImageSmoothingQuality
-{
-    return state().imageSmoothingQuality;
-}
-
-void CanvasRenderingContext2D::setImageSmoothingQuality(ImageSmoothingQuality quality)
-{
-    if (quality == state().imageSmoothingQuality)
-        return;
-
-    realizeSaves();
-    modifiableState().imageSmoothingQuality = quality;
-
-    if (!state().imageSmoothingEnabled)
-        return;
-
-    if (auto* context = drawingContext())
-        context->setImageInterpolationQuality(smoothingToInterpolationQuality(quality));
-}
-
-bool CanvasRenderingContext2D::imageSmoothingEnabled() const
-{
-    return state().imageSmoothingEnabled;
-}
-
-void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
-{
-    if (enabled == state().imageSmoothingEnabled)
-        return;
-
-    realizeSaves();
-    modifiableState().imageSmoothingEnabled = enabled;
-    auto* c = drawingContext();
-    if (c)
-        c->setImageInterpolationQuality(enabled ? smoothingToInterpolationQuality(state().imageSmoothingQuality) : InterpolationNone);
-}
-
-void CanvasRenderingContext2D::setPath(Path2D& path)
-{
-    m_path = path.path();
-}
-
-Ref<Path2D> CanvasRenderingContext2D::getPath() const
-{
-    return Path2D::create(m_path);
-}
-
 } // namespace WebCore
index 63361cf..dffb5a7 100644 (file)
 
 #pragma once
 
-#include "AffineTransform.h"
-#include "CanvasDirection.h"
-#include "CanvasFillRule.h"
-#include "CanvasLineCap.h"
-#include "CanvasLineJoin.h"
-#include "CanvasPath.h"
-#include "CanvasRenderingContext.h"
-#include "CanvasStyle.h"
+#include "CanvasRenderingContext2DBase.h"
 #include "CanvasTextAlign.h"
 #include "CanvasTextBaseline.h"
-#include "Color.h"
-#include "FloatSize.h"
 #include "FontCascade.h"
 #include "FontSelectorClient.h"
-#include "GraphicsContext.h"
-#include "GraphicsTypes.h"
-#include "ImageBuffer.h"
-#include "ImageSmoothingQuality.h"
-#include "Path.h"
-#include "PlatformLayer.h"
-#include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
-class CanvasBase;
-class CanvasGradient;
-class CanvasPattern;
-class DOMMatrix;
-class FloatRect;
-class GraphicsContext;
-class HTMLImageElement;
-class HTMLVideoElement;
-class ImageBitmap;
-class ImageData;
-class Path2D;
 class TextMetrics;
 
-struct DOMMatrix2DInit;
-
-#if ENABLE(VIDEO)
-using CanvasImageSource = Variant<RefPtr<HTMLImageElement>, RefPtr<HTMLVideoElement>, RefPtr<HTMLCanvasElement>, RefPtr<ImageBitmap>>;
-#else
-using CanvasImageSource = Variant<RefPtr<HTMLImageElement>, RefPtr<HTMLCanvasElement>, RefPtr<ImageBitmap>>;
-#endif
-
-class CanvasRenderingContext2D final : public CanvasRenderingContext, public CanvasPath {
+class CanvasRenderingContext2D final : public CanvasRenderingContext2DBase {
 public:
     CanvasRenderingContext2D(CanvasBase&, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode);
     virtual ~CanvasRenderingContext2D();
 
     HTMLCanvasElement& canvas() const { return downcast<HTMLCanvasElement>(canvasBase()); }
 
-    float lineWidth() const;
-    void setLineWidth(float);
-
-    CanvasLineCap lineCap() const;
-    void setLineCap(CanvasLineCap);
-    void setLineCap(const String&);
-
-    CanvasLineJoin lineJoin() const;
-    void setLineJoin(CanvasLineJoin);
-    void setLineJoin(const String&);
-
-    float miterLimit() const;
-    void setMiterLimit(float);
-
-    const Vector<float>& getLineDash() const;
-    void setLineDash(const Vector<float>&);
-    const Vector<float>& webkitLineDash() const { return getLineDash(); }
-    void setWebkitLineDash(const Vector<float>&);
-
-    float lineDashOffset() const;
-    void setLineDashOffset(float);
-
-    float shadowOffsetX() const;
-    void setShadowOffsetX(float);
-
-    float shadowOffsetY() const;
-    void setShadowOffsetY(float);
-
-    float shadowBlur() const;
-    void setShadowBlur(float);
-
-    String shadowColor() const;
-    void setShadowColor(const String&);
-
-    float globalAlpha() const;
-    void setGlobalAlpha(float);
-
-    String globalCompositeOperation() const;
-    void setGlobalCompositeOperation(const String&);
-
-    void save() { ++m_unrealizedSaveCount; }
-    void restore();
-
-    void scale(float sx, float sy);
-    void rotate(float angleInRadians);
-    void translate(float tx, float ty);
-    void transform(float m11, float m12, float m21, float m22, float dx, float dy);
-
-    Ref<DOMMatrix> getTransform() const;
-    void setTransform(float m11, float m12, float m21, float m22, float dx, float dy);
-    ExceptionOr<void> setTransform(DOMMatrix2DInit&&);
-    void resetTransform();
-
-    void setStrokeColor(const String& color, std::optional<float> alpha = std::nullopt);
-    void setStrokeColor(float grayLevel, float alpha = 1.0);
-    void setStrokeColor(float r, float g, float b, float a);
-    void setStrokeColor(float c, float m, float y, float k, float a);
-
-    void setFillColor(const String& color, std::optional<float> alpha = std::nullopt);
-    void setFillColor(float grayLevel, float alpha = 1.0f);
-    void setFillColor(float r, float g, float b, float a);
-    void setFillColor(float c, float m, float y, float k, float a);
-
-    void beginPath();
-
-    void fill(CanvasFillRule = CanvasFillRule::Nonzero);
-    void stroke();
-    void clip(CanvasFillRule = CanvasFillRule::Nonzero);
-
-    void fill(Path2D&, CanvasFillRule = CanvasFillRule::Nonzero);
-    void stroke(Path2D&);
-    void clip(Path2D&, CanvasFillRule = CanvasFillRule::Nonzero);
-
-    bool isPointInPath(float x, float y, CanvasFillRule = CanvasFillRule::Nonzero);
-    bool isPointInStroke(float x, float y);
-
-    bool isPointInPath(Path2D&, float x, float y, CanvasFillRule = CanvasFillRule::Nonzero);
-    bool isPointInStroke(Path2D&, float x, float y);
-
-    void clearRect(float x, float y, float width, float height);
-    void fillRect(float x, float y, float width, float height);
-    void strokeRect(float x, float y, float width, float height);
-
-    void setShadow(float width, float height, float blur, const String& color = String(), std::optional<float> alpha = std::nullopt);
-    void setShadow(float width, float height, float blur, float grayLevel, float alpha = 1.0);
-    void setShadow(float width, float height, float blur, float r, float g, float b, float a);
-    void setShadow(float width, float height, float blur, float c, float m, float y, float k, float a);
-
-    void clearShadow();
-
-    ExceptionOr<void> drawImage(CanvasImageSource&&, float dx, float dy);
-    ExceptionOr<void> drawImage(CanvasImageSource&&, float dx, float dy, float dw, float dh);
-    ExceptionOr<void> drawImage(CanvasImageSource&&, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh);
-
-    void drawImageFromRect(HTMLImageElement&, float sx = 0, float sy = 0, float sw = 0, float sh = 0, float dx = 0, float dy = 0, float dw = 0, float dh = 0, const String& compositeOperation = emptyString());
-
-    using Style = Variant<String, RefPtr<CanvasGradient>, RefPtr<CanvasPattern>>;
-    Style strokeStyle() const;
-    void setStrokeStyle(Style&&);
-    Style fillStyle() const;
-    void setFillStyle(Style&&);
-
-    ExceptionOr<Ref<CanvasGradient>> createLinearGradient(float x0, float y0, float x1, float y1);
-    ExceptionOr<Ref<CanvasGradient>> createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1);
-    ExceptionOr<RefPtr<CanvasPattern>> createPattern(CanvasImageSource&&, const String& repetition);
-
-    ExceptionOr<RefPtr<ImageData>> createImageData(ImageData*) const;
-    ExceptionOr<RefPtr<ImageData>> createImageData(float width, float height) const;
-    ExceptionOr<RefPtr<ImageData>> getImageData(float sx, float sy, float sw, float sh) const;
-    void putImageData(ImageData&, float dx, float dy);
-    void putImageData(ImageData&, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight);
-
     void drawFocusIfNeeded(Element&);
     void drawFocusIfNeeded(Path2D&, Element&);
 
     float webkitBackingStorePixelRatio() const { return 1; }
 
-    void reset();
-
     String font() const;
     void setFont(const String&);
 
@@ -213,196 +63,22 @@ public:
     void strokeText(const String& text, float x, float y, std::optional<float> maxWidth = std::nullopt);
     Ref<TextMetrics> measureText(const String& text);
 
-    LineCap getLineCap() const { return state().lineCap; }
-    LineJoin getLineJoin() const { return state().lineJoin; }
-
-    bool imageSmoothingEnabled() const;
-    void setImageSmoothingEnabled(bool);
-
-    ImageSmoothingQuality imageSmoothingQuality() const;
-    void setImageSmoothingQuality(ImageSmoothingQuality);
-
-    void setPath(Path2D&);
-    Ref<Path2D> getPath() const;
-
-    bool usesDisplayListDrawing() const { return m_usesDisplayListDrawing; };
-    void setUsesDisplayListDrawing(bool flag) { m_usesDisplayListDrawing = flag; };
-
-    bool tracksDisplayListReplay() const { return m_tracksDisplayListReplay; }
-    void setTracksDisplayListReplay(bool);
-
-    String displayListAsText(DisplayList::AsTextFlags) const;
-    String replayDisplayListAsText(DisplayList::AsTextFlags) const;
-
-    using Direction = CanvasDirection;
-
-    class FontProxy : public FontSelectorClient {
-    public:
-        FontProxy() = default;
-        virtual ~FontProxy();
-        FontProxy(const FontProxy&);
-        FontProxy& operator=(const FontProxy&);
-
-        bool realized() const { return m_font.fontSelector(); }
-        void initialize(FontSelector&, const RenderStyle&);
-        const FontMetrics& fontMetrics() const;
-        const FontCascadeDescription& fontDescription() const;
-        float width(const TextRun&, GlyphOverflow* = 0) const;
-        void drawBidiText(GraphicsContext&, const TextRun&, const FloatPoint&, FontCascade::CustomFontNotReadyAction) const;
-
-    private:
-        void update(FontSelector&);
-        void fontsNeedUpdate(FontSelector&) override;
-
-        FontCascade m_font;
-    };
-
-    struct State final {
-        State();
-
-        State(const State&);
-        State& operator=(const State&);
-
-        String unparsedStrokeColor;
-        String unparsedFillColor;
-        CanvasStyle strokeStyle;
-        CanvasStyle fillStyle;
-        float lineWidth;
-        LineCap lineCap;
-        LineJoin lineJoin;
-        float miterLimit;
-        FloatSize shadowOffset;
-        float shadowBlur;
-        Color shadowColor;
-        float globalAlpha;
-        CompositeOperator globalComposite;
-        BlendMode globalBlend;
-        AffineTransform transform;
-        bool hasInvertibleTransform;
-        Vector<float> lineDash;
-        float lineDashOffset;
-        bool imageSmoothingEnabled;
-        ImageSmoothingQuality imageSmoothingQuality;
-
-        // Text state.
-        TextAlign textAlign;
-        TextBaseline textBaseline;
-        Direction direction;
-
-        String unparsedFont;
-        FontProxy font;
-    };
-
-    const State& state() const { return m_stateStack.last(); }
+    bool is2d() const override { return true; }
 
 private:
-    enum CanvasDidDrawOption {
-        CanvasDidDrawApplyNone = 0,
-        CanvasDidDrawApplyTransform = 1,
-        CanvasDidDrawApplyShadow = 1 << 1,
-        CanvasDidDrawApplyClip = 1 << 2,
-        CanvasDidDrawApplyAll = 0xffffffff
-    };
-
-    State& modifiableState() { ASSERT(!m_unrealizedSaveCount || m_stateStack.size() >= MaxSaveCount); return m_stateStack.last(); }
-
-    void applyLineDash() const;
-    void setShadow(const FloatSize& offset, float blur, const Color&);
-    void applyShadow();
-    bool shouldDrawShadows() const;
-
-    void didDraw(const FloatRect&, unsigned options = CanvasDidDrawApplyAll);
-    void didDrawEntireCanvas();
-
-    void paintRenderingResultsToCanvas() override;
-
-    GraphicsContext* drawingContext() const;
-
-    void unwindStateStack();
-    void realizeSaves();
-    void realizeSavesLoop();
-
-    void applyStrokePattern();
-    void applyFillPattern();
-
-    void setStrokeStyle(CanvasStyle);
-    void setFillStyle(CanvasStyle);
-
-    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLImageElement&, bool repeatX, bool repeatY);
-    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLCanvasElement&, bool repeatX, bool repeatY);
-#if ENABLE(VIDEO)
-    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLVideoElement&, bool repeatX, bool repeatY);
-#endif
-    ExceptionOr<RefPtr<CanvasPattern>> createPattern(ImageBitmap&, bool repeatX, bool repeatY);
-
-    ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect);
-    ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&);
-    ExceptionOr<void> drawImage(HTMLCanvasElement&, const FloatRect& srcRect, const FloatRect& dstRect);
-#if ENABLE(VIDEO)
-    ExceptionOr<void> drawImage(HTMLVideoElement&, const FloatRect& srcRect, const FloatRect& dstRect);
-#endif
-    ExceptionOr<void> drawImage(ImageBitmap&, const FloatRect& srcRect, const FloatRect& dstRect);
-
-    void drawTextInternal(const String& text, float x, float y, bool fill, std::optional<float> maxWidth = std::nullopt);
-
     // The relationship between FontCascade and CanvasRenderingContext2D::FontProxy must hold certain invariants.
     // Therefore, all font operations must pass through the State.
     const FontProxy& fontProxy();
 
-    void clearPathForDashboardBackwardCompatibilityMode();
-
-    void beginCompositeLayer();
-    void endCompositeLayer();
-
-    void fillInternal(const Path&, CanvasFillRule);
-    void strokeInternal(const Path&);
-    void clipInternal(const Path&, CanvasFillRule);
-
-    bool isPointInPathInternal(const Path&, float x, float y, CanvasFillRule);
-    bool isPointInStrokeInternal(const Path&, float x, float y);
+    void drawTextInternal(const String& text, float x, float y, bool fill, std::optional<float> maxWidth = std::nullopt);
 
     void drawFocusIfNeededInternal(const Path&, Element&);
 
-    void clearCanvas();
-    Path transformAreaToDevice(const Path&) const;
-    Path transformAreaToDevice(const FloatRect&) const;
-    bool rectContainsCanvas(const FloatRect&) const;
-
-    template<class T> IntRect calculateCompositingBufferRect(const T&, IntSize*);
-    std::unique_ptr<ImageBuffer> createCompositingBuffer(const IntRect&);
-    void compositeBuffer(ImageBuffer&, const IntRect&, CompositeOperator);
-
-    void inflateStrokeRect(FloatRect&) const;
-
-    template<class T> void fullCanvasCompositedDrawImage(T&, const FloatRect&, const FloatRect&, CompositeOperator);
-
     void prepareGradientForDashboard(CanvasGradient& gradient) const;
 
-    ExceptionOr<RefPtr<ImageData>> getImageData(ImageBuffer::CoordinateSystem, float sx, float sy, float sw, float sh) const;
-    void putImageData(ImageData&, ImageBuffer::CoordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight);
-
-    bool is2d() const override { return true; }
-    bool isAccelerated() const override;
-
-    bool hasInvertibleTransform() const override { return state().hasInvertibleTransform; }
-    TextDirection toTextDirection(Direction, const RenderStyle** computedStyle = nullptr) const;
+    TextDirection toTextDirection(CanvasRenderingContext2DBase::Direction, const RenderStyle** computedStyle = nullptr) const;
 
     FloatPoint textOffset(float width, TextDirection);
-
-#if ENABLE(ACCELERATED_2D_CANVAS)
-    PlatformLayer* platformLayer() const override;
-#endif
-
-    static const unsigned MaxSaveCount = 1024 * 16;
-    Vector<State, 1> m_stateStack;
-    unsigned m_unrealizedSaveCount { 0 };
-    bool m_usesCSSCompatibilityParseMode;
-#if ENABLE(DASHBOARD_SUPPORT)
-    bool m_usesDashboardCompatibilityMode;
-#endif
-    bool m_usesDisplayListDrawing { false };
-    bool m_tracksDisplayListReplay { false };
-    mutable std::unique_ptr<struct DisplayListDrawingContext> m_recordingContext;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
new file mode 100644 (file)
index 0000000..6169fa4
--- /dev/null
@@ -0,0 +1,2332 @@
+/*
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013, 2014 Adobe Systems Incorporated. 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 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 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 "config.h"
+#include "CanvasRenderingContext2DBase.h"
+
+#include "BitmapImage.h"
+#include "CSSFontSelector.h"
+#include "CSSParser.h"
+#include "CSSPropertyNames.h"
+#include "CachedImage.h"
+#include "CanvasGradient.h"
+#include "CanvasPattern.h"
+#include "DOMMatrix.h"
+#include "DOMMatrix2DInit.h"
+#include "DisplayListRecorder.h"
+#include "DisplayListReplayer.h"
+#include "FloatQuad.h"
+#include "HTMLImageElement.h"
+#include "HTMLVideoElement.h"
+#include "ImageBitmap.h"
+#include "ImageBuffer.h"
+#include "ImageData.h"
+#include "Path2D.h"
+#include "RenderElement.h"
+#include "RenderImage.h"
+#include "RenderLayer.h"
+#include "RenderTheme.h"
+#include "SecurityOrigin.h"
+#include "StrokeStyleApplier.h"
+#include "StyleProperties.h"
+#include "StyleResolver.h"
+#include "TextMetrics.h"
+#include "TextRun.h"
+#include <wtf/CheckedArithmetic.h>
+#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/TextStream.h>
+
+#if USE(CG) && !PLATFORM(IOS)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+#if USE(CG)
+const ImageSmoothingQuality defaultSmoothingQuality = ImageSmoothingQuality::Low;
+#else
+const ImageSmoothingQuality defaultSmoothingQuality = ImageSmoothingQuality::Medium;
+#endif
+
+const int CanvasRenderingContext2DBase::DefaultFontSize = 10;
+const char* const CanvasRenderingContext2DBase::DefaultFontFamily = "sans-serif";
+const char* const CanvasRenderingContext2DBase::DefaultFont = "10px sans-serif";
+
+struct DisplayListDrawingContext {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    GraphicsContext context;
+    DisplayList::DisplayList displayList;
+    
+    DisplayListDrawingContext(const FloatRect& clip)
+        : context([&](GraphicsContext& context) {
+            return std::make_unique<DisplayList::Recorder>(context, displayList, clip, AffineTransform());
+        })
+    {
+    }
+};
+
+typedef HashMap<const CanvasRenderingContext2DBase*, std::unique_ptr<DisplayList::DisplayList>> ContextDisplayListHashMap;
+
+static ContextDisplayListHashMap& contextDisplayListMap()
+{
+    static NeverDestroyed<ContextDisplayListHashMap> sharedHashMap;
+    return sharedHashMap;
+}
+
+class CanvasStrokeStyleApplier : public StrokeStyleApplier {
+public:
+    CanvasStrokeStyleApplier(CanvasRenderingContext2DBase* canvasContext)
+        : m_canvasContext(canvasContext)
+    {
+    }
+
+    void strokeStyle(GraphicsContext* c) override
+    {
+        c->setStrokeThickness(m_canvasContext->lineWidth());
+        c->setLineCap(m_canvasContext->getLineCap());
+        c->setLineJoin(m_canvasContext->getLineJoin());
+        c->setMiterLimit(m_canvasContext->miterLimit());
+        const Vector<float>& lineDash = m_canvasContext->getLineDash();
+        DashArray convertedLineDash(lineDash.size());
+        for (size_t i = 0; i < lineDash.size(); ++i)
+            convertedLineDash[i] = static_cast<DashArrayElement>(lineDash[i]);
+        c->setLineDash(convertedLineDash, m_canvasContext->lineDashOffset());
+    }
+
+private:
+    CanvasRenderingContext2DBase* m_canvasContext;
+};
+
+CanvasRenderingContext2DBase::CanvasRenderingContext2DBase(CanvasBase& canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
+    : CanvasRenderingContext(canvas)
+    , m_stateStack(1)
+    , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
+#if ENABLE(DASHBOARD_SUPPORT)
+    , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
+#endif
+{
+#if !ENABLE(DASHBOARD_SUPPORT)
+    ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode);
+#endif
+}
+
+void CanvasRenderingContext2DBase::unwindStateStack()
+{
+    // Ensure that the state stack in the ImageBuffer's context
+    // is cleared before destruction, to avoid assertions in the
+    // GraphicsContext dtor.
+    if (size_t stackSize = m_stateStack.size()) {
+        auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+        if (GraphicsContext* context = canvas.existingDrawingContext()) {
+            while (--stackSize)
+                context->restore();
+        }
+    }
+}
+
+CanvasRenderingContext2DBase::~CanvasRenderingContext2DBase()
+{
+#if !ASSERT_DISABLED
+    unwindStateStack();
+#endif
+
+    if (UNLIKELY(tracksDisplayListReplay()))
+        contextDisplayListMap().remove(this);
+}
+
+bool CanvasRenderingContext2DBase::isAccelerated() const
+{
+#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    if (!canvas.hasCreatedImageBuffer())
+        return false;
+    auto* context = drawingContext();
+    return context && context->isAcceleratedContext();
+#else
+    return false;
+#endif
+}
+
+void CanvasRenderingContext2DBase::reset()
+{
+    unwindStateStack();
+    m_stateStack.resize(1);
+    m_stateStack.first() = State();
+    m_path.clear();
+    m_unrealizedSaveCount = 0;
+    
+    m_recordingContext = nullptr;
+}
+
+CanvasRenderingContext2DBase::State::State()
+    : strokeStyle(Color::black)
+    , fillStyle(Color::black)
+    , lineWidth(1)
+    , lineCap(ButtCap)
+    , lineJoin(MiterJoin)
+    , miterLimit(10)
+    , shadowBlur(0)
+    , shadowColor(Color::transparent)
+    , globalAlpha(1)
+    , globalComposite(CompositeSourceOver)
+    , globalBlend(BlendModeNormal)
+    , hasInvertibleTransform(true)
+    , lineDashOffset(0)
+    , imageSmoothingEnabled(true)
+    , imageSmoothingQuality(defaultSmoothingQuality)
+    , textAlign(StartTextAlign)
+    , textBaseline(AlphabeticTextBaseline)
+    , direction(Direction::Inherit)
+    , unparsedFont(DefaultFont)
+{
+}
+
+CanvasRenderingContext2DBase::State::State(const State& other)
+    : unparsedStrokeColor(other.unparsedStrokeColor)
+    , unparsedFillColor(other.unparsedFillColor)
+    , strokeStyle(other.strokeStyle)
+    , fillStyle(other.fillStyle)
+    , lineWidth(other.lineWidth)
+    , lineCap(other.lineCap)
+    , lineJoin(other.lineJoin)
+    , miterLimit(other.miterLimit)
+    , shadowOffset(other.shadowOffset)
+    , shadowBlur(other.shadowBlur)
+    , shadowColor(other.shadowColor)
+    , globalAlpha(other.globalAlpha)
+    , globalComposite(other.globalComposite)
+    , globalBlend(other.globalBlend)
+    , transform(other.transform)
+    , hasInvertibleTransform(other.hasInvertibleTransform)
+    , lineDashOffset(other.lineDashOffset)
+    , imageSmoothingEnabled(other.imageSmoothingEnabled)
+    , imageSmoothingQuality(other.imageSmoothingQuality)
+    , textAlign(other.textAlign)
+    , textBaseline(other.textBaseline)
+    , direction(other.direction)
+    , unparsedFont(other.unparsedFont)
+    , font(other.font)
+{
+}
+
+CanvasRenderingContext2DBase::State& CanvasRenderingContext2DBase::State::operator=(const State& other)
+{
+    if (this == &other)
+        return *this;
+
+    unparsedStrokeColor = other.unparsedStrokeColor;
+    unparsedFillColor = other.unparsedFillColor;
+    strokeStyle = other.strokeStyle;
+    fillStyle = other.fillStyle;
+    lineWidth = other.lineWidth;
+    lineCap = other.lineCap;
+    lineJoin = other.lineJoin;
+    miterLimit = other.miterLimit;
+    shadowOffset = other.shadowOffset;
+    shadowBlur = other.shadowBlur;
+    shadowColor = other.shadowColor;
+    globalAlpha = other.globalAlpha;
+    globalComposite = other.globalComposite;
+    globalBlend = other.globalBlend;
+    transform = other.transform;
+    hasInvertibleTransform = other.hasInvertibleTransform;
+    imageSmoothingEnabled = other.imageSmoothingEnabled;
+    imageSmoothingQuality = other.imageSmoothingQuality;
+    textAlign = other.textAlign;
+    textBaseline = other.textBaseline;
+    direction = other.direction;
+    unparsedFont = other.unparsedFont;
+    font = other.font;
+
+    return *this;
+}
+
+CanvasRenderingContext2DBase::FontProxy::~FontProxy()
+{
+    if (realized())
+        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
+}
+
+CanvasRenderingContext2DBase::FontProxy::FontProxy(const FontProxy& other)
+    : m_font(other.m_font)
+{
+    if (realized())
+        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
+}
+
+auto CanvasRenderingContext2DBase::FontProxy::operator=(const FontProxy& other) -> FontProxy&
+{
+    if (realized())
+        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
+
+    m_font = other.m_font;
+
+    if (realized())
+        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
+
+    return *this;
+}
+
+inline void CanvasRenderingContext2DBase::FontProxy::update(FontSelector& selector)
+{
+    ASSERT(&selector == m_font.fontSelector()); // This is an invariant. We should only ever be registered for callbacks on m_font.m_fonts.m_fontSelector.
+    if (realized())
+        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
+    m_font.update(&selector);
+    if (realized())
+        m_font.fontSelector()->registerForInvalidationCallbacks(*this);
+    ASSERT(&selector == m_font.fontSelector());
+}
+
+void CanvasRenderingContext2DBase::FontProxy::fontsNeedUpdate(FontSelector& selector)
+{
+    ASSERT_ARG(selector, &selector == m_font.fontSelector());
+    ASSERT(realized());
+
+    update(selector);
+}
+
+inline void CanvasRenderingContext2DBase::FontProxy::initialize(FontSelector& fontSelector, const RenderStyle& newStyle)
+{
+    // Beware! m_font.fontSelector() might not point to document.fontSelector()!
+    ASSERT(newStyle.fontCascade().fontSelector() == &fontSelector);
+    if (realized())
+        m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
+    m_font = newStyle.fontCascade();
+    m_font.update(&fontSelector);
+    ASSERT(&fontSelector == m_font.fontSelector());
+    m_font.fontSelector()->registerForInvalidationCallbacks(*this);
+}
+
+inline const FontMetrics& CanvasRenderingContext2DBase::FontProxy::fontMetrics() const
+{
+    return m_font.fontMetrics();
+}
+
+inline const FontCascadeDescription& CanvasRenderingContext2DBase::FontProxy::fontDescription() const
+{
+    return m_font.fontDescription();
+}
+
+inline float CanvasRenderingContext2DBase::FontProxy::width(const TextRun& textRun, GlyphOverflow* overflow) const
+{
+    return m_font.width(textRun, 0, overflow);
+}
+
+inline void CanvasRenderingContext2DBase::FontProxy::drawBidiText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction action) const
+{
+    context.drawBidiText(m_font, run, point, action);
+}
+
+void CanvasRenderingContext2DBase::realizeSaves()
+{
+    if (m_unrealizedSaveCount)
+        realizeSavesLoop();
+
+    if (m_unrealizedSaveCount) {
+        static NeverDestroyed<String> consoleMessage(MAKE_STATIC_STRING_IMPL("CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save()."));
+
+        canvasBase().scriptExecutionContext()->addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage);
+    }
+}
+
+void CanvasRenderingContext2DBase::realizeSavesLoop()
+{
+    ASSERT(m_unrealizedSaveCount);
+    ASSERT(m_stateStack.size() >= 1);
+    GraphicsContext* context = drawingContext();
+    do {
+        if (m_stateStack.size() > MaxSaveCount)
+            break;
+        m_stateStack.append(state());
+        if (context)
+            context->save();
+    } while (--m_unrealizedSaveCount);
+}
+
+void CanvasRenderingContext2DBase::restore()
+{
+    if (m_unrealizedSaveCount) {
+        --m_unrealizedSaveCount;
+        return;
+    }
+    ASSERT(m_stateStack.size() >= 1);
+    if (m_stateStack.size() <= 1)
+        return;
+    m_path.transform(state().transform);
+    m_stateStack.removeLast();
+    if (std::optional<AffineTransform> inverse = state().transform.inverse())
+        m_path.transform(inverse.value());
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->restore();
+}
+
+void CanvasRenderingContext2DBase::setStrokeStyle(CanvasStyle style)
+{
+    if (!style.isValid())
+        return;
+
+    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentColor(style))
+        return;
+
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    if (style.isCurrentColor()) {
+        if (style.hasOverrideAlpha()) {
+            // FIXME: Should not use RGBA32 here.
+            style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas).rgb(), style.overrideAlpha()));
+        } else
+            style = CanvasStyle(currentColor(&canvas));
+    } else
+        checkOrigin(style.canvasPattern().get());
+
+    realizeSaves();
+    State& state = modifiableState();
+    state.strokeStyle = style;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    state.strokeStyle.applyStrokeColor(*c);
+    state.unparsedStrokeColor = String();
+}
+
+void CanvasRenderingContext2DBase::setFillStyle(CanvasStyle style)
+{
+    if (!style.isValid())
+        return;
+
+    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentColor(style))
+        return;
+
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    if (style.isCurrentColor()) {
+        if (style.hasOverrideAlpha()) {
+            // FIXME: Should not use RGBA32 here.
+            style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas).rgb(), style.overrideAlpha()));
+        } else
+            style = CanvasStyle(currentColor(&canvas));
+    } else
+        checkOrigin(style.canvasPattern().get());
+
+    realizeSaves();
+    State& state = modifiableState();
+    state.fillStyle = style;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    state.fillStyle.applyFillColor(*c);
+    state.unparsedFillColor = String();
+}
+
+float CanvasRenderingContext2DBase::lineWidth() const
+{
+    return state().lineWidth;
+}
+
+void CanvasRenderingContext2DBase::setLineWidth(float width)
+{
+    if (!(std::isfinite(width) && width > 0))
+        return;
+    if (state().lineWidth == width)
+        return;
+    realizeSaves();
+    modifiableState().lineWidth = width;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setStrokeThickness(width);
+}
+
+static CanvasLineCap toCanvasLineCap(LineCap lineCap)
+{
+    switch (lineCap) {
+    case ButtCap:
+        return CanvasLineCap::Butt;
+    case RoundCap:
+        return CanvasLineCap::Round;
+    case SquareCap:
+        return CanvasLineCap::Square;
+    }
+
+    ASSERT_NOT_REACHED();
+    return CanvasLineCap::Butt;
+}
+
+static LineCap fromCanvasLineCap(CanvasLineCap canvasLineCap)
+{
+    switch (canvasLineCap) {
+    case CanvasLineCap::Butt:
+        return ButtCap;
+    case CanvasLineCap::Round:
+        return RoundCap;
+    case CanvasLineCap::Square:
+        return SquareCap;
+    }
+    
+    ASSERT_NOT_REACHED();
+    return ButtCap;
+}
+
+CanvasLineCap CanvasRenderingContext2DBase::lineCap() const
+{
+    return toCanvasLineCap(state().lineCap);
+}
+
+void CanvasRenderingContext2DBase::setLineCap(CanvasLineCap canvasLineCap)
+{
+    auto lineCap = fromCanvasLineCap(canvasLineCap);
+    if (state().lineCap == lineCap)
+        return;
+    realizeSaves();
+    modifiableState().lineCap = lineCap;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setLineCap(lineCap);
+}
+
+void CanvasRenderingContext2DBase::setLineCap(const String& stringValue)
+{
+    CanvasLineCap cap;
+    if (stringValue == "butt")
+        cap = CanvasLineCap::Butt;
+    else if (stringValue == "round")
+        cap = CanvasLineCap::Round;
+    else if (stringValue == "square")
+        cap = CanvasLineCap::Square;
+    else
+        return;
+    
+    setLineCap(cap);
+}
+
+static CanvasLineJoin toCanvasLineJoin(LineJoin lineJoin)
+{
+    switch (lineJoin) {
+    case RoundJoin:
+        return CanvasLineJoin::Round;
+    case BevelJoin:
+        return CanvasLineJoin::Bevel;
+    case MiterJoin:
+        return CanvasLineJoin::Miter;
+    }
+
+    ASSERT_NOT_REACHED();
+    return CanvasLineJoin::Round;
+}
+
+static LineJoin fromCanvasLineJoin(CanvasLineJoin canvasLineJoin)
+{
+    switch (canvasLineJoin) {
+    case CanvasLineJoin::Round:
+        return RoundJoin;
+    case CanvasLineJoin::Bevel:
+        return BevelJoin;
+    case CanvasLineJoin::Miter:
+        return MiterJoin;
+    }
+    
+    ASSERT_NOT_REACHED();
+    return RoundJoin;
+}
+
+CanvasLineJoin CanvasRenderingContext2DBase::lineJoin() const
+{
+    return toCanvasLineJoin(state().lineJoin);
+}
+
+void CanvasRenderingContext2DBase::setLineJoin(CanvasLineJoin canvasLineJoin)
+{
+    auto lineJoin = fromCanvasLineJoin(canvasLineJoin);
+    if (state().lineJoin == lineJoin)
+        return;
+    realizeSaves();
+    modifiableState().lineJoin = lineJoin;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setLineJoin(lineJoin);
+}
+
+void CanvasRenderingContext2DBase::setLineJoin(const String& stringValue)
+{
+    CanvasLineJoin join;
+    if (stringValue == "round")
+        join = CanvasLineJoin::Round;
+    else if (stringValue == "bevel")
+        join = CanvasLineJoin::Bevel;
+    else if (stringValue == "miter")
+        join = CanvasLineJoin::Miter;
+    else
+        return;
+
+    setLineJoin(join);
+}
+
+float CanvasRenderingContext2DBase::miterLimit() const
+{
+    return state().miterLimit;
+}
+
+void CanvasRenderingContext2DBase::setMiterLimit(float limit)
+{
+    if (!(std::isfinite(limit) && limit > 0))
+        return;
+    if (state().miterLimit == limit)
+        return;
+    realizeSaves();
+    modifiableState().miterLimit = limit;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setMiterLimit(limit);
+}
+
+float CanvasRenderingContext2DBase::shadowOffsetX() const
+{
+    return state().shadowOffset.width();
+}
+
+void CanvasRenderingContext2DBase::setShadowOffsetX(float x)
+{
+    if (!std::isfinite(x))
+        return;
+    if (state().shadowOffset.width() == x)
+        return;
+    realizeSaves();
+    modifiableState().shadowOffset.setWidth(x);
+    applyShadow();
+}
+
+float CanvasRenderingContext2DBase::shadowOffsetY() const
+{
+    return state().shadowOffset.height();
+}
+
+void CanvasRenderingContext2DBase::setShadowOffsetY(float y)
+{
+    if (!std::isfinite(y))
+        return;
+    if (state().shadowOffset.height() == y)
+        return;
+    realizeSaves();
+    modifiableState().shadowOffset.setHeight(y);
+    applyShadow();
+}
+
+float CanvasRenderingContext2DBase::shadowBlur() const
+{
+    return state().shadowBlur;
+}
+
+void CanvasRenderingContext2DBase::setShadowBlur(float blur)
+{
+    if (!(std::isfinite(blur) && blur >= 0))
+        return;
+    if (state().shadowBlur == blur)
+        return;
+    realizeSaves();
+    modifiableState().shadowBlur = blur;
+    applyShadow();
+}
+
+String CanvasRenderingContext2DBase::shadowColor() const
+{
+    return Color(state().shadowColor).serialized();
+}
+
+void CanvasRenderingContext2DBase::setShadowColor(const String& colorString)
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    Color color = parseColorOrCurrentColor(colorString, &canvas);
+    if (!color.isValid())
+        return;
+    if (state().shadowColor == color)
+        return;
+    realizeSaves();
+    modifiableState().shadowColor = color;
+    applyShadow();
+}
+
+const Vector<float>& CanvasRenderingContext2DBase::getLineDash() const
+{
+    return state().lineDash;
+}
+
+static bool lineDashSequenceIsValid(const Vector<float>& dash)
+{
+    for (size_t i = 0; i < dash.size(); i++) {
+        if (!std::isfinite(dash[i]) || dash[i] < 0)
+            return false;
+    }
+    return true;
+}
+
+void CanvasRenderingContext2DBase::setLineDash(const Vector<float>& dash)
+{
+    if (!lineDashSequenceIsValid(dash))
+        return;
+
+    realizeSaves();
+    modifiableState().lineDash = dash;
+    // Spec requires the concatenation of two copies the dash list when the
+    // number of elements is odd
+    if (dash.size() % 2)
+        modifiableState().lineDash.appendVector(dash);
+
+    applyLineDash();
+}
+
+void CanvasRenderingContext2DBase::setWebkitLineDash(const Vector<float>& dash)
+{
+    if (!lineDashSequenceIsValid(dash))
+        return;
+
+    realizeSaves();
+    modifiableState().lineDash = dash;
+
+    applyLineDash();
+}
+
+float CanvasRenderingContext2DBase::lineDashOffset() const
+{
+    return state().lineDashOffset;
+}
+
+void CanvasRenderingContext2DBase::setLineDashOffset(float offset)
+{
+    if (!std::isfinite(offset) || state().lineDashOffset == offset)
+        return;
+
+    realizeSaves();
+    modifiableState().lineDashOffset = offset;
+    applyLineDash();
+}
+
+void CanvasRenderingContext2DBase::applyLineDash() const
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    DashArray convertedLineDash(state().lineDash.size());
+    for (size_t i = 0; i < state().lineDash.size(); ++i)
+        convertedLineDash[i] = static_cast<DashArrayElement>(state().lineDash[i]);
+    c->setLineDash(convertedLineDash, state().lineDashOffset);
+}
+
+float CanvasRenderingContext2DBase::globalAlpha() const
+{
+    return state().globalAlpha;
+}
+
+void CanvasRenderingContext2DBase::setGlobalAlpha(float alpha)
+{
+    if (!(alpha >= 0 && alpha <= 1))
+        return;
+    if (state().globalAlpha == alpha)
+        return;
+    realizeSaves();
+    modifiableState().globalAlpha = alpha;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setAlpha(alpha);
+}
+
+String CanvasRenderingContext2DBase::globalCompositeOperation() const
+{
+    return compositeOperatorName(state().globalComposite, state().globalBlend);
+}
+
+void CanvasRenderingContext2DBase::setGlobalCompositeOperation(const String& operation)
+{
+    CompositeOperator op = CompositeSourceOver;
+    BlendMode blendMode = BlendModeNormal;
+    if (!parseCompositeAndBlendOperator(operation, op, blendMode))
+        return;
+    if ((state().globalComposite == op) && (state().globalBlend == blendMode))
+        return;
+    realizeSaves();
+    modifiableState().globalComposite = op;
+    modifiableState().globalBlend = blendMode;
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    c->setCompositeOperation(op, blendMode);
+}
+
+void CanvasRenderingContext2DBase::scale(float sx, float sy)
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    if (!std::isfinite(sx) || !std::isfinite(sy))
+        return;
+
+    AffineTransform newTransform = state().transform;
+    newTransform.scaleNonUniform(sx, sy);
+    if (state().transform == newTransform)
+        return;
+
+    realizeSaves();
+
+    if (!sx || !sy) {
+        modifiableState().hasInvertibleTransform = false;
+        return;
+    }
+
+    modifiableState().transform = newTransform;
+    c->scale(FloatSize(sx, sy));
+    m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
+}
+
+void CanvasRenderingContext2DBase::rotate(float angleInRadians)
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    if (!std::isfinite(angleInRadians))
+        return;
+
+    AffineTransform newTransform = state().transform;
+    newTransform.rotate(angleInRadians / piDouble * 180.0);
+    if (state().transform == newTransform)
+        return;
+
+    realizeSaves();
+
+    modifiableState().transform = newTransform;
+    c->rotate(angleInRadians);
+    m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
+}
+
+void CanvasRenderingContext2DBase::translate(float tx, float ty)
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    if (!std::isfinite(tx) | !std::isfinite(ty))
+        return;
+
+    AffineTransform newTransform = state().transform;
+    newTransform.translate(tx, ty);
+    if (state().transform == newTransform)
+        return;
+
+    realizeSaves();
+
+    modifiableState().transform = newTransform;
+    c->translate(tx, ty);
+    m_path.transform(AffineTransform().translate(-tx, -ty));
+}
+
+void CanvasRenderingContext2DBase::transform(float m11, float m12, float m21, float m22, float dx, float dy)
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
+        return;
+
+    AffineTransform transform(m11, m12, m21, m22, dx, dy);
+    AffineTransform newTransform = state().transform * transform;
+    if (state().transform == newTransform)
+        return;
+
+    realizeSaves();
+
+    if (auto inverse = transform.inverse()) {
+        modifiableState().transform = newTransform;
+        c->concatCTM(transform);
+        m_path.transform(inverse.value());
+        return;
+    }
+    modifiableState().hasInvertibleTransform = false;
+}
+
+Ref<DOMMatrix> CanvasRenderingContext2DBase::getTransform() const
+{
+    return DOMMatrix::create(state().transform.toTransformationMatrix(), DOMMatrixReadOnly::Is2D::Yes);
+}
+
+void CanvasRenderingContext2DBase::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+
+    if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
+        return;
+
+    resetTransform();
+    transform(m11, m12, m21, m22, dx, dy);
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::setTransform(DOMMatrix2DInit&& matrixInit)
+{
+    auto checkValid = DOMMatrixReadOnly::validateAndFixup(matrixInit);
+    if (checkValid.hasException())
+        return checkValid.releaseException();
+
+    setTransform(matrixInit.a.value_or(1), matrixInit.b.value_or(0), matrixInit.c.value_or(0), matrixInit.d.value_or(1), matrixInit.e.value_or(0), matrixInit.f.value_or(0));
+    return { };
+}
+
+void CanvasRenderingContext2DBase::resetTransform()
+{
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+
+    AffineTransform ctm = state().transform;
+    bool hasInvertibleTransform = state().hasInvertibleTransform;
+
+    realizeSaves();
+
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    c->setCTM(canvas.baseTransform());
+    modifiableState().transform = AffineTransform();
+
+    if (hasInvertibleTransform)
+        m_path.transform(ctm);
+
+    modifiableState().hasInvertibleTransform = true;
+}
+
+void CanvasRenderingContext2DBase::setStrokeColor(const String& color, std::optional<float> alpha)
+{
+    if (alpha) {
+        setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
+        return;
+    }
+
+    if (color == state().unparsedStrokeColor)
+        return;
+
+    realizeSaves();
+    setStrokeStyle(CanvasStyle::createFromString(color));
+    modifiableState().unparsedStrokeColor = color;
+}
+
+void CanvasRenderingContext2DBase::setStrokeColor(float grayLevel, float alpha)
+{
+    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
+        return;
+    setStrokeStyle(CanvasStyle(grayLevel, alpha));
+}
+
+void CanvasRenderingContext2DBase::setStrokeColor(float r, float g, float b, float a)
+{
+    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(r, g, b, a))
+        return;
+    setStrokeStyle(CanvasStyle(r, g, b, a));
+}
+
+void CanvasRenderingContext2DBase::setStrokeColor(float c, float m, float y, float k, float a)
+{
+    if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentCMYKA(c, m, y, k, a))
+        return;
+    setStrokeStyle(CanvasStyle(c, m, y, k, a));
+}
+
+void CanvasRenderingContext2DBase::setFillColor(const String& color, std::optional<float> alpha)
+{
+    if (alpha) {
+        setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
+        return;
+    }
+
+    if (color == state().unparsedFillColor)
+        return;
+
+    realizeSaves();
+    setFillStyle(CanvasStyle::createFromString(color));
+    modifiableState().unparsedFillColor = color;
+}
+
+void CanvasRenderingContext2DBase::setFillColor(float grayLevel, float alpha)
+{
+    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
+        return;
+    setFillStyle(CanvasStyle(grayLevel, alpha));
+}
+
+void CanvasRenderingContext2DBase::setFillColor(float r, float g, float b, float a)
+{
+    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(r, g, b, a))
+        return;
+    setFillStyle(CanvasStyle(r, g, b, a));
+}
+
+void CanvasRenderingContext2DBase::setFillColor(float c, float m, float y, float k, float a)
+{
+    if (state().fillStyle.isValid() && state().fillStyle.isEquivalentCMYKA(c, m, y, k, a))
+        return;
+    setFillStyle(CanvasStyle(c, m, y, k, a));
+}
+
+void CanvasRenderingContext2DBase::beginPath()
+{
+    m_path.clear();
+}
+
+static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
+{
+    if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
+        return false;
+
+    if (!width && !height)
+        return false;
+
+    if (width < 0) {
+        width = -width;
+        x -= width;
+    }
+
+    if (height < 0) {
+        height = -height;
+        y -= height;
+    }
+
+    return true;
+}
+
+bool CanvasRenderingContext2DBase::isFullCanvasCompositeMode(CompositeOperator op)
+{
+    // See 4.8.11.1.3 Compositing
+    // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
+    // implement the specification's behavior.
+    return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
+}
+
+static WindRule toWindRule(CanvasFillRule rule)
+{
+    return rule == CanvasFillRule::Nonzero ? RULE_NONZERO : RULE_EVENODD;
+}
+
+void CanvasRenderingContext2DBase::fill(CanvasFillRule windingRule)
+{
+    fillInternal(m_path, windingRule);
+    clearPathForDashboardBackwardCompatibilityMode();
+}
+
+void CanvasRenderingContext2DBase::stroke()
+{
+    strokeInternal(m_path);
+    clearPathForDashboardBackwardCompatibilityMode();
+}
+
+void CanvasRenderingContext2DBase::clip(CanvasFillRule windingRule)
+{
+    clipInternal(m_path, windingRule);
+    clearPathForDashboardBackwardCompatibilityMode();
+}
+
+void CanvasRenderingContext2DBase::fill(Path2D& path, CanvasFillRule windingRule)
+{
+    fillInternal(path.path(), windingRule);
+}
+
+void CanvasRenderingContext2DBase::stroke(Path2D& path)
+{
+    strokeInternal(path.path());
+}
+
+void CanvasRenderingContext2DBase::clip(Path2D& path, CanvasFillRule windingRule)
+{
+    clipInternal(path.path(), windingRule);
+}
+
+void CanvasRenderingContext2DBase::fillInternal(const Path& path, CanvasFillRule windingRule)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    // If gradient size is zero, then paint nothing.
+    auto gradient = c->fillGradient();
+    if (gradient && gradient->isZeroSize())
+        return;
+
+    if (!path.isEmpty()) {
+        auto savedFillRule = c->fillRule();
+        c->setFillRule(toWindRule(windingRule));
+
+        if (isFullCanvasCompositeMode(state().globalComposite)) {
+            beginCompositeLayer();
+            c->fillPath(path);
+            endCompositeLayer();
+            didDrawEntireCanvas();
+        } else if (state().globalComposite == CompositeCopy) {
+            clearCanvas();
+            c->fillPath(path);
+            didDrawEntireCanvas();
+        } else {
+            c->fillPath(path);
+            didDraw(path.fastBoundingRect());
+        }
+        
+        c->setFillRule(savedFillRule);
+    }
+}
+
+void CanvasRenderingContext2DBase::strokeInternal(const Path& path)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    // If gradient size is zero, then paint nothing.
+    auto gradient = c->strokeGradient();
+    if (gradient && gradient->isZeroSize())
+        return;
+
+    if (!path.isEmpty()) {
+        if (isFullCanvasCompositeMode(state().globalComposite)) {
+            beginCompositeLayer();
+            c->strokePath(path);
+            endCompositeLayer();
+            didDrawEntireCanvas();
+        } else if (state().globalComposite == CompositeCopy) {
+            clearCanvas();
+            c->strokePath(path);
+            didDrawEntireCanvas();
+        } else {
+            FloatRect dirtyRect = path.fastBoundingRect();
+            inflateStrokeRect(dirtyRect);
+            c->strokePath(path);
+            didDraw(dirtyRect);
+        }
+    }
+}
+
+void CanvasRenderingContext2DBase::clipInternal(const Path& path, CanvasFillRule windingRule)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    realizeSaves();
+    c->canvasClip(path, toWindRule(windingRule));
+}
+
+inline void CanvasRenderingContext2DBase::beginCompositeLayer()
+{
+#if !USE(CAIRO)
+    drawingContext()->beginTransparencyLayer(1);
+#endif
+}
+
+inline void CanvasRenderingContext2DBase::endCompositeLayer()
+{
+#if !USE(CAIRO)
+    drawingContext()->endTransparencyLayer();    
+#endif
+}
+
+bool CanvasRenderingContext2DBase::isPointInPath(float x, float y, CanvasFillRule windingRule)
+{
+    return isPointInPathInternal(m_path, x, y, windingRule);
+}
+
+bool CanvasRenderingContext2DBase::isPointInStroke(float x, float y)
+{
+    return isPointInStrokeInternal(m_path, x, y);
+}
+
+bool CanvasRenderingContext2DBase::isPointInPath(Path2D& path, float x, float y, CanvasFillRule windingRule)
+{
+    return isPointInPathInternal(path.path(), x, y, windingRule);
+}
+
+bool CanvasRenderingContext2DBase::isPointInStroke(Path2D& path, float x, float y)
+{
+    return isPointInStrokeInternal(path.path(), x, y);
+}
+
+bool CanvasRenderingContext2DBase::isPointInPathInternal(const Path& path, float x, float y, CanvasFillRule windingRule)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return false;
+    if (!state().hasInvertibleTransform)
+        return false;
+
+    auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
+
+    if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
+        return false;
+
+    return path.contains(transformedPoint, toWindRule(windingRule));
+}
+
+bool CanvasRenderingContext2DBase::isPointInStrokeInternal(const Path& path, float x, float y)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return false;
+    if (!state().hasInvertibleTransform)
+        return false;
+
+    auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
+    if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
+        return false;
+
+    CanvasStrokeStyleApplier applier(this);
+    return path.strokeContains(&applier, transformedPoint);
+}
+
+void CanvasRenderingContext2DBase::clearRect(float x, float y, float width, float height)
+{
+    if (!validateRectForCanvas(x, y, width, height))
+        return;
+    auto* context = drawingContext();
+    if (!context)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+    FloatRect rect(x, y, width, height);
+
+    bool saved = false;
+    if (shouldDrawShadows()) {
+        context->save();
+        saved = true;
+        context->setLegacyShadow(FloatSize(), 0, Color::transparent);
+    }
+    if (state().globalAlpha != 1) {
+        if (!saved) {
+            context->save();
+            saved = true;
+        }
+        context->setAlpha(1);
+    }
+    if (state().globalComposite != CompositeSourceOver) {
+        if (!saved) {
+            context->save();
+            saved = true;
+        }
+        context->setCompositeOperation(CompositeSourceOver);
+    }
+    context->clearRect(rect);
+    if (saved)
+        context->restore();
+    didDraw(rect);
+}
+
+void CanvasRenderingContext2DBase::fillRect(float x, float y, float width, float height)
+{
+    if (!validateRectForCanvas(x, y, width, height))
+        return;
+
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+    // from the HTML5 Canvas spec:
+    // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
+    // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
+    auto gradient = c->fillGradient();
+    if (gradient && gradient->isZeroSize())
+        return;
+
+    FloatRect rect(x, y, width, height);
+
+    if (rectContainsCanvas(rect)) {
+        c->fillRect(rect);
+        didDrawEntireCanvas();
+    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
+        beginCompositeLayer();
+        c->fillRect(rect);
+        endCompositeLayer();
+        didDrawEntireCanvas();
+    } else if (state().globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->fillRect(rect);
+        didDrawEntireCanvas();
+    } else {
+        c->fillRect(rect);
+        didDraw(rect);
+    }
+}
+
+void CanvasRenderingContext2DBase::strokeRect(float x, float y, float width, float height)
+{
+    if (!validateRectForCanvas(x, y, width, height))
+        return;
+
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+    if (!(state().lineWidth >= 0))
+        return;
+
+    // If gradient size is zero, then paint nothing.
+    auto gradient = c->strokeGradient();
+    if (gradient && gradient->isZeroSize())
+        return;
+
+    FloatRect rect(x, y, width, height);
+    if (isFullCanvasCompositeMode(state().globalComposite)) {
+        beginCompositeLayer();
+        c->strokeRect(rect, state().lineWidth);
+        endCompositeLayer();
+        didDrawEntireCanvas();
+    } else if (state().globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->strokeRect(rect, state().lineWidth);
+        didDrawEntireCanvas();
+    } else {
+        FloatRect boundingRect = rect;
+        boundingRect.inflate(state().lineWidth / 2);
+        c->strokeRect(rect, state().lineWidth);
+        didDraw(boundingRect);
+    }
+}
+
+void CanvasRenderingContext2DBase::setShadow(float width, float height, float blur, const String& colorString, std::optional<float> alpha)
+{
+    Color color = Color::transparent;
+    if (!colorString.isNull()) {
+        auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+        color = parseColorOrCurrentColor(colorString, &canvas);
+        if (!color.isValid())
+            return;
+    }
+    // FIXME: Should not use RGBA32 here.
+    setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(color.rgb(), alpha));
+}
+
+void CanvasRenderingContext2DBase::setShadow(float width, float height, float blur, float grayLevel, float alpha)
+{
+    setShadow(FloatSize(width, height), blur, Color(grayLevel, grayLevel, grayLevel, alpha));
+}
+
+void CanvasRenderingContext2DBase::setShadow(float width, float height, float blur, float r, float g, float b, float a)
+{
+    setShadow(FloatSize(width, height), blur, Color(r, g, b, a));
+}
+
+void CanvasRenderingContext2DBase::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
+{
+    setShadow(FloatSize(width, height), blur, Color(c, m, y, k, a));
+}
+
+void CanvasRenderingContext2DBase::clearShadow()
+{
+    setShadow(FloatSize(), 0, Color::transparent);
+}
+
+void CanvasRenderingContext2DBase::setShadow(const FloatSize& offset, float blur, const Color& color)
+{
+    if (state().shadowOffset == offset && state().shadowBlur == blur && state().shadowColor == color)
+        return;
+    bool wasDrawingShadows = shouldDrawShadows();
+    realizeSaves();
+    modifiableState().shadowOffset = offset;
+    modifiableState().shadowBlur = blur;
+    modifiableState().shadowColor = color;
+    if (!wasDrawingShadows && !shouldDrawShadows())
+        return;
+    applyShadow();
+}
+
+void CanvasRenderingContext2DBase::applyShadow()
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+
+    if (shouldDrawShadows()) {
+        float width = state().shadowOffset.width();
+        float height = state().shadowOffset.height();
+        c->setLegacyShadow(FloatSize(width, -height), state().shadowBlur, state().shadowColor);
+    } else
+        c->setLegacyShadow(FloatSize(), 0, Color::transparent);
+}
+
+bool CanvasRenderingContext2DBase::shouldDrawShadows() const
+{
+    return state().shadowColor.isVisible() && (state().shadowBlur || !state().shadowOffset.isZero());
+}
+
+enum class ImageSizeType { AfterDevicePixelRatio, BeforeDevicePixelRatio };
+static LayoutSize size(HTMLImageElement& element, ImageSizeType sizeType = ImageSizeType::BeforeDevicePixelRatio)
+{
+    LayoutSize size;
+    if (auto* cachedImage = element.cachedImage()) {
+        size = cachedImage->imageSizeForRenderer(element.renderer(), 1.0f); // FIXME: Not sure about this.
+        if (sizeType == ImageSizeType::AfterDevicePixelRatio && is<RenderImage>(element.renderer()) && cachedImage->image() && !cachedImage->image()->hasRelativeWidth())
+            size.scale(downcast<RenderImage>(*element.renderer()).imageDevicePixelRatio());
+    }
+    return size;
+}
+
+static inline FloatSize size(HTMLCanvasElement& canvasElement)
+{
+    return canvasElement.size();
+}
+
+static inline FloatSize size(ImageBitmap& imageBitmap)
+{
+    return FloatSize { static_cast<float>(imageBitmap.width()), static_cast<float>(imageBitmap.height()) };
+}
+
+#if ENABLE(VIDEO)
+
+static inline FloatSize size(HTMLVideoElement& video)
+{
+    auto player = video.player();
+    if (!player)
+        return { };
+    return player->naturalSize();
+}
+
+#endif
+
+static inline FloatRect normalizeRect(const FloatRect& rect)
+{
+    return FloatRect(std::min(rect.x(), rect.maxX()),
+        std::min(rect.y(), rect.maxY()),
+        std::max(rect.width(), -rect.width()),
+        std::max(rect.height(), -rect.height()));
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(CanvasImageSource&& image, float dx, float dy)
+{
+    return WTF::switchOn(image,
+        [&] (RefPtr<HTMLImageElement>& imageElement) -> ExceptionOr<void> {
+            LayoutSize destRectSize = size(*imageElement, ImageSizeType::AfterDevicePixelRatio);
+            LayoutSize sourceRectSize = size(*imageElement, ImageSizeType::BeforeDevicePixelRatio);
+            return this->drawImage(*imageElement, FloatRect { 0, 0, sourceRectSize.width(), sourceRectSize.height() }, FloatRect { dx, dy, destRectSize.width(), destRectSize.height() });
+        },
+        [&] (auto& element) -> ExceptionOr<void> {
+            FloatSize elementSize = size(*element);
+            return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, elementSize.width(), elementSize.height() });
+        }
+    );
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(CanvasImageSource&& image, float dx, float dy, float dw, float dh)
+{
+    return WTF::switchOn(image,
+        [&] (auto& element) -> ExceptionOr<void> {
+            FloatSize elementSize = size(*element);
+            return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, dw, dh });
+        }
+    );
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(CanvasImageSource&& image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh)
+{
+    return WTF::switchOn(image,
+        [&] (auto& element) -> ExceptionOr<void> {
+            return this->drawImage(*element, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh });
+        }
+    );
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect)
+{
+    return drawImage(imageElement, srcRect, dstRect, state().globalComposite, state().globalBlend);
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode)
+{
+    if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
+        || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
+        return { };
+
+    if (!dstRect.width() || !dstRect.height())
+        return { };
+
+    if (!imageElement.complete())
+        return { };
+
+    FloatRect normalizedSrcRect = normalizeRect(srcRect);
+    FloatRect normalizedDstRect = normalizeRect(dstRect);
+
+    FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));
+    if (!srcRect.width() || !srcRect.height())
+        return Exception { IndexSizeError };
+
+    // When the source rectangle is outside the source image, the source rectangle must be clipped
+    // to the source image and the destination rectangle must be clipped in the same proportion.
+    FloatRect originalNormalizedSrcRect = normalizedSrcRect;
+    normalizedSrcRect.intersect(imageRect);
+    if (normalizedSrcRect.isEmpty())
+        return { };
+
+    if (normalizedSrcRect != originalNormalizedSrcRect) {
+        normalizedDstRect.setWidth(normalizedDstRect.width() * normalizedSrcRect.width() / originalNormalizedSrcRect.width());
+        normalizedDstRect.setHeight(normalizedDstRect.height() * normalizedSrcRect.height() / originalNormalizedSrcRect.height());
+        if (normalizedDstRect.isEmpty())
+            return { };
+    }
+
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return { };
+    if (!state().hasInvertibleTransform)
+        return { };
+
+    CachedImage* cachedImage = imageElement.cachedImage();
+    if (!cachedImage)
+        return { };
+
+    RefPtr<Image> image = cachedImage->imageForRenderer(imageElement.renderer());
+    if (!image)
+        return { };
+
+    ImageObserver* observer = image->imageObserver();
+
+    if (image->isSVGImage()) {
+        image->setImageObserver(nullptr);
+        image->setContainerSize(imageRect.size());
+    }
+
+    if (image->isBitmapImage())
+        downcast<BitmapImage>(*image).updateFromSettings(imageElement.document().settings());
+
+    if (rectContainsCanvas(normalizedDstRect)) {
+        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
+        didDrawEntireCanvas();
+    } else if (isFullCanvasCompositeMode(op)) {
+        fullCanvasCompositedDrawImage(*image, normalizedDstRect, normalizedSrcRect, op);
+        didDrawEntireCanvas();
+    } else if (op == CompositeCopy) {
+        clearCanvas();
+        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
+        didDrawEntireCanvas();
+    } else {
+        c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
+        didDraw(normalizedDstRect);
+    }
+    
+    if (image->isSVGImage())
+        image->setImageObserver(observer);
+
+    checkOrigin(&imageElement);
+
+    return { };
+}
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLCanvasElement& sourceCanvas, const FloatRect& srcRect, const FloatRect& dstRect)
+{
+    FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas.size());
+
+    if (!srcCanvasRect.width() || !srcCanvasRect.height())
+        return Exception { InvalidStateError };
+
+    if (!srcRect.width() || !srcRect.height())
+        return Exception { IndexSizeError };
+
+    if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
+        return { };
+
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return { };
+    if (!state().hasInvertibleTransform)
+        return { };
+
+    // FIXME: Do this through platform-independent GraphicsContext API.
+    ImageBuffer* buffer = sourceCanvas.buffer();
+    if (!buffer)
+        return { };
+
+    checkOrigin(&sourceCanvas);
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+    // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas.makeRenderingResultsAvailable()
+    // as that will do a readback to software.
+    RefPtr<CanvasRenderingContext> sourceContext = sourceCanvas.renderingContext();
+    // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
+    if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d())
+        sourceCanvas.makeRenderingResultsAvailable();
+#else
+    sourceCanvas.makeRenderingResultsAvailable();
+#endif
+
+    if (rectContainsCanvas(dstRect)) {
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDrawEntireCanvas();
+    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
+        fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
+        didDrawEntireCanvas();
+    } else if (state().globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDrawEntireCanvas();
+    } else {
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDraw(dstRect);
+    }
+
+    return { };
+}
+
+#if ENABLE(VIDEO)
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(HTMLVideoElement& video, const FloatRect& srcRect, const FloatRect& dstRect)
+{
+    if (video.readyState() == HTMLMediaElement::HAVE_NOTHING || video.readyState() == HTMLMediaElement::HAVE_METADATA)
+        return { };
+
+    FloatRect videoRect = FloatRect(FloatPoint(), size(video));
+    if (!srcRect.width() || !srcRect.height())
+        return Exception { IndexSizeError };
+
+    if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
+        return { };
+
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return { };
+    if (!state().hasInvertibleTransform)
+        return { };
+
+    checkOrigin(&video);
+
+#if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
+    if (NativeImagePtr image = video.nativeImageForCurrentTime()) {
+        c->drawNativeImage(image, FloatSize(video.videoWidth(), video.videoHeight()), dstRect, srcRect);
+        if (rectContainsCanvas(dstRect))
+            didDrawEntireCanvas();
+        else
+            didDraw(dstRect);
+
+        return { };
+    }
+#endif
+
+    GraphicsContextStateSaver stateSaver(*c);
+    c->clip(dstRect);
+    c->translate(dstRect.location());
+    c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
+    c->translate(-srcRect.location());
+    video.paintCurrentFrameInContext(*c, FloatRect(FloatPoint(), size(video)));
+    stateSaver.restore();
+    didDraw(dstRect);
+
+    return { };
+}
+
+#endif
+
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(ImageBitmap& imageBitmap, const FloatRect& srcRect, const FloatRect& dstRect)
+{
+    if (!imageBitmap.width() || !imageBitmap.height())
+        return Exception { InvalidStateError };
+
+    if (!srcRect.width() || !srcRect.height())
+        return Exception { IndexSizeError };
+
+    FloatRect srcBitmapRect = FloatRect(FloatPoint(), FloatSize(imageBitmap.width(), imageBitmap.height()));
+
+    if (!srcBitmapRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
+        return { };
+
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return { };
+    if (!state().hasInvertibleTransform)
+        return { };
+
+    ImageBuffer* buffer = imageBitmap.buffer();
+    if (!buffer)
+        return { };
+
+    checkOrigin(&imageBitmap);
+
+    if (rectContainsCanvas(dstRect)) {
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDrawEntireCanvas();
+    } else if (isFullCanvasCompositeMode(state().globalComposite)) {
+        fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
+        didDrawEntireCanvas();
+    } else if (state().globalComposite == CompositeCopy) {
+        clearCanvas();
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDrawEntireCanvas();
+    } else {
+        c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
+        didDraw(dstRect);
+    }
+
+    return { };
+}
+
+void CanvasRenderingContext2DBase::drawImageFromRect(HTMLImageElement& imageElement, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, const String& compositeOperation)
+{
+    CompositeOperator op;
+    auto blendOp = BlendModeNormal;
+    if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != BlendModeNormal)
+        op = CompositeSourceOver;
+    drawImage(imageElement, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh }, op, BlendModeNormal);
+}
+
+void CanvasRenderingContext2DBase::clearCanvas()
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+
+    c->save();
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    c->setCTM(canvas.baseTransform());
+    c->clearRect(FloatRect(0, 0, canvas.width(), canvas.height()));
+    c->restore();
+}
+
+Path CanvasRenderingContext2DBase::transformAreaToDevice(const Path& path) const
+{
+    Path transformed(path);
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    transformed.transform(state().transform);
+    transformed.transform(canvas.baseTransform());
+    return transformed;
+}
+
+Path CanvasRenderingContext2DBase::transformAreaToDevice(const FloatRect& rect) const
+{
+    Path path;
+    path.addRect(rect);
+    return transformAreaToDevice(path);
+}
+
+bool CanvasRenderingContext2DBase::rectContainsCanvas(const FloatRect& rect) const
+{
+    FloatQuad quad(rect);
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    FloatQuad canvasQuad(FloatRect(0, 0, canvas.width(), canvas.height()));
+    return state().transform.mapQuad(quad).containsQuad(canvasQuad);
+}
+
+template<class T> IntRect CanvasRenderingContext2DBase::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset)
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    IntRect canvasRect(0, 0, canvas.width(), canvas.height());
+    canvasRect = canvas.baseTransform().mapRect(canvasRect);
+    Path path = transformAreaToDevice(area);
+    IntRect bufferRect = enclosingIntRect(path.fastBoundingRect());
+    IntPoint originalLocation = bufferRect.location();
+    bufferRect.intersect(canvasRect);
+    if (croppedOffset)
+        *croppedOffset = originalLocation - bufferRect.location();
+    return bufferRect;
+}
+
+std::unique_ptr<ImageBuffer> CanvasRenderingContext2DBase::createCompositingBuffer(const IntRect& bufferRect)
+{
+    return ImageBuffer::create(bufferRect.size(), isAccelerated() ? Accelerated : Unaccelerated);
+}
+
+void CanvasRenderingContext2DBase::compositeBuffer(ImageBuffer& buffer, const IntRect& bufferRect, CompositeOperator op)
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    IntRect canvasRect(0, 0, canvas.width(), canvas.height());
+    canvasRect = canvas.baseTransform().mapRect(canvasRect);
+
+    auto* c = drawingContext();
+    if (!c)
+        return;
+
+    c->save();
+    c->setCTM(AffineTransform());
+    c->setCompositeOperation(op);
+
+    c->save();
+    c->clipOut(bufferRect);
+    c->clearRect(canvasRect);
+    c->restore();
+    c->drawImageBuffer(buffer, bufferRect.location(), state().globalComposite);
+    c->restore();
+}
+
+static void drawImageToContext(Image& image, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
+{
+    context.drawImage(image, dest, src, op);
+}
+
+static void drawImageToContext(ImageBuffer& imageBuffer, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
+{
+    context.drawImageBuffer(imageBuffer, dest, src, op);
+}
+
+template<class T> void CanvasRenderingContext2DBase::fullCanvasCompositedDrawImage(T& image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
+{
+    ASSERT(isFullCanvasCompositeMode(op));
+
+    IntSize croppedOffset;
+    auto bufferRect = calculateCompositingBufferRect(dest, &croppedOffset);
+    if (bufferRect.isEmpty()) {
+        clearCanvas();
+        return;
+    }
+
+    auto buffer = createCompositingBuffer(bufferRect);
+    if (!buffer)
+        return;
+
+    auto* c = drawingContext();
+    if (!c)
+        return;
+
+    FloatRect adjustedDest = dest;
+    adjustedDest.setLocation(FloatPoint(0, 0));
+    AffineTransform effectiveTransform = c->getCTM();
+    IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest));
+    buffer->context().translate(-transformedAdjustedRect.location());
+    buffer->context().translate(croppedOffset);
+    buffer->context().concatCTM(effectiveTransform);
+    drawImageToContext(image, buffer->context(), adjustedDest, src, CompositeSourceOver);
+
+    compositeBuffer(*buffer, bufferRect, op);
+}
+
+void CanvasRenderingContext2DBase::prepareGradientForDashboard(CanvasGradient& gradient) const
+{
+#if ENABLE(DASHBOARD_SUPPORT)
+    if (m_usesDashboardCompatibilityMode)
+        gradient.setDashboardCompatibilityMode();
+#else
+    UNUSED_PARAM(gradient);
+#endif
+}
+
+static CanvasRenderingContext2DBase::Style toStyle(const CanvasStyle& style)
+{
+    if (auto gradient = style.canvasGradient())
+        return gradient;
+    if (auto pattern = style.canvasPattern())
+        return pattern;
+    return style.color();
+}
+
+CanvasRenderingContext2DBase::Style CanvasRenderingContext2DBase::strokeStyle() const
+{
+    return toStyle(state().strokeStyle);
+}
+
+void CanvasRenderingContext2DBase::setStrokeStyle(CanvasRenderingContext2DBase::Style&& style)
+{
+    WTF::switchOn(style,
+        [this] (const String& string) { this->setStrokeColor(string); },
+        [this] (const RefPtr<CanvasGradient>& gradient) { this->setStrokeStyle(CanvasStyle(*gradient)); },
+        [this] (const RefPtr<CanvasPattern>& pattern) { this->setStrokeStyle(CanvasStyle(*pattern)); }
+    );
+}
+
+CanvasRenderingContext2DBase::Style CanvasRenderingContext2DBase::fillStyle() const
+{
+    return toStyle(state().fillStyle);
+}
+
+void CanvasRenderingContext2DBase::setFillStyle(CanvasRenderingContext2DBase::Style&& style)
+{
+    WTF::switchOn(style,
+        [this] (const String& string) { this->setFillColor(string); },
+        [this] (const RefPtr<CanvasGradient>& gradient) { this->setFillStyle(CanvasStyle(*gradient)); },
+        [this] (const RefPtr<CanvasPattern>& pattern) { this->setFillStyle(CanvasStyle(*pattern)); }
+    );
+}
+
+ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2DBase::createLinearGradient(float x0, float y0, float x1, float y1)
+{
+    if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1))
+        return Exception { NotSupportedError };
+
+    auto gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
+    prepareGradientForDashboard(gradient.get());
+    return WTFMove(gradient);
+}
+
+ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2DBase::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
+{
+    if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1))
+        return Exception { NotSupportedError };
+
+    if (r0 < 0 || r1 < 0)
+        return Exception { IndexSizeError };
+
+    auto gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
+    prepareGradientForDashboard(gradient.get());
+    return WTFMove(gradient);
+}
+
+ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2DBase::createPattern(CanvasImageSource&& image, const String& repetition)
+{
+    bool repeatX, repeatY;
+    if (!CanvasPattern::parseRepetitionType(repetition, repeatX, repeatY))
+        return Exception { SyntaxError };
+
+    return WTF::switchOn(image,
+        [&] (auto& element) -> ExceptionOr<RefPtr<CanvasPattern>> { return this->createPattern(*element, repeatX, repeatY); }
+    );
+}
+
+ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2DBase::createPattern(HTMLImageElement& imageElement, bool repeatX, bool repeatY)
+{
+    auto* cachedImage = imageElement.cachedImage();
+
+    // If the image loading hasn't started or the image is not complete, it is not fully decodable.
+    if (!cachedImage || !imageElement.complete())
+        return nullptr;
+
+    if (cachedImage->status() == CachedResource::LoadError)
+        return Exception { InvalidStateError };
+
+    bool originClean = cachedImage->isOriginClean(canvasBase().securityOrigin());
+
+    // FIXME: SVG images with animations can switch between clean and dirty (leaking cross-origin
+    // data). We should either:
+    //   1) Take a fixed snapshot of an SVG image when creating a pattern and determine then whether
+    //      the origin is clean.
+    //   2) Dynamically verify the origin checks at draw time, and dirty the canvas accordingly.
+    // To be on the safe side, taint the origin for all patterns containing SVG images for now.
+    if (cachedImage->image()->isSVGImage())
+        originClean = false;
+
+    return RefPtr<CanvasPattern> { CanvasPattern::create(*cachedImage->imageForRenderer(imageElement.renderer()), repeatX, repeatY, originClean) };
+}
+
+ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2DBase::createPattern(HTMLCanvasElement& canvas, bool repeatX, bool repeatY)
+{
+    if (!canvas.width() || !canvas.height() || !canvas.buffer())
+        return Exception { InvalidStateError };
+
+    return RefPtr<CanvasPattern> { CanvasPattern::create(*canvas.copiedImage(), repeatX, repeatY, canvas.originClean()) };
+}
+    
+#if ENABLE(VIDEO)
+
+ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2DBase::createPattern(HTMLVideoElement& videoElement, bool repeatX, bool repeatY)
+{
+    if (videoElement.readyState() < HTMLMediaElement::HAVE_CURRENT_DATA)
+        return nullptr;
+    
+    checkOrigin(&videoElement);
+    bool originClean = canvasBase().originClean();
+
+#if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
+    if (auto nativeImage = videoElement.nativeImageForCurrentTime())
+        return RefPtr<CanvasPattern> { CanvasPattern::create(BitmapImage::create(WTFMove(nativeImage)), repeatX, repeatY, originClean) };
+#endif
+
+    auto imageBuffer = ImageBuffer::create(size(videoElement), drawingContext() ? drawingContext()->renderingMode() : Accelerated);
+    videoElement.paintCurrentFrameInContext(imageBuffer->context(), FloatRect(FloatPoint(), size(videoElement)));
+    
+    return RefPtr<CanvasPattern> { CanvasPattern::create(ImageBuffer::sinkIntoImage(WTFMove(imageBuffer), Unscaled).releaseNonNull(), repeatX, repeatY, originClean) };
+}
+
+#endif
+
+ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2DBase::createPattern(ImageBitmap&, bool, bool)
+{
+    // FIXME: Implement.
+    return Exception { TypeError };
+}
+
+void CanvasRenderingContext2DBase::didDrawEntireCanvas()
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    didDraw(FloatRect(FloatPoint::zero(), canvas.size()), CanvasDidDrawApplyClip);
+}
+
+void CanvasRenderingContext2DBase::didDraw(const FloatRect& r, unsigned options)
+{
+    auto* c = drawingContext();
+    if (!c)
+        return;
+    if (!state().hasInvertibleTransform)
+        return;
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+    // If we are drawing to hardware and we have a composited layer, just call contentChanged().
+    if (isAccelerated()) {
+        RenderBox* renderBox = canvas().renderBox();
+        if (renderBox && renderBox->hasAcceleratedCompositing()) {
+            renderBox->contentChanged(CanvasPixelsChanged);
+            canvas().clearCopiedImage();
+            canvas().notifyObserversCanvasChanged(r);
+            return;
+        }
+    }
+#endif
+
+    FloatRect dirtyRect = r;
+    if (options & CanvasDidDrawApplyTransform) {
+        AffineTransform ctm = state().transform;
+        dirtyRect = ctm.mapRect(r);
+    }
+
+    if (options & CanvasDidDrawApplyShadow && state().shadowColor.isVisible()) {
+        // The shadow gets applied after transformation
+        FloatRect shadowRect(dirtyRect);
+        shadowRect.move(state().shadowOffset);
+        shadowRect.inflate(state().shadowBlur);
+        dirtyRect.unite(shadowRect);
+    }
+
+    if (options & CanvasDidDrawApplyClip) {
+        // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
+        // back out of the GraphicsContext, so to take clip into account for incremental painting,
+        // we'd have to keep the clip path around.
+    }
+
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    canvas.didDraw(dirtyRect);
+}
+
+void CanvasRenderingContext2DBase::setTracksDisplayListReplay(bool tracksDisplayListReplay)
+{
+    if (tracksDisplayListReplay == m_tracksDisplayListReplay)
+        return;
+
+    m_tracksDisplayListReplay = tracksDisplayListReplay;
+    if (!m_tracksDisplayListReplay)
+        contextDisplayListMap().remove(this);
+}
+
+String CanvasRenderingContext2DBase::displayListAsText(DisplayList::AsTextFlags flags) const
+{
+    if (!m_recordingContext)
+        return { };
+    return m_recordingContext->displayList.asText(flags);
+}
+
+String CanvasRenderingContext2DBase::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
+{
+    auto* displayList = contextDisplayListMap().get(this);
+    if (!displayList)
+        return { };
+    return displayList->asText(flags);
+}
+
+void CanvasRenderingContext2DBase::paintRenderingResultsToCanvas()
+{
+    if (UNLIKELY(m_usesDisplayListDrawing)) {
+        if (!m_recordingContext)
+            return;
+
+        auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+        FloatRect clip(FloatPoint::zero(), canvas.size());
+        DisplayList::Replayer replayer(*canvas.drawingContext(), m_recordingContext->displayList);
+
+        if (UNLIKELY(m_tracksDisplayListReplay)) {
+            auto replayList = replayer.replay(clip, m_tracksDisplayListReplay);
+            contextDisplayListMap().add(this, WTFMove(replayList));
+        } else
+            replayer.replay(clip);
+
+        m_recordingContext->displayList.clear();
+    }
+}
+
+GraphicsContext* CanvasRenderingContext2DBase::drawingContext() const
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+    if (UNLIKELY(m_usesDisplayListDrawing)) {
+        if (!m_recordingContext)
+            m_recordingContext = std::make_unique<DisplayListDrawingContext>(FloatRect(FloatPoint::zero(), canvas.size()));
+        return &m_recordingContext->context;
+    }
+
+    return canvas.drawingContext();
+}
+
+static RefPtr<ImageData> createEmptyImageData(const IntSize& size)
+{
+    auto data = ImageData::create(size);
+    if (data)
+        data->data()->zeroFill();
+    return data;
+}
+
+ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2DBase::createImageData(ImageData* imageData) const
+{
+    if (!imageData)
+        return Exception { NotSupportedError };
+
+    return createEmptyImageData(imageData->size());
+}
+
+ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2DBase::createImageData(float sw, float sh) const
+{
+    if (!sw || !sh)
+        return Exception { IndexSizeError };
+
+    FloatSize logicalSize(std::abs(sw), std::abs(sh));
+    if (!logicalSize.isExpressibleAsIntSize())
+        return nullptr;
+
+    IntSize size = expandedIntSize(logicalSize);
+    if (size.width() < 1)
+        size.setWidth(1);
+    if (size.height() < 1)
+        size.setHeight(1);
+
+    return createEmptyImageData(size);
+}
+
+ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2DBase::getImageData(float sx, float sy, float sw, float sh) const
+{
+    return getImageData(ImageBuffer::LogicalCoordinateSystem, sx, sy, sw, sh);
+}
+
+ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2DBase::getImageData(ImageBuffer::CoordinateSystem coordinateSystem, float sx, float sy, float sw, float sh) const
+{
+    if (!canvasBase().originClean()) {
+        static NeverDestroyed<String> consoleMessage(MAKE_STATIC_STRING_IMPL("Unable to get image data from canvas because the canvas has been tainted by cross-origin data."));
+        canvasBase().scriptExecutionContext()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
+        return Exception { SecurityError };
+    }
+
+    if (!sw || !sh)
+        return Exception { IndexSizeError };
+
+    if (sw < 0) {
+        sx += sw;
+        sw = -sw;
+    }    
+    if (sh < 0) {
+        sy += sh;
+        sh = -sh;
+    }
+
+    FloatRect logicalRect(sx, sy, sw, sh);
+    if (logicalRect.width() < 1)
+        logicalRect.setWidth(1);
+    if (logicalRect.height() < 1)
+        logicalRect.setHeight(1);
+    if (!logicalRect.isExpressibleAsIntRect())
+        return nullptr;
+
+    IntRect imageDataRect = enclosingIntRect(logicalRect);
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    ImageBuffer* buffer = canvas.buffer();
+    if (!buffer)
+        return createEmptyImageData(imageDataRect.size());
+
+    auto byteArray = buffer->getUnmultipliedImageData(imageDataRect, nullptr, coordinateSystem);
+    if (!byteArray) {
+        StringBuilder consoleMessage;
+        consoleMessage.appendLiteral("Unable to get image data from canvas. Requested size was ");
+        consoleMessage.appendNumber(imageDataRect.width());
+        consoleMessage.appendLiteral(" x ");
+        consoleMessage.appendNumber(imageDataRect.height());
+
+        canvasBase().scriptExecutionContext()->addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage.toString());
+        return Exception { InvalidStateError };
+    }
+
+    return ImageData::create(imageDataRect.size(), byteArray.releaseNonNull());
+}
+
+void CanvasRenderingContext2DBase::putImageData(ImageData& data, float dx, float dy)
+{
+    putImageData(data, dx, dy, 0, 0, data.width(), data.height());
+}
+
+void CanvasRenderingContext2DBase::putImageData(ImageData& data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
+{
+    putImageData(data, ImageBuffer::LogicalCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+}
+
+void CanvasRenderingContext2DBase::putImageData(ImageData& data, ImageBuffer::CoordinateSystem coordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    ImageBuffer* buffer = canvas.buffer();
+    if (!buffer)
+        return;
+
+    if (!data.data())
+        return;
+
+    if (dirtyWidth < 0) {
+        dirtyX += dirtyWidth;
+        dirtyWidth = -dirtyWidth;
+    }
+
+    if (dirtyHeight < 0) {
+        dirtyY += dirtyHeight;
+        dirtyHeight = -dirtyHeight;
+    }
+
+    FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+    clipRect.intersect(IntRect(0, 0, data.width(), data.height()));
+    IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
+    IntRect destRect = enclosingIntRect(clipRect);
+    destRect.move(destOffset);
+    destRect.intersect(IntRect(IntPoint(), coordinateSystem == ImageBuffer::LogicalCoordinateSystem ? buffer->logicalSize() : buffer->internalSize()));
+    if (destRect.isEmpty())
+        return;
+    IntRect sourceRect(destRect);
+    sourceRect.move(-destOffset);
+    sourceRect.intersect(IntRect(0, 0, data.width(), data.height()));
+
+    if (!sourceRect.isEmpty())
+        buffer->putByteArray(*data.data(), AlphaPremultiplication::Unpremultiplied, IntSize(data.width(), data.height()), sourceRect, IntPoint(destOffset), coordinateSystem);
+
+    didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip
+}
+
+void CanvasRenderingContext2DBase::inflateStrokeRect(FloatRect& rect) const
+{
+    // Fast approximation of the stroke's bounding rect.
+    // This yields a slightly oversized rect but is very fast
+    // compared to Path::strokeBoundingRect().
+    static const float root2 = sqrtf(2);
+    float delta = state().lineWidth / 2;
+    if (state().lineJoin == MiterJoin)
+        delta *= state().miterLimit;
+    else if (state().lineCap == SquareCap)
+        delta *= root2;
+    rect.inflate(delta);
+}
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+
+PlatformLayer* CanvasRenderingContext2DBase::platformLayer() const
+{
+    auto& canvas = downcast<HTMLCanvasElement>(canvasBase());
+
+    return canvas.buffer() ? canvas.buffer()->platformLayer() : nullptr;
+}
+
+#endif
+
+static inline InterpolationQuality smoothingToInterpolationQuality(ImageSmoothingQuality quality)
+{
+    switch (quality) {
+    case ImageSmoothingQuality::Low:
+        return InterpolationLow;
+    case ImageSmoothingQuality::Medium:
+        return InterpolationMedium;
+    case ImageSmoothingQuality::High:
+        return InterpolationHigh;
+    }
+
+    ASSERT_NOT_REACHED();
+    return InterpolationLow;
+};
+
+auto CanvasRenderingContext2DBase::imageSmoothingQuality() const -> ImageSmoothingQuality
+{
+    return state().imageSmoothingQuality;
+}
+
+void CanvasRenderingContext2DBase::setImageSmoothingQuality(ImageSmoothingQuality quality)
+{
+    if (quality == state().imageSmoothingQuality)
+        return;
+
+    realizeSaves();
+    modifiableState().imageSmoothingQuality = quality;
+
+    if (!state().imageSmoothingEnabled)
+        return;
+
+    if (auto* context = drawingContext())
+        context->setImageInterpolationQuality(smoothingToInterpolationQuality(quality));
+}
+
+bool CanvasRenderingContext2DBase::imageSmoothingEnabled() const
+{
+    return state().imageSmoothingEnabled;
+}
+
+void CanvasRenderingContext2DBase::setImageSmoothingEnabled(bool enabled)
+{
+    if (enabled == state().imageSmoothingEnabled)
+        return;
+
+    realizeSaves();
+    modifiableState().imageSmoothingEnabled = enabled;
+    auto* c = drawingContext();
+    if (c)
+        c->setImageInterpolationQuality(enabled ? smoothingToInterpolationQuality(state().imageSmoothingQuality) : InterpolationNone);
+}
+
+void CanvasRenderingContext2DBase::setPath(Path2D& path)
+{
+    m_path = path.path();
+}
+
+Ref<Path2D> CanvasRenderingContext2DBase::getPath() const
+{
+    return Path2D::create(m_path);
+}
+
+inline void CanvasRenderingContext2DBase::clearPathForDashboardBackwardCompatibilityMode()
+{
+#if ENABLE(DASHBOARD_SUPPORT)
+    if (m_usesDashboardCompatibilityMode)
+        m_path.clear();
+#endif
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
new file mode 100644 (file)
index 0000000..f75e454
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2017 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 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 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.
+ */
+
+#pragma once
+
+#include "AffineTransform.h"
+#include "CanvasDirection.h"
+#include "CanvasFillRule.h"
+#include "CanvasLineCap.h"
+#include "CanvasLineJoin.h"
+#include "CanvasPath.h"
+#include "CanvasRenderingContext.h"
+#include "CanvasStyle.h"
+#include "CanvasTextAlign.h"
+#include "CanvasTextBaseline.h"
+#include "Color.h"
+#include "FloatSize.h"
+#include "FontCascade.h"
+#include "FontSelectorClient.h"
+#include "GraphicsContext.h"
+#include "GraphicsTypes.h"
+#include "ImageBuffer.h"
+#include "ImageSmoothingQuality.h"
+#include "Path.h"
+#include "PlatformLayer.h"
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class CanvasBase;
+class CanvasGradient;
+class CanvasPattern;
+class DOMMatrix;
+class FloatRect;
+class GraphicsContext;
+class HTMLImageElement;
+class HTMLVideoElement;
+class ImageBitmap;
+class ImageData;
+class Path2D;
+class TextMetrics;
+
+struct DOMMatrix2DInit;
+
+#if ENABLE(VIDEO)
+using CanvasImageSource = Variant<RefPtr<HTMLImageElement>, RefPtr<HTMLVideoElement>, RefPtr<HTMLCanvasElement>, RefPtr<ImageBitmap>>;
+#else
+using CanvasImageSource = Variant<RefPtr<HTMLImageElement>, RefPtr<HTMLCanvasElement>, RefPtr<ImageBitmap>>;
+#endif
+
+class CanvasRenderingContext2DBase : public CanvasRenderingContext, public CanvasPath {
+public:
+    CanvasRenderingContext2DBase(CanvasBase&, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode);
+    virtual ~CanvasRenderingContext2DBase();
+
+    float lineWidth() const;
+    void setLineWidth(float);
+
+    CanvasLineCap lineCap() const;
+    void setLineCap(CanvasLineCap);
+    void setLineCap(const String&);
+
+    CanvasLineJoin lineJoin() const;
+    void setLineJoin(CanvasLineJoin);
+    void setLineJoin(const String&);
+
+    float miterLimit() const;
+    void setMiterLimit(float);
+
+    const Vector<float>& getLineDash() const;
+    void setLineDash(const Vector<float>&);
+    const Vector<float>& webkitLineDash() const { return getLineDash(); }
+    void setWebkitLineDash(const Vector<float>&);
+
+    float lineDashOffset() const;
+    void setLineDashOffset(float);
+
+    float shadowOffsetX() const;
+    void setShadowOffsetX(float);
+
+    float shadowOffsetY() const;
+    void setShadowOffsetY(float);
+
+    float shadowBlur() const;
+    void setShadowBlur(float);
+
+    String shadowColor() const;
+    void setShadowColor(const String&);
+
+    float globalAlpha() const;
+    void setGlobalAlpha(float);
+
+    String globalCompositeOperation() const;
+    void setGlobalCompositeOperation(const String&);
+
+    void save() { ++m_unrealizedSaveCount; }
+    void restore();
+
+    void scale(float sx, float sy);
+    void rotate(float angleInRadians);
+    void translate(float tx, float ty);
+    void transform(float m11, float m12, float m21, float m22, float dx, float dy);
+
+    Ref<DOMMatrix> getTransform() const;
+    void setTransform(float m11, float m12, float m21, float m22, float dx, float dy);
+    ExceptionOr<void> setTransform(DOMMatrix2DInit&&);
+    void resetTransform();
+
+    void setStrokeColor(const String& color, std::optional<float> alpha = std::nullopt);
+    void setStrokeColor(float grayLevel, float alpha = 1.0);
+    void setStrokeColor(float r, float g, float b, float a);
+    void setStrokeColor(float c, float m, float y, float k, float a);
+
+    void setFillColor(const String& color, std::optional<float> alpha = std::nullopt);
+    void setFillColor(float grayLevel, float alpha = 1.0f);
+    void setFillColor(float r, float g, float b, float a);
+    void setFillColor(float c, float m, float y, float k, float a);
+
+    void beginPath();
+
+    void fill(CanvasFillRule = CanvasFillRule::Nonzero);
+    void stroke();
+    void clip(CanvasFillRule = CanvasFillRule::Nonzero);
+
+    void fill(Path2D&, CanvasFillRule = CanvasFillRule::Nonzero);
+    void stroke(Path2D&);
+    void clip(Path2D&, CanvasFillRule = CanvasFillRule::Nonzero);
+
+    bool isPointInPath(float x, float y, CanvasFillRule = CanvasFillRule::Nonzero);
+    bool isPointInStroke(float x, float y);
+
+    bool isPointInPath(Path2D&, float x, float y, CanvasFillRule = CanvasFillRule::Nonzero);
+    bool isPointInStroke(Path2D&, float x, float y);
+
+    void clearRect(float x, float y, float width, float height);
+    void fillRect(float x, float y, float width, float height);
+    void strokeRect(float x, float y, float width, float height);
+
+    void setShadow(float width, float height, float blur, const String& color = String(), std::optional<float> alpha = std::nullopt);
+    void setShadow(float width, float height, float blur, float grayLevel, float alpha = 1.0);
+    void setShadow(float width, float height, float blur, float r, float g, float b, float a);
+    void setShadow(float width, float height, float blur, float c, float m, float y, float k, float a);
+
+    void clearShadow();
+
+    ExceptionOr<void> drawImage(CanvasImageSource&&, float dx, float dy);
+    ExceptionOr<void> drawImage(CanvasImageSource&&, float dx, float dy, float dw, float dh);
+    ExceptionOr<void> drawImage(CanvasImageSource&&, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh);
+
+    void drawImageFromRect(HTMLImageElement&, float sx = 0, float sy = 0, float sw = 0, float sh = 0, float dx = 0, float dy = 0, float dw = 0, float dh = 0, const String& compositeOperation = emptyString());
+
+    using Style = Variant<String, RefPtr<CanvasGradient>, RefPtr<CanvasPattern>>;
+    Style strokeStyle() const;
+    void setStrokeStyle(Style&&);
+    Style fillStyle() const;
+    void setFillStyle(Style&&);
+
+    ExceptionOr<Ref<CanvasGradient>> createLinearGradient(float x0, float y0, float x1, float y1);
+    ExceptionOr<Ref<CanvasGradient>> createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1);
+    ExceptionOr<RefPtr<CanvasPattern>> createPattern(CanvasImageSource&&, const String& repetition);
+
+    ExceptionOr<RefPtr<ImageData>> createImageData(ImageData*) const;
+    ExceptionOr<RefPtr<ImageData>> createImageData(float width, float height) const;
+    ExceptionOr<RefPtr<ImageData>> getImageData(float sx, float sy, float sw, float sh) const;
+    void putImageData(ImageData&, float dx, float dy);
+    void putImageData(ImageData&, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight);
+
+    float webkitBackingStorePixelRatio() const { return 1; }
+
+    void reset();
+
+    LineCap getLineCap() const { return state().lineCap; }
+    LineJoin getLineJoin() const { return state().lineJoin; }
+
+    bool imageSmoothingEnabled() const;
+    void setImageSmoothingEnabled(bool);
+
+    ImageSmoothingQuality imageSmoothingQuality() const;
+    void setImageSmoothingQuality(ImageSmoothingQuality);
+
+    void setPath(Path2D&);
+    Ref<Path2D> getPath() const;
+
+    bool usesDisplayListDrawing() const { return m_usesDisplayListDrawing; };
+    void setUsesDisplayListDrawing(bool flag) { m_usesDisplayListDrawing = flag; };
+
+    bool tracksDisplayListReplay() const { return m_tracksDisplayListReplay; }
+    void setTracksDisplayListReplay(bool);
+
+    String displayListAsText(DisplayList::AsTextFlags) const;
+    String replayDisplayListAsText(DisplayList::AsTextFlags) const;
+
+    using Direction = CanvasDirection;
+
+    class FontProxy : public FontSelectorClient {
+    public:
+        FontProxy() = default;
+        virtual ~FontProxy();
+        FontProxy(const FontProxy&);
+        FontProxy& operator=(const FontProxy&);
+
+        bool realized() const { return m_font.fontSelector(); }
+        void initialize(FontSelector&, const RenderStyle&);
+        const FontMetrics& fontMetrics() const;
+        const FontCascadeDescription& fontDescription() const;
+        float width(const TextRun&, GlyphOverflow* = 0) const;
+        void drawBidiText(GraphicsContext&, const TextRun&, const FloatPoint&, FontCascade::CustomFontNotReadyAction) const;
+
+    private:
+        void update(FontSelector&);
+        void fontsNeedUpdate(FontSelector&) override;
+
+        FontCascade m_font;
+    };
+
+    struct State final {
+        State();
+
+        State(const State&);
+        State& operator=(const State&);
+
+        String unparsedStrokeColor;
+        String unparsedFillColor;
+        CanvasStyle strokeStyle;
+        CanvasStyle fillStyle;
+        float lineWidth;
+        LineCap lineCap;
+        LineJoin lineJoin;
+        float miterLimit;
+        FloatSize shadowOffset;
+        float shadowBlur;
+        Color shadowColor;
+        float globalAlpha;
+        CompositeOperator globalComposite;
+        BlendMode globalBlend;
+        AffineTransform transform;
+        bool hasInvertibleTransform;
+        Vector<float> lineDash;
+        float lineDashOffset;
+        bool imageSmoothingEnabled;
+        ImageSmoothingQuality imageSmoothingQuality;
+
+        // Text state.
+        TextAlign textAlign;
+        TextBaseline textBaseline;
+        Direction direction;
+
+        String unparsedFont;
+        FontProxy font;
+    };
+
+    const State& state() const { return m_stateStack.last(); }
+
+protected:
+    static const int DefaultFontSize;
+    static const char* const DefaultFontFamily;
+    static const char* const DefaultFont;
+
+    enum CanvasDidDrawOption {
+        CanvasDidDrawApplyNone = 0,
+        CanvasDidDrawApplyTransform = 1,
+        CanvasDidDrawApplyShadow = 1 << 1,
+        CanvasDidDrawApplyClip = 1 << 2,
+        CanvasDidDrawApplyAll = 0xffffffff
+    };
+
+    bool isFullCanvasCompositeMode(CompositeOperator);
+
+    State& modifiableState() { ASSERT(!m_unrealizedSaveCount || m_stateStack.size() >= MaxSaveCount); return m_stateStack.last(); }
+
+    void applyLineDash() const;
+    void setShadow(const FloatSize& offset, float blur, const Color&);
+    void applyShadow();
+    bool shouldDrawShadows() const;
+
+    void didDraw(const FloatRect&, unsigned options = CanvasDidDrawApplyAll);
+    void didDrawEntireCanvas();
+
+    void paintRenderingResultsToCanvas() override;
+
+    GraphicsContext* drawingContext() const;
+
+    void unwindStateStack();
+    void realizeSaves();
+    void realizeSavesLoop();
+
+    void applyStrokePattern();
+    void applyFillPattern();
+
+    void setStrokeStyle(CanvasStyle);
+    void setFillStyle(CanvasStyle);
+
+    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLImageElement&, bool repeatX, bool repeatY);
+    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLCanvasElement&, bool repeatX, bool repeatY);
+#if ENABLE(VIDEO)
+    ExceptionOr<RefPtr<CanvasPattern>> createPattern(HTMLVideoElement&, bool repeatX, bool repeatY);
+#endif
+    ExceptionOr<RefPtr<CanvasPattern>> createPattern(ImageBitmap&, bool repeatX, bool repeatY);
+
+    ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect);
+    ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&);
+    ExceptionOr<void> drawImage(HTMLCanvasElement&, const FloatRect& srcRect, const FloatRect& dstRect);
+#if ENABLE(VIDEO)
+    ExceptionOr<void> drawImage(HTMLVideoElement&, const FloatRect& srcRect, const FloatRect& dstRect);
+#endif
+    ExceptionOr<void> drawImage(ImageBitmap&, const FloatRect& srcRect, const FloatRect& dstRect);
+
+    void beginCompositeLayer();
+    void endCompositeLayer();
+
+    void fillInternal(const Path&, CanvasFillRule);
+    void strokeInternal(const Path&);
+    void clipInternal(const Path&, CanvasFillRule);
+
+    bool isPointInPathInternal(const Path&, float x, float y, CanvasFillRule);
+    bool isPointInStrokeInternal(const Path&, float x, float y);
+
+    void clearCanvas();
+    Path transformAreaToDevice(const Path&) const;
+    Path transformAreaToDevice(const FloatRect&) const;
+    bool rectContainsCanvas(const FloatRect&) const;
+
+    template<class T> IntRect calculateCompositingBufferRect(const T&, IntSize*);
+    std::unique_ptr<ImageBuffer> createCompositingBuffer(const IntRect&);
+    void compositeBuffer(ImageBuffer&, const IntRect&, CompositeOperator);
+
+    void inflateStrokeRect(FloatRect&) const;
+
+    template<class T> void fullCanvasCompositedDrawImage(T&, const FloatRect&, const FloatRect&, CompositeOperator);
+
+    void prepareGradientForDashboard(CanvasGradient&) const;
+
+    ExceptionOr<RefPtr<ImageData>> getImageData(ImageBuffer::CoordinateSystem, float sx, float sy, float sw, float sh) const;
+    void putImageData(ImageData&, ImageBuffer::CoordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight);
+
+    bool isAccelerated() const override;
+
+    bool hasInvertibleTransform() const override { return state().hasInvertibleTransform; }
+
+#if ENABLE(ACCELERATED_2D_CANVAS)
+    PlatformLayer* platformLayer() const override;
+#endif
+
+    void clearPathForDashboardBackwardCompatibilityMode();
+
+    static const unsigned MaxSaveCount = 1024 * 16;
+    Vector<State, 1> m_stateStack;
+    unsigned m_unrealizedSaveCount { 0 };
+    bool m_usesCSSCompatibilityParseMode;
+#if ENABLE(DASHBOARD_SUPPORT)
+    bool m_usesDashboardCompatibilityMode;
+#endif
+    bool m_usesDisplayListDrawing { false };
+    bool m_tracksDisplayListReplay { false };
+    mutable std::unique_ptr<struct DisplayListDrawingContext> m_recordingContext;
+};
+
+} // namespace WebCore
+
diff --git a/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.cpp b/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.cpp
new file mode 100644 (file)
index 0000000..d1626d1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013, 2014 Adobe Systems Incorporated. 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 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 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 "config.h"
+#include "OffscreenCanvasRenderingContext2D.h"
+
+namespace WebCore {
+
+OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D(CanvasBase& canvas)
+    : CanvasRenderingContext2DBase(canvas, false, false)
+{
+}
+
+OffscreenCanvasRenderingContext2D::~OffscreenCanvasRenderingContext2D() = default;
+
+} // namespace WebCore
+
diff --git a/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.h b/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.h
new file mode 100644 (file)
index 0000000..497ddd3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012 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 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 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.
+ */
+
+#pragma once
+
+#include "CanvasRenderingContext2DBase.h"
+
+#include "OffscreenCanvas.h"
+
+namespace WebCore {
+
+class OffscreenCanvasRenderingContext2D final : public CanvasRenderingContext2DBase {
+public:
+    OffscreenCanvasRenderingContext2D(CanvasBase&);
+    virtual ~OffscreenCanvasRenderingContext2D();
+
+    bool isOffscreen2d() const override { return true; }
+
+    OffscreenCanvas& canvas() const { return downcast<OffscreenCanvas>(canvasBase()); }
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_CANVASRENDERINGCONTEXT(WebCore::OffscreenCanvasRenderingContext2D, isOffscreen2d())
+
diff --git a/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.idl b/Source/WebCore/html/canvas/OffscreenCanvasRenderingContext2D.idl
new file mode 100644 (file)
index 0000000..06dd57f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2017 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 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 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.
+*/
+
+[
+    CustomIsReachable,
+    EnabledAtRuntime=ImageBitmapOffscreenCanvas,
+    Exposed=(Window), // FIXME: OffscreenCanvas - should be Window,Worker
+    JSGenerateToJSObject,
+    JSCustomMarkFunction,
+    // CallTracingCallback=recordCanvasAction, // FIXME: OffscreenCanvas.
+] interface OffscreenCanvasRenderingContext2D {
+    readonly attribute OffscreenCanvas canvas;
+    // FIXME: OffscreenCanvas.
+    // void commit();
+
+    // Inspector-only.
+    // FIXME: OffscreenCanvas.
+    // [EnabledAtRuntime=InspectorAdditions] void setPath(Path2D path);
+    // [EnabledAtRuntime=InspectorAdditions, NewObject] Path2D getPath();
+};
+
+OffscreenCanvasRenderingContext2D implements CanvasState;
+OffscreenCanvasRenderingContext2D implements CanvasTransform;
+OffscreenCanvasRenderingContext2D implements CanvasCompositing;
+OffscreenCanvasRenderingContext2D implements CanvasImageSmoothing;
+OffscreenCanvasRenderingContext2D implements CanvasFillStrokeStyles;
+OffscreenCanvasRenderingContext2D implements CanvasShadowStyles;
+OffscreenCanvasRenderingContext2D implements CanvasFilters;
+OffscreenCanvasRenderingContext2D implements CanvasRect;
+OffscreenCanvasRenderingContext2D implements CanvasDrawPath;
+OffscreenCanvasRenderingContext2D implements CanvasDrawImage;
+OffscreenCanvasRenderingContext2D implements CanvasImageData;
+OffscreenCanvasRenderingContext2D implements CanvasPathDrawingStyles;
+OffscreenCanvasRenderingContext2D implements CanvasPath;