LCOV - code coverage report
Current view: top level - dom/worklet - Worklet.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 195 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 27 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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 "Worklet.h"
       8             : #include "AudioWorkletGlobalScope.h"
       9             : #include "PaintWorkletGlobalScope.h"
      10             : 
      11             : #include "mozilla/dom/WorkletBinding.h"
      12             : #include "mozilla/dom/BlobBinding.h"
      13             : #include "mozilla/dom/Fetch.h"
      14             : #include "mozilla/dom/PromiseNativeHandler.h"
      15             : #include "mozilla/dom/RegisterWorkletBindings.h"
      16             : #include "mozilla/dom/Response.h"
      17             : #include "mozilla/dom/ScriptSettings.h"
      18             : #include "mozilla/dom/ScriptLoader.h"
      19             : #include "nsIInputStreamPump.h"
      20             : #include "nsIThreadRetargetableRequest.h"
      21             : #include "nsNetUtil.h"
      22             : #include "xpcprivate.h"
      23             : 
      24             : namespace mozilla {
      25             : namespace dom {
      26             : 
      27             : // ---------------------------------------------------------------------------
      28             : // WorkletFetchHandler
      29             : 
      30             : class WorkletFetchHandler : public PromiseNativeHandler
      31             :                           , public nsIStreamLoaderObserver
      32             : {
      33             : public:
      34             :   NS_DECL_ISUPPORTS
      35             : 
      36             :   static already_AddRefed<Promise>
      37           0 :   Fetch(Worklet* aWorklet, const nsAString& aModuleURL, CallerType aCallerType,
      38             :         ErrorResult& aRv)
      39             :   {
      40           0 :     MOZ_ASSERT(aWorklet);
      41             : 
      42             :     nsCOMPtr<nsIGlobalObject> global =
      43           0 :       do_QueryInterface(aWorklet->GetParentObject());
      44           0 :     MOZ_ASSERT(global);
      45             : 
      46           0 :     RefPtr<Promise> promise = Promise::Create(global, aRv);
      47           0 :     if (NS_WARN_IF(aRv.Failed())) {
      48           0 :       return nullptr;
      49             :     }
      50             : 
      51           0 :     nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject();
      52           0 :     MOZ_ASSERT(window);
      53             : 
      54           0 :     nsCOMPtr<nsIDocument> doc;
      55           0 :     doc = window->GetExtantDoc();
      56           0 :     if (!doc) {
      57           0 :       promise->MaybeReject(NS_ERROR_FAILURE);
      58           0 :       return promise.forget();
      59             :     }
      60             : 
      61           0 :     nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
      62           0 :     nsCOMPtr<nsIURI> resolvedURI;
      63           0 :     nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr, baseURI);
      64           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
      65           0 :       promise->MaybeReject(rv);
      66           0 :       return promise.forget();
      67             :     }
      68             : 
      69           0 :     nsAutoCString spec;
      70           0 :     rv = resolvedURI->GetSpec(spec);
      71           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
      72           0 :       promise->MaybeReject(rv);
      73           0 :       return promise.forget();
      74             :     }
      75             : 
      76             :     // Maybe we already have an handler for this URI
      77             :     {
      78           0 :       WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec);
      79           0 :       if (handler) {
      80           0 :         handler->AddPromise(promise);
      81           0 :         return promise.forget();
      82             :       }
      83             :     }
      84             : 
      85           0 :     RequestOrUSVString request;
      86           0 :     request.SetAsUSVString().Rebind(aModuleURL.Data(), aModuleURL.Length());
      87             : 
      88           0 :     RequestInit init;
      89             : 
      90             :     RefPtr<Promise> fetchPromise =
      91           0 :       FetchRequest(global, request, init, aCallerType, aRv);
      92           0 :     if (NS_WARN_IF(aRv.Failed())) {
      93           0 :       promise->MaybeReject(aRv);
      94           0 :       return promise.forget();
      95             :     }
      96             : 
      97             :     RefPtr<WorkletFetchHandler> handler =
      98           0 :       new WorkletFetchHandler(aWorklet, aModuleURL, promise);
      99           0 :     fetchPromise->AppendNativeHandler(handler);
     100             : 
     101           0 :     aWorklet->AddImportFetchHandler(spec, handler);
     102           0 :     return promise.forget();
     103             :   }
     104             : 
     105             :   virtual void
     106           0 :   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
     107             :   {
     108           0 :     if (!aValue.isObject()) {
     109           0 :       RejectPromises(NS_ERROR_FAILURE);
     110           0 :       return;
     111             :     }
     112             : 
     113           0 :     RefPtr<Response> response;
     114           0 :     nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
     115           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     116           0 :       RejectPromises(NS_ERROR_FAILURE);
     117           0 :       return;
     118             :     }
     119             : 
     120           0 :     if (!response->Ok()) {
     121           0 :       RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
     122           0 :       return;
     123             :     }
     124             : 
     125           0 :     nsCOMPtr<nsIInputStream> inputStream;
     126           0 :     response->GetBody(getter_AddRefs(inputStream));
     127           0 :     if (!inputStream) {
     128           0 :       RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
     129           0 :       return;
     130             :     }
     131             : 
     132           0 :     nsCOMPtr<nsIInputStreamPump> pump;
     133           0 :     rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream);
     134           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     135           0 :       RejectPromises(rv);
     136           0 :       return;
     137             :     }
     138             : 
     139           0 :     nsCOMPtr<nsIStreamLoader> loader;
     140           0 :     rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
     141           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     142           0 :       RejectPromises(rv);
     143           0 :       return;
     144             :     }
     145             : 
     146           0 :     rv = pump->AsyncRead(loader, nullptr);
     147           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     148           0 :       RejectPromises(rv);
     149           0 :       return;
     150             :     }
     151             : 
     152           0 :     nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
     153           0 :     if (rr) {
     154             :       nsCOMPtr<nsIEventTarget> sts =
     155           0 :         do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
     156           0 :       rv = rr->RetargetDeliveryTo(sts);
     157           0 :       if (NS_FAILED(rv)) {
     158           0 :         NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
     159             :       }
     160             :     }
     161             :   }
     162             : 
     163             :   NS_IMETHOD
     164           0 :   OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
     165             :                    nsresult aStatus, uint32_t aStringLen,
     166             :                    const uint8_t* aString) override
     167             :   {
     168           0 :     MOZ_ASSERT(NS_IsMainThread());
     169             : 
     170           0 :     if (NS_FAILED(aStatus)) {
     171           0 :       RejectPromises(aStatus);
     172           0 :       return NS_OK;
     173             :     }
     174             : 
     175             :     char16_t* scriptTextBuf;
     176             :     size_t scriptTextLength;
     177             :     nsresult rv =
     178           0 :       ScriptLoader::ConvertToUTF16(nullptr, aString, aStringLen,
     179           0 :                                    NS_LITERAL_STRING("UTF-8"), nullptr,
     180           0 :                                    scriptTextBuf, scriptTextLength);
     181           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     182           0 :       RejectPromises(rv);
     183           0 :       return NS_OK;
     184             :     }
     185             : 
     186             :     // Moving the ownership of the buffer
     187             :     JS::SourceBufferHolder buffer(scriptTextBuf, scriptTextLength,
     188           0 :                                   JS::SourceBufferHolder::GiveOwnership);
     189             : 
     190           0 :     AutoJSAPI jsapi;
     191           0 :     jsapi.Init();
     192             : 
     193             :     RefPtr<WorkletGlobalScope> globalScope =
     194           0 :       mWorklet->GetOrCreateGlobalScope(jsapi.cx());
     195           0 :     MOZ_ASSERT(globalScope);
     196             : 
     197           0 :     AutoEntryScript aes(globalScope, "Worklet");
     198           0 :     JSContext* cx = aes.cx();
     199             : 
     200           0 :     JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
     201             : 
     202           0 :     (void) new XPCWrappedNativeScope(cx, globalObj);
     203             : 
     204           0 :     JS::CompileOptions compileOptions(cx);
     205           0 :     compileOptions.setIntroductionType("Worklet");
     206           0 :     compileOptions.setFileAndLine(NS_ConvertUTF16toUTF8(mURL).get(), 0);
     207           0 :     compileOptions.setVersion(JSVERSION_DEFAULT);
     208           0 :     compileOptions.setIsRunOnce(true);
     209           0 :     compileOptions.setNoScriptRval(true);
     210             : 
     211           0 :     JSAutoCompartment comp(cx, globalObj);
     212             : 
     213           0 :     JS::Rooted<JS::Value> unused(cx);
     214           0 :     if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
     215           0 :       ErrorResult error;
     216           0 :       error.MightThrowJSException();
     217           0 :       error.StealExceptionFromJSContext(cx);
     218           0 :       RejectPromises(error.StealNSResult());
     219           0 :       return NS_OK;
     220             :     }
     221             : 
     222             :     // All done.
     223           0 :     ResolvePromises();
     224           0 :     return NS_OK;
     225             :   }
     226             : 
     227             :   virtual void
     228           0 :   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
     229             :   {
     230           0 :     RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
     231           0 :   }
     232             : 
     233             : private:
     234           0 :   WorkletFetchHandler(Worklet* aWorklet, const nsAString& aURL,
     235             :                       Promise* aPromise)
     236           0 :     : mWorklet(aWorklet)
     237             :     , mStatus(ePending)
     238             :     , mErrorStatus(NS_OK)
     239           0 :     , mURL(aURL)
     240             :   {
     241           0 :     MOZ_ASSERT(aWorklet);
     242           0 :     MOZ_ASSERT(aPromise);
     243             : 
     244           0 :     mPromises.AppendElement(aPromise);
     245           0 :   }
     246             : 
     247           0 :   ~WorkletFetchHandler()
     248           0 :   {}
     249             : 
     250             :   void
     251           0 :   AddPromise(Promise* aPromise)
     252             :   {
     253           0 :     MOZ_ASSERT(aPromise);
     254             : 
     255           0 :     switch (mStatus) {
     256             :       case ePending:
     257           0 :         mPromises.AppendElement(aPromise);
     258           0 :         return;
     259             : 
     260             :       case eRejected:
     261           0 :         MOZ_ASSERT(NS_FAILED(mErrorStatus));
     262           0 :         aPromise->MaybeReject(mErrorStatus);
     263           0 :         return;
     264             : 
     265             :       case eResolved:
     266           0 :         aPromise->MaybeResolveWithUndefined();
     267           0 :         return;
     268             :     }
     269             :   }
     270             : 
     271             :   void
     272           0 :   RejectPromises(nsresult aResult)
     273             :   {
     274           0 :     MOZ_ASSERT(mStatus == ePending);
     275           0 :     MOZ_ASSERT(NS_FAILED(aResult));
     276             : 
     277           0 :     for (uint32_t i = 0; i < mPromises.Length(); ++i) {
     278           0 :       mPromises[i]->MaybeReject(aResult);
     279             :     }
     280           0 :     mPromises.Clear();
     281             : 
     282           0 :     mStatus = eRejected;
     283           0 :     mErrorStatus = aResult;
     284           0 :     mWorklet = nullptr;
     285           0 :   }
     286             : 
     287             :   void
     288           0 :   ResolvePromises()
     289             :   {
     290           0 :     MOZ_ASSERT(mStatus == ePending);
     291             : 
     292           0 :     for (uint32_t i = 0; i < mPromises.Length(); ++i) {
     293           0 :       mPromises[i]->MaybeResolveWithUndefined();
     294             :     }
     295           0 :     mPromises.Clear();
     296             : 
     297           0 :     mStatus = eResolved;
     298           0 :     mWorklet = nullptr;
     299           0 :   }
     300             : 
     301             :   RefPtr<Worklet> mWorklet;
     302             :   nsTArray<RefPtr<Promise>> mPromises;
     303             : 
     304             :   enum {
     305             :     ePending,
     306             :     eRejected,
     307             :     eResolved
     308             :   } mStatus;
     309             : 
     310             :   nsresult mErrorStatus;
     311             : 
     312             :   nsString mURL;
     313             : };
     314             : 
     315           0 : NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
     316             : 
     317             : // ---------------------------------------------------------------------------
     318             : // Worklet
     319             : 
     320           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mWindow, mScope)
     321           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
     322           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet)
     323             : 
     324           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worklet)
     325           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     326           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     327           0 : NS_INTERFACE_MAP_END
     328             : 
     329           0 : Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
     330           0 :                  WorkletType aWorkletType)
     331             :   : mWindow(aWindow)
     332             :   , mPrincipal(aPrincipal)
     333           0 :   , mWorkletType(aWorkletType)
     334             : {
     335           0 :   MOZ_ASSERT(aWindow);
     336           0 :   MOZ_ASSERT(aPrincipal);
     337             : 
     338             : #ifdef RELEASE_OR_BETA
     339             :   MOZ_CRASH("This code should not go to release/beta yet!");
     340             : #endif
     341           0 : }
     342             : 
     343           0 : Worklet::~Worklet()
     344           0 : {}
     345             : 
     346             : JSObject*
     347           0 : Worklet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     348             : {
     349           0 :   return WorkletBinding::Wrap(aCx, this, aGivenProto);
     350             : }
     351             : 
     352             : already_AddRefed<Promise>
     353           0 : Worklet::Import(const nsAString& aModuleURL, CallerType aCallerType,
     354             :                 ErrorResult& aRv)
     355             : {
     356           0 :   return WorkletFetchHandler::Fetch(this, aModuleURL, aCallerType, aRv);
     357             : }
     358             : 
     359             : WorkletGlobalScope*
     360           0 : Worklet::GetOrCreateGlobalScope(JSContext* aCx)
     361             : {
     362           0 :   if (!mScope) {
     363           0 :     switch (mWorkletType) {
     364             :       case eAudioWorklet:
     365           0 :         mScope = new AudioWorkletGlobalScope(mWindow);
     366           0 :         break;
     367             :       case ePaintWorklet:
     368           0 :         mScope = new PaintWorkletGlobalScope(mWindow);
     369           0 :         break;
     370             :     }
     371             : 
     372           0 :     JS::Rooted<JSObject*> global(aCx);
     373           0 :     NS_ENSURE_TRUE(mScope->WrapGlobalObject(aCx, mPrincipal, &global), nullptr);
     374             : 
     375           0 :     JSAutoCompartment ac(aCx, global);
     376             : 
     377             :     // Init Web IDL bindings
     378           0 :     if (!RegisterWorkletBindings(aCx, global)) {
     379           0 :       mScope = nullptr;
     380           0 :       return nullptr;
     381             :     }
     382             : 
     383           0 :     JS_FireOnNewGlobalObject(aCx, global);
     384             :   }
     385             : 
     386           0 :   return mScope;
     387             : }
     388             : 
     389             : WorkletFetchHandler*
     390           0 : Worklet::GetImportFetchHandler(const nsACString& aURI)
     391             : {
     392           0 :   return mImportHandlers.GetWeak(aURI);
     393             : }
     394             : 
     395             : void
     396           0 : Worklet::AddImportFetchHandler(const nsACString& aURI,
     397             :                                WorkletFetchHandler* aHandler)
     398             : {
     399           0 :   MOZ_ASSERT(aHandler);
     400           0 :   MOZ_ASSERT(!mImportHandlers.GetWeak(aURI));
     401             : 
     402           0 :   mImportHandlers.Put(aURI, aHandler);
     403           0 : }
     404             : 
     405             : } // dom namespace
     406             : } // mozilla namespace

Generated by: LCOV version 1.13