diff options
Diffstat (limited to 'dom/workers/ServiceWorkerClients.cpp')
-rw-r--r-- | dom/workers/ServiceWorkerClients.cpp | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp new file mode 100644 index 000000000..11f864443 --- /dev/null +++ b/dom/workers/ServiceWorkerClients.cpp @@ -0,0 +1,864 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ServiceWorkerClients.h" + +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseWorkerProxy.h" + +#include "ServiceWorkerClient.h" +#include "ServiceWorkerManager.h" +#include "ServiceWorkerPrivate.h" +#include "ServiceWorkerWindowClient.h" + +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" +#include "WorkerScope.h" + +#include "nsContentUtils.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIDOMChromeWindow.h" +#include "nsIDOMWindow.h" +#include "nsIWebNavigation.h" +#include "nsIWebProgress.h" +#include "nsIWebProgressListener.h" +#include "nsIWindowMediator.h" +#include "nsIWindowWatcher.h" +#include "nsNetUtil.h" +#include "nsPIWindowWatcher.h" +#include "nsWindowWatcher.h" +#include "nsWeakReference.h" + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::workers; + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClients) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +ServiceWorkerClients::ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope) + : mWorkerScope(aWorkerScope) +{ + MOZ_ASSERT(mWorkerScope); +} + +JSObject* +ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return ClientsBinding::Wrap(aCx, this, aGivenProto); +} + +namespace { + +class GetRunnable final : public Runnable +{ + class ResolvePromiseWorkerRunnable final : public WorkerRunnable + { + RefPtr<PromiseWorkerProxy> mPromiseProxy; + UniquePtr<ServiceWorkerClientInfo> mValue; + nsresult mRv; + + public: + ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate, + PromiseWorkerProxy* aPromiseProxy, + UniquePtr<ServiceWorkerClientInfo>&& aValue, + nsresult aRv) + : WorkerRunnable(aWorkerPrivate), + mPromiseProxy(aPromiseProxy), + mValue(Move(aValue)), + mRv(Move(aRv)) + { + AssertIsOnMainThread(); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + Promise* promise = mPromiseProxy->WorkerPromise(); + MOZ_ASSERT(promise); + + if (NS_FAILED(mRv)) { + promise->MaybeReject(mRv); + } else if (mValue) { + RefPtr<ServiceWorkerWindowClient> windowClient = + new ServiceWorkerWindowClient(promise->GetParentObject(), *mValue); + promise->MaybeResolve(windowClient.get()); + } else { + promise->MaybeResolveWithUndefined(); + } + mPromiseProxy->CleanUp(); + return true; + } + }; + + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsString mClientId; +public: + GetRunnable(PromiseWorkerProxy* aPromiseProxy, + const nsAString& aClientId) + : mPromiseProxy(aPromiseProxy), + mClientId(aClientId) + { + } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + UniquePtr<ServiceWorkerClientInfo> result; + ErrorResult rv; + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + rv = NS_ERROR_FAILURE; + } else { + result = swm->GetClient(workerPrivate->GetPrincipal(), mClientId, rv); + } + + RefPtr<ResolvePromiseWorkerRunnable> r = + new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(), + mPromiseProxy, Move(result), + rv.StealNSResult()); + rv.SuppressException(); + + r->Dispatch(); + return NS_OK; + } +}; + +class MatchAllRunnable final : public Runnable +{ + class ResolvePromiseWorkerRunnable final : public WorkerRunnable + { + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsTArray<ServiceWorkerClientInfo> mValue; + + public: + ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate, + PromiseWorkerProxy* aPromiseProxy, + nsTArray<ServiceWorkerClientInfo>& aValue) + : WorkerRunnable(aWorkerPrivate), + mPromiseProxy(aPromiseProxy) + { + AssertIsOnMainThread(); + mValue.SwapElements(aValue); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + Promise* promise = mPromiseProxy->WorkerPromise(); + MOZ_ASSERT(promise); + + nsTArray<RefPtr<ServiceWorkerClient>> ret; + for (size_t i = 0; i < mValue.Length(); i++) { + ret.AppendElement(RefPtr<ServiceWorkerClient>( + new ServiceWorkerWindowClient(promise->GetParentObject(), + mValue.ElementAt(i)))); + } + + promise->MaybeResolve(ret); + mPromiseProxy->CleanUp(); + return true; + } + }; + + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsCString mScope; + bool mIncludeUncontrolled; +public: + MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy, + const nsCString& aScope, + bool aIncludeUncontrolled) + : mPromiseProxy(aPromiseProxy), + mScope(aScope), + mIncludeUncontrolled(aIncludeUncontrolled) + { + MOZ_ASSERT(mPromiseProxy); + } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + nsTArray<ServiceWorkerClientInfo> result; + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (swm) { + swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), + mScope, mIncludeUncontrolled, result); + } + RefPtr<ResolvePromiseWorkerRunnable> r = + new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(), + mPromiseProxy, result); + + r->Dispatch(); + return NS_OK; + } +}; + +class ResolveClaimRunnable final : public WorkerRunnable +{ + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsresult mResult; + +public: + ResolveClaimRunnable(WorkerPrivate* aWorkerPrivate, + PromiseWorkerProxy* aPromiseProxy, + nsresult aResult) + : WorkerRunnable(aWorkerPrivate) + , mPromiseProxy(aPromiseProxy) + , mResult(aResult) + { + AssertIsOnMainThread(); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr<Promise> promise = mPromiseProxy->WorkerPromise(); + MOZ_ASSERT(promise); + + if (NS_SUCCEEDED(mResult)) { + promise->MaybeResolveWithUndefined(); + } else { + promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + } + + mPromiseProxy->CleanUp(); + return true; + } +}; + +class ClaimRunnable final : public Runnable +{ + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsCString mScope; + uint64_t mServiceWorkerID; + +public: + ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope) + : mPromiseProxy(aPromiseProxy) + , mScope(aScope) + // Safe to call GetWorkerPrivate() since we are being called on the worker + // thread via script (so no clean up has occured yet). + , mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID()) + { + MOZ_ASSERT(aPromiseProxy); + } + + NS_IMETHOD + Run() override + { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + nsresult rv = NS_OK; + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown + rv = NS_ERROR_FAILURE; + } else { + rv = swm->ClaimClients(workerPrivate->GetPrincipal(), mScope, + mServiceWorkerID); + } + + RefPtr<ResolveClaimRunnable> r = + new ResolveClaimRunnable(workerPrivate, mPromiseProxy, rv); + + r->Dispatch(); + return NS_OK; + } +}; + +class ResolveOpenWindowRunnable final : public WorkerRunnable +{ +public: + ResolveOpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy, + UniquePtr<ServiceWorkerClientInfo>&& aClientInfo, + const nsresult aStatus) + : WorkerRunnable(aPromiseProxy->GetWorkerPrivate()) + , mPromiseProxy(aPromiseProxy) + , mClientInfo(Move(aClientInfo)) + , mStatus(aStatus) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aPromiseProxy); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + Promise* promise = mPromiseProxy->WorkerPromise(); + if (NS_WARN_IF(NS_FAILED(mStatus))) { + promise->MaybeReject(mStatus); + } else if (mClientInfo) { + RefPtr<ServiceWorkerWindowClient> client = + new ServiceWorkerWindowClient(promise->GetParentObject(), + *mClientInfo); + promise->MaybeResolve(client); + } else { + promise->MaybeResolve(JS::NullHandleValue); + } + + mPromiseProxy->CleanUp(); + return true; + } + +private: + RefPtr<PromiseWorkerProxy> mPromiseProxy; + UniquePtr<ServiceWorkerClientInfo> mClientInfo; + const nsresult mStatus; +}; + +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; + } + + // Our caller keeps a strong reference, so it is safe to remove the listener + // from ServiceWorkerPrivate. + mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this)); + aWebProgress->RemoveProgressListener(this); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc(); + UniquePtr<ServiceWorkerClientInfo> clientInfo; + if (doc) { + // Check same origin. + nsCOMPtr<nsIScriptSecurityManager> securityManager = + nsContentUtils::GetSecurityManager(); + nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(), + mBaseURI, false); + if (NS_SUCCEEDED(rv)) { + clientInfo.reset(new ServiceWorkerClientInfo(doc)); + } + } + + RefPtr<ResolveOpenWindowRunnable> r = + new ResolveOpenWindowRunnable(mPromiseProxy, + Move(clientInfo), + NS_OK); + r->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_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, const char16_t* aMessage) override + { + MOZ_ASSERT(false, "Unexpected notification."); + return NS_OK; + } + + NS_IMETHOD + OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) override + { + MOZ_ASSERT(false, "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 OpenWindowRunnable final : public Runnable +{ + RefPtr<PromiseWorkerProxy> mPromiseProxy; + nsString mUrl; + nsString mScope; + +public: + OpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy, + const nsAString& aUrl, + const nsAString& aScope) + : mPromiseProxy(aPromiseProxy) + , mUrl(aUrl) + , mScope(aScope) + { + MOZ_ASSERT(aPromiseProxy); + MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate()); + aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread(); + } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + +#ifdef MOZ_WIDGET_ANDROID + // This fires an intent that will start launching Fennec and foreground it, + // if necessary. + java::GeckoAppShell::OpenWindowForNotification(); +#endif + + nsCOMPtr<nsPIDOMWindowOuter> window; + nsresult rv = OpenWindow(getter_AddRefs(window)); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(window); + + rv = nsContentUtils::DispatchFocusChromeEvent(window); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo(); + nsCOMPtr<nsIURI> baseURI; + nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); + nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell); + + if (!webProgress) { + return NS_ERROR_FAILURE; + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal(); + MOZ_ASSERT(principal); + RefPtr<ServiceWorkerRegistrationInfo> registration = + swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope)); + if (NS_WARN_IF(!registration)) { + return NS_ERROR_FAILURE; + } + RefPtr<ServiceWorkerInfo> serviceWorkerInfo = + registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID()); + if (NS_WARN_IF(!serviceWorkerInfo)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIWebProgressListener> listener = + new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(), + window, baseURI); + + rv = webProgress->AddProgressListener(listener, + nsIWebProgress::NOTIFY_STATE_DOCUMENT); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + return NS_OK; + } +#ifdef MOZ_WIDGET_ANDROID + else if (rv == NS_ERROR_NOT_AVAILABLE) { + // We couldn't get a browser window, so Fennec must not be running. + // Send an Intent to launch Fennec and wait for "BrowserChrome:Ready" + // to try opening a window again. + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + NS_ENSURE_STATE(os); + + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal(); + MOZ_ASSERT(principal); + + RefPtr<ServiceWorkerRegistrationInfo> registration = + swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope)); + if (NS_WARN_IF(!registration)) { + return NS_ERROR_FAILURE; + } + + RefPtr<ServiceWorkerInfo> serviceWorkerInfo = + registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID()); + if (NS_WARN_IF(!serviceWorkerInfo)) { + return NS_ERROR_FAILURE; + } + + os->AddObserver(static_cast<nsIObserver*>(serviceWorkerInfo->WorkerPrivate()), + "BrowserChrome:Ready", true); + serviceWorkerInfo->WorkerPrivate()->AddPendingWindow(this); + return NS_OK; + } +#endif + + RefPtr<ResolveOpenWindowRunnable> resolveRunnable = + new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv); + + Unused << NS_WARN_IF(!resolveRunnable->Dispatch()); + + return NS_OK; + } + +private: + nsresult + OpenWindow(nsPIDOMWindowOuter** aWindow) + { + MOZ_DIAGNOSTIC_ASSERT(aWindow); + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); + + // [[1. Let url be the result of parsing url with entry settings object's API + // base URL.]] + nsCOMPtr<nsIURI> uri; + WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo(); + + nsCOMPtr<nsIURI> baseURI; + nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_TYPE_ERR; + } + + rv = NS_NewURI(getter_AddRefs(uri), mUrl, nullptr, baseURI); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_TYPE_ERR; + } + + // [[6.1 Open Window]] + nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, + &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (XRE_IsContentProcess()) { + // ContentProcess + nsCOMPtr<nsIWindowWatcher> wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch)); + NS_ENSURE_STATE(pwwatch); + + nsCString spec; + rv = uri->GetSpec(spec); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr<mozIDOMWindowProxy> newWindow; + rv = pwwatch->OpenWindow2(nullptr, + spec.get(), + nullptr, + nullptr, + false, false, true, nullptr, + // Not a spammy popup; we got permission, we swear! + /* aIsPopupSpam = */ false, + // Don't force noopener. We're not passing in an + // opener anyway, and we _do_ want the returned + // window. + /* aForceNoOpener = */ false, + /* aLoadInfp = */ nullptr, + getter_AddRefs(newWindow)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow); + pwindow.forget(aWindow); + MOZ_DIAGNOSTIC_ASSERT(*aWindow); + return NS_OK; + } + + // Find the most recent browser window and open a new tab in it. + nsCOMPtr<nsPIDOMWindowOuter> browserWindow = + nsContentUtils::GetMostRecentNonPBWindow(); + if (!browserWindow) { + // It is possible to be running without a browser window on Mac OS, so + // we need to open a new chrome window. + // TODO(catalinb): open new chrome window. Bug 1218080 + return NS_ERROR_NOT_AVAILABLE; + } + + nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow); + if (NS_WARN_IF(!chromeWin)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIBrowserDOMWindow> bwin; + chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin)); + + if (NS_WARN_IF(!bwin)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<mozIDOMWindowProxy> win; + rv = bwin->OpenURI(uri, nullptr, + nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW, + nsIBrowserDOMWindow::OPEN_NEW, + getter_AddRefs(win)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ENSURE_STATE(win); + + nsCOMPtr<nsPIDOMWindowOuter> pWin = nsPIDOMWindowOuter::From(win); + pWin.forget(aWindow); + MOZ_DIAGNOSTIC_ASSERT(*aWindow); + + return NS_OK; + } +}; + +} // namespace + +already_AddRefed<Promise> +ServiceWorkerClients::Get(const nsAString& aClientId, ErrorResult& aRv) +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr<PromiseWorkerProxy> promiseProxy = + PromiseWorkerProxy::Create(workerPrivate, promise); + if (!promiseProxy) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + return promise.forget(); + } + + RefPtr<GetRunnable> r = + new GetRunnable(promiseProxy, aClientId); + MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget())); + return promise.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions, + ErrorResult& aRv) +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + nsString scope; + mWorkerScope->GetScope(scope); + + if (aOptions.mType != ClientType::Window) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + + RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr<PromiseWorkerProxy> promiseProxy = + PromiseWorkerProxy::Create(workerPrivate, promise); + if (!promiseProxy) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + return promise.forget(); + } + + RefPtr<MatchAllRunnable> r = + new MatchAllRunnable(promiseProxy, + NS_ConvertUTF16toUTF8(scope), + aOptions.mIncludeUncontrolled); + MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget())); + return promise.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerClients::OpenWindow(const nsAString& aUrl, + ErrorResult& aRv) +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + if (aUrl.EqualsLiteral("about:blank")) { + promise->MaybeReject(NS_ERROR_TYPE_ERR); + return promise.forget(); + } + + // [[4. If this algorithm is not allowed to show a popup ..]] + // In Gecko the service worker is allowed to show a popup only if the user + // just clicked on a notification. + if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) { + promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return promise.forget(); + } + + RefPtr<PromiseWorkerProxy> promiseProxy = + PromiseWorkerProxy::Create(workerPrivate, promise); + + if (!promiseProxy) { + return nullptr; + } + + nsString scope; + mWorkerScope->GetScope(scope); + + RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy, + aUrl, scope); + MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget())); + + return promise.forget(); +} + +already_AddRefed<Promise> +ServiceWorkerClients::Claim(ErrorResult& aRv) +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr<PromiseWorkerProxy> promiseProxy = + PromiseWorkerProxy::Create(workerPrivate, promise); + if (!promiseProxy) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + return promise.forget(); + } + + nsString scope; + mWorkerScope->GetScope(scope); + + RefPtr<ClaimRunnable> runnable = + new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope)); + + MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget())); + return promise.forget(); +} |