LCOV - code coverage report
Current view: top level - js/xpconnect/loader - mozJSSubScriptLoader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 120 385 31.2 %
Date: 2017-07-14 16:53:18 Functions: 11 59 18.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozJSSubScriptLoader.h"
       8             : #include "mozJSComponentLoader.h"
       9             : #include "mozJSLoaderUtils.h"
      10             : 
      11             : #include "nsIURI.h"
      12             : #include "nsIIOService.h"
      13             : #include "nsIChannel.h"
      14             : #include "nsIInputStream.h"
      15             : #include "nsNetCID.h"
      16             : #include "nsNetUtil.h"
      17             : #include "nsIFileURL.h"
      18             : #include "nsIScriptSecurityManager.h"
      19             : #include "nsThreadUtils.h"
      20             : 
      21             : #include "jsapi.h"
      22             : #include "jsfriendapi.h"
      23             : #include "nsJSPrincipals.h"
      24             : #include "xpcprivate.h" // For xpc::OptionsBase
      25             : #include "jswrapper.h"
      26             : 
      27             : #include "mozilla/dom/Promise.h"
      28             : #include "mozilla/dom/ToJSValue.h"
      29             : #include "mozilla/dom/ScriptLoader.h"
      30             : #include "mozilla/HoldDropJSObjects.h"
      31             : #include "mozilla/ScriptPreloader.h"
      32             : #include "mozilla/scache/StartupCache.h"
      33             : #include "mozilla/scache/StartupCacheUtils.h"
      34             : #include "mozilla/Unused.h"
      35             : #include "nsContentUtils.h"
      36             : #include "nsStringGlue.h"
      37             : #include "nsCycleCollectionParticipant.h"
      38             : #include "GeckoProfiler.h"
      39             : 
      40             : using namespace mozilla::scache;
      41             : using namespace JS;
      42             : using namespace xpc;
      43             : using namespace mozilla;
      44             : using namespace mozilla::dom;
      45             : 
      46          33 : class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
      47             : public:
      48          33 :     explicit LoadSubScriptOptions(JSContext* cx = xpc_GetSafeJSContext(),
      49             :                                   JSObject* options = nullptr)
      50          33 :         : OptionsBase(cx, options)
      51             :         , target(cx)
      52          33 :         , charset(NullString())
      53             :         , ignoreCache(false)
      54             :         , async(false)
      55          66 :         , wantReturnValue(false)
      56          33 :     { }
      57             : 
      58           0 :     virtual bool Parse() {
      59           0 :       return ParseObject("target", &target) &&
      60           0 :              ParseString("charset", charset) &&
      61           0 :              ParseBoolean("ignoreCache", &ignoreCache) &&
      62           0 :              ParseBoolean("async", &async) &&
      63           0 :              ParseBoolean("wantReturnValue", &wantReturnValue);
      64             :     }
      65             : 
      66             :     RootedObject target;
      67             :     nsString charset;
      68             :     bool ignoreCache;
      69             :     bool async;
      70             :     bool wantReturnValue;
      71             : };
      72             : 
      73             : 
      74             : /* load() error msgs, XXX localize? */
      75             : #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
      76             : #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
      77             : #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme.  This is bad."
      78             : #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
      79             : #define LOAD_ERROR_NOSTREAM  "Error opening input stream (invalid filename?)"
      80             : #define LOAD_ERROR_NOCONTENT "ContentLength not available (not a local URL?)"
      81             : #define LOAD_ERROR_BADCHARSET "Error converting to specified charset"
      82             : #define LOAD_ERROR_BADREAD   "File Read Error."
      83             : #define LOAD_ERROR_READUNDERFLOW "File Read Error (underflow.)"
      84             : #define LOAD_ERROR_NOPRINCIPALS "Failed to get principals."
      85             : #define LOAD_ERROR_NOSPEC "Failed to get URI spec.  This is bad."
      86             : #define LOAD_ERROR_CONTENTTOOBIG "ContentLength is too large"
      87             : 
      88           2 : mozJSSubScriptLoader::mozJSSubScriptLoader()
      89             : {
      90           2 : }
      91             : 
      92           0 : mozJSSubScriptLoader::~mozJSSubScriptLoader()
      93             : {
      94           0 : }
      95             : 
      96         224 : NS_IMPL_ISUPPORTS(mozJSSubScriptLoader, mozIJSSubScriptLoader)
      97             : 
      98             : static void
      99           0 : ReportError(JSContext* cx, const nsACString& msg)
     100             : {
     101           0 :     NS_ConvertUTF8toUTF16 ucMsg(msg);
     102             : 
     103           0 :     RootedValue exn(cx);
     104           0 :     if (xpc::NonVoidStringToJsval(cx, ucMsg, &exn)) {
     105           0 :         JS_SetPendingException(cx, exn);
     106             :     }
     107           0 : }
     108             : 
     109             : static void
     110           0 : ReportError(JSContext* cx, const char* origMsg, nsIURI* uri)
     111             : {
     112           0 :     if (!uri) {
     113           0 :         ReportError(cx, nsDependentCString(origMsg));
     114           0 :         return;
     115             :     }
     116             : 
     117           0 :     nsAutoCString spec;
     118           0 :     nsresult rv = uri->GetSpec(spec);
     119           0 :     if (NS_FAILED(rv))
     120           0 :         spec.Assign("(unknown)");
     121             : 
     122           0 :     nsAutoCString msg(origMsg);
     123           0 :     msg.Append(": ");
     124           0 :     msg.Append(spec);
     125           0 :     ReportError(cx, msg);
     126             : }
     127             : 
     128             : static bool
     129          15 : PrepareScript(nsIURI* uri,
     130             :               JSContext* cx,
     131             :               HandleObject targetObj,
     132             :               const char* uriStr,
     133             :               const nsAString& charset,
     134             :               const char* buf,
     135             :               int64_t len,
     136             :               bool wantReturnValue,
     137             :               MutableHandleScript script)
     138             : {
     139          30 :     JS::CompileOptions options(cx);
     140          15 :     options.setFileAndLine(uriStr, 1)
     141          15 :            .setVersion(JSVERSION_LATEST)
     142          30 :            .setNoScriptRval(!wantReturnValue);
     143          15 :     if (!charset.IsVoid()) {
     144           0 :         char16_t* scriptBuf = nullptr;
     145           0 :         size_t scriptLength = 0;
     146             : 
     147             :         nsresult rv =
     148           0 :             ScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf), len,
     149           0 :                                          charset, nullptr, scriptBuf, scriptLength);
     150             : 
     151             :         JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
     152           0 :                                       JS::SourceBufferHolder::GiveOwnership);
     153             : 
     154           0 :         if (NS_FAILED(rv)) {
     155           0 :             ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
     156           0 :             return false;
     157             :         }
     158             : 
     159           0 :         if (JS_IsGlobalObject(targetObj)) {
     160           0 :             return JS::Compile(cx, options, srcBuf, script);
     161             :         }
     162           0 :         return JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
     163             :     }
     164             :     // We only use lazy source when no special encoding is specified because
     165             :     // the lazy source loader doesn't know the encoding.
     166          15 :     options.setSourceIsLazy(true);
     167          15 :     if (JS_IsGlobalObject(targetObj)) {
     168          14 :         return JS::Compile(cx, options, buf, len, script);
     169             :     }
     170           1 :     return JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
     171             : }
     172             : 
     173             : static bool
     174          33 : EvalScript(JSContext* cx,
     175             :            HandleObject targetObj,
     176             :            MutableHandleValue retval,
     177             :            nsIURI* uri,
     178             :            bool startupCache,
     179             :            bool preloadCache,
     180             :            MutableHandleScript script)
     181             : {
     182          33 :     if (JS_IsGlobalObject(targetObj)) {
     183          15 :         if (!JS::CloneAndExecuteScript(cx, script, retval)) {
     184           0 :             return false;
     185             :         }
     186             :     } else {
     187          36 :         JS::AutoObjectVector envChain(cx);
     188          18 :         if (!envChain.append(targetObj)) {
     189           0 :             return false;
     190             :         }
     191          18 :         if (!JS::CloneAndExecuteScript(cx, envChain, script, retval)) {
     192           0 :             return false;
     193             :         }
     194             :     }
     195             : 
     196          66 :     JSAutoCompartment rac(cx, targetObj);
     197          33 :     if (!JS_WrapValue(cx, retval)) {
     198           0 :         return false;
     199             :     }
     200             : 
     201          33 :     if (script && (startupCache || preloadCache)) {
     202          66 :         nsAutoCString cachePath;
     203          33 :         JSVersion version = JS_GetVersion(cx);
     204          33 :         cachePath.AppendPrintf("jssubloader/%d", version);
     205          33 :         PathifyURI(uri, cachePath);
     206             : 
     207          66 :         nsCString uriStr;
     208          33 :         if (preloadCache && NS_SUCCEEDED(uri->GetSpec(uriStr))) {
     209             :             // Note that, when called during startup, this will keep the
     210             :             // original JSScript object alive for an indefinite amount of time.
     211             :             // This has the side-effect of keeping the global that the script
     212             :             // was compiled for alive, too.
     213             :             //
     214             :             // For most startups, the global in question will be the
     215             :             // CompilationScope, since we pre-compile any scripts that were
     216             :             // needed during the last startup in that scope. But for startups
     217             :             // when a non-cached script is used (e.g., after add-on
     218             :             // installation), this may be a Sandbox global, which may be
     219             :             // nuked but held alive by the JSScript.
     220             :             //
     221             :             // In general, this isn't a problem, since add-on Sandboxes which
     222             :             // use the script preloader are not destroyed until add-on shutdown,
     223             :             // and when add-ons are uninstalled or upgraded, the preloader cache
     224             :             // is immediately flushed after shutdown. But it's possible to
     225             :             // disable and reenable an add-on without uninstalling it, leading
     226             :             // to cached scripts being held alive, and tied to nuked Sandbox
     227             :             // globals. Given the unusual circumstances required to trigger
     228             :             // this, it's not a major concern. But it should be kept in mind.
     229          33 :             ScriptPreloader::GetSingleton().NoteScript(uriStr, cachePath, script);
     230             :         }
     231             : 
     232          33 :         if (startupCache) {
     233          28 :             JSAutoCompartment ac(cx, script);
     234          14 :             WriteCachedScript(StartupCache::GetSingleton(), cachePath, cx, script);
     235             :         }
     236             :     }
     237             : 
     238          33 :     return true;
     239             : }
     240             : 
     241             : class AsyncScriptLoader : public nsIIncrementalStreamLoaderObserver
     242             : {
     243             : public:
     244             :     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     245             :     NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
     246             : 
     247           0 :     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
     248             : 
     249           0 :     AsyncScriptLoader(nsIChannel* aChannel, bool aWantReturnValue,
     250             :                       JSObject* aTargetObj, const nsAString& aCharset,
     251             :                       bool aCache, Promise* aPromise)
     252           0 :         : mChannel(aChannel)
     253             :         , mTargetObj(aTargetObj)
     254             :         , mPromise(aPromise)
     255             :         , mCharset(aCharset)
     256             :         , mWantReturnValue(aWantReturnValue)
     257           0 :         , mCache(aCache)
     258             :     {
     259             :         // Needed for the cycle collector to manage mTargetObj.
     260           0 :         mozilla::HoldJSObjects(this);
     261           0 :     }
     262             : 
     263             : private:
     264           0 :     virtual ~AsyncScriptLoader() {
     265           0 :         mozilla::DropJSObjects(this);
     266           0 :     }
     267             : 
     268             :     RefPtr<nsIChannel> mChannel;
     269             :     Heap<JSObject*> mTargetObj;
     270             :     RefPtr<Promise> mPromise;
     271             :     nsString mCharset;
     272             :     bool mWantReturnValue;
     273             :     bool mCache;
     274             : };
     275             : 
     276             : NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader)
     277             : 
     278           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader)
     279           0 :   NS_INTERFACE_MAP_ENTRY(nsIIncrementalStreamLoaderObserver)
     280           0 : NS_INTERFACE_MAP_END
     281             : 
     282           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader)
     283           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
     284           0 :   tmp->mTargetObj = nullptr;
     285           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     286             : 
     287           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader)
     288           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
     289           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     290             : 
     291           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader)
     292           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj)
     293           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     294             : 
     295           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader)
     296           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader)
     297             : 
     298             : class MOZ_STACK_CLASS AutoRejectPromise
     299             : {
     300             : public:
     301           0 :     AutoRejectPromise(AutoEntryScript& aAutoEntryScript,
     302             :                       Promise* aPromise,
     303             :                       nsIGlobalObject* aGlobalObject)
     304           0 :         : mAutoEntryScript(aAutoEntryScript)
     305             :         , mPromise(aPromise)
     306           0 :         , mGlobalObject(aGlobalObject) {}
     307             : 
     308           0 :     ~AutoRejectPromise() {
     309           0 :         if (mPromise) {
     310           0 :             JSContext* cx = mAutoEntryScript.cx();
     311           0 :             RootedValue rejectionValue(cx, JS::UndefinedValue());
     312           0 :             if (mAutoEntryScript.HasException()) {
     313           0 :                 Unused << mAutoEntryScript.PeekException(&rejectionValue);
     314             :             }
     315           0 :             mPromise->MaybeReject(cx, rejectionValue);
     316             :         }
     317           0 :     }
     318             : 
     319           0 :     void ResolvePromise(HandleValue aResolveValue) {
     320           0 :         mPromise->MaybeResolve(aResolveValue);
     321           0 :         mPromise = nullptr;
     322           0 :     }
     323             : 
     324             : private:
     325             :     AutoEntryScript& mAutoEntryScript;
     326             :     RefPtr<Promise> mPromise;
     327             :     nsCOMPtr<nsIGlobalObject> mGlobalObject;
     328             : };
     329             : 
     330             : NS_IMETHODIMP
     331           0 : AsyncScriptLoader::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
     332             :                                      nsISupports* aContext,
     333             :                                      uint32_t aDataLength,
     334             :                                      const uint8_t* aData,
     335             :                                      uint32_t *aConsumedData)
     336             : {
     337           0 :     return NS_OK;
     338             : }
     339             : 
     340             : NS_IMETHODIMP
     341           0 : AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
     342             :                                     nsISupports* aContext,
     343             :                                     nsresult aStatus,
     344             :                                     uint32_t aLength,
     345             :                                     const uint8_t* aBuf)
     346             : {
     347           0 :     nsCOMPtr<nsIURI> uri;
     348           0 :     mChannel->GetURI(getter_AddRefs(uri));
     349             : 
     350           0 :     nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(mTargetObj);
     351           0 :     AutoEntryScript aes(globalObject, "async loadSubScript");
     352           0 :     AutoRejectPromise autoPromise(aes, mPromise, globalObject);
     353           0 :     JSContext* cx = aes.cx();
     354             : 
     355           0 :     if (NS_FAILED(aStatus)) {
     356           0 :         ReportError(cx, "Unable to load script.", uri);
     357             :     }
     358             :     // Just notify that we are done with this load.
     359           0 :     NS_ENSURE_SUCCESS(aStatus, NS_OK);
     360             : 
     361           0 :     if (aLength == 0) {
     362           0 :         ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
     363           0 :         return NS_OK;
     364             :     }
     365             : 
     366           0 :     if (aLength > INT32_MAX) {
     367           0 :         ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
     368           0 :         return NS_OK;
     369             :     }
     370             : 
     371           0 :     RootedScript script(cx);
     372           0 :     nsAutoCString spec;
     373           0 :     nsresult rv = uri->GetSpec(spec);
     374           0 :     NS_ENSURE_SUCCESS(rv, rv);
     375             : 
     376           0 :     RootedObject targetObj(cx, mTargetObj);
     377             : 
     378           0 :     if (!PrepareScript(uri, cx, targetObj, spec.get(), mCharset,
     379             :                        reinterpret_cast<const char*>(aBuf), aLength,
     380           0 :                        mWantReturnValue, &script))
     381             :     {
     382           0 :         return NS_OK;
     383             :     }
     384             : 
     385           0 :     JS::Rooted<JS::Value> retval(cx);
     386           0 :     if (EvalScript(cx, targetObj, &retval, uri, mCache,
     387           0 :                    mCache && !mWantReturnValue,
     388             :                    &script)) {
     389           0 :         autoPromise.ResolvePromise(retval);
     390             :     }
     391             : 
     392           0 :     return NS_OK;
     393             : }
     394             : 
     395             : nsresult
     396           0 : mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri,
     397             :                                       HandleObject targetObj,
     398             :                                       const nsAString& charset,
     399             :                                       nsIIOService* serv,
     400             :                                       bool wantReturnValue,
     401             :                                       bool cache,
     402             :                                       MutableHandleValue retval)
     403             : {
     404           0 :     nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(targetObj);
     405           0 :     ErrorResult result;
     406             : 
     407           0 :     AutoJSAPI jsapi;
     408           0 :     if (NS_WARN_IF(!jsapi.Init(globalObject))) {
     409           0 :         return NS_ERROR_UNEXPECTED;
     410             :     }
     411             : 
     412           0 :     RefPtr<Promise> promise = Promise::Create(globalObject, result);
     413           0 :     if (result.Failed()) {
     414           0 :         return result.StealNSResult();
     415             :     }
     416             : 
     417           0 :     DebugOnly<bool> asJS = ToJSValue(jsapi.cx(), promise, retval);
     418           0 :     MOZ_ASSERT(asJS, "Should not fail to convert the promise to a JS value");
     419             : 
     420             :     // We create a channel and call SetContentType, to avoid expensive MIME type
     421             :     // lookups (bug 632490).
     422           0 :     nsCOMPtr<nsIChannel> channel;
     423             :     nsresult rv;
     424           0 :     rv = NS_NewChannel(getter_AddRefs(channel),
     425             :                        uri,
     426             :                        nsContentUtils::GetSystemPrincipal(),
     427             :                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     428             :                        nsIContentPolicy::TYPE_OTHER,
     429             :                        nullptr,  // aLoadGroup
     430             :                        nullptr,  // aCallbacks
     431             :                        nsIRequest::LOAD_NORMAL,
     432           0 :                        serv);
     433             : 
     434           0 :     if (!NS_SUCCEEDED(rv)) {
     435           0 :         return rv;
     436             :     }
     437             : 
     438           0 :     channel->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
     439             : 
     440             :     RefPtr<AsyncScriptLoader> loadObserver =
     441             :         new AsyncScriptLoader(channel,
     442             :                               wantReturnValue,
     443             :                               targetObj,
     444             :                               charset,
     445             :                               cache,
     446           0 :                               promise);
     447             : 
     448           0 :     nsCOMPtr<nsIIncrementalStreamLoader> loader;
     449           0 :     rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
     450           0 :     NS_ENSURE_SUCCESS(rv, rv);
     451             : 
     452           0 :     nsCOMPtr<nsIStreamListener> listener = loader.get();
     453           0 :     return channel->AsyncOpen2(listener);
     454             : }
     455             : 
     456             : bool
     457          15 : mozJSSubScriptLoader::ReadScript(nsIURI* uri,
     458             :                                  JSContext* cx,
     459             :                                  HandleObject targetObj,
     460             :                                  const nsAString& charset,
     461             :                                  const char* uriStr,
     462             :                                  nsIIOService* serv,
     463             :                                  bool wantReturnValue,
     464             :                                  MutableHandleScript script)
     465             : {
     466          15 :     script.set(nullptr);
     467             : 
     468             :     // We create a channel and call SetContentType, to avoid expensive MIME type
     469             :     // lookups (bug 632490).
     470          30 :     nsCOMPtr<nsIChannel> chan;
     471          30 :     nsCOMPtr<nsIInputStream> instream;
     472             :     nsresult rv;
     473          30 :     rv = NS_NewChannel(getter_AddRefs(chan),
     474             :                        uri,
     475             :                        nsContentUtils::GetSystemPrincipal(),
     476             :                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     477             :                        nsIContentPolicy::TYPE_OTHER,
     478             :                        nullptr,  // aLoadGroup
     479             :                        nullptr,  // aCallbacks
     480             :                        nsIRequest::LOAD_NORMAL,
     481          15 :                        serv);
     482             : 
     483          15 :     if (NS_SUCCEEDED(rv)) {
     484          15 :         chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
     485          15 :         rv = chan->Open2(getter_AddRefs(instream));
     486             :     }
     487             : 
     488          15 :     if (NS_FAILED(rv)) {
     489           0 :         ReportError(cx, LOAD_ERROR_NOSTREAM, uri);
     490           0 :         return false;
     491             :     }
     492             : 
     493          15 :     int64_t len = -1;
     494             : 
     495          15 :     rv = chan->GetContentLength(&len);
     496          15 :     if (NS_FAILED(rv) || len == -1) {
     497           0 :         ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
     498           0 :         return false;
     499             :     }
     500             : 
     501          15 :     if (len > INT32_MAX) {
     502           0 :         ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
     503           0 :         return false;
     504             :     }
     505             : 
     506          30 :     nsCString buf;
     507          15 :     rv = NS_ReadInputStreamToString(instream, buf, len);
     508          15 :     NS_ENSURE_SUCCESS(rv, false);
     509             : 
     510          15 :     return PrepareScript(uri, cx, targetObj, uriStr, charset,
     511             :                          buf.get(), len, wantReturnValue,
     512          15 :                          script);
     513             : }
     514             : 
     515             : NS_IMETHODIMP
     516          33 : mozJSSubScriptLoader::LoadSubScript(const nsAString& url,
     517             :                                     HandleValue target,
     518             :                                     const nsAString& charset,
     519             :                                     JSContext* cx,
     520             :                                     MutableHandleValue retval)
     521             : {
     522             :     /*
     523             :      * Loads a local url and evals it into the current cx
     524             :      * Synchronous (an async version would be cool too.)
     525             :      *   url: The url to load.  Must be local so that it can be loaded
     526             :      *        synchronously.
     527             :      *   targetObj: Optional object to eval the script onto (defaults to context
     528             :      *              global)
     529             :      *   charset: Optional character set to use for reading
     530             :      *   returns: Whatever jsval the script pointed to by the url returns.
     531             :      * Should ONLY (O N L Y !) be called from JavaScript code.
     532             :      */
     533          66 :     LoadSubScriptOptions options(cx);
     534          33 :     options.charset = charset;
     535          33 :     options.target = target.isObject() ? &target.toObject() : nullptr;
     536          66 :     return DoLoadSubScriptWithOptions(url, options, cx, retval);
     537             : }
     538             : 
     539             : 
     540             : NS_IMETHODIMP
     541           0 : mozJSSubScriptLoader::LoadSubScriptWithOptions(const nsAString& url,
     542             :                                                HandleValue optionsVal,
     543             :                                                JSContext* cx,
     544             :                                                MutableHandleValue retval)
     545             : {
     546           0 :     if (!optionsVal.isObject())
     547           0 :         return NS_ERROR_INVALID_ARG;
     548           0 :     LoadSubScriptOptions options(cx, &optionsVal.toObject());
     549           0 :     if (!options.Parse())
     550           0 :         return NS_ERROR_INVALID_ARG;
     551           0 :     return DoLoadSubScriptWithOptions(url, options, cx, retval);
     552             : }
     553             : 
     554             : nsresult
     555          33 : mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
     556             :                                                  LoadSubScriptOptions& options,
     557             :                                                  JSContext* cx,
     558             :                                                  MutableHandleValue retval)
     559             : {
     560          33 :     nsresult rv = NS_OK;
     561          66 :     RootedObject targetObj(cx);
     562          33 :     if (options.target) {
     563          33 :         targetObj = options.target;
     564             :     } else {
     565           0 :         mozJSComponentLoader* loader = mozJSComponentLoader::Get();
     566           0 :         loader->FindTargetObject(cx, &targetObj);
     567           0 :         MOZ_ASSERT(JS_IsGlobalObject(targetObj));
     568             :     }
     569             : 
     570          33 :     targetObj = JS_FindCompilationScope(cx, targetObj);
     571          33 :     if (!targetObj)
     572           0 :         return NS_ERROR_FAILURE;
     573             : 
     574             :     /* load up the url.  From here on, failures are reflected as ``custom''
     575             :      * js exceptions */
     576          66 :     nsCOMPtr<nsIURI> uri;
     577          66 :     nsAutoCString uriStr;
     578          66 :     nsAutoCString scheme;
     579             : 
     580             :     // Figure out who's calling us
     581          66 :     JS::AutoFilename filename;
     582          33 :     if (!JS::DescribeScriptedCaller(cx, &filename)) {
     583             :         // No scripted frame means we don't know who's calling, bail.
     584           0 :         return NS_ERROR_FAILURE;
     585             :     }
     586             : 
     587          66 :     JSAutoCompartment ac(cx, targetObj);
     588             : 
     589          66 :     nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
     590          33 :     if (!serv) {
     591           0 :         ReportError(cx, NS_LITERAL_CSTRING(LOAD_ERROR_NOSERVICE));
     592           0 :         return NS_OK;
     593             :     }
     594             : 
     595          66 :     const nsCString& asciiUrl = NS_LossyConvertUTF16toASCII(url);
     596          66 :     AUTO_PROFILER_LABEL_DYNAMIC(
     597             :         "mozJSSubScriptLoader::DoLoadSubScriptWithOptions", OTHER,
     598             :         asciiUrl.get());
     599             : 
     600             :     // Make sure to explicitly create the URI, since we'll need the
     601             :     // canonicalized spec.
     602          33 :     rv = NS_NewURI(getter_AddRefs(uri), asciiUrl.get(), nullptr, serv);
     603          33 :     if (NS_FAILED(rv)) {
     604           0 :         ReportError(cx, NS_LITERAL_CSTRING(LOAD_ERROR_NOURI));
     605           0 :         return NS_OK;
     606             :     }
     607             : 
     608          33 :     rv = uri->GetSpec(uriStr);
     609          33 :     if (NS_FAILED(rv)) {
     610           0 :         ReportError(cx, NS_LITERAL_CSTRING(LOAD_ERROR_NOSPEC));
     611           0 :         return NS_OK;
     612             :     }
     613             : 
     614          33 :     rv = uri->GetScheme(scheme);
     615          33 :     if (NS_FAILED(rv)) {
     616           0 :         ReportError(cx, LOAD_ERROR_NOSCHEME, uri);
     617           0 :         return NS_OK;
     618             :     }
     619             : 
     620          66 :     if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("app") &&
     621          33 :         !scheme.EqualsLiteral("blob")) {
     622             :         // This might be a URI to a local file, though!
     623          66 :         nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
     624          66 :         nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
     625          33 :         if (!fileURL) {
     626           0 :             ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL, uri);
     627           0 :             return NS_OK;
     628             :         }
     629             : 
     630             :         // For file URIs prepend the filename with the filename of the
     631             :         // calling script, and " -> ". See bug 418356.
     632          66 :         nsAutoCString tmp(filename.get());
     633          33 :         tmp.AppendLiteral(" -> ");
     634          33 :         tmp.Append(uriStr);
     635             : 
     636          33 :         uriStr = tmp;
     637             :     }
     638             : 
     639             :     // Suppress caching if we're compiling as content or if we're loading a
     640             :     // blob: URI.
     641          33 :     bool ignoreCache = options.ignoreCache
     642          33 :         || !GetObjectPrincipal(targetObj)->GetIsSystemPrincipal()
     643          66 :         || scheme.EqualsLiteral("blob");
     644          33 :     StartupCache* cache = ignoreCache ? nullptr : StartupCache::GetSingleton();
     645             : 
     646          33 :     JSVersion version = JS_GetVersion(cx);
     647          66 :     nsAutoCString cachePath;
     648          33 :     cachePath.AppendPrintf("jssubloader/%d", version);
     649          33 :     PathifyURI(uri, cachePath);
     650             : 
     651          66 :     RootedScript script(cx);
     652          33 :     if (!options.ignoreCache) {
     653          33 :         if (!options.wantReturnValue)
     654          33 :             script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
     655          33 :         if (!script && cache)
     656          14 :             rv = ReadCachedScript(cache, cachePath, cx, &script);
     657          33 :         if (NS_FAILED(rv) || !script) {
     658             :             // ReadCachedScript may have set a pending exception.
     659          15 :             JS_ClearPendingException(cx);
     660             :         }
     661             :     }
     662             : 
     663             :     // If we are doing an async load, trigger it and bail out.
     664          33 :     if (!script && options.async) {
     665           0 :         return ReadScriptAsync(uri, targetObj, options.charset, serv,
     666           0 :                                options.wantReturnValue, !!cache, retval);
     667             :     }
     668             : 
     669          33 :     if (script) {
     670             :         // |script| came from the cache, so don't bother writing it
     671             :         // |back there.
     672          18 :         cache = nullptr;
     673          45 :     } else if (!ReadScript(uri, cx, targetObj, options.charset,
     674          15 :                         static_cast<const char*>(uriStr.get()), serv,
     675          15 :                         options.wantReturnValue, &script)) {
     676           0 :         return NS_OK;
     677             :     }
     678             : 
     679          66 :     Unused << EvalScript(cx, targetObj, retval, uri, !!cache,
     680          33 :                          !ignoreCache && !options.wantReturnValue,
     681             :                          &script);
     682          33 :     return NS_OK;
     683             : }
     684             : 
     685             : /**
     686             :   * Let us compile scripts from a URI off the main thread.
     687             :   */
     688             : 
     689             : class ScriptPrecompiler : public nsIIncrementalStreamLoaderObserver
     690             : {
     691             : public:
     692             :     NS_DECL_ISUPPORTS
     693             :     NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
     694             : 
     695           0 :     ScriptPrecompiler(nsIObserver* aObserver,
     696             :                       nsIPrincipal* aPrincipal,
     697             :                       nsIChannel* aChannel)
     698           0 :         : mObserver(aObserver)
     699             :         , mPrincipal(aPrincipal)
     700             :         , mChannel(aChannel)
     701             :         , mScriptBuf(nullptr)
     702           0 :         , mScriptLength(0)
     703           0 :     {}
     704             : 
     705             :     static void OffThreadCallback(void* aToken, void* aData);
     706             : 
     707             :     /* Sends the "done" notification back. Main thread only. */
     708             :     void SendObserverNotification();
     709             : 
     710             : private:
     711           0 :     virtual ~ScriptPrecompiler()
     712           0 :     {
     713           0 :       if (mScriptBuf) {
     714           0 :         js_free(mScriptBuf);
     715             :       }
     716           0 :     }
     717             : 
     718             :     RefPtr<nsIObserver> mObserver;
     719             :     RefPtr<nsIPrincipal> mPrincipal;
     720             :     RefPtr<nsIChannel> mChannel;
     721             :     char16_t* mScriptBuf;
     722             :     size_t mScriptLength;
     723             : };
     724             : 
     725           0 : NS_IMPL_ISUPPORTS(ScriptPrecompiler, nsIIncrementalStreamLoaderObserver);
     726             : 
     727           0 : class NotifyPrecompilationCompleteRunnable : public Runnable
     728             : {
     729             : public:
     730             :     NS_DECL_NSIRUNNABLE
     731             : 
     732           0 :     explicit NotifyPrecompilationCompleteRunnable(
     733             :       ScriptPrecompiler* aPrecompiler)
     734           0 :       : mozilla::Runnable("NotifyPrecompilationCompleteRunnable")
     735             :       , mPrecompiler(aPrecompiler)
     736           0 :       , mToken(nullptr)
     737           0 :     {}
     738             : 
     739           0 :     void SetToken(void* aToken) {
     740           0 :         MOZ_ASSERT(aToken && !mToken);
     741           0 :         mToken = aToken;
     742           0 :     }
     743             : 
     744             : protected:
     745             :     RefPtr<ScriptPrecompiler> mPrecompiler;
     746             :     void* mToken;
     747             : };
     748             : 
     749             : /* RAII helper class to send observer notifications */
     750             : class AutoSendObserverNotification {
     751             : public:
     752           0 :     explicit AutoSendObserverNotification(ScriptPrecompiler* aPrecompiler)
     753           0 :         : mPrecompiler(aPrecompiler)
     754           0 :     {}
     755             : 
     756           0 :     ~AutoSendObserverNotification() {
     757           0 :         if (mPrecompiler) {
     758           0 :             mPrecompiler->SendObserverNotification();
     759             :         }
     760           0 :     }
     761             : 
     762           0 :     void Disarm() {
     763           0 :         mPrecompiler = nullptr;
     764           0 :     }
     765             : 
     766             : private:
     767             :     ScriptPrecompiler* mPrecompiler;
     768             : };
     769             : 
     770             : NS_IMETHODIMP
     771           0 : NotifyPrecompilationCompleteRunnable::Run(void)
     772             : {
     773           0 :     MOZ_ASSERT(NS_IsMainThread());
     774           0 :     MOZ_ASSERT(mPrecompiler);
     775             : 
     776           0 :     AutoSendObserverNotification notifier(mPrecompiler);
     777             : 
     778           0 :     if (mToken) {
     779           0 :         JSContext* cx = XPCJSContext::Get()->Context();
     780           0 :         NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
     781           0 :         JS::CancelOffThreadScript(cx, mToken);
     782             :     }
     783             : 
     784           0 :     return NS_OK;
     785             : }
     786             : 
     787             : NS_IMETHODIMP
     788           0 : ScriptPrecompiler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
     789             :                                      nsISupports* aContext,
     790             :                                      uint32_t aDataLength,
     791             :                                      const uint8_t* aData,
     792             :                                      uint32_t *aConsumedData)
     793             : {
     794           0 :   return NS_OK;
     795             : }
     796             : 
     797             : NS_IMETHODIMP
     798           0 : ScriptPrecompiler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
     799             :                                     nsISupports* aContext,
     800             :                                     nsresult aStatus,
     801             :                                     uint32_t aLength,
     802             :                                     const uint8_t* aString)
     803             : {
     804           0 :     AutoSendObserverNotification notifier(this);
     805             : 
     806             :     // Just notify that we are done with this load.
     807           0 :     NS_ENSURE_SUCCESS(aStatus, NS_OK);
     808             : 
     809             :     // Convert data to char16_t* and prepare to call CompileOffThread.
     810           0 :     nsAutoString hintCharset;
     811             :     nsresult rv =
     812           0 :         ScriptLoader::ConvertToUTF16(mChannel, aString, aLength,
     813             :                                      hintCharset, nullptr,
     814           0 :                                      mScriptBuf, mScriptLength);
     815             : 
     816           0 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     817             : 
     818             :     // Our goal is to cache persistently the compiled script and to avoid quota
     819             :     // checks. Since the caching mechanism decide the persistence type based on
     820             :     // the principal, we create a new global with the app's principal.
     821             :     // We then enter its compartment to compile with its principal.
     822           0 :     AutoSafeJSContext cx;
     823           0 :     RootedValue v(cx);
     824           0 :     SandboxOptions sandboxOptions;
     825           0 :     sandboxOptions.sandboxName.AssignASCII("asm.js precompilation");
     826           0 :     sandboxOptions.invisibleToDebugger = true;
     827           0 :     sandboxOptions.discardSource = true;
     828           0 :     rv = CreateSandboxObject(cx, &v, mPrincipal, sandboxOptions);
     829           0 :     NS_ENSURE_SUCCESS(rv, NS_OK);
     830             : 
     831           0 :     JSAutoCompartment ac(cx, js::UncheckedUnwrap(&v.toObject()));
     832             : 
     833           0 :     JS::CompileOptions options(cx, JSVERSION_DEFAULT);
     834           0 :     options.forceAsync = true;
     835             : 
     836           0 :     nsCOMPtr<nsIURI> uri;
     837           0 :     mChannel->GetURI(getter_AddRefs(uri));
     838           0 :     nsAutoCString spec;
     839           0 :     uri->GetSpec(spec);
     840           0 :     options.setFile(spec.get());
     841             : 
     842           0 :     if (!JS::CanCompileOffThread(cx, options, mScriptLength)) {
     843           0 :         NS_WARNING("Can't compile script off thread!");
     844           0 :         return NS_OK;
     845             :     }
     846             : 
     847             :     RefPtr<NotifyPrecompilationCompleteRunnable> runnable =
     848           0 :         new NotifyPrecompilationCompleteRunnable(this);
     849             : 
     850           0 :     if (!JS::CompileOffThread(cx, options,
     851           0 :                               mScriptBuf, mScriptLength,
     852             :                               OffThreadCallback,
     853           0 :                               static_cast<void*>(runnable))) {
     854           0 :         NS_WARNING("Failed to compile script off thread!");
     855           0 :         return NS_OK;
     856             :     }
     857             : 
     858           0 :     Unused << runnable.forget();
     859           0 :     notifier.Disarm();
     860             : 
     861           0 :     return NS_OK;
     862             : }
     863             : 
     864             : /* static */
     865             : void
     866           0 : ScriptPrecompiler::OffThreadCallback(void* aToken, void* aData)
     867             : {
     868             :     RefPtr<NotifyPrecompilationCompleteRunnable> runnable =
     869           0 :         dont_AddRef(static_cast<NotifyPrecompilationCompleteRunnable*>(aData));
     870           0 :     runnable->SetToken(aToken);
     871             : 
     872           0 :     NS_DispatchToMainThread(runnable);
     873           0 : }
     874             : 
     875             : void
     876           0 : ScriptPrecompiler::SendObserverNotification()
     877             : {
     878           0 :     MOZ_ASSERT(mChannel && mObserver);
     879           0 :     MOZ_ASSERT(NS_IsMainThread());
     880             : 
     881           0 :     nsCOMPtr<nsIURI> uri;
     882           0 :     mChannel->GetURI(getter_AddRefs(uri));
     883           0 :     mObserver->Observe(uri, "script-precompiled", nullptr);
     884           0 : }
     885             : 
     886             : NS_IMETHODIMP
     887           0 : mozJSSubScriptLoader::PrecompileScript(nsIURI* aURI,
     888             :                                        nsIPrincipal* aPrincipal,
     889             :                                        nsIObserver* aObserver)
     890             : {
     891           0 :     nsCOMPtr<nsIChannel> channel;
     892           0 :     nsresult rv = NS_NewChannel(getter_AddRefs(channel),
     893             :                                 aURI,
     894             :                                 nsContentUtils::GetSystemPrincipal(),
     895             :                                 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     896           0 :                                 nsIContentPolicy::TYPE_OTHER);
     897             : 
     898           0 :     NS_ENSURE_SUCCESS(rv, rv);
     899             : 
     900             :     RefPtr<ScriptPrecompiler> loadObserver =
     901           0 :         new ScriptPrecompiler(aObserver, aPrincipal, channel);
     902             : 
     903           0 :     nsCOMPtr<nsIIncrementalStreamLoader> loader;
     904           0 :     rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
     905           0 :     NS_ENSURE_SUCCESS(rv, rv);
     906             : 
     907           0 :     nsCOMPtr<nsIStreamListener> listener = loader.get();
     908           0 :     rv = channel->AsyncOpen2(listener);
     909           0 :     NS_ENSURE_SUCCESS(rv, rv);
     910             : 
     911           0 :     return NS_OK;
     912             : }

Generated by: LCOV version 1.13