Log Message
Implement [Transferable] property of OffscreenCanvas https://bugs.webkit.org/show_bug.cgi?id=202574
Patch by Chris Lord <[email protected]> on 2020-01-28 Reviewed by Antti Koivisto. LayoutTests/imported/w3c: * web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt: * web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt: * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt: * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt: * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt: * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt: Source/WebCore: Bug 202574 - Implement [Transferable] property of OffscreenCanvas No new tests. Covered by existing tests. * bindings/js/SerializedScriptValue.cpp: (WebCore::CloneSerializer::serialize): (WebCore::CloneSerializer::CloneSerializer): (WebCore::CloneSerializer::dumpOffscreenCanvas): (WebCore::CloneSerializer::dumpIfTerminal): (WebCore::CloneDeserializer::deserialize): (WebCore::CloneDeserializer::CloneDeserializer): (WebCore::CloneDeserializer::readOffscreenCanvas): (WebCore::CloneDeserializer::readTerminal): (WebCore::SerializedScriptValue::SerializedScriptValue): (WebCore::SerializedScriptValue::create): (WebCore::canOffscreenCanvasesDetach): (WebCore::SerializedScriptValue::deserialize): * bindings/js/SerializedScriptValue.h: (WebCore::SerializedScriptValue::SerializedScriptValue): * html/CanvasBase.h: (WebCore::CanvasBase::width const): (WebCore::CanvasBase::height const): * html/OffscreenCanvas.cpp: (WebCore::DetachedOffscreenCanvas::DetachedOffscreenCanvas): (WebCore::DetachedOffscreenCanvas::takeImageBuffer): (WebCore::OffscreenCanvas::create): (WebCore::OffscreenCanvas::width const): (WebCore::OffscreenCanvas::height const): (WebCore::OffscreenCanvas::setWidth): (WebCore::OffscreenCanvas::setHeight): (WebCore::OffscreenCanvas::getContext): (WebCore::OffscreenCanvas::transferToImageBitmap): (WebCore::OffscreenCanvas::convertToBlob): (WebCore::OffscreenCanvas::canDetach const): (WebCore::OffscreenCanvas::detach): (WebCore::OffscreenCanvas::takeImageBuffer const): * html/OffscreenCanvas.h: (WebCore::DetachedOffscreenCanvas::size const): (WebCore::DetachedOffscreenCanvas::originClean const):
Modified Paths
- trunk/LayoutTests/imported/w3c/ChangeLog
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt
- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt
- trunk/Source/WebCore/ChangeLog
- trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp
- trunk/Source/WebCore/bindings/js/SerializedScriptValue.h
- trunk/Source/WebCore/html/CanvasBase.h
- trunk/Source/WebCore/html/OffscreenCanvas.cpp
- trunk/Source/WebCore/html/OffscreenCanvas.h
Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2020-01-28 22:48:05 UTC (rev 255315)
@@ -1,3 +1,17 @@
+2020-01-28 Chris Lord <[email protected]>
+
+ Implement [Transferable] property of OffscreenCanvas
+ https://bugs.webkit.org/show_bug.cgi?id=202574
+
+ Reviewed by Antti Koivisto.
+
+ * web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt:
+ * web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt:
+ * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt:
+ * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt:
+ * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt:
+ * web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt:
+
2020-01-28 Carlos Alberto Lopez Perez <[email protected]>
[CSS Backgrounds] Import WPT css-backgrounds tests
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -3,7 +3,7 @@
PASS Test that convertToBlob with png produces correct result
PASS Test that convertToBlob with jpge produces correct result
PASS Test that convertToBlob with webp produces correct result
-FAIL Test that call convertToBlob on a detached OffscreenCanvas throws exception The object can not be cloned.
+PASS Test that call convertToBlob on a detached OffscreenCanvas throws exception
PASS Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception
PASS Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/convert-to-blob/offscreencanvas.convert.to.blob.w-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -1,6 +1,4 @@
-Harness Error (FAIL), message = DataCloneError: The object can not be cloned.
-
PASS Test that convertToBlob with default arguments produces correct result in a worker
PASS Test that convertToBlob with default type/1.0 quality produces correct result in a worker
PASS Test that convertToBlob with default type/0.2 quality produces correct result in a worker
@@ -13,7 +11,7 @@
PASS Test that convertToBlob with webp/default quality produces correct result in a worker
PASS Test that convertToBlob with webp/1.0 quality produces correct result in a worker
PASS Test that convertToBlob with webp/0.2 quality produces correct result in a worker
-FAIL Test that call convertToBlob on a detached OffscreenCanvas throws exception in a worker assert_unreached: error Reached unreachable code
+PASS Test that call convertToBlob on a detached OffscreenCanvas throws exception in a worker
PASS Test that call convertToBlob on a OffscreenCanvas with size 0 throws exception in a worker
PASS Test that call convertToBlob on a OffscreenCanvas with tainted origin throws exception in a worker
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -3,6 +3,6 @@
FAIL Test that transferToImageBitmap returns an ImageBitmap with correct color assert_equals: Alpha channel of the pixel at (50, 25) expected 255 but got 0
PASS Test that transferToImageBitmap won't change context's property
FAIL Test that transferToImageBitmap preserves transform assert_equals: Green channel of the pixel at (23, 25) expected 255 but got 0
-FAIL Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception The object can not be cloned.
+PASS Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception
PASS Test that transferToImageBitmap without a context throws an exception
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -5,6 +5,6 @@
FAIL Test that call transferToImageBitmap twice on a alpha-disabled context returns an ImageBitmap with correct color in a worker assert_array_equals: property 3, expected 255 but got 0
PASS Test that transferToImageBitmap won't change context's property in a worker
FAIL Test that call transferToImageBitmap preserves transform in a worker assert_array_equals: property 1, expected 255 but got 0
-FAIL Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception in a worker assert_unreached: error Reached unreachable code
+PASS Test that call transferToImageBitmap on a detached OffscreenCanvas throws an exception in a worker
PASS Test that call transferToImageBitmap without a context throws an exception in a worker
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -1,9 +1,7 @@
-FAIL Test that offscreenCanvas's size is correct after being transferred to a worker. The object can not be cloned.
-FAIL Test that transfer an OffscreenCanvas that has a context throws exception. assert_throws: function "function () {
- worker.postMessage({offscreenCanvas}, [offscreenCanvas]);
- }" threw object "DataCloneError: The object can not be cloned." that is not a DOMException InvalidStateError: property "code" is equal to 25, expected 11
-FAIL Test that transfer an OffscreenCanvas twice throws exception. The object can not be cloned.
-FAIL Test that calling getContext('2d') on a detached OffscreenCanvas throws exception. The object can not be cloned.
-FAIL Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception. The object can not be cloned.
+PASS Test that offscreenCanvas's size is correct after being transferred to a worker.
+PASS Test that transfer an OffscreenCanvas that has a context throws exception.
+PASS Test that transfer an OffscreenCanvas twice throws exception.
+PASS Test that calling getContext('2d') on a detached OffscreenCanvas throws exception.
+PASS Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception.
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt (255314 => 255315)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/offscreen-canvas/the-offscreen-canvas/offscreencanvas.transferrable.w-expected.txt 2020-01-28 22:48:05 UTC (rev 255315)
@@ -1,10 +1,8 @@
-Harness Error (FAIL), message = DataCloneError: The object can not be cloned.
+PASS Test that OffscreenCanvas's size is correct after being transferred from a worker.
+PASS Test that transfer an OffscreenCanvas that has a 2d context throws exception in a worker.
+PASS Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker.
+PASS Test that transfer an OffscreenCanvas twice throws exception in a worker.
+PASS Test that calling getContext('2d') on a detached OffscreenCanvas throws exception in a worker.
+PASS Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception in a worker.
-FAIL Test that OffscreenCanvas's size is correct after being transferred from a worker. assert_unreached: error Reached unreachable code
-FAIL Test that transfer an OffscreenCanvas that has a 2d context throws exception in a worker. assert_true: expected true got false
-FAIL Test that transfer an OffscreenCanvas that has a webgl context throws exception in a worker. assert_true: expected true got false
-FAIL Test that transfer an OffscreenCanvas twice throws exception in a worker. assert_unreached: error Reached unreachable code
-FAIL Test that calling getContext('2d') on a detached OffscreenCanvas throws exception in a worker. assert_unreached: error Reached unreachable code
-FAIL Test that calling getContext('webgl') on a detached OffscreenCanvas throws exception in a worker. assert_unreached: error Reached unreachable code
-
Modified: trunk/Source/WebCore/ChangeLog (255314 => 255315)
--- trunk/Source/WebCore/ChangeLog 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/ChangeLog 2020-01-28 22:48:05 UTC (rev 255315)
@@ -1,3 +1,50 @@
+2020-01-28 Chris Lord <[email protected]>
+
+ Implement [Transferable] property of OffscreenCanvas
+ https://bugs.webkit.org/show_bug.cgi?id=202574
+
+ Reviewed by Antti Koivisto.
+
+ Bug 202574 - Implement [Transferable] property of OffscreenCanvas
+
+ No new tests. Covered by existing tests.
+
+ * bindings/js/SerializedScriptValue.cpp:
+ (WebCore::CloneSerializer::serialize):
+ (WebCore::CloneSerializer::CloneSerializer):
+ (WebCore::CloneSerializer::dumpOffscreenCanvas):
+ (WebCore::CloneSerializer::dumpIfTerminal):
+ (WebCore::CloneDeserializer::deserialize):
+ (WebCore::CloneDeserializer::CloneDeserializer):
+ (WebCore::CloneDeserializer::readOffscreenCanvas):
+ (WebCore::CloneDeserializer::readTerminal):
+ (WebCore::SerializedScriptValue::SerializedScriptValue):
+ (WebCore::SerializedScriptValue::create):
+ (WebCore::canOffscreenCanvasesDetach):
+ (WebCore::SerializedScriptValue::deserialize):
+ * bindings/js/SerializedScriptValue.h:
+ (WebCore::SerializedScriptValue::SerializedScriptValue):
+ * html/CanvasBase.h:
+ (WebCore::CanvasBase::width const):
+ (WebCore::CanvasBase::height const):
+ * html/OffscreenCanvas.cpp:
+ (WebCore::DetachedOffscreenCanvas::DetachedOffscreenCanvas):
+ (WebCore::DetachedOffscreenCanvas::takeImageBuffer):
+ (WebCore::OffscreenCanvas::create):
+ (WebCore::OffscreenCanvas::width const):
+ (WebCore::OffscreenCanvas::height const):
+ (WebCore::OffscreenCanvas::setWidth):
+ (WebCore::OffscreenCanvas::setHeight):
+ (WebCore::OffscreenCanvas::getContext):
+ (WebCore::OffscreenCanvas::transferToImageBitmap):
+ (WebCore::OffscreenCanvas::convertToBlob):
+ (WebCore::OffscreenCanvas::canDetach const):
+ (WebCore::OffscreenCanvas::detach):
+ (WebCore::OffscreenCanvas::takeImageBuffer const):
+ * html/OffscreenCanvas.h:
+ (WebCore::DetachedOffscreenCanvas::size const):
+ (WebCore::DetachedOffscreenCanvas::originClean const):
+
2020-01-28 Andres Gonzalez <[email protected]>
Properly clear pending changes during AXIsolatedTree removal and applyPendingChanges.
Modified: trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp (255314 => 255315)
--- trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp 2020-01-28 22:48:05 UTC (rev 255315)
@@ -87,6 +87,11 @@
#include <wtf/RunLoop.h>
#include <wtf/Vector.h>
+#if ENABLE(OFFSCREEN_CANVAS)
+#include "JSOffscreenCanvas.h"
+#include "OffscreenCanvas.h"
+#endif
+
#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
#define ASSUME_LITTLE_ENDIAN 0
#else
@@ -169,6 +174,9 @@
RTCCertificateTag = 44,
#endif
ImageBitmapTag = 45,
+#if ENABLE(OFFSCREEN_CANVAS)
+ OffscreenCanvasTransferTag = 46,
+#endif
ErrorTag = 255
};
@@ -352,6 +360,7 @@
* | ImageBitmapTransferTag <value:uint32_t>
* | RTCCertificateTag
* | ImageBitmapTag <originClean:uint8_t> <logicalWidth:int32_t> <logicalHeight:int32_t> <resolutionScale:double> <byteLength:uint32_t>(<imageByteData:uint8_t>)
+ * | OffscreenCanvasTransferTag <value:uint32_t>
*
* Inside certificate, data is serialized in this format as per spec:
*
@@ -549,6 +558,9 @@
class CloneSerializer : CloneBase {
public:
static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, const Vector<RefPtr<ImageBitmap>>& imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
#if ENABLE(WEBASSEMBLY)
WasmModuleArray& wasmModules,
#endif
@@ -555,6 +567,9 @@
Vector<String>& blobURLs, Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers)
{
CloneSerializer serializer(lexicalGlobalObject, messagePorts, arrayBuffers, imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS)
+ offscreenCanvases,
+#endif
#if ENABLE(WEBASSEMBLY)
wasmModules,
#endif
@@ -582,6 +597,9 @@
typedef HashMap<JSObject*, uint32_t> ObjectPool;
CloneSerializer(JSGlobalObject* lexicalGlobalObject, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, const Vector<RefPtr<ImageBitmap>>& imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
#if ENABLE(WEBASSEMBLY)
WasmModuleArray& wasmModules,
#endif
@@ -600,6 +618,9 @@
fillTransferMap(messagePorts, m_transferredMessagePorts);
fillTransferMap(arrayBuffers, m_transferredArrayBuffers);
fillTransferMap(imageBitmaps, m_transferredImageBitmaps);
+#if ENABLE(OFFSCREEN_CANVAS)
+ fillTransferMap(offscreenCanvases, m_transferredOffscreenCanvases);
+#endif
}
template <class T>
@@ -944,6 +965,20 @@
write(static_cast<const uint8_t*>(arrayBuffer->data()), arrayBuffer->byteLength());
}
+#if ENABLE(OFFSCREEN_CANVAS)
+ void dumpOffscreenCanvas(JSObject* obj, SerializationReturnCode& code)
+ {
+ auto index = m_transferredOffscreenCanvases.find(obj);
+ if (index != m_transferredOffscreenCanvases.end()) {
+ write(OffscreenCanvasTransferTag);
+ write(index->value);
+ return;
+ }
+
+ code = SerializationReturnCode::DataCloneError;
+ }
+#endif
+
bool dumpIfTerminal(JSValue value, SerializationReturnCode& code)
{
if (!value.isCell()) {
@@ -1095,6 +1130,9 @@
#endif
ArrayBufferContentsArray dummySharedBuffers;
CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyMessagePorts, dummyArrayBuffers, { },
+#if ENABLE(OFFSCREEN_CANVAS)
+ { },
+#endif
#if ENABLE(WEBASSEMBLY)
dummyModules,
#endif
@@ -1154,6 +1192,12 @@
dumpImageBitmap(obj, code);
return true;
}
+#if ENABLE(OFFSCREEN_CANVAS)
+ if (obj->inherits(vm, JSOffscreenCanvas::info())) {
+ dumpOffscreenCanvas(obj, code);
+ return true;
+ }
+#endif
return false;
}
// Any other types are expected to serialize as null.
@@ -1501,6 +1545,9 @@
ObjectPool m_transferredMessagePorts;
ObjectPool m_transferredArrayBuffers;
ObjectPool m_transferredImageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS)
+ ObjectPool m_transferredOffscreenCanvases;
+#endif
typedef HashMap<RefPtr<UniquedStringImpl>, uint32_t, IdentifierRepHash> StringConstantPool;
StringConstantPool m_constantPool;
Identifier m_emptyIdentifier;
@@ -1780,7 +1827,11 @@
return str;
}
- static DeserializationResult deserialize(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers, ArrayBufferContentsArray* arrayBufferContentsArray, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers
+ static DeserializationResult deserialize(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
+#if ENABLE(OFFSCREEN_CANVAS)
+ , Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
+ , ArrayBufferContentsArray* arrayBufferContentsArray, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers
#if ENABLE(WEBASSEMBLY)
, WasmModuleArray* wasmModules
#endif
@@ -1789,6 +1840,9 @@
if (!buffer.size())
return std::make_pair(jsNull(), SerializationReturnCode::UnspecifiedError);
CloneDeserializer deserializer(lexicalGlobalObject, globalObject, messagePorts, arrayBufferContentsArray, buffer, blobURLs, blobFilePaths, sharedBuffers, WTFMove(imageBuffers)
+#if ENABLE(OFFSCREEN_CANVAS)
+ , WTFMove(detachedOffscreenCanvases)
+#endif
#if ENABLE(WEBASSEMBLY)
, wasmModules
#endif
@@ -1838,11 +1892,14 @@
size_t m_index;
};
- CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers,
+ CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers, const Vector<uint8_t>& buffer
+#if ENABLE(OFFSCREEN_CANVAS)
+ , Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases = { }
+#endif
#if ENABLE(WEBASSEMBLY)
- WasmModuleArray* wasmModules,
+ , WasmModuleArray* wasmModules = nullptr
#endif
- const Vector<uint8_t>& buffer)
+ )
: CloneBase(lexicalGlobalObject)
, m_globalObject(globalObject)
, m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>(globalObject->vm()))
@@ -1854,6 +1911,10 @@
, m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
, m_imageBuffers(WTFMove(imageBuffers))
, m_imageBitmaps(m_imageBuffers.size())
+#if ENABLE(OFFSCREEN_CANVAS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+ , m_offscreenCanvases(m_detachedOffscreenCanvases.size())
+#endif
#if ENABLE(WEBASSEMBLY)
, m_wasmModules(wasmModules)
#endif
@@ -1863,6 +1924,9 @@
}
CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
+#if ENABLE(OFFSCREEN_CANVAS)
+ , Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
#if ENABLE(WEBASSEMBLY)
, WasmModuleArray* wasmModules
#endif
@@ -1881,6 +1945,10 @@
, m_sharedBuffers(sharedBuffers)
, m_imageBuffers(WTFMove(imageBuffers))
, m_imageBitmaps(m_imageBuffers.size())
+#if ENABLE(OFFSCREEN_CANVAS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+ , m_offscreenCanvases(m_detachedOffscreenCanvases.size())
+#endif
#if ENABLE(WEBASSEMBLY)
, m_wasmModules(wasmModules)
#endif
@@ -2719,6 +2787,24 @@
return getJSValue(bitmap);
}
+#if ENABLE(OFFSCREEN_CANVAS)
+ JSValue readOffscreenCanvas()
+ {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_detachedOffscreenCanvases.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_offscreenCanvases[index])
+ m_offscreenCanvases[index] = OffscreenCanvas::create(*scriptExecutionContextFromExecState(m_lexicalGlobalObject), WTFMove(m_detachedOffscreenCanvases.at(index)));
+
+ auto offscreenCanvas = m_offscreenCanvases[index].get();
+ return getJSValue(offscreenCanvas);
+ }
+#endif
+
#if ENABLE(WEB_RTC)
JSValue readRTCCertificate()
{
@@ -3058,11 +3144,7 @@
}
JSValue cryptoKey;
Vector<RefPtr<MessagePort>> dummyMessagePorts;
- CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, dummyMessagePorts, nullptr, { },
-#if ENABLE(WEBASSEMBLY)
- nullptr,
-#endif
- serializedKey);
+ CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, dummyMessagePorts, nullptr, { }, serializedKey);
if (!rawKeyDeserializer.readCryptoKey(cryptoKey)) {
fail();
return JSValue();
@@ -3094,6 +3176,10 @@
#endif
case ImageBitmapTag:
return readImageBitmap();
+#if ENABLE(OFFSCREEN_CANVAS)
+ case OffscreenCanvasTransferTag:
+ return readOffscreenCanvas();
+#endif
default:
m_ptr--; // Push the tag back
return JSValue();
@@ -3123,6 +3209,10 @@
ArrayBufferContentsArray* m_sharedBuffers;
Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>> m_imageBuffers;
Vector<RefPtr<ImageBitmap>> m_imageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases;
+ Vector<RefPtr<OffscreenCanvas>> m_offscreenCanvases;
+#endif
#if ENABLE(WEBASSEMBLY)
WasmModuleArray* m_wasmModules;
#endif
@@ -3340,6 +3430,9 @@
}
SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, const Vector<String>& blobURLs, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
+#if ENABLE(OFFSCREEN_CANVAS)
+ , Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
#if ENABLE(WEBASSEMBLY)
, std::unique_ptr<WasmModuleArray> wasmModulesArray
#endif
@@ -3348,6 +3441,9 @@
, m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
, m_sharedBufferContentsArray(WTFMove(sharedBufferContentsArray))
, m_imageBuffers(WTFMove(imageBuffers))
+#if ENABLE(OFFSCREEN_CANVAS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+#endif
#if ENABLE(WEBASSEMBLY)
, m_wasmModulesArray(WTFMove(wasmModulesArray))
#endif
@@ -3435,6 +3531,9 @@
Vector<String> blobURLs;
Vector<RefPtr<MessagePort>> dummyMessagePorts;
Vector<RefPtr<ImageBitmap>> dummyImageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS)
+ Vector<RefPtr<OffscreenCanvas>> dummyOffscreenCanvases;
+#endif
Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers;
#if ENABLE(WEBASSEMBLY)
WasmModuleArray dummyModules;
@@ -3441,6 +3540,9 @@
#endif
ArrayBufferContentsArray dummySharedBuffers;
auto code = CloneSerializer::serialize(&lexicalGlobalObject, value, dummyMessagePorts, dummyArrayBuffers, dummyImageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS)
+ dummyOffscreenCanvases,
+#endif
#if ENABLE(WEBASSEMBLY)
dummyModules,
#endif
@@ -3456,11 +3558,7 @@
if (code != SerializationReturnCode::SuccessfullyCompleted)
return nullptr;
- return adoptRef(*new SerializedScriptValue(WTFMove(buffer), blobURLs, nullptr, nullptr, { }
-#if ENABLE(WEBASSEMBLY)
- , nullptr
-#endif
- ));
+ return adoptRef(*new SerializedScriptValue(WTFMove(buffer), blobURLs, nullptr, nullptr, { }));
}
static bool containsDuplicates(const Vector<RefPtr<ImageBitmap>>& imageBitmaps)
@@ -3473,11 +3571,29 @@
return false;
}
+#if ENABLE(OFFSCREEN_CANVAS)
+static bool canOffscreenCanvasesDetach(const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases)
+{
+ HashSet<OffscreenCanvas*> visited;
+ for (auto& offscreenCanvas : offscreenCanvases) {
+ if (!offscreenCanvas->canDetach())
+ return false;
+ // Check the return value of add, we should not encounter duplicates.
+ if (!visited.add(offscreenCanvas.get()))
+ return false;
+ }
+ return true;
+}
+#endif
+
ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& lexicalGlobalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationContext context)
{
VM& vm = lexicalGlobalObject.vm();
Vector<RefPtr<JSC::ArrayBuffer>> arrayBuffers;
Vector<RefPtr<ImageBitmap>> imageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS)
+ Vector<RefPtr<OffscreenCanvas>> offscreenCanvases;
+#endif
for (auto& transferable : transferList) {
if (auto arrayBuffer = toPossiblySharedArrayBuffer(vm, transferable.get())) {
if (arrayBuffer->isNeutered())
@@ -3504,11 +3620,22 @@
continue;
}
+#if ENABLE(OFFSCREEN_CANVAS)
+ if (auto offscreenCanvas = JSOffscreenCanvas::toWrapped(vm, transferable.get())) {
+ offscreenCanvases.append(WTFMove(offscreenCanvas));
+ continue;
+ }
+#endif
+
return Exception { DataCloneError };
}
if (containsDuplicates(imageBitmaps))
return Exception { DataCloneError };
+#if ENABLE(OFFSCREEN_CANVAS)
+ if (!canOffscreenCanvasesDetach(offscreenCanvases))
+ return Exception { InvalidStateError };
+#endif
Vector<uint8_t> buffer;
Vector<String> blobURLs;
@@ -3517,6 +3644,9 @@
#endif
std::unique_ptr<ArrayBufferContentsArray> sharedBuffers = makeUnique<ArrayBufferContentsArray>();
auto code = CloneSerializer::serialize(&lexicalGlobalObject, value, messagePorts, arrayBuffers, imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS)
+ offscreenCanvases,
+#endif
#if ENABLE(WEBASSEMBLY)
wasmModules,
#endif
@@ -3531,7 +3661,16 @@
auto imageBuffers = ImageBitmap::detachBitmaps(WTFMove(imageBitmaps));
+#if ENABLE(OFFSCREEN_CANVAS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> detachedCanvases;
+ for (auto offscreenCanvas : offscreenCanvases)
+ detachedCanvases.append(offscreenCanvas->detach());
+#endif
+
return adoptRef(*new SerializedScriptValue(WTFMove(buffer), blobURLs, arrayBufferContentsArray.releaseReturnValue(), context == SerializationContext::WorkerPostMessage ? WTFMove(sharedBuffers) : nullptr, WTFMove(imageBuffers)
+#if ENABLE(OFFSCREEN_CANVAS)
+ , WTFMove(detachedCanvases)
+#endif
#if ENABLE(WEBASSEMBLY)
, makeUnique<WasmModuleArray>(wasmModules)
#endif
@@ -3584,7 +3723,11 @@
JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions)
{
- DeserializationResult result = CloneDeserializer::deserialize(&lexicalGlobalObject, globalObject, messagePorts, WTFMove(m_imageBuffers), m_arrayBufferContentsArray.get(), m_data, blobURLs, blobFilePaths, m_sharedBufferContentsArray.get()
+ DeserializationResult result = CloneDeserializer::deserialize(&lexicalGlobalObject, globalObject, messagePorts, WTFMove(m_imageBuffers)
+#if ENABLE(OFFSCREEN_CANVAS)
+ , WTFMove(m_detachedOffscreenCanvases)
+#endif
+ , m_arrayBufferContentsArray.get(), m_data, blobURLs, blobFilePaths, m_sharedBufferContentsArray.get()
#if ENABLE(WEBASSEMBLY)
, m_wasmModulesArray.get()
#endif
Modified: trunk/Source/WebCore/bindings/js/SerializedScriptValue.h (255314 => 255315)
--- trunk/Source/WebCore/bindings/js/SerializedScriptValue.h 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/bindings/js/SerializedScriptValue.h 2020-01-28 22:48:05 UTC (rev 255315)
@@ -47,6 +47,9 @@
namespace WebCore {
+#if ENABLE(OFFSCREEN_CANVAS)
+class DetachedOffscreenCanvas;
+#endif
class IDBValue;
class ImageBitmap;
class MessagePort;
@@ -113,8 +116,11 @@
WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&);
WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>);
SerializedScriptValue(Vector<unsigned char>&&, const Vector<String>& blobURLs, std::unique_ptr<ArrayBufferContentsArray>, std::unique_ptr<ArrayBufferContentsArray> sharedBuffers, Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>>&& imageBuffers
+#if ENABLE(OFFSCREEN_CANVAS)
+ , Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& = { }
+#endif
#if ENABLE(WEBASSEMBLY)
- , std::unique_ptr<WasmModuleArray>
+ , std::unique_ptr<WasmModuleArray> = nullptr
#endif
);
@@ -122,6 +128,9 @@
std::unique_ptr<ArrayBufferContentsArray> m_arrayBufferContentsArray;
std::unique_ptr<ArrayBufferContentsArray> m_sharedBufferContentsArray;
Vector<std::pair<std::unique_ptr<ImageBuffer>, bool>> m_imageBuffers;
+#if ENABLE(OFFSCREEN_CANVAS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases;
+#endif
#if ENABLE(WEBASSEMBLY)
std::unique_ptr<WasmModuleArray> m_wasmModulesArray;
#endif
Modified: trunk/Source/WebCore/html/CanvasBase.h (255314 => 255315)
--- trunk/Source/WebCore/html/CanvasBase.h 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/html/CanvasBase.h 2020-01-28 22:48:05 UTC (rev 255315)
@@ -65,8 +65,8 @@
virtual bool isOffscreenCanvas() const { return false; }
virtual bool isCustomPaintCanvas() const { return false; }
- unsigned width() const { return m_size.width(); }
- unsigned height() const { return m_size.height(); }
+ virtual unsigned width() const { return m_size.width(); }
+ virtual unsigned height() const { return m_size.height(); }
const IntSize& size() const { return m_size; }
ImageBuffer* buffer() const;
Modified: trunk/Source/WebCore/html/OffscreenCanvas.cpp (255314 => 255315)
--- trunk/Source/WebCore/html/OffscreenCanvas.cpp 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/html/OffscreenCanvas.cpp 2020-01-28 22:48:05 UTC (rev 255315)
@@ -43,11 +43,33 @@
WTF_MAKE_ISO_ALLOCATED_IMPL(OffscreenCanvas);
+DetachedOffscreenCanvas::DetachedOffscreenCanvas(std::unique_ptr<ImageBuffer>&& buffer, const IntSize& size, bool originClean)
+ : m_buffer(WTFMove(buffer))
+ , m_size(size)
+ , m_originClean(originClean)
+{
+}
+
+std::unique_ptr<ImageBuffer> DetachedOffscreenCanvas::takeImageBuffer()
+{
+ return WTFMove(m_buffer);
+}
+
Ref<OffscreenCanvas> OffscreenCanvas::create(ScriptExecutionContext& context, unsigned width, unsigned height)
{
return adoptRef(*new OffscreenCanvas(context, width, height));
}
+Ref<OffscreenCanvas> OffscreenCanvas::create(ScriptExecutionContext& context, std::unique_ptr<DetachedOffscreenCanvas>&& detachedCanvas)
+{
+ Ref<OffscreenCanvas> clone = adoptRef(*new OffscreenCanvas(context, detachedCanvas->size().width(), detachedCanvas->size().height()));
+ clone->setImageBuffer(detachedCanvas->takeImageBuffer());
+ if (!detachedCanvas->originClean())
+ clone->setOriginTainted();
+
+ return clone;
+}
+
OffscreenCanvas::OffscreenCanvas(ScriptExecutionContext& context, unsigned width, unsigned height)
: CanvasBase(IntSize(width, height))
, ContextDestructionObserver(&context)
@@ -62,13 +84,31 @@
setImageBuffer(nullptr);
}
+unsigned OffscreenCanvas::width() const
+{
+ if (m_detached)
+ return 0;
+ return CanvasBase::width();
+}
+
+unsigned OffscreenCanvas::height() const
+{
+ if (m_detached)
+ return 0;
+ return CanvasBase::height();
+}
+
void OffscreenCanvas::setWidth(unsigned newWidth)
{
+ if (m_detached)
+ return;
setSize(IntSize(newWidth, height()));
}
void OffscreenCanvas::setHeight(unsigned newHeight)
{
+ if (m_detached)
+ return;
setSize(IntSize(width(), newHeight));
}
@@ -80,6 +120,9 @@
ExceptionOr<OffscreenRenderingContext> OffscreenCanvas::getContext(JSC::JSGlobalObject& state, RenderingContextType contextType, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
{
+ if (m_detached)
+ return Exception { InvalidStateError };
+
if (contextType == RenderingContextType::_2d) {
if (m_context) {
if (!is<OffscreenCanvasRenderingContext2D>(*m_context))
@@ -118,7 +161,7 @@
ExceptionOr<RefPtr<ImageBitmap>> OffscreenCanvas::transferToImageBitmap()
{
- if (!m_context)
+ if (m_detached || !m_context)
return Exception { InvalidStateError };
if (is<OffscreenCanvasRenderingContext2D>(*m_context)) {
@@ -192,7 +235,7 @@
promise->reject(IndexSizeError);
return;
}
- if (!buffer()) {
+ if (m_detached || !buffer()) {
promise->reject(InvalidStateError);
return;
}
@@ -226,6 +269,21 @@
return &downcast<Document>(context).securityOrigin();
}
+bool OffscreenCanvas::canDetach() const
+{
+ return !m_detached && !m_context;
+}
+
+std::unique_ptr<DetachedOffscreenCanvas> OffscreenCanvas::detach()
+{
+ if (!canDetach())
+ return nullptr;
+
+ m_detached = true;
+
+ return makeUnique<DetachedOffscreenCanvas>(takeImageBuffer(), size(), originClean());
+}
+
CSSValuePool& OffscreenCanvas::cssValuePool()
{
auto* context = canvasBaseScriptExecutionContext();
@@ -248,7 +306,8 @@
std::unique_ptr<ImageBuffer> OffscreenCanvas::takeImageBuffer() const
{
- m_hasCreatedImageBuffer = true;
+ if (!m_detached)
+ m_hasCreatedImageBuffer = true;
// This function is primarily for use with transferToImageBitmap, which
// requires that the canvas bitmap refer to a new, blank bitmap of the same
@@ -257,7 +316,7 @@
if (size().isEmpty())
return nullptr;
- return setImageBuffer(ImageBuffer::create(size(), RenderingMode::Unaccelerated));
+ return setImageBuffer(m_detached ? nullptr : ImageBuffer::create(size(), RenderingMode::Unaccelerated));
}
void OffscreenCanvas::reset()
Modified: trunk/Source/WebCore/html/OffscreenCanvas.h (255314 => 255315)
--- trunk/Source/WebCore/html/OffscreenCanvas.h 2020-01-28 22:19:03 UTC (rev 255314)
+++ trunk/Source/WebCore/html/OffscreenCanvas.h 2020-01-28 22:48:05 UTC (rev 255315)
@@ -54,6 +54,22 @@
using OffscreenRenderingContext = Variant<RefPtr<OffscreenCanvasRenderingContext2D>>;
#endif
+class DetachedOffscreenCanvas {
+ WTF_MAKE_NONCOPYABLE(DetachedOffscreenCanvas);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ DetachedOffscreenCanvas(std::unique_ptr<ImageBuffer>&&, const IntSize&, bool originClean);
+
+ std::unique_ptr<ImageBuffer> takeImageBuffer();
+ const IntSize& size() const { return m_size; }
+ bool originClean() const { return m_originClean; }
+
+private:
+ std::unique_ptr<ImageBuffer> m_buffer;
+ IntSize m_size;
+ bool m_originClean;
+};
+
class OffscreenCanvas final : public RefCounted<OffscreenCanvas>, public CanvasBase, public EventTargetWithInlineData, private ContextDestructionObserver {
WTF_MAKE_ISO_ALLOCATED(OffscreenCanvas);
public:
@@ -69,8 +85,11 @@
};
static Ref<OffscreenCanvas> create(ScriptExecutionContext&, unsigned width, unsigned height);
+ static Ref<OffscreenCanvas> create(ScriptExecutionContext&, std::unique_ptr<DetachedOffscreenCanvas>&&);
virtual ~OffscreenCanvas();
+ unsigned width() const final;
+ unsigned height() const final;
void setWidth(unsigned);
void setHeight(unsigned);
@@ -88,6 +107,10 @@
bool hasCreatedImageBuffer() const final { return m_hasCreatedImageBuffer; }
SecurityOrigin* securityOrigin() const final;
+
+ bool canDetach() const;
+ std::unique_ptr<DetachedOffscreenCanvas> detach();
+
CSSValuePool& cssValuePool();
using RefCounted::ref;
@@ -119,6 +142,8 @@
// m_hasCreatedImageBuffer means we tried to malloc the buffer. We didn't necessarily get it.
mutable bool m_hasCreatedImageBuffer { false };
+
+ bool m_detached { false };
};
}
_______________________________________________ webkit-changes mailing list [email protected] https://lists.webkit.org/mailman/listinfo/webkit-changes
