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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ServiceWorkerClients.h"
8 :
9 : #include "mozilla/dom/Promise.h"
10 : #include "mozilla/dom/PromiseWorkerProxy.h"
11 :
12 : #include "ServiceWorkerClient.h"
13 : #include "ServiceWorkerManager.h"
14 : #include "ServiceWorkerPrivate.h"
15 : #include "ServiceWorkerWindowClient.h"
16 :
17 : #include "WorkerPrivate.h"
18 : #include "WorkerRunnable.h"
19 : #include "WorkerScope.h"
20 :
21 : #include "nsContentUtils.h"
22 : #include "nsIBrowserDOMWindow.h"
23 : #include "nsIDocShell.h"
24 : #include "nsIDOMChromeWindow.h"
25 : #include "nsIDOMWindow.h"
26 : #include "nsIWebNavigation.h"
27 : #include "nsIWebProgress.h"
28 : #include "nsIWebProgressListener.h"
29 : #include "nsIWindowMediator.h"
30 : #include "nsIWindowWatcher.h"
31 : #include "nsNetUtil.h"
32 : #include "nsPIWindowWatcher.h"
33 : #include "nsWindowWatcher.h"
34 : #include "nsWeakReference.h"
35 :
36 : #ifdef MOZ_WIDGET_ANDROID
37 : #include "FennecJNIWrappers.h"
38 : #endif
39 :
40 : using namespace mozilla;
41 : using namespace mozilla::dom;
42 : using namespace mozilla::dom::workers;
43 :
44 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
45 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
46 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope)
47 :
48 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClients)
49 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
50 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
51 0 : NS_INTERFACE_MAP_END
52 :
53 0 : ServiceWorkerClients::ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope)
54 0 : : mWorkerScope(aWorkerScope)
55 : {
56 0 : MOZ_ASSERT(mWorkerScope);
57 0 : }
58 :
59 : JSObject*
60 0 : ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
61 : {
62 0 : return ClientsBinding::Wrap(aCx, this, aGivenProto);
63 : }
64 :
65 : namespace {
66 :
67 0 : class GetRunnable final : public Runnable
68 : {
69 0 : class ResolvePromiseWorkerRunnable final : public WorkerRunnable
70 : {
71 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
72 : UniquePtr<ServiceWorkerClientInfo> mValue;
73 : nsresult mRv;
74 :
75 : public:
76 0 : ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
77 : PromiseWorkerProxy* aPromiseProxy,
78 : UniquePtr<ServiceWorkerClientInfo>&& aValue,
79 : nsresult aRv)
80 0 : : WorkerRunnable(aWorkerPrivate),
81 : mPromiseProxy(aPromiseProxy),
82 0 : mValue(Move(aValue)),
83 0 : mRv(Move(aRv))
84 : {
85 0 : AssertIsOnMainThread();
86 0 : }
87 :
88 : bool
89 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
90 : {
91 0 : MOZ_ASSERT(aWorkerPrivate);
92 0 : aWorkerPrivate->AssertIsOnWorkerThread();
93 :
94 0 : Promise* promise = mPromiseProxy->WorkerPromise();
95 0 : MOZ_ASSERT(promise);
96 :
97 0 : if (NS_FAILED(mRv)) {
98 0 : promise->MaybeReject(mRv);
99 0 : } else if (mValue) {
100 : RefPtr<ServiceWorkerWindowClient> windowClient =
101 0 : new ServiceWorkerWindowClient(promise->GetParentObject(), *mValue);
102 0 : promise->MaybeResolve(windowClient.get());
103 : } else {
104 0 : promise->MaybeResolveWithUndefined();
105 : }
106 0 : mPromiseProxy->CleanUp();
107 0 : return true;
108 : }
109 : };
110 :
111 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
112 : nsString mClientId;
113 : public:
114 0 : GetRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aClientId)
115 0 : : mozilla::Runnable("GetRunnable")
116 : , mPromiseProxy(aPromiseProxy)
117 0 : , mClientId(aClientId)
118 : {
119 0 : }
120 :
121 : NS_IMETHOD
122 0 : Run() override
123 : {
124 0 : AssertIsOnMainThread();
125 :
126 0 : MutexAutoLock lock(mPromiseProxy->Lock());
127 0 : if (mPromiseProxy->CleanedUp()) {
128 0 : return NS_OK;
129 : }
130 :
131 0 : WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
132 0 : MOZ_ASSERT(workerPrivate);
133 :
134 0 : UniquePtr<ServiceWorkerClientInfo> result;
135 0 : ErrorResult rv;
136 :
137 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
138 0 : if (!swm) {
139 0 : rv = NS_ERROR_FAILURE;
140 : } else {
141 0 : result = swm->GetClient(workerPrivate->GetPrincipal(), mClientId, rv);
142 : }
143 :
144 : RefPtr<ResolvePromiseWorkerRunnable> r =
145 0 : new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
146 : mPromiseProxy, Move(result),
147 0 : rv.StealNSResult());
148 0 : rv.SuppressException();
149 :
150 0 : r->Dispatch();
151 0 : return NS_OK;
152 : }
153 : };
154 :
155 0 : class MatchAllRunnable final : public Runnable
156 : {
157 0 : class ResolvePromiseWorkerRunnable final : public WorkerRunnable
158 : {
159 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
160 : nsTArray<ServiceWorkerClientInfo> mValue;
161 :
162 : public:
163 0 : ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
164 : PromiseWorkerProxy* aPromiseProxy,
165 : nsTArray<ServiceWorkerClientInfo>& aValue)
166 0 : : WorkerRunnable(aWorkerPrivate),
167 0 : mPromiseProxy(aPromiseProxy)
168 : {
169 0 : AssertIsOnMainThread();
170 0 : mValue.SwapElements(aValue);
171 0 : }
172 :
173 : bool
174 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
175 : {
176 0 : MOZ_ASSERT(aWorkerPrivate);
177 0 : aWorkerPrivate->AssertIsOnWorkerThread();
178 :
179 0 : Promise* promise = mPromiseProxy->WorkerPromise();
180 0 : MOZ_ASSERT(promise);
181 :
182 0 : nsTArray<RefPtr<ServiceWorkerClient>> ret;
183 0 : for (size_t i = 0; i < mValue.Length(); i++) {
184 0 : ret.AppendElement(RefPtr<ServiceWorkerClient>(
185 0 : new ServiceWorkerWindowClient(promise->GetParentObject(),
186 0 : mValue.ElementAt(i))));
187 : }
188 :
189 0 : promise->MaybeResolve(ret);
190 0 : mPromiseProxy->CleanUp();
191 0 : return true;
192 : }
193 : };
194 :
195 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
196 : const nsCString mScope;
197 : const uint64_t mServiceWorkerID;
198 : const bool mIncludeUncontrolled;
199 : public:
200 0 : MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
201 : const nsCString& aScope,
202 : uint64_t aServiceWorkerID,
203 : bool aIncludeUncontrolled)
204 0 : : mozilla::Runnable("MatchAllRunnable")
205 : , mPromiseProxy(aPromiseProxy)
206 : , mScope(aScope)
207 : , mServiceWorkerID(aServiceWorkerID)
208 0 : , mIncludeUncontrolled(aIncludeUncontrolled)
209 : {
210 0 : MOZ_ASSERT(mPromiseProxy);
211 0 : }
212 :
213 : NS_IMETHOD
214 0 : Run() override
215 : {
216 0 : AssertIsOnMainThread();
217 :
218 0 : MutexAutoLock lock(mPromiseProxy->Lock());
219 0 : if (mPromiseProxy->CleanedUp()) {
220 0 : return NS_OK;
221 : }
222 :
223 0 : nsTArray<ServiceWorkerClientInfo> result;
224 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
225 0 : if (swm) {
226 0 : swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
227 0 : mScope, mServiceWorkerID, mIncludeUncontrolled,
228 0 : result);
229 : }
230 : RefPtr<ResolvePromiseWorkerRunnable> r =
231 0 : new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
232 0 : mPromiseProxy, result);
233 :
234 0 : r->Dispatch();
235 0 : return NS_OK;
236 : }
237 : };
238 :
239 0 : class ResolveClaimRunnable final : public WorkerRunnable
240 : {
241 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
242 : nsresult mResult;
243 :
244 : public:
245 0 : ResolveClaimRunnable(WorkerPrivate* aWorkerPrivate,
246 : PromiseWorkerProxy* aPromiseProxy,
247 : nsresult aResult)
248 0 : : WorkerRunnable(aWorkerPrivate)
249 : , mPromiseProxy(aPromiseProxy)
250 0 : , mResult(aResult)
251 : {
252 0 : AssertIsOnMainThread();
253 0 : }
254 :
255 : bool
256 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
257 : {
258 0 : MOZ_ASSERT(aWorkerPrivate);
259 0 : aWorkerPrivate->AssertIsOnWorkerThread();
260 :
261 0 : RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
262 0 : MOZ_ASSERT(promise);
263 :
264 0 : if (NS_SUCCEEDED(mResult)) {
265 0 : promise->MaybeResolveWithUndefined();
266 : } else {
267 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
268 : }
269 :
270 0 : mPromiseProxy->CleanUp();
271 0 : return true;
272 : }
273 : };
274 :
275 0 : class ClaimRunnable final : public Runnable
276 : {
277 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
278 : nsCString mScope;
279 : uint64_t mServiceWorkerID;
280 :
281 : public:
282 0 : ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope)
283 0 : : mozilla::Runnable("ClaimRunnable")
284 : , mPromiseProxy(aPromiseProxy)
285 : , mScope(aScope)
286 : // Safe to call GetWorkerPrivate() since we are being called on the worker
287 : // thread via script (so no clean up has occured yet).
288 0 : , mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID())
289 : {
290 0 : MOZ_ASSERT(aPromiseProxy);
291 0 : }
292 :
293 : NS_IMETHOD
294 0 : Run() override
295 : {
296 0 : MutexAutoLock lock(mPromiseProxy->Lock());
297 0 : if (mPromiseProxy->CleanedUp()) {
298 0 : return NS_OK;
299 : }
300 :
301 0 : WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
302 0 : MOZ_ASSERT(workerPrivate);
303 :
304 0 : nsresult rv = NS_OK;
305 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
306 0 : if (!swm) {
307 : // browser shutdown
308 0 : rv = NS_ERROR_FAILURE;
309 : } else {
310 0 : rv = swm->ClaimClients(workerPrivate->GetPrincipal(), mScope,
311 0 : mServiceWorkerID);
312 : }
313 :
314 : RefPtr<ResolveClaimRunnable> r =
315 0 : new ResolveClaimRunnable(workerPrivate, mPromiseProxy, rv);
316 :
317 0 : r->Dispatch();
318 0 : return NS_OK;
319 : }
320 : };
321 :
322 0 : class ResolveOpenWindowRunnable final : public WorkerRunnable
323 : {
324 : public:
325 0 : ResolveOpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
326 : UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
327 : const nsresult aStatus)
328 0 : : WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
329 : , mPromiseProxy(aPromiseProxy)
330 0 : , mClientInfo(Move(aClientInfo))
331 0 : , mStatus(aStatus)
332 : {
333 0 : AssertIsOnMainThread();
334 0 : MOZ_ASSERT(aPromiseProxy);
335 0 : }
336 :
337 : bool
338 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
339 : {
340 0 : MOZ_ASSERT(aWorkerPrivate);
341 0 : aWorkerPrivate->AssertIsOnWorkerThread();
342 :
343 0 : Promise* promise = mPromiseProxy->WorkerPromise();
344 0 : if (NS_WARN_IF(NS_FAILED(mStatus))) {
345 0 : promise->MaybeReject(mStatus);
346 0 : } else if (mClientInfo) {
347 : RefPtr<ServiceWorkerWindowClient> client =
348 0 : new ServiceWorkerWindowClient(promise->GetParentObject(),
349 0 : *mClientInfo);
350 0 : promise->MaybeResolve(client);
351 : } else {
352 0 : promise->MaybeResolve(JS::NullHandleValue);
353 : }
354 :
355 0 : mPromiseProxy->CleanUp();
356 0 : return true;
357 : }
358 :
359 : private:
360 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
361 : UniquePtr<ServiceWorkerClientInfo> mClientInfo;
362 : const nsresult mStatus;
363 : };
364 :
365 : class WebProgressListener final : public nsIWebProgressListener,
366 : public nsSupportsWeakReference
367 : {
368 : public:
369 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
370 0 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener, nsIWebProgressListener)
371 :
372 0 : WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
373 : ServiceWorkerPrivate* aServiceWorkerPrivate,
374 : nsPIDOMWindowOuter* aWindow,
375 : nsIURI* aBaseURI)
376 0 : : mPromiseProxy(aPromiseProxy)
377 : , mServiceWorkerPrivate(aServiceWorkerPrivate)
378 : , mWindow(aWindow)
379 0 : , mBaseURI(aBaseURI)
380 : {
381 0 : MOZ_ASSERT(aPromiseProxy);
382 0 : MOZ_ASSERT(aServiceWorkerPrivate);
383 0 : MOZ_ASSERT(aWindow);
384 0 : MOZ_ASSERT(aWindow->IsOuterWindow());
385 0 : MOZ_ASSERT(aBaseURI);
386 0 : AssertIsOnMainThread();
387 :
388 0 : mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
389 0 : }
390 :
391 : NS_IMETHOD
392 0 : OnStateChange(nsIWebProgress* aWebProgress,
393 : nsIRequest* aRequest,
394 : uint32_t aStateFlags, nsresult aStatus) override
395 : {
396 0 : if (!(aStateFlags & STATE_IS_DOCUMENT) ||
397 0 : !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
398 0 : return NS_OK;
399 : }
400 :
401 : // Our caller keeps a strong reference, so it is safe to remove the listener
402 : // from ServiceWorkerPrivate.
403 0 : mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
404 0 : aWebProgress->RemoveProgressListener(this);
405 :
406 0 : MutexAutoLock lock(mPromiseProxy->Lock());
407 0 : if (mPromiseProxy->CleanedUp()) {
408 0 : return NS_OK;
409 : }
410 :
411 0 : nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
412 0 : UniquePtr<ServiceWorkerClientInfo> clientInfo;
413 0 : if (doc) {
414 : // Check same origin.
415 : nsCOMPtr<nsIScriptSecurityManager> securityManager =
416 0 : nsContentUtils::GetSecurityManager();
417 0 : nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
418 0 : mBaseURI, false);
419 0 : if (NS_SUCCEEDED(rv)) {
420 0 : clientInfo.reset(new ServiceWorkerClientInfo(doc));
421 : }
422 : }
423 :
424 : RefPtr<ResolveOpenWindowRunnable> r =
425 : new ResolveOpenWindowRunnable(mPromiseProxy,
426 : Move(clientInfo),
427 0 : NS_OK);
428 0 : r->Dispatch();
429 :
430 0 : return NS_OK;
431 : }
432 :
433 : NS_IMETHOD
434 0 : OnProgressChange(nsIWebProgress* aWebProgress,
435 : nsIRequest* aRequest,
436 : int32_t aCurSelfProgress,
437 : int32_t aMaxSelfProgress,
438 : int32_t aCurTotalProgress,
439 : int32_t aMaxTotalProgress) override
440 : {
441 0 : MOZ_ASSERT(false, "Unexpected notification.");
442 : return NS_OK;
443 : }
444 :
445 : NS_IMETHOD
446 0 : OnLocationChange(nsIWebProgress* aWebProgress,
447 : nsIRequest* aRequest,
448 : nsIURI* aLocation,
449 : uint32_t aFlags) override
450 : {
451 0 : MOZ_ASSERT(false, "Unexpected notification.");
452 : return NS_OK;
453 : }
454 :
455 : NS_IMETHOD
456 0 : OnStatusChange(nsIWebProgress* aWebProgress,
457 : nsIRequest* aRequest,
458 : nsresult aStatus, const char16_t* aMessage) override
459 : {
460 0 : MOZ_ASSERT(false, "Unexpected notification.");
461 : return NS_OK;
462 : }
463 :
464 : NS_IMETHOD
465 0 : OnSecurityChange(nsIWebProgress* aWebProgress,
466 : nsIRequest* aRequest,
467 : uint32_t aState) override
468 : {
469 0 : MOZ_ASSERT(false, "Unexpected notification.");
470 : return NS_OK;
471 : }
472 :
473 : private:
474 0 : ~WebProgressListener()
475 0 : { }
476 :
477 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
478 : RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
479 : nsCOMPtr<nsPIDOMWindowOuter> mWindow;
480 : nsCOMPtr<nsIURI> mBaseURI;
481 : };
482 :
483 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
484 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
485 0 : NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
486 : mServiceWorkerPrivate, mWindow)
487 :
488 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
489 0 : NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
490 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
491 0 : NS_INTERFACE_MAP_END
492 :
493 : class OpenWindowRunnable final : public Runnable
494 : , public nsIObserver
495 : , public nsSupportsWeakReference
496 : {
497 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
498 : nsString mUrl;
499 : nsString mScope;
500 :
501 : public:
502 : NS_DECL_ISUPPORTS_INHERITED
503 : // Note: |OpenWindowRunnable| cannot be cycle collected because it inherits
504 : // thread safe reference counting from |mozilla::Runnable|. On Fennec, we
505 : // might use |ServiceWorkerPrivate::StoreISupports| to keep this object alive
506 : // while waiting for an event from the observer service. As such, to avoid
507 : // creating a cycle that will leak, |OpenWindowRunnable| must not hold a strong
508 : // reference to |ServiceWorkerPrivate|.
509 :
510 0 : OpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
511 : const nsAString& aUrl,
512 : const nsAString& aScope)
513 0 : : mozilla::Runnable("OpenWindowRunnable")
514 : , mPromiseProxy(aPromiseProxy)
515 : , mUrl(aUrl)
516 0 : , mScope(aScope)
517 : {
518 0 : MOZ_ASSERT(aPromiseProxy);
519 0 : MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
520 0 : aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
521 0 : }
522 :
523 : NS_IMETHOD
524 0 : Observe(nsISupports* aSubject, const char* aTopic, const char16_t* /* aData */) override
525 : {
526 0 : AssertIsOnMainThread();
527 :
528 0 : nsCString topic(aTopic);
529 0 : if (!topic.Equals(NS_LITERAL_CSTRING("BrowserChrome:Ready"))) {
530 0 : MOZ_ASSERT(false, "Unexpected topic.");
531 : return NS_ERROR_FAILURE;
532 : }
533 :
534 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
535 0 : NS_ENSURE_STATE(os);
536 0 : os->RemoveObserver(this, "BrowserChrome:Ready");
537 :
538 0 : RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
539 0 : NS_ENSURE_STATE(swp);
540 :
541 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
542 0 : swp->RemoveISupports(static_cast<nsIObserver*>(this));
543 :
544 0 : return NS_OK;
545 : }
546 :
547 : NS_IMETHOD
548 0 : Run() override
549 : {
550 0 : AssertIsOnMainThread();
551 :
552 0 : MutexAutoLock lock(mPromiseProxy->Lock());
553 0 : if (mPromiseProxy->CleanedUp()) {
554 0 : return NS_OK;
555 : }
556 :
557 : #ifdef MOZ_WIDGET_ANDROID
558 : // This fires an intent that will start launching Fennec and foreground it,
559 : // if necessary.
560 : if (jni::IsFennec()) {
561 : java::GeckoApp::LaunchOrBringToFront();
562 : }
563 : #endif
564 :
565 0 : nsCOMPtr<nsPIDOMWindowOuter> window;
566 0 : nsresult rv = OpenWindow(getter_AddRefs(window));
567 0 : if (NS_SUCCEEDED(rv)) {
568 0 : MOZ_ASSERT(window);
569 :
570 0 : rv = nsContentUtils::DispatchFocusChromeEvent(window);
571 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
572 0 : return rv;
573 : }
574 :
575 0 : WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
576 0 : MOZ_ASSERT(workerPrivate);
577 :
578 0 : WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
579 0 : nsCOMPtr<nsIURI> baseURI;
580 0 : nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
581 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
582 0 : return NS_ERROR_FAILURE;
583 : }
584 :
585 0 : nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
586 0 : nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
587 :
588 0 : if (!webProgress) {
589 0 : return NS_ERROR_FAILURE;
590 : }
591 :
592 0 : RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
593 0 : NS_ENSURE_STATE(swp);
594 :
595 : nsCOMPtr<nsIWebProgressListener> listener =
596 0 : new WebProgressListener(mPromiseProxy, swp, window, baseURI);
597 :
598 0 : rv = webProgress->AddProgressListener(listener,
599 0 : nsIWebProgress::NOTIFY_STATE_DOCUMENT);
600 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
601 0 : return NS_OK;
602 : }
603 : #ifdef MOZ_WIDGET_ANDROID
604 : else if (rv == NS_ERROR_NOT_AVAILABLE && jni::IsFennec()) {
605 : // We couldn't get a browser window, so Fennec must not be running.
606 : // Send an Intent to launch Fennec and wait for "BrowserChrome:Ready"
607 : // to try opening a window again.
608 : RefPtr<ServiceWorkerPrivate> swp = GetServiceWorkerPrivate();
609 : NS_ENSURE_STATE(swp);
610 :
611 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
612 : NS_ENSURE_STATE(os);
613 :
614 : rv = os->AddObserver(this, "BrowserChrome:Ready", /* weakRef */ true);
615 : NS_ENSURE_SUCCESS(rv, rv);
616 :
617 : swp->StoreISupports(static_cast<nsIObserver*>(this));
618 :
619 : return NS_OK;
620 : }
621 : #endif
622 :
623 : RefPtr<ResolveOpenWindowRunnable> resolveRunnable =
624 0 : new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv);
625 :
626 0 : Unused << NS_WARN_IF(!resolveRunnable->Dispatch());
627 :
628 0 : return NS_OK;
629 : }
630 :
631 : private:
632 0 : ~OpenWindowRunnable()
633 0 : { }
634 :
635 : ServiceWorkerPrivate*
636 0 : GetServiceWorkerPrivate() const
637 : {
638 0 : AssertIsOnMainThread();
639 :
640 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
641 0 : if (!swm) {
642 : // browser shutdown
643 0 : return nullptr;
644 : }
645 :
646 0 : WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
647 0 : MOZ_ASSERT(workerPrivate);
648 :
649 0 : nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
650 0 : MOZ_DIAGNOSTIC_ASSERT(principal);
651 :
652 : RefPtr<ServiceWorkerRegistrationInfo> registration =
653 0 : swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
654 0 : if (NS_WARN_IF(!registration)) {
655 0 : return nullptr;
656 : }
657 :
658 : RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
659 0 : registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
660 0 : if (NS_WARN_IF(!serviceWorkerInfo)) {
661 0 : return nullptr;
662 : }
663 :
664 0 : return serviceWorkerInfo->WorkerPrivate();
665 : }
666 :
667 : nsresult
668 0 : OpenWindow(nsPIDOMWindowOuter** aWindow)
669 : {
670 0 : MOZ_DIAGNOSTIC_ASSERT(aWindow);
671 0 : WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
672 :
673 : // [[1. Let url be the result of parsing url with entry settings object's API
674 : // base URL.]]
675 0 : nsCOMPtr<nsIURI> uri;
676 0 : WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
677 :
678 0 : nsCOMPtr<nsIURI> baseURI;
679 0 : nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
680 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
681 0 : return NS_ERROR_TYPE_ERR;
682 : }
683 :
684 0 : rv = NS_NewURI(getter_AddRefs(uri), mUrl, nullptr, baseURI);
685 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
686 0 : return NS_ERROR_TYPE_ERR;
687 : }
688 :
689 : // [[6.1 Open Window]]
690 0 : nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
691 0 : &rv);
692 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
693 0 : return rv;
694 : }
695 :
696 0 : if (XRE_IsContentProcess()) {
697 : // ContentProcess
698 : nsCOMPtr<nsIWindowWatcher> wwatch =
699 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
700 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
701 0 : return rv;
702 : }
703 0 : nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
704 0 : NS_ENSURE_STATE(pwwatch);
705 :
706 0 : nsCString spec;
707 0 : rv = uri->GetSpec(spec);
708 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
709 0 : return rv;
710 : }
711 :
712 0 : nsCOMPtr<mozIDOMWindowProxy> newWindow;
713 0 : rv = pwwatch->OpenWindow2(nullptr,
714 : spec.get(),
715 : nullptr,
716 : nullptr,
717 : false, false, true, nullptr,
718 : // Not a spammy popup; we got permission, we swear!
719 : /* aIsPopupSpam = */ false,
720 : // Don't force noopener. We're not passing in an
721 : // opener anyway, and we _do_ want the returned
722 : // window.
723 : /* aForceNoOpener = */ false,
724 : /* aLoadInfp = */ nullptr,
725 0 : getter_AddRefs(newWindow));
726 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
727 0 : return rv;
728 : }
729 0 : nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow);
730 0 : pwindow.forget(aWindow);
731 0 : MOZ_DIAGNOSTIC_ASSERT(*aWindow);
732 0 : return NS_OK;
733 : }
734 :
735 : // Find the most recent browser window and open a new tab in it.
736 : nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
737 0 : nsContentUtils::GetMostRecentNonPBWindow();
738 0 : if (!browserWindow) {
739 : // It is possible to be running without a browser window on Mac OS, so
740 : // we need to open a new chrome window.
741 : // TODO(catalinb): open new chrome window. Bug 1218080
742 0 : return NS_ERROR_NOT_AVAILABLE;
743 : }
744 :
745 0 : nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
746 0 : if (NS_WARN_IF(!chromeWin)) {
747 0 : return NS_ERROR_FAILURE;
748 : }
749 :
750 0 : nsCOMPtr<nsIBrowserDOMWindow> bwin;
751 0 : chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
752 :
753 0 : if (NS_WARN_IF(!bwin)) {
754 0 : return NS_ERROR_FAILURE;
755 : }
756 :
757 0 : nsCOMPtr<nsIPrincipal> triggeringPrincipal = workerPrivate->GetPrincipal();
758 0 : MOZ_DIAGNOSTIC_ASSERT(triggeringPrincipal);
759 :
760 0 : nsCOMPtr<mozIDOMWindowProxy> win;
761 0 : rv = bwin->OpenURI(uri, nullptr,
762 : nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
763 : nsIBrowserDOMWindow::OPEN_NEW,
764 : triggeringPrincipal,
765 0 : getter_AddRefs(win));
766 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
767 0 : return rv;
768 : }
769 0 : NS_ENSURE_STATE(win);
770 :
771 0 : nsCOMPtr<nsPIDOMWindowOuter> pWin = nsPIDOMWindowOuter::From(win);
772 0 : pWin.forget(aWindow);
773 0 : MOZ_DIAGNOSTIC_ASSERT(*aWindow);
774 :
775 0 : return NS_OK;
776 : }
777 : };
778 :
779 0 : NS_IMPL_ADDREF_INHERITED(OpenWindowRunnable, Runnable) \
780 0 : NS_IMPL_RELEASE_INHERITED(OpenWindowRunnable, Runnable)
781 :
782 0 : NS_INTERFACE_MAP_BEGIN(OpenWindowRunnable)
783 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
784 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
785 0 : NS_INTERFACE_MAP_ENTRY(nsIRunnable)
786 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
787 0 : NS_INTERFACE_MAP_END
788 :
789 : } // namespace
790 :
791 : already_AddRefed<Promise>
792 0 : ServiceWorkerClients::Get(const nsAString& aClientId, ErrorResult& aRv)
793 : {
794 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
795 0 : MOZ_ASSERT(workerPrivate);
796 0 : workerPrivate->AssertIsOnWorkerThread();
797 :
798 0 : RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
799 0 : if (NS_WARN_IF(aRv.Failed())) {
800 0 : return nullptr;
801 : }
802 :
803 : RefPtr<PromiseWorkerProxy> promiseProxy =
804 0 : PromiseWorkerProxy::Create(workerPrivate, promise);
805 0 : if (!promiseProxy) {
806 0 : promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
807 0 : return promise.forget();
808 : }
809 :
810 : RefPtr<GetRunnable> r =
811 0 : new GetRunnable(promiseProxy, aClientId);
812 0 : MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
813 0 : return promise.forget();
814 : }
815 :
816 : already_AddRefed<Promise>
817 0 : ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
818 : ErrorResult& aRv)
819 : {
820 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
821 0 : MOZ_ASSERT(workerPrivate);
822 0 : workerPrivate->AssertIsOnWorkerThread();
823 :
824 0 : nsString scope;
825 0 : mWorkerScope->GetScope(scope);
826 :
827 0 : if (aOptions.mType != ClientType::Window) {
828 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
829 0 : return nullptr;
830 : }
831 :
832 0 : RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
833 0 : if (NS_WARN_IF(aRv.Failed())) {
834 0 : return nullptr;
835 : }
836 :
837 : RefPtr<PromiseWorkerProxy> promiseProxy =
838 0 : PromiseWorkerProxy::Create(workerPrivate, promise);
839 0 : if (!promiseProxy) {
840 0 : promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
841 0 : return promise.forget();
842 : }
843 :
844 : RefPtr<MatchAllRunnable> r =
845 : new MatchAllRunnable(promiseProxy,
846 0 : NS_ConvertUTF16toUTF8(scope),
847 0 : workerPrivate->ServiceWorkerID(),
848 0 : aOptions.mIncludeUncontrolled);
849 0 : MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
850 0 : return promise.forget();
851 : }
852 :
853 : already_AddRefed<Promise>
854 0 : ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
855 : ErrorResult& aRv)
856 : {
857 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
858 0 : MOZ_ASSERT(workerPrivate);
859 :
860 0 : RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
861 0 : if (NS_WARN_IF(aRv.Failed())) {
862 0 : return nullptr;
863 : }
864 :
865 0 : if (aUrl.EqualsLiteral("about:blank")) {
866 0 : promise->MaybeReject(NS_ERROR_TYPE_ERR);
867 0 : return promise.forget();
868 : }
869 :
870 : // [[4. If this algorithm is not allowed to show a popup ..]]
871 : // In Gecko the service worker is allowed to show a popup only if the user
872 : // just clicked on a notification.
873 0 : if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
874 0 : promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
875 0 : return promise.forget();
876 : }
877 :
878 : RefPtr<PromiseWorkerProxy> promiseProxy =
879 0 : PromiseWorkerProxy::Create(workerPrivate, promise);
880 :
881 0 : if (!promiseProxy) {
882 0 : return nullptr;
883 : }
884 :
885 0 : nsString scope;
886 0 : mWorkerScope->GetScope(scope);
887 :
888 : RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
889 0 : aUrl, scope);
890 0 : MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
891 :
892 0 : return promise.forget();
893 : }
894 :
895 : already_AddRefed<Promise>
896 0 : ServiceWorkerClients::Claim(ErrorResult& aRv)
897 : {
898 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
899 0 : MOZ_ASSERT(workerPrivate);
900 :
901 0 : RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
902 0 : if (NS_WARN_IF(aRv.Failed())) {
903 0 : return nullptr;
904 : }
905 :
906 : RefPtr<PromiseWorkerProxy> promiseProxy =
907 0 : PromiseWorkerProxy::Create(workerPrivate, promise);
908 0 : if (!promiseProxy) {
909 0 : promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
910 0 : return promise.forget();
911 : }
912 :
913 0 : nsString scope;
914 0 : mWorkerScope->GetScope(scope);
915 :
916 : RefPtr<ClaimRunnable> runnable =
917 0 : new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
918 :
919 0 : MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget()));
920 0 : return promise.forget();
921 : }
|