Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/ipc/StructuredCloneData.h
Line
Count
Source (jump to first uncovered line)
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
#include "mozilla/RefPtr.h"
12
#include "mozilla/dom/StructuredCloneHolder.h"
13
#include "nsISupportsImpl.h"
14
#include "nsIInputStream.h"
15
16
namespace IPC {
17
class Message;
18
}
19
class PickleIterator;
20
21
namespace mozilla {
22
namespace ipc {
23
24
class AutoIPCStream;
25
class PBackgroundChild;
26
class PBackgroundParent;
27
28
} // namespace ipc
29
30
namespace dom {
31
32
class nsIContentChild;
33
class nsIContentParent;
34
35
namespace ipc {
36
37
/**
38
 * Wraps the non-reference-counted JSStructuredCloneData class to have a
39
 * reference count so that multiple StructuredCloneData instances can reference
40
 * a single underlying serialized representation.
41
 *
42
 * As used by StructuredCloneData, it is an invariant that our
43
 * JSStructuredCloneData owns its buffers.  (For the non-owning case,
44
 * StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed
45
 * read-only view of the data.)
46
 */
47
class SharedJSAllocatedData final
48
{
49
public:
50
  explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
51
    : mData(std::move(aData))
52
0
  { }
53
54
  static already_AddRefed<SharedJSAllocatedData>
55
  CreateFromExternalData(const char* aData, size_t aDataLength)
56
0
  {
57
0
    JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess);
58
0
    buf.AppendBytes(aData, aDataLength);
59
0
    RefPtr<SharedJSAllocatedData> sharedData =
60
0
      new SharedJSAllocatedData(std::move(buf));
61
0
    return sharedData.forget();
62
0
  }
63
64
  static already_AddRefed<SharedJSAllocatedData>
65
  CreateFromExternalData(const JSStructuredCloneData& aData)
66
0
  {
67
0
    JSStructuredCloneData buf(aData.scope());
68
0
    buf.Append(aData);
69
0
    RefPtr<SharedJSAllocatedData> sharedData =
70
0
      new SharedJSAllocatedData(std::move(buf));
71
0
    return sharedData.forget();
72
0
  }
73
74
  NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
75
76
  JSStructuredCloneData& Data() { return mData; }
77
  size_t DataLength() const { return mData.Size(); }
78
79
private:
80
0
  ~SharedJSAllocatedData() { }
81
82
  JSStructuredCloneData mData;
83
};
84
85
/**
86
 * IPC-aware StructuredCloneHolder subclass that serves as both a helper class
87
 * for dealing with message data (blobs, transferables) and also an IPDL
88
 * data-type in cases where message data is not needed.  If your use-case does
89
 * not (potentially) involve IPC, then you should use StructuredCloneHolder or
90
 * one of its other subclasses instead.
91
 *
92
 * ## Usage ##
93
 *
94
 * The general recipe for using this class is:
95
 * - In your IPDL definition, use the ClonedMessageData type whenever you want
96
 *   to send a structured clone that may include blobs or transferables such as
97
 *   message ports.
98
 * - To send the data, instantiate a StructuredCloneData instance and Write()
99
 *   into it like a normal structure clone.  When you are ready to send the
100
 *   ClonedMessageData-bearing IPC message, use the appropriate
101
 *   BuildClonedMessageDataFor{Parent,Child,BackgroundParent,BackgroundChild}
102
 *   method to populate the ClonedMessageData and then send it before your
103
 *   StructuredCloneData instance is destroyed.  (Buffer borrowing is used
104
 *   under-the-hood to avoid duplicating the serialized data, requiring this.)
105
 * - To receive the data, instantiate a StructuredCloneData and use the
106
 *   appropriate {Borrow,Copy,Steal}FromClonedMessageDataFor{Parent,Child,
107
 *   BackgroundParent,BackgroundChild} method.  See the memory management
108
 *   section for more info.
109
 *
110
 * Variations:
111
 * - If transferables are not allowed (ex: BroadcastChannel), then use the
112
 *   StructuredCloneDataNoTransfers subclass instead of StructuredCloneData.
113
 *
114
 * ## Memory Management ##
115
 *
116
 * Serialized structured clone representations can be quite large.  So it's best
117
 * to avoid wasteful duplication.  When Write()ing into the StructuredCloneData,
118
 * you don't need to worry about this[1], but when you already have serialized
119
 * structured clone data you plan to Read(), you do need to.  Similarly, if
120
 * you're using StructuredCloneData as an IPDL type, it efficiently unmarshals.
121
 *
122
 * The from-ClonedMessageData memory management strategies available are:
123
 * - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only
124
 *   BufferList::Borrow()ed copy of the source.  Your StructuredCloneData needs
125
 *   to be destroyed before the source is.  Commonly used when the
126
 *   StructuredCloneData instance is stack-allocated (and Read() is used before
127
 *   the function returns).
128
 * - Copy: Makes a reference-counted copy of the source JSStructuredCloneData,
129
 *   making it safe for the StructuredCloneData to outlive the source data.
130
 * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that
131
 *   it's safe for the StructuredCloneData to outlive the source data.  This is
132
 *   safe to use with IPC-provided ClonedMessageData instances because
133
 *   JSStructuredCloneData's IPC ParamTraits::Read method uses ExtractBuffers,
134
 *   returning a fatal false if unable to extract.  (And
135
 *   SerializedStructuredCloneBuffer wraps/defers to it.)  But if it's possible
136
 *   the ClonedMessageData came from a different source that might have borrowed
137
 *   the buffers itself, then things will crash.  That would be a pretty crazy
138
 *   implementation; if you see one, change it to use SharedJSAllocatedData.
139
 *
140
 * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is
141
 *    created efficiently (by stealing from StructuredCloneHolder).  The
142
 *    BuildClonedMessageDataFor* method can be called at any time and it will
143
 *    borrow the underlying memory.  While it would be even better if
144
 *    SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref,
145
 *    there's no reason you can't wait to BuildClonedMessageDataFor* until you
146
 *    need to make the IPC Send* call.
147
 */
148
class StructuredCloneData : public StructuredCloneHolder
149
{
150
public:
151
  StructuredCloneData();
152
153
  StructuredCloneData(const StructuredCloneData&) = delete;
154
155
  StructuredCloneData(StructuredCloneData&& aOther);
156
157
  ~StructuredCloneData();
158
159
  StructuredCloneData&
160
  operator=(const StructuredCloneData& aOther) = delete;
161
162
  StructuredCloneData&
163
  operator=(StructuredCloneData&& aOther);
164
165
  const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
166
  {
167
    return mBlobImplArray;
168
  }
169
170
  nsTArray<RefPtr<BlobImpl>>& BlobImpls()
171
  {
172
    return mBlobImplArray;
173
  }
174
175
  const nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() const
176
0
  {
177
0
    return mInputStreamArray;
178
0
  }
179
180
  nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams()
181
  {
182
    return mInputStreamArray;
183
  }
184
185
  bool Copy(const StructuredCloneData& aData);
186
187
  void Read(JSContext* aCx,
188
            JS::MutableHandle<JS::Value> aValue,
189
            ErrorResult &aRv);
190
191
  void Write(JSContext* aCx,
192
             JS::Handle<JS::Value> aValue,
193
             ErrorResult &aRv);
194
195
  void Write(JSContext* aCx,
196
             JS::Handle<JS::Value> aValue,
197
             JS::Handle<JS::Value> aTransfers,
198
             ErrorResult &aRv);
199
200
  // Actor-varying methods to convert the structured clone stored in this holder
201
  // by a previous call to Write() into ClonedMessageData IPC representation.
202
  // (Blobs are represented in IPC by IPCBlob actors, so we need the parent to
203
  // be able to create them.)
204
  bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
205
                                       ClonedMessageData& aClonedData);
206
  bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
207
                                      ClonedMessageData& aClonedData);
208
  bool BuildClonedMessageDataForBackgroundParent(mozilla::ipc::PBackgroundParent* aParent,
209
                                                 ClonedMessageData& aClonedData);
210
  bool BuildClonedMessageDataForBackgroundChild(mozilla::ipc::PBackgroundChild* aChild,
211
                                                ClonedMessageData& aClonedData);
212
213
  // Actor-varying and memory-management-strategy-varying methods to initialize
214
  // this holder from a ClonedMessageData representation.
215
  void BorrowFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
216
  void BorrowFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
217
  void BorrowFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
218
  void BorrowFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
219
220
  void CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
221
  void CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
222
  void CopyFromClonedMessageDataForBackgroundParent(const ClonedMessageData& aClonedData);
223
  void CopyFromClonedMessageDataForBackgroundChild(const ClonedMessageData& aClonedData);
224
225
  // The steal variants of course take a non-const ClonedMessageData.
226
  void StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData);
227
  void StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData);
228
  void StealFromClonedMessageDataForBackgroundParent(ClonedMessageData& aClonedData);
229
  void StealFromClonedMessageDataForBackgroundChild(ClonedMessageData& aClonedData);
230
231
232
  // Initialize this instance, borrowing the contents of the given
233
  // JSStructuredCloneData.  You are responsible for ensuring that this
234
  // StructuredCloneData instance is destroyed before aData is destroyed.
235
  bool UseExternalData(const JSStructuredCloneData& aData)
236
0
  {
237
0
    auto iter = aData.Start();
238
0
    bool success = false;
239
0
    mExternalData = aData.Borrow(iter, aData.Size(), &success);
240
0
    mInitialized = true;
241
0
    return success;
242
0
  }
243
244
  // Initialize this instance by copying the given data that probably came from
245
  // nsStructuredClone doing a base64 decode.  Don't use this.
246
  bool CopyExternalData(const char* aData, size_t aDataLength);
247
  // Initialize this instance by copying the contents of an existing
248
  // JSStructuredCloneData.  Use when this StructuredCloneData instance may
249
  // outlive aData.
250
  bool CopyExternalData(const JSStructuredCloneData& aData);
251
252
  // Initialize this instance by stealing the contents of aData via Move
253
  // constructor, clearing the original aData as a side-effect.  This is only
254
  // safe if aData owns the underlying buffers.  This is the case for instances
255
  // provided by IPC to Recv calls.
256
  bool StealExternalData(JSStructuredCloneData& aData);
257
258
  JSStructuredCloneData& Data()
259
  {
260
    return mSharedData ? mSharedData->Data() : mExternalData;
261
  }
262
263
  const JSStructuredCloneData& Data() const
264
  {
265
    return mSharedData ? mSharedData->Data() : mExternalData;
266
  }
267
268
  void InitScope(JS::StructuredCloneScope aScope)
269
  {
270
    Data().initScope(aScope);
271
  }
272
273
  size_t DataLength() const
274
  {
275
    return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
276
  }
277
278
  SharedJSAllocatedData* SharedData() const
279
0
  {
280
0
    return mSharedData;
281
0
  }
282
283
  bool SupportsTransferring()
284
0
  {
285
0
    return mSupportsTransferring;
286
0
  }
287
288
  FallibleTArray<mozilla::ipc::AutoIPCStream>& IPCStreams()
289
0
  {
290
0
    return mIPCStreams;
291
0
  }
292
293
  // For IPC serialization
294
  void WriteIPCParams(IPC::Message* aMessage) const;
295
  bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
296
297
protected:
298
  explicit StructuredCloneData(TransferringSupport aSupportsTransferring);
299
300
  already_AddRefed<SharedJSAllocatedData>
301
  TakeSharedData();
302
303
private:
304
  JSStructuredCloneData mExternalData;
305
  RefPtr<SharedJSAllocatedData> mSharedData;
306
307
  // This array is needed because AutoIPCStream DTOR must be executed after the
308
  // sending of the data via IPC. This will be fixed by bug 1353475.
309
  FallibleTArray<mozilla::ipc::AutoIPCStream> mIPCStreams;
310
  bool mInitialized;
311
};
312
313
/**
314
 * For use when transferring should not be supported.
315
 */
316
class StructuredCloneDataNoTransfers : public StructuredCloneData
317
{
318
public:
319
  StructuredCloneDataNoTransfers()
320
    : StructuredCloneData(StructuredCloneHolder::TransferringNotSupported)
321
  {}
322
};
323
324
} // namespace ipc
325
} // namespace dom
326
} // namespace mozilla
327
328
#endif // mozilla_dom_ipc_StructuredCloneData_h