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