summaryrefslogtreecommitdiffstats
path: root/dom/clients/manager/ClientSourceOpChild.cpp
blob: 01176d7dca382746aaf22a512991b30e2c40f4bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* -*- 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 "ClientSourceOpChild.h"

#include "ClientSource.h"
#include "ClientSourceChild.h"
#include "mozilla/Assertions.h"
#include "mozilla/Unused.h"

namespace mozilla::dom {

ClientSource* ClientSourceOpChild::GetSource() const {
  auto actor = static_cast<ClientSourceChild*>(Manager());
  return actor->GetSource();
}

template <typename Method, typename... Args>
void ClientSourceOpChild::DoSourceOp(Method aMethod, Args&&... aArgs) {
  RefPtr<ClientOpPromise> promise;
  nsCOMPtr<nsISerialEventTarget> target;

  // Some ClientSource operations can cause the ClientSource to be destroyed.
  // This means we should reference the ClientSource pointer for the minimum
  // possible to start the operation.  Use an extra block scope here to help
  // enforce this and prevent accidental usage later in the method.
  {
    ClientSource* source = GetSource();
    if (!source) {
      CopyableErrorResult rv;
      rv.ThrowAbortError("Unknown Client");
      Unused << PClientSourceOpChild::Send__delete__(this, rv);
      return;
    }

    target = source->EventTarget();

    // This may cause the ClientSource object to be destroyed.  Do not
    // use the source variable after this call.
    promise = (source->*aMethod)(std::forward<Args>(aArgs)...);
  }

  // The ClientSource methods are required to always return a promise.  If
  // they encounter an error they should just immediately resolve or reject
  // the promise as appropriate.
  MOZ_DIAGNOSTIC_ASSERT(promise);

  // Capture 'this' is safe here because we disconnect the promise
  // ActorDestroy() which ensures neither lambda is called if the
  // actor is destroyed before the source operation completes.
  //
  // Also capture the promise to ensure it lives until we get a reaction
  // or the actor starts shutting down and we disconnect our Thenable.
  // If the ClientSource is doing something async it may throw away the
  // promise on its side if the global is closed.
  promise
      ->Then(
          target, __func__,
          [this, promise](const mozilla::dom::ClientOpResult& aResult) {
            mPromiseRequestHolder.Complete();
            Unused << PClientSourceOpChild::Send__delete__(this, aResult);
          },
          [this, promise](const CopyableErrorResult& aRv) {
            mPromiseRequestHolder.Complete();
            Unused << PClientSourceOpChild::Send__delete__(this, aRv);
          })
      ->Track(mPromiseRequestHolder);
}

void ClientSourceOpChild::ActorDestroy(ActorDestroyReason aReason) {
  Cleanup();
}

void ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs) {
  switch (aArgs.type()) {
    case ClientOpConstructorArgs::TClientControlledArgs: {
      DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
      break;
    }
    case ClientOpConstructorArgs::TClientFocusArgs: {
      DoSourceOp(&ClientSource::Focus, aArgs.get_ClientFocusArgs());
      break;
    }
    case ClientOpConstructorArgs::TClientPostMessageArgs: {
      DoSourceOp(&ClientSource::PostMessage, aArgs.get_ClientPostMessageArgs());
      break;
    }
    case ClientOpConstructorArgs::TClientClaimArgs: {
      MOZ_ASSERT_UNREACHABLE("shouldn't happen with parent intercept");
      break;
    }
    case ClientOpConstructorArgs::TClientGetInfoAndStateArgs: {
      DoSourceOp(&ClientSource::GetInfoAndState,
                 aArgs.get_ClientGetInfoAndStateArgs());
      break;
    }
    case ClientOpConstructorArgs::TClientEvictBFCacheArgs: {
      DoSourceOp(&ClientSource::EvictFromBFCacheOp);
      break;
    }
    default: {
      MOZ_ASSERT_UNREACHABLE("unknown client operation!");
      break;
    }
  }

  mInitialized.Flip();

  if (mDeletionRequested) {
    Cleanup();
    delete this;
  }
}

void ClientSourceOpChild::ScheduleDeletion() {
  if (mInitialized) {
    Cleanup();
    delete this;
    return;
  }

  mDeletionRequested.Flip();
}

ClientSourceOpChild::~ClientSourceOpChild() {
  MOZ_DIAGNOSTIC_ASSERT(mInitialized);
}

void ClientSourceOpChild::Cleanup() {
  mPromiseRequestHolder.DisconnectIfExists();
}

}  // namespace mozilla::dom