diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
index 0e4a855340..73347d5dee 100644
--- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
+++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h
@@ -61,6 +61,9 @@ public:
static constexpr bool OVERRIDES_FINALIZE = true;
static JS::ThrowCompletionOr> create(JS::Realm&, HTMLCanvasElement&, JS::Value options);
+
+ // https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
+ bool origin_clean() const { return m_origin_clean; }
virtual ~CanvasRenderingContext2D() override;
virtual void fill_rect(float x, float y, float width, float height) override;
diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
index 0d1870a9f9..68ba78fb06 100644
--- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
namespace Web::HTML {
@@ -333,10 +334,21 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz
return Gfx::IntSize(width, height);
}
-// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl
-String HTMLCanvasElement::to_data_url(StringView type, Optional js_quality)
+// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
+bool HTMLCanvasElement::is_origin_clean() const
{
- // FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
+ return m_context.visit(
+ [](GC::Ref const& context) { return context->origin_clean(); },
+ // FIXME: WebGL and WebGL2 contexts do not track the origin-clean flag yet.
+ [](auto const&) { return true; });
+}
+
+// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl
+WebIDL::ExceptionOr HTMLCanvasElement::to_data_url(StringView type, Optional js_quality)
+{
+ // 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
+ if (!is_origin_clean())
+ return WebIDL::SecurityError::create(realm(), "Canvas is not origin-clean"_utf16);
// 2. If this canvas element's bitmap has no pixels (i.e. either its horizontal dimension or its vertical dimension is zero),
// then return the string "data:,". (This is the shortest data: URL; it represents the empty string in a text/plain resource.)
@@ -365,7 +377,9 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional js_qu
// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-toblob
WebIDL::ExceptionOr HTMLCanvasElement::to_blob(GC::Ref callback, StringView type, Optional js_quality)
{
- // FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
+ // 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
+ if (!is_origin_clean())
+ return WebIDL::SecurityError::create(realm(), "Canvas is not origin-clean"_utf16);
// 2. Let result be null.
// 3. If this canvas element's bitmap has pixels (i.e., neither its horizontal dimension nor its vertical dimension is zero),
diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Libraries/LibWeb/HTML/HTMLCanvasElement.h
index 507c832d70..71cea78363 100644
--- a/Libraries/LibWeb/HTML/HTMLCanvasElement.h
+++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.h
@@ -42,8 +42,9 @@ public:
virtual void attribute_changed(FlyString const& local_name, Optional const& old_value, Optional const& value, Optional const& namespace_) override;
- String to_data_url(StringView type, Optional quality);
+ WebIDL::ExceptionOr to_data_url(StringView type, Optional quality);
WebIDL::ExceptionOr to_blob(GC::Ref callback, StringView type, Optional quality);
+ bool is_origin_clean() const;
RefPtr get_bitmap_from_surface();
void prepare_for_compositing();
diff --git a/Libraries/LibWeb/WebDriver/Screenshot.cpp b/Libraries/LibWeb/WebDriver/Screenshot.cpp
index ad8cdeb619..7e60f440cb 100644
--- a/Libraries/LibWeb/WebDriver/Screenshot.cpp
+++ b/Libraries/LibWeb/WebDriver/Screenshot.cpp
@@ -80,7 +80,9 @@ ErrorOr, WebDriver::Error> draw_bounding_box_fr
// https://w3c.github.io/webdriver/#dfn-encoding-a-canvas-as-base64
Response encode_canvas_element(HTML::HTMLCanvasElement& canvas)
{
- // FIXME: 1. If the canvas element’s bitmap’s origin-clean flag is set to false, return error with error code unable to capture screen.
+ // 1. If the canvas element’s bitmap’s origin-clean flag is set to false, return error with error code unable to capture screen.
+ if (!canvas.is_origin_clean())
+ return Error::from_code(ErrorCode::UnableToCaptureScreen, "Canvas is not origin-clean"sv);
// 2. If the canvas element’s bitmap has no pixels (i.e. either its horizontal dimension or vertical dimension is zero) then return error with error code unable to capture screen.
if (!canvas.canvas_surface_content_size().has_value())
@@ -88,7 +90,7 @@ Response encode_canvas_element(HTML::HTMLCanvasElement& canvas)
// 3. Let file be a serialization of the canvas element’s bitmap as a file, using "image/png" as an argument.
// 4. Let data url be a data: URL representing file. [RFC2397]
- auto data_url = canvas.to_data_url("image/png"sv, JS::js_undefined());
+ auto data_url = MUST(canvas.to_data_url("image/png"sv, JS::js_undefined()));
// 5. Let index be the index of "," in data url.
auto index = data_url.find_byte_offset(',');
diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini
index c32eee6225..2cd1778eb4 100644
--- a/Tests/LibWeb/TestConfig.ini
+++ b/Tests/LibWeb/TestConfig.ini
@@ -50,6 +50,9 @@ Text/input/HTML/parser-streams-bytes.html
Text/input/HTML/parser-streams-with-document-write.html
Text/input/HTML/parser-streams-utf8-split.html
+; This test needs to taint a canvas with a cross-origin image.
+Text/input/HTML/canvas-toDataURL-toBlob-origin-clean.html
+
; Navigation has entries and events disabled for opaque origins, so this crash only reproduces over HTTP.
Crash/DOM/document-open-navigation-api.html
diff --git a/Tests/LibWeb/Text/expected/HTML/canvas-toDataURL-toBlob-origin-clean.txt b/Tests/LibWeb/Text/expected/HTML/canvas-toDataURL-toBlob-origin-clean.txt
new file mode 100644
index 0000000000..8f82f84488
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/HTML/canvas-toDataURL-toBlob-origin-clean.txt
@@ -0,0 +1,4 @@
+untainted toDataURL ok: true
+tainted toDataURL: SecurityError
+tainted toBlob: SecurityError
+tainted getImageData: SecurityError
diff --git a/Tests/LibWeb/Text/input/HTML/canvas-toDataURL-toBlob-origin-clean.html b/Tests/LibWeb/Text/input/HTML/canvas-toDataURL-toBlob-origin-clean.html
new file mode 100644
index 0000000000..abd125f9c4
--- /dev/null
+++ b/Tests/LibWeb/Text/input/HTML/canvas-toDataURL-toBlob-origin-clean.html
@@ -0,0 +1,71 @@
+
+
+