diff -r 000000000000 -r 6474c204b198 dom/workers/File.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/workers/File.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,501 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "File.h" + +#include "nsIDOMFile.h" +#include "nsDOMBlobBuilder.h" +#include "nsError.h" + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "nsCOMPtr.h" +#include "nsJSUtils.h" +#include "nsString.h" + +#include "mozilla/dom/Exceptions.h" +#include "WorkerInlines.h" +#include "WorkerPrivate.h" + +USING_WORKERS_NAMESPACE +using mozilla::dom::Throw; + +namespace { + +class Blob +{ + // Blob should never be instantiated. + Blob(); + ~Blob(); + + static const JSClass sClass; + static const JSPropertySpec sProperties[]; + static const JSFunctionSpec sFunctions[]; + +public: + static JSObject* + InitClass(JSContext* aCx, JS::Handle aObj) + { + return JS_InitClass(aCx, aObj, JS::NullPtr(), &sClass, Construct, 0, + sProperties, sFunctions, nullptr, nullptr); + } + + static JSObject* + Create(JSContext* aCx, nsIDOMBlob* aBlob) + { + MOZ_ASSERT(SameCOMIdentity(static_cast(aBlob), aBlob)); + + JSObject* obj = JS_NewObject(aCx, &sClass, JS::NullPtr(), JS::NullPtr()); + if (obj) { + JS_SetPrivate(obj, aBlob); + NS_ADDREF(aBlob); + } + return obj; + } + + static nsIDOMBlob* + GetPrivate(JSObject* aObj); + +private: + static nsIDOMBlob* + GetInstancePrivate(JSContext* aCx, JS::Handle aObj, const char* aFunctionName) + { + nsIDOMBlob* blob = GetPrivate(aObj); + if (blob) { + return blob; + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName, + JS_GetClass(aObj)->name); + return nullptr; + } + + static nsIDOMBlob* + Unwrap(JSContext* aCx, JSObject* aObj) + { + return GetPrivate(aObj); + } + + static bool + Construct(JSContext* aCx, unsigned aArgc, jsval* aVp) + { + JS::CallArgs args = CallArgsFromVp(aArgc, aVp); + + nsRefPtr file = new nsDOMMultipartFile(); + nsresult rv = file->InitBlob(aCx, args.length(), args.array(), Unwrap); + if (NS_FAILED(rv)) { + return Throw(aCx, rv); + } + + JSObject* obj = file::CreateBlob(aCx, file); + if (!obj) { + return false; + } + + args.rval().setObject(*obj); + return true; + } + + static void + Finalize(JSFreeOp* aFop, JSObject* aObj) + { + MOZ_ASSERT(JS_GetClass(aObj) == &sClass); + + nsIDOMBlob* blob = GetPrivate(aObj); + NS_IF_RELEASE(blob); + } + + static bool + IsBlob(JS::Handle v) + { + return v.isObject() && GetPrivate(&v.toObject()) != nullptr; + } + + static bool + GetSizeImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "size"); + MOZ_ASSERT(blob); + + uint64_t size; + if (NS_FAILED(blob->GetSize(&size))) { + return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR); + } + + aArgs.rval().setNumber(double(size)); + return true; + } + + static bool + GetSize(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } + + static bool + GetTypeImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "type"); + MOZ_ASSERT(blob); + + nsString type; + if (NS_FAILED(blob->GetType(type))) { + return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR); + } + + JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length()); + if (!jsType) { + return false; + } + + aArgs.rval().setString(jsType); + return true; + } + + static bool + GetType(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } + + static bool + Slice(JSContext* aCx, unsigned aArgc, jsval* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + + JS::Rooted obj(aCx, args.thisv().toObjectOrNull()); + if (!obj) { + return false; + } + + nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "slice"); + if (!blob) { + return false; + } + + double start = 0, end = 0; + JS::Rooted jsContentType(aCx, JS_GetEmptyString(JS_GetRuntime(aCx))); + if (!JS_ConvertArguments(aCx, args, "/IIS", &start, + &end, jsContentType.address())) { + return false; + } + + nsDependentJSString contentType; + if (!contentType.init(aCx, jsContentType)) { + return false; + } + + uint8_t optionalArgc = aArgc; + nsCOMPtr rtnBlob; + if (NS_FAILED(blob->Slice(static_cast(start), + static_cast(end), + contentType, optionalArgc, + getter_AddRefs(rtnBlob)))) { + return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR); + } + + JSObject* rtnObj = file::CreateBlob(aCx, rtnBlob); + if (!rtnObj) { + return false; + } + + args.rval().setObject(*rtnObj); + return true; + } +}; + +const JSClass Blob::sClass = { + "Blob", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize +}; + +const JSPropertySpec Blob::sProperties[] = { + JS_PSGS("size", GetSize, GetterOnlyJSNative, JSPROP_ENUMERATE), + JS_PSGS("type", GetType, GetterOnlyJSNative, JSPROP_ENUMERATE), + JS_PS_END +}; + +const JSFunctionSpec Blob::sFunctions[] = { + JS_FN("slice", Slice, 1, JSPROP_ENUMERATE), + JS_FS_END +}; + +class File : public Blob +{ + // File should never be instantiated. + File(); + ~File(); + + static const JSClass sClass; + static const JSPropertySpec sProperties[]; + +public: + static JSObject* + InitClass(JSContext* aCx, JS::Handle aObj, JS::Handle aParentProto) + { + return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0, + sProperties, nullptr, nullptr, nullptr); + } + + static JSObject* + Create(JSContext* aCx, nsIDOMFile* aFile) + { + MOZ_ASSERT(SameCOMIdentity(static_cast(aFile), aFile)); + + JSObject* obj = JS_NewObject(aCx, &sClass, JS::NullPtr(), JS::NullPtr()); + if (obj) { + JS_SetPrivate(obj, aFile); + NS_ADDREF(aFile); + } + return obj; + } + + static nsIDOMFile* + GetPrivate(JSObject* aObj) + { + if (aObj) { + const JSClass* classPtr = JS_GetClass(aObj); + if (classPtr == &sClass) { + nsISupports* priv = static_cast(JS_GetPrivate(aObj)); + nsCOMPtr file = do_QueryInterface(priv); + MOZ_ASSERT_IF(priv, file); + return file; + } + } + return nullptr; + } + + static const JSClass* + Class() + { + return &sClass; + } + +private: + static nsIDOMFile* + GetInstancePrivate(JSContext* aCx, JS::Handle aObj, const char* aFunctionName) + { + nsIDOMFile* file = GetPrivate(aObj); + if (file) { + return file; + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName, + JS_GetClass(aObj)->name); + return nullptr; + } + + static bool + Construct(JSContext* aCx, unsigned aArgc, jsval* aVp) + { + JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, + JSMSG_WRONG_CONSTRUCTOR, + sClass.name); + return false; + } + + static void + Finalize(JSFreeOp* aFop, JSObject* aObj) + { + MOZ_ASSERT(JS_GetClass(aObj) == &sClass); + + nsIDOMFile* file = GetPrivate(aObj); + NS_IF_RELEASE(file); + } + + static bool + IsFile(JS::Handle v) + { + return v.isObject() && GetPrivate(&v.toObject()) != nullptr; + } + + static bool + GetMozFullPathImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMFile* file = GetInstancePrivate(aCx, obj, "mozFullPath"); + MOZ_ASSERT(file); + + nsString fullPath; + + if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() && + NS_FAILED(file->GetMozFullPathInternal(fullPath))) { + return Throw(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR); + } + + JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(), + fullPath.Length()); + if (!jsFullPath) { + return false; + } + + aArgs.rval().setString(jsFullPath); + return true; + } + + static bool + GetMozFullPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } + + static bool + GetNameImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMFile* file = GetInstancePrivate(aCx, obj, "name"); + MOZ_ASSERT(file); + + nsString name; + if (NS_FAILED(file->GetName(name))) { + name.Truncate(); + } + + JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length()); + if (!jsName) { + return false; + } + + aArgs.rval().setString(jsName); + return true; + } + + static bool + GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } + + static bool + GetPathImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMFile* file = GetInstancePrivate(aCx, obj, "path"); + MOZ_ASSERT(file); + + nsString path; + if (NS_FAILED(file->GetPath(path))) { + path.Truncate(); + } + + JSString* jsPath = JS_NewUCStringCopyN(aCx, path.get(), path.Length()); + if (!jsPath) { + return false; + } + + aArgs.rval().setString(jsPath); + return true; + } + + static bool + GetPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } + + static bool + GetLastModifiedDateImpl(JSContext* aCx, JS::CallArgs aArgs) + { + JS::Rooted obj(aCx, &aArgs.thisv().toObject()); + nsIDOMFile* file = GetInstancePrivate(aCx, obj, "lastModifiedDate"); + MOZ_ASSERT(file); + + if (NS_FAILED(file->GetLastModifiedDate(aCx, aArgs.rval()))) { + return false; + } + return true; + } + + static bool + GetLastModifiedDate(JSContext* aCx, unsigned aArgc, JS::Value* aVp) + { + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + return JS::CallNonGenericMethod(aCx, args); + } +}; + +const JSClass File::sClass = { + "File", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize +}; + +const JSPropertySpec File::sProperties[] = { + JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE), + JS_PSGS("path", GetPath, GetterOnlyJSNative, JSPROP_ENUMERATE), + JS_PSGS("lastModifiedDate", GetLastModifiedDate, GetterOnlyJSNative, + JSPROP_ENUMERATE), + JS_PSGS("mozFullPath", GetMozFullPath, GetterOnlyJSNative, JSPROP_ENUMERATE), + JS_PS_END +}; + +nsIDOMBlob* +Blob::GetPrivate(JSObject* aObj) +{ + if (aObj) { + const JSClass* classPtr = JS_GetClass(aObj); + if (classPtr == &sClass || classPtr == File::Class()) { + nsISupports* priv = static_cast(JS_GetPrivate(aObj)); + nsCOMPtr blob = do_QueryInterface(priv); + MOZ_ASSERT_IF(priv, blob); + return blob; + } + } + return nullptr; +} + +} // anonymous namespace + +BEGIN_WORKERS_NAMESPACE + +namespace file { + +JSObject* +CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob) +{ + return Blob::Create(aCx, aBlob); +} + +bool +InitClasses(JSContext* aCx, JS::Handle aGlobal) +{ + JS::Rooted blobProto(aCx, Blob::InitClass(aCx, aGlobal)); + return blobProto && File::InitClass(aCx, aGlobal, blobProto); +} + +nsIDOMBlob* +GetDOMBlobFromJSObject(JSObject* aObj) +{ + return Blob::GetPrivate(aObj); +} + +JSObject* +CreateFile(JSContext* aCx, nsIDOMFile* aFile) +{ + return File::Create(aCx, aFile); +} + +nsIDOMFile* +GetDOMFileFromJSObject(JSObject* aObj) +{ + return File::GetPrivate(aObj); +} + +} // namespace file + +END_WORKERS_NAMESPACE