summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc')
-rw-r--r--chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc202
1 files changed, 202 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc b/chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
new file mode 100644
index 00000000000..2c0fe8dcd47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/modules/encoding/text_decoder_stream.cc
@@ -0,0 +1,202 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/encoding/text_decoder_stream.h"
+
+#include <memory>
+#include <utility>
+
+#include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/streams/retain_wrapper_during_construction.h"
+#include "third_party/blink/renderer/core/streams/transform_stream_default_controller.h"
+#include "third_party/blink/renderer/core/streams/transform_stream_transformer.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
+#include "third_party/blink/renderer/modules/encoding/encoding.h"
+#include "third_party/blink/renderer/modules/encoding/text_decoder_options.h"
+#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/to_v8.h"
+#include "third_party/blink/renderer/platform/wtf/string_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
+
+namespace blink {
+
+class TextDecoderStream::Transformer final : public TransformStreamTransformer {
+ public:
+ explicit Transformer(ScriptState* script_state,
+ WTF::TextEncoding encoding,
+ bool fatal,
+ bool ignore_bom)
+ : decoder_(NewTextCodec(encoding)),
+ script_state_(script_state),
+ fatal_(fatal),
+ ignore_bom_(ignore_bom),
+ encoding_has_bom_removal_(EncodingHasBomRemoval(encoding)) {}
+
+ // Implements the type conversion part of the "decode and enqueue a chunk"
+ // algorithm.
+ void Transform(v8::Local<v8::Value> chunk,
+ TransformStreamDefaultController* controller,
+ ExceptionState& exception_state) override {
+ ArrayBufferOrArrayBufferView bufferSource;
+ V8ArrayBufferOrArrayBufferView::ToImpl(
+ script_state_->GetIsolate(), chunk, bufferSource,
+ UnionTypeConversionMode::kNotNullable, exception_state);
+ if (exception_state.HadException())
+ return;
+
+ // This implements the "get a copy of the bytes held by the buffer source"
+ // algorithm (https://heycam.github.io/webidl/#dfn-get-buffer-source-copy).
+ if (bufferSource.IsArrayBufferView()) {
+ const auto* view = bufferSource.GetAsArrayBufferView().View();
+ // If IsDetachedBuffer(O), then throw a TypeError.
+ if (view->buffer()->IsNeutered()) {
+ exception_state.ThrowTypeError(
+ ExceptionMessages::FailedToConvertJSValue("BufferSource"));
+ return;
+ }
+ const char* start = static_cast<const char*>(view->BaseAddress());
+ size_t length = view->byteLength();
+ DecodeAndEnqueue(start, length, WTF::FlushBehavior::kDoNotFlush,
+ controller, exception_state);
+ return;
+ }
+ DCHECK(bufferSource.IsArrayBuffer());
+ const auto* array_buffer = bufferSource.GetAsArrayBuffer();
+ // If IsDetachedBuffer(O), then throw a TypeError.
+ if (array_buffer->IsNeutered()) {
+ exception_state.ThrowTypeError(
+ ExceptionMessages::FailedToConvertJSValue("BufferSource"));
+ return;
+ }
+ const char* start = static_cast<const char*>(array_buffer->Data());
+ size_t length = array_buffer->ByteLength();
+ DecodeAndEnqueue(start, length, WTF::FlushBehavior::kDoNotFlush, controller,
+ exception_state);
+ }
+
+ // Implements the "encode and flush" algorithm.
+ void Flush(TransformStreamDefaultController* controller,
+ ExceptionState& exception_state) override {
+ DecodeAndEnqueue(nullptr, 0u, WTF::FlushBehavior::kDataEOF, controller,
+ exception_state);
+ }
+
+ void Trace(Visitor* visitor) override {
+ visitor->Trace(script_state_);
+ TransformStreamTransformer::Trace(visitor);
+ }
+
+ private:
+ // Implements the second part of "decode and enqueue a chunk" as well as the
+ // "flush and enqueue" algorithm.
+ void DecodeAndEnqueue(const char* start,
+ size_t length,
+ WTF::FlushBehavior flush,
+ TransformStreamDefaultController* controller,
+ ExceptionState& exception_state) {
+ const UChar kBOM = 0xFEFF;
+
+ bool saw_error = false;
+ String outputChunk =
+ decoder_->Decode(start, length, flush, fatal_, saw_error);
+
+ if (fatal_ && saw_error) {
+ exception_state.ThrowTypeError("The encoded data was not valid.");
+ return;
+ }
+
+ if (outputChunk.IsEmpty())
+ return;
+
+ if (!ignore_bom_ && !bom_seen_) {
+ bom_seen_ = true;
+ if (encoding_has_bom_removal_ && outputChunk[0] == kBOM) {
+ outputChunk.Remove(0);
+ if (outputChunk.IsEmpty())
+ return;
+ }
+ }
+
+ controller->Enqueue(ToV8(outputChunk, script_state_), exception_state);
+ }
+
+ static bool EncodingHasBomRemoval(const WTF::TextEncoding& encoding) {
+ String name(encoding.GetName());
+ return name == "UTF-8" || name == "UTF-16LE" || name == "UTF-16BE";
+ }
+
+ std::unique_ptr<WTF::TextCodec> decoder_;
+ // There is no danger of ScriptState leaking across worlds because a
+ // TextDecoderStream can only be accessed from the world that created it.
+ Member<ScriptState> script_state_;
+ const bool fatal_;
+ const bool ignore_bom_;
+ const bool encoding_has_bom_removal_;
+ bool bom_seen_;
+
+ DISALLOW_COPY_AND_ASSIGN(Transformer);
+};
+
+TextDecoderStream* TextDecoderStream::Create(ScriptState* script_state,
+ const String& label,
+ const TextDecoderOptions& options,
+ ExceptionState& exception_state) {
+ WTF::TextEncoding encoding(
+ label.StripWhiteSpace(&Encoding::IsASCIIWhiteSpace));
+ // The replacement encoding is not valid, but the Encoding API also
+ // rejects aliases of the replacement encoding.
+ if (!encoding.IsValid() ||
+ strcasecmp(encoding.GetName(), "replacement") == 0) {
+ exception_state.ThrowRangeError("The encoding label provided ('" + label +
+ "') is invalid.");
+ return nullptr;
+ }
+
+ return new TextDecoderStream(script_state, encoding, options,
+ exception_state);
+}
+
+TextDecoderStream::~TextDecoderStream() = default;
+
+String TextDecoderStream::encoding() const {
+ return String(encoding_.GetName()).LowerASCII();
+}
+
+ScriptValue TextDecoderStream::readable(ScriptState* script_state,
+ ExceptionState& exception_state) const {
+ return transform_->Readable(script_state, exception_state);
+}
+
+ScriptValue TextDecoderStream::writable(ScriptState* script_state,
+ ExceptionState& exception_state) const {
+ return transform_->Writable(script_state, exception_state);
+}
+
+void TextDecoderStream::Trace(Visitor* visitor) {
+ visitor->Trace(transform_);
+ ScriptWrappable::Trace(visitor);
+}
+
+TextDecoderStream::TextDecoderStream(ScriptState* script_state,
+ const WTF::TextEncoding& encoding,
+ const TextDecoderOptions& options,
+ ExceptionState& exception_state)
+ : transform_(new TransformStream()),
+ encoding_(encoding),
+ fatal_(options.fatal()),
+ ignore_bom_(options.ignoreBOM()) {
+ if (!RetainWrapperDuringConstruction(this, script_state)) {
+ exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+ "Cannot queue task to retain wrapper");
+ return;
+ }
+ transform_->Init(new Transformer(script_state, encoding, fatal_, ignore_bom_),
+ script_state, exception_state);
+}
+
+} // namespace blink