Bug 1418048 - Add a callback-based Send API to async returning IPDL methods, r=billm
authorNika Layzell <nika@thelayzells.com>
Thu, 16 Nov 2017 15:12:06 -0500
changeset 392811 e37007fcbd347557bb6f7935037536a46c91fcbd
parent 392810 e75a922032c701a6321b5c41fab0611fc03dc4d1
child 392812 ea19dfc66ee0a3711d4c6f9050a2ae69a545eada
push id32942
push usertoros@mozilla.com
push dateTue, 21 Nov 2017 09:56:05 +0000
treeherdermozilla-central@72ee4800d415 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1418048
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1418048 - Add a callback-based Send API to async returning IPDL methods, r=billm Currently if you write an async IPDL method which has a return value, we expose a SendXXX method which returns a MozPromise. This MozPromise can then be ->Then-ed to run code when it is resolved or rejected. Unfortunately, using this API loses ordering guarantees which IPDL provides. MozPromise::Then takes an event target, which the resolve runnable is dispatched to. This means that the resolve callback's code doesn't have any ordering guarantees relative to the processing of other IPC messages coming over the same protocol. This adds a new overload to SendXXX with two additional arguments, a lambda callback which is called if the call succeeds, and a lambda callback which is called if the call fails. These will be called in order with other IPC messages sent over the same protocol. MozReview-Commit-ID: FZHJJaSDoZy
dom/cache/CacheStreamControlChild.cpp
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/ipdl/ipdl/lower.py
ipc/ipdl/test/cxx/TestAsyncReturns.cpp
netwerk/protocol/res/ExtensionProtocolHandler.cpp
toolkit/components/extensions/webrequest/StreamFilter.cpp
tools/profiler/gecko/nsProfiler.cpp
tools/profiler/public/ProfilerParent.h
widget/PuppetWidget.cpp
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -123,17 +123,17 @@ CacheStreamControlChild::OpenStream(cons
   // MozPromise resolve runnable is already in the event queue when the
   // worker wants to shut down.
   RefPtr<CacheWorkerHolder> holder = GetWorkerHolder();
 
   SendOpenStream(aId)->Then(GetCurrentThreadSerialEventTarget(), __func__,
   [aResolver, holder](const OptionalIPCStream& aOptionalStream) {
     nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream);
     aResolver(Move(stream));
-  }, [aResolver, holder](PromiseRejectReason aReason) {
+  }, [aResolver, holder](ResponseRejectReason aReason) {
     aResolver(nullptr);
   });
 }
 
 void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -479,36 +479,36 @@ private:
 
     // Next item in mChan->mTransactionStack.
     AutoEnterTransaction *mNext;
 
     // Pointer the a reply received for this message, if one was received.
     UniquePtr<IPC::Message> mReply;
 };
 
-class PromiseReporter final : public nsIMemoryReporter
+class PendingResponseReporter final : public nsIMemoryReporter
 {
-    ~PromiseReporter() {}
+    ~PendingResponseReporter() {}
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     NS_IMETHOD
     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
                    bool aAnonymize) override
     {
         MOZ_COLLECT_REPORT(
-            "unresolved-ipc-promises", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedPromises,
-            "Outstanding IPC async message promises that is still not resolved.");
+            "unresolved-ipc-responses", KIND_OTHER, UNITS_COUNT, MessageChannel::gUnresolvedResponses,
+            "Outstanding IPC async message responses that are still not resolved.");
         return NS_OK;
     }
 };
 
-NS_IMPL_ISUPPORTS(PromiseReporter, nsIMemoryReporter)
-
-Atomic<size_t> MessageChannel::gUnresolvedPromises;
+NS_IMPL_ISUPPORTS(PendingResponseReporter, nsIMemoryReporter)
+
+Atomic<size_t> MessageChannel::gUnresolvedResponses;
 
 MessageChannel::MessageChannel(const char* aName,
                                IToplevelProtocol *aListener)
   : mName(aName),
     mListener(aListener),
     mChannelState(ChannelClosed),
     mSide(UnknownSide),
     mLink(nullptr),
@@ -549,17 +549,17 @@ MessageChannel::MessageChannel(const cha
 
 #ifdef OS_WIN
     mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
     MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
 #endif
 
     static Atomic<bool> registered;
     if (registered.compareExchange(false, true)) {
-        RegisterStrongMemoryReporter(new PromiseReporter());
+        RegisterStrongMemoryReporter(new PendingResponseReporter());
     }
 }
 
 MessageChannel::~MessageChannel()
 {
     MOZ_COUNT_DTOR(ipc::MessageChannel);
     IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
 #ifdef OS_WIN
@@ -712,23 +712,21 @@ MessageChannel::Clear()
     if (gParentProcessBlocker == this) {
         gParentProcessBlocker = nullptr;
     }
 
     if (mWorkerLoop) {
         mWorkerLoop->RemoveDestructionObserver(this);
     }
 
-    gUnresolvedPromises -= mPendingPromises.size();
-    for (auto& pair : mPendingPromises) {
-        pair.second.mRejectFunction(pair.second.mPromise,
-                                    PromiseRejectReason::ChannelClosed,
-                                    __func__);
+    gUnresolvedResponses -= mPendingResponses.size();
+    for (auto& pair : mPendingResponses) {
+        pair.second.get()->Reject(ResponseRejectReason::ChannelClosed);
     }
-    mPendingPromises.clear();
+    mPendingResponses.clear();
 
     mWorkerLoop = nullptr;
     delete mLink;
     mLink = nullptr;
 
     mOnChannelConnectedTask->Cancel();
 
     if (mChannelErrorTask) {
@@ -946,46 +944,43 @@ MessageChannel::StopPostponingSends()
     }
 
     // We unset this after SendMessage so we can make correct thread
     // assertions in MessageLink.
     mIsPostponingSends = false;
     mPostponedSends.clear();
 }
 
-already_AddRefed<MozPromiseRefcountable>
-MessageChannel::PopPromise(const Message& aMsg)
+UniquePtr<MessageChannel::UntypedCallbackHolder>
+MessageChannel::PopCallback(const Message& aMsg)
 {
-    auto iter = mPendingPromises.find(aMsg.seqno());
-    if (iter != mPendingPromises.end()) {
-        PromiseHolder ret = iter->second;
-        mPendingPromises.erase(iter);
-        gUnresolvedPromises--;
-        return ret.mPromise.forget();
+    auto iter = mPendingResponses.find(aMsg.seqno());
+    if (iter != mPendingResponses.end()) {
+        UniquePtr<MessageChannel::UntypedCallbackHolder> ret = Move(iter->second);
+        mPendingResponses.erase(iter);
+        gUnresolvedResponses--;
+        return ret;
     }
     return nullptr;
 }
 
 void
-MessageChannel::RejectPendingPromisesForActor(ActorIdType aActorId)
+MessageChannel::RejectPendingResponsesForActor(ActorIdType aActorId)
 {
-  auto itr = mPendingPromises.begin();
-  while (itr != mPendingPromises.end()) {
-    if (itr->second.mActorId != aActorId) {
+  auto itr = mPendingResponses.begin();
+  while (itr != mPendingResponses.end()) {
+    if (itr->second.get()->mActorId != aActorId) {
       ++itr;
       continue;
     }
-    auto& promise = itr->second.mPromise;
-    itr->second.mRejectFunction(promise,
-                                PromiseRejectReason::ActorDestroyed,
-                                __func__);
+    itr->second.get()->Reject(ResponseRejectReason::ActorDestroyed);
     // Take special care of advancing the iterator since we are
     // removing it while iterating.
-    itr = mPendingPromises.erase(itr);
-    gUnresolvedPromises--;
+    itr = mPendingResponses.erase(itr);
+    gUnresolvedResponses--;
   }
 }
 
 class BuildIDMessage : public IPC::Message
 {
 public:
     BuildIDMessage()
         : IPC::Message(MSG_ROUTING_NONE, BUILD_ID_MESSAGE_TYPE)
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -61,24 +61,29 @@ enum class SyncSendError {
     NotConnectedBeforeSend,
     DisconnectedDuringSend,
     CancelledBeforeSend,
     CancelledAfterSend,
     TimedOut,
     ReplyError,
 };
 
-enum class PromiseRejectReason {
+enum class ResponseRejectReason {
     SendError,
     ChannelClosed,
     HandlerRejected,
     ActorDestroyed,
     EndGuard_,
 };
 
+template<typename T>
+using ResolveCallback = std::function<void (T&&)>;
+
+using RejectCallback = std::function<void (ResponseRejectReason)>;
+
 enum ChannelState {
     ChannelClosed,
     ChannelOpening,
     ChannelConnected,
     ChannelTimeout,
     ChannelClosing,
     ChannelError
 };
@@ -95,30 +100,55 @@ class MessageChannel : HasResultCodes, M
 
     typedef mozilla::Monitor Monitor;
 
     // We could templatize the actor type but it would unnecessarily
     // expand the code size. Using the actor address as the
     // identifier is already good enough.
     typedef void* ActorIdType;
 
-    struct PromiseHolder
+public:
+    struct UntypedCallbackHolder
     {
-        RefPtr<MozPromiseRefcountable> mPromise;
+        UntypedCallbackHolder(ActorIdType aActorId,
+                              RejectCallback aReject)
+            : mActorId(aActorId)
+            , mReject(Move(aReject))
+        {}
 
-        // For rejecting and removing the pending promises when a
-        // subprotocol is destoryed.
+        virtual ~UntypedCallbackHolder() {}
+
+        void Reject(ResponseRejectReason aReason) {
+            mReject(aReason);
+        }
+
         ActorIdType mActorId;
+        RejectCallback mReject;
+    };
 
-        std::function<void(MozPromiseRefcountable*,
-                           PromiseRejectReason,
-                           const char*)> mRejectFunction;
+    template<typename Value>
+    struct CallbackHolder : public UntypedCallbackHolder
+    {
+        CallbackHolder(ActorIdType aActorId,
+                       ResolveCallback<Value> aResolve,
+                       RejectCallback aReject)
+            : UntypedCallbackHolder(aActorId, Move(aReject))
+            , mResolve(Move(aResolve))
+        {}
+
+        void Resolve(Value&& aReason) {
+            mResolve(Move(aReason));
+        }
+
+        ResolveCallback<Value> mResolve;
     };
-    static Atomic<size_t> gUnresolvedPromises;
-    friend class PromiseReporter;
+
+private:
+    static Atomic<size_t> gUnresolvedResponses;
+    friend class PendingResponseReporter;
 
   public:
     static const int32_t kNoTimeout;
 
     typedef IPC::Message Message;
     typedef IPC::MessageInfo MessageInfo;
     typedef mozilla::ipc::Transport Transport;
 
@@ -183,35 +213,34 @@ class MessageChannel : HasResultCodes, M
     };
     void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
     ChannelFlags GetChannelFlags() { return mFlags; }
 
     // Asynchronously send a message to the other side of the channel
     bool Send(Message* aMsg);
 
     // Asynchronously send a message to the other side of the channel
-    // and wait for asynchronous reply
-    template<typename Promise>
-    bool Send(Message* aMsg, Promise* aPromise, ActorIdType aActorId) {
+    // and wait for asynchronous reply.
+    template<typename Value>
+    void Send(Message* aMsg,
+              ActorIdType aActorId,
+              ResolveCallback<Value> aResolve,
+              RejectCallback aReject) {
         int32_t seqno = NextSeqno();
         aMsg->set_seqno(seqno);
         if (!Send(aMsg)) {
-            return false;
+            aReject(ResponseRejectReason::SendError);
+            return;
         }
-        PromiseHolder holder;
-        holder.mPromise = aPromise;
-        holder.mActorId = aActorId;
-        holder.mRejectFunction = [](MozPromiseRefcountable* aRejectPromise,
-                                    PromiseRejectReason aReason,
-                                    const char* aRejectSite) {
-            static_cast<Promise*>(aRejectPromise)->Reject(aReason, aRejectSite);
-        };
-        mPendingPromises.insert(std::make_pair(seqno, Move(holder)));
-        gUnresolvedPromises++;
-        return true;
+
+        UniquePtr<UntypedCallbackHolder> callback =
+            MakeUnique<CallbackHolder<Value>>(
+                aActorId, Move(aResolve), Move(aReject));
+        mPendingResponses.insert(std::make_pair(seqno, Move(callback)));
+        gUnresolvedResponses++;
     }
 
     void SendBuildID();
 
     // Asynchronously deliver a message back to this side of the
     // channel
     bool Echo(Message* aMsg);
 
@@ -221,22 +250,22 @@ class MessageChannel : HasResultCodes, M
     // Make an Interrupt call to the other side of the channel
     bool Call(Message* aMsg, Message* aReply);
 
     // Wait until a message is received
     bool WaitForIncomingMessage();
 
     bool CanSend() const;
 
-    // Remove and return a promise that needs reply
-    already_AddRefed<MozPromiseRefcountable> PopPromise(const Message& aMsg);
+    // Remove and return a callback that needs reply
+    UniquePtr<UntypedCallbackHolder> PopCallback(const Message& aMsg);
 
-    // Used to reject and remove pending promises owned by the given
+    // Used to reject and remove pending responses owned by the given
     // actor when it's about to be destroyed.
-    void RejectPendingPromisesForActor(ActorIdType aActorId);
+    void RejectPendingResponsesForActor(ActorIdType aActorId);
 
     // If sending a sync message returns an error, this function gives a more
     // descriptive error message.
     SyncSendError LastSendError() const {
         AssertWorkerThread();
         return mLastSendError;
     }
 
@@ -576,17 +605,17 @@ class MessageChannel : HasResultCodes, M
         bool mScheduled : 1;
     };
 
     bool ShouldRunMessage(const Message& aMsg);
     void RunMessage(MessageTask& aTask);
 
     typedef LinkedList<RefPtr<MessageTask>> MessageQueue;
     typedef std::map<size_t, Message> MessageMap;
-    typedef std::map<size_t, PromiseHolder> PromiseMap;
+    typedef std::map<size_t, UniquePtr<UntypedCallbackHolder>> CallbackMap;
     typedef IPC::Message::msgid_t msgid_t;
 
     void WillDestroyCurrentMessageLoop() override;
 
   private:
     // This will be a string literal, so lifetime is not an issue.
     const char* mName;
 
@@ -787,18 +816,18 @@ class MessageChannel : HasResultCodes, M
     // mMonitor.
     bool mIsWaitingForIncoming;
 
     // Map of replies received "out of turn", because of Interrupt
     // in-calls racing with replies to outstanding in-calls.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
     MessageMap mOutOfTurnReplies;
 
-    // Map of async Promises that are still waiting replies.
-    PromiseMap mPendingPromises;
+    // Map of async Callbacks that are still waiting replies.
+    CallbackMap mPendingResponses;
 
     // Stack of Interrupt in-calls that were deferred because of race
     // conditions.
     std::stack<Message> mDeferred;
 
 #ifdef OS_WIN
     HANDLE mEvent;
 #endif
@@ -830,16 +859,16 @@ class MessageChannel : HasResultCodes, M
 void
 CancelCPOWs();
 
 } // namespace ipc
 } // namespace mozilla
 
 namespace IPC {
 template <>
-struct ParamTraits<mozilla::ipc::PromiseRejectReason>
-    : public ContiguousEnumSerializer<mozilla::ipc::PromiseRejectReason,
-                                      mozilla::ipc::PromiseRejectReason::SendError,
-                                      mozilla::ipc::PromiseRejectReason::EndGuard_>
+struct ParamTraits<mozilla::ipc::ResponseRejectReason>
+    : public ContiguousEnumSerializer<mozilla::ipc::ResponseRejectReason,
+                                      mozilla::ipc::ResponseRejectReason::SendError,
+                                      mozilla::ipc::ResponseRejectReason::EndGuard_>
 { };
 } // namespace IPC
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -337,17 +337,17 @@ def _makePromise(returns, side, resolver
     if len(returns) > 1:
         resolvetype = _tuple([d.bareType(side) for d in returns])
     else:
         resolvetype = returns[0].bareType(side)
 
     needmove = not all(d.isCopyable() for d in returns)
 
     return _promise(resolvetype,
-                    _PromiseRejectReason.Type(),
+                    _ResponseRejectReason.Type(),
                     ExprLiteral.TRUE if needmove else ExprLiteral.FALSE,
                     resolver=resolver)
 
 def _makeResolver(returns, side):
     if len(returns) > 1:
         resolvetype = _tuple([d.moveType(side) for d in returns])
     else:
         resolvetype = returns[0].moveType(side)
@@ -512,25 +512,25 @@ class _DestroyReason:
     def Type():  return Type('ActorDestroyReason')
 
     Deletion = ExprVar('Deletion')
     AncestorDeletion = ExprVar('AncestorDeletion')
     NormalShutdown = ExprVar('NormalShutdown')
     AbnormalShutdown = ExprVar('AbnormalShutdown')
     FailedConstructor = ExprVar('FailedConstructor')
 
-class _PromiseRejectReason:
+class _ResponseRejectReason:
     @staticmethod
     def Type():
-        return Type('PromiseRejectReason')
-
-    SendError = ExprVar('PromiseRejectReason::SendError')
-    ChannelClosed = ExprVar('PromiseRejectReason::ChannelClosed')
-    HandlerRejected = ExprVar('PromiseRejectReason::HandlerRejected')
-    ActorDestroyed = ExprVar('PromiseRejectReason::ActorDestroyed')
+        return Type('ResponseRejectReason')
+
+    SendError = ExprVar('ResponseRejectReason::SendError')
+    ChannelClosed = ExprVar('ResponseRejectReason::ChannelClosed')
+    HandlerRejected = ExprVar('ResponseRejectReason::HandlerRejected')
+    ActorDestroyed = ExprVar('ResponseRejectReason::ActorDestroyed')
 
 
 ##-----------------------------------------------------------------------------
 ## Intermediate representation (IR) nodes used during lowering
 
 class _ConvertToCxxType(TypeVisitor):
     def __init__(self, side, fq):
         self.side = side
@@ -1015,22 +1015,37 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 return Decl(d.moveType(side), d.name)
             elif sems is 'out':
                 return Decl(d.outType(side), d.name)
             else: assert 0
 
         def makeResolverDecl(returns):
             return Decl(Type(self.resolverName(), ref=2), 'aResolve')
 
+        def makeCallbackResolveDecl(returns):
+            if len(returns) > 1:
+                resolvetype = _tuple([d.bareType(side) for d in returns])
+            else:
+                resolvetype = returns[0].bareType(side)
+
+            return Decl(Type("mozilla::ipc::ResolveCallback", T=resolvetype),
+                        'aResolve')
+
+        def makeCallbackRejectDecl(returns):
+            return Decl(Type("mozilla::ipc::RejectCallback"), 'aReject')
+
         cxxparams = [ ]
         if paramsems is not None:
             cxxparams.extend([ makeDecl(d, paramsems) for d in self.params ])
 
         if returnsems is 'promise' and self.returns:
             pass
+        elif returnsems is 'callback' and self.returns:
+            cxxparams.extend([ makeCallbackResolveDecl(self.returns),
+                               makeCallbackRejectDecl(self.returns) ])
         elif returnsems is 'resolver' and self.returns:
             cxxparams.extend([ makeResolverDecl(self.returns) ])
         elif returnsems is not None:
             cxxparams.extend([ makeDecl(r, returnsems) for r in self.returns ])
 
         if not implicit and self.decl.type.hasImplicitActorParam():
             cxxparams = cxxparams[1:]
 
@@ -1319,18 +1334,18 @@ with some new IPDL/C++ nodes that are tu
                                 Typedef(Type('mozilla::ipc::ProtocolId'),
                                         'ProtocolId'),
                                 Typedef(Type('mozilla::ipc::Transport'),
                                         'Transport'),
                                 Typedef(Type('mozilla::ipc::Endpoint'),
                                         'Endpoint', ['FooSide']),
                                 Typedef(Type('mozilla::ipc::TransportDescriptor'),
                                         'TransportDescriptor'),
-                                Typedef(Type('mozilla::ipc::PromiseRejectReason'),
-                                        'PromiseRejectReason') ])
+                                Typedef(Type('mozilla::ipc::ResponseRejectReason'),
+                                        'ResponseRejectReason') ])
         self.protocolName = None
 
     def visitTranslationUnit(self, tu):
         if tu not in self.visitedTus:
             self.visitedTus.add(tu)
             ipdl.ast.Visitor.visitTranslationUnit(self, tu)
             if not isinstance(tu, TranslationUnit):
                 TranslationUnit.upgrade(tu)
@@ -3141,23 +3156,23 @@ class _GenerateProtocolActorCode(ipdl.as
                                   args=[ kidsvar ])),
                 foreachdestroy,
             ])
             destroysubtree.addstmt(block)
 
         if len(ptype.manages):
             destroysubtree.addstmt(Whitespace.NL)
 
-        # Reject pending promises for actor before calling ActorDestroy().
-        rejectPendingPromiseMethod = ExprSelect(self.protocol.callGetChannel(),
-                                                '->',
-                                                'RejectPendingPromisesForActor')
-        destroysubtree.addstmts([ Whitespace('// Reject owning pending promises.\n',
+        # Reject pending responses for actor before calling ActorDestroy().
+        rejectPendingResponsesMethod = ExprSelect(self.protocol.callGetChannel(),
+                                                  '->',
+                                                  'RejectPendingResponsesForActor')
+        destroysubtree.addstmts([ Whitespace('// Reject owning pending responses.\n',
                                              indent=1),
-                                  StmtExpr(ExprCall(rejectPendingPromiseMethod,
+                                  StmtExpr(ExprCall(rejectPendingResponsesMethod,
                                                     args=[ ExprVar('this') ])),
                                   Whitespace.NL
                                  ])
 
         destroysubtree.addstmts([ Whitespace('// Finally, destroy "us".\n',
                                              indent=1),
                                   StmtExpr(ExprCall(_destroyMethod(),
                                                     args=[ whyvar ]))
@@ -3858,16 +3873,17 @@ class _GenerateProtocolActorCode(ipdl.as
         return block
 
 
     def visitMessageDecl(self, md):
         isctor = md.decl.type.isCtor()
         isdtor = md.decl.type.isDtor()
         decltype = md.decl.type
         sendmethod = None
+        promisesendmethod = None
         helpermethod = None
         recvlbl, recvcase = None, None
 
         def addRecvCase(lbl, case):
             if decltype.isAsync():
                 self.asyncSwitch.addcase(lbl, case)
             elif decltype.isSync():
                 self.syncSwitch.addcase(lbl, case)
@@ -3885,26 +3901,29 @@ class _GenerateProtocolActorCode(ipdl.as
                 sendmethod, (recvlbl, recvcase) = self.genAsyncCtor(md)
             elif isctor:
                 sendmethod = self.genBlockingCtorMethod(md)
             elif isdtor and isasync:
                 sendmethod, (recvlbl, recvcase) = self.genAsyncDtor(md)
             elif isdtor:
                 sendmethod = self.genBlockingDtorMethod(md)
             elif isasync:
-                sendmethod, (recvlbl, recvcase) = self.genAsyncSendMethod(md)
+                sendmethod, promisesendmethod, (recvlbl, recvcase) = \
+                    self.genAsyncSendMethod(md)
             else:
                 sendmethod = self.genBlockingSendMethod(md)
 
         # XXX figure out what to do here
         if isdtor and md.decl.type.constructedType().isToplevel():
             sendmethod = None
 
         if sendmethod is not None:
             self.cls.addstmts([ sendmethod, Whitespace.NL ])
+        if promisesendmethod is not None:
+            self.cls.addstmts([ promisesendmethod, Whitespace.NL ])
         if recvcase is not None:
             addRecvCase(recvlbl, recvcase)
             recvlbl, recvcase = None, None
 
         if self.receivesMessage(md):
             if isctor:
                 recvlbl, recvcase = self.genCtorRecvCase(md)
             elif isdtor:
@@ -4112,65 +4131,85 @@ class _GenerateProtocolActorCode(ipdl.as
     def dtorEpilogue(self, md, actorexpr):
         return self.destroyActor(md, actorexpr)
 
     def genRecvAsyncReplyCase(self, md):
         lbl = CaseLabel(md.pqReplyId())
         case = StmtBlock()
         resolve, reason, prologue, desrej, desstmts = self.deserializeAsyncReply(
             md, self.side, errfnRecv, errfnSentinel(_Result.ValuError))
-        ifnotpromise = StmtIf(ExprNot(ExprVar('promise')))
-        ifnotpromise.addifstmts(errfnRecv("Error unknown promise",
+        ifnocallback = StmtIf(ExprNot(ExprVar('callback')))
+        ifnocallback.addifstmts(errfnRecv("Error unknown callback",
                                           _Result.ProcessingError))
-        promise = _makePromise(md.returns, self.side, resolver=True)
-        promiseptr = _makePromise(md.returns, self.side, resolver=True)
-        promiseptr.ptr = 1
-        getpromise = [ Whitespace.NL,
-                       StmtDecl(Decl(_refptr(promise), 'promise'),
-                                init=ExprCall(ExprSelect(ExprCall(ExprSelect(self.protocol.callGetChannel(), '->', 'PopPromise'),
-                                                                  args=[ self.msgvar ]),
-                                                         '.', Type('downcast', T=promise)))),
-                       ifnotpromise ]
+
         if len(md.returns) > 1:
+            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
             resolvearg = ExprCall(ExprVar('MakeTuple'),
                                   args=[ExprMove(p.var()) for p in md.returns])
         else:
+            resolvetype = md.returns[0].bareType(self.side)
             resolvearg = ExprMove(md.returns[0].var())
 
-        resolvepromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Resolve'),
-                                             args=[ resolvearg,
-                                                    ExprVar('__func__')])) ]
-        rejectpromise = [ StmtExpr(ExprCall(ExprSelect(ExprVar('promise'), '->', 'Reject'),
-                                            args=[ reason, ExprVar('__func__') ])) ]
+        untypedcallback = Type("MessageChannel::UntypedCallbackHolder")
+        callbackptr = Type("MessageChannel::CallbackHolder", T=resolvetype)
+        callbackptr.ptr = 1
+        callback = ExprVar('callback')
+
+        getcallback = [ Whitespace.NL,
+                        # Get the untyped callback object by calling PopCallback()
+                        StmtDecl(Decl(_uniqueptr(untypedcallback), 'untypedCallback'),
+                                 init=ExprCall(ExprSelect(self.protocol.callGetChannel(),
+                                                          '->', 'PopCallback'),
+                                               args=[ self.msgvar ])),
+                        # Cast the untyped callback pointer to the correct poiner type
+                        StmtDecl(Decl(callbackptr, callback.name),
+                                 init=ExprCast(ExprCall(ExprSelect(ExprVar('untypedCallback'),
+                                                                   '.', 'get')),
+                                               callbackptr,
+                                               static=1)),
+                        ifnocallback ]
+
+        resolvecallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Resolve'),
+                                              args=[ resolvearg ])) ]
+        rejectcallback = [ StmtExpr(ExprCall(ExprSelect(callback, '->', 'Reject'),
+                                             args=[ reason ])) ]
         ifresolve = StmtIf(resolve)
         ifresolve.addifstmts(desstmts)
-        ifresolve.addifstmts(resolvepromise)
+        ifresolve.addifstmts(resolvecallback)
         ifresolve.addelsestmts(desrej)
-        ifresolve.addelsestmts(rejectpromise)
+        ifresolve.addelsestmts(rejectcallback)
         case.addstmts(prologue)
-        case.addstmts(getpromise)
+        case.addstmts(getcallback)
         case.addstmt(ifresolve)
         case.addstmt(StmtReturn(_Result.Processed))
         return (lbl, case)
 
     def genAsyncSendMethod(self, md):
         method = MethodDefn(self.makeSendMethodDecl(md))
         msgvar, stmts = self.makeMessage(md, errfnSend)
         retvar, sendstmts = self.sendAsync(md, msgvar)
 
         method.addstmts(stmts
                         +[ Whitespace.NL ]
                         + self.genVerifyMessage(md.decl.type.verify, md.params,
                                                 errfnSend, ExprVar('msg__'))
                         + sendstmts
                         +[ StmtReturn(retvar) ])
 
-        (lbl, case) = self.genRecvAsyncReplyCase(md) if md.returns else (None, None)
-        return method, (lbl, case)
-
+        # Add the promise overload if we need one.
+        if md.returns:
+            promisemethod = MethodDefn(self.makeSendMethodDecl(md, promise=True))
+            stmts = self.sendAsyncWithPromise(md)
+            promisemethod.addstmts(stmts)
+
+            (lbl, case) = self.genRecvAsyncReplyCase(md)
+        else:
+            (promisemethod, lbl, case) = (None, None, None)
+
+        return method, promisemethod, (lbl, case)
 
     def genBlockingSendMethod(self, md, fromActor=None):
         method = MethodDefn(self.makeSendMethodDecl(md))
 
         msgvar, serstmts = self.makeMessage(md, errfnSend, fromActor)
         replyvar = self.replyvar
 
         sendok, sendstmts = self.sendBlocking(md, msgvar, replyvar)
@@ -4339,21 +4378,21 @@ class _GenerateProtocolActorCode(ipdl.as
                                'aParam')
             destructexpr = ExprCall(ExprVar('Tie'),
                                     args=[ p.var() for p in md.returns ])
         else:
             resolvedecl = Decl(md.returns[0].moveType(self.side), 'aParam')
             destructexpr = md.returns[0].var()
         selfvar = ExprVar('self__')
         ifactorisdead = StmtIf(ExprNot(selfvar))
-        ifactorisdead.addifstmts([_printWarningMessage("Not resolving promise because actor is dead."),
+        ifactorisdead.addifstmts([_printWarningMessage("Not resolving response because actor is dead."),
                                   StmtReturn()])
         ifactorisdestroyed = StmtIf(ExprBinary(self.protocol.stateVar(), '==',
                                                self.protocol.deadState()))
-        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving promise because actor is destroyed."),
+        ifactorisdestroyed.addifstmts([_printWarningMessage("Not resolving response because actor is destroyed."),
                                   StmtReturn()])
         returnifactorisdead = [ ifactorisdead,
                                 ifactorisdestroyed ]
         resolverfn = ExprLambda([ExprVar.THIS, selfvar, routingId, seqno],
                                  [resolvedecl])
         resolverfn.addstmts(returnifactorisdead
                             + [ StmtDecl(Decl(Type.BOOL, resolve.name),
                                          init=ExprLiteral.TRUE) ]
@@ -4365,25 +4404,25 @@ class _GenerateProtocolActorCode(ipdl.as
                                                        args=[ routingId ])) ]
                             + [ self.checkedWrite(None, resolve, self.replyvar,
                                                   sentinelKey=resolve.name) ]
                             + [ self.checkedWrite(r.ipdltype, r.var(), self.replyvar,
                                                   sentinelKey=r.name)
                                  for r in md.returns ])
         resolverfn.addstmts(sendmsg)
 
-        makepromise = [ Whitespace.NL,
-                        StmtDecl(Decl(Type.INT32, seqno.name),
-                                 init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
-                        StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
-                                      selfvar.name),
-                                 init=ExprVar.THIS),
-                        StmtDecl(Decl(resolvertype, 'resolver'),
-                                 init=resolverfn) ]
-        return makepromise
+        makeresolver = [ Whitespace.NL,
+                         StmtDecl(Decl(Type.INT32, seqno.name),
+                                  init=ExprCall(ExprSelect(self.msgvar, '.', 'seqno'))),
+                         StmtDecl(Decl(Type('WeakPtr', T=ExprVar(self.clsname)),
+                                       selfvar.name),
+                                  init=ExprVar.THIS),
+                         StmtDecl(Decl(resolvertype, 'resolver'),
+                                  init=resolverfn) ]
+        return makeresolver
 
     def makeReply(self, md, errfn, routingId):
         if routingId is None:
             routingId = self.protocol.routingId()
         # TODO special cases for async ctor/dtor replies
         if not md.decl.type.hasReply():
             return [ ]
         if md.decl.type.isAsync() and md.decl.type.hasReply():
@@ -4500,17 +4539,17 @@ class _GenerateProtocolActorCode(ipdl.as
         isctor = md.decl.type.isCtor()
         resolve = ExprVar('resolve__')
         reason = ExprVar('reason__')
         desresolve = [ StmtDecl(Decl(Type.BOOL, resolve.name)),
                        self.checkedRead(None, ExprAddrOf(resolve), msgexpr,
                                         ExprAddrOf(itervar),
                                         errfn, "'%s'" % resolve.name,
                                         sentinelKey=resolve.name, errfnSentinel=errfnSent) ]
-        desrej = [ StmtDecl(Decl(_PromiseRejectReason.Type(), reason.name)),
+        desrej = [ StmtDecl(Decl(_ResponseRejectReason.Type(), reason.name)),
                    self.checkedRead(None, ExprAddrOf(reason), msgexpr,
                                     ExprAddrOf(itervar),
                                     errfn, "'%s'" % reason.name,
                                     sentinelKey=reason.name, errfnSentinel=errfnSent),
                    self.endRead(msgvar, itervar) ]
         prologue = ([
             self.logMessage(md, msgexpr, 'Received ',
                             receiving=True),
@@ -4576,47 +4615,37 @@ class _GenerateProtocolActorCode(ipdl.as
                                  sentinelKey=r.name, errfnSentinel=errfnSentinel)
                 for r in md.returns ]
             + [ self.endRead(self.replyvar, itervar) ])
 
         return stmts
 
     def sendAsync(self, md, msgexpr, actor=None):
         sendok = ExprVar('sendok__')
-        retvar = sendok
-        if md.returns:
-            retpromise = ExprVar('promise__')
-            promise = _makePromise(md.returns, self.side, resolver=True)
-            promisedecl = [ Whitespace.NL,
-                            StmtDecl(Decl(_refptr(promise), retpromise.name),
-                                     init=ExprNew(promise, args=[ExprVar('__func__')])) ]
-            rejectifsendok = StmtIf(ExprNot(sendok))
-            rejectifsendok.addifstmts(
-                [ StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
-                                    args=[ _PromiseRejectReason.SendError,
-                                           ExprVar('__func__') ])) ])
+        resolvefn = ExprVar('aResolve')
+        rejectfn = ExprVar('aReject')
+
         sendargs = [ msgexpr ]
         stmts = [ Whitespace.NL,
                   self.logMessage(md, msgexpr, 'Sending ', actor),
                   self.profilerLabel(md) ] + self.transition(md, actor)
-
+        stmts.append(Whitespace.NL)
+
+        # Generate the actual call expression.
+        send = ExprSelect(self.protocol.callGetChannel(actor), '->', 'Send')
         if md.returns:
-            sendargs.append(ExprCall(ExprSelect(retpromise, '.', 'get')))
-            sendargs.append(ExprVar('this'))
-            stmts.extend(promisedecl)
-            retvar = retpromise
-
-        stmts.extend([ Whitespace.NL,
-                       StmtDecl(Decl(Type.BOOL, sendok.name),
-                                init=ExprCall(
-                                    ExprSelect(self.protocol.callGetChannel(actor),
-                                               '->', 'Send'),
-                                    args=sendargs)) ])
-        if md.returns:
-            stmts.append(rejectifsendok)
+            stmts.append(StmtExpr(ExprCall(send, args=[ msgexpr,
+                                                        ExprVar('this'),
+                                                        resolvefn,
+                                                        rejectfn ])))
+            retvar = None
+        else:
+            stmts.append(StmtDecl(Decl(Type.BOOL, sendok.name),
+                                  init=ExprCall(send, args=[ msgexpr ])))
+            retvar = sendok
 
         return (retvar, stmts)
 
     def sendBlocking(self, md, msgexpr, replyexpr, actor=None):
         sendok = ExprVar('sendok__')
         return (
             sendok,
             ([ Whitespace.NL,
@@ -4633,16 +4662,52 @@ class _GenerateProtocolActorCode(ipdl.as
                                       ExprCall(ExprSelect(self.protocol.callGetChannel(actor),
                                                           '->',
                                                           _sendPrefix(md.decl.type)),
                                                args=[ msgexpr, ExprAddrOf(replyexpr) ]))),
                 ])
             ])
         )
 
+    def sendAsyncWithPromise(self, md):
+        # Create a new promise, and forward to the callback send overload.
+        retpromise = ExprVar('promise__')
+        promise = _makePromise(md.returns, self.side, resolver=True)
+        stmts = [ Whitespace.NL,
+                  StmtDecl(Decl(_refptr(promise), retpromise.name),
+                           init=ExprNew(promise, args=[ExprVar('__func__')])) ]
+
+        if len(md.returns) > 1:
+            resolvetype = _tuple([d.bareType(self.side) for d in md.returns])
+        else:
+            resolvetype = md.returns[0].bareType(self.side)
+        resolvetype.ref = 2
+
+        resolvefn = ExprLambda([ retpromise ],
+                               [ Decl(resolvetype, "aValue") ])
+        resolvefn.addstmts([
+            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Resolve'),
+                              args=[ ExprMove(ExprVar('aValue')),
+                                     ExprVar('__func__') ])),
+        ])
+
+        rejectfn = ExprLambda([ retpromise ],
+                              [ Decl(_ResponseRejectReason.Type(), "aReason") ])
+        rejectfn.addstmts([
+            StmtExpr(ExprCall(ExprSelect(retpromise, '->', 'Reject'),
+                              args=[ ExprVar('aReason'),
+                                     ExprVar('__func__') ])),
+        ])
+
+        args = [ p.var() for p in md.params ] + [ resolvefn, rejectfn ]
+        stmts += [ Whitespace.NL,
+                   StmtExpr(ExprCall(ExprVar(md.sendMethod().name), args=args)),
+                   StmtReturn(retpromise) ]
+        return stmts
+
     def callAllocActor(self, md, retsems, side):
         return ExprCall(
             _allocMethod(md.decl.type.constructedType(), side),
             args=md.makeCxxArgs(retsems=retsems, retcallsems='out',
                                 implicit=0))
 
     def callActorDestroy(self, actorexpr, why=_DestroyReason.Deletion):
         return ExprCall(ExprSelect(actorexpr, '->', 'DestroySubtree'),
@@ -4681,29 +4746,34 @@ class _GenerateProtocolActorCode(ipdl.as
         ])
         return [ failif ]
 
     def makeDtorMethodDecl(self, md):
         decl = self.makeSendMethodDecl(md)
         decl.static = 1
         return decl
 
-    def makeSendMethodDecl(self, md):
+    def makeSendMethodDecl(self, md, promise=False):
         implicit = md.decl.type.hasImplicitActorParam()
         if md.decl.type.isAsync() and md.returns:
-            returnsems = 'promise'
-            rettype = _refptr(Type(md.promiseName()))
+            if promise:
+                returnsems = 'promise'
+                rettype = _refptr(Type(md.promiseName()))
+            else:
+                returnsems = 'callback'
+                rettype = Type.VOID
         else:
+            assert not promise
             returnsems = 'out'
             rettype = Type.BOOL
         decl = MethodDecl(
             md.sendMethod().name,
             params=md.makeCxxParams(paramsems='in', returnsems=returnsems,
                                     side=self.side, implicit=implicit),
-            warn_unused=(self.side == 'parent'),
+            warn_unused=(self.side == 'parent' and returnsems != 'callback'),
             ret=rettype)
         if md.decl.type.isCtor():
             decl.ret = md.actorDecl().bareType(self.side)
         return decl
 
     def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
         actorname = _actorName(self.protocol.name, self.side)
 
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -26,34 +26,47 @@ TestAsyncReturnsParent::~TestAsyncReturn
 
 void
 TestAsyncReturnsParent::Main()
 {
   SendNoReturn()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                        [](bool unused) {
                          fail("resolve handler should not be called");
                        },
-                       [](PromiseRejectReason aReason) {
+                       [](ResponseRejectReason aReason) {
                          // MozPromise asserts in debug build if the
                          // handler is not called
-                         if (aReason != PromiseRejectReason::ChannelClosed) {
+                         if (aReason != ResponseRejectReason::ChannelClosed) {
                            fail("reject with wrong reason");
                          }
                          passed("reject handler called on channel close");
                        });
   SendPing()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                    [this](bool one) {
                      if (one) {
                        passed("take one argument");
                      } else {
                        fail("get one argument but has wrong value");
                      }
-                     Close();
+
+                     // Also try with the callback-based API.
+                     SendPing(
+                       [this](bool one) {
+                         if (one) {
+                           passed("take one argument");
+                         } else {
+                           fail("get one argument but has wrong value");
+                         }
+                         Close();
+                       },
+                       [](ResponseRejectReason aReason) {
+                         fail("sending Ping");
+                       });
                    },
-                   [](PromiseRejectReason aReason) {
+                   [](ResponseRejectReason aReason) {
                      fail("sending Ping");
                    });
 }
 
 
 mozilla::ipc::IPCResult
 TestAsyncReturnsParent::RecvPong(PongResolver&& aResolve)
 {
@@ -89,17 +102,17 @@ TestAsyncReturnsChild::RecvPing(PingReso
                    [aResolve](const Tuple<uint32_t, uint32_t>& aParam) {
                      if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
                        passed("take two arguments");
                      } else {
                        fail("get two argument but has wrong value");
                      }
                      aResolve(true);
                    },
-                   [](PromiseRejectReason aReason) {
+                   [](ResponseRejectReason aReason) {
                      fail("sending Pong");
                    });
   return IPC_OK();
 }
 
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -223,17 +223,17 @@ ExtensionStreamGetter::GetAsync(nsIStrea
   if (mIsJarChannel) {
     // Request an FD for this moz-extension URI
     gNeckoChild->SendGetExtensionFD(uri)->Then(
       mMainThreadEventTarget,
       __func__,
       [self] (const FileDescriptor& fd) {
         self->OnFD(fd);
       },
-      [self] (const mozilla::ipc::PromiseRejectReason) {
+      [self] (const mozilla::ipc::ResponseRejectReason) {
         self->OnFD(FileDescriptor());
       }
     );
     return Ok();
   }
 
   // Request an input stream for this moz-extension URI
   gNeckoChild->SendGetExtensionStream(uri)->Then(
@@ -241,17 +241,17 @@ ExtensionStreamGetter::GetAsync(nsIStrea
     __func__,
     [self] (const OptionalIPCStream& stream) {
       nsCOMPtr<nsIInputStream> inputStream;
       if (stream.type() == OptionalIPCStream::OptionalIPCStream::TIPCStream) {
         inputStream = ipc::DeserializeIPCStream(stream);
       }
       self->OnStream(inputStream);
     },
-    [self] (const mozilla::ipc::PromiseRejectReason) {
+    [self] (const mozilla::ipc::ResponseRejectReason) {
       self->OnStream(nullptr);
     }
   );
   return Ok();
 }
 
 static void
 CancelRequest(nsIStreamListener* aListener,
--- a/toolkit/components/extensions/webrequest/StreamFilter.cpp
+++ b/toolkit/components/extensions/webrequest/StreamFilter.cpp
@@ -90,17 +90,17 @@ StreamFilter::Connect()
     RefPtr<StreamFilter> self(this);
 
     cc->SendInitStreamFilter(mChannelId, addonId)->Then(
       GetCurrentThreadSerialEventTarget(),
       __func__,
       [=] (mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
         self->FinishConnect(Move(aEndpoint));
       },
-      [=] (mozilla::ipc::PromiseRejectReason aReason) {
+      [=] (mozilla::ipc::ResponseRejectReason aReason) {
         self->mActor->RecvInitialized(false);
       });
   } else {
     mozilla::ipc::Endpoint<PStreamFilterChild> endpoint;
     Unused << StreamFilterParent::Create(nullptr, mChannelId, addonId, &endpoint);
 
     // Always dispatch asynchronously so JS callers have a chance to attach
     // event listeners before we dispatch events.
--- a/tools/profiler/gecko/nsProfiler.cpp
+++ b/tools/profiler/gecko/nsProfiler.cpp
@@ -621,17 +621,17 @@ nsProfiler::StartGathering(double aSince
 
   mPendingProfiles = profiles.Length();
   RefPtr<nsProfiler> self = this;
   for (auto profile : profiles) {
     profile->Then(GetMainThreadSerialEventTarget(), __func__,
       [self](const nsCString& aResult) {
         self->GatheredOOPProfile(aResult);
       },
-      [self](ipc::PromiseRejectReason aReason) {
+      [self](ipc::ResponseRejectReason aReason) {
         self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
       });
   }
   if (!mPendingProfiles) {
     FinishGathering();
   }
 
   return promise;
--- a/tools/profiler/public/ProfilerParent.h
+++ b/tools/profiler/public/ProfilerParent.h
@@ -30,17 +30,17 @@ class ProfilerParentTracker;
 // and handles shutdown.
 class ProfilerParent final : public PProfilerParent
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ProfilerParent)
 
   static mozilla::ipc::Endpoint<PProfilerChild> CreateForProcess(base::ProcessId aOtherPid);
 
-  typedef MozPromise<nsCString, PromiseRejectReason, false> SingleProcessProfilePromise;
+  typedef MozPromise<nsCString, ResponseRejectReason, false> SingleProcessProfilePromise;
 
   // The following static methods can be called on any thread, but they are
   // no-ops on anything other than the main thread.
   // If called on the main thread, the call will be broadcast to all
   // registered processes (all processes for which we have a ProfilerParent
   // object).
   // At the moment, the main process always calls these methods on the main
   // thread, and that's the only process in which we need to forward these
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -798,17 +798,17 @@ PuppetWidget::NotifyIMEOfFocusChange(con
   IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL);
   RefPtr<PuppetWidget> self = this;
   mTabChild->SendNotifyIMEFocus(mContentCache, aIMENotification)->Then(
     mTabChild->TabGroup()->EventTargetFor(TaskCategory::UI),
     __func__,
     [self] (IMENotificationRequests aRequests) {
       self->mIMENotificationRequestsOfParent = aRequests;
     },
-    [self] (mozilla::ipc::PromiseRejectReason aReason) {
+    [self] (mozilla::ipc::ResponseRejectReason aReason) {
       NS_WARNING("SendNotifyIMEFocus got rejected.");
     });
 
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::NotifyIMEOfCompositionUpdate(