StructuredCloneData.h (10862B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_ipc_StructuredCloneData_h 8 #define mozilla_dom_ipc_StructuredCloneData_h 9 10 #include <algorithm> 11 12 #include "mozilla/RefPtr.h" 13 #include "mozilla/UniquePtr.h" 14 #include "mozilla/dom/StructuredCloneHolder.h" 15 #include "nsIInputStream.h" 16 #include "nsISupportsImpl.h" 17 18 namespace IPC { 19 class Message; 20 class MessageReader; 21 class MessageWriter; 22 } // namespace IPC 23 class PickleIterator; 24 25 namespace mozilla { 26 namespace ipc { 27 28 class PBackgroundChild; 29 class PBackgroundParent; 30 31 } // namespace ipc 32 33 namespace dom { 34 35 class ContentChild; 36 class ContentParent; 37 38 namespace ipc { 39 40 /** 41 * Wraps the non-reference-counted JSStructuredCloneData class to have a 42 * reference count so that multiple StructuredCloneData instances can reference 43 * a single underlying serialized representation. 44 * 45 * As used by StructuredCloneData, it is an invariant that our 46 * JSStructuredCloneData owns its buffers. (For the non-owning case, 47 * StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed 48 * read-only view of the data.) 49 */ 50 class SharedJSAllocatedData final { 51 public: 52 explicit SharedJSAllocatedData(JSStructuredCloneData&& aData) 53 : mData(std::move(aData)) {} 54 55 static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData( 56 const char* aData, size_t aDataLength) { 57 JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess); 58 NS_ENSURE_TRUE(buf.AppendBytes(aData, aDataLength), nullptr); 59 RefPtr<SharedJSAllocatedData> sharedData = 60 new SharedJSAllocatedData(std::move(buf)); 61 return sharedData.forget(); 62 } 63 64 static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData( 65 const JSStructuredCloneData& aData) { 66 JSStructuredCloneData buf(aData.scope()); 67 NS_ENSURE_TRUE(buf.Append(aData), nullptr); 68 RefPtr<SharedJSAllocatedData> sharedData = 69 new SharedJSAllocatedData(std::move(buf)); 70 return sharedData.forget(); 71 } 72 73 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedJSAllocatedData) 74 75 JSStructuredCloneData& Data() { return mData; } 76 size_t DataLength() const { return mData.Size(); } 77 78 private: 79 ~SharedJSAllocatedData() = default; 80 81 JSStructuredCloneData mData; 82 }; 83 84 /** 85 * IPC-aware StructuredCloneHolder subclass that serves as both a helper class 86 * for dealing with message data (blobs, transferables) and also an IPDL 87 * data-type in cases where message data is not needed. If your use-case does 88 * not (potentially) involve IPC, then you should use StructuredCloneHolder or 89 * one of its other subclasses instead. 90 * 91 * ## Usage ## 92 * 93 * The general recipe for using this class is: 94 * - In your IPDL definition, use the ClonedMessageData type whenever you want 95 * to send a structured clone that may include blobs or transferables such as 96 * message ports. 97 * - To send the data, instantiate a StructuredCloneData instance and Write() 98 * into it like a normal structured clone. When you are ready to send the 99 * ClonedMessageData-bearing IPC message, call the BuildClonedMessageData 100 * method to populate the ClonedMessageData and then send it before your 101 * StructuredCloneData instance is destroyed. (Buffer borrowing is used 102 * under-the-hood to avoid duplicating the serialized data, requiring this.) 103 * - To receive the data, instantiate a StructuredCloneData and then call 104 * the BorrowFromClonedMessageData method. See the memory management 105 * section for more information. 106 * 107 * ## Memory Management ## 108 * 109 * Serialized structured clone representations can be quite large. So it's best 110 * to avoid wasteful duplication. When Write()ing into the StructuredCloneData, 111 * you don't need to worry about this[1], but when you already have serialized 112 * structured clone data you plan to Read(), you do need to. Similarly, if 113 * you're using StructuredCloneData as an IPDL type, it efficiently unmarshals. 114 * 115 * The from-ClonedMessageData memory management strategies available are: 116 * - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only 117 * BufferList::Borrow()ed copy of the source. Your StructuredCloneData needs 118 * to be destroyed before the source is. Commonly used when the 119 * StructuredCloneData instance is stack-allocated (and Read() is used before 120 * the function returns). 121 * - Copy: Makes a reference-counted copy of the source JSStructuredCloneData, 122 * making it safe for the StructuredCloneData to outlive the source data. 123 * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that 124 * it's safe for the StructuredCloneData to outlive the source data. This is 125 * safe to use with IPC-provided ClonedMessageData instances because 126 * JSStructuredCloneData's IPC ParamTraits::Read method copies the relevant 127 * data into owned buffers. But if it's possible the ClonedMessageData came 128 * from a different source that might have borrowed the buffers itself, then 129 * things will crash. That would be a pretty strange implementation; if you 130 * see one, change it to use SharedJSAllocatedData. 131 * 132 * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is 133 * created efficiently (by stealing from StructuredCloneHolder). The 134 * BuildClonedMessageData method can be called at any time and it will 135 * borrow the underlying memory. While it would be even better if 136 * SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref, 137 * there's no reason you can't wait to call the BuildClonedMessageData 138 * method until you need to make the IPC Send* call. 139 */ 140 class StructuredCloneData : public StructuredCloneHolder { 141 public: 142 StructuredCloneData(); 143 144 // StructuredCloneData is a large data structure, so don't let it be 145 // implicitly copied. 146 StructuredCloneData(const StructuredCloneData&) = delete; 147 StructuredCloneData& operator=(const StructuredCloneData& aOther) = delete; 148 149 // It would be safe to define a move operator for StructuredCloneData, unlike 150 // for the parent classes, but the large number of arrays for storing extra 151 // DOM data mean that it would fragile. Instead, use 152 // UniquePtr<StructuredCloneData> to pass around SCDs without copying. 153 StructuredCloneData(StructuredCloneData&& aOther) = delete; 154 StructuredCloneData& operator=(StructuredCloneData&& aOther) = delete; 155 156 // Only DifferentProcess and UnknownDestination scopes are supported. 157 StructuredCloneData(StructuredCloneScope aScope, 158 TransferringSupport aSupportsTransferring); 159 160 ~StructuredCloneData(); 161 162 const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const { return mBlobImplArray; } 163 164 nsTArray<RefPtr<BlobImpl>>& BlobImpls() { return mBlobImplArray; } 165 166 const nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() const { 167 return mInputStreamArray; 168 } 169 170 nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() { 171 return mInputStreamArray; 172 } 173 174 bool Copy(const StructuredCloneData& aData); 175 176 void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue, 177 ErrorResult& aRv); 178 179 void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue, 180 const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv); 181 182 // Write with no transfer objects and with the default CloneDataPolicy. With 183 // a default CloneDataPolicy, read and write will not be considered as part of 184 // the same agent cluster and shared memory objects will not be supported. 185 void Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 186 ErrorResult& aRv) override; 187 188 // The most generic Write method, with tansfers and CloneDataPolicy. 189 void Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 190 JS::Handle<JS::Value> aTransfers, 191 const JS::CloneDataPolicy& aCloneDataPolicy, 192 ErrorResult& aRv) override; 193 194 // Method to convert the structured clone stored in this holder by a previous 195 // call to Write() into ClonedMessageData IPC representation. 196 bool BuildClonedMessageData(ClonedMessageData& aClonedData); 197 198 // Memory-management-strategy-varying methods to initialize this holder from a 199 // ClonedMessageData representation. 200 void BorrowFromClonedMessageData(const ClonedMessageData& aClonedData); 201 202 void CopyFromClonedMessageData(const ClonedMessageData& aClonedData); 203 204 // The steal variant of course takes a non-const ClonedMessageData. 205 void StealFromClonedMessageData(ClonedMessageData& aClonedData); 206 207 // Initialize this instance, borrowing the contents of the given 208 // JSStructuredCloneData. You are responsible for ensuring that this 209 // StructuredCloneData instance is destroyed before aData is destroyed. 210 bool UseExternalData(const JSStructuredCloneData& aData) { 211 auto iter = aData.Start(); 212 bool success = false; 213 mExternalData = aData.Borrow(iter, aData.Size(), &success); 214 mInitialized = true; 215 return success; 216 } 217 218 // Initialize this instance by copying the given data that probably came from 219 // nsStructuredClone doing a base64 decode. Don't use this. 220 bool CopyExternalData(const char* aData, size_t aDataLength); 221 // Initialize this instance by copying the contents of an existing 222 // JSStructuredCloneData. Use when this StructuredCloneData instance may 223 // outlive aData. 224 bool CopyExternalData(const JSStructuredCloneData& aData); 225 226 // Initialize this instance by stealing the contents of aData via Move 227 // constructor, clearing the original aData as a side-effect. This is only 228 // safe if aData owns the underlying buffers. This is the case for instances 229 // provided by IPC to Recv calls. 230 bool StealExternalData(JSStructuredCloneData& aData); 231 232 JSStructuredCloneData& Data() { 233 return mSharedData ? mSharedData->Data() : mExternalData; 234 } 235 236 const JSStructuredCloneData& Data() const { 237 return mSharedData ? mSharedData->Data() : mExternalData; 238 } 239 240 void InitScope(JS::StructuredCloneScope aScope) { Data().initScope(aScope); } 241 242 size_t DataLength() const { 243 return mSharedData ? mSharedData->DataLength() : mExternalData.Size(); 244 } 245 246 SharedJSAllocatedData* SharedData() const { return mSharedData; } 247 248 bool SupportsTransferring() { return mSupportsTransferring; } 249 250 // For IPC serialization 251 void WriteIPCParams(IPC::MessageWriter* aWriter) const; 252 bool ReadIPCParams(IPC::MessageReader* aReader); 253 254 protected: 255 already_AddRefed<SharedJSAllocatedData> TakeSharedData(); 256 257 private: 258 JSStructuredCloneData mExternalData; 259 RefPtr<SharedJSAllocatedData> mSharedData; 260 261 bool mInitialized; 262 }; 263 264 } // namespace ipc 265 } // namespace dom 266 } // namespace mozilla 267 268 #endif // mozilla_dom_ipc_StructuredCloneData_h