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
20 NS_IMETHODIMP
SetData(JS::Handle<JS::Value> aBuffer,uint64_t aByteOffset,uint64_t aLength)21 ArrayBufferInputStream::SetData(JS::Handle<JS::Value> aBuffer,
22 uint64_t aByteOffset, uint64_t aLength) {
23 NS_ASSERT_OWNINGTHREAD(ArrayBufferInputStream);
24
25 if (!aBuffer.isObject()) {
26 return NS_ERROR_FAILURE;
27 }
28 JS::Rooted<JSObject*> arrayBuffer(RootingCx(), &aBuffer.toObject());
29 if (!JS::IsArrayBufferObject(arrayBuffer)) {
30 return NS_ERROR_FAILURE;
31 }
32
33 uint64_t buflen = JS::GetArrayBufferByteLength(arrayBuffer);
34 uint64_t offset = std::min(buflen, aByteOffset);
35 uint64_t bufferLength = std::min(buflen - offset, aLength);
36
37 // Prevent truncation.
38 if (bufferLength > UINT32_MAX) {
39 return NS_ERROR_INVALID_ARG;
40 }
41
42 mArrayBuffer = mozilla::MakeUniqueFallible<char[]>(bufferLength);
43 if (!mArrayBuffer) {
44 return NS_ERROR_OUT_OF_MEMORY;
45 }
46
47 mBufferLength = bufferLength;
48
49 JS::AutoCheckCannotGC nogc;
50 bool isShared;
51 char* src =
52 (char*)JS::GetArrayBufferData(arrayBuffer, &isShared, nogc) + offset;
53 memcpy(&mArrayBuffer[0], src, mBufferLength);
54 return NS_OK;
55 }
56
57 NS_IMETHODIMP
Close()58 ArrayBufferInputStream::Close() {
59 mClosed = true;
60 return NS_OK;
61 }
62
63 NS_IMETHODIMP
Available(uint64_t * aCount)64 ArrayBufferInputStream::Available(uint64_t* aCount) {
65 if (mClosed) {
66 return NS_BASE_STREAM_CLOSED;
67 }
68 if (mArrayBuffer) {
69 *aCount = mBufferLength ? mBufferLength - mPos : 0;
70 } else {
71 *aCount = 0;
72 }
73 return NS_OK;
74 }
75
76 NS_IMETHODIMP
Read(char * aBuf,uint32_t aCount,uint32_t * aReadCount)77 ArrayBufferInputStream::Read(char* aBuf, uint32_t aCount,
78 uint32_t* aReadCount) {
79 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount);
80 }
81
82 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun writer,void * closure,uint32_t aCount,uint32_t * result)83 ArrayBufferInputStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
84 uint32_t aCount, uint32_t* result) {
85 NS_ASSERTION(result, "null ptr");
86 NS_ASSERTION(mBufferLength >= mPos, "bad stream state");
87
88 if (mClosed) {
89 return NS_BASE_STREAM_CLOSED;
90 }
91
92 MOZ_ASSERT(mArrayBuffer || (mPos == mBufferLength),
93 "stream inited incorrectly");
94
95 *result = 0;
96 while (mPos < mBufferLength) {
97 uint32_t remaining = mBufferLength - mPos;
98 MOZ_ASSERT(mArrayBuffer);
99
100 uint32_t count = std::min(aCount, remaining);
101 if (count == 0) {
102 break;
103 }
104
105 uint32_t written;
106 nsresult rv = writer(this, closure, &mArrayBuffer[0] + mPos, *result, count,
107 &written);
108 if (NS_FAILED(rv)) {
109 // InputStreams do not propagate errors to caller.
110 return NS_OK;
111 }
112
113 NS_ASSERTION(written <= count,
114 "writer should not write more than we asked it to write");
115 mPos += written;
116 *result += written;
117 aCount -= written;
118 }
119
120 return NS_OK;
121 }
122
123 NS_IMETHODIMP
IsNonBlocking(bool * aNonBlocking)124 ArrayBufferInputStream::IsNonBlocking(bool* aNonBlocking) {
125 *aNonBlocking = true;
126 return NS_OK;
127 }
128