tor-browser

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

UnderlyingSourceCallbackHelpers.h (12509B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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_UnderlyingSourceCallbackHelpers_h
      8 #define mozilla_dom_UnderlyingSourceCallbackHelpers_h
      9 
     10 #include "mozilla/DOMEventTargetHelper.h"
     11 #include "mozilla/HoldDropJSObjects.h"
     12 #include "mozilla/WeakPtr.h"
     13 #include "mozilla/dom/Promise.h"
     14 #include "mozilla/dom/UnderlyingSourceBinding.h"
     15 #include "nsIAsyncInputStream.h"
     16 #include "nsISupports.h"
     17 #include "nsISupportsImpl.h"
     18 
     19 /* Since the streams specification has native descriptions of some callbacks
     20 * (i.e. described in prose, rather than provided by user code), we need to be
     21 * able to pass around native callbacks. To handle this, we define polymorphic
     22 * classes That cover the difference between native callback and user-provided.
     23 *
     24 * The Streams specification wants us to invoke these callbacks, run through
     25 * WebIDL as if they were methods. So we have to preserve the underlying object
     26 * to use as the This value on invocation.
     27 */
     28 enum class nsresult : uint32_t;
     29 
     30 namespace mozilla::dom {
     31 
     32 class StrongWorkerRef;
     33 class BodyStreamHolder;
     34 class ReadableStreamControllerBase;
     35 class ReadableStream;
     36 
     37 class UnderlyingSourceAlgorithmsBase : public nsISupports {
     38 public:
     39  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     40  NS_DECL_CYCLE_COLLECTION_CLASS(UnderlyingSourceAlgorithmsBase)
     41 
     42  MOZ_CAN_RUN_SCRIPT virtual void StartCallback(
     43      JSContext* aCx, ReadableStreamControllerBase& aController,
     44      JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) = 0;
     45 
     46  // A promise-returning algorithm that pulls data from the underlying byte
     47  // source
     48  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> PullCallback(
     49      JSContext* aCx, ReadableStreamControllerBase& aController,
     50      ErrorResult& aRv) = 0;
     51 
     52  // A promise-returning algorithm, taking one argument (the cancel reason),
     53  // which communicates a requested cancelation to the underlying byte source
     54  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CancelCallback(
     55      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
     56      ErrorResult& aRv) = 0;
     57 
     58  // Implement this when you need to release underlying resources immediately
     59  // from closed(canceled)/errored streams, without waiting for GC.
     60  virtual void ReleaseObjects() {}
     61 
     62  // Can be used to read chunks directly via nsIInputStream to skip JS-related
     63  // overhead, if this readable stream is a wrapper of a native stream.
     64  // Currently used by Fetch helper functions e.g. new Response(stream).text()
     65  virtual nsIInputStream* MaybeGetInputStreamIfUnread() { return nullptr; }
     66 
     67  // https://streams.spec.whatwg.org/#other-specs-rs-create
     68  // By "native" we mean "instances initialized via the above set up or set up
     69  // with byte reading support algorithms (not, e.g., on web-developer-created
     70  // instances)"
     71  virtual bool IsNative() { return true; }
     72 
     73 protected:
     74  virtual ~UnderlyingSourceAlgorithmsBase() = default;
     75 };
     76 
     77 class UnderlyingSourceAlgorithms final : public UnderlyingSourceAlgorithmsBase {
     78 public:
     79  NS_DECL_ISUPPORTS_INHERITED
     80  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
     81      UnderlyingSourceAlgorithms, UnderlyingSourceAlgorithmsBase)
     82 
     83  UnderlyingSourceAlgorithms(nsIGlobalObject* aGlobal,
     84                             JS::Handle<JSObject*> aUnderlyingSource,
     85                             UnderlyingSource& aUnderlyingSourceDict)
     86      : mGlobal(aGlobal), mUnderlyingSource(aUnderlyingSource) {
     87    // Step 6. (implicit Step 2.)
     88    if (aUnderlyingSourceDict.mStart.WasPassed()) {
     89      mStartCallback = aUnderlyingSourceDict.mStart.Value();
     90    }
     91 
     92    // Step 7. (implicit Step 3.)
     93    if (aUnderlyingSourceDict.mPull.WasPassed()) {
     94      mPullCallback = aUnderlyingSourceDict.mPull.Value();
     95    }
     96 
     97    // Step 8. (implicit Step 4.)
     98    if (aUnderlyingSourceDict.mCancel.WasPassed()) {
     99      mCancelCallback = aUnderlyingSourceDict.mCancel.Value();
    100    }
    101 
    102    mozilla::HoldJSObjects(this);
    103  };
    104 
    105  MOZ_CAN_RUN_SCRIPT void StartCallback(
    106      JSContext* aCx, ReadableStreamControllerBase& aController,
    107      JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) override;
    108 
    109  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallback(
    110      JSContext* aCx, ReadableStreamControllerBase& aController,
    111      ErrorResult& aRv) override;
    112 
    113  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
    114      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    115      ErrorResult& aRv) override;
    116 
    117  bool IsNative() override { return false; }
    118 
    119 protected:
    120  ~UnderlyingSourceAlgorithms() override { mozilla::DropJSObjects(this); };
    121 
    122 private:
    123  // Virtually const, but are cycle collected
    124  nsCOMPtr<nsIGlobalObject> mGlobal;
    125  JS::Heap<JSObject*> mUnderlyingSource;
    126  MOZ_KNOWN_LIVE RefPtr<UnderlyingSourceStartCallback> mStartCallback;
    127  MOZ_KNOWN_LIVE RefPtr<UnderlyingSourcePullCallback> mPullCallback;
    128  MOZ_KNOWN_LIVE RefPtr<UnderlyingSourceCancelCallback> mCancelCallback;
    129 };
    130 
    131 // https://streams.spec.whatwg.org/#readablestream-set-up
    132 // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
    133 // Wrappers defined by the "Set up" methods in the spec. This helps you just
    134 // return nullptr when an error occurred as this wrapper converts it to a
    135 // rejected promise.
    136 // Note that StartCallback is only for JS consumers to access
    137 // the controller, and thus is no-op here since native consumers can call
    138 // `EnqueueNative()` etc. without direct controller access.
    139 class UnderlyingSourceAlgorithmsWrapper
    140    : public UnderlyingSourceAlgorithmsBase {
    141  void StartCallback(JSContext*, ReadableStreamControllerBase&,
    142                     JS::MutableHandle<JS::Value> aRetVal, ErrorResult&) final;
    143 
    144  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> PullCallback(
    145      JSContext* aCx, ReadableStreamControllerBase& aController,
    146      ErrorResult& aRv) final;
    147 
    148  MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback(
    149      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    150      ErrorResult& aRv) final;
    151 
    152  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> PullCallbackImpl(
    153      JSContext* aCx, ReadableStreamControllerBase& aController,
    154      ErrorResult& aRv) {
    155    // pullAlgorithm is optional, return null by default
    156    return nullptr;
    157  }
    158 
    159  MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CancelCallbackImpl(
    160      JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
    161      ErrorResult& aRv) {
    162    // cancelAlgorithm is optional, return null by default
    163    return nullptr;
    164  }
    165 };
    166 
    167 class InputToReadableStreamAlgorithms;
    168 
    169 // This class exists to isolate InputToReadableStreamAlgorithms from the
    170 // nsIAsyncInputStream.  If we call AsyncWait(this,...), it holds a
    171 // reference to 'this' which can't be cc'd, and we can leak the stream,
    172 // causing a Worker to assert with globalScopeAlive.  By isolating
    173 // ourselves from the inputstream, we can safely be CC'd if needed and
    174 // will inform the inputstream to shut down.
    175 class InputStreamHolder final : public nsIInputStreamCallback,
    176                                public GlobalTeardownObserver {
    177 public:
    178  NS_DECL_THREADSAFE_ISUPPORTS
    179  NS_DECL_NSIINPUTSTREAMCALLBACK
    180 
    181  InputStreamHolder(nsIGlobalObject* aGlobal,
    182                    InputToReadableStreamAlgorithms* aCallback,
    183                    nsIAsyncInputStream* aInput);
    184 
    185  void Init(JSContext* aCx);
    186 
    187  void DisconnectFromOwner() override;
    188 
    189  // Used by global teardown
    190  void Shutdown();
    191 
    192  // These just proxy the calls to the nsIAsyncInputStream
    193  nsresult AsyncWait(uint32_t aFlags, uint32_t aRequestedCount,
    194                     nsIEventTarget* aEventTarget);
    195  nsresult Available(uint64_t* aSize) { return mInput->Available(aSize); }
    196  nsresult Read(char* aBuffer, uint32_t aLength, uint32_t* aWritten) {
    197    return mInput->Read(aBuffer, aLength, aWritten);
    198  }
    199  nsresult CloseWithStatus(nsresult aStatus) {
    200    return mInput->CloseWithStatus(aStatus);
    201  }
    202 
    203  nsIAsyncInputStream* GetInputStream() { return mInput; }
    204 
    205 private:
    206  ~InputStreamHolder();
    207 
    208  // WeakPtr to avoid cycles
    209  WeakPtr<InputToReadableStreamAlgorithms> mCallback;
    210  // To ensure the worker sticks around
    211  RefPtr<StrongWorkerRef> mAsyncWaitWorkerRef;
    212  RefPtr<StrongWorkerRef> mWorkerRef;
    213  nsCOMPtr<nsIAsyncInputStream> mInput;
    214 
    215  // To ensure the underlying source sticks around during an ongoing read
    216  // operation. mAlgorithms is not cycle collected on purpose, and this holder
    217  // is responsible to keep the underlying source algorithms until
    218  // nsIAsyncInputStream responds.
    219  //
    220  // This is done because otherwise the whole stream objects may be cycle
    221  // collected, including the promises created from read(), as our JS engine may
    222  // throw unsettled promises away for optimization. See bug 1849860.
    223  RefPtr<InputToReadableStreamAlgorithms> mAsyncWaitAlgorithms;
    224 };
    225 
    226 // Using this class means you are also passing the lifetime control of your
    227 // nsIAsyncInputStream, as it will be closed when this class tears down.
    228 class InputToReadableStreamAlgorithms final
    229    : public UnderlyingSourceAlgorithmsWrapper,
    230      public nsIInputStreamCallback,
    231      public SupportsWeakPtr {
    232  NS_DECL_ISUPPORTS_INHERITED
    233  NS_DECL_NSIINPUTSTREAMCALLBACK
    234  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InputToReadableStreamAlgorithms,
    235                                           UnderlyingSourceAlgorithmsWrapper)
    236 
    237  InputToReadableStreamAlgorithms(JSContext* aCx, nsIAsyncInputStream* aInput,
    238                                  ReadableStream* aStream);
    239 
    240  // Streams algorithms
    241 
    242  already_AddRefed<Promise> PullCallbackImpl(
    243      JSContext* aCx, ReadableStreamControllerBase& aController,
    244      ErrorResult& aRv) override;
    245 
    246  void ReleaseObjects() override;
    247 
    248  nsIInputStream* MaybeGetInputStreamIfUnread() override;
    249 
    250 private:
    251  ~InputToReadableStreamAlgorithms() {
    252    if (mInput) {
    253      mInput->Shutdown();
    254    }
    255  }
    256 
    257  MOZ_CAN_RUN_SCRIPT_BOUNDARY void CloseAndReleaseObjects(
    258      JSContext* aCx, ReadableStream* aStream);
    259 
    260  void WriteIntoReadRequestBuffer(JSContext* aCx, ReadableStream* aStream,
    261                                  JS::Handle<JSObject*> aBuffer,
    262                                  uint32_t aLength, uint32_t* aByteWritten,
    263                                  ErrorResult& aRv);
    264 
    265  // https://streams.spec.whatwg.org/#readablestream-pull-from-bytes
    266  // (Uses InputStreamHolder for the "byte sequence" in the spec)
    267  MOZ_CAN_RUN_SCRIPT void PullFromInputStream(JSContext* aCx,
    268                                              uint64_t aAvailable,
    269                                              ErrorResult& aRv);
    270 
    271  void ErrorPropagation(JSContext* aCx, ReadableStream* aStream,
    272                        nsresult aError);
    273 
    274  // Common methods
    275 
    276  bool IsClosed() { return !mInput; }
    277 
    278  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
    279 
    280  // This promise is created by PullCallback and resolved when
    281  // OnInputStreamReady succeeds. No need to try hard to settle it though, see
    282  // also ReleaseObjects() for the reason.
    283  RefPtr<Promise> mPullPromise;
    284 
    285  RefPtr<InputStreamHolder> mInput;
    286 
    287  // mStream never changes after construction and before CC
    288  MOZ_KNOWN_LIVE RefPtr<ReadableStream> mStream;
    289 };
    290 
    291 class NonAsyncInputToReadableStreamAlgorithms
    292    : public UnderlyingSourceAlgorithmsWrapper {
    293 public:
    294  NS_DECL_ISUPPORTS_INHERITED
    295  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    296      NonAsyncInputToReadableStreamAlgorithms,
    297      UnderlyingSourceAlgorithmsWrapper)
    298 
    299  explicit NonAsyncInputToReadableStreamAlgorithms(nsIInputStream& aInput)
    300      : mInput(&aInput) {}
    301 
    302  already_AddRefed<Promise> PullCallbackImpl(
    303      JSContext* aCx, ReadableStreamControllerBase& aController,
    304      ErrorResult& aRv) override;
    305 
    306  void ReleaseObjects() override {
    307    if (RefPtr<InputToReadableStreamAlgorithms> algorithms =
    308            mAsyncAlgorithms.forget()) {
    309      algorithms->ReleaseObjects();
    310    }
    311    if (nsCOMPtr<nsIInputStream> input = mInput.forget()) {
    312      input->Close();
    313    }
    314  }
    315 
    316  nsIInputStream* MaybeGetInputStreamIfUnread() override {
    317    MOZ_ASSERT(mInput, "Should be only called on non-disturbed streams");
    318    return mInput;
    319  }
    320 
    321 private:
    322  ~NonAsyncInputToReadableStreamAlgorithms() = default;
    323 
    324  nsCOMPtr<nsIInputStream> mInput;
    325  RefPtr<InputToReadableStreamAlgorithms> mAsyncAlgorithms;
    326 };
    327 
    328 }  // namespace mozilla::dom
    329 
    330 #endif