Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 #include "nsDOMBlobBuilder.h"
7 #include "jsfriendapi.h"
8 #include "mozilla/dom/BlobBinding.h"
9 #include "mozilla/dom/FileBinding.h"
10 #include "nsAutoPtr.h"
11 #include "nsDOMClassInfoID.h"
12 #include "nsIMultiplexInputStream.h"
13 #include "nsStringStream.h"
14 #include "nsTArray.h"
15 #include "nsJSUtils.h"
16 #include "nsContentUtils.h"
17 #include "nsIScriptError.h"
18 #include "nsIXPConnect.h"
19 #include <algorithm>
21 using namespace mozilla;
22 using namespace mozilla::dom;
24 NS_IMPL_ISUPPORTS_INHERITED(nsDOMMultipartFile, nsDOMFile,
25 nsIJSNativeInitializer)
27 NS_IMETHODIMP
28 nsDOMMultipartFile::GetSize(uint64_t* aLength)
29 {
30 if (mLength == UINT64_MAX) {
31 CheckedUint64 length = 0;
33 uint32_t i;
34 uint32_t len = mBlobs.Length();
35 for (i = 0; i < len; i++) {
36 nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
37 uint64_t l = 0;
39 nsresult rv = blob->GetSize(&l);
40 NS_ENSURE_SUCCESS(rv, rv);
42 length += l;
43 }
45 NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE);
47 mLength = length.value();
48 }
50 *aLength = mLength;
51 return NS_OK;
52 }
54 NS_IMETHODIMP
55 nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream)
56 {
57 nsresult rv;
58 *aStream = nullptr;
60 nsCOMPtr<nsIMultiplexInputStream> stream =
61 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
62 NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
64 uint32_t i;
65 for (i = 0; i < mBlobs.Length(); i++) {
66 nsCOMPtr<nsIInputStream> scratchStream;
67 nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
69 rv = blob->GetInternalStream(getter_AddRefs(scratchStream));
70 NS_ENSURE_SUCCESS(rv, rv);
72 rv = stream->AppendStream(scratchStream);
73 NS_ENSURE_SUCCESS(rv, rv);
74 }
76 return CallQueryInterface(stream, aStream);
77 }
79 already_AddRefed<nsIDOMBlob>
80 nsDOMMultipartFile::CreateSlice(uint64_t aStart, uint64_t aLength,
81 const nsAString& aContentType)
82 {
83 // If we clamped to nothing we create an empty blob
84 nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
86 uint64_t length = aLength;
87 uint64_t skipStart = aStart;
89 // Prune the list of blobs if we can
90 uint32_t i;
91 for (i = 0; length && skipStart && i < mBlobs.Length(); i++) {
92 nsIDOMBlob* blob = mBlobs[i].get();
94 uint64_t l;
95 nsresult rv = blob->GetSize(&l);
96 NS_ENSURE_SUCCESS(rv, nullptr);
98 if (skipStart < l) {
99 uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
101 nsCOMPtr<nsIDOMBlob> firstBlob;
102 rv = blob->Slice(skipStart, skipStart + upperBound,
103 aContentType, 3,
104 getter_AddRefs(firstBlob));
105 NS_ENSURE_SUCCESS(rv, nullptr);
107 // Avoid wrapping a single blob inside an nsDOMMultipartFile
108 if (length == upperBound) {
109 return firstBlob.forget();
110 }
112 blobs.AppendElement(firstBlob);
113 length -= upperBound;
114 i++;
115 break;
116 }
117 skipStart -= l;
118 }
120 // Now append enough blobs until we're done
121 for (; length && i < mBlobs.Length(); i++) {
122 nsIDOMBlob* blob = mBlobs[i].get();
124 uint64_t l;
125 nsresult rv = blob->GetSize(&l);
126 NS_ENSURE_SUCCESS(rv, nullptr);
128 if (length < l) {
129 nsCOMPtr<nsIDOMBlob> lastBlob;
130 rv = blob->Slice(0, length, aContentType, 3,
131 getter_AddRefs(lastBlob));
132 NS_ENSURE_SUCCESS(rv, nullptr);
134 blobs.AppendElement(lastBlob);
135 } else {
136 blobs.AppendElement(blob);
137 }
138 length -= std::min<uint64_t>(l, length);
139 }
141 // we can create our blob now
142 nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType);
143 return blob.forget();
144 }
146 /* static */ nsresult
147 nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject)
148 {
149 nsCOMPtr<nsISupports> file =
150 do_QueryObject(new nsDOMMultipartFile(aName));
151 file.forget(aNewObject);
152 return NS_OK;
153 }
155 /* static */ nsresult
156 nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject)
157 {
158 nsCOMPtr<nsISupports> file = do_QueryObject(new nsDOMMultipartFile());
159 file.forget(aNewObject);
160 return NS_OK;
161 }
163 static nsIDOMBlob*
164 GetXPConnectNative(JSContext* aCx, JSObject* aObj) {
165 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(
166 nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, aObj));
167 return blob;
168 }
170 NS_IMETHODIMP
171 nsDOMMultipartFile::Initialize(nsISupports* aOwner,
172 JSContext* aCx,
173 JSObject* aObj,
174 const JS::CallArgs& aArgs)
175 {
176 if (!mIsFile) {
177 return InitBlob(aCx, aArgs.length(), aArgs.array(), GetXPConnectNative);
178 }
180 if (!nsContentUtils::IsCallerChrome()) {
181 return InitFile(aCx, aArgs.length(), aArgs.array());
182 }
184 if (aArgs.length() > 0) {
185 JS::Value* argv = aArgs.array();
186 if (argv[0].isObject()) {
187 JS::Rooted<JSObject*> obj(aCx, &argv[0].toObject());
188 if (JS_IsArrayObject(aCx, obj)) {
189 return InitFile(aCx, aArgs.length(), aArgs.array());
190 }
191 }
192 }
194 return InitChromeFile(aCx, aArgs.length(), aArgs.array());
195 }
197 nsresult
198 nsDOMMultipartFile::InitBlob(JSContext* aCx,
199 uint32_t aArgc,
200 JS::Value* aArgv,
201 UnwrapFuncPtr aUnwrapFunc)
202 {
203 bool nativeEOL = false;
204 if (aArgc > 1) {
205 BlobPropertyBag d;
206 if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) {
207 return NS_ERROR_TYPE_ERR;
208 }
209 mContentType = d.mType;
210 nativeEOL = d.mEndings == EndingTypes::Native;
211 }
213 if (aArgc > 0) {
214 return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, aUnwrapFunc);
215 }
217 return NS_OK;
218 }
220 nsresult
221 nsDOMMultipartFile::ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue,
222 bool aNativeEOL,
223 UnwrapFuncPtr aUnwrapFunc)
224 {
225 if (!aValue.isObject()) {
226 return NS_ERROR_TYPE_ERR; // We're not interested
227 }
229 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
230 if (!JS_IsArrayObject(aCx, obj)) {
231 return NS_ERROR_TYPE_ERR; // We're not interested
232 }
234 BlobSet blobSet;
236 uint32_t length;
237 MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, obj, &length));
238 for (uint32_t i = 0; i < length; ++i) {
239 JS::Rooted<JS::Value> element(aCx);
240 if (!JS_GetElement(aCx, obj, i, &element))
241 return NS_ERROR_TYPE_ERR;
243 if (element.isObject()) {
244 JS::Rooted<JSObject*> obj(aCx, &element.toObject());
245 nsCOMPtr<nsIDOMBlob> blob = aUnwrapFunc(aCx, obj);
246 if (blob) {
247 // Flatten so that multipart blobs will never nest
248 nsDOMFileBase* file = static_cast<nsDOMFileBase*>(
249 static_cast<nsIDOMBlob*>(blob));
250 const nsTArray<nsCOMPtr<nsIDOMBlob> >*
251 subBlobs = file->GetSubBlobs();
252 if (subBlobs) {
253 blobSet.AppendBlobs(*subBlobs);
254 } else {
255 blobSet.AppendBlob(blob);
256 }
257 continue;
258 }
259 if (JS_IsArrayBufferViewObject(obj)) {
260 nsresult rv = blobSet.AppendVoidPtr(
261 JS_GetArrayBufferViewData(obj),
262 JS_GetArrayBufferViewByteLength(obj));
263 NS_ENSURE_SUCCESS(rv, rv);
264 continue;
265 }
266 if (JS_IsArrayBufferObject(obj)) {
267 nsresult rv = blobSet.AppendArrayBuffer(obj);
268 NS_ENSURE_SUCCESS(rv, rv);
269 continue;
270 }
271 }
273 // coerce it to a string
274 JSString* str = JS::ToString(aCx, element);
275 NS_ENSURE_TRUE(str, NS_ERROR_TYPE_ERR);
277 nsresult rv = blobSet.AppendString(str, aNativeEOL, aCx);
278 NS_ENSURE_SUCCESS(rv, rv);
279 }
281 mBlobs = blobSet.GetBlobs();
282 return NS_OK;
283 }
285 NS_IMETHODIMP
286 nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename)
287 {
288 if (!mIsFromNsiFile || mBlobs.Length() == 0) {
289 return nsDOMFile::GetMozFullPathInternal(aFilename);
290 }
292 nsIDOMBlob* blob = mBlobs.ElementAt(0).get();
293 nsDOMFileFile* file = static_cast<nsDOMFileFile*>(blob);
294 if (!file) {
295 return nsDOMFile::GetMozFullPathInternal(aFilename);
296 }
298 return file->GetMozFullPathInternal(aFilename);
299 }
301 nsresult
302 nsDOMMultipartFile::InitChromeFile(JSContext* aCx,
303 uint32_t aArgc,
304 JS::Value* aArgv)
305 {
306 nsresult rv;
308 NS_ASSERTION(!mImmutable, "Something went wrong ...");
309 NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED);
310 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
311 NS_ENSURE_TRUE(aArgc > 0, NS_ERROR_UNEXPECTED);
313 if (aArgc > 1) {
314 FilePropertyBag d;
315 if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]))) {
316 return NS_ERROR_TYPE_ERR;
317 }
318 mName = d.mName;
319 mContentType = d.mType;
320 }
323 // We expect to get a path to represent as a File object or
324 // Blob object, an nsIFile, or an nsIDOMFile.
325 nsCOMPtr<nsIFile> file;
326 nsCOMPtr<nsIDOMBlob> blob;
327 if (!aArgv[0].isString()) {
328 // Lets see if it's an nsIFile
329 if (!aArgv[0].isObject()) {
330 return NS_ERROR_UNEXPECTED; // We're not interested
331 }
333 JSObject* obj = &aArgv[0].toObject();
335 nsISupports* supports =
336 nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
337 if (!supports) {
338 return NS_ERROR_UNEXPECTED;
339 }
341 blob = do_QueryInterface(supports);
342 file = do_QueryInterface(supports);
343 if (!blob && !file) {
344 return NS_ERROR_UNEXPECTED;
345 }
347 mIsFromNsiFile = true;
348 } else {
349 // It's a string
350 JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[0]));
351 NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
353 nsDependentJSString xpcomStr;
354 if (!xpcomStr.init(aCx, str)) {
355 return NS_ERROR_XPC_BAD_CONVERT_JS;
356 }
358 rv = NS_NewLocalFile(xpcomStr, false, getter_AddRefs(file));
359 NS_ENSURE_SUCCESS(rv, rv);
360 }
362 if (file) {
363 bool exists;
364 rv = file->Exists(&exists);
365 NS_ENSURE_SUCCESS(rv, rv);
366 NS_ENSURE_TRUE(exists, NS_ERROR_FILE_NOT_FOUND);
368 bool isDir;
369 rv = file->IsDirectory(&isDir);
370 NS_ENSURE_SUCCESS(rv, rv);
371 NS_ENSURE_FALSE(isDir, NS_ERROR_FILE_IS_DIRECTORY);
373 if (mName.IsEmpty()) {
374 file->GetLeafName(mName);
375 }
377 blob = new nsDOMFileFile(file);
378 }
380 // XXXkhuey this is terrible
381 if (mContentType.IsEmpty()) {
382 blob->GetType(mContentType);
383 }
385 BlobSet blobSet;
386 blobSet.AppendBlob(blob);
387 mBlobs = blobSet.GetBlobs();
389 return NS_OK;
390 }
392 nsresult
393 nsDOMMultipartFile::InitFile(JSContext* aCx,
394 uint32_t aArgc,
395 JS::Value* aArgv)
396 {
397 NS_ASSERTION(!mImmutable, "Something went wrong ...");
398 NS_ENSURE_TRUE(!mImmutable, NS_ERROR_UNEXPECTED);
400 if (aArgc < 2) {
401 return NS_ERROR_TYPE_ERR;
402 }
404 // File name
405 JSString* str = JS::ToString(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[1]));
406 NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
408 nsDependentJSString xpcomStr;
409 if (!xpcomStr.init(aCx, str)) {
410 return NS_ERROR_XPC_BAD_CONVERT_JS;
411 }
413 mName = xpcomStr;
415 // Optional params
416 bool nativeEOL = false;
417 if (aArgc > 2) {
418 BlobPropertyBag d;
419 if (!d.Init(aCx, JS::Handle<JS::Value>::fromMarkedLocation(&aArgv[2]))) {
420 return NS_ERROR_TYPE_ERR;
421 }
422 mContentType = d.mType;
423 nativeEOL = d.mEndings == EndingTypes::Native;
424 }
426 return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, GetXPConnectNative);
427 }
429 nsresult
430 BlobSet::AppendVoidPtr(const void* aData, uint32_t aLength)
431 {
432 NS_ENSURE_ARG_POINTER(aData);
434 uint64_t offset = mDataLen;
436 if (!ExpandBufferSize(aLength))
437 return NS_ERROR_OUT_OF_MEMORY;
439 memcpy((char*)mData + offset, aData, aLength);
440 return NS_OK;
441 }
443 nsresult
444 BlobSet::AppendString(JSString* aString, bool nativeEOL, JSContext* aCx)
445 {
446 nsDependentJSString xpcomStr;
447 if (!xpcomStr.init(aCx, aString)) {
448 return NS_ERROR_XPC_BAD_CONVERT_JS;
449 }
451 nsCString utf8Str = NS_ConvertUTF16toUTF8(xpcomStr);
453 if (nativeEOL) {
454 if (utf8Str.FindChar('\r') != kNotFound) {
455 utf8Str.ReplaceSubstring("\r\n", "\n");
456 utf8Str.ReplaceSubstring("\r", "\n");
457 }
458 #ifdef XP_WIN
459 utf8Str.ReplaceSubstring("\n", "\r\n");
460 #endif
461 }
463 return AppendVoidPtr((void*)utf8Str.Data(),
464 utf8Str.Length());
465 }
467 nsresult
468 BlobSet::AppendBlob(nsIDOMBlob* aBlob)
469 {
470 NS_ENSURE_ARG_POINTER(aBlob);
472 Flush();
473 mBlobs.AppendElement(aBlob);
475 return NS_OK;
476 }
478 nsresult
479 BlobSet::AppendBlobs(const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlob)
480 {
481 Flush();
482 mBlobs.AppendElements(aBlob);
484 return NS_OK;
485 }
487 nsresult
488 BlobSet::AppendArrayBuffer(JSObject* aBuffer)
489 {
490 return AppendVoidPtr(JS_GetArrayBufferData(aBuffer),
491 JS_GetArrayBufferByteLength(aBuffer));
492 }