WritableStream.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/PromiseCapability.h>
  7. #include <LibWeb/Bindings/Intrinsics.h>
  8. #include <LibWeb/Bindings/WritableStreamPrototype.h>
  9. #include <LibWeb/Streams/AbstractOperations.h>
  10. #include <LibWeb/Streams/UnderlyingSink.h>
  11. #include <LibWeb/Streams/WritableStream.h>
  12. #include <LibWeb/Streams/WritableStreamDefaultController.h>
  13. #include <LibWeb/Streams/WritableStreamDefaultWriter.h>
  14. #include <LibWeb/WebIDL/ExceptionOr.h>
  15. namespace Web::Streams {
  16. // https://streams.spec.whatwg.org/#ws-constructor
  17. WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStream>> WritableStream::construct_impl(JS::Realm& realm, Optional<JS::Handle<JS::Object>> const& underlying_sink_object)
  18. {
  19. auto& vm = realm.vm();
  20. auto writable_stream = MUST_OR_THROW_OOM(realm.heap().allocate<WritableStream>(realm, realm));
  21. // 1. If underlyingSink is missing, set it to null.
  22. auto underlying_sink = underlying_sink_object.has_value() ? JS::Value(underlying_sink_object.value().ptr()) : JS::js_null();
  23. // 2. Let underlyingSinkDict be underlyingSink, converted to an IDL value of type UnderlyingSink.
  24. auto underlying_sink_dict = TRY(UnderlyingSink::from_value(vm, underlying_sink));
  25. // 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
  26. if (!underlying_sink_dict.type.has_value())
  27. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Invalid use of reserved key 'type'"sv };
  28. // 4. Perform ! InitializeWritableStream(this).
  29. // Note: This AO configures slot values which are already specified in the class's field initializers.
  30. // FIXME: 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
  31. SizeAlgorithm size_algorithm = [](auto const&) { return JS::normal_completion(JS::Value(1)); };
  32. // FIXME: 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
  33. auto high_water_mark = 1.0;
  34. // 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
  35. TRY(set_up_writable_stream_default_controller_from_underlying_sink(*writable_stream, underlying_sink, underlying_sink_dict, high_water_mark, move(size_algorithm)));
  36. return writable_stream;
  37. }
  38. // https://streams.spec.whatwg.org/#ws-locked
  39. bool WritableStream::locked() const
  40. {
  41. // 1. Return ! IsWritableStreamLocked(this).
  42. return is_writable_stream_locked(*this);
  43. }
  44. // https://streams.spec.whatwg.org/#ws-close
  45. WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> WritableStream::close()
  46. {
  47. auto& realm = this->realm();
  48. // 1. If ! IsWritableStreamLocked(this) is true, return a promise rejected with a TypeError exception.
  49. if (is_writable_stream_locked(*this)) {
  50. auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot close a locked stream"sv));
  51. return WebIDL::create_rejected_promise(realm, exception)->promise();
  52. }
  53. // 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a promise rejected with a TypeError exception.
  54. if (writable_stream_close_queued_or_in_flight(*this)) {
  55. auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot close a stream that is already closed or errored"sv));
  56. return WebIDL::create_rejected_promise(realm, exception)->promise();
  57. }
  58. // 3. Return ! WritableStreamClose(this).
  59. return TRY(writable_stream_close(*this))->promise();
  60. }
  61. // https://streams.spec.whatwg.org/#ws-abort
  62. WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> WritableStream::abort(JS::Value reason)
  63. {
  64. auto& realm = this->realm();
  65. // 1. If ! IsWritableStreamLocked(this) is true, return a promise rejected with a TypeError exception.
  66. if (is_writable_stream_locked(*this)) {
  67. auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot abort a locked stream"sv));
  68. return WebIDL::create_rejected_promise(realm, exception)->promise();
  69. }
  70. // 2. Return ! WritableStreamAbort(this, reason).
  71. return TRY(writable_stream_abort(*this, reason))->promise();
  72. }
  73. // https://streams.spec.whatwg.org/#ws-get-writer
  74. WebIDL::ExceptionOr<JS::NonnullGCPtr<WritableStreamDefaultWriter>> WritableStream::get_writer()
  75. {
  76. // 1. Return ? AcquireWritableStreamDefaultWriter(this).
  77. return acquire_writable_stream_default_writer(*this);
  78. }
  79. WritableStream::WritableStream(JS::Realm& realm)
  80. : Bindings::PlatformObject(realm)
  81. {
  82. }
  83. JS::ThrowCompletionOr<void> WritableStream::initialize(JS::Realm& realm)
  84. {
  85. MUST_OR_THROW_OOM(Base::initialize(realm));
  86. set_prototype(&Bindings::ensure_web_prototype<Bindings::WritableStreamPrototype>(realm, "WritableStream"));
  87. return {};
  88. }
  89. void WritableStream::visit_edges(Cell::Visitor& visitor)
  90. {
  91. Base::visit_edges(visitor);
  92. visitor.visit(m_close_request);
  93. visitor.visit(m_controller);
  94. visitor.visit(m_in_flight_write_request);
  95. visitor.visit(m_in_flight_close_request);
  96. if (m_pending_abort_request.has_value()) {
  97. visitor.visit(m_pending_abort_request->promise);
  98. visitor.visit(m_pending_abort_request->reason);
  99. }
  100. visitor.visit(m_stored_error);
  101. visitor.visit(m_writer);
  102. for (auto& write_request : m_write_requests)
  103. visitor.visit(write_request);
  104. }
  105. }