123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- #include "ServiceWorkerWindowClient.h"
- #include "js/Value.h"
- #include "mozilla/Mutex.h"
- #include "mozilla/dom/ClientBinding.h"
- #include "mozilla/dom/Promise.h"
- #include "mozilla/dom/PromiseWorkerProxy.h"
- #include "mozilla/UniquePtr.h"
- #include "nsContentUtils.h"
- #include "nsGlobalWindow.h"
- #include "nsIDocShell.h"
- #include "nsIDocShellLoadInfo.h"
- #include "nsIDocument.h"
- #include "nsIGlobalObject.h"
- #include "nsIInterfaceRequestor.h"
- #include "nsIPrincipal.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsIWebNavigation.h"
- #include "nsIWebProgress.h"
- #include "nsIWebProgressListener.h"
- #include "nsNetUtil.h"
- #include "nsString.h"
- #include "nsWeakReference.h"
- #include "ServiceWorker.h"
- #include "ServiceWorkerInfo.h"
- #include "ServiceWorkerManager.h"
- #include "ServiceWorkerPrivate.h"
- #include "WorkerPrivate.h"
- #include "WorkerScope.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- using namespace mozilla::dom::workers;
- using mozilla::UniquePtr;
- JSObject*
- ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return WindowClientBinding::Wrap(aCx, this, aGivenProto);
- }
- namespace {
- class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
- {
- RefPtr<PromiseWorkerProxy> mPromiseProxy;
- UniquePtr<ServiceWorkerClientInfo> mClientInfo;
- nsresult mRv;
- public:
- // Passing a null clientInfo will resolve the promise with a null value.
- ResolveOrRejectPromiseRunnable(
- WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy,
- UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
- : WorkerRunnable(aWorkerPrivate)
- , mPromiseProxy(aPromiseProxy)
- , mClientInfo(Move(aClientInfo))
- , mRv(NS_OK)
- {
- AssertIsOnMainThread();
- }
- // Reject the promise with passed nsresult.
- ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
- PromiseWorkerProxy* aPromiseProxy,
- nsresult aRv)
- : WorkerRunnable(aWorkerPrivate)
- , mPromiseProxy(aPromiseProxy)
- , mClientInfo(nullptr)
- , mRv(aRv)
- {
- MOZ_ASSERT(NS_FAILED(aRv));
- AssertIsOnMainThread();
- }
- bool
- WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
- {
- MOZ_ASSERT(aWorkerPrivate);
- aWorkerPrivate->AssertIsOnWorkerThread();
- RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
- MOZ_ASSERT(promise);
- if (NS_WARN_IF(NS_FAILED(mRv))) {
- promise->MaybeReject(mRv);
- } else if (mClientInfo) {
- RefPtr<ServiceWorkerWindowClient> client =
- new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
- promise->MaybeResolve(client);
- } else {
- promise->MaybeResolve(JS::NullHandleValue);
- }
- // Release the reference on the worker thread.
- mPromiseProxy->CleanUp();
- return true;
- }
- };
- class ClientFocusRunnable final : public Runnable
- {
- uint64_t mWindowId;
- RefPtr<PromiseWorkerProxy> mPromiseProxy;
- public:
- ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
- : mWindowId(aWindowId)
- , mPromiseProxy(aPromiseProxy)
- {
- MOZ_ASSERT(mPromiseProxy);
- }
- NS_IMETHOD
- Run() override
- {
- AssertIsOnMainThread();
- nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
- UniquePtr<ServiceWorkerClientInfo> clientInfo;
- if (window) {
- nsCOMPtr<nsIDocument> doc = window->GetDocument();
- if (doc) {
- nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
- clientInfo.reset(new ServiceWorkerClientInfo(doc));
- }
- }
- DispatchResult(Move(clientInfo));
- return NS_OK;
- }
- private:
- void
- DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
- {
- AssertIsOnMainThread();
- MutexAutoLock lock(mPromiseProxy->Lock());
- if (mPromiseProxy->CleanedUp()) {
- return;
- }
- RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
- if (aClientInfo) {
- resolveRunnable = new ResolveOrRejectPromiseRunnable(
- mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, Move(aClientInfo));
- } else {
- resolveRunnable = new ResolveOrRejectPromiseRunnable(
- mPromiseProxy->GetWorkerPrivate(), mPromiseProxy,
- NS_ERROR_DOM_INVALID_ACCESS_ERR);
- }
- resolveRunnable->Dispatch();
- }
- };
- } // namespace
- already_AddRefed<Promise>
- ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
- {
- WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
- MOZ_ASSERT(workerPrivate);
- workerPrivate->AssertIsOnWorkerThread();
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- RefPtr<Promise> promise = Promise::Create(global, aRv);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
- }
- if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
- RefPtr<PromiseWorkerProxy> promiseProxy =
- PromiseWorkerProxy::Create(workerPrivate, promise);
- if (promiseProxy) {
- RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
- promiseProxy);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
- } else {
- promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
- }
- } else {
- promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
- }
- return promise.forget();
- }
- class WebProgressListener final : public nsIWebProgressListener,
- public nsSupportsWeakReference
- {
- public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener,
- nsIWebProgressListener)
- WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
- ServiceWorkerPrivate* aServiceWorkerPrivate,
- nsPIDOMWindowOuter* aWindow, nsIURI* aBaseURI)
- : mPromiseProxy(aPromiseProxy)
- , mServiceWorkerPrivate(aServiceWorkerPrivate)
- , mWindow(aWindow)
- , mBaseURI(aBaseURI)
- {
- MOZ_ASSERT(aPromiseProxy);
- MOZ_ASSERT(aServiceWorkerPrivate);
- MOZ_ASSERT(aWindow);
- MOZ_ASSERT(aWindow->IsOuterWindow());
- MOZ_ASSERT(aBaseURI);
- AssertIsOnMainThread();
- mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
- }
- NS_IMETHOD
- OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
- uint32_t aStateFlags, nsresult aStatus) override
- {
- if (!(aStateFlags & STATE_IS_DOCUMENT) ||
- !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
- return NS_OK;
- }
- // This is safe because our caller holds a strong ref.
- mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
- aWebProgress->RemoveProgressListener(this);
- WorkerPrivate* workerPrivate;
- {
- MutexAutoLock lock(mPromiseProxy->Lock());
- if (mPromiseProxy->CleanedUp()) {
- return NS_OK;
- }
- workerPrivate = mPromiseProxy->GetWorkerPrivate();
- }
- nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
- RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
- UniquePtr<ServiceWorkerClientInfo> clientInfo;
- if (!doc) {
- resolveRunnable = new ResolveOrRejectPromiseRunnable(
- workerPrivate, mPromiseProxy, NS_ERROR_TYPE_ERR);
- resolveRunnable->Dispatch();
- return NS_OK;
- }
- // Check same origin.
- nsCOMPtr<nsIScriptSecurityManager> securityManager =
- nsContentUtils::GetSecurityManager();
- nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
- mBaseURI, false);
- if (NS_SUCCEEDED(rv)) {
- nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
- clientInfo.reset(new ServiceWorkerClientInfo(doc));
- }
- resolveRunnable = new ResolveOrRejectPromiseRunnable(
- workerPrivate, mPromiseProxy, Move(clientInfo));
- resolveRunnable->Dispatch();
- return NS_OK;
- }
- NS_IMETHOD
- OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
- int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
- int32_t aCurTotalProgress,
- int32_t aMaxTotalProgress) override
- {
- MOZ_CRASH("Unexpected notification.");
- return NS_OK;
- }
- NS_IMETHOD
- OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
- nsIURI* aLocation, uint32_t aFlags) override
- {
- MOZ_CRASH("Unexpected notification.");
- return NS_OK;
- }
- NS_IMETHOD
- OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
- nsresult aStatus, const char16_t* aMessage) override
- {
- MOZ_CRASH("Unexpected notification.");
- return NS_OK;
- }
- NS_IMETHOD
- OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
- uint32_t aState) override
- {
- MOZ_CRASH("Unexpected notification.");
- return NS_OK;
- }
- private:
- ~WebProgressListener() {}
- RefPtr<PromiseWorkerProxy> mPromiseProxy;
- RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
- nsCOMPtr<nsPIDOMWindowOuter> mWindow;
- nsCOMPtr<nsIURI> mBaseURI;
- };
- NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
- NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
- mServiceWorkerPrivate, mWindow)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
- NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
- NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
- NS_INTERFACE_MAP_END
- class ClientNavigateRunnable final : public Runnable
- {
- uint64_t mWindowId;
- nsString mUrl;
- nsCString mBaseUrl;
- nsString mScope;
- RefPtr<PromiseWorkerProxy> mPromiseProxy;
- MOZ_INIT_OUTSIDE_CTOR WorkerPrivate* mWorkerPrivate;
- public:
- ClientNavigateRunnable(uint64_t aWindowId, const nsAString& aUrl,
- const nsAString& aScope,
- PromiseWorkerProxy* aPromiseProxy)
- : mWindowId(aWindowId)
- , mUrl(aUrl)
- , mScope(aScope)
- , mPromiseProxy(aPromiseProxy)
- , mWorkerPrivate(nullptr)
- {
- MOZ_ASSERT(aPromiseProxy);
- MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
- aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
- }
- NS_IMETHOD
- Run() override
- {
- AssertIsOnMainThread();
- nsCOMPtr<nsIPrincipal> principal;
- {
- MutexAutoLock lock(mPromiseProxy->Lock());
- if (mPromiseProxy->CleanedUp()) {
- return NS_OK;
- }
- mWorkerPrivate = mPromiseProxy->GetWorkerPrivate();
- WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
- mBaseUrl = info.mHref;
- principal = mWorkerPrivate->GetPrincipal();
- MOZ_DIAGNOSTIC_ASSERT(principal);
- }
- nsCOMPtr<nsIURI> baseUrl;
- nsCOMPtr<nsIURI> url;
- nsresult rv = ParseUrl(getter_AddRefs(baseUrl), getter_AddRefs(url));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return RejectPromise(NS_ERROR_TYPE_ERR);
- }
- rv = principal->CheckMayLoad(url, true, false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return RejectPromise(rv);
- }
- nsGlobalWindow* window;
- rv = Navigate(url, principal, &window);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return RejectPromise(rv);
- }
- nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
- nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
- if (NS_WARN_IF(!webProgress)) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
- if (!swm) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<ServiceWorkerRegistrationInfo> registration =
- swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
- if (NS_WARN_IF(!registration)) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
- registration->GetServiceWorkerInfoById(mWorkerPrivate->ServiceWorkerID());
- if (NS_WARN_IF(!serviceWorkerInfo)) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIWebProgressListener> listener =
- new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
- window->GetOuterWindow(), baseUrl);
- rv = webProgress->AddProgressListener(
- listener, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return RejectPromise(rv);
- }
- return NS_OK;
- }
- private:
- nsresult
- RejectPromise(nsresult aRv)
- {
- MOZ_ASSERT(mWorkerPrivate);
- RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
- new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy, aRv);
- resolveRunnable->Dispatch();
- return NS_OK;
- }
- nsresult
- ResolvePromise(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
- {
- MOZ_ASSERT(mWorkerPrivate);
- RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
- new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy,
- Move(aClientInfo));
- resolveRunnable->Dispatch();
- return NS_OK;
- }
- nsresult
- ParseUrl(nsIURI** aBaseUrl, nsIURI** aUrl)
- {
- MOZ_ASSERT(aBaseUrl);
- MOZ_ASSERT(aUrl);
- AssertIsOnMainThread();
- nsCOMPtr<nsIURI> baseUrl;
- nsresult rv = NS_NewURI(getter_AddRefs(baseUrl), mBaseUrl);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIURI> url;
- rv = NS_NewURI(getter_AddRefs(url), mUrl, nullptr, baseUrl);
- NS_ENSURE_SUCCESS(rv, rv);
- baseUrl.forget(aBaseUrl);
- url.forget(aUrl);
- return NS_OK;
- }
- nsresult
- Navigate(nsIURI* aUrl, nsIPrincipal* aPrincipal, nsGlobalWindow** aWindow)
- {
- MOZ_ASSERT(aWindow);
- nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
- if (NS_WARN_IF(!window)) {
- return NS_ERROR_TYPE_ERR;
- }
- nsCOMPtr<nsIDocument> doc = window->GetDocument();
- if (NS_WARN_IF(!doc)) {
- return NS_ERROR_TYPE_ERR;
- }
- if (NS_WARN_IF(!doc->IsActive())) {
- return NS_ERROR_TYPE_ERR;
- }
- nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
- if (NS_WARN_IF(!docShell)) {
- return NS_ERROR_TYPE_ERR;
- }
- nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
- nsresult rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- loadInfo->SetTriggeringPrincipal(aPrincipal);
- loadInfo->SetReferrer(doc->GetOriginalURI());
- loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
- loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
- loadInfo->SetSourceDocShell(docShell);
- rv =
- docShell->LoadURI(aUrl, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- *aWindow = window;
- return NS_OK;
- }
- };
- already_AddRefed<Promise>
- ServiceWorkerWindowClient::Navigate(const nsAString& aUrl, ErrorResult& aRv)
- {
- WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
- MOZ_ASSERT(workerPrivate);
- workerPrivate->AssertIsOnWorkerThread();
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
- MOZ_ASSERT(global);
- RefPtr<Promise> promise = Promise::Create(global, aRv);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
- }
- if (aUrl.EqualsLiteral("about:blank")) {
- promise->MaybeReject(NS_ERROR_TYPE_ERR);
- return promise.forget();
- }
- ServiceWorkerGlobalScope* globalScope =
- static_cast<ServiceWorkerGlobalScope*>(workerPrivate->GlobalScope());
- nsString scope;
- globalScope->GetScope(scope);
- RefPtr<PromiseWorkerProxy> promiseProxy =
- PromiseWorkerProxy::Create(workerPrivate, promise);
- if (promiseProxy) {
- RefPtr<ClientNavigateRunnable> r =
- new ClientNavigateRunnable(mWindowId, aUrl, scope, promiseProxy);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
- } else {
- promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
- }
- return promise.forget();
- }
|