BaseAlgorithms.cpp (7618B)
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 #include "BaseAlgorithms.h" 8 9 #include "mozilla/dom/BufferSourceBinding.h" 10 #include "mozilla/dom/BufferSourceBindingFwd.h" 11 #include "mozilla/dom/TransformStreamDefaultController.h" 12 #include "mozilla/dom/UnionTypes.h" 13 14 namespace mozilla::dom::compression { 15 16 // Step 3 of 17 // https://compression.spec.whatwg.org/#dom-compressionstream-compressionstream 18 // Let transformAlgorithm be an algorithm which takes a chunk argument and 19 // runs the compress and enqueue a chunk algorithm with this and chunk. 20 MOZ_CAN_RUN_SCRIPT 21 void CompressionStreamAlgorithms::TransformCallbackImpl( 22 JS::Handle<JS::Value> aChunk, TransformStreamDefaultController& aController, 23 ErrorResult& aRv) { 24 AutoJSAPI jsapi; 25 if (!jsapi.Init(aController.GetParentObject())) { 26 aRv.ThrowUnknownError("Internal error"); 27 return; 28 } 29 JSContext* cx = jsapi.cx(); 30 31 // https://compression.spec.whatwg.org/#compress-and-enqueue-a-chunk 32 33 // Step 1: If chunk is not a BufferSource type, then throw a TypeError. 34 RootedUnion<OwningBufferSource> bufferSource(cx); 35 if (!bufferSource.Init(cx, aChunk)) { 36 aRv.MightThrowJSException(); 37 aRv.StealExceptionFromJSContext(cx); 38 return; 39 } 40 41 // Step 2 - 5: (Done in CompressAndEnqueue) 42 ProcessTypedArraysFixed( 43 bufferSource, 44 [&](const Span<uint8_t>& aData) MOZ_CAN_RUN_SCRIPT_BOUNDARY { 45 CompressAndEnqueue(cx, aData, Flush::No, aController, aRv); 46 }); 47 } 48 49 // Step 4 of 50 // https://compression.spec.whatwg.org/#dom-compressionstream-compressionstream 51 // Let flushAlgorithm be an algorithm which takes no argument and runs the 52 // compress flush and enqueue algorithm with this. 53 MOZ_CAN_RUN_SCRIPT void CompressionStreamAlgorithms::FlushCallbackImpl( 54 TransformStreamDefaultController& aController, ErrorResult& aRv) { 55 AutoJSAPI jsapi; 56 if (!jsapi.Init(aController.GetParentObject())) { 57 aRv.ThrowUnknownError("Internal error"); 58 return; 59 } 60 JSContext* cx = jsapi.cx(); 61 62 // https://compression.spec.whatwg.org/#compress-flush-and-enqueue 63 64 // Step 1 - 4: (Done in CompressAndEnqueue) 65 CompressAndEnqueue(cx, Span<const uint8_t>(), Flush::Yes, aController, aRv); 66 } 67 68 // Shared by: 69 // https://compression.spec.whatwg.org/#compress-and-enqueue-a-chunk 70 // https://compression.spec.whatwg.org/#compress-flush-and-enqueue 71 MOZ_CAN_RUN_SCRIPT void CompressionStreamAlgorithms::CompressAndEnqueue( 72 JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, 73 TransformStreamDefaultController& aController, ErrorResult& aRv) { 74 MOZ_ASSERT_IF(aFlush == Flush::Yes, !aInput.Length()); 75 76 JS::RootedVector<JSObject*> array(aCx); 77 78 // Step 2: Let buffer be the result of compressing chunk with cs’s 79 // format and context. 80 // Step 3: If buffer is empty, return. (implicit as array will be empty then) 81 // Step 4: Let arrays be the result of buffer into one or more non-empty 82 // pieces and converting them into Uint8Arrays. 83 Compress(aCx, aInput, &array, aFlush, aRv); 84 if (aRv.Failed()) { 85 return; 86 } 87 88 // Step 5: For each Uint8Array array, enqueue array in cs's transform. 89 for (const auto& view : array) { 90 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*view)); 91 aController.Enqueue(aCx, value, aRv); 92 if (aRv.Failed()) { 93 return; 94 } 95 } 96 } 97 98 // Step 3 of 99 // https://compression.spec.whatwg.org/#dom-decompressionstream-decompressionstream 100 // Let transformAlgorithm be an algorithm which takes a chunk argument and 101 // runs the compress and enqueue a chunk algorithm with this and chunk. 102 MOZ_CAN_RUN_SCRIPT 103 void DecompressionStreamAlgorithms::TransformCallbackImpl( 104 JS::Handle<JS::Value> aChunk, TransformStreamDefaultController& aController, 105 ErrorResult& aRv) { 106 AutoJSAPI jsapi; 107 if (!jsapi.Init(aController.GetParentObject())) { 108 aRv.ThrowUnknownError("Internal error"); 109 return; 110 } 111 JSContext* cx = jsapi.cx(); 112 113 // https://compression.spec.whatwg.org/#decompress-and-enqueue-a-chunk 114 115 // Step 1: If chunk is not a BufferSource type, then throw a TypeError. 116 RootedUnion<OwningBufferSource> bufferSource(cx); 117 if (!bufferSource.Init(cx, aChunk)) { 118 aRv.MightThrowJSException(); 119 aRv.StealExceptionFromJSContext(cx); 120 return; 121 } 122 123 // Step 2: Let buffer be the result of decompressing chunk with ds's format 124 // and context. If this results in an error, then throw a TypeError. 125 // Step 3 - 5: (Done in DecompressAndEnqueue) 126 ProcessTypedArraysFixed( 127 bufferSource, 128 [&](const Span<uint8_t>& aData) MOZ_CAN_RUN_SCRIPT_BOUNDARY { 129 DecompressAndEnqueue(cx, aData, Flush::No, aController, aRv); 130 }); 131 } 132 133 // Step 4 of 134 // https://compression.spec.whatwg.org/#dom-decompressionstream-decompressionstream 135 // Let flushAlgorithm be an algorithm which takes no argument and runs the 136 // compress flush and enqueue algorithm with this. 137 MOZ_CAN_RUN_SCRIPT void DecompressionStreamAlgorithms::FlushCallbackImpl( 138 TransformStreamDefaultController& aController, ErrorResult& aRv) { 139 AutoJSAPI jsapi; 140 if (!jsapi.Init(aController.GetParentObject())) { 141 aRv.ThrowUnknownError("Internal error"); 142 return; 143 } 144 JSContext* cx = jsapi.cx(); 145 146 // https://compression.spec.whatwg.org/#decompress-flush-and-enqueue 147 148 // Step 1: Let buffer be the result of decompressing an empty input with 149 // ds's format and context, with the finish flag. 150 // Step 2 - 6: (Done in DecompressAndEnqueue) 151 DecompressAndEnqueue(cx, Span<const uint8_t>(), Flush::Yes, aController, aRv); 152 } 153 154 // Shared by: 155 // https://compression.spec.whatwg.org/#decompress-and-enqueue-a-chunk 156 // https://compression.spec.whatwg.org/#decompress-flush-and-enqueue 157 MOZ_CAN_RUN_SCRIPT void DecompressionStreamAlgorithms::DecompressAndEnqueue( 158 JSContext* aCx, Span<const uint8_t> aInput, Flush aFlush, 159 TransformStreamDefaultController& aController, ErrorResult& aRv) { 160 MOZ_ASSERT_IF(aFlush == Flush::Yes, !aInput.Length()); 161 162 JS::RootedVector<JSObject*> array(aCx); 163 164 // Step 2: Let buffer be the result of decompressing chunk with ds’s format 165 // and context. If this results in an error, then throw a TypeError. 166 // Step 3: If buffer is empty, return. 167 // (Skipping, see https://github.com/whatwg/compression/issues/78) 168 // Step 4: Let arrays be the result of splitting buffer into one or more 169 // non-empty pieces and converting them into Uint8Arrays. 170 bool fullyConsumed = Decompress(aCx, aInput, &array, aFlush, aRv); 171 if (aRv.Failed()) { 172 return; 173 } 174 175 // Step 5: For each Uint8Array array, enqueue array in ds's transform. 176 for (const auto& view : array) { 177 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*view)); 178 aController.Enqueue(aCx, value, aRv); 179 if (aRv.Failed()) { 180 return; 181 } 182 } 183 184 // Step 6: If the end of the compressed input has been reached, and ds's 185 // context has not fully consumed chunk, then throw a TypeError. 186 if (mObservedStreamEnd && !fullyConsumed) { 187 aRv.ThrowTypeError("Unexpected input after the end of stream"); 188 return; 189 } 190 191 // Step 3 of 192 // https://compression.spec.whatwg.org/#decompress-flush-and-enqueue 193 // If the end of the compressed input has not been reached, then throw a 194 // TypeError. 195 if (aFlush == Flush::Yes && !mObservedStreamEnd) { 196 aRv.ThrowTypeError("The input is ended without reaching the stream end"); 197 return; 198 } 199 } 200 201 } // namespace mozilla::dom::compression