tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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