1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include <algorithm>
7 #include "ArrayBufferInputStream.h"
8 #include "nsStreamUtils.h"
9 #include "js/ArrayBuffer.h" // JS::{GetArrayBuffer{ByteLength,Data},IsArrayBufferObject}
10 #include "js/RootingAPI.h" // JS::{Handle,Rooted}
11 #include "js/Value.h" // JS::Value
12 #include "mozilla/UniquePtrExtensions.h"
13 #include "mozilla/dom/ScriptSettings.h"
14
15 using mozilla::dom::RootingCx;
16
17 NS_IMPL_ISUPPORTS(ArrayBufferInputStream, nsIArrayBufferInputStream,
18 nsIInputStream);
19
ArrayBufferInputStream()20 ArrayBufferInputStream::ArrayBufferInputStream()
21 : mBufferLength(0), mPos(0), mClosed(false) {}
22
23 NS_IMETHODIMP
SetData(JS::Handle<JS::Value> aBuffer,uint32_t aByteOffset,uint32_t aLength)24 ArrayBufferInputStream::SetData(JS::Handle<JS::Value> aBuffer,
25 uint32_t aByteOffset, uint32_t aLength) {
26 NS_ASSERT_OWNINGTHREAD(ArrayBufferInputStream);
27
28 if (!aBuffer.isObject()) {
29 return NS_ERROR_FAILURE;
30 }
31 JS::Rooted<JSObject*> arrayBuffer(RootingCx(), &aBuffer.toObject());
32 if (!JS::IsArrayBufferObject(arrayBuffer)) {
33 return NS_ERROR_FAILURE;
34 }
35
36 uint32_t buflen = JS::GetArrayBufferByteLength(arrayBuffer);
37 uint32_t offset = std::min(buflen, aByteOffset);
38 uint32_t bufferLength = std::min(buflen - offset, aLength);
39
40 mArrayBuffer = mozilla::MakeUniqueFallible<char[]>(bufferLength);
41 if (!mArrayBuffer) {
42 return NS_ERROR_OUT_OF_MEMORY;
43 }
44
45 mBufferLength = bufferLength;
46
47 JS::AutoCheckCannotGC nogc;
48 bool isShared;
49 char* src =
50 (char*)JS::GetArrayBufferData(arrayBuffer, &isShared, nogc) + offset;
51 memcpy(&mArrayBuffer[0], src, mBufferLength);
52 return NS_OK;
53 }
54
55 NS_IMETHODIMP
Close()56 ArrayBufferInputStream::Close() {
57 mClosed = true;
58 return NS_OK;
59 }
60
61 NS_IMETHODIMP
Available(uint64_t * aCount)62 ArrayBufferInputStream::Available(uint64_t* aCount) {
63 if (mClosed) {
64 return NS_BASE_STREAM_CLOSED;
65 }
66 if (mArrayBuffer) {
67 *aCount = mBufferLength ? mBufferLength - mPos : 0;
68 } else {
69 *aCount = 0;
70 }
71 return NS_OK;
72 }
73
74 NS_IMETHODIMP
Read(char * aBuf,uint32_t aCount,uint32_t * aReadCount)75 ArrayBufferInputStream::Read(char* aBuf, uint32_t aCount,
76 uint32_t* aReadCount) {
77 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
78 }
79
80 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun writer,void * closure,uint32_t aCount,uint32_t * result)81 ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
82 uint32_t aCount, uint32_t* result) {
83 NS_ASSERTION(result, "null ptr");
84 NS_ASSERTION(mBufferLength >= mPos, "bad stream state");
85
86 if (mClosed) {
87 return NS_BASE_STREAM_CLOSED;
88 }
89
90 MOZ_ASSERT(mArrayBuffer || (mPos == mBufferLength),
91 "stream inited incorrectly");
92
93 *result = 0;
94 while (mPos < mBufferLength) {
95 uint32_t remaining = mBufferLength - mPos;
96 MOZ_ASSERT(mArrayBuffer);
97
98 uint32_t count = std::min(aCount, remaining);
99 if (count == 0) {
100 break;
101 }
102
103 uint32_t written;
104 nsresult rv = writer(this, closure, &mArrayBuffer[0] + mPos, *result, count,
105 &written);
106 if (NS_FAILED(rv)) {
107 // InputStreams do not propagate errors to caller.
108 return NS_OK;
109 }
110
111 NS_ASSERTION(written <= count,
112 "writer should not write more than we asked it to write");
113 mPos += written;
114 *result += written;
115 aCount -= written;
116 }
117
118 return NS_OK;
119 }
120
121 NS_IMETHODIMP
IsNonBlocking(bool * aNonBlocking)122 ArrayBufferInputStream::IsNonBlocking(bool* aNonBlocking) {
123 *aNonBlocking = true;
124 return NS_OK;
125 }
126