Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ClientManagerService.h"
8 :
9 : #include "ClientManagerParent.h"
10 : #include "ClientNavigateOpParent.h"
11 : #include "ClientOpenWindowOpParent.h"
12 : #include "ClientOpenWindowUtils.h"
13 : #include "ClientPrincipalUtils.h"
14 : #include "ClientSourceParent.h"
15 : #include "mozilla/dom/ContentParent.h"
16 : #include "mozilla/dom/ServiceWorkerManager.h"
17 : #include "mozilla/dom/ServiceWorkerUtils.h"
18 : #include "mozilla/ipc/BackgroundParent.h"
19 : #include "mozilla/ipc/PBackgroundSharedTypes.h"
20 : #include "mozilla/ClearOnShutdown.h"
21 : #include "mozilla/SystemGroup.h"
22 : #include "nsIAsyncShutdown.h"
23 : #include "nsIXULRuntime.h"
24 : #include "nsProxyRelease.h"
25 :
26 : namespace mozilla {
27 : namespace dom {
28 :
29 : using mozilla::ipc::AssertIsOnBackgroundThread;
30 : using mozilla::ipc::PrincipalInfo;
31 :
32 : namespace {
33 :
34 : ClientManagerService* sClientManagerServiceInstance = nullptr;
35 : bool sClientManagerServiceShutdownRegistered = false;
36 :
37 : class ClientShutdownBlocker final : public nsIAsyncShutdownBlocker
38 : {
39 : RefPtr<GenericPromise::Private> mPromise;
40 :
41 0 : ~ClientShutdownBlocker() = default;
42 :
43 : public:
44 1 : explicit ClientShutdownBlocker(GenericPromise::Private* aPromise)
45 0 : : mPromise(aPromise)
46 : {
47 2 : MOZ_DIAGNOSTIC_ASSERT(mPromise);
48 1 : }
49 :
50 : NS_IMETHOD
51 4 : GetName(nsAString& aNameOut) override
52 : {
53 : aNameOut =
54 8 : NS_LITERAL_STRING("ClientManagerService: start destroying IPC actors early");
55 4 : return NS_OK;
56 : }
57 :
58 : NS_IMETHOD
59 0 : BlockShutdown(nsIAsyncShutdownClient* aClient) override
60 : {
61 0 : mPromise->Resolve(true, __func__);
62 0 : aClient->RemoveBlocker(this);
63 0 : return NS_OK;
64 : }
65 :
66 : NS_IMETHOD
67 0 : GetState(nsIPropertyBag**) override
68 : {
69 0 : return NS_OK;
70 : }
71 :
72 : NS_DECL_ISUPPORTS
73 : };
74 :
75 41 : NS_IMPL_ISUPPORTS(ClientShutdownBlocker, nsIAsyncShutdownBlocker)
76 :
77 : // Helper function the resolves a MozPromise when we detect that the browser
78 : // has begun to shutdown.
79 : RefPtr<GenericPromise>
80 0 : OnShutdown()
81 : {
82 0 : RefPtr<GenericPromise::Private> ref = new GenericPromise::Private(__func__);
83 :
84 0 : nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("ClientManagerServer::OnShutdown",
85 0 : [ref] () {
86 1 : nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
87 0 : if (!svc) {
88 0 : ref->Resolve(true, __func__);
89 0 : return;
90 : }
91 :
92 0 : nsCOMPtr<nsIAsyncShutdownClient> phase;
93 1 : MOZ_ALWAYS_SUCCEEDS(svc->GetXpcomWillShutdown(getter_AddRefs(phase)));
94 0 : if (!phase) {
95 0 : ref->Resolve(true, __func__);
96 0 : return;
97 : }
98 :
99 0 : nsCOMPtr<nsIAsyncShutdownBlocker> blocker = new ClientShutdownBlocker(ref);
100 : nsresult rv =
101 5 : phase->AddBlocker(blocker, NS_LITERAL_STRING(__FILE__), __LINE__,
102 0 : NS_LITERAL_STRING("ClientManagerService shutdown"));
103 :
104 0 : if (NS_FAILED(rv)) {
105 0 : ref->Resolve(true, __func__);
106 0 : return;
107 : }
108 0 : });
109 :
110 1 : MOZ_ALWAYS_SUCCEEDS(
111 : SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
112 :
113 3 : return ref.forget();
114 : }
115 :
116 : } // anonymous namespace
117 :
118 1 : ClientManagerService::ClientManagerService()
119 0 : : mShutdown(false)
120 : {
121 1 : AssertIsOnBackgroundThread();
122 :
123 : // Only register one shutdown handler at a time. If a previous service
124 : // instance did this, but shutdown has not come, then we can avoid
125 : // doing it again.
126 1 : if (!sClientManagerServiceShutdownRegistered) {
127 1 : sClientManagerServiceShutdownRegistered = true;
128 :
129 : // While the ClientManagerService will be gracefully terminated as windows
130 : // and workers are naturally killed, this can cause us to do extra work
131 : // relatively late in the shutdown process. To avoid this we eagerly begin
132 : // shutdown at the first sign it has begun. Since we handle normal shutdown
133 : // gracefully we don't really need to block anything here. We just begin
134 : // destroying our IPC actors immediately.
135 2 : OnShutdown()->Then(GetCurrentThreadSerialEventTarget(), __func__,
136 0 : [] () {
137 : // Look up the latest service instance, if it exists. This may
138 : // be different from the instance that registered the shutdown
139 : // handler.
140 0 : RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
141 0 : if (svc) {
142 0 : svc->Shutdown();
143 : }
144 0 : });
145 : }
146 0 : }
147 :
148 0 : ClientManagerService::~ClientManagerService()
149 : {
150 0 : AssertIsOnBackgroundThread();
151 0 : MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0);
152 0 : MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
153 :
154 0 : MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this);
155 0 : sClientManagerServiceInstance = nullptr;
156 0 : }
157 :
158 : void
159 0 : ClientManagerService::Shutdown()
160 : {
161 0 : AssertIsOnBackgroundThread();
162 0 : MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceShutdownRegistered);
163 :
164 : // If many ClientManagerService are created and destroyed quickly we can
165 : // in theory get more than one shutdown listener calling us.
166 0 : if (mShutdown) {
167 0 : return;
168 : }
169 0 : mShutdown = true;
170 :
171 : // Begin destroying our various manager actors which will in turn destroy
172 : // all source, handle, and operation actors.
173 0 : AutoTArray<ClientManagerParent*, 16> list(mManagerList);
174 0 : for (auto actor : list) {
175 0 : Unused << PClientManagerParent::Send__delete__(actor);
176 : }
177 : }
178 :
179 : // static
180 : already_AddRefed<ClientManagerService>
181 0 : ClientManagerService::GetOrCreateInstance()
182 : {
183 0 : AssertIsOnBackgroundThread();
184 :
185 34 : if (!sClientManagerServiceInstance) {
186 1 : sClientManagerServiceInstance = new ClientManagerService();
187 : }
188 :
189 68 : RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
190 68 : return ref.forget();
191 : }
192 :
193 : // static
194 : already_AddRefed<ClientManagerService>
195 0 : ClientManagerService::GetInstance()
196 : {
197 0 : AssertIsOnBackgroundThread();
198 :
199 0 : if (!sClientManagerServiceInstance) {
200 : return nullptr;
201 : }
202 :
203 0 : RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
204 0 : return ref.forget();
205 : }
206 :
207 : bool
208 0 : ClientManagerService::AddSource(ClientSourceParent* aSource)
209 : {
210 0 : AssertIsOnBackgroundThread();
211 28 : MOZ_ASSERT(aSource);
212 84 : auto entry = mSourceTable.LookupForAdd(aSource->Info().Id());
213 : // Do not permit overwriting an existing ClientSource with the same
214 : // UUID. This would allow a spoofed ClientParentSource actor to
215 : // intercept postMessage() intended for the real actor.
216 28 : if (NS_WARN_IF(!!entry)) {
217 : return false;
218 : }
219 56 : entry.OrInsert([&] { return aSource; });
220 28 : return true;
221 : }
222 :
223 : bool
224 0 : ClientManagerService::RemoveSource(ClientSourceParent* aSource)
225 : {
226 0 : AssertIsOnBackgroundThread();
227 0 : MOZ_ASSERT(aSource);
228 42 : auto entry = mSourceTable.Lookup(aSource->Info().Id());
229 14 : if (NS_WARN_IF(!entry)) {
230 : return false;
231 : }
232 14 : entry.Remove();
233 14 : return true;
234 : }
235 :
236 : ClientSourceParent*
237 0 : ClientManagerService::FindSource(const nsID& aID, const PrincipalInfo& aPrincipalInfo)
238 : {
239 0 : AssertIsOnBackgroundThread();
240 :
241 0 : auto entry = mSourceTable.Lookup(aID);
242 0 : if (!entry) {
243 : return nullptr;
244 : }
245 :
246 0 : ClientSourceParent* source = entry.Data();
247 0 : if (source->IsFrozen() ||
248 0 : !ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), aPrincipalInfo)) {
249 : return nullptr;
250 : }
251 :
252 0 : return source;
253 : }
254 :
255 : void
256 0 : ClientManagerService::AddManager(ClientManagerParent* aManager)
257 : {
258 0 : AssertIsOnBackgroundThread();
259 0 : MOZ_DIAGNOSTIC_ASSERT(aManager);
260 6 : MOZ_ASSERT(!mManagerList.Contains(aManager));
261 6 : mManagerList.AppendElement(aManager);
262 :
263 : // If shutdown has already begun then immediately destroy the actor.
264 6 : if (mShutdown) {
265 0 : Unused << PClientManagerParent::Send__delete__(aManager);
266 : }
267 6 : }
268 :
269 : void
270 0 : ClientManagerService::RemoveManager(ClientManagerParent* aManager)
271 : {
272 0 : AssertIsOnBackgroundThread();
273 0 : MOZ_DIAGNOSTIC_ASSERT(aManager);
274 0 : DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
275 1 : MOZ_ASSERT(removed);
276 1 : }
277 :
278 : RefPtr<ClientOpPromise>
279 0 : ClientManagerService::Navigate(const ClientNavigateArgs& aArgs)
280 : {
281 0 : RefPtr<ClientOpPromise> ref;
282 :
283 0 : ClientSourceParent* source = FindSource(aArgs.target().id(),
284 0 : aArgs.target().principalInfo());
285 0 : if (!source) {
286 0 : ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
287 0 : return ref.forget();
288 : }
289 :
290 0 : PClientManagerParent* manager = source->Manager();
291 0 : MOZ_DIAGNOSTIC_ASSERT(manager);
292 :
293 0 : ClientNavigateOpConstructorArgs args;
294 0 : args.url() = aArgs.url();
295 0 : args.baseURL() = aArgs.baseURL();
296 :
297 : // This is safe to do because the ClientSourceChild cannot directly delete
298 : // itself. Instead it sends a Teardown message to the parent which then
299 : // calls delete. That means we can be sure that we are not racing with
300 : // source destruction here.
301 0 : args.targetParent() = source;
302 :
303 : RefPtr<ClientOpPromise::Private> promise =
304 0 : new ClientOpPromise::Private(__func__);
305 :
306 0 : ClientNavigateOpParent* op = new ClientNavigateOpParent(args, promise);
307 : PClientNavigateOpParent* result =
308 0 : manager->SendPClientNavigateOpConstructor(op, args);
309 0 : if (!result) {
310 0 : promise->Reject(NS_ERROR_FAILURE, __func__);
311 0 : ref = promise;
312 0 : return ref.forget();
313 : }
314 :
315 0 : ref = promise;
316 0 : return ref.forget();
317 : }
318 :
319 : namespace
320 : {
321 :
322 : class PromiseListHolder final
323 : {
324 : RefPtr<ClientOpPromise::Private> mResultPromise;
325 : nsTArray<RefPtr<ClientOpPromise>> mPromiseList;
326 : nsTArray<ClientInfoAndState> mResultList;
327 : uint32_t mOutstandingPromiseCount;
328 :
329 : void
330 0 : ProcessSuccess(const ClientInfoAndState& aResult)
331 : {
332 0 : mResultList.AppendElement(aResult);
333 0 : ProcessCompletion();
334 0 : }
335 :
336 : void
337 0 : ProcessCompletion()
338 : {
339 0 : MOZ_DIAGNOSTIC_ASSERT(mOutstandingPromiseCount > 0);
340 0 : mOutstandingPromiseCount -= 1;
341 0 : MaybeFinish();
342 0 : }
343 :
344 0 : ~PromiseListHolder() = default;
345 : public:
346 0 : PromiseListHolder()
347 0 : : mResultPromise(new ClientOpPromise::Private(__func__))
348 0 : , mOutstandingPromiseCount(0)
349 : {
350 0 : }
351 :
352 : already_AddRefed<ClientOpPromise>
353 0 : GetResultPromise()
354 : {
355 0 : RefPtr<PromiseListHolder> kungFuDeathGrip = this;
356 0 : mResultPromise->Then(
357 : GetCurrentThreadSerialEventTarget(), __func__,
358 0 : [kungFuDeathGrip] (const ClientOpResult& aResult) { },
359 0 : [kungFuDeathGrip] (nsresult aResult) { });
360 :
361 0 : RefPtr<ClientOpPromise> ref = mResultPromise;
362 0 : return ref.forget();
363 : }
364 :
365 : void
366 0 : AddPromise(RefPtr<ClientOpPromise>&& aPromise)
367 : {
368 0 : mPromiseList.AppendElement(std::move(aPromise));
369 0 : MOZ_DIAGNOSTIC_ASSERT(mPromiseList.LastElement());
370 0 : mOutstandingPromiseCount += 1;
371 :
372 0 : RefPtr<PromiseListHolder> self(this);
373 0 : mPromiseList.LastElement()->Then(
374 : GetCurrentThreadSerialEventTarget(), __func__,
375 0 : [self] (const ClientOpResult& aResult) {
376 : // TODO: This is pretty clunky. Try to figure out a better
377 : // wait for MatchAll() and Claim() to share this code
378 : // even though they expect different return values.
379 0 : if (aResult.type() == ClientOpResult::TClientInfoAndState) {
380 0 : self->ProcessSuccess(aResult.get_ClientInfoAndState());
381 : } else {
382 0 : self->ProcessCompletion();
383 : }
384 0 : }, [self] (nsresult aResult) {
385 0 : self->ProcessCompletion();
386 0 : });
387 0 : }
388 :
389 : void
390 0 : MaybeFinish()
391 : {
392 0 : if (!mOutstandingPromiseCount) {
393 0 : mResultPromise->Resolve(mResultList, __func__);
394 : }
395 0 : }
396 :
397 0 : NS_INLINE_DECL_REFCOUNTING(PromiseListHolder)
398 : };
399 :
400 : } // anonymous namespace
401 :
402 : RefPtr<ClientOpPromise>
403 0 : ClientManagerService::MatchAll(const ClientMatchAllArgs& aArgs)
404 : {
405 0 : AssertIsOnBackgroundThread();
406 :
407 0 : ServiceWorkerDescriptor swd(aArgs.serviceWorker());
408 0 : const PrincipalInfo& principalInfo = swd.PrincipalInfo();
409 :
410 0 : RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
411 :
412 0 : for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
413 0 : ClientSourceParent* source = iter.UserData();
414 0 : MOZ_DIAGNOSTIC_ASSERT(source);
415 :
416 0 : if (source->IsFrozen() || !source->ExecutionReady()) {
417 : continue;
418 : }
419 :
420 0 : if (aArgs.type() != ClientType::All &&
421 0 : source->Info().Type() != aArgs.type()) {
422 : continue;
423 : }
424 :
425 0 : if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) {
426 : continue;
427 : }
428 :
429 0 : if (!aArgs.includeUncontrolled()) {
430 : const Maybe<ServiceWorkerDescriptor>& controller =
431 0 : source->GetController();
432 0 : if (controller.isNothing()) {
433 : continue;
434 : }
435 :
436 0 : if(controller.ref().Id() != swd.Id() ||
437 0 : controller.ref().Scope() != swd.Scope()) {
438 : continue;
439 : }
440 : }
441 :
442 0 : promiseList->AddPromise(
443 0 : source->StartOp(ClientGetInfoAndStateArgs(source->Info().Id(),
444 0 : source->Info().PrincipalInfo())));
445 : }
446 :
447 : // Maybe finish the promise now in case we didn't find any matching clients.
448 0 : promiseList->MaybeFinish();
449 :
450 0 : return promiseList->GetResultPromise();
451 : }
452 :
453 : namespace {
454 :
455 : RefPtr<ClientOpPromise>
456 0 : ClaimOnMainThread(const ClientInfo& aClientInfo,
457 : const ServiceWorkerDescriptor& aDescriptor)
458 : {
459 : RefPtr<ClientOpPromise::Private> promise =
460 0 : new ClientOpPromise::Private(__func__);
461 :
462 0 : nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__,
463 0 : [promise, clientInfo = std::move(aClientInfo), desc = std::move(aDescriptor)] () {
464 0 : auto scopeExit = MakeScopeExit([&] {
465 0 : promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
466 0 : });
467 :
468 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
469 0 : NS_ENSURE_TRUE_VOID(swm);
470 :
471 0 : RefPtr<GenericPromise> inner = swm->MaybeClaimClient(clientInfo, desc);
472 : inner->Then(SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
473 0 : [promise] (bool aResult) {
474 0 : promise->Resolve(NS_OK, __func__);
475 0 : }, [promise] (nsresult aRv) {
476 0 : promise->Reject(aRv, __func__);
477 0 : });
478 0 : });
479 :
480 0 : MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
481 :
482 0 : return promise.forget();
483 : }
484 :
485 : } // anonymous namespace
486 :
487 : RefPtr<ClientOpPromise>
488 0 : ClientManagerService::Claim(const ClientClaimArgs& aArgs)
489 : {
490 0 : AssertIsOnBackgroundThread();
491 :
492 0 : const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker();
493 0 : const PrincipalInfo& principalInfo = serviceWorker.principalInfo();
494 :
495 0 : RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
496 :
497 0 : for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
498 0 : ClientSourceParent* source = iter.UserData();
499 0 : MOZ_DIAGNOSTIC_ASSERT(source);
500 :
501 0 : if (source->IsFrozen()) {
502 : continue;
503 : }
504 :
505 0 : if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(), principalInfo)) {
506 : continue;
507 : }
508 :
509 0 : const Maybe<ServiceWorkerDescriptor>& controller = source->GetController();
510 0 : if (controller.isSome() &&
511 0 : controller.ref().Scope() == serviceWorker.scope() &&
512 0 : controller.ref().Id() == serviceWorker.id()) {
513 : continue;
514 : }
515 :
516 : // TODO: This logic to determine if a service worker should control
517 : // a particular client should be moved to the ServiceWorkerManager.
518 : // This can't happen until the SWM is moved to the parent process,
519 : // though.
520 0 : if (!source->ExecutionReady() ||
521 0 : source->Info().Type() == ClientType::Serviceworker ||
522 0 : source->Info().URL().Find(serviceWorker.scope()) != 0) {
523 : continue;
524 : }
525 :
526 0 : if (ServiceWorkerParentInterceptEnabled()) {
527 0 : promiseList->AddPromise(
528 0 : ClaimOnMainThread(source->Info(),
529 0 : ServiceWorkerDescriptor(serviceWorker)));
530 : } else {
531 0 : promiseList->AddPromise(source->StartOp(aArgs));
532 : }
533 : }
534 :
535 : // Maybe finish the promise now in case we didn't find any matching clients.
536 0 : promiseList->MaybeFinish();
537 :
538 0 : return promiseList->GetResultPromise();
539 : }
540 :
541 : RefPtr<ClientOpPromise>
542 0 : ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
543 : {
544 0 : RefPtr<ClientOpPromise> ref;
545 :
546 0 : ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
547 0 : if (!source || !source->ExecutionReady()) {
548 0 : ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
549 0 : return ref.forget();
550 : }
551 :
552 0 : return source->StartOp(aArgs);
553 : }
554 :
555 : namespace {
556 :
557 : class OpenWindowRunnable final : public Runnable
558 : {
559 : RefPtr<ClientOpPromise::Private> mPromise;
560 : const ClientOpenWindowArgs mArgs;
561 : RefPtr<ContentParent> mSourceProcess;
562 :
563 0 : ~OpenWindowRunnable()
564 0 : {
565 0 : NS_ReleaseOnMainThreadSystemGroup(mSourceProcess.forget());
566 0 : }
567 :
568 : public:
569 0 : OpenWindowRunnable(ClientOpPromise::Private* aPromise,
570 : const ClientOpenWindowArgs& aArgs,
571 : already_AddRefed<ContentParent> aSourceProcess)
572 0 : : Runnable("ClientManagerService::OpenWindowRunnable")
573 : , mPromise(aPromise)
574 : , mArgs(aArgs)
575 0 : , mSourceProcess(aSourceProcess)
576 : {
577 0 : MOZ_DIAGNOSTIC_ASSERT(mPromise);
578 0 : }
579 :
580 : NS_IMETHOD
581 0 : Run() override
582 : {
583 0 : MOZ_ASSERT(NS_IsMainThread());
584 :
585 0 : if (!BrowserTabsRemoteAutostart()) {
586 0 : RefPtr<ClientOpPromise> p = ClientOpenWindowInCurrentProcess(mArgs);
587 0 : p->ChainTo(mPromise.forget(), __func__);
588 : return NS_OK;
589 : }
590 :
591 0 : RefPtr<ContentParent> targetProcess;
592 :
593 : // Possibly try to open the window in the same process that called
594 : // openWindow(). This is a temporary compat setting until the
595 : // multi-e10s service worker refactor is complete.
596 0 : if (Preferences::GetBool("dom.clients.openwindow_favors_same_process",
597 : false)) {
598 0 : targetProcess = mSourceProcess;
599 : }
600 :
601 : // Otherwise, use our normal remote process selection mechanism for
602 : // opening the window. This will start a process if one is not
603 : // present.
604 : if (!targetProcess) {
605 : targetProcess =
606 : ContentParent::GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
607 : ContentParent::GetInitialProcessPriority(nullptr),
608 : nullptr);
609 : }
610 :
611 : // But starting a process can failure for any number of reasons. Reject the
612 : // promise if we could not.
613 : if (!targetProcess) {
614 : mPromise->Reject(NS_ERROR_ABORT, __func__);
615 : mPromise = nullptr;
616 : return NS_OK;
617 : }
618 :
619 : ClientOpenWindowOpParent* actor =
620 : new ClientOpenWindowOpParent(mArgs, mPromise);
621 :
622 : // If this fails the actor will be automatically destroyed which will
623 : // reject the promise.
624 : Unused << targetProcess->SendPClientOpenWindowOpConstructor(actor, mArgs);
625 :
626 : return NS_OK;
627 : }
628 : };
629 :
630 : } // anonymous namespace
631 :
632 : RefPtr<ClientOpPromise>
633 : ClientManagerService::OpenWindow(const ClientOpenWindowArgs& aArgs,
634 : already_AddRefed<ContentParent> aSourceProcess)
635 : {
636 : RefPtr<ClientOpPromise::Private> promise =
637 : new ClientOpPromise::Private(__func__);
638 :
639 : nsCOMPtr<nsIRunnable> r = new OpenWindowRunnable(promise, aArgs,
640 : std::move(aSourceProcess));
641 : MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other,
642 : r.forget()));
643 :
644 : RefPtr<ClientOpPromise> ref = promise;
645 : return ref.forget();
646 : }
647 :
648 : } // namespace dom
649 : } // namespace mozilla
|