michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ 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 "mozJSSubScriptLoader.h" michael@0: #include "mozJSComponentLoader.h" michael@0: #include "mozJSLoaderUtils.h" michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsIIOService.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsScriptLoader.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsfriendapi.h" michael@0: #include "js/OldDebugAPI.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "xpcpublic.h" // For xpc::SystemErrorReporter michael@0: #include "xpcprivate.h" // For xpc::OptionsBase michael@0: #include "jswrapper.h" michael@0: michael@0: #include "mozilla/scache/StartupCache.h" michael@0: #include "mozilla/scache/StartupCacheUtils.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: using namespace mozilla::scache; michael@0: using namespace JS; michael@0: using namespace xpc; michael@0: using namespace mozilla; michael@0: michael@0: class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase { michael@0: public: michael@0: LoadSubScriptOptions(JSContext *cx = xpc_GetSafeJSContext(), michael@0: JSObject *options = nullptr) michael@0: : OptionsBase(cx, options) michael@0: , target(cx) michael@0: , charset(NullString()) michael@0: , ignoreCache(false) michael@0: { } michael@0: michael@0: virtual bool Parse() { michael@0: return ParseObject("target", &target) && michael@0: ParseString("charset", charset) && michael@0: ParseBoolean("ignoreCache", &ignoreCache); michael@0: } michael@0: michael@0: RootedObject target; michael@0: nsString charset; michael@0: bool ignoreCache; michael@0: }; michael@0: michael@0: michael@0: /* load() error msgs, XXX localize? */ michael@0: #define LOAD_ERROR_NOSERVICE "Error creating IO Service." michael@0: #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)" michael@0: #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme. This is bad." michael@0: #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI." michael@0: #define LOAD_ERROR_NOSTREAM "Error opening input stream (invalid filename?)" michael@0: #define LOAD_ERROR_NOCONTENT "ContentLength not available (not a local URL?)" michael@0: #define LOAD_ERROR_BADCHARSET "Error converting to specified charset" michael@0: #define LOAD_ERROR_BADREAD "File Read Error." michael@0: #define LOAD_ERROR_READUNDERFLOW "File Read Error (underflow.)" michael@0: #define LOAD_ERROR_NOPRINCIPALS "Failed to get principals." michael@0: #define LOAD_ERROR_NOSPEC "Failed to get URI spec. This is bad." michael@0: #define LOAD_ERROR_CONTENTTOOBIG "ContentLength is too large" michael@0: michael@0: mozJSSubScriptLoader::mozJSSubScriptLoader() : mSystemPrincipal(nullptr) michael@0: { michael@0: // Force construction of the JS component loader. We may need it later. michael@0: nsCOMPtr componentLoader = michael@0: do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID); michael@0: } michael@0: michael@0: mozJSSubScriptLoader::~mozJSSubScriptLoader() michael@0: { michael@0: /* empty */ michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(mozJSSubScriptLoader, mozIJSSubScriptLoader) michael@0: michael@0: static nsresult michael@0: ReportError(JSContext *cx, const char *msg) michael@0: { michael@0: RootedValue exn(cx, JS::StringValue(JS_NewStringCopyZ(cx, msg))); michael@0: JS_SetPendingException(cx, exn); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObjArg, michael@0: const nsAString &charset, const char *uriStr, michael@0: nsIIOService *serv, nsIPrincipal *principal, michael@0: bool reuseGlobal, JS::MutableHandleScript script, michael@0: JS::MutableHandleFunction function) michael@0: { michael@0: RootedObject target_obj(cx, targetObjArg); michael@0: michael@0: script.set(nullptr); michael@0: function.set(nullptr); michael@0: michael@0: // Instead of calling NS_OpenURI, we create the channel ourselves and call michael@0: // SetContentType, to avoid expensive MIME type lookups (bug 632490). michael@0: nsCOMPtr chan; michael@0: nsCOMPtr instream; michael@0: nsresult rv = NS_NewChannel(getter_AddRefs(chan), uri, serv, michael@0: nullptr, nullptr, nsIRequest::LOAD_NORMAL); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: chan->SetContentType(NS_LITERAL_CSTRING("application/javascript")); michael@0: rv = chan->Open(getter_AddRefs(instream)); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return ReportError(cx, LOAD_ERROR_NOSTREAM); michael@0: } michael@0: michael@0: int64_t len = -1; michael@0: michael@0: rv = chan->GetContentLength(&len); michael@0: if (NS_FAILED(rv) || len == -1) { michael@0: return ReportError(cx, LOAD_ERROR_NOCONTENT); michael@0: } michael@0: michael@0: if (len > INT32_MAX) { michael@0: return ReportError(cx, LOAD_ERROR_CONTENTTOOBIG); michael@0: } michael@0: michael@0: nsCString buf; michael@0: rv = NS_ReadInputStreamToString(instream, buf, len); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: /* set our own error reporter so we can report any bad things as catchable michael@0: * exceptions, including the source/line number */ michael@0: JSErrorReporter er = JS_SetErrorReporter(cx, xpc::SystemErrorReporter); michael@0: michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(uriStr, 1); michael@0: if (!charset.IsVoid()) { michael@0: jschar *scriptBuf = nullptr; michael@0: size_t scriptLength = 0; michael@0: michael@0: rv = nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast(buf.get()), len, michael@0: charset, nullptr, scriptBuf, scriptLength); michael@0: michael@0: JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, michael@0: JS::SourceBufferHolder::GiveOwnership); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return ReportError(cx, LOAD_ERROR_BADCHARSET); michael@0: } michael@0: michael@0: if (!reuseGlobal) { michael@0: JS::Compile(cx, target_obj, options, srcBuf, script); michael@0: } else { michael@0: JS::CompileFunction(cx, target_obj, options, michael@0: nullptr, 0, nullptr, michael@0: srcBuf, michael@0: function); michael@0: } michael@0: } else { michael@0: // We only use lazy source when no special encoding is specified because michael@0: // the lazy source loader doesn't know the encoding. michael@0: if (!reuseGlobal) { michael@0: options.setSourceIsLazy(true); michael@0: script.set(JS::Compile(cx, target_obj, options, buf.get(), len)); michael@0: } else { michael@0: function.set(JS::CompileFunction(cx, target_obj, options, michael@0: nullptr, 0, nullptr, buf.get(), michael@0: len)); michael@0: } michael@0: } michael@0: michael@0: /* repent for our evil deeds */ michael@0: JS_SetErrorReporter(cx, er); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSSubScriptLoader::LoadSubScript(const nsAString &url, michael@0: HandleValue target, michael@0: const nsAString &charset, michael@0: JSContext *cx, michael@0: MutableHandleValue retval) michael@0: { michael@0: /* michael@0: * Loads a local url and evals it into the current cx michael@0: * Synchronous (an async version would be cool too.) michael@0: * url: The url to load. Must be local so that it can be loaded michael@0: * synchronously. michael@0: * target_obj: Optional object to eval the script onto (defaults to context michael@0: * global) michael@0: * charset: Optional character set to use for reading michael@0: * returns: Whatever jsval the script pointed to by the url returns. michael@0: * Should ONLY (O N L Y !) be called from JavaScript code. michael@0: */ michael@0: LoadSubScriptOptions options(cx); michael@0: options.charset = charset; michael@0: options.target = target.isObject() ? &target.toObject() : nullptr; michael@0: return DoLoadSubScriptWithOptions(url, options, cx, retval); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString &url, michael@0: HandleValue optionsVal, michael@0: JSContext *cx, michael@0: MutableHandleValue retval) michael@0: { michael@0: if (!optionsVal.isObject()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: LoadSubScriptOptions options(cx, &optionsVal.toObject()); michael@0: if (!options.Parse()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: return DoLoadSubScriptWithOptions(url, options, cx, retval); michael@0: } michael@0: michael@0: nsresult michael@0: mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString &url, michael@0: LoadSubScriptOptions &options, michael@0: JSContext *cx, michael@0: MutableHandleValue retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: /* set the system principal if it's not here already */ michael@0: if (!mSystemPrincipal) { michael@0: nsCOMPtr secman = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: if (!secman) michael@0: return NS_OK; michael@0: michael@0: rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal)); michael@0: if (NS_FAILED(rv) || !mSystemPrincipal) michael@0: return rv; michael@0: } michael@0: michael@0: RootedObject targetObj(cx); michael@0: mozJSComponentLoader *loader = mozJSComponentLoader::Get(); michael@0: rv = loader->FindTargetObject(cx, &targetObj); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We base reusingGlobal off of what the loader told us, but we may not michael@0: // actually be using that object. michael@0: bool reusingGlobal = !JS_IsGlobalObject(targetObj); michael@0: michael@0: if (options.target) michael@0: targetObj = options.target; michael@0: michael@0: // Remember an object out of the calling compartment so that we michael@0: // can properly wrap the result later. michael@0: nsCOMPtr principal = mSystemPrincipal; michael@0: RootedObject result_obj(cx, targetObj); michael@0: targetObj = JS_FindCompilationScope(cx, targetObj); michael@0: if (!targetObj) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (targetObj != result_obj) michael@0: principal = GetObjectPrincipal(targetObj); michael@0: michael@0: JSAutoCompartment ac(cx, targetObj); michael@0: michael@0: /* load up the url. From here on, failures are reflected as ``custom'' michael@0: * js exceptions */ michael@0: nsCOMPtr uri; michael@0: nsAutoCString uriStr; michael@0: nsAutoCString scheme; michael@0: michael@0: // Figure out who's calling us michael@0: JS::AutoFilename filename; michael@0: if (!JS::DescribeScriptedCaller(cx, &filename)) { michael@0: // No scripted frame means we don't know who's calling, bail. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Suppress caching if we're compiling as content. michael@0: StartupCache* cache = (principal == mSystemPrincipal) michael@0: ? StartupCache::GetSingleton() michael@0: : nullptr; michael@0: nsCOMPtr serv = do_GetService(NS_IOSERVICE_CONTRACTID); michael@0: if (!serv) { michael@0: return ReportError(cx, LOAD_ERROR_NOSERVICE); michael@0: } michael@0: michael@0: // Make sure to explicitly create the URI, since we'll need the michael@0: // canonicalized spec. michael@0: rv = NS_NewURI(getter_AddRefs(uri), NS_LossyConvertUTF16toASCII(url).get(), nullptr, serv); michael@0: if (NS_FAILED(rv)) { michael@0: return ReportError(cx, LOAD_ERROR_NOURI); michael@0: } michael@0: michael@0: rv = uri->GetSpec(uriStr); michael@0: if (NS_FAILED(rv)) { michael@0: return ReportError(cx, LOAD_ERROR_NOSPEC); michael@0: } michael@0: michael@0: rv = uri->GetScheme(scheme); michael@0: if (NS_FAILED(rv)) { michael@0: return ReportError(cx, LOAD_ERROR_NOSCHEME); michael@0: } michael@0: michael@0: if (!scheme.EqualsLiteral("chrome")) { michael@0: // This might be a URI to a local file, though! michael@0: nsCOMPtr innerURI = NS_GetInnermostURI(uri); michael@0: nsCOMPtr fileURL = do_QueryInterface(innerURI); michael@0: if (!fileURL) { michael@0: return ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL); michael@0: } michael@0: michael@0: // For file URIs prepend the filename with the filename of the michael@0: // calling script, and " -> ". See bug 418356. michael@0: nsAutoCString tmp(filename.get()); michael@0: tmp.AppendLiteral(" -> "); michael@0: tmp.Append(uriStr); michael@0: michael@0: uriStr = tmp; michael@0: } michael@0: michael@0: bool writeScript = false; michael@0: JSVersion version = JS_GetVersion(cx); michael@0: nsAutoCString cachePath; michael@0: cachePath.AppendPrintf("jssubloader/%d", version); michael@0: PathifyURI(uri, cachePath); michael@0: michael@0: RootedFunction function(cx); michael@0: RootedScript script(cx); michael@0: if (cache && !options.ignoreCache) michael@0: rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script); michael@0: if (!script) { michael@0: rv = ReadScript(uri, cx, targetObj, options.charset, michael@0: static_cast(uriStr.get()), serv, michael@0: principal, reusingGlobal, &script, &function); michael@0: writeScript = !!script; michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || (!script && !function)) michael@0: return rv; michael@0: michael@0: if (function) { michael@0: script = JS_GetFunctionScript(cx, function); michael@0: } michael@0: michael@0: loader->NoteSubScript(script, targetObj); michael@0: michael@0: michael@0: bool ok = false; michael@0: if (function) { michael@0: ok = JS_CallFunction(cx, targetObj, function, JS::HandleValueArray::empty(), michael@0: retval); michael@0: } else { michael@0: ok = JS_ExecuteScriptVersion(cx, targetObj, script, retval, version); michael@0: } michael@0: michael@0: if (ok) { michael@0: JSAutoCompartment rac(cx, result_obj); michael@0: if (!JS_WrapValue(cx, retval)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (cache && ok && writeScript) { michael@0: WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Let us compile scripts from a URI off the main thread. michael@0: */ michael@0: michael@0: class ScriptPrecompiler : public nsIStreamLoaderObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISTREAMLOADEROBSERVER michael@0: michael@0: ScriptPrecompiler(nsIObserver* aObserver, michael@0: nsIPrincipal* aPrincipal, michael@0: nsIChannel* aChannel) michael@0: : mObserver(aObserver) michael@0: , mPrincipal(aPrincipal) michael@0: , mChannel(aChannel) michael@0: , mScriptBuf(nullptr) michael@0: , mScriptLength(0) michael@0: {} michael@0: michael@0: virtual ~ScriptPrecompiler() michael@0: { michael@0: if (mScriptBuf) { michael@0: js_free(mScriptBuf); michael@0: } michael@0: } michael@0: michael@0: static void OffThreadCallback(void *aToken, void *aData); michael@0: michael@0: /* Sends the "done" notification back. Main thread only. */ michael@0: void SendObserverNotification(); michael@0: michael@0: private: michael@0: nsRefPtr mObserver; michael@0: nsRefPtr mPrincipal; michael@0: nsRefPtr mChannel; michael@0: jschar* mScriptBuf; michael@0: size_t mScriptLength; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIStreamLoaderObserver); michael@0: michael@0: class NotifyPrecompilationCompleteRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: NotifyPrecompilationCompleteRunnable(ScriptPrecompiler* aPrecompiler) michael@0: : mPrecompiler(aPrecompiler) michael@0: , mToken(nullptr) michael@0: {} michael@0: michael@0: void SetToken(void* aToken) { michael@0: MOZ_ASSERT(aToken && !mToken); michael@0: mToken = aToken; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mPrecompiler; michael@0: void* mToken; michael@0: }; michael@0: michael@0: /* RAII helper class to send observer notifications */ michael@0: class AutoSendObserverNotification { michael@0: public: michael@0: AutoSendObserverNotification(ScriptPrecompiler* aPrecompiler) michael@0: : mPrecompiler(aPrecompiler) michael@0: {} michael@0: michael@0: ~AutoSendObserverNotification() { michael@0: if (mPrecompiler) { michael@0: mPrecompiler->SendObserverNotification(); michael@0: } michael@0: } michael@0: michael@0: void Disarm() { michael@0: mPrecompiler = nullptr; michael@0: } michael@0: michael@0: private: michael@0: ScriptPrecompiler* mPrecompiler; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: NotifyPrecompilationCompleteRunnable::Run(void) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mPrecompiler); michael@0: michael@0: AutoSendObserverNotification notifier(mPrecompiler); michael@0: michael@0: if (mToken) { michael@0: JSRuntime *rt = XPCJSRuntime::Get()->Runtime(); michael@0: NS_ENSURE_TRUE(rt, NS_ERROR_FAILURE); michael@0: JS::FinishOffThreadScript(nullptr, rt, mToken); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: ScriptPrecompiler::OnStreamComplete(nsIStreamLoader* aLoader, michael@0: nsISupports* aContext, michael@0: nsresult aStatus, michael@0: uint32_t aLength, michael@0: const uint8_t* aString) michael@0: { michael@0: AutoSendObserverNotification notifier(this); michael@0: michael@0: // Just notify that we are done with this load. michael@0: NS_ENSURE_SUCCESS(aStatus, NS_OK); michael@0: michael@0: // Convert data to jschar* and prepare to call CompileOffThread. michael@0: nsAutoString hintCharset; michael@0: nsresult rv = michael@0: nsScriptLoader::ConvertToUTF16(mChannel, aString, aLength, michael@0: hintCharset, nullptr, michael@0: mScriptBuf, mScriptLength); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: michael@0: // Our goal is to cache persistently the compiled script and to avoid quota michael@0: // checks. Since the caching mechanism decide the persistence type based on michael@0: // the principal, we create a new global with the app's principal. michael@0: // We then enter its compartment to compile with its principal. michael@0: AutoSafeJSContext cx; michael@0: RootedValue v(cx); michael@0: SandboxOptions sandboxOptions; michael@0: sandboxOptions.sandboxName.AssignASCII("asm.js precompilation"); michael@0: sandboxOptions.invisibleToDebugger = true; michael@0: sandboxOptions.discardSource = true; michael@0: rv = CreateSandboxObject(cx, &v, mPrincipal, sandboxOptions); michael@0: NS_ENSURE_SUCCESS(rv, NS_OK); michael@0: michael@0: JSAutoCompartment ac(cx, js::UncheckedUnwrap(&v.toObject())); michael@0: michael@0: JS::CompileOptions options(cx, JSVERSION_DEFAULT); michael@0: options.forceAsync = true; michael@0: options.compileAndGo = true; michael@0: options.installedFile = true; michael@0: michael@0: nsCOMPtr uri; michael@0: mChannel->GetURI(getter_AddRefs(uri)); michael@0: nsAutoCString spec; michael@0: uri->GetSpec(spec); michael@0: options.setFile(spec.get()); michael@0: michael@0: if (!JS::CanCompileOffThread(cx, options, mScriptLength)) { michael@0: NS_WARNING("Can't compile script off thread!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr runnable = michael@0: new NotifyPrecompilationCompleteRunnable(this); michael@0: michael@0: if (!JS::CompileOffThread(cx, options, michael@0: mScriptBuf, mScriptLength, michael@0: OffThreadCallback, michael@0: static_cast(runnable))) { michael@0: NS_WARNING("Failed to compile script off thread!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: unused << runnable.forget(); michael@0: notifier.Disarm(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: ScriptPrecompiler::OffThreadCallback(void* aToken, void* aData) michael@0: { michael@0: nsRefPtr runnable = michael@0: dont_AddRef(static_cast(aData)); michael@0: runnable->SetToken(aToken); michael@0: michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: void michael@0: ScriptPrecompiler::SendObserverNotification() michael@0: { michael@0: MOZ_ASSERT(mChannel && mObserver); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr uri; michael@0: mChannel->GetURI(getter_AddRefs(uri)); michael@0: mObserver->Observe(uri, "script-precompiled", nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSSubScriptLoader::PrecompileScript(nsIURI* aURI, michael@0: nsIPrincipal* aPrincipal, michael@0: nsIObserver *aObserver) michael@0: { michael@0: nsCOMPtr channel; michael@0: nsresult rv = NS_NewChannel(getter_AddRefs(channel), michael@0: aURI, nullptr, nullptr, nullptr, michael@0: nsIRequest::LOAD_NORMAL, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr loadObserver = michael@0: new ScriptPrecompiler(aObserver, aPrincipal, channel); michael@0: michael@0: nsCOMPtr loader; michael@0: rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr listener = loader.get(); michael@0: rv = channel->AsyncOpen(listener, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: }