diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 93e9e8a6887..c7e8d2934c6 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -337,7 +337,8 @@ class Include(object): # handling. if module == module_name(self.inclname) or \ module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ + module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': return 0 if '/' in self.inclname: diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 4246b46ea13..aa3781b2a3e 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -5,6 +5,8 @@ #ifndef StructuredCloneTags_h__ #define StructuredCloneTags_h__ +#include "js/StructuredClone.h" + namespace mozilla { namespace dom { diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 9f9bee55f30..8cacc592803 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -14,6 +14,7 @@ #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" +#include "js/StructuredClone.h" #include "mozilla/Base64.h" diff --git a/dom/bluetooth/BluetoothReplyRunnable.h b/dom/bluetooth/BluetoothReplyRunnable.h index 8124ba94005..e700b5fbb06 100644 --- a/dom/bluetooth/BluetoothReplyRunnable.h +++ b/dom/bluetooth/BluetoothReplyRunnable.h @@ -10,7 +10,7 @@ #include "mozilla/Attributes.h" #include "BluetoothCommon.h" #include "nsThreadUtils.h" -#include "jsapi.h" +#include "js/Value.h" class nsIDOMDOMRequest; diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index b0ea8965f6c..582dfc34c39 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -10,7 +10,7 @@ #include "nsIProgrammingLanguage.h" #include "mozilla/Attributes.h" -#include "jsapi.h" +#include "js/StructuredClone.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsDebug.h" diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 2ce40d03497..8b4932193b1 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -11,6 +11,8 @@ #include "mozIStorageStatement.h" +#include "js/Value.h" + namespace IPC { template struct ParamTraits; } // namespace IPC @@ -159,7 +161,7 @@ public: } nsresult SetFromJSVal(JSContext* aCx, - const jsval aVal) + const JS::Value aVal) { mBuffer.Truncate(); @@ -209,7 +211,7 @@ public: nsresult AppendItem(JSContext* aCx, bool aFirstOfArray, - const jsval aVal) + const JS::Value aVal) { nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); if (NS_FAILED(rv)) { @@ -303,7 +305,7 @@ private: } // Encoding functions. These append the encoded value to the end of mBuffer - inline nsresult EncodeJSVal(JSContext* aCx, const jsval aVal, + inline nsresult EncodeJSVal(JSContext* aCx, const JS::Value aVal, uint8_t aTypeOffset) { return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); @@ -329,7 +331,7 @@ private: nsCString mBuffer; private: - nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal, + nsresult EncodeJSValInternal(JSContext* aCx, const JS::Value aVal, uint8_t aTypeOffset, uint16_t aRecursionDepth); static nsresult DecodeJSValInternal(const unsigned char*& aPos, diff --git a/dom/ipc/StructuredCloneUtils.cpp b/dom/ipc/StructuredCloneUtils.cpp index 39c0507b184..00f64235d93 100644 --- a/dom/ipc/StructuredCloneUtils.cpp +++ b/dom/ipc/StructuredCloneUtils.cpp @@ -15,6 +15,7 @@ #include "nsJSEnvironment.h" #include "nsThreadUtils.h" #include "StructuredCloneTags.h" +#include "jsapi.h" using namespace mozilla::dom; diff --git a/dom/ipc/StructuredCloneUtils.h b/dom/ipc/StructuredCloneUtils.h index 9c285775b2d..d403558661c 100644 --- a/dom/ipc/StructuredCloneUtils.h +++ b/dom/ipc/StructuredCloneUtils.h @@ -11,7 +11,7 @@ #include "nsTArray.h" #include "nsIDOMFile.h" -#include "jsapi.h" +#include "js/StructuredClone.h" namespace mozilla { diff --git a/dom/workers/Events.h b/dom/workers/Events.h index cbd25230667..16575e56f5d 100644 --- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -8,7 +8,7 @@ #include "Workers.h" -class JSAutoStructuredCloneBuffer; +#include "js/StructuredClone.h" BEGIN_WORKERS_NAMESPACE diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h index 458813570ec..24239eacbb4 100644 --- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -15,6 +15,8 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/TypedArray.h" +#include "js/StructuredClone.h" + BEGIN_WORKERS_NAMESPACE class Proxy; diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index bbc1fb66116..83e267b25ed 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -22,7 +22,7 @@ #include "nsMemory.h" #include "nsStringGlue.h" #include "nsTArray.h" -#include "jsapi.h" +#include "js/StructuredClone.h" #include "nsCSSProperty.h" #ifdef _MSC_VER diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h new file mode 100644 index 00000000000..22084596e73 --- /dev/null +++ b/js/public/StructuredClone.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_StructuredClone_h +#define js_StructuredClone_h + +#include + +#include "jstypes.h" + +struct JSContext; +class JSObject; +struct JSRuntime; +struct JSStructuredCloneReader; +struct JSStructuredCloneWriter; + +namespace JS { +template class Handle; +class Value; +} + +// API for the HTML5 internal structured cloning algorithm. + +// Read structured data from the reader r. This hook is used to read a value +// previously serialized by a call to the WriteStructuredCloneOp hook. +// +// tag and data are the pair of uint32_t values from the header. The callback +// may use the JS_Read* APIs to read any other relevant parts of the object +// from the reader r. closure is any value passed to the JS_ReadStructuredClone +// function. Return the new object on success, NULL on error/exception. +typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, + uint32_t tag, uint32_t data, void *closure); + +// Structured data serialization hook. The engine can write primitive values, +// Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other +// type of object requires application support. This callback must first use +// the JS_WriteUint32Pair API to write an object header, passing a value +// greater than JS_SCTAG_USER to the tag parameter. Then it can use the +// JS_Write* APIs to write any other relevant parts of the value v to the +// writer w. closure is any value passed to the JS_WriteStructuredCLone function. +// +// Return true on success, false on error/exception. +typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, + JS::Handle obj, void *closure); + +// This is called when JS_WriteStructuredClone is given an invalid transferable. +// To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException +// with error set to one of the JS_SCERR_* values. +typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); + +// The maximum supported structured-clone serialization format version. +#define JS_STRUCTURED_CLONE_VERSION 2 + +struct JSStructuredCloneCallbacks { + ReadStructuredCloneOp read; + WriteStructuredCloneOp write; + StructuredCloneErrorOp reportError; +}; + +// Note: if the *data contains transferable objects, it can be read only once. +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, uint32_t version, + JS::Value *vp, const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure); + +// Note: On success, the caller is responsible for calling +// JS_ClearStructuredClone(*datap, nbytesp). +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, JS::Value v, uint64_t **datap, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, JS::Value transferable); + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, bool *hasTransferable); + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, JS::Value v, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, void *closure); + +// RAII sugar for JS_WriteStructuredClone. +class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { + uint64_t *data_; + size_t nbytes_; + uint32_t version_; + + public: + JSAutoStructuredCloneBuffer() + : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} + + ~JSAutoStructuredCloneBuffer() { clear(); } + + uint64_t *data() const { return data_; } + size_t nbytes() const { return nbytes_; } + + void clear(); + + // Copy some memory. It will be automatically freed by the destructor. + bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + // Adopt some memory. It will be automatically freed by the destructor. + // data must have been allocated by the JS engine (e.g., extracted via + // JSAutoStructuredCloneBuffer::steal). + void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); + + // Remove the buffer so that it will not be automatically freed. + // After this, the caller is responsible for feeding the memory back to + // JSAutoStructuredCloneBuffer::adopt. + void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); + + bool read(JSContext *cx, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + bool write(JSContext *cx, JS::Value v, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + bool write(JSContext *cx, JS::Value v, JS::Value transferable, + const JSStructuredCloneCallbacks *optionalCallbacks=NULL, void *closure=NULL); + + // Swap ownership with another JSAutoStructuredCloneBuffer. + void swap(JSAutoStructuredCloneBuffer &other); + + private: + // Copy and assignment are not supported. + JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); + JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); +}; + +// The range of tag values the application may use for its own custom object types. +#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) +#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) + +#define JS_SCERR_RECURSION 0 +#define JS_SCERR_TRANSFERABLE 1 + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); + +#endif /* js_StructuredClone_h */ diff --git a/js/public/Value.h b/js/public/Value.h index e62ea805591..394c25e09e8 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -1896,6 +1896,17 @@ JSVAL_TO_PRIVATE(jsval v) return JSVAL_TO_PRIVATE_PTR_IMPL(JSVAL_TO_IMPL(v)); } +// JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and +// constructing values from scratch (e.g. Int32Value(0)). These constants are +// stored in memory and initialized at startup, so testing against them and +// using them requires memory loads and will be correspondingly slow. +extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; +extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; +extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; + #undef JS_VALUE_IS_CONSTEXPR #undef JS_RETURN_LAYOUT_FROM_BITS diff --git a/js/src/config/check_spidermonkey_style.py b/js/src/config/check_spidermonkey_style.py index 93e9e8a6887..c7e8d2934c6 100644 --- a/js/src/config/check_spidermonkey_style.py +++ b/js/src/config/check_spidermonkey_style.py @@ -337,7 +337,8 @@ class Include(object): # handling. if module == module_name(self.inclname) or \ module == 'jsmemorymetrics' and self.inclname == 'js/MemoryMetrics.h' or \ - module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h': + module == 'vm/PropertyKey' and self.inclname == 'js/PropertyKey.h' or \ + module == 'vm/StructuredClone' and self.inclname == 'js/StructuredClone.h': return 0 if '/' in self.inclname: diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1dd25e6abcd..a7050b13586 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -21,7 +21,6 @@ #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" -#include "jsclone.h" #include "jscntxt.h" #include "jsdate.h" #include "jsexn.h" @@ -60,6 +59,7 @@ #include "gc/Marking.h" #include "jit/AsmJSLink.h" #include "js/CharacterEncoding.h" +#include "js/StructuredClone.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" @@ -134,10 +134,8 @@ const jsval JSVAL_ONE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_INT32, 1)); const jsval JSVAL_FALSE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, false)); const jsval JSVAL_TRUE = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_BOOLEAN, true)); const jsval JSVAL_VOID = IMPL_TO_JSVAL(BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0)); -const HandleValue JS::NullHandleValue = - HandleValue::fromMarkedLocation(&JSVAL_NULL); -const HandleValue JS::UndefinedHandleValue = - HandleValue::fromMarkedLocation(&JSVAL_VOID); +const HandleValue JS::NullHandleValue = HandleValue::fromMarkedLocation(&JSVAL_NULL); +const HandleValue JS::UndefinedHandleValue = HandleValue::fromMarkedLocation(&JSVAL_VOID); const jsid voidIdValue = JSID_VOID; const jsid emptyIdValue = JSID_EMPTY; @@ -185,18 +183,22 @@ JS_GetEmptyString(JSRuntime *rt) return rt->emptyString; } -static void +namespace js { + +void AssertHeapIsIdle(JSRuntime *rt) { JS_ASSERT(rt->heapState == js::Idle); } -static void +void AssertHeapIsIdle(JSContext *cx) { AssertHeapIsIdle(cx->runtime()); } +} + static void AssertHeapIsIdleOrIterating(JSRuntime *rt) { @@ -5932,220 +5934,6 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval return true; } -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, - uint32_t version, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - - if (version > JS_STRUCTURED_CLONE_VERSION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); - return false; - } - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); -} - -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, jsval valueArg, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, jsval transferable) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - return WriteStructuredClone(cx, value, (uint64_t **) bufp, nbytesp, - callbacks, closure, transferable); -} - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) -{ - return ClearStructuredClone(data, nbytes); -} - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable) -{ - bool transferable; - if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) - return false; - - *hasTransferable = transferable; - return true; -} - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, jsval valueArg, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - AssertHeapIsIdle(cx); - CHECK_REQUEST(cx); - assertSameCompartment(cx, value); - - const JSStructuredCloneCallbacks *callbacks = - optionalCallbacks ? - optionalCallbacks : - cx->runtime()->structuredCloneCallbacks; - JSAutoStructuredCloneBuffer buf; - return buf.write(cx, value, callbacks, closure) && - buf.read(cx, vp, callbacks, closure); -} - -void -JSAutoStructuredCloneBuffer::clear() -{ - if (data_) { - ClearStructuredClone(data_, nbytes_); - data_ = NULL; - nbytes_ = 0; - version_ = 0; - } -} - -void -JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) -{ - clear(); - data_ = data; - nbytes_ = nbytes; - version_ = version; -} - -bool -JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) -{ - // transferable objects cannot be copied - bool hasTransferable; - if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || - hasTransferable) - return false; - - uint64_t *newData = static_cast(js_malloc(nbytes)); - if (!newData) - return false; - - js_memcpy(newData, srcData, nbytes); - - clear(); - data_ = newData; - nbytes_ = nbytes; - version_ = version; - return true; -} -void -JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) -{ - *datap = data_; - *nbytesp = nbytes_; - if (versionp) - *versionp = version_; - - data_ = NULL; - nbytes_ = 0; - version_ = 0; -} - -bool -JSAutoStructuredCloneBuffer::read(JSContext *cx, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - JS_ASSERT(cx); - JS_ASSERT(data_); - return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, - optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - jsval transferable = JSVAL_VOID; - return write(cx, valueArg, transferable, optionalCallbacks, closure); -} - -bool -JSAutoStructuredCloneBuffer::write(JSContext *cx, jsval valueArg, - jsval transferable, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure) -{ - RootedValue value(cx, valueArg); - clear(); - bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, - optionalCallbacks, closure, - transferable); - if (!ok) { - data_ = NULL; - nbytes_ = 0; - version_ = JS_STRUCTURED_CLONE_VERSION; - } - return ok; -} - -void -JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) -{ - uint64_t *data = other.data_; - size_t nbytes = other.nbytes_; - uint32_t version = other.version_; - - other.data_ = this->data_; - other.nbytes_ = this->nbytes_; - other.version_ = this->version_; - - this->data_ = data; - this->nbytes_ = nbytes; - this->version_ = version; -} - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) -{ - rt->structuredCloneCallbacks = callbacks; -} - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) -{ - return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); -} - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) -{ - return r->input().readBytes(p, len); -} - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) -{ - return w->output().writePair(tag, data); -} - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) -{ - return w->output().writeBytes(p, len); -} - /************************************************************************/ JS_PUBLIC_API(void) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c103ec36787..6306cf8aae5 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1147,54 +1147,8 @@ typedef void (* JSCompartmentNameCallback)(JSRuntime *rt, JSCompartment *compartment, char *buf, size_t bufsize); -/* - * Read structured data from the reader r. This hook is used to read a value - * previously serialized by a call to the WriteStructuredCloneOp hook. - * - * tag and data are the pair of uint32_t values from the header. The callback - * may use the JS_Read* APIs to read any other relevant parts of the object - * from the reader r. closure is any value passed to the JS_ReadStructuredClone - * function. Return the new object on success, NULL on error/exception. - */ -typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r, - uint32_t tag, uint32_t data, void *closure); - -/* - * Structured data serialization hook. The engine can write primitive values, - * Objects, Arrays, Dates, RegExps, TypedArrays, and ArrayBuffers. Any other - * type of object requires application support. This callback must first use - * the JS_WriteUint32Pair API to write an object header, passing a value - * greater than JS_SCTAG_USER to the tag parameter. Then it can use the - * JS_Write* APIs to write any other relevant parts of the value v to the - * writer w. closure is any value passed to the JS_WriteStructuredCLone function. - * - * Return true on success, false on error/exception. - */ -typedef bool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, - JS::Handle obj, void *closure); - -/* - * This is called when JS_WriteStructuredClone is given an invalid transferable. - * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException - * with error set to one of the JS_SCERR_* values. - */ -typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid); - /************************************************************************/ -/* - * JS constants. For efficiency, prefer predicates (e.g. v.isNull()) and - * constructing values from scratch (e.g. Int32Value(0)). These constants are - * stored in memory and initialized at startup, so testing against them and - * using them requires memory loads and will be correspondingly slow. - */ -extern JS_PUBLIC_DATA(const jsval) JSVAL_NULL; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ZERO; -extern JS_PUBLIC_DATA(const jsval) JSVAL_ONE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_FALSE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_TRUE; -extern JS_PUBLIC_DATA(const jsval) JSVAL_VOID; - static JS_ALWAYS_INLINE jsval JS_NumberValue(double d) { @@ -1876,6 +1830,12 @@ template <> struct GCMethods #endif }; +void +AssertHeapIsIdle(JSRuntime *rt); + +void +AssertHeapIsIdle(JSContext *cx); + } /* namespace js */ class JSAutoRequest @@ -4709,134 +4669,6 @@ JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, jsval /************************************************************************/ -/* API for the HTML5 internal structured cloning algorithm. */ - -/* The maximum supported structured-clone serialization format version. */ -#define JS_STRUCTURED_CLONE_VERSION 2 - -struct JSStructuredCloneCallbacks { - ReadStructuredCloneOp read; - WriteStructuredCloneOp write; - StructuredCloneErrorOp reportError; -}; - -/* Note: if the *data contains transferable objects, it can be read - * only once */ -JS_PUBLIC_API(bool) -JS_ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, - uint32_t version, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -/* Note: On success, the caller is responsible for calling - * JS_ClearStructuredClone(*datap, nbytesp). */ -JS_PUBLIC_API(bool) -JS_WriteStructuredClone(JSContext *cx, jsval v, uint64_t **datap, size_t *nbytesp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure, jsval transferable); - -JS_PUBLIC_API(bool) -JS_ClearStructuredClone(const uint64_t *data, size_t nbytes); - -JS_PUBLIC_API(bool) -JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, - bool *hasTransferable); - -JS_PUBLIC_API(bool) -JS_StructuredClone(JSContext *cx, jsval v, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks, - void *closure); - -/* RAII sugar for JS_WriteStructuredClone. */ -class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { - uint64_t *data_; - size_t nbytes_; - uint32_t version_; - - public: - JSAutoStructuredCloneBuffer() - : data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {} - - ~JSAutoStructuredCloneBuffer() { clear(); } - - uint64_t *data() const { return data_; } - size_t nbytes() const { return nbytes_; } - - void clear(); - - /* Copy some memory. It will be automatically freed by the destructor. */ - bool copy(const uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - /* - * Adopt some memory. It will be automatically freed by the destructor. - * data must have been allocated by the JS engine (e.g., extracted via - * JSAutoStructuredCloneBuffer::steal). - */ - void adopt(uint64_t *data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION); - - /* - * Remove the buffer so that it will not be automatically freed. - * After this, the caller is responsible for feeding the memory back to - * JSAutoStructuredCloneBuffer::adopt. - */ - void steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp=NULL); - - bool read(JSContext *cx, jsval *vp, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - bool write(JSContext *cx, jsval v, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - bool write(JSContext *cx, jsval v, - jsval transferable, - const JSStructuredCloneCallbacks *optionalCallbacks=NULL, - void *closure=NULL); - - /** - * Swap ownership with another JSAutoStructuredCloneBuffer. - */ - void swap(JSAutoStructuredCloneBuffer &other); - - private: - /* Copy and assignment are not supported. */ - JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other); - JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other); -}; - -/* API for implementing custom serialization behavior (for ImageData, File, etc.) */ - -/* The range of tag values the application may use for its own custom object types. */ -#define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000) -#define JS_SCTAG_USER_MAX ((uint32_t) 0xFFFFFFFF) - -#define JS_SCERR_RECURSION 0 -#define JS_SCERR_TRANSFERABLE 1 - -JS_PUBLIC_API(void) -JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks); - -JS_PUBLIC_API(bool) -JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2); - -JS_PUBLIC_API(bool) -JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); - -JS_PUBLIC_API(bool) -JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data); - -JS_PUBLIC_API(bool) -JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len); - -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); - -/************************************************************************/ - /* * The default locale for the ECMAScript Internationalization API * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat). diff --git a/js/src/jsclone.h b/js/src/jsclone.h deleted file mode 100644 index daec0e86bfe..00000000000 --- a/js/src/jsclone.h +++ /dev/null @@ -1,206 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef jsclone_h -#define jsclone_h - -#include "jsapi.h" -#include "jscntxt.h" - -#include "js/Vector.h" - -namespace js { - -bool -WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable); - -bool -ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure); - -bool -ClearStructuredClone(const uint64_t *data, size_t nbytes); - -bool -StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, - bool *hasTransferable); - -struct SCOutput { - public: - explicit SCOutput(JSContext *cx); - - JSContext *context() const { return cx; } - - bool write(uint64_t u); - bool writePair(uint32_t tag, uint32_t data); - bool writeDouble(double d); - bool writeBytes(const void *p, size_t nbytes); - bool writeChars(const jschar *p, size_t nchars); - bool writePtr(const void *); - - template - bool writeArray(const T *p, size_t nbytes); - - bool extractBuffer(uint64_t **datap, size_t *sizep); - - uint64_t count() { return buf.length(); } - - private: - JSContext *cx; - js::Vector buf; -}; - -struct SCInput { - public: - SCInput(JSContext *cx, uint64_t *data, size_t nbytes); - - JSContext *context() const { return cx; } - - bool read(uint64_t *p); - bool readPair(uint32_t *tagp, uint32_t *datap); - bool readDouble(double *p); - bool readBytes(void *p, size_t nbytes); - bool readChars(jschar *p, size_t nchars); - bool readPtr(void **); - - bool get(uint64_t *p); - bool getPair(uint32_t *tagp, uint32_t *datap); - - bool replace(uint64_t u); - bool replacePair(uint32_t tag, uint32_t data); - - template - bool readArray(T *p, size_t nelems); - - private: - bool eof(); - - void staticAssertions() { - JS_STATIC_ASSERT(sizeof(jschar) == 2); - JS_STATIC_ASSERT(sizeof(uint32_t) == 4); - JS_STATIC_ASSERT(sizeof(double) == 8); - } - - JSContext *cx; - uint64_t *point; - uint64_t *end; -}; - -} /* namespace js */ - -struct JSStructuredCloneReader { - public: - explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, - void *cbClosure) - : in(in), objs(in.context()), allObjs(in.context()), - callbacks(cb), closure(cbClosure) { } - - js::SCInput &input() { return in; } - bool read(js::Value *vp); - - private: - JSContext *context() { return in.context(); } - - bool readTransferMap(); - - bool checkDouble(double d); - JSString *readString(uint32_t nchars); - bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); - bool readArrayBuffer(uint32_t nbytes, js::Value *vp); - bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); - bool readId(jsid *idp); - bool startRead(js::Value *vp); - - js::SCInput ∈ - - // Stack of objects with properties remaining to be read. - js::AutoValueVector objs; - - // Stack of all objects read during this deserialization - js::AutoValueVector allObjs; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_ReadStructuredClone. - void *closure; - - friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp); -}; - -struct JSStructuredCloneWriter { - public: - explicit JSStructuredCloneWriter(js::SCOutput &out, - const JSStructuredCloneCallbacks *cb, - void *cbClosure, - jsval tVal) - : out(out), objs(out.context()), - counts(out.context()), ids(out.context()), - memory(out.context()), callbacks(cb), closure(cbClosure), - transferable(out.context(), tVal), transferableObjects(out.context()) { } - - bool init() { return transferableObjects.init() && parseTransferable() && - memory.init() && writeTransferMap(); } - - bool write(const js::Value &v); - - js::SCOutput &output() { return out; } - - private: - JSContext *context() { return out.context(); } - - bool writeTransferMap(); - - bool writeString(uint32_t tag, JSString *str); - bool writeId(jsid id); - bool writeArrayBuffer(JS::HandleObject obj); - bool writeTypedArray(JS::HandleObject obj); - bool startObject(JS::HandleObject obj, bool *backref); - bool startWrite(const js::Value &v); - bool traverseObject(JS::HandleObject obj); - - bool parseTransferable(); - void reportErrorTransferable(); - - inline void checkStack(); - - js::SCOutput &out; - - // Vector of objects with properties remaining to be written. - // - // NB: These can span multiple compartments, so the compartment must be - // entered before any manipulation is performed. - js::AutoValueVector objs; - - // counts[i] is the number of properties of objs[i] remaining to be written. - // counts.length() == objs.length() and sum(counts) == ids.length(). - js::Vector counts; - - // Ids of properties remaining to be written. - js::AutoIdVector ids; - - // The "memory" list described in the HTML5 internal structured cloning algorithm. - // memory is a superset of objs; items are never removed from Memory - // until a serialization operation is finished - typedef js::AutoObjectUnsigned32HashMap CloneMemory; - CloneMemory memory; - - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks *callbacks; - - // Any value passed to JS_WriteStructuredClone. - void *closure; - - // List of transferable objects - JS::RootedValue transferable; - js::AutoObjectHashSet transferableObjects; - - friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v); -}; - -#endif /* jsclone_h */ diff --git a/js/src/moz.build b/js/src/moz.build index 82e97b5a9be..43d314bf6e9 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -71,6 +71,7 @@ EXPORTS.js += [ '../public/PropertyKey.h', '../public/RequiredDefines.h', '../public/RootingAPI.h', + '../public/StructuredClone.h', '../public/Utility.h', '../public/Value.h', '../public/Vector.h', @@ -125,6 +126,7 @@ CPP_SOURCES += [ 'StoreBuffer.cpp', 'String.cpp', 'StringBuffer.cpp', + 'StructuredClone.cpp', 'TestingFunctions.cpp', 'ThreadPool.cpp', 'TokenStream.cpp', @@ -143,7 +145,6 @@ CPP_SOURCES += [ 'jsarray.cpp', 'jsatom.cpp', 'jsbool.cpp', - 'jsclone.cpp', 'jscntxt.cpp', 'jscompartment.cpp', 'jscrashreport.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index ff9642cfe7b..d8c0313eee5 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -62,6 +62,7 @@ #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "jit/Ion.h" +#include "js/StructuredClone.h" #include "perf/jsperf.h" #include "shell/jsheaptools.h" #include "shell/jsoptparse.h" diff --git a/js/src/jsclone.cpp b/js/src/vm/StructuredClone.cpp similarity index 75% rename from js/src/jsclone.cpp rename to js/src/vm/StructuredClone.cpp index dac5ca55988..c3e1dc33b80 100644 --- a/js/src/jsclone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -27,11 +27,13 @@ * array object. */ -#include "jsclone.h" +#include "js/StructuredClone.h" #include "mozilla/Endian.h" #include "mozilla/FloatingPoint.h" +#include "jsapi.h" +#include "jscntxt.h" #include "jsdate.h" #include "jswrapper.h" @@ -86,6 +88,181 @@ enum TransferableMapHeader { SCTAG_TM_MARKED }; +namespace js { + +struct SCOutput { + public: + explicit SCOutput(JSContext *cx); + + JSContext *context() const { return cx; } + + bool write(uint64_t u); + bool writePair(uint32_t tag, uint32_t data); + bool writeDouble(double d); + bool writeBytes(const void *p, size_t nbytes); + bool writeChars(const jschar *p, size_t nchars); + bool writePtr(const void *); + + template + bool writeArray(const T *p, size_t nbytes); + + bool extractBuffer(uint64_t **datap, size_t *sizep); + + uint64_t count() { return buf.length(); } + + private: + JSContext *cx; + js::Vector buf; +}; + +struct SCInput { + public: + SCInput(JSContext *cx, uint64_t *data, size_t nbytes); + + JSContext *context() const { return cx; } + + bool read(uint64_t *p); + bool readPair(uint32_t *tagp, uint32_t *datap); + bool readDouble(double *p); + bool readBytes(void *p, size_t nbytes); + bool readChars(jschar *p, size_t nchars); + bool readPtr(void **); + + bool get(uint64_t *p); + bool getPair(uint32_t *tagp, uint32_t *datap); + + bool replace(uint64_t u); + bool replacePair(uint32_t tag, uint32_t data); + + template + bool readArray(T *p, size_t nelems); + + private: + bool eof(); + + void staticAssertions() { + JS_STATIC_ASSERT(sizeof(jschar) == 2); + JS_STATIC_ASSERT(sizeof(uint32_t) == 4); + JS_STATIC_ASSERT(sizeof(double) == 8); + } + + JSContext *cx; + uint64_t *point; + uint64_t *end; +}; + +} + +struct JSStructuredCloneReader { + public: + explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb, + void *cbClosure) + : in(in), objs(in.context()), allObjs(in.context()), + callbacks(cb), closure(cbClosure) { } + + js::SCInput &input() { return in; } + bool read(js::Value *vp); + + private: + JSContext *context() { return in.context(); } + + bool readTransferMap(); + + bool checkDouble(double d); + JSString *readString(uint32_t nchars); + bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false); + bool readArrayBuffer(uint32_t nbytes, js::Value *vp); + bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp); + bool readId(jsid *idp); + bool startRead(js::Value *vp); + + js::SCInput ∈ + + // Stack of objects with properties remaining to be read. + js::AutoValueVector objs; + + // Stack of all objects read during this deserialization + js::AutoValueVector allObjs; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_ReadStructuredClone. + void *closure; + + friend bool JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp); +}; + +struct JSStructuredCloneWriter { + public: + explicit JSStructuredCloneWriter(js::SCOutput &out, + const JSStructuredCloneCallbacks *cb, + void *cbClosure, + jsval tVal) + : out(out), objs(out.context()), + counts(out.context()), ids(out.context()), + memory(out.context()), callbacks(cb), closure(cbClosure), + transferable(out.context(), tVal), transferableObjects(out.context()) { } + + bool init() { return transferableObjects.init() && parseTransferable() && + memory.init() && writeTransferMap(); } + + bool write(const js::Value &v); + + js::SCOutput &output() { return out; } + + private: + JSContext *context() { return out.context(); } + + bool writeTransferMap(); + + bool writeString(uint32_t tag, JSString *str); + bool writeId(jsid id); + bool writeArrayBuffer(JS::HandleObject obj); + bool writeTypedArray(JS::HandleObject obj); + bool startObject(JS::HandleObject obj, bool *backref); + bool startWrite(const js::Value &v); + bool traverseObject(JS::HandleObject obj); + + bool parseTransferable(); + void reportErrorTransferable(); + + inline void checkStack(); + + js::SCOutput &out; + + // Vector of objects with properties remaining to be written. + // + // NB: These can span multiple compartments, so the compartment must be + // entered before any manipulation is performed. + js::AutoValueVector objs; + + // counts[i] is the number of properties of objs[i] remaining to be written. + // counts.length() == objs.length() and sum(counts) == ids.length(). + js::Vector counts; + + // Ids of properties remaining to be written. + js::AutoIdVector ids; + + // The "memory" list described in the HTML5 internal structured cloning algorithm. + // memory is a superset of objs; items are never removed from Memory + // until a serialization operation is finished + typedef js::AutoObjectUnsigned32HashMap CloneMemory; + CloneMemory memory; + + // The user defined callbacks that will be used for cloning. + const JSStructuredCloneCallbacks *callbacks; + + // Any value passed to JS_WriteStructuredClone. + void *closure; + + // List of transferable objects + JS::RootedValue transferable; + js::AutoObjectHashSet transferableObjects; + + friend bool JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v); +}; + JS_FRIEND_API(uint64_t) js_GetSCOffset(JSStructuredCloneWriter* writer) { @@ -97,10 +274,12 @@ JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); JS_STATIC_ASSERT(TypedArrayObject::TYPE_INT8 == 0); +namespace js { + bool -js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, - const JSStructuredCloneCallbacks *cb, void *cbClosure, - jsval transferable) +WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *cb, void *cbClosure, + jsval transferable) { SCOutput out(cx); JSStructuredCloneWriter w(out, cb, cbClosure, transferable); @@ -108,8 +287,8 @@ js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t * } bool -js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, - const JSStructuredCloneCallbacks *cb, void *cbClosure) +ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, + const JSStructuredCloneCallbacks *cb, void *cbClosure) { SCInput in(cx, data, nbytes); @@ -121,7 +300,7 @@ js::ReadStructuredClone(JSContext *cx, uint64_t *data, size_t nbytes, Value *vp, } bool -js::ClearStructuredClone(const uint64_t *data, size_t nbytes) +ClearStructuredClone(const uint64_t *data, size_t nbytes) { const uint64_t *point = data; const uint64_t *end = data + nbytes / 8; @@ -152,7 +331,7 @@ js::ClearStructuredClone(const uint64_t *data, size_t nbytes) } bool -js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) +StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable) { *hasTransferable = false; @@ -167,6 +346,8 @@ js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool return true; } +} + static inline uint64_t PairToUInt64(uint32_t tag, uint32_t data) { @@ -561,24 +742,6 @@ JSStructuredCloneWriter::checkStack() #endif } -JS_PUBLIC_API(bool) -JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v) -{ - JS_ASSERT(v.isObject()); - assertSameCompartment(w->context(), v); - RootedObject obj(w->context(), &v.toObject()); - - // If the object is a security wrapper, see if we're allowed to unwrap it. - // If we aren't, throw. - if (obj->is()) - obj = CheckedUnwrap(obj); - if (!obj) { - JS_ReportError(w->context(), "Permission denied to access object"); - return false; - } - return w->writeTypedArray(obj); -} - /* * Write out a typed array. Note that post-v1 structured clone buffers do not * perform endianness conversion on stored data, so multibyte typed arrays @@ -852,26 +1015,6 @@ TagToV1ArrayType(uint32_t tag) return tag - SCTAG_TYPED_ARRAY_V1_MIN; } -JS_PUBLIC_API(bool) -JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp) -{ - uint32_t tag, nelems; - if (!r->input().readPair(&tag, &nelems)) - return false; - if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { - return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); - } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { - uint64_t arrayType; - if (!r->input().read(&arrayType)) - return false; - return r->readTypedArray(arrayType, nelems, vp); - } else { - JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, - JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); - return false; - } -} - bool JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Value *vp, bool v1Read) @@ -1280,3 +1423,257 @@ JSStructuredCloneReader::read(Value *vp) return true; } +using namespace js; + +JS_PUBLIC_API(bool) +JS_ReadStructuredClone(JSContext *cx, uint64_t *buf, size_t nbytes, + uint32_t version, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + + if (version > JS_STRUCTURED_CLONE_VERSION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION); + return false; + } + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return ReadStructuredClone(cx, buf, nbytes, vp, callbacks, closure); +} + +JS_PUBLIC_API(bool) +JS_WriteStructuredClone(JSContext *cx, JS::Value valueArg, uint64_t **bufp, size_t *nbytesp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure, JS::Value transferable) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable); +} + +JS_PUBLIC_API(bool) +JS_ClearStructuredClone(const uint64_t *data, size_t nbytes) +{ + return ClearStructuredClone(data, nbytes); +} + +JS_PUBLIC_API(bool) +JS_StructuredCloneHasTransferables(const uint64_t *data, size_t nbytes, + bool *hasTransferable) +{ + bool transferable; + if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable)) + return false; + + *hasTransferable = transferable; + return true; +} + +JS_PUBLIC_API(bool) +JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, value); + + const JSStructuredCloneCallbacks *callbacks = + optionalCallbacks ? + optionalCallbacks : + cx->runtime()->structuredCloneCallbacks; + JSAutoStructuredCloneBuffer buf; + return buf.write(cx, value, callbacks, closure) && + buf.read(cx, vp, callbacks, closure); +} + +void +JSAutoStructuredCloneBuffer::clear() +{ + if (data_) { + ClearStructuredClone(data_, nbytes_); + data_ = NULL; + nbytes_ = 0; + version_ = 0; + } +} + +bool +JSAutoStructuredCloneBuffer::copy(const uint64_t *srcData, size_t nbytes, uint32_t version) +{ + // transferable objects cannot be copied + bool hasTransferable; + if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) || + hasTransferable) + return false; + + uint64_t *newData = static_cast(js_malloc(nbytes)); + if (!newData) + return false; + + js_memcpy(newData, srcData, nbytes); + + clear(); + data_ = newData; + nbytes_ = nbytes; + version_ = version; + return true; +} + +void +JSAutoStructuredCloneBuffer::adopt(uint64_t *data, size_t nbytes, uint32_t version) +{ + clear(); + data_ = data; + nbytes_ = nbytes; + version_ = version; +} + +void +JSAutoStructuredCloneBuffer::steal(uint64_t **datap, size_t *nbytesp, uint32_t *versionp) +{ + *datap = data_; + *nbytesp = nbytes_; + if (versionp) + *versionp = version_; + + data_ = NULL; + nbytes_ = 0; + version_ = 0; +} + +bool +JSAutoStructuredCloneBuffer::read(JSContext *cx, JS::Value *vp, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS_ASSERT(cx); + JS_ASSERT(data_); + return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp, + optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + JS::Value transferable = JSVAL_VOID; + return write(cx, valueArg, transferable, optionalCallbacks, closure); +} + +bool +JSAutoStructuredCloneBuffer::write(JSContext *cx, JS::Value valueArg, + JS::Value transferable, + const JSStructuredCloneCallbacks *optionalCallbacks, + void *closure) +{ + RootedValue value(cx, valueArg); + clear(); + bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_, + optionalCallbacks, closure, + transferable); + if (!ok) { + data_ = NULL; + nbytes_ = 0; + version_ = JS_STRUCTURED_CLONE_VERSION; + } + return ok; +} + +void +JSAutoStructuredCloneBuffer::swap(JSAutoStructuredCloneBuffer &other) +{ + uint64_t *data = other.data_; + size_t nbytes = other.nbytes_; + uint32_t version = other.version_; + + other.data_ = this->data_; + other.nbytes_ = this->nbytes_; + other.version_ = this->version_; + + this->data_ = data; + this->nbytes_ = nbytes; + this->version_ = version; +} + +JS_PUBLIC_API(void) +JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks) +{ + rt->structuredCloneCallbacks = callbacks; +} + +JS_PUBLIC_API(bool) +JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2) +{ + return r->input().readPair((uint32_t *) p1, (uint32_t *) p2); +} + +JS_PUBLIC_API(bool) +JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len) +{ + return r->input().readBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_ReadTypedArray(JSStructuredCloneReader *r, JS::Value *vp) +{ + uint32_t tag, nelems; + if (!r->input().readPair(&tag, &nelems)) + return false; + if (tag >= SCTAG_TYPED_ARRAY_V1_MIN && tag <= SCTAG_TYPED_ARRAY_V1_MAX) { + return r->readTypedArray(TagToV1ArrayType(tag), nelems, vp, true); + } else if (tag == SCTAG_TYPED_ARRAY_OBJECT) { + uint64_t arrayType; + if (!r->input().read(&arrayType)) + return false; + return r->readTypedArray(arrayType, nelems, vp); + } else { + JS_ReportErrorNumber(r->context(), js_GetErrorMessage, NULL, + JSMSG_SC_BAD_SERIALIZED_DATA, "expected type array"); + return false; + } +} + +JS_PUBLIC_API(bool) +JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data) +{ + return w->output().writePair(tag, data); +} + +JS_PUBLIC_API(bool) +JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len) +{ + return w->output().writeBytes(p, len); +} + +JS_PUBLIC_API(bool) +JS_WriteTypedArray(JSStructuredCloneWriter *w, JS::Value v) +{ + JS_ASSERT(v.isObject()); + assertSameCompartment(w->context(), v); + RootedObject obj(w->context(), &v.toObject()); + + // If the object is a security wrapper, see if we're allowed to unwrap it. + // If we aren't, throw. + if (obj->is()) + obj = CheckedUnwrap(obj); + if (!obj) { + JS_ReportError(w->context(), "Permission denied to access object"); + return false; + } + return w->writeTypedArray(obj); +} + diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 93b05c797ac..a63910a8750 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -12,6 +12,7 @@ #include "jsdbgapi.h" #include "jsfriendapi.h" #include "jsproxy.h" +#include "js/StructuredClone.h" #include "nsContentUtils.h" #include "nsCxPusher.h" #include "nsGlobalWindow.h"