summaryrefslogtreecommitdiffstats
path: root/dom/workers/ServiceWorkerClients.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/ServiceWorkerClients.cpp')
-rw-r--r--dom/workers/ServiceWorkerClients.cpp864
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();
+}