Back out 12 changesets (bug 1177013) on suspicion of causing b2g emulator debug test bustage
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 02 Jul 2015 22:08:54 -0700
changeset 251301 0d06cc55aed105e8ed3e5ff0a0b3568dbdd83429
parent 251300 117d439135fbe865b646920e6bcfbb3db8566229
child 251302 b6a79816ee715edba178fb7e93c91c9879c564b0
child 251303 7a874cf3bf8e1378092bec6cc05a88738e1e0483
push id28991
push usercbook@mozilla.com
push dateFri, 03 Jul 2015 10:08:14 +0000
treeherdermozilla-central@b6a79816ee71 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1177013
milestone42.0a1
backs out912aae0815f8494c155b0fb705b14d716029570c
3b6448172e508f647468fd9ddc5beaad687e7c6c
2af18bef57036e968f2a6fa6472f65062f51a51b
e6bf35115c11f33898491a01f9da8bd68fa2c40b
4d7f5205b60b71589166ed406fe3cc8f9815e10e
f7de893911bc2eea6e779a72bcb88c0aee10e63d
de79eba232f017380e07646364aea5e7ca0e6061
978a77b60f2a671b93f8f38a242ccb085d1c0341
f5b52fa19511f3ce9bfbdde6d773e775f205afb2
e14a7b70b6fa384c1b7cf567b7687cd4853c5d72
d0f5a347465905056cd9c9d670dd80a8d84ad396
bff9f07dad5259c482ae8fa15ac4b977612cb99d
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
Back out 12 changesets (bug 1177013) on suspicion of causing b2g emulator debug test bustage CLOSED TREE Backed out changeset 912aae0815f8 (bug 1177013) Backed out changeset 3b6448172e50 (bug 1177013) Backed out changeset 2af18bef5703 (bug 1177013) Backed out changeset e6bf35115c11 (bug 1177013) Backed out changeset 4d7f5205b60b (bug 1177013) Backed out changeset f7de893911bc (bug 1177013) Backed out changeset de79eba232f0 (bug 1177013) Backed out changeset 978a77b60f2a (bug 1177013) Backed out changeset f5b52fa19511 (bug 1177013) Backed out changeset e14a7b70b6fa (bug 1177013) Backed out changeset d0f5a3474659 (bug 1177013) Backed out changeset bff9f07dad52 (bug 1177013)
browser/base/content/content.js
dom/base/test/chrome/cpows_child.js
dom/base/test/chrome/cpows_parent.xul
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
ipc/ipdl/test/cxx/PTestCancel.ipdl
ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
ipc/ipdl/test/cxx/PTestRPC.ipdl
ipc/ipdl/test/cxx/PTestUrgency.ipdl
ipc/ipdl/test/cxx/TestCancel.cpp
ipc/ipdl/test/cxx/TestCancel.h
ipc/ipdl/test/cxx/TestRPC.cpp
ipc/ipdl/test/cxx/TestRPC.h
ipc/ipdl/test/cxx/moz.build
js/ipc/WrapperOwner.cpp
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/extensions/test/xpinstall/head.js
widget/windows/nsWindow.cpp
xpcom/threads/nsThread.cpp
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -145,22 +145,22 @@ let handleContentContextMenu = function 
     // determine what was context-clicked on. Then, update the state of the
     // commands on the context menu.
     docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
             .setCommandNode(event.target);
     event.target.ownerDocument.defaultView.updateCommands("contentcontextmenu");
 
     let customMenuItems = PageMenuChild.build(event.target);
     let principal = doc.nodePrincipal;
-    sendRpcMessage("contextmenu",
-                   { editFlags, spellInfo, customMenuItems, addonInfo,
-                     principal, docLocation, charSet, baseURI, referrer,
-                     referrerPolicy, contentType, contentDisposition,
-                     frameOuterWindowID, selectionInfo, disableSetDesktopBg },
-                   { event, popupNode: event.target });
+    sendSyncMessage("contextmenu",
+                    { editFlags, spellInfo, customMenuItems, addonInfo,
+                      principal, docLocation, charSet, baseURI, referrer,
+                      referrerPolicy, contentType, contentDisposition,
+                      frameOuterWindowID, selectionInfo, disableSetDesktopBg },
+                    { event, popupNode: event.target });
   }
   else {
     // Break out to the parent window and pass the add-on info along
     let browser = docShell.chromeEventHandler;
     let mainWin = browser.ownerDocument.defaultView;
     mainWin.gContextMenuContentData = {
       isRemote: false,
       event: event,
--- a/dom/base/test/chrome/cpows_child.js
+++ b/dom/base/test/chrome/cpows_child.js
@@ -1,45 +1,43 @@
 dump('loaded child cpow test\n');
 
+content.document.title = "Hello, Kitty";
 const Cu = Components.utils;
 
+var done_count = 0;
+var is_remote;
+
 (function start() {
-  [is_remote] = sendRpcMessage("cpows:is_remote");
-
-  var tests = [
-    parent_test,
-    error_reporting_test,
-    dom_test,
-    xray_test,
-    symbol_test,
-    compartment_test,
-    regexp_test,
-    postmessage_test,
-    sync_test,
-    async_test,
-    rpc_test,
-    lifetime_test
-  ];
-
-  function go() {
-    if (tests.length == 0) {
-      sendRpcMessage("cpows:done", {});
-      return;
+    [is_remote] = sendSyncMessage("cpows:is_remote");
+    parent_test();
+    error_reporting_test();
+    dom_test();
+    xray_test();
+    if (typeof Symbol === "function") {
+      symbol_test();
     }
-
-    var test = tests[0];
-    tests.shift();
-    test(function() {
-      go();
+    compartment_test();
+    regexp_test();
+    postmessage_test();
+    sync_test();
+    async_test();
+    rpc_test();
+    nested_sync_test();
+    // The sync-ness of this call is important, because otherwise
+    // we tear down the child's document while we are
+    // still in the async test in the parent.
+    // This test races with itself to be the final test.
+    lifetime_test(function() {
+      done_count++;
+      if (done_count == 2)
+        sendSyncMessage("cpows:done", {});
     });
   }
-
-  go();
-})();
+)();
 
 function ok(condition, message) {
   dump('condition: ' + condition  + ', ' + message + '\n');
   if (!condition) {
     sendAsyncMessage("cpows:fail", { message: message });
     throw 'failed check: ' + message;
   }
 }
@@ -66,33 +64,32 @@ function make_object()
 
   let for_json = { "n": 3, "a": array, "s": "hello", o: { "x": 10 } };
 
   let proto = { data: 42 };
   let with_proto = Object.create(proto);
 
   let with_null_proto = Object.create(null);
 
-  content.document.title = "Hello, Kitty";
   return { "data": o,
            "throwing": throwing,
            "document": content.document,
            "array": array,
            "for_json": for_json,
            "with_proto": with_proto,
            "with_null_proto": with_null_proto
          };
 }
 
 function make_json()
 {
   return { check: "ok" };
 }
 
-function parent_test(finish)
+function parent_test()
 {
   function f(check_func) {
     let result = check_func(10);
     ok(result == 20, "calling function in parent worked");
     return result;
   }
 
   addMessageListener("cpows:from_parent", (msg) => {
@@ -102,70 +99,67 @@ function parent_test(finish)
     // Test that a CPOW reference to a function in the chrome process
     // is callable from unprivileged content. Greasemonkey uses this
     // functionality.
     let func = msg.objects.func;
     let sb = Cu.Sandbox('http://www.example.com', {});
     sb.func = func;
     ok(sb.eval('func()') == 101, "can call parent's function in child");
 
-    finish();
+    done_count++;
+    if (done_count == 2)
+      sendSyncMessage("cpows:done", {});
   });
-  sendRpcMessage("cpows:parent_test", {}, {func: f});
+  sendSyncMessage("cpows:parent_test", {}, {func: f});
 }
 
-function error_reporting_test(finish) {
-  sendRpcMessage("cpows:error_reporting_test", {}, {});
-  finish();
+function error_reporting_test() {
+  sendSyncMessage("cpows:error_reporting_test", {}, {});
 }
 
-function dom_test(finish)
+function dom_test()
 {
   let element = content.document.createElement("div");
   element.id = "it_works";
   content.document.body.appendChild(element);
 
-  sendRpcMessage("cpows:dom_test", {}, {element: element});
+  sendAsyncMessage("cpows:dom_test", {}, {element: element});
   Components.utils.schedulePreciseGC(function() {
-    sendRpcMessage("cpows:dom_test_after_gc");
-    finish();
+    sendSyncMessage("cpows:dom_test_after_gc");
   });
 }
 
-function xray_test(finish)
+function xray_test()
 {
   let element = content.document.createElement("div");
   element.wrappedJSObject.foo = "hello";
 
-  sendRpcMessage("cpows:xray_test", {}, {element: element});
-  finish();
+  sendSyncMessage("cpows:xray_test", {}, {element: element});
 }
 
-function symbol_test(finish)
+function symbol_test()
 {
   let iterator = Symbol.iterator;
   let named = Symbol.for("cpow-test");
 
   let object = {
     [iterator]: iterator,
     [named]: named,
   };
   let test = ['a'];
-  sendRpcMessage("cpows:symbol_test", {}, {object: object, test: test});
-  finish();
+  sendSyncMessage("cpows:symbol_test", {}, {object: object, test: test});
 }
 
 // Parent->Child references should go X->parent.privilegedJunkScope->child.privilegedJunkScope->Y
 // Child->Parent references should go X->child.privilegedJunkScope->parent.unprivilegedJunkScope->Y
-function compartment_test(finish)
+function compartment_test()
 {
   // This test primarily checks various compartment invariants for CPOWs, and
   // doesn't make sense to run in-process.
   if (!is_remote) {
-    finish();
     return;
   }
 
   let sb = Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ['XMLHttpRequest'] });
   sb.eval('function getUnprivilegedObject() { var xhr = new XMLHttpRequest(); xhr.expando = 42; return xhr; }');
   function testParentObject(obj) {
     let results = [];
     function is(a, b, msg) { results.push({ result: a === b ? "PASS" : "FAIL", message: msg }) };
@@ -179,86 +173,99 @@ function compartment_test(finish)
       obj.expando;
       ok(false, "child->parent CPOW cannot access properties");
     } catch (e) {
       ok(true, "child->parent CPOW cannot access properties");
     }
 
     return results;
   }
-  sendRpcMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
-                                                 testParentObject: testParentObject });
-  finish();
+  sendSyncMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
+                                                  testParentObject: testParentObject });
 }
 
-function regexp_test(finish)
+function regexp_test()
 {
-  sendRpcMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
-  finish();
+  sendSyncMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
 }
 
-function postmessage_test(finish)
+function postmessage_test()
 {
-  sendRpcMessage("cpows:postmessage_test", {}, { win: content.window });
-  finish();
+  sendSyncMessage("cpows:postmessage_test", {}, { win: content.window });
 }
 
-function sync_test(finish)
+function sync_test()
 {
   dump('beginning cpow sync test\n');
   sync_obj = make_object();
-  sendRpcMessage("cpows:sync",
+  sendSyncMessage("cpows:sync",
     make_json(),
     make_object());
-  finish();
 }
 
-function async_test(finish)
+function async_test()
 {
   dump('beginning cpow async test\n');
   async_obj = make_object();
   sendAsyncMessage("cpows:async",
     make_json(),
     async_obj);
-
-  addMessageListener("cpows:async_done", finish);
 }
 
 var rpc_obj;
 
-function rpc_test(finish)
+function rpc_test()
 {
   dump('beginning cpow rpc test\n');
   rpc_obj = make_object();
   rpc_obj.data.reenter = function  () {
     sendRpcMessage("cpows:reenter", { }, { data: { valid: true } });
     return "ok";
   }
   sendRpcMessage("cpows:rpc",
     make_json(),
     rpc_obj);
-  finish();
+}
+
+function nested_sync_test()
+{
+  dump('beginning cpow nested sync test\n');
+  sync_obj = make_object();
+  sync_obj.data.reenter = function () {
+    let caught = false;
+    try {
+       sendSyncMessage("cpows:reenter_sync", { }, { });
+    } catch (e) {
+      caught = true;
+    }
+    if (!ok(caught, "should not allow nested sync"))
+      return "fail";
+    return "ok";
+  }
+  sendSyncMessage("cpows:nested_sync",
+    make_json(),
+    rpc_obj);
 }
 
 function lifetime_test(finish)
 {
   if (!is_remote) {
     // Only run this test when running out-of-process. Otherwise it
     // will fail, since local CPOWs don't follow the same ownership
     // rules.
     finish();
     return;
   }
 
   dump("beginning lifetime test\n");
   var obj = {"will_die": {"f": 1}};
-  let [result] = sendRpcMessage("cpows:lifetime_test_1", {}, {obj: obj});
+  let [result] = sendSyncMessage("cpows:lifetime_test_1", {}, {obj: obj});
   ok(result == 10, "got sync result");
   ok(obj.wont_die.f == 2, "got reverse CPOW");
   obj.will_die = null;
   Components.utils.schedulePreciseGC(function() {
     addMessageListener("cpows:lifetime_test_3", (msg) => {
       ok(obj.wont_die.f == 2, "reverse CPOW still works");
       finish();
     });
-    sendRpcMessage("cpows:lifetime_test_2");
+    sendSyncMessage("cpows:lifetime_test_2");
   });
 }
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -59,17 +59,17 @@
       ok(data.x.i === 10, "nested property");
       ok(data.f() === 99, "function call");
       is(Object.getOwnPropertyDescriptor(data, "doesn't exist"), undefined,
          "getOwnPropertyDescriptor returns undefined for non-existant properties");
       ok(Object.getOwnPropertyDescriptor(data, "i").value, 5,
          "getOwnPropertyDescriptor.value works");
       let obj = new data.ctor();
       ok(obj.a === 3, "constructor call");
-      is(document.title, "Hello, Kitty", "document node");
+      ok(document.title === "Hello, Kitty", "document node");
       is(typeof document.cookie, "string", "can get document.cookie");
       is(typeof document.defaultView.navigator.userAgent, "string", "can get navigator.userAgent");
 
       // Don't crash.
       document.defaultView.screen;
 
       data.i = 6;
       data.b = false;
@@ -136,17 +136,16 @@
 
       let with_null_proto = message.objects.with_null_proto;
       proto = Object.getPrototypeOf(with_null_proto);
       ok(proto === null, "Object.getPrototypeOf works on CPOW (null proto)");
     }
 
     function recvAsyncMessage(message) {
       testCpowMessage(message);
-      savedMM.sendAsyncMessage("cpows:async_done");
     }
 
     function recvSyncMessage(message) {
       testCpowMessage(message);
     }
 
     function recvRpcMessage(message) {
       ok(message.json.check == "ok", "correct json");
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/ipc/ProtocolUtils.h"
 
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "mozilla/SizePrintfMacros.h"
-#include "mozilla/Telemetry.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsContentUtils.h"
 
 #include "prprf.h"
 
 // Undo the damage done by mozzconf.h
 #undef compress
@@ -28,54 +27,49 @@
  * IPC design:
  *
  * There are three kinds of messages: async, sync, and intr. Sync and intr
  * messages are blocking. Only intr and high-priority sync messages can nest.
  *
  * Terminology: To dispatch a message Foo is to run the RecvFoo code for
  * it. This is also called "handling" the message.
  *
- * Sync and async messages have priorities while intr messages always have
+ * Sync messages have priorities while async and intr messages always have
  * normal priority. The three possible priorities are normal, high, and urgent.
  * The intended uses of these priorities are:
  *   NORMAL - most messages.
  *   HIGH   - CPOW-related messages, which can go in either direction.
  *   URGENT - messages where we don't want to dispatch
  *            incoming CPOWs while waiting for the response.
- * Async messages cannot have HIGH priority.
  *
  * To avoid jank, the parent process is not allowed to send sync messages of
- * normal priority. When a process is waiting for a response to a sync message
- * M0, it will dispatch an incoming message M if:
+ * normal priority. The parent also is not allowed to send urgent messages at
+ * all.  When a process is waiting for a response to a sync message M0, it will
+ * dispatch an incoming message M if:
  *   1. M has a higher priority than M0, or
  *   2. if M has the same priority as M0 and we're in the child, or
  *   3. if M has the same priority as M0 and it was sent by the other side
- *      while dispatching M0 (nesting).
+        while dispatching M0 (nesting).
  * The idea is that higher priority messages should take precendence, and we
  * also want to allow nesting. The purpose of rule 2 is to handle a race where
  * both processes send to each other simultaneously. In this case, we resolve
  * the race in favor of the parent (so the child dispatches first).
  *
- * Messages satisfy the following properties:
+ * Sync messages satisfy the following properties:
  *   A. When waiting for a response to a sync message, we won't dispatch any
  *      messages of lower priority.
- *   B. Messages of the same priority will be dispatched roughly in the
+ *   B. Sync messages of the same priority will be dispatched roughly in the
  *      order they were sent. The exception is when the parent and child send
  *      sync messages to each other simulataneously. In this case, the parent's
  *      message is dispatched first. While it is dispatched, the child may send
  *      further nested messages, and these messages may be dispatched before the
  *      child's original message. We can consider ordering to be preserved here
  *      because we pretend that the child's original message wasn't sent until
  *      after the parent's message is finished being dispatched.
  *
- * When waiting for a sync message reply, we dispatch an async message only if
- * it has URGENT priority. Normally URGENT async messages are sent only from the
- * child. However, the parent can send URGENT async messages when it is creating
- * a bridged protocol.
- *
  * Intr messages are blocking but not prioritized. While waiting for an intr
  * response, all incoming messages are dispatched until a response is
  * received. Intr messages also can be nested. When two intr messages race with
  * each other, a similar scheme is used to ensure that one side wins. The
  * winning side is chosen based on the message type.
  *
  * Intr messages differ from sync messages in that, while sending an intr
  * message, we may dispatch an async message. This causes some additional
@@ -101,17 +95,17 @@ struct RunnableMethodTraits<mozilla::ipc
 };
 
 #define IPC_ASSERT(_cond, ...)                                      \
     do {                                                            \
         if (!(_cond))                                               \
             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
     } while (0)
 
-static MessageChannel* gParentProcessBlocker;
+static bool gParentIsBlocked;
 
 namespace mozilla {
 namespace ipc {
 
 const int32_t MessageChannel::kNoTimeout = INT32_MIN;
 
 // static
 bool MessageChannel::sIsPumpingMessages = false;
@@ -399,20 +393,16 @@ MessageChannel::Clear()
     //
     // Also don't clear mListener.  If we clear it, then sending a message
     // through this channel after it's Clear()'ed can cause this process to
     // crash.
     //
     // In practice, mListener owns the channel, so the channel gets deleted
     // before mListener.  But just to be safe, mListener is a weak pointer.
 
-    if (gParentProcessBlocker == this) {
-        gParentProcessBlocker = nullptr;
-    }
-
     mDequeueOneTask->Cancel();
 
     mWorkerLoop = nullptr;
     delete mLink;
     mLink = nullptr;
 
     mOnChannelConnectedTask->Cancel();
 
@@ -557,52 +547,33 @@ MessageChannel::Send(Message* aMsg)
     if (!Connected()) {
         ReportConnectionError("MessageChannel", msg);
         return false;
     }
     mLink->SendMessage(msg.forget());
     return true;
 }
 
-class CancelMessage : public IPC::Message
-{
-public:
-    CancelMessage() :
-        IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE, PRIORITY_NORMAL)
-    {
-    }
-    static bool Read(const Message* msg) {
-        return true;
-    }
-    void Log(const std::string& aPrefix, FILE* aOutf) const {
-        fputs("(special `Cancel' message)", aOutf);
-    }
-};
-
 bool
 MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg)
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
-    if (MSG_ROUTING_NONE == aMsg.routing_id()) {
-        if (GOODBYE_MESSAGE_TYPE == aMsg.type()) {
-            // :TODO: Sort out Close() on this side racing with Close() on the
-            // other side
-            mChannelState = ChannelClosing;
-            if (LoggingEnabled()) {
-                printf("NOTE: %s process received `Goodbye', closing down\n",
-                       (mSide == ChildSide) ? "child" : "parent");
-            }
-            return true;
-        } else if (CANCEL_MESSAGE_TYPE == aMsg.type()) {
-            CancelCurrentTransactionInternal();
-            NotifyWorkerThread();
-            return true;
+    if (MSG_ROUTING_NONE == aMsg.routing_id() &&
+        GOODBYE_MESSAGE_TYPE == aMsg.type())
+    {
+        // :TODO: Sort out Close() on this side racing with Close() on the
+        // other side
+        mChannelState = ChannelClosing;
+        if (LoggingEnabled()) {
+            printf("NOTE: %s process received `Goodbye', closing down\n",
+                   (mSide == ChildSide) ? "child" : "parent");
         }
+        return true;
     }
     return false;
 }
 
 bool
 MessageChannel::ShouldDeferMessage(const Message& aMsg)
 {
     // Never defer messages that have the highest priority, even async
@@ -667,17 +638,16 @@ MessageChannel::OnMessageReceivedFromLin
     // we know that it needs to be immediately handled to unblock us.
     if (aMsg.is_sync() && aMsg.is_reply()) {
         if (aMsg.seqno() == mTimedOutMessageSeqno) {
             // Drop the message, but allow future sync messages to be sent.
             mTimedOutMessageSeqno = 0;
             return;
         }
 
-        MOZ_ASSERT(aMsg.transaction_id() == mCurrentTransaction);
         MOZ_ASSERT(AwaitingSyncReply());
         MOZ_ASSERT(!mRecvd);
 
         // Rather than storing errors in mRecvd, we mark them in
         // mRecvdErrors. We need a counter because multiple replies can arrive
         // when a timeout happens, as in the following example. Imagine the
         // child is running slowly. The parent sends a sync message P1. It times
         // out. The child eventually sends a sync message C1. While waiting for
@@ -792,115 +762,57 @@ MessageChannel::ProcessPendingRequests()
         // Processing these messages could result in more messages, so we
         // loop around to check for more afterwards.
         for (auto it = toProcess.begin(); it != toProcess.end(); it++)
             ProcessPendingRequest(*it);
     }
 }
 
 bool
-MessageChannel::WasTransactionCanceled(int transaction, int prio)
-{
-    if (transaction == mCurrentTransaction) {
-        return false;
-    }
-
-    // This isn't an assert so much as an intentional crash because we're in a
-    // situation that we don't know how to recover from: The child is awaiting
-    // a reply to a normal-priority sync message. The transaction that this
-    // message initiated has now been canceled. That could only happen if a CPOW
-    // raced with the sync message and was dispatched by the child while the
-    // child was awaiting the sync reply; at some point while dispatching the
-    // CPOW, the transaction was canceled.
-    //
-    // Notes:
-    //
-    // 1. We don't want to cancel the normal-priority sync message along with
-    // the CPOWs because the browser relies on these messages working
-    // reliably.
-    //
-    // 2. Ideally we would like to avoid dispatching CPOWs while awaiting a sync
-    // response. This isn't possible though. To avoid deadlock, the parent would
-    // have to dispatch the sync message while waiting for the CPOW
-    // response. However, it wouldn't have dispatched async messages at that
-    // time, so we would have a message ordering bug. Dispatching the async
-    // messages first causes other hard-to-handle situations (what if they send
-    // CPOWs?).
-    //
-    // 3. We would like to be able to cancel the CPOWs but not the sync
-    // message. However, that would leave both the parent and the child running
-    // code at the same time, all while the sync message is still
-    // outstanding. That can cause a problem where message replies are received
-    // out of order.
-    IPC_ASSERT(prio != IPC::Message::PRIORITY_NORMAL,
-               "Intentional crash: We canceled a CPOW that was racing with a sync message.");
-
-    return true;
-}
-
-bool
 MessageChannel::Send(Message* aMsg, Message* aReply)
 {
-    nsAutoPtr<Message> msg(aMsg);
-
     // See comment in DispatchSyncMessage.
     MaybeScriptBlocker scriptBlocker(this, true);
 
     // Sanity checks.
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
     if (mCurrentTransaction == 0)
         mListener->OnBeginSyncTransaction();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, false);
 #endif
 
-    CxxStackFrame f(*this, OUT_MESSAGE, msg);
+    CxxStackFrame f(*this, OUT_MESSAGE, aMsg);
 
     MonitorAutoLock lock(*mMonitor);
 
     if (mTimedOutMessageSeqno) {
         // Don't bother sending another sync message if a previous one timed out
         // and we haven't received a reply for it. Once the original timed-out
         // message receives a reply, we'll be able to send more sync messages
         // again.
         return false;
     }
 
-    if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
-        msg->priority() > IPC::Message::PRIORITY_NORMAL)
-    {
-        // Don't allow sending CPOWs while we're dispatching a sync message.
-        // If you want to do that, use sendRpcMessage instead.
-        return false;
-    }
-
-    if (mCurrentTransaction &&
-        (msg->priority() < DispatchingSyncMessagePriority() ||
-         mAwaitingSyncReplyPriority > msg->priority() ||
-         DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT ||
-         DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT))
-    {
-        CancelCurrentTransactionInternal();
-        mLink->SendMessage(new CancelMessage());
-    }
-
-    IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here");
-    IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(),
+    IPC_ASSERT(aMsg->is_sync(), "can only Send() sync messages here");
+    IPC_ASSERT(aMsg->priority() >= DispatchingSyncMessagePriority(),
                "can't send sync message of a lesser priority than what's being dispatched");
-    IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(),
+    IPC_ASSERT(mAwaitingSyncReplyPriority <= aMsg->priority(),
                "nested sync message sends must be of increasing priority");
 
     IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
                "not allowed to send messages while dispatching urgent messages");
     IPC_ASSERT(DispatchingAsyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
                "not allowed to send messages while dispatching urgent messages");
 
+    nsAutoPtr<Message> msg(aMsg);
+
     if (!Connected()) {
         ReportConnectionError("MessageChannel::SendAndWait", msg);
         return false;
     }
 
     msg->set_seqno(NextSeqno());
 
     int32_t seqno = msg->seqno();
@@ -910,27 +822,21 @@ MessageChannel::Send(Message* aMsg, Mess
     AutoSetValue<bool> replies(mAwaitingSyncReply, true);
     AutoSetValue<int> prioSet(mAwaitingSyncReplyPriority, prio);
     AutoEnterTransaction transact(this, seqno);
 
     int32_t transaction = mCurrentTransaction;
     msg->set_transaction_id(transaction);
 
     ProcessPendingRequests();
-    if (WasTransactionCanceled(transaction, prio)) {
-        return false;
-    }
 
     mLink->SendMessage(msg.forget());
 
     while (true) {
         ProcessPendingRequests();
-        if (WasTransactionCanceled(transaction, prio)) {
-            return false;
-        }
 
         // See if we've received a reply.
         if (mRecvdErrors) {
             mRecvdErrors--;
             return false;
         }
 
         if (mRecvd) {
@@ -941,20 +847,16 @@ MessageChannel::Send(Message* aMsg, Mess
 
         bool maybeTimedOut = !WaitForSyncNotify();
 
         if (!Connected()) {
             ReportConnectionError("MessageChannel::SendAndWait");
             return false;
         }
 
-        if (WasTransactionCanceled(transaction, prio)) {
-            return false;
-        }
-
         // We only time out a message if it initiated a new transaction (i.e.,
         // if neither side has any other message Sends on the stack).
         bool canTimeOut = transaction == seqno;
         if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) {
             // We might have received a reply during WaitForSyncNotify or inside
             // ShouldContinueFromTimeout (which drops the lock). We need to make
             // sure not to set mTimedOutMessageSeqno if that happens, since then
             // there would be no way to unset it.
@@ -1067,17 +969,22 @@ MessageChannel::Call(Message* aMsg, Mess
             // deferred in-call that needs to be processed.  either way, we
             // won't break the inner while loop again until something new
             // happens.
             continue;
         }
 
         // If the message is not Interrupt, we can dispatch it as normal.
         if (!recvd.is_interrupt()) {
-            DispatchMessage(recvd);
+            {
+                AutoEnterTransaction transaction(this, recvd);
+                MonitorAutoUnlock unlock(*mMonitor);
+                CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+                DispatchMessage(recvd);
+            }
             if (!Connected()) {
                 ReportConnectionError("MessageChannel::DispatchMessage");
                 return false;
             }
             continue;
         }
 
         // If the message is an Interrupt reply, either process it as a reply to our
@@ -1195,17 +1102,24 @@ MessageChannel::ProcessPendingRequest(co
     // therefore mPendingUrgentRequest is set *and* mRecvd is set as
     // well, because the link thread received both before the worker
     // thread woke up.
     //
     // In this case, we process the urgent message first, but we need
     // to save the reply.
     nsAutoPtr<Message> savedReply(mRecvd.forget());
 
-    DispatchMessage(aUrgent);
+    {
+        // In order to send the parent RPC messages and guarantee it will
+        // wake up, we must re-use its transaction.
+        AutoEnterTransaction transaction(this, aUrgent);
+
+        MonitorAutoUnlock unlock(*mMonitor);
+        DispatchMessage(aUrgent);
+    }
     if (!Connected()) {
         ReportConnectionError("MessageChannel::ProcessPendingRequest");
         return false;
     }
 
     // In between having dispatched our reply to the parent process, and
     // re-acquiring the monitor, the parent process could have already
     // processed that reply and sent the reply to our sync message. If so,
@@ -1252,82 +1166,66 @@ MessageChannel::OnMaybeDequeueOne()
 
     if (IsOnCxxStack() && recvd.is_interrupt() && recvd.is_reply()) {
         // We probably just received a reply in a nested loop for an
         // Interrupt call sent before entering that loop.
         mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
         return false;
     }
 
-    // We should not be in a transaction yet if we're not blocked.
-    MOZ_ASSERT(mCurrentTransaction == 0);
-    DispatchMessage(recvd);
+    {
+        // We should not be in a transaction yet if we're not blocked.
+        MOZ_ASSERT(mCurrentTransaction == 0);
+        AutoEnterTransaction transaction(this, recvd);
 
+        MonitorAutoUnlock unlock(*mMonitor);
+
+        CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+        DispatchMessage(recvd);
+    }
     return true;
 }
 
 void
 MessageChannel::DispatchMessage(const Message &aMsg)
 {
     Maybe<AutoNoJSAPI> nojsapi;
     if (ScriptSettingsInitialized() && NS_IsMainThread())
         nojsapi.emplace();
-
-    nsAutoPtr<Message> reply;
-
-    {
-        AutoEnterTransaction transaction(this, aMsg);
-
-        int id = aMsg.transaction_id();
-        MOZ_ASSERT_IF(aMsg.is_sync(), id == mCurrentTransaction);
-
-        {
-            MonitorAutoUnlock unlock(*mMonitor);
-            CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
-
-            if (aMsg.is_sync())
-                DispatchSyncMessage(aMsg, *getter_Transfers(reply));
-            else if (aMsg.is_interrupt())
-                DispatchInterruptMessage(aMsg, 0);
-            else
-                DispatchAsyncMessage(aMsg);
-        }
-
-        if (mCurrentTransaction != id) {
-            // The transaction has been canceled. Don't send a reply.
-            reply = nullptr;
-        }
-    }
-
-    if (reply && ChannelConnected == mChannelState) {
-        mLink->SendMessage(reply.forget());
-    }
+    if (aMsg.is_sync())
+        DispatchSyncMessage(aMsg);
+    else if (aMsg.is_interrupt())
+        DispatchInterruptMessage(aMsg, 0);
+    else
+        DispatchAsyncMessage(aMsg);
 }
 
 void
-MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
+MessageChannel::DispatchSyncMessage(const Message& aMsg)
 {
     AssertWorkerThread();
 
+    nsAutoPtr<Message> reply;
+
     int prio = aMsg.priority();
 
     // We don't want to run any code that might run a nested event loop here, so
     // we avoid running event handlers. Once we've sent the response to the
     // urgent message, it's okay to run event handlers again since the parent is
     // no longer blocked.
     MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
     MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL);
 
-    IPC_ASSERT(prio >= DispatchingSyncMessagePriority(),
+    IPC_ASSERT(prio >= mDispatchingSyncMessagePriority,
                "priority inversion while dispatching sync message");
     IPC_ASSERT(prio >= mAwaitingSyncReplyPriority,
                "dispatching a message of lower priority while waiting for a response");
 
-    MessageChannel* dummy;
-    MessageChannel*& blockingVar = ShouldBlockScripts() ? gParentProcessBlocker : dummy;
+    bool dummy;
+    bool& blockingVar = ShouldBlockScripts() ? gParentIsBlocked : dummy;
 
     Result rv;
     if (mTimedOutMessageSeqno && mTimedOutMessagePriority >= prio) {
         // If the other side sends a message in response to one of our messages
         // that we've timed out, then we reply with an error.
         //
         // We do this because want to avoid a situation where we process an
         // incoming message from the child here while it simultaneously starts
@@ -1336,31 +1234,36 @@ MessageChannel::DispatchSyncMessage(cons
         //
         // The only exception is if the incoming message has urgent priority and
         // our timed-out message had only high priority. In that case it's safe
         // to process the incoming message because we know that the child won't
         // process anything (the child will defer incoming messages when waiting
         // for a response to its urgent message).
         rv = MsgNotAllowed;
     } else {
-        AutoSetValue<MessageChannel*> blocked(blockingVar, this);
+        AutoSetValue<bool> blocked(blockingVar, true);
         AutoSetValue<bool> sync(mDispatchingSyncMessage, true);
         AutoSetValue<int> prioSet(mDispatchingSyncMessagePriority, prio);
-        rv = mListener->OnMessageReceived(aMsg, aReply);
+        rv = mListener->OnMessageReceived(aMsg, *getter_Transfers(reply));
     }
 
     if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
-        aReply = new Message();
-        aReply->set_sync();
-        aReply->set_priority(aMsg.priority());
-        aReply->set_reply();
-        aReply->set_reply_error();
+        reply = new Message();
+        reply->set_sync();
+        reply->set_priority(aMsg.priority());
+        reply->set_reply();
+        reply->set_reply_error();
     }
-    aReply->set_seqno(aMsg.seqno());
-    aReply->set_transaction_id(aMsg.transaction_id());
+    reply->set_seqno(aMsg.seqno());
+    reply->set_transaction_id(aMsg.transaction_id());
+
+    MonitorAutoLock lock(*mMonitor);
+    if (ChannelConnected == mChannelState) {
+        mLink->SendMessage(reply.forget());
+    }
 }
 
 void
 MessageChannel::DispatchAsyncMessage(const Message& aMsg)
 {
     AssertWorkerThread();
     MOZ_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
 
@@ -2007,44 +1910,16 @@ MessageChannel::GetTopmostMessageRouting
     MOZ_ASSERT(MessageLoop::current() == mWorkerLoop);
     if (mCxxStackFrames.empty()) {
         return MSG_ROUTING_NONE;
     }
     const InterruptFrame& frame = mCxxStackFrames.back();
     return frame.GetRoutingId();
 }
 
-void
-MessageChannel::CancelCurrentTransactionInternal()
+bool
+ParentProcessIsBlocked()
 {
-    // When we cancel a transaction, we need to behave as if there's no longer
-    // any IPC on the stack. Anything we were dispatching or sending will get
-    // canceled. Consequently, we have to update the state variables below.
-    //
-    // We also need to ensure that when any IPC functions on the stack return,
-    // they don't reset these values using an RAII class like AutoSetValue. To
-    // avoid that, these RAII classes check if the variable they set has been
-    // tampered with (by us). If so, they don't reset the variable to the old
-    // value.
-
-    MOZ_ASSERT(!mCurrentTransaction);
-    mCurrentTransaction = 0;
-}
-
-void
-MessageChannel::CancelCurrentTransaction()
-{
-    MonitorAutoLock lock(*mMonitor);
-    CancelCurrentTransactionInternal();
-    mLink->SendMessage(new CancelMessage());
-}
-
-void
-CancelCPOWs()
-{
-    if (gParentProcessBlocker) {
-        mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true);
-        gParentProcessBlocker->CancelCurrentTransaction();
-    }
+    return gParentIsBlocked;
 }
 
 } // ipc
 } // mozilla
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -130,18 +130,16 @@ class MessageChannel : HasResultCodes
     bool CanSend() const;
 
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
     bool IsOnCxxStack() const {
         return !mCxxStackFrames.empty();
     }
 
-    void CancelCurrentTransaction();
-
     /**
      * This function is used by hang annotation code to determine which IPDL
      * actor is highest in the call stack at the time of the hang. It should
      * be called from the main thread when a sync or intr message is about to
      * be sent.
      */
     int32_t GetTopmostMessageRoutingId() const;
 
@@ -239,17 +237,17 @@ class MessageChannel : HasResultCodes
     bool OnMaybeDequeueOne();
     bool DequeueOne(Message *recvd);
 
     // Dispatches an incoming message to its appropriate handler.
     void DispatchMessage(const Message &aMsg);
 
     // DispatchMessage will route to one of these functions depending on the
     // protocol type of the message.
-    void DispatchSyncMessage(const Message &aMsg, Message*& aReply);
+    void DispatchSyncMessage(const Message &aMsg);
     void DispatchUrgentMessage(const Message &aMsg);
     void DispatchAsyncMessage(const Message &aMsg);
     void DispatchRPCMessage(const Message &aMsg);
     void DispatchInterruptMessage(const Message &aMsg, size_t aStackDepth);
 
     // Return true if the wait ended because a notification was received.
     //
     // Return false if the time elapsed from when we started the process of
@@ -262,18 +260,16 @@ class MessageChannel : HasResultCodes
     // necessarily.
     bool WaitForSyncNotify();
     bool WaitForInterruptNotify();
 
     bool WaitResponse(bool aWaitTimedOut);
 
     bool ShouldContinueFromTimeout();
 
-    void CancelCurrentTransactionInternal();
-
     // The "remote view of stack depth" can be different than the
     // actual stack depth when there are out-of-turn replies.  When we
     // receive one, our actual Interrupt stack depth doesn't decrease, but
     // the other side (that sent the reply) thinks it has.  So, the
     // "view" returned here is |stackDepth| minus the number of
     // out-of-turn replies.
     //
     // Only called from the worker thread.
@@ -400,17 +396,16 @@ class MessageChannel : HasResultCodes
     // thread, in which case it shouldn't be delivered to the worker.
     bool MaybeInterceptSpecialIOMessage(const Message& aMsg);
 
     void OnChannelConnected(int32_t peer_id);
 
     // Tell the IO thread to close the channel and wait for it to ACK.
     void SynchronouslyClose();
 
-    bool WasTransactionCanceled(int transaction, int prio);
     bool ShouldDeferMessage(const Message& aMsg);
     void OnMessageReceivedFromLink(const Message& aMsg);
     void OnChannelErrorFromLink();
 
   private:
     // Run on the not current thread.
     void NotifyChannelClosed();
     void NotifyMaybeChannelError();
@@ -546,52 +541,46 @@ class MessageChannel : HasResultCodes
     // To ensure IDs are unique, we use sequence numbers for transaction IDs,
     // which grow in opposite directions from child to parent.
 
     // The current transaction ID.
     int32_t mCurrentTransaction;
 
     class AutoEnterTransaction
     {
-     public:
+      public:
        explicit AutoEnterTransaction(MessageChannel *aChan, int32_t aMsgSeqno)
         : mChan(aChan),
-          mNewTransaction(0),
           mOldTransaction(mChan->mCurrentTransaction)
        {
            mChan->mMonitor->AssertCurrentThreadOwns();
-           if (mChan->mCurrentTransaction == 0) {
-               mNewTransaction = aMsgSeqno;
+           if (mChan->mCurrentTransaction == 0)
                mChan->mCurrentTransaction = aMsgSeqno;
-           }
        }
        explicit AutoEnterTransaction(MessageChannel *aChan, const Message &aMessage)
         : mChan(aChan),
-          mNewTransaction(aMessage.transaction_id()),
           mOldTransaction(mChan->mCurrentTransaction)
        {
            mChan->mMonitor->AssertCurrentThreadOwns();
 
            if (!aMessage.is_sync())
                return;
 
            MOZ_ASSERT_IF(mChan->mSide == ParentSide && mOldTransaction != aMessage.transaction_id(),
                          !mOldTransaction || aMessage.priority() > mChan->AwaitingSyncReplyPriority());
            mChan->mCurrentTransaction = aMessage.transaction_id();
        }
        ~AutoEnterTransaction() {
            mChan->mMonitor->AssertCurrentThreadOwns();
-           if (mChan->mCurrentTransaction == mNewTransaction) {
-               mChan->mCurrentTransaction = mOldTransaction;
-           }
+           mChan->mCurrentTransaction = mOldTransaction;
        }
 
       private:
        MessageChannel *mChan;
-       int32_t mNewTransaction, mOldTransaction;
+       int32_t mOldTransaction;
     };
 
     // If a sync message times out, we store its sequence number here. Any
     // future sync messages will fail immediately. Once the reply for original
     // sync message is received, we allow sync messages again.
     //
     // When a message times out, nothing is done to inform the other side. The
     // other side will eventually dispatch the message and send a reply. Our
@@ -727,15 +716,15 @@ class MessageChannel : HasResultCodes
     // Task and state used to asynchronously notify channel has been connected
     // safely.  This is necessary to be able to cancel notification if we are
     // closed at the same time.
     nsRefPtr<RefCountedTask> mOnChannelConnectedTask;
     DebugOnly<bool> mPeerPidSet;
     int32_t mPeerPid;
 };
 
-void
-CancelCPOWs();
+bool
+ParentProcessIsBlocked();
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -40,19 +40,16 @@ void ProtocolCloneContext::SetContentPar
 
 static StaticMutex gProtocolMutex;
 
 IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId)
  : mOpener(nullptr)
  , mProtocolId(aProtoId)
  , mTrans(nullptr)
 {
-#ifdef DEBUG
-  StaticMutexAutoLock al(gProtocolMutex);
-#endif
 }
 
 IToplevelProtocol::~IToplevelProtocol()
 {
   StaticMutexAutoLock al(gProtocolMutex);
 
   for (IToplevelProtocol* actor = mOpenActors.getFirst();
        actor;
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -33,21 +33,20 @@
 namespace {
 // XXX the max message ID is actually kuint32max now ... when this
 // changed, the assumptions of the special message IDs changed in that
 // they're not carving out messages from likely-unallocated space, but
 // rather carving out messages from the end of space allocated to
 // protocol 0.  Oops!  We can get away with this until protocol 0
 // starts approaching its 65,536th message.
 enum {
-    CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
-    SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
-    SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
-    GOODBYE_MESSAGE_TYPE       = kuint16max - 3,
-    CANCEL_MESSAGE_TYPE        = kuint16max - 2,
+    CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 5,
+    SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 4,
+    SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 3,
+    GOODBYE_MESSAGE_TYPE       = kuint16max - 2
 
     // kuint16max - 1 is used by ipc_channel.h.
 };
 }
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
deleted file mode 100644
--- a/ipc/ipdl/test/cxx/PTestCancel.ipdl
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace mozilla {
-namespace _ipdltest {
-
-prio(normal upto high) sync protocol PTestCancel
-{
-// Test1
-child:
-    prio(high) sync Test1_1();
-parent:
-    async Done1();
-
-// Test2
-child:
-    async Start2();
-    prio(high) sync Test2_2();
-parent:
-    sync Test2_1();
-
-// Test3
-child:
-    prio(high) sync Test3_1();
-parent:
-    async Start3();
-    prio(high) sync Test3_2();
-
-parent:
-    async Done();
-
-child:
-    prio(high) sync CheckChild() returns (uint32_t reply);
-parent:
-    prio(high) sync CheckParent() returns (uint32_t reply);
-};
-
-} // namespace _ipdltest
-} // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
+++ b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
@@ -1,16 +1,16 @@
 namespace mozilla {
 namespace _ipdltest {
 
 prio(normal upto urgent) sync protocol PTestHighestPrio
 {
 parent:
   prio(urgent) async Msg1();
-  prio(high) sync Msg2();
+  sync Msg2();
   prio(urgent) async Msg3();
   prio(urgent) sync Msg4();
 
 child:
   async Start();
   prio(high) sync StartInner();
 };
 
--- a/ipc/ipdl/test/cxx/PTestRPC.ipdl
+++ b/ipc/ipdl/test/cxx/PTestRPC.ipdl
@@ -3,19 +3,22 @@ namespace _ipdltest {
 
 prio(normal upto high) sync protocol PTestRPC
 {
 parent:
     prio(high) sync Test1_Start() returns (uint32_t result);
     prio(high) sync Test1_InnerEvent() returns (uint32_t result);
     async Test2_Start();
     prio(high) sync Test2_OutOfOrder();
+    sync Test3_Start() returns (uint32_t result);
+    prio(high) sync Test3_InnerEvent() returns (uint32_t result);
 
 child:
     async Start();
     prio(high) sync Test1_InnerQuery() returns (uint32_t result);
     prio(high) sync Test1_NoReenter() returns (uint32_t result);
     prio(high) sync Test2_FirstUrgent();
     prio(high) sync Test2_SecondUrgent();
+    prio(high) sync Test3_WakeUp() returns (uint32_t result);
 };
 
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/PTestUrgency.ipdl
+++ b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
@@ -1,15 +1,15 @@
 namespace mozilla {
 namespace _ipdltest {
 
 prio(normal upto high) sync protocol PTestUrgency
 {
 parent:
-    prio(high) sync Test1() returns (uint32_t result);
+    sync Test1() returns (uint32_t result);
     async Test2();
     sync Test3() returns (uint32_t result);
     sync FinalTest_Begin();
 
 child:
     async Start();
     prio(high) sync Reply1() returns (uint32_t result);
     prio(high) sync Reply2() returns (uint32_t result);
deleted file mode 100644
--- a/ipc/ipdl/test/cxx/TestCancel.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "TestCancel.h"
-
-#include "IPDLUnitTests.h"      // fail etc.
-
-template<>
-struct RunnableMethodTraits<mozilla::_ipdltest::TestCancelParent>
-{
-    static void RetainCallee(mozilla::_ipdltest::TestCancelParent* obj) { }
-    static void ReleaseCallee(mozilla::_ipdltest::TestCancelParent* obj) { }
-};
-
-namespace mozilla {
-namespace _ipdltest {
-
-//-----------------------------------------------------------------------------
-// parent
-
-TestCancelParent::TestCancelParent()
-{
-    MOZ_COUNT_CTOR(TestCancelParent);
-}
-
-TestCancelParent::~TestCancelParent()
-{
-    MOZ_COUNT_DTOR(TestCancelParent);
-}
-
-void
-TestCancelParent::Main()
-{
-    if (SendTest1_1())
-	fail("sending Test1_1");
-
-    uint32_t value = 0;
-    if (!SendCheckChild(&value))
-	fail("Test1 CheckChild");
-
-    if (value != 12)
-	fail("Test1 CheckChild reply");
-}
-
-bool
-TestCancelParent::RecvDone1()
-{
-    if (!SendStart2())
-	fail("sending Start2");
-
-    return true;
-}
-
-bool
-TestCancelParent::RecvTest2_1()
-{
-    if (SendTest2_2())
-	fail("sending Test2_2");
-
-    return true;
-}
-
-bool
-TestCancelParent::RecvStart3()
-{
-    if (SendTest3_1())
-	fail("sending Test3_1");
-
-    uint32_t value = 0;
-    if (!SendCheckChild(&value))
-	fail("Test1 CheckChild");
-
-    if (value != 12)
-	fail("Test1 CheckChild reply");
-
-    return true;
-}
-
-bool
-TestCancelParent::RecvTest3_2()
-{
-    GetIPCChannel()->CancelCurrentTransaction();
-    return true;
-}
-
-bool
-TestCancelParent::RecvDone()
-{
-    MessageLoop::current()->PostTask(
-	FROM_HERE, NewRunnableMethod(this, &TestCancelParent::Close));
-    return true;
-}
-
-bool
-TestCancelParent::RecvCheckParent(uint32_t *reply)
-{
-    *reply = 12;
-    return true;
-}
-
-//-----------------------------------------------------------------------------
-// child
-
-bool
-TestCancelChild::RecvTest1_1()
-{
-    GetIPCChannel()->CancelCurrentTransaction();
-
-    uint32_t value = 0;
-    if (!SendCheckParent(&value))
-	fail("Test1 CheckParent");
-
-    if (value != 12)
-	fail("Test1 CheckParent reply");
-
-    if (!SendDone1())
-	fail("Test1 CheckParent");
-
-    return true;
-}
-
-bool
-TestCancelChild::RecvStart2()
-{
-    if (!SendTest2_1())
-	fail("sending Test2_1");
-
-    if (!SendStart3())
-	fail("sending Start3");
-
-    return true;
-}
-
-bool
-TestCancelChild::RecvTest2_2()
-{
-    GetIPCChannel()->CancelCurrentTransaction();
-    return true;
-}
-
-bool
-TestCancelChild::RecvTest3_1()
-{
-    if (SendTest3_2())
-	fail("sending Test3_2");
-
-    uint32_t value = 0;
-    if (!SendCheckParent(&value))
-	fail("Test1 CheckParent");
-
-    if (value != 12)
-	fail("Test1 CheckParent reply");
-
-    if (!SendDone())
-	fail("sending Done");
-
-    return true;
-}
-
-bool
-TestCancelChild::RecvCheckChild(uint32_t *reply)
-{
-    *reply = 12;
-    return true;
-}
-
-TestCancelChild::TestCancelChild()
-{
-    MOZ_COUNT_CTOR(TestCancelChild);
-}
-
-TestCancelChild::~TestCancelChild()
-{
-    MOZ_COUNT_DTOR(TestCancelChild);
-}
-
-} // namespace _ipdltest
-} // namespace mozilla
deleted file mode 100644
--- a/ipc/ipdl/test/cxx/TestCancel.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef mozilla__ipdltest_TestCancel_h
-#define mozilla__ipdltest_TestCancel_h 1
-
-#include "mozilla/_ipdltest/IPDLUnitTests.h"
-
-#include "mozilla/_ipdltest/PTestCancelParent.h"
-#include "mozilla/_ipdltest/PTestCancelChild.h"
-
-namespace mozilla {
-namespace _ipdltest {
-
-
-class TestCancelParent :
-    public PTestCancelParent
-{
-public:
-    TestCancelParent();
-    virtual ~TestCancelParent();
-
-    static bool RunTestInProcesses() { return true; }
-    static bool RunTestInThreads() { return false; }
-
-    void Main();
-
-    virtual bool RecvDone1() override;
-    virtual bool RecvTest2_1() override;
-    virtual bool RecvStart3() override;
-    virtual bool RecvTest3_2() override;
-    virtual bool RecvDone() override;
-
-    virtual bool RecvCheckParent(uint32_t *reply) override;
-
-    virtual void ActorDestroy(ActorDestroyReason why) override
-    {
-        passed("ok");
-        QuitParent();
-    }
-};
-
-
-class TestCancelChild :
-    public PTestCancelChild
-{
-public:
-    TestCancelChild();
-    virtual ~TestCancelChild();
-
-    virtual bool RecvTest1_1() override;
-    virtual bool RecvStart2() override;
-    virtual bool RecvTest2_2() override;
-    virtual bool RecvTest3_1() override;
-
-    virtual bool RecvCheckChild(uint32_t *reply) override;
-
-    virtual void ActorDestroy(ActorDestroyReason why) override
-    {
-        QuitChild();
-    }
-};
-
-
-} // namespace _ipdltest
-} // namespace mozilla
-
-
-#endif // ifndef mozilla__ipdltest_TestCancel_h
--- a/ipc/ipdl/test/cxx/TestRPC.cpp
+++ b/ipc/ipdl/test/cxx/TestRPC.cpp
@@ -78,16 +78,32 @@ TestRPCParent::RecvTest2_OutOfOrder()
   // CPOW to resolve, replies will be processed out of order, and we'll crash.
   if (!SendTest2_SecondUrgent())
     fail("SendTest2_SecondUrgent");
 
   reentered_ = true;
   return true;
 }
 
+bool
+TestRPCParent::RecvTest3_Start(uint32_t* aResult)
+{
+  if (!SendTest3_WakeUp(aResult))
+    fail("SendTest3_WakeUp");
+
+  return true;
+}
+
+bool
+TestRPCParent::RecvTest3_InnerEvent(uint32_t* aResult)
+{
+  *aResult = 200;
+  return true;
+}
+
 //-----------------------------------------------------------------------------
 // child
 
 
 TestRPCChild::TestRPCChild()
 {
     MOZ_COUNT_CTOR(TestRPCChild);
 }
@@ -107,16 +123,22 @@ TestRPCChild::RecvStart()
     fail("Wrong result (expected 100)");
 
   if (!SendTest2_Start())
     fail("SendTest2_Start");
 
   if (!SendTest2_OutOfOrder())
     fail("SendTest2_OutOfOrder");
 
+  result = 0;
+  if (!SendTest3_Start(&result))
+    fail("SendTest3_Start");
+  if (result != 200)
+    fail("Wrong result (expected 200)");
+
   Close();
   return true;
 }
 
 bool
 TestRPCChild::RecvTest1_InnerQuery(uint32_t* aResult)
 {
   uint32_t result;
@@ -143,10 +165,19 @@ TestRPCChild::RecvTest2_FirstUrgent()
 }
 
 bool
 TestRPCChild::RecvTest2_SecondUrgent()
 {
   return true;
 }
 
+bool
+TestRPCChild::RecvTest3_WakeUp(uint32_t* aResult)
+{
+  if (!SendTest3_InnerEvent(aResult))
+    fail("SendTest3_InnerEvent");
+
+  return true;
+}
+
 } // namespace _ipdltest
 } // namespace mozilla
--- a/ipc/ipdl/test/cxx/TestRPC.h
+++ b/ipc/ipdl/test/cxx/TestRPC.h
@@ -21,16 +21,18 @@ public:
     static bool RunTestInThreads() { return false; }
 
     void Main();
 
     bool RecvTest1_Start(uint32_t* aResult) override;
     bool RecvTest1_InnerEvent(uint32_t* aResult) override;
     bool RecvTest2_Start() override;
     bool RecvTest2_OutOfOrder() override;
+    bool RecvTest3_Start(uint32_t* aResult) override;
+    bool RecvTest3_InnerEvent(uint32_t* aResult) override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         if (NormalShutdown != why)
             fail("unexpected destruction!");  
         if (!reentered_)
             fail("never processed raced RPC call!");
         if (!resolved_first_cpow_)
@@ -52,16 +54,17 @@ public:
     TestRPCChild();
     virtual ~TestRPCChild();
 
     bool RecvStart() override;
     bool RecvTest1_InnerQuery(uint32_t* aResult) override;
     bool RecvTest1_NoReenter(uint32_t* aResult) override;
     bool RecvTest2_FirstUrgent() override;
     bool RecvTest2_SecondUrgent() override;
+    bool RecvTest3_WakeUp(uint32_t* aResult) override;
 
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         if (NormalShutdown != why)
             fail("unexpected destruction!");
         QuitChild();
     }
 };
--- a/ipc/ipdl/test/cxx/moz.build
+++ b/ipc/ipdl/test/cxx/moz.build
@@ -12,17 +12,16 @@ EXPORTS.mozilla._ipdltest += [
     'IPDLUnitTestTypes.h',
     'IPDLUnitTestUtils.h',
 ]
 
 SOURCES += [
     'TestActorPunning.cpp',
     'TestBadActor.cpp',
     'TestBridgeMain.cpp',
-    'TestCancel.cpp',
     'TestCrashCleanup.cpp',
     'TestDataStructures.cpp',
     'TestDesc.cpp',
     'TestFailedCtor.cpp',
     'TestHangs.cpp',
     'TestHighestPrio.cpp',
     'TestInterruptErrorCleanup.cpp',
     'TestInterruptRaces.cpp',
@@ -65,17 +64,16 @@ IPDL_SOURCES += [
     'PTestActorPunning.ipdl',
     'PTestActorPunningPunned.ipdl',
     'PTestActorPunningSub.ipdl',
     'PTestBadActor.ipdl',
     'PTestBadActorSub.ipdl',
     'PTestBridgeMain.ipdl',
     'PTestBridgeMainSub.ipdl',
     'PTestBridgeSub.ipdl',
-    'PTestCancel.ipdl',
     'PTestCrashCleanup.ipdl',
     'PTestDataStructures.ipdl',
     'PTestDataStructuresCommon.ipdlh',
     'PTestDataStructuresSub.ipdl',
     'PTestDesc.ipdl',
     'PTestDescSub.ipdl',
     'PTestDescSubsub.ipdl',
     'PTestFailedCtor.ipdl',
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -995,17 +995,17 @@ WrapperOwner::ActorDestroy(ActorDestroyR
     objects_.clear();
     unwaivedObjectIds_.clear();
     waivedObjectIds_.clear();
 }
 
 bool
 WrapperOwner::ipcfail(JSContext* cx)
 {
-    JS_ReportError(cx, "cross-process JS call failed");
+    JS_ReportError(cx, "child process crashed or timedout");
     return false;
 }
 
 bool
 WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
 {
     if (status.type() == ReturnStatus::TReturnSuccess)
         return true;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4838,22 +4838,16 @@
     "description": "Security UI Telemetry"
   },
   "JS_TELEMETRY_ADDON_EXCEPTIONS" : {
     "expires_in_version" : "never",
     "kind": "count",
     "keyed" : true,
     "description" : "Exceptions thrown by add-ons"
   },
-  "IPC_TRANSACTION_CANCEL": {
-    "alert_emails": ["billm@mozilla.com"],
-    "expires_in_version": "never",
-    "kind": "boolean",
-    "description": "True when an IPC transaction is canceled"
-  },
   "MISBEHAVING_ADDONS_CPOW_TIME_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "low": 1,
     "high": 10000,
     "n_buckets": 20,
     "keyed": true,
     "description": "Time spent by an add-on performing blocking cross-process communications (ms, keyed by add-on ID, updated every 15s by default)"
--- a/toolkit/mozapps/extensions/test/xpinstall/head.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/head.js
@@ -282,27 +282,25 @@ var Harness = {
   // Addon Install Listener
 
   onNewInstall: function(install) {
     this.runningInstalls.push(install);
 
     if (this.finalContentEvent && !this.waitingForEvent) {
       this.waitingForEvent = true;
       info("Waiting for " + this.finalContentEvent);
-      let mm = gBrowser.selectedBrowser.messageManager;
-      mm.loadFrameScript(`data:,content.addEventListener("${this.finalContentEvent}", () => { sendAsyncMessage("Test:GotNewInstallEvent"); });`, false);
       let win = gBrowser.contentWindow;
       let listener = () => {
         info("Saw " + this.finalContentEvent);
-        mm.removeMessageListener("Test:GotNewInstallEvent", listener);
+        win.removeEventListener(this.finalContentEvent, listener, false);
         this.waitingForEvent = false;
         if (this.pendingCount == 0)
           this.endTest();
       }
-      mm.addMessageListener("Test:GotNewInstallEvent", listener);
+      win.addEventListener(this.finalContentEvent, listener, false);
     }
   },
 
   onDownloadStarted: function(install) {
     this.pendingCount++;
     if (this.downloadStartedCallback)
       this.downloadStartedCallback(install);
   },
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -4339,17 +4339,17 @@ inline static mozilla::HangMonitor::Acti
   return mozilla::HangMonitor::kActivityUIAVail;
 }
 
 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
 // exceptions and passes the real work to WindowProcInternal. See bug 587406
 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
-  ipc::CancelCPOWs();
+  MOZ_RELEASE_ASSERT(!ipc::ParentProcessIsBlocked());
 
   HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));
 
   return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg, wParam, lParam);
 }
 
 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -733,19 +733,18 @@ void canary_alarm_handler(int signum)
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait,
        mNestedEventLoopDepth));
 
 #if !defined(MOZILLA_XPCOMRT_API)
   // If we're on the main thread, we shouldn't be dispatching CPOWs.
-  if (mIsMainThread == MAIN_THREAD) {
-    ipc::CancelCPOWs();
-  }
+  MOZ_RELEASE_ASSERT(mIsMainThread != MAIN_THREAD ||
+                     !ipc::ParentProcessIsBlocked());
 #endif // !defined(MOZILLA_XPCOMRT_API)
 
   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   // The toplevel event loop normally blocks waiting for the next event, but
   // if we're trying to shut this thread down, we must exit the event loop when