Bug 430251 - Update to latest HTML5 and make postMessage dispatch its event asynchronously, as well as dispatch at the window, not the document. r=jst, r=smaug, sr=sicking, a=damons, c=cookie
authorjwalden@mit.edu
Fri, 02 May 2008 12:26:47 -0700
changeset 14898 c29d990c4499d37578a6a4c8679a5c9e80f02ccf
parent 14897 00cc150366cd205afa09c0ee600ab0fcecbba635
child 14899 4bf47dd6f0294333c1c2c805640d92be9daff5ba
push idunknown
push userunknown
push dateunknown
reviewersjst, smaug, sicking, damons
bugs430251
milestone1.9pre
Bug 430251 - Update to latest HTML5 and make postMessage dispatch its event asynchronously, as well as dispatch at the window, not the document. r=jst, r=smaug, sr=sicking, a=damons, c=cookie
content/events/src/nsDOMMessageEvent.cpp
content/events/src/nsDOMMessageEvent.h
dom/public/idl/base/nsIDOMWindowInternal.idl
dom/public/idl/events/nsIDOMMessageEvent.idl
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsGlobalWindow.h
dom/tests/mochitest/bugs/iframe_bug407839-1.html
dom/tests/mochitest/bugs/iframe_bug407839-2.html
dom/tests/mochitest/bugs/iframe_bug409349.html
dom/tests/mochitest/bugs/iframe_bug424093.html
dom/tests/mochitest/bugs/test_bug407839.html
dom/tests/mochitest/bugs/test_bug409349.html
dom/tests/mochitest/bugs/test_bug424093.html
dom/tests/mochitest/dom-level0/child_ip_address.html
dom/tests/mochitest/dom-level0/idn_child.html
dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html
dom/tests/mochitest/dom-level0/test_setting_document.domain_to_shortened_ipaddr.html
dom/tests/mochitest/whatwg/Makefile.in
dom/tests/mochitest/whatwg/postMessage.jar
dom/tests/mochitest/whatwg/postMessage.jar^headers^
dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
dom/tests/mochitest/whatwg/postMessage_closed_helper.html
dom/tests/mochitest/whatwg/postMessage_hash.html
dom/tests/mochitest/whatwg/postMessage_helper.html
dom/tests/mochitest/whatwg/postMessage_idn_helper.html
dom/tests/mochitest/whatwg/postMessage_joined_helper.html
dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
dom/tests/mochitest/whatwg/postMessage_onOther.html
dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml
dom/tests/mochitest/whatwg/postMessage_override_helper.html
dom/tests/mochitest/whatwg/postMessage_throw_helper.html
dom/tests/mochitest/whatwg/postMessage_userpass_helper.html
dom/tests/mochitest/whatwg/test_MessageEvent.html
dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html
dom/tests/mochitest/whatwg/test_postMessage.html
dom/tests/mochitest/whatwg/test_postMessage_basehref.html
dom/tests/mochitest/whatwg/test_postMessage_chrome.html
dom/tests/mochitest/whatwg/test_postMessage_closed.html
dom/tests/mochitest/whatwg/test_postMessage_hash.html
dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
dom/tests/mochitest/whatwg/test_postMessage_jar.html
dom/tests/mochitest/whatwg/test_postMessage_joined.html
dom/tests/mochitest/whatwg/test_postMessage_onOther.html
dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
dom/tests/mochitest/whatwg/test_postMessage_override.html
dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
dom/tests/mochitest/whatwg/test_postMessage_throw.html
dom/tests/mochitest/whatwg/test_postMessage_userpass.html
extensions/cookie/test/file_domain_inner.html
extensions/cookie/test/file_domain_inner_inner.html
extensions/cookie/test/file_image_inner.html
extensions/cookie/test/file_image_inner_inner.html
extensions/cookie/test/file_loadflags_inner.html
extensions/cookie/test/file_localhost_inner.html
extensions/cookie/test/file_loopback_inner.html
extensions/cookie/test/file_subdomain_inner.html
extensions/cookie/test/file_testcommon.js
extensions/cookie/test/file_testloadflags.js
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -67,47 +67,57 @@ nsDOMMessageEvent::GetData(nsAString& aD
 NS_IMETHODIMP
 nsDOMMessageEvent::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMMessageEvent::GetLastEventId(nsAString& aLastEventId)
+{
+  aLastEventId = mLastEventId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMMessageEvent::GetSource(nsIDOMWindow** aSource)
 {
   NS_IF_ADDREF(*aSource = mSource);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::InitMessageEvent(const nsAString& aType,
                                     PRBool aCanBubble,
                                     PRBool aCancelable,
                                     const nsAString& aData,
                                     const nsAString& aOrigin,
+                                    const nsAString& aLastEventId,
                                     nsIDOMWindow* aSource)
 {
   nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mData = aData;
   mOrigin = aOrigin;
+  mLastEventId = aLastEventId;
   mSource = aSource;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::InitMessageEventNS(const nsAString& aNamespaceURI,
                                       const nsAString& aType,
                                       PRBool aCanBubble,
                                       PRBool aCancelable,
                                       const nsAString& aData,
                                       const nsAString& aOrigin,
+                                      const nsAString& aLastEventId,
                                       nsIDOMWindow* aSource)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult,
                       nsPresContext* aPresContext,
--- a/content/events/src/nsDOMMessageEvent.h
+++ b/content/events/src/nsDOMMessageEvent.h
@@ -39,17 +39,18 @@
 #ifndef nsDOMMessageEvent_h__
 #define nsDOMMessageEvent_h__
 
 #include "nsIDOMMessageEvent.h"
 #include "nsDOMEvent.h"
 #include "nsCycleCollectionParticipant.h"
 
 /**
- * Implements the MessageEvent event, used for cross-document messaging.
+ * Implements the MessageEvent event, used for cross-document messaging and
+ * server-sent events.
  *
  * See http://www.whatwg.org/specs/web-apps/current-work/#messageevent for
  * further details.
  */
 class nsDOMMessageEvent : public nsIDOMMessageEvent,
                           public nsDOMEvent
 {
 public:
@@ -64,12 +65,13 @@ public:
   NS_DECL_NSIDOMMESSAGEEVENT
 
   // Forward to base class
   NS_FORWARD_TO_NSDOMEVENT
 
 private:
   nsString mData;
   nsString mOrigin;
+  nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mSource;
 };
 
 #endif // nsDOMMessageEvent_h__
--- a/dom/public/idl/base/nsIDOMWindowInternal.idl
+++ b/dom/public/idl/base/nsIDOMWindowInternal.idl
@@ -39,17 +39,17 @@
 
 #include "nsIDOMWindow2.idl"
 
 interface nsIPrompt;
 interface nsIControllers;
 interface nsIDOMLocation;
 interface nsIVariant;
 
-[scriptable, uuid(89b9ff5a-78db-430b-b3b4-66469457435a)]
+[scriptable, uuid(3414EBC7-731F-4697-9F43-ACA6F5050875)]
 interface nsIDOMWindowInternal : nsIDOMWindow2
 {
   readonly attribute nsIDOMWindowInternal        window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindowInternal        self;
 
   readonly attribute nsIDOMNavigator             navigator;
@@ -200,20 +200,24 @@ interface nsIDOMWindowInternal : nsIDOMW
   nsIVariant                showModalDialog(in DOMString aURI,
                                             [optional] in nsIVariant aArgs,
                                             [optional] in DOMString aOptions);
 
   /**
    * Implements a safe message-passing system which can cross same-origin
    * boundaries.
    *
-   * This method, when called, causes a MessageEvent to be dispatched at the
-   * primary document for the window upon which this method is called.  (Note
-   * that the postMessage property on windows is allAccess and thus is readable
-   * cross-origin.)  The dispatched event will have message as its data, the
-   * calling context's window as its source, and a domain and URI determined by
-   * the calling context's main document URI.
+   * This method, when called, causes a MessageEvent to be asynchronously
+   * dispatched at the primary document for the window upon which this method is
+   * called.  (Note that the postMessage property on windows is allAccess and
+   * thus is readable cross-origin.)  The dispatched event will have message as
+   * its data, the calling context's window as its source, and an origin
+   * determined by the calling context's main document URI.  The targetOrigin
+   * argument specifies a URI and is used to restrict the message to be sent
+   * only when the target window has the same origin as targetOrigin (since,
+   * when the sender and the target have different origins, neither can read the
+   * location of the other).
    * 
    * See the WHATWG HTML5 specification, section 6.4, for more details.
    */
   [binaryname(PostMessageMoz)] void postMessage(in DOMString message,
-                                                [optional] in DOMString origin);
+                                                in DOMString targetOrigin);
 };
--- a/dom/public/idl/events/nsIDOMMessageEvent.idl
+++ b/dom/public/idl/events/nsIDOMMessageEvent.idl
@@ -38,56 +38,64 @@
 
 #include "nsIDOMEvent.idl"
 
 /**
  * The nsIDOMMessageEvent interface is used for server-sent events and for
  * cross-domain messaging.
  *
  * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/multipage/section-event0.html#event0
+ * http://www.whatwg.org/specs/web-apps/current-work/#messageevent
  */
-[scriptable, uuid(ca081997-91f9-40c1-890c-3edf39b6c571)]
+[scriptable, uuid(98150805-6A15-4667-815A-1A8C87CB4BBC)]
 interface nsIDOMMessageEvent : nsIDOMEvent
 {
   /**
    * Custom string data associated with this event.
    */
   readonly attribute DOMString data;
   
   /**
    * The origin of the site from which this event originated, which is the
    * scheme, ":", and if the URI has a host, "//" followed by the
    * host, and if the port is not the default for the given scheme,
    * ":" followed by that port.  This value does not have a trailing slash.
    */
   readonly attribute DOMString origin;
+
+  /**
+   * The last event ID string of the event source, for server-sent DOM events; this
+   * value is the empty string for cross-origin messaging.
+   */
+  readonly attribute DOMString lastEventId;
   
   /**
    * The window which originated this event.
    */
   readonly attribute nsIDOMWindow source;
 
   /**
    * Initializes this event with the given data, in a manner analogous to
    * the similarly-named method on the nsIDOMEvent interface, also setting the
-   * data, origin, and source attributes of this appropriately.
+   * data, origin, source, and lastEventId attributes of this appropriately.
    */
   void initMessageEvent(in DOMString aType,
                         in boolean aCanBubble,
                         in boolean aCancelable,
                         in DOMString aData,
                         in DOMString aOrigin,
+                        in DOMString aLastEventId,
                         in nsIDOMWindow aSource);
   
   /**
    * Initializes this event with the given data, in a manner analogous to
    * the similarly-named method on the Event interface, also setting the data,
-   * origin, and source attributes of this appropriately.
+   * origin, source, and lastEventId attributes of this appropriately.
    */
   void initMessageEventNS(in DOMString aNamespaceURI,
                           in DOMString aType,
                           in boolean aCanBubble,
                           in boolean aCancelable,
                           in DOMString aData,
                           in DOMString aOrigin,
+                          in DOMString aLastEventId,
                           in nsIDOMWindow aSource);
 };
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -5181,151 +5181,238 @@ nsGlobalWindow::CallerInnerWindow()
   // raw pointer here and let the QI's addref be balanced by the nsCOMPtr
   // destructor's release.
   nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
   if (!win)
     return GetCurrentInnerWindowInternal();
   return static_cast<nsGlobalWindow*>(win.get());
 }
 
+
+/**
+ * Class used to represent events generated by calls to Window.postMessage,
+ * which asynchronously creates and dispatches events.
+ */
+class PostMessageEvent : public nsRunnable
+{
+  public:
+    NS_DECL_NSIRUNNABLE
+
+    PostMessageEvent(nsGlobalWindow* aSource,
+                     const nsAString& aCallerOrigin,
+                     const nsAString& aMessage,
+                     nsGlobalWindow* aTargetWindow,
+                     nsIURI* aProvidedOrigin,
+                     PRBool aTrustedCaller)
+    : mSource(aSource),
+      mCallerOrigin(aCallerOrigin),
+      mMessage(aMessage),
+      mTargetWindow(aTargetWindow),
+      mProvidedOrigin(aProvidedOrigin),
+      mTrustedCaller(aTrustedCaller)
+    {
+      MOZ_COUNT_CTOR(PostMessageEvent);
+    }
+    
+    ~PostMessageEvent()
+    {
+      MOZ_COUNT_DTOR(PostMessageEvent);
+    }
+
+  private:
+    nsRefPtr<nsGlobalWindow> mSource;
+    nsString mCallerOrigin;
+    nsString mMessage;
+    nsRefPtr<nsGlobalWindow> mTargetWindow;
+    nsCOMPtr<nsIURI> mProvidedOrigin;
+    PRBool mTrustedCaller;
+};
+
+NS_IMETHODIMP
+PostMessageEvent::Run()
+{
+  NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(),
+                    "should have been passed an outer window!");
+  NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
+                    "should have been passed an outer window!");
+
+  nsRefPtr<nsGlobalWindow> targetWindow =
+    mTargetWindow->GetCurrentInnerWindowInternal();
+  NS_ABORT_IF_FALSE(targetWindow->IsInnerWindow(),
+                    "we ordered an inner window!");
+
+  // Ensure that any origin which might have been provided is the origin of this
+  // window's document.  Note that we do this *now* instead of when postMessage
+  // is called because the target window might have been navigated to a
+  // different location between then and now.  If this check happened when
+  // postMessage was called, it would be fairly easy for a malicious webpage to
+  // intercept messages intended for another site by carefully timing navigation
+  // of the target window so it changed location after postMessage but before
+  // now.
+  if (mProvidedOrigin) {
+    // Get the target's origin either from its principal or, in the case the
+    // principal doesn't carry a URI (e.g. the system principal), the target's
+    // document.
+    nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
+    if (!targetPrin)
+      return NS_OK;
+    nsCOMPtr<nsIURI> targetURI;
+    if (NS_FAILED(targetPrin->GetURI(getter_AddRefs(targetURI))))
+      return NS_OK;
+    if (!targetURI) {
+      targetURI = targetWindow->mDoc->GetDocumentURI();
+      if (!targetURI)
+        return NS_OK;
+    }
+
+    // Note: This is contrary to the spec with respect to file: URLs, which
+    //       the spec groups into a single origin, but given we intentionally
+    //       don't do that in other places it seems better to hold the line for
+    //       now.  Long-term, we want HTML5 to address this so that we can
+    //       be compliant while being safer.
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    nsresult rv =
+      ssm->CheckSameOriginURI(mProvidedOrigin, targetURI, PR_TRUE);
+    if (NS_FAILED(rv))
+      return NS_OK;
+  }
+
+
+  // Create the event
+  nsCOMPtr<nsIDOMDocumentEvent> docEvent =
+    do_QueryInterface(targetWindow->mDocument);
+  if (!docEvent)
+    return NS_OK;
+  nsCOMPtr<nsIDOMEvent> event;
+  docEvent->CreateEvent(NS_LITERAL_STRING("MessageEvent"),
+                        getter_AddRefs(event));
+  if (!event)
+    return NS_OK;
+
+  nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
+  nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
+                                          PR_FALSE /* non-bubbling */,
+                                          PR_TRUE /* cancelable */,
+                                          mMessage,
+                                          mCallerOrigin,
+                                          EmptyString(),
+                                          mSource);
+  if (NS_FAILED(rv))
+    return NS_OK;
+
+
+  // We can't simply call dispatchEvent on the window because doing so ends
+  // up flipping the trusted bit on the event, and we don't want that to
+  // happen because then untrusted content can call postMessage on a chrome
+  // window if it can get a reference to it.
+
+  nsIPresShell *shell = targetWindow->mDoc->GetPrimaryShell();
+  nsRefPtr<nsPresContext> presContext;
+  if (shell)
+    presContext = shell->GetPresContext();
+
+  nsEvent* internalEvent;
+  nsCOMPtr<nsIPrivateDOMEvent> privEvent = do_QueryInterface(message);
+  privEvent->SetTrusted(mTrustedCaller);
+  privEvent->GetInternalNSEvent(&internalEvent);
+
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsEventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
+                              presContext,
+                              internalEvent,
+                              message,
+                              &status);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrigin)
 {
-  FORWARD_TO_INNER_CREATE(PostMessageMoz, (aMessage, aOrigin));
+  // NB: Since much of what this method does must happen at event dispatch time,
+  //     this method does not forward to the inner window, unlike most other
+  //     methods.  We do this because the only time we need to refer to this
+  //     window, we need a reference to the outer window (the PostMessageEvent
+  //     ctor call), and we don't want to pay the price of forwarding to the
+  //     inner window for no actual benefit.  Furthermore, this function must
+  //     only be called from script anyway, which should only have references to
+  //     outer windows (and if script has an inner window we've already lost).
+  NS_ABORT_IF_FALSE(IsOuterWindow(), "only call this method on outer windows");
 
   //
   // Window.postMessage is an intentional subversion of the same-origin policy.
   // As such, this code must be particularly careful in the information it
   // exposes to calling code.
   //
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   //
 
-
   // First, get the caller's window
   nsRefPtr<nsGlobalWindow> callerInnerWin = CallerInnerWindow();
   if (!callerInnerWin)
     return NS_OK;
-  NS_ASSERTION(callerInnerWin->IsInnerWindow(), "should have gotten an inner window here");
+  NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(),
+                    "should have gotten an inner window here");
 
   // Compute the caller's origin either from its principal or, in the case the
   // principal doesn't carry a URI (e.g. the system principal), the caller's
-  // document.
+  // document.  We must get this now instead of when the event is created and
+  // dispatched, because ultimately it is the identity of the calling window
+  // *now* that determines who sent the message (and not an identity which might
+  // have changed due to intervening navigations).
   nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal();
   if (!callerPrin)
     return NS_OK;
-  nsCOMPtr<nsIURI> callerURI;
-  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerURI))))
+  nsCOMPtr<nsIURI> callerOuterURI;
+  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI))))
     return NS_OK;
-  if (!callerURI) {
+  if (!callerOuterURI) {
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(callerInnerWin->mDocument);
     if (!doc)
       return NS_OK;
-    callerURI = doc->GetDocumentURI();
-    if (!callerURI)
+    callerOuterURI = doc->GetDocumentURI();
+    if (!callerOuterURI)
       return NS_OK;
   }
+  nsCOMPtr<nsIURI> callerURI = NS_GetInnermostURI(callerOuterURI);
+  if (!callerURI)
+    return NS_OK;
   const nsCString& empty = EmptyCString();
   nsCOMPtr<nsIURI> callerOrigin;
   if (NS_FAILED(callerURI->Clone(getter_AddRefs(callerOrigin))) ||
       NS_FAILED(callerOrigin->SetUserPass(empty)))
     return NS_OK;
 
 
-  // Calling postMessage on a closed window does nothing.
-  if (!mDocument)
-    return NS_OK;
-
-  nsCOMPtr<nsIDOMEventTarget> targetDoc = do_QueryInterface(mDocument);
-  nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(mDocument);
-
-
-  // Ensure that any origin which might have been provided is the origin of this
-  // window's document.
-  if (!aOrigin.IsVoid()) {
-    nsCOMPtr<nsIURI> providedOrigin;
+  // Convert the provided origin string into a URI for comparison purposes.
+  // "*" indicates no specific origin is required.
+  nsCOMPtr<nsIURI> providedOrigin;
+  if (!aOrigin.EqualsASCII("*")) {
     if (NS_FAILED(NS_NewURI(getter_AddRefs(providedOrigin), aOrigin)))
       return NS_ERROR_DOM_SYNTAX_ERR;
     if (NS_FAILED(providedOrigin->SetUserPass(empty)) ||
         NS_FAILED(providedOrigin->SetPath(empty)))
       return NS_OK;
-
-    // Get the target's origin either from its principal or, in the case the
-    // principal doesn't carry a URI (e.g. the system principal), the target's
-    // document.
-    nsIPrincipal* targetPrin = GetPrincipal();
-    if (!targetPrin)
-      return NS_OK;
-    nsCOMPtr<nsIURI> targetURI;
-    if (NS_FAILED(targetPrin->GetURI(getter_AddRefs(targetURI))))
-      return NS_OK;
-    if (!targetURI) {
-      nsCOMPtr<nsIDocument> targetDoc = do_QueryInterface(mDocument);
-      if (!targetDoc)
-        return NS_OK;
-      targetURI = targetDoc->GetDocumentURI();
-      if (!targetURI)
-        return NS_OK;
-    }
-    nsCOMPtr<nsIURI> targetOrigin;
-    if (NS_FAILED(targetURI->Clone(getter_AddRefs(targetOrigin))) ||
-        NS_FAILED(targetOrigin->SetUserPass(empty)) ||
-        NS_FAILED(targetOrigin->SetPath(empty)))
-      return NS_OK;
-
-    PRBool equal = PR_FALSE;
-    if (NS_FAILED(targetOrigin->Equals(providedOrigin, &equal)) || !equal)
-      return NS_OK;
-  }
-
-
-  // Create the event
-  nsCOMPtr<nsIDOMEvent> event;
-  docEvent->CreateEvent(NS_LITERAL_STRING("MessageEvent"),
-                        getter_AddRefs(event));
-  if (!event)
-    return NS_OK;
-  
+  }
+
   nsCAutoString origin;
   if (NS_FAILED(callerOrigin->GetPrePath(origin)))
     return NS_OK;
 
-  nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
-  nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
-                                          PR_TRUE /* bubbling */,
-                                          PR_TRUE /* cancelable */,
-                                          aMessage,
-                                          NS_ConvertUTF8toUTF16(origin),
-                                          nsContentUtils::IsCallerChrome()
-                                          ? nsnull
-                                          : callerInnerWin->GetOuterWindowInternal());
-  if (NS_FAILED(rv))
-    return NS_OK;
-
-
-  // Finally, dispatch the event, ignoring the result to prevent an exception
-  // from revealing anything about the document for this window.
-  PRBool dummy;
-  targetDoc->DispatchEvent(message, &dummy);
-
-  // Cancel exceptions that might somehow be pending. XPConnect swallows these
-  // exceptions across JS contexts, but there can be concerns if the caller
-  // and the thrower are same-context but different-origin -- see bug 387706
-  // comment 26, waring the typo in it.  Consequently, we play it safe and always
-  // cancel exceptions.
-  nsAXPCNativeCallContext *ncc;
-  rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);
-  if (NS_FAILED(rv) || !ncc)
-    return NS_OK;
-
-  JSContext *cx = nsnull;
-  if (NS_SUCCEEDED(ncc->GetJSContext(&cx)))
-    ::JS_ClearPendingException(cx);
-
-  return NS_OK;
+  // Create and asynchronously dispatch a runnable which will handle actual DOM
+  // event creation and dispatch.
+  nsRefPtr<PostMessageEvent> event =
+    new PostMessageEvent(nsContentUtils::IsCallerChrome()
+                         ? nsnull
+                         : callerInnerWin->GetOuterWindowInternal(),
+                         NS_ConvertUTF8toUTF16(origin),
+                         aMessage,
+                         this,
+                         providedOrigin,
+                         nsContentUtils::IsCallerTrustedForWrite());
+  return NS_DispatchToCurrentThread(event);
 }
 
 class nsCloseEvent : public nsRunnable {
 public:
   nsCloseEvent (nsGlobalWindow *aWindow)
     : mWindow(aWindow)
   {
   }
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -117,16 +117,17 @@ class nsScreen;
 class nsHistory;
 class nsIDocShellLoadInfo;
 class WindowStateHolder;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 #ifdef OJI
 class nsDummyJavaPluginOwner;
 #endif
+class PostMessageEvent;
 
 class nsDOMOfflineResourceList;
 class nsDOMOfflineLoadStatusList;
 
 // permissible values for CheckOpenAllow
 enum OpenAllowValue {
   allowNot = 0,     // the window opening is denied
   allowNoAbuse,     // allowed: not a popup
@@ -738,16 +739,17 @@ protected:
 #endif
 
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
   nsDataHashtable<nsVoidPtrHashKey, void*> mCachedXBLPrototypeHandlers;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
+  friend class PostMessageEvent;
   static nsIFactory *sComputedDOMStyleFactory;
 };
 
 /*
  * nsGlobalChromeWindow inherits from nsGlobalWindow. It is the global
  * object created for a Chrome Window only.
  */
 class nsGlobalChromeWindow : public nsGlobalWindow,
--- a/dom/tests/mochitest/bugs/iframe_bug407839-1.html
+++ b/dom/tests/mochitest/bugs/iframe_bug407839-1.html
@@ -52,17 +52,17 @@ function run()
 
   try {
     storage = globalStorage[""];
     message += "\n passed globalStorage[\"\"]";
   }
   catch (ex) {
   }
 
-  window.parent.postMessage(message);
+  window.parent.postMessage(message, "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/iframe_bug407839-2.html
+++ b/dom/tests/mochitest/bugs/iframe_bug407839-2.html
@@ -18,17 +18,17 @@ function run()
 
   try {
     storage = globalStorage["sub1.ält.example.org"];
   }
   catch (ex) {
     message += "\n failed globalStorage[sub1.ält.example.org]";
   }
 
-  window.parent.postMessage(message);
+  window.parent.postMessage(message, "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/iframe_bug409349.html
+++ b/dom/tests/mochitest/bugs/iframe_bug409349.html
@@ -47,17 +47,17 @@ function run()
   
   try {
     storage = globalStorage["X.0.0.1"];
     message += "\n passed globalStorage[\"X.0.0.1\"]";
   }
   catch (ex) {
   }
   
-  window.parent.postMessage(message);
+  window.parent.postMessage(message, "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/iframe_bug424093.html
+++ b/dom/tests/mochitest/bugs/iframe_bug424093.html
@@ -26,17 +26,17 @@ function run()
 
   try {
     storage = globalStorage["example.org"];
   }
   catch (ex) {
     message += "\n failed globalStorage[\"example.org\"]";
   }
 
-window.parent.postMessage(message);
+  window.parent.postMessage(message, "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug407839.html
+++ b/dom/tests/mochitest/bugs/test_bug407839.html
@@ -31,14 +31,14 @@ function receiveMessage(evt)
 {
   is(evt.data, "child-response", "got wrong response");
 
   if (++gNumMessages == 2) {
     SimpleTest.finish();
   }
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug409349.html
+++ b/dom/tests/mochitest/bugs/test_bug409349.html
@@ -24,13 +24,13 @@ function receiveMessage(evt)
   is(evt.origin, "http://127.0.0.1:8888", "wrong sender");
   ok(evt.source === window.frames.child, "wrong sender");
 
   is(evt.data, "child-response", "got wrong response");
 
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug424093.html
+++ b/dom/tests/mochitest/bugs/test_bug424093.html
@@ -26,14 +26,14 @@ SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
   is(evt.data, "child-response", "got wrong response");
 
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/dom-level0/child_ip_address.html
+++ b/dom/tests/mochitest/dom-level0/child_ip_address.html
@@ -22,17 +22,17 @@ function run()
   }
   catch (e)
   {
     domain = document.domain;
     if (domain !== "127.0.0.1")
       message += " ip-address-mutated-on-throw(" + domain + ")";
   }
 
-  window.parent.postMessage(message);
+  window.parent.postMessage(message, "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 <h1 id="location">Somewhere!</h1>
 </body>
--- a/dom/tests/mochitest/dom-level0/idn_child.html
+++ b/dom/tests/mochitest/dom-level0/idn_child.html
@@ -51,17 +51,17 @@ function receiveMessage(evt)
       message  += punycodeTest("xn--exmple-cua.test");
       break;
 
     default:
       message += " unexpected-query(" + location.search + ")";
       break;
   }
 
-  evt.source.postMessage(message);
+  evt.source.postMessage(message, evt.origin);
 }
 
 function idnTest(newDomain)
 {
   var errors = "";
 
   try
   {
@@ -86,16 +86,16 @@ function punycodeTest(newDomain)
   catch (e)
   {
     errors += " error-thrown-setting-to-punycode(" + String(e).split("").join(",") + ")";
   }
 
   return errors;
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 window.addEventListener("load", run, false);
   </script>
 </head>
 <body>
 <h1 id="location">Somewhere!</h1>
 </body>
 </html>
--- a/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html
+++ b/dom/tests/mochitest/dom-level0/test_setting_document.domain_idn.html
@@ -84,22 +84,22 @@ var messages =
 
 function receiveMessage(evt)
 {
   var origin = evt.origin;
   var match;
   if (/test$/.test(origin))
   {
     // XXX bug 414090
-    // The value of MessageEvent.domain with postMessage *should* always be IDN;
+    // The value of MessageEvent.origin with postMessage *should* always be IDN;
     // unfortunately, given our current setup for dealing with Unicode-based
     // domain-name spoofing, whether a domain is in the safe-for-IDN whitelist
     // affects the value of this property (likewise for window.location,
-    // document.location, MessageEvent.uri, document.domain, and probably a slew
-    // of other things).  :-(
+    // document.location, document.domain, and probably a slew of other
+    // things).  :-(
     //
     // These two tests should illustrate what currently happens and what should
     // happen once bug 414090 is fixed.
     todo_is(evt.origin, "http://sub1.exämple.test", "wrong sender");
     todo_isnot(evt.origin, "http://sub1.xn--exmple-cua.test", "wrong sender");
   }
   else
   {
@@ -142,29 +142,67 @@ function receiveMessage(evt)
       break;
   }
 
   state++;
 }
 
 function run()
 {
-  window.frames.idnKidWhitelist.postMessage("idn-whitelist");
-  ok(gotIDNWhitelist, "IDN whitelist message not received");
+  var target = window.frames.idnKidWhitelist;
+  target.postMessage("idn-whitelist", "http://sub1.παράδειγμα.δοκιμή");
+
+  // Double-timeouts account for 1) delay for message to be received by target
+  // window and 2) delay for response from target window to be received by this
+  // window.
 
-  window.frames.punycodeKidWhitelist.postMessage("punycode-whitelist");
-  ok(gotPunycodeWhitelist, "punycode whitelist message not received");
+  setTimeout(function()
+  {
+    setTimeout(function()
+    {
+      ok(gotIDNWhitelist, "IDN whitelist message not received");
+
+      var target = window.frames.punycodeKidWhitelist;
+      target.postMessage("punycode-whitelist", "http://sub1.παράδειγμα.δοκιμή");
+
+      setTimeout(function()
+      {
+        setTimeout(function()
+        {
+          ok(gotPunycodeWhitelist, "punycode whitelist message not received");
+
+          var target = window.frames.idnKidNoWhitelist;
+          target.postMessage("idn-nowhitelist", "http://sub1.exämple.test");
 
-  window.frames.idnKidNoWhitelist.postMessage("idn-nowhitelist");
-  ok(gotIDNNoWhitelist, "IDN no-whitelist message not received");
+          setTimeout(function()
+          {
+            setTimeout(function()
+            {
+              ok(gotIDNNoWhitelist, "IDN no-whitelist message not received");
+    
+              var target = window.frames.punycodeKidNoWhitelist;
+              target.postMessage("punycode-nowhitelist",
+                                 "http://sub1.exämple.test");
 
-  window.frames.punycodeKidNoWhitelist.postMessage("punycode-nowhitelist");
-  ok(gotPunycodeNoWhitelist, "punycode no-whitelist message not received");
+              setTimeout(function()
+              {
+                setTimeout(function()
+                {
+                  ok(gotPunycodeNoWhitelist,
+                     "punycode no-whitelist message not received");
 
-  SimpleTest.finish();
+                  SimpleTest.finish();
+                }, 0);
+              }, 0);
+            }, 0);
+          }, 0);
+        }, 0);
+      }, 0);
+    }, 0);
+  }, 0);
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/dom-level0/test_setting_document.domain_to_shortened_ipaddr.html
+++ b/dom/tests/mochitest/dom-level0/test_setting_document.domain_to_shortened_ipaddr.html
@@ -24,13 +24,13 @@ function receiveMessage(evt)
   is(evt.origin, "http://127.0.0.1:8888", "wrong sender");
   ok(evt.source === window.frames.child, "wrong sender");
 
   is(evt.data, "child-response", "got wrong response");
 
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/Makefile.in
+++ b/dom/tests/mochitest/whatwg/Makefile.in
@@ -68,16 +68,19 @@ include $(topsrcdir)/config/rules.mk
 		test_postMessage_hash.html \
 		postMessage_hash.html \
 		test_postMessage_userpass.html \
 		postMessage_userpass_helper.html \
 		test_postMessage_origin.xhtml \
 		postMessage_origin_helper.xhtml \
 		test_postMessage_closed.html \
 		postMessage_closed_helper.html \
+		test_postMessage_jar.html \
+		postMessage.jar \
+		postMessage.jar^headers^ \
 		$(NULL)
 
 _CHROME_FILES	= \
 		test_postMessage_chrome.html \
 		$(NULL)		
 
 libs:: 	$(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
index 0000000000000000000000000000000000000000..e7861c7da512b3b2e1566bfa5e99f95b9fcb66fb
GIT binary patch
literal 598
zc$^FHW@Zs#U|`^2Fi)6k!6kmAeH9}E!!%|F1`!5PhJyU!65rI~;>7gS_^iYty^NCF
zoX`+X2IfpRagRhG4y|Be_zKj}I@x~mEdzn0)%9JjQzb2|uiq^==Bg($^W=UdU$3K4
z4(3jmJi0k2pDsVoC3!~G`O&dyU+>+Xza-SS^83%1uea-K-?(&ci)(S+!jN3X3lr3u
z*OpJTOX?|V{-AT1i9@;Zd17$7>!o9Q8U7CL{oaau&38>+GI#C#RQ=COGP=LsD|@NW
z@+RQB#gl0_E^qm9(sD14jriWpf{C0-YuEPO-W?KTBc^j9+vC+K*1Z?S)V#%VJO3<N
z*ZX?OO{2Gu8s!aBO?wiJ#SST2?B03$UeE!ab&Jvzmc=CNEL1reqcDkQielb!wV$e=
zpZwJ}+*SMH!<5PkVLP3lraE|Uci#Lsv}5ALFx%btR{MPCU#9(*_3bTtfldEK{Yy)q
zJ+&;aD_$($9q($$;<424p?rzT774G7r!ABI9bb4pQP<a{tUp(9;<tS=(T(40n~(G^
zh}nK$)ktm~&$9E|k8fGA#)HRTh4vx&FHdx)`=4W(y=gT|&Qre2i^MaHPo17p-uU1t
ztK7Xm>AJ@}k8)ck_Wil@uQac4pKa+ryR+rX`(7H|Z4Ntaoc8-<Y=fH4UjH`7$mg%W
xO9yx}GKn+eigI2C5CC!+mNbG`*b@LNBmsn0Ffasov$8?7Ff#Z9>5m}23;?aF`c(h`
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage.jar^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/java-archive
--- a/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
@@ -1,18 +1,18 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>postMessage chrome message receiver</title>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
       // Content cannot post to chrome without privileges
-      window.parent.postMessage("SHOULD NOT GET THIS!");
-      
+      window.parent.postMessage("SHOULD NOT GET THIS!", "*");
+
       var msg = "post-to-content-response";
 
       if (evt.source !== null)
         msg += " wrong-source(" + evt.source + ")";
       if (!evt.isTrusted)
         msg += " unexpected-untrusted-event";
       if (evt.type !== "message")
         msg += " wrong-type(" + evt.type + ")";
@@ -23,18 +23,18 @@
 
       respond(msg);
     }
     
     function respond(msg)
     {
       // ...so get privileges and test that this works with privileges
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-      window.parent.postMessage(msg);
+      window.parent.postMessage(msg, "*");
     }
     
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
   </script>
 </head>
 <body>
 <h1 id="domain">example.org</h1>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_closed_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_closed_helper.html
@@ -1,26 +1,26 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>postMessage closed page</title>
   <script type="application/javascript">
 function receiveMessage(evt)
 {
-  evt.source.postMessage("FAIL");
+  evt.source.postMessage("FAIL", "*");
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 function setup()
 {
   var query = location.search.substring(1);
 
   if (query == "opener")
-    window.opener.postMessage("message");
+    window.opener.postMessage("message", "http://localhost:8888");
 }
 
 window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_hash.html
+++ b/dom/tests/mochitest/whatwg/postMessage_hash.html
@@ -11,19 +11,21 @@ function receiveMessage(evt)
     response += " kid-at-wrong-uri(" + window.location.href + ")";
 
   if (evt.origin !== "http://localhost:8888")
     response += " wrong-origin(" + evt.origin + ")";
   if (evt.source !== window.parent)
     response += " wrong-source";
   if (evt.data !== "from-parent")
     response += " wrong-data(" + evt.data + ")";
+  if (evt.lastEventId !== "")
+    response += " wrong-lastEventId(" + evt.lastEventId + ")";
 
-  window.parent.postMessage(response);
+  window.parent.postMessage(response, "http://localhost:8888");
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
   </script>
 </head>
 <body>
 <p>Kid iframe</p>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_helper.html
@@ -3,49 +3,56 @@
 <head>
   <title>postMessage message receiver</title>
   <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function $(id) { return document.getElementById(id); }
 
     function setup()
     {
-      $("domain").textContent = location.hostname + ":" + (location.port || 80);
+      var target = $("domain");
+      target.textContent = location.hostname + ":" + (location.port || 80);
     }
 
     function receiveMessage(evt)
     {
       var response = evt.data + "-response";
 
+      if (evt.lastEventId !== "")
+        response += " wrong-lastEventId(" + evt.lastEventId + ")";
+
       if (evt.source !== window.parent)
       {
         response += " unexpected-source(" + evt.source + ")";
         response += " window-parent-is(" + window.parent + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      if (isMozilla && evt.isTrusted)
-        response += " unexpected-trusted";
+      if (isMozilla)
+      {
+        if (evt.isTrusted !== false)
+          response += " unexpected-trusted";
+      }
 
       if (evt.type != "message")
         response += " wrong-type(" + evt.type + ")";
 
       var data = evt.data;
       if (data == "post-to-other-same-domain")
       {
         receiveSame(evt, response);
       }
       else if (data == "post-to-other-cross-domain")
       {
         receiveCross(evt, response);
       }
       else
       {
         response += " unexpected-message-to(" + window.location.href + ")";
-        window.parent.postMessage(response);
+        window.parent.postMessage(response, "http://localhost:8888");
         return;
       }
     }
 
     function receiveSame(evt, response)
     {
       var source = evt.source;
       try
@@ -63,17 +70,17 @@
           threw = true;
         }
         if (threw || privateVariable !== window.parent.privateVariable)
           response += " accessed-source!!!";
   
       }
       finally
       {
-        source.postMessage(response);
+        source.postMessage(response, evt.origin);
       }
     }
 
     function receiveCross(evt, response)
     {
       var source = evt.source;
       if (evt.origin != "http://localhost:8888")
         response += " unexpected-origin(" + evt.origin + ")";
@@ -85,19 +92,19 @@
       }
       catch (e)
       {
         threw = true;
       }
       if (!threw || privateVariable !== undefined)
         response += " accessed-source!!!";
 
-      source.postMessage(response);
+      source.postMessage(response, evt.origin);
     }
 
     window.addEventListener("load", setup, false);
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
   </script>
 </head>
 <body>
 <h1 id="domain"></h1>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_idn_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_idn_helper.html
@@ -8,31 +8,33 @@
       var response = "idn-response";
 
       if (!(evt instanceof MessageEvent))
         response += " not-a-MessageEvent";
       if (evt.origin !== "http://localhost:8888")
         response += " wrong-sender-origin(" + evt.origin + ")";
       if (evt.data !== "idn-message")
         response += " wrong-data(" + evt.data + ")";
+      if (evt.lastEventId !== "")
+        response += " wrong-lastEventId(" + evt.lastEventId + ")";
       if (evt.source !== window.parent)
         response += " wrong-source";
-      if (evt.target !== document)
+      if (evt.target !== window)
         response += " wrong-target";
       if (evt.type !== "message")
         response += " wrong-type(" + evt.type + ")";
 
-      evt.source.postMessage(response);
+      evt.source.postMessage(response, evt.origin);
     }
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
 
     function setup()
     {
       var target = document.getElementById("location");
-      target.textContent = document.domain;
+      target.textContent = location.hostname + ":" + (location.port || 80);
     }
 
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <h1 id="location">No location!</h1>
 </body>
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
@@ -4,38 +4,44 @@
 http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
 -->
 <head>
   <title>postMessage joined domains, inner frame</title>
   <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
-      var response, target;
+      var response, target, providedOrigin;
       var data = evt.data;
       if (data === "subframe-test-finished")
       {
         target = window.parent;
+        providedOrigin = "http://localhost:8888";
         response = "test-passed";
       }
       else if (data === "start-test")
       {
         target = window.frames.innermost;
+        providedOrigin = "http://example.org";
         response = "start-test";
       }
       else
       {
         target = window.parent;
+        providedOrigin = "http://localhost:8888";
         response = "not reached";
       }
 
+      if (evt.lastEventId !== "")
+        response += " wrong-lastEventId(" + evt.lastEventId + ")";
+
       if (evt.type !== "message")
         response += " wrong-type(" + evt.type + ")";
 
-      if (evt.target !== document)
+      if (evt.target !== window)
       {
         response += " wrong-target(" + evt.target + ")";
         response += " location(" + window.location.href + ")";
       }
 
       if (isMozilla && evt.isTrusted === true)
       {
         response += " unexpected-trusted-event";
@@ -50,31 +56,31 @@ http://sub1.test1.example.org/tests/dom/
         origin = "unreached";
 
       if (evt.origin !== origin)
       {
         response += " wrong-origin(" + evt.origin + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      target.postMessage(response);
+      target.postMessage(response, providedOrigin);
     }
 
     function setup()
     {
       var oldDomain = document.domain;
       var newDomain = "example.org";
 
       document.domain = newDomain;
 
       var target = document.getElementById("location");
       target.textContent = "Location: " + oldDomain +
                            ", effective domain: " + newDomain;
 
-      document.addEventListener("message", receiveMessage, false);
+      window.addEventListener("message", receiveMessage, false);
     }
 
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <p id="location">No location!</p>
 
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
@@ -6,29 +6,36 @@ http://example.org/tests/dom/tests/mochi
 <head>
   <title>postMessage joined domains, innermost frame</title>
   <script type="application/javascript" src="browserFu.js"></script>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
       var response = "subframe-test-finished";
 
+      if (evt.origin !== "http://sub1.test1.example.org")
+      {
+        response += " wrong-origin(" + evt.origin + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
       if (evt.data !== "start-test")
         response += " incorrect-subframe-data(" + evt.data + ")";
       if (evt.type !== "message")
         response += " wrong-type(" + evt.type + ")";
-      if (evt.target !== document)
+      if (evt.target !== window)
       {
         response += " wrong-target(" + evt.target + ")";
         response += " location(" + window.location.href + ")";
       }
 
-      if (isMozilla && evt.isTrusted)
+      if (isMozilla)
       {
-        response += " unexpected-trusted-event";
+        if (evt.isTrusted !== false)
+          response += " unexpected-trusted-event";
       }
 
       if (evt.source !== window.parent)
       {
         response += " unexpected-source(" + evt.source + ")";
         response += " window-parent-is(" + window.parent + ")";
         response += " location(" + window.location.href + ")";
       }
@@ -40,37 +47,31 @@ http://example.org/tests/dom/tests/mochi
       }
       catch (e)
       {
       }
 
       if (!passed)
         response += " expected-joined-domains";
 
-      if (evt.origin !== "http://sub1.test1.example.org")
-      {
-        response += " wrong-origin(" + evt.origin + ")";
-        response += " location(" + window.location.href + ")";
-      }
-
-      window.parent.postMessage(response);
+      window.parent.postMessage(response, "http://sub1.test1.example.org");
     }
     
     function setup()
     {
       var oldDomain = document.domain;
       var newDomain = "example.org"; // join with parent
 
       document.domain = newDomain;
 
       var target = document.getElementById("location");
       target.textContent = "Location: " + oldDomain +
                            ", effective domain: " + newDomain;
 
-      document.addEventListener("message", receiveMessage, false);
+      window.addEventListener("message", receiveMessage, false);
     }
 
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <p id="location">No location!</p>
 </body>
--- a/dom/tests/mochitest/whatwg/postMessage_onOther.html
+++ b/dom/tests/mochitest/whatwg/postMessage_onOther.html
@@ -1,52 +1,109 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>postMessage called through another frame</title>
   <script type="application/javascript">
+    var PATH = "/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html";
+
     function receiveMessage(evt)
     {
-      var response = "response-to-sibling-sent-message";
+      if (evt.lastEventId !== "")
+      {
+        fail("unexpected non-empty lastEventId");
+        return;
+      }
+
+      switch (window.location.href)
+      {
+        case "http://example.com" + PATH:
+          receiveTopDomain(evt);
+          break;
+
+        case "http://test1.example.com" + PATH:
+          receiveSubDomain(evt);
+          break;
+
+        default:
+          fail("unexpected location");
+      }
+    }
+
+    function fail(msg)
+    {
+      window.parent.postMessage("FAIL " + msg, "*");
+    }
 
-      // Our parent frame called testSiblingPostMessage (below) on a frame
-      // containing this page on localhost:8888.  testSiblingPostMessage then
-      // called postMessage on this page on example.org:8000.  We thus expect
-      // to see an event whose source is the window of our sibling frame on
-      // localhost:8888.  In other words, the event we receive should have:
-      //
-      // http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html
-      //
-      // and not
-      //
-      // http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
-      //
-      // as its source.
+    // The parent frame sends "start-test" to the subdomain frame to start.
+    // The subdomain frame then sets document.domain to the top domain so that
+    // the top domain frame can access it.  It then sends a message to the top
+    // domain frame to tell it to do likewise; once that happens, the top domain
+    // frame can then call a method on the subdomain frame window, which will
+    // call a method *on the top domain window* to send a message to the parent
+    // window.  We thus expect to see an event whose source is the subdomain
+    // window -- *not* the top domain window.  Therefore, its .origin should be:
+    //
+    //   http://test1.example.com
+    //
+    // and not
+    //
+    //   http://example.com
 
-      if (evt.data !== "message-from-sibling")
-        response += " wrong-data(" + evt.data + ")";
+    function receiveSubDomain(evt)
+    {
       if (evt.origin !== "http://localhost:8888")
-        response += " failed-wrong-origin(" + evt.origin + ")";
-      if (evt.source !== window.parent.firstFrame)
-        response += " failed-wrong-source";
+      {
+        fail("wrong top-domain origin: " + evt.origin);
+        return;
+      }
+      if (evt.data !== "start-test")
+      {
+        fail("wrong top-domain message: " + evt.origin);
+        return;
+      }
 
-      window.parent.postMessage(response);
+      document.domain = "example.com";
+      window.parent.topDomainFrame.postMessage("domain-switch",
+                                               "http://example.com");
+    }
+    
+    function receiveTopDomain(evt)
+    {
+      if (evt.origin !== "http://test1.example.com")
+      {
+        fail("wrong subdomain origin: " + evt.origin);
+        return;
+      }
+      if (evt.data !== "domain-switch")
+      {
+        fail("wrong subdomain message: " + evt.origin);
+        return;
+      }
+      if (evt.source !== window.parent.subDomainFrame)
+      {
+        fail("wrong source on message from subdomain");
+        return;
+      }
+
+      document.domain = "example.com";
+      window.parent.subDomainFrame.testSiblingPostMessage();
     }
 
     function testSiblingPostMessage()
     {
-      window.parent.secondFrame.postMessage("message-from-sibling");
+      window.parent.postMessage("test-finished", "http://localhost:8888");
     }
 
     function setup()
     {
       var target = document.getElementById("location");
-      target.textContent = document.domain;
+      target.textContent = location.hostname + ":" + (location.port || 80);
     }
 
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <h1 id="location">No location!</h1>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml
+++ b/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml
@@ -1,36 +1,36 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
-  <title>postMessage throwing page</title>
+  <title>postMessage origin-testing helper page</title>
   <script type="application/javascript"><![CDATA[
 function receiveMessage(evt)
 {
   var response = "PASS";
 
   if (evt.origin !== "http://localhost:8888")
     response += " wrong-origin(" + evt.origin + ")";
   if (evt.source !== window.parent)
     response += " wrong-source";
   if (evt.data !== "PASS")
     response += " wrong-data(" + evt.data + ")";
 
-  window.parent.postMessage(response);
+  window.parent.postMessage(response, "http://localhost:8888");
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 
 // Aids for identifying origins
 
 function setup()
 {
   var target = document.getElementById("location");
-  target.textContent = document.domain + ":" + (location.port || 80);
+  target.textContent = location.hostname + ":" + (location.port || 80);
 }
 
 window.addEventListener("load", setup, false);
   ]]></script>
 </head>
 <body>
 <h1 id="location">No location!</h1>
 </body>
--- a/dom/tests/mochitest/whatwg/postMessage_override_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_override_helper.html
@@ -1,42 +1,42 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>Overriding postMessage and dispatchEvent bindings</title>
   <script type="application/javascript">
     window.postMessage = function (evt)
     {
-      window.parent.postMessage("FAIL overridden postMessage called");
+      window.parent.postMessage("FAIL overridden postMessage called", "*");
     };
 
     var count = 0;
 
     function receiveMessage(evt)
     {
       count++;
       if (count == 1)
       {
         window.dispatchEvent = function(evt)
         {
-          window.parent.postMessage("FAIL");
+          window.parent.postMessage("FAIL", "*");
           throw "dispatchEvent threw";
         };
       }
 
-      window.parent.postMessage(evt.data);
+      window.parent.postMessage(evt.data, "http://localhost:8888");
     }
     
     function setup()
     {
       var target = document.getElementById("location");
-      target.textContent = document.domain;
+      target.textContent = location.hostname + ":" + (location.port || 80);
     }
 
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
 
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <h1 id="location">No location!</h1>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/postMessage_throw_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_throw_helper.html
@@ -2,22 +2,22 @@
 <html>
 <head>
   <title>postMessage throwing page</title>
   <script type="application/javascript">
     function receiveMessage(evt)
     {
       throw 17;
     }
-    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("message", receiveMessage, false);
 
     function setup()
     {
       var target = document.getElementById("location");
-      target.textContent = document.domain;
+      target.textContent = location.hostname + ":" + (location.port || 80);
     }
 
     window.addEventListener("load", setup, false);
   </script>
 </head>
 <body>
 <h1 id="location">No location!</h1>
 </body>
--- a/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html
@@ -6,26 +6,28 @@
 function sendMessage(evt)
 {
   var msg = "child-message";
 
   if (evt.origin !== "http://localhost:8888")
     msg += " wrong-origin(" + evt.origin + ")";
   if (evt.data !== "parent-message")
     msg += " wrong-data(" + evt.data + ")";
+  if (evt.lastEventId !== "")
+    msg += " wrong-lastEventId(" + evt.lastEventId + ")";
   if (evt.source !== window.parent)
     msg += " wrong-source";
 
   // It would be good to guarantee that we've been opened with a userinfo of
   // "bobhope:password", but Gecko elides that from the content-visible URL,
   // and I can't find another way to actually detect this programmatically.
 
-  window.parent.postMessage(msg);
+  window.parent.postMessage(msg, "http://localhost:8888");
 }
 
-document.addEventListener("message", sendMessage, false);
+window.addEventListener("message", sendMessage, false);
   </script>
 </head>
 <body>
 <p>Kid iframe</p>
 </body>
 </html>
 
--- a/dom/tests/mochitest/whatwg/test_MessageEvent.html
+++ b/dom/tests/mochitest/whatwg/test_MessageEvent.html
@@ -23,41 +23,47 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script class="testbody" type="application/javascript">
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
 var data = "foobar";
 var origin = "http://cool.example.com";
 var bubbles = true, cancelable = true;
+var lastEventId = "lastEventId";
 
 var target;
 
 var count = 0;
 
 function sendMsg()
 {
   try
   {
     var evt = document.createEvent("MessageEvent");
     ok(evt instanceof MessageEvent, "I ordered a MessageEvent!");
   
     if (isMozilla)
     {
       is(evt.source, null,
-         "not initialized yet, so null in our implementation");    
+         "not initialized yet, so null in our implementation");
+      is(evt.lastEventId, "",
+         "not initialized yet, so empty string in our implementation");
     }
   
-    evt.initMessageEvent("message", bubbles, cancelable, data, origin, null);
+    evt.initMessageEvent("message", bubbles, cancelable, data, origin,
+                         lastEventId, null);
     ok(evt.source === null, "null source is fine for a MessageEvent");
   
-    evt.initMessageEvent("message", bubbles, cancelable, data, origin, window);
+    evt.initMessageEvent("message", bubbles, cancelable, data, origin,
+                         lastEventId, window);
   
     is(evt.data, data, "unexpected data");
     is(evt.origin, origin, "unexpected origin");
+    is(evt.lastEventId, lastEventId, "unexpected lastEventId");
   
     is(evt.cancelable, cancelable, "wrong cancelable property");
     is(evt.bubbles, bubbles, "wrong bubbling property");
     is(evt.source, window, "wrong source");
   
     return target.dispatchEvent(evt);
   }
   catch (e)
@@ -66,16 +72,17 @@ function sendMsg()
     return false;
   }
 }
 
 function recvMsg(evt)
 {
   is(evt.data, data, "unexpected data");
   is(evt.origin, origin, "unexpected origin");
+  is(evt.lastEventId, lastEventId, "unexpected lastEventId");
 
   is(evt.cancelable, cancelable, "wrong cancelable property");
   is(evt.bubbles, bubbles, "wrong bubbling property");
   is(evt.source, window, "wrong source");
 
   is(evt.target, target, "wrong target");
 
   if (target == evt.currentTarget)
--- a/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html
+++ b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html
@@ -23,17 +23,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function run()
 {
   try
   {
     var msg = document.createEvent("MessageEvent");
     msg.initMessageEvent("message", true, true,
-                         "foo", "http://evil.com", window);
+                         "foo", "http://evil.com", "",
+                         window);
 
     try
     {
       var ex;
       window.frames.otherDomain.dispatchEvent(msg);
       ok(false, "should have thrown a security exception per HTML5");
     }
     catch (e)
--- a/dom/tests/mochitest/whatwg/test_postMessage.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage.html
@@ -22,30 +22,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
-var testsCompletedCount = 0;
-
 /** Variable for receivers to attempt to get. */
 window.privateVariable = 17;
 
-/** For sentinel finish, if necessary in deficient browsers */
+/** For sentinel finish, if necessary in deficient browsers. */
 var finished = false;
 
-/** Receives MessageEvents to this window. */
+/** Ends testing if it isn't already done. */
+function finish()
+{
+  if (!finished)
+  {
+    finished = true;
+    SimpleTest.finish();
+  }
+}
+
+/** Receives MessageEvents. */
 function messageReceiver(evt)
 {
   try
   {
     ok(evt instanceof MessageEvent, "umm, how did we get this?");
+    is(evt.lastEventId, "",
+       "postMessage creates events with empty lastEventId");
     is(evt.type, "message", "expected events of type 'message'");
   
     if (isMozilla)
     {
       ok(evt.isTrusted === false, "shouldn't have been a trusted event");
     }
   
     var data = evt.data;
@@ -56,78 +66,80 @@ function messageReceiver(evt)
     if (data === "post-to-self")
     {
       respondToSelf(evt);
       return;
     }
 
     switch (evt.data)
     {
-      case "post-to-self":
-        
       case "post-to-self-response":
         receiveSelf(evt);
         break;
   
       case "post-to-other-same-domain-response":
         receiveOtherSameDomain(evt);
         break;
   
       case "post-to-other-cross-domain-response":
         receiveOtherCrossDomain(evt);
+        
+        // All the tests have executed, so we're done.
+        finish();
         break;
   
       default:
         ok(false, "unexpected message: " + evt.data);
+        finish();
         break;
     }
   }
   catch (e)
   {
     ok(false, "error processing event with data '" + evt.data + "': " + e);
-  }
-
-  // if all the tests have executed, we're done
-  if (++testsCompletedCount == allTests.length)
-  {
-    finished = true;
-    SimpleTest.finish();
+    finish();
   }
 }
 
 
 /******************
  * SELF-RESPONDER *
  ******************/
 
 function respondToSelf(evt)
 {
   is(evt.origin, "http://localhost:8888", "event has wrong origin");
   is(evt.source, window, "we posted this message!");
   
-  evt.source.postMessage("post-to-self-response");
+  evt.source.postMessage("post-to-self-response", evt.origin);
 }
 
 
 /*************
  * RECEIVERS *
  *************/
 
 function receiveSelf(evt)
 {
   is(evt.origin, "http://localhost:8888", "event has wrong origin");
   is(evt.source, window, "we posted this message!");
+
+  window.frames.otherSameDomain.postMessage("post-to-other-same-domain",
+                                            "http://localhost:8888");
 }
 
 function receiveOtherSameDomain(evt)
 {
   is(evt.origin, "http://localhost:8888",
      "same-domain response event has wrong origin");
   is(evt.source, window.frames.otherSameDomain,
      "wrong source for same-domain message!");
+
+  window.frames.otherCrossDomain.postMessage("post-to-other-cross-domain",
+                                            "http://example.org:8000");
 }
 
 function receiveOtherCrossDomain(evt)
 {
   is(evt.origin, "http://example.org:8000",
      "same-domain response event has wrong origin");
 
   // can't use |is| here, because ok tries to get properties on its arguments
@@ -136,61 +148,20 @@ function receiveOtherCrossDomain(evt)
      "wrong source for cross-domain message!");
 }
 
 
 /**************
  * TEST SETUP *
  **************/
 
-document.addEventListener("message", messageReceiver, false);
-
-/**
- * Returns a nullary function which posts the given message to the given
- * destination.
- */
-function createMessageDispatcher(message, destination)
+function start()
 {
-  function dispatcher()
-  {
-    try
-    {
-      destination.postMessage(message);
-    }
-    catch (e)
-    {
-      ok(false, "error while calling postMessage: " + e);
-    }
-  }
-
-  return dispatcher;
+  window.postMessage("post-to-self", "http://localhost:8888");
 }
 
-var allTests =
-  [
-    createMessageDispatcher("post-to-self", window),
-    createMessageDispatcher("post-to-other-same-domain",
-                            window.frames.otherSameDomain),
-    createMessageDispatcher("post-to-other-cross-domain",
-                            window.frames.otherCrossDomain),
-  ];
-
-for (var i = 0, sz = allTests.length; i != sz; i++)
-  addLoadEvent(allTests[i]);
+window.addEventListener("load", start, false);
+window.addEventListener("message", messageReceiver, false);
 
-/**
- * Browsers which fail to send a response to a postMessage need this to
- * finish the test.
- */
-function sentinel()
-{
-  if (!finished)
-  {
-    ok(false, "shouldn't be necessary (finished in last of allTests)");
-    SimpleTest.finish();
-  }
-}
-
-addLoadEvent(sentinel);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
@@ -23,24 +23,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
   is(evt.origin, "http://localhost:8888", "wrong sender");
   ok(evt.source === window, "wrong source");
 
   is(evt.data, "generate-event", "wrong data");
+  is(evt.lastEventId, "", "wrong lastEventId");
 
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 function run()
 {
-  window.postMessage("generate-event");
+  window.postMessage("generate-event", "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
@@ -24,89 +24,88 @@ chrome://mochikit/content/chrome/dom/tes
 
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
-var testsCompletedCount = 0;
+var finished = false;
+function finish()
+{
+  if (!finished)
+  {
+    finished = true;
+    SimpleTest.finish();
+  }
+}
 
 /** Receives MessageEvents to this window. */
 function messageReceiver(evt)
 {
   ok(evt instanceof MessageEvent, "umm, how did we get this?");
   is(evt.type, "message", "expected events of type 'message'");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
 
   switch (evt.data)
   {
     case "post-to-self":
       checkSelf(evt);
       break;
       
     case "post-to-content-response":
       receiveContent(evt);
       break;
 
     default:
       ok(false, "unexpected message: " + evt.data);
+      finish();
       break;
   }
-
-  // if all the tests have executed, we're done
-  if (++testsCompletedCount == allTests.length)
-    setTimeout(SimpleTest.finish, 0);
 }
 
 
 /******************
  * SELF-RESPONDER *
  ******************/
 
 function checkSelf(evt)
 {
   is(evt.isTrusted, true, "should have sent a trusted event");
   is(evt.origin, "chrome://mochikit", "wrong origin for chrome: URL");
   is(evt.source, null, "chrome posters get a null source, for security");
+
+  window.frames.contentDomain.postMessage("post-to-content",
+                                          "http://example.org");
 }
 
 
 /*************
  * RECEIVERS *
  *************/
 
 function receiveContent(evt)
 {
   is(evt.isTrusted, true, "should have sent a trusted event");
   is(evt.origin, "http://example.org", "content response event has wrong URI");
   is(evt.source, window.frames.contentDomain,
      "wrong source for same-domain message!");
+
+  finish();
 }
 
 
 /**************
  * TEST SETUP *
  **************/
 
-document.addEventListener("message", messageReceiver, false);
-
-/**
- * Returns a nullary function which posts the given message to the given
- * destination.
- */
-function createMessageDispatcher(message, destination)
+function run()
 {
-  return function() { destination.postMessage(message); };
+  window.addEventListener("message", messageReceiver, false);
+  window.postMessage("post-to-self", "*");
 }
 
-var allTests =
-  [
-    createMessageDispatcher("post-to-self", window),
-    createMessageDispatcher("post-to-content",
-                            window.frames.contentDomain),
-  ];
-  
-allTests.forEach(addLoadEvent);
+window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_closed.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_closed.html
@@ -18,48 +18,49 @@
 <script class="testbody" type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
   is(evt.origin, "http://localhost:8888", "wrong origin");
   ok(evt.source === openedWindow, "wrong source");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
 
   is(evt.data, "message", "wrong data");
   if (evt.data !== "message")
     return; // prevent recursion if bugs
 
   evt.source.close();
 
   function afterClose()
   {
     document.removeEventListener("message", receiveMessage, false);
-    evt.source.postMessage("NOT-RECEIVED");
+    evt.source.postMessage("NOT-RECEIVED", "*");
 
     var iframe = document.createElement("iframe");
     iframe.id = "insertedIframe";
     $("holder").appendChild(iframe);
     iframe.addEventListener("load", iframeLoaded, false);
     iframe.src = "postMessage_closed_helper.html?parent";
   }
 
   setTimeout(afterClose, 0);
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 function iframeLoaded(evt)
 {
   var iframe = $("insertedIframe");
   iframe.removeEventListener("load", iframeLoaded, false);
 
   var iframeWindow = iframe.contentWindow;
   $("holder").removeChild($("insertedIframe"));
-  iframeWindow.postMessage("NOT-RECEIVED");
+  iframeWindow.postMessage("NOT-RECEIVED", "*");
 
   SimpleTest.finish();
 }
 
 var openedWindow;
 
 function run()
 {
--- a/dom/tests/mochitest/whatwg/test_postMessage_hash.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_hash.html
@@ -20,26 +20,27 @@
 <script class="testbody" type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
   is(evt.origin, "http://localhost:8888", "wrong origin");
   ok(evt.source === window.frames.kid, "wrong source");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
 
   is(evt.data, "response-message", "wrong data");
 
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 function run()
 {
-  window.frames.kid.postMessage("from-parent");
+  window.frames.kid.postMessage("from-parent", "http://localhost:8888");
 }
 
 window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
@@ -36,39 +36,37 @@ function receiveMessage(evt)
   {
     ok(evt.isTrusted === false, "shouldn't have been a trusted event");
   }
 
   is(evt.origin, "http://sub1.ält.example.org:8000",
      "wrong origin -- IDN issue, perhaps?");
 
   is(evt.data, "idn-response", "unexpected test result");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
   ok(evt.source === idnWindow, "wrong source");
 
-  responseReceived = true;
+  SimpleTest.finish();
 }
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 var xhtmlns = "http://www.w3.org/1999/xhtml";
 
 function setup()
 {
+  var idnFrame = document.getElementsByTagNameNS(xhtmlns, "iframe")[0];
+  idnWindow = idnFrame.contentWindow;
   try
   {
-    var idnFrame = document.getElementsByTagNameNS(xhtmlns, "iframe")[0];
-    idnWindow = idnFrame.contentWindow;
-    idnWindow.postMessage("idn-message");
-
-    ok(responseReceived, "should have gotten a response before returning");
+    idnWindow.postMessage("idn-message", "http://sub1.ält.example.org:8000");
   }
   catch (e)
   {
     ok(false, "failed to post message: " + e);
+    SimpleTest.finish();
   }
-
-  SimpleTest.finish();
 }
 
 addLoadEvent(setup);
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_jar.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430251
+-->
+<head>
+  <title>postMessage's interaction with pages at jar: URIs</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <script type="text/javascript" src="browserFu.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <base href="http://example.com/" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430251">Mozilla Bug 430251</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe src="jar:http://example.com/tests/dom/tests/mochitest/whatwg/postMessage.jar!/postMessage_jar.html"
+        name="kid"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 430251 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function receiveMessage(evt)
+{
+  is(evt.origin, "http://example.com", "wrong sender");
+  ok(evt.source === window.frames.kid, "wrong source");
+
+  is(evt.data, "finish-test", "wrong data");
+  is(evt.lastEventId, "", "wrong lastEventId");
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage, false);
+
+function run()
+{
+  window.frames.kid.postMessage("start-test", "http://example.com");
+}
+
+window.addEventListener("load", run, false);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_joined.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_joined.html
@@ -30,37 +30,30 @@ function receiveTestResult(evt)
   ok(evt instanceof MessageEvent, "umm, how did we get this?");
   is(evt.type, "message", "expected events of type 'message'");
 
   if (isMozilla)
   {
     ok(evt.isTrusted === false, "shouldn't have been a trusted event");
   }
 
-  var data = evt.data;
-  
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
+
   // Either we passed the test or we failed it.  The message's
   // contents should help to diagnose the failure.  Either way,
   // consider this the end of the test.
-  is(data, "test-passed", "unexpected test result");
+  is(evt.data, "test-passed", "unexpected test result");
   SimpleTest.finish();
 }
 
 function setup()
 {
-  document.addEventListener("message", receiveTestResult, false);
-  try
-  {
-    window.frames.container.postMessage("start-test");
-  }
-  catch (e)
-  {
-    ok(false, "failed to post message");
-    SimpleTest.finish();
-  }
+  window.addEventListener("message", receiveTestResult, false);
+  window.frames.container.postMessage("start-test",
+                                      "http://sub1.test1.example.org");
 }
 
 addLoadEvent(setup);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
@@ -9,68 +9,44 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
-<iframe src="http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html"
-        name="firstFrame"></iframe>
-<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html"
-        name="secondFrame"></iframe>
+<iframe src="http://example.com/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html"
+        name="topDomainFrame"></iframe>
+<iframe src="http://test1.example.com/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html"
+        name="subDomainFrame"></iframe>
 
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
-var finished = false;
-
 /** Receives MessageEvents to this window. */
 function messageReceiver(evt)
 {
   ok(evt instanceof MessageEvent, "wrong event type");
-  is(evt.origin, "http://example.org:8000", "unexpected origin");
-  is(evt.data, "response-to-sibling-sent-message",
+  is(evt.origin, "http://test1.example.com", "unexpected origin");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
+  is(evt.data, "test-finished",
      "unexpected data in message");
 
-  // Handle buggy browsers that might somehow have received a message twice
-  if (finished)
-    return;
-
-  finished = true;
   SimpleTest.finish();
 }
 
-function postToSecondFrameThroughFirstFrame()
+function run()
 {
-  try
-  {
-    window.frames.firstFrame.testSiblingPostMessage();
-  }
-  catch (e)
-  {
-    ok(false, "threw exception trying to post through firstFrame: " + e);
-  }
+  window.frames.subDomainFrame.postMessage("start-test",
+                                           "http://test1.example.com");
 }
 
-/** For buggy browsers that didn't send a response. */
-function sentinel()
-{
-  if (!finished)
-  {
-    ok(false, "should have been finished by now -- didn't receive response?");
-    SimpleTest.finish();
-  }
-}
-
-document.addEventListener("message", messageReceiver, false);
-
-addLoadEvent(postToSecondFrameThroughFirstFrame);
-addLoadEvent(sentinel);
+window.addEventListener("message", messageReceiver, false);
+window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
@@ -38,24 +38,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function errorCheck(i, called, errorCode, actualCode)
 {
   ok(!called, "receiver should not have been called for test #" + i);
   is(actualCode, errorCode, "wrong error thrown in test #" + i);
 }
 
-function errorCheckTodo(i, called, errorCode, actualCode)
-{
-  todo(!called, "receiver should not have been called for test #" + i);
-  todo_is(actualCode, errorCode, "wrong error thrown in test #" + i);
-}
-
-var ONE_PASS = ["PASS"];
-
 var tests =
   [
    // 0
    {
      args: ["NOT-RECEIVED", ""],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
@@ -74,94 +66,94 @@ var tests =
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
    {
      args: ["NOT-RECEIVED", "http: //"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    // 5
    {
      args: ["NOT-RECEIVED", "http ://"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
    {
      args: ["TODO", " http://localhost:8888"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
      returnOrigin: "http://localhost:8888",
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    {
      args: ["NOT-RECEIVED", "hä"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
    {
      args: ["NOT-RECEIVED", "http://lo\0k.com"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
    {
      args: ["NOT-RECEIVED", "http: //localhost:8888"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    // 10
    {
      args: ["NOT-RECEIVED", "http://localhost :8888"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
    {
      args: ["NOT-RECEIVED", "http:// localhost:8888"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    {
      args: ["TODO", "http://\nlocalhost:8888"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
      returnOrigin: "http://localhost:8888",
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    {
      args: ["TODO", "http://localhost:8888\0"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
      returnOrigin: "http://localhost:8888",
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    {
      args: ["TODO", "http://localhost:8888\n"],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR,
 
      returnOrigin: "http://localhost:8888",
-     throwsNoException: true
+     hasThrowsNoExceptionBug: true
    },
    // 15
    {
-     args: ONE_PASS,
+     args: ["PASS", "*"],
      source: "sameDomain",
      returnOrigin: "http://localhost:8888"
    },
    {
-     args: ["PASS", null],
+     args: ["PASS", "http://localhost:8888"],
      source: "sameDomain",
      returnOrigin: "http://localhost:8888"
    },
    {
      args: ["PASS", "http://example.com"],
      source: "otherDomain",
      returnOrigin: "http://example.com"
    },
@@ -306,171 +298,232 @@ var tests =
      returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
    },
    // 45
    {
      args: ["PASS", "http://sub1.exämple.test"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.exämple.test:80"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.exämple.test:80/"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.exämple.test/"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.exämple.test/foobar"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    // 50
    {
      args: ["PASS", "http://sub1.xn--exmple-cua.test"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.xn--exmple-cua.test:80"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.xn--exmple-cua.test:80/"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.xn--exmple-cua.test/"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    {
      args: ["PASS", "http://sub1.xn--exmple-cua.test/foobar"],
      source: "idnKidNoWhitelist",
      returnOrigin: "http://sub1.exämple.test",
 
-     wrongReturnOrigin: true
+     hasWrongReturnOriginBug: true
    },
    // 55
    {
      args: ["NOT-RECEIVED", undefined],
      source: "sameDomain",
      code: DOMException.SYNTAX_ERR
    },
   ];
 
-function allTests()
+function allTests(callback)
 {
   var test, target, called;
 
-  function receive(evt)
+  function eventCheck(evt)
   {
-    var originCheck = test.wrongReturnOrigin ? todo_is : is;
+    ok(test === tests[i],
+       "i and test are out of sync!  async is hard, let's go shopping");
+
+    var originCheck = test.hasWrongReturnOriginBug ? todo_is : is;
     originCheck(evt.origin, test.returnOrigin, "wrong origin for #" + i);
     if (test.args[0] == "TODO")
       todo_is(evt.data, "PASS", "wrong data");
     else
       is(evt.data, "PASS", "wrong data");
+    is(evt.lastEventId, "",
+       "postMessage creates events with empty lastEventId");
     ok(evt.source === target, "wrong source");
     called = true;
   }
 
-  function post(win, args, err)
+  function nextTest()
   {
-    called = false;
-    win.postMessage.apply(win, args);
-  }
+    i++;
 
-  document.addEventListener("message", receive, false);
+    if (i === end)
+    {
+      removeListener(listener);
 
-  for (var i = 0, sz = tests.length; i < sz; i++)
-  {
+      setTimeout(callback, 0);
+      return;
+    }
+
     test = tests[i];
 
     target = $(test.source).contentWindow;
     try
     {
       called = false;
       target.postMessage.apply(target, test.args);
-      if (test.throwsNoException)
-        todo(false, "should throw on test #" + i);
-      else if (test.expectNoCallback)
-        (test.checkCallback || ok)(!called, "shouldn't have been called #" + i);
-      else
-        (test.checkCallback || ok)(called, "should have been called #" + i);
     }
     catch (e)
     {
-      (test.errorCheck || errorCheck)(i, called, e.code, test.code);
+      // Since an exception was thrown, we know at this point that we're not
+      // waiting on anything else in the queue of script to run, and we can just
+      // call nextTest directly.
+      errorCheck(i, called, e.code, test.code);
+      nextTest();
+      return;
     }
+
+    // We've set up the event generated by postMessage to be dispatched, and
+    // it's sitting in a queue somewhere.  However, it hasn't arrived yet, and
+    // the target document hasn't received the event -- so we need to continue
+    // our tests later.
+    setTimeout(function()
+    {
+      // The event was dispatched, and the target frame's code processed it and
+      // responded -- so now *that* response is sitting in the queue of script to
+      // execute.  Process pending script one more time so we know we've gotten
+      // the response.
+      setTimeout(function()
+      {
+        // Finally, we've posted our message and received our response, and
+        // there's nothing further coming down the pipe.
+        if (test.hasThrowsNoExceptionBug)
+          todo(false, "should throw on test #" + i);
+        else
+          ok(test.expectNoCallback ^ called, "should have been called #" + i);
+
+        nextTest();
+      }, 0);
+    }, 0);
   }
 
-  document.removeEventListener("message", receive, false);
+  var listener = registerMessageListener(eventCheck);
+
+  var i = -1, end = tests.length;
+  nextTest();
 }
 
-function oddballTests()
+function registerMessageListener(func, callback)
 {
-  var called;
   function receive(evt)
   {
+    func(evt);
+    if (callback)
+      callback();
+  }
+  
+  window.addEventListener("message", receive, false);
+  
+  return receive;
+}
+
+function removeListener(listener)
+{
+  window.removeEventListener("message", listener, false);
+}
+
+function oddballTests(callback)
+{
+  var called = false;
+
+  function eventChecks(evt)
+  {
     is(evt.origin, "http://localhost:8888", "wrong sender");
     is(evt.data, "PASS", "wrong data");
+    is(evt.lastEventId, "",
+       "postMessage creates events with empty lastEventId");
     ok(evt.source === window, "wrong source");
     called = true;
   }
-  document.addEventListener("message", receive, false);
-
-  try
-  {
-    called = false;
-    window.postMessage("PASS");
-    is(called, true, "should have been called");
 
-    called = false;
-    window.postMessage("PASS", null);
+  var listener = registerMessageListener(eventChecks, function()
+  {
     is(called, true, "should have been called");
-  }
-  finally
-  {
-    document.removeEventListener("message", receive, false);
-  }
+    removeListener(listener);
+    
+    called = false;
+    listener = registerMessageListener(eventChecks, function()
+    {
+      is(called, true, "should have been called");
+
+      removeListener(listener);
+      callback();
+    });
+    
+    window.postMessage("PASS", "http://localhost:8888");
+  });
+
+  window.postMessage("PASS", "http://localhost:8888");
 }
 
 function run()
 {
-  oddballTests();
-  allTests();
-  SimpleTest.finish();
+  oddballTests(function()
+  {
+    allTests(function()
+    {
+      SimpleTest.finish();
+    });
+  });
 }
 
 window.addEventListener("load", run, false);
 ]]></script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_override.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_override.html
@@ -20,39 +20,50 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="application/javascript">
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
 var count = 0;
 
-function test()
+function test(callback)
 {
   try
   {
     count++;
     switch (count)
     {
       case 1:
-        window.frames[0].postMessage("PASS 1");
+        window.frames[0].postMessage("PASS 1", "http://example.org:8000");
         break;
 
       case 2:
-        window.frames[0].postMessage("PASS 2");
+        window.frames[0].postMessage("PASS 2", "http://example.org:8000");
         break;
   
       default:
         ok(false, "unexpected");
     }
   }
   catch (e)
   {
     ok(false, "error running test " + count + ": " + e);
   }
+
+  // Waiting for sent postMessage event to be processed...
+  setTimeout(function()
+  {
+    // Waiting for responding postMessage event to be processed...
+    setTimeout(function()
+    {
+      // We have our response!
+      callback();
+    }, 0);
+  }, 0);
 }
 
 function messageReceiver(evt)
 {
   var expect;
 
   switch (count)
   {
@@ -64,20 +75,30 @@ function messageReceiver(evt)
       expect = "PASS 2";
       break;
 
     default:
       expect = "unexpected";
       break;
   }
 
-  is(evt.data, expect, "uh-oh, we didn't get the right postMessage!");
+  is(evt.data, expect,
+     "uh-oh, we (" + count + ") didn't get the right postMessage!");
 }
 
-document.addEventListener("message", messageReceiver, false);
+window.addEventListener("message", messageReceiver, false);
 
-addLoadEvent(test);
-addLoadEvent(test);
-addLoadEvent(SimpleTest.finish);
+function run()
+{
+  test(function()
+  {
+    test(function()
+    {
+      SimpleTest.finish();
+    }, 0);
+  });
+}
+
+window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
@@ -24,17 +24,18 @@ SimpleTest.waitForExplicitFinish();
 var B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /**
  * Encodes an array of bytes into a string using the base 64 encoding scheme.
  *
  * @param bytes
  *   An array of bytes to encode.
  */
-function b64(str) {
+function b64(str)
+{
   var byteArray = new Array(str.length);
   for (var i = 0, sz = str.length; i < sz; i++)
     byteArray[i] = str.charCodeAt(i);
 
   var index = 0;
   function get3Bytes()
   {
     if (byteArray.length - index < 3)
@@ -127,35 +128,42 @@ function messageReceiver(evt)
       // speaking.  For the specific case of about:blank it's also possible
       // that the origin is determined by the code that opens the window.  It's
       // not codified yet which of these two causes the identifier tokens on
       // the event generated by the new window to be those of this window, but
       // in either case this is what they should be.
       is(evt.origin, "http://localhost:8888",
          "wrong origin for event from about:blank");
       is(evt.source, aboutBlankWindow, "wrong source");
+
+      // ...and onto the next test
       aboutBlankResponseReceived = true;
+      setupBlank2();
     }
     else if (evt.data === "about:blank2-response")
     {
       is(evt.origin, "http://localhost:8888",
          "wrong origin for event from about:blank #2");
       is(evt.source, aboutBlank2Window, "wrong source");
       aboutBlank2ResponseReceived = true;
+
+      setupData();
     }
     else if (evt.data === "data-response")
     {
       // HTML5 defines the origin of a data: URI as the origin of the window or
       // script that opened the data: URI.
       is(evt.origin, "http://localhost:8888",
          "wrong origin for event from data URL (should be the origin of the " +
          "window/script that opened the URL, in this case the origin of this " +
          "file)");
       is(evt.source, dataWindow, "wrong source");
+
       dataResponseReceived = true;
+      finish();
     }
     else
     {
       ok(false, "unexpected message: " + evt.data);
     }
   }
   catch (e)
   {
@@ -177,34 +185,44 @@ function getContents(description, respon
     "\n" +
     "  if (evt.source !== window.parent)\n" +
     "    response += ' wrong-source';\n" +
     "  if (evt.origin !== 'http://localhost:8888')\n" +
     "    response += ' wrong-origin(' + evt.origin + ')';\n" +
     "  if (evt.data !== 'from-opener')\n" +
     "    response += ' wrong-data(' + evt.data + ')';\n" +
     "\n" +
-    "  window.parent.postMessage(response);\n" +
+    "  window.parent.postMessage(response, 'http://localhost:8888');\n" +
     "}\n" +
     "\n" +
     "function ready()\n" +
     "{\n" +
-    "  window.parent.postMessage('next-test');\n" +
+    "  window.parent.postMessage('next-test', 'http://localhost:8888');\n" +
     "}\n" +
     "\n" +
     "window.addEventListener('load', ready, false);\n" +
-    "document.addEventListener('message', receive, false);\n" +
+    "window.addEventListener('message', receive, false);\n" +
     "  </script>\n" +
     "</head>\n" +
     "<body><p>" + description + "</p></body>\n" +
     "</html>";
 
   return contents;
 }
 
+/** Finish the test, but don't finish twice if we timed out. */
+function finish()
+{
+  if (!finished)
+  {
+    finished = true;
+    SimpleTest.finish();
+  }
+}
+
 var xhtmlns = "http://www.w3.org/1999/xhtml";
 
 function insert(el)
 {
   var content = $("content");
   content.parentNode.insertBefore(el, content);
 }
 
@@ -231,18 +249,17 @@ function setupBlank()
 
 function blankFailed()
 {
   if (!aboutBlankResponseReceived && !finished)
   {
     ok(false,
        "test timed out (postMessage not accessible on window.parent in " +
        "the first about:blank iframe?)");
-    finished = true;
-    SimpleTest.finish();
+    finish();
   }
 }
 
 function setupBlank2()
 {
   var aboutBlank2Frame = document.createElementNS(xhtmlns, "iframe");
   aboutBlank2Frame.addEventListener("load", nextTest, false);
   aboutBlank2Frame.setAttribute("src", "about:blank");
@@ -271,18 +288,17 @@ function setupData()
 
 function dataFailed()
 {
   if (!dataResponseReceived && !finished)
   {
     ok(false,
        "test timed out (postMessage not accessible on window.parent in " +
        "the data: iframe?)");
-    finished = true;
-    SimpleTest.finish();
+    finish();
   }
 }
 
 var count = 0;
 function nextTest()
 {
   switch (count++)
   {
@@ -301,72 +317,40 @@ function nextTest()
     default:
       ok(false, "unreached");
       break;
   }
 }
 
 function testBlank()
 {
-  try
-  {
-    aboutBlankWindow.postMessage("from-opener");
-  }
-  catch (e)
-  {
-    ok(false, "exception thrown trying to post message #1 to about:blank");
-  }
-
-  ok(aboutBlankResponseReceived, "about:blank never got a response!");
-
-  setTimeout(setupBlank2, 0);
+  aboutBlankWindow.postMessage("from-opener", "http://localhost:8888");
 }
 
 function testBlank2()
 {
   // For some reason we can't access this across browsers prior to the iframe
   // loading, so set its value here.
   aboutBlank2Window = window.frames[1];
 
   var doc = aboutBlank2Window.document;
 
   doc.body.textContent = "This was about:blank #2";
 
   var script = doc.createElement("script");
-  script.textContent = "window.parent.postMessage('about:blank2-response');";
+  script.textContent =
+    "window.parent.postMessage('about:blank2-response', " +
+    "                          'http://localhost:8888');";
   doc.body.appendChild(script);
-
-  // Note that this script gets run synchronously, so we're done with the
-  // test here.
-  ok(aboutBlank2ResponseReceived, "postMessage from about:blank #2 failed");
-
-  setTimeout(setupData, 0);
 }
 
 function testData()
 {
-  try
-  {
-    dataWindow.postMessage("from-opener");
-  }
-  catch (e)
-  {
-    ok(false, "exception thrown trying to post message to data: URL window");
-  }
-
-  ok(dataResponseReceived, "we never got a response!");
-
-  // Don't re-report -- we must have already failed, and this can
-  // screw up the displayed results.
-  if (finished)
-    return;
-
-  finished = true;
-  SimpleTest.finish();
+  dataWindow.postMessage("from-opener", "http://localhost:8888");
 }
 
-document.addEventListener("message", messageReceiver, false);
+window.addEventListener("message", messageReceiver, false);
 
 addLoadEvent(setupBlank);
 ]]></script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_throw.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_throw.html
@@ -25,17 +25,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 387706 **/
 
 SimpleTest.waitForExplicitFinish();
 
 function atLoad()
 {
   try
   {
-    sameDomain.postMessage("foo");
+    sameDomain.postMessage("foo", "http://localhost:8888");
     ok(true, "should not have thrown for same-domain exception");
   }
   catch (e)
   {
     ok(false, "uh-oh, threw a same-domain exception: " + e);
   }
 
   setTimeout(next, 0);
@@ -46,17 +46,17 @@ function next()
   ok(true, "no pending-exception wackiness for same-domain");
   setTimeout(next2, 0);
 }
 
 function next2()
 {
   try
   {
-    crossDomain.postMessage("foo");
+    crossDomain.postMessage("foo", "http://example.org:8000");
     ok(true, "should not have thrown for cross-domain exception");
   }
   catch (e)
   {
     ok(false, "uh-oh, threw a cross-domain exception: " + e);
   }
 
   setTimeout(next3, 0);
--- a/dom/tests/mochitest/whatwg/test_postMessage_userpass.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_userpass.html
@@ -23,24 +23,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 417075 **/
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
   is(evt.origin, "http://example.org", "wrong origin");
   is(evt.data, "child-message", "wrong data");
+  is(evt.lastEventId, "", "postMessage creates events with empty lastEventId");
   ok(evt.source === window.frames.userPassKid, "wrong source");
   SimpleTest.finish();
 }
 
-document.addEventListener("message", receiveMessage, false);
+window.addEventListener("message", receiveMessage, false);
 
 function sendMessage(evt)
 {
-  window.frames.userPassKid.postMessage("parent-message");
+  window.frames.userPassKid.postMessage("parent-message", "http://example.org");
 }
 
 window.addEventListener("load", sendMessage, false);
 </script>
 </pre>
 </body>
 </html>
--- a/extensions/cookie/test/file_domain_inner.html
+++ b/extensions/cookie/test/file_domain_inner.html
@@ -1,14 +1,14 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
-  <script type="text/javascript">
+<!DOCTYPE HTML>
+<html>
+<head>
+  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+  <script type="text/javascript">
     document.cookie = "can=has";
 
     // send a message to our test document, to say we're done loading
-    window.opener.postMessage("message");
-  </script>
-<body>
+    window.opener.postMessage("message", "http://localhost:8888");
+  </script>
+<body>
 <iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_domain_inner_inner.html
+++ b/extensions/cookie/test/file_domain_inner_inner.html
@@ -1,14 +1,14 @@
-<!DOCTYPE HTML>
-<html>
-<head>
+<!DOCTYPE HTML>
+<html>
+<head>
   <META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
-  <script type="text/javascript">
+  <script type="text/javascript">
     document.cookie = "can2=has2";
 
     // send a message to our test document, to say we're done loading
-    window.parent.opener.postMessage("message");
-  </script>
-</head>
+    window.parent.opener.postMessage("message", "http://localhost:8888");
+  </script>
+</head>
 <body>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_image_inner.html
+++ b/extensions/cookie/test/file_image_inner.html
@@ -1,15 +1,15 @@
-<!DOCTYPE HTML>
-<html>
-<head>
+<!DOCTYPE HTML>
+<html>
+<head>
   <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
   <script type="text/javascript">
     document.cookie = "can=has";
 
     // send a message to our test document, to say we're done loading
-    window.opener.postMessage("message");
+    window.opener.postMessage("message", "http://localhost:8888");
   </script>
-</head>
+</head>
 <body>
 <iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_image_inner_inner.html"></iframe>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_image_inner_inner.html
+++ b/extensions/cookie/test/file_image_inner_inner.html
@@ -1,20 +1,20 @@
-<!DOCTYPE HTML>
-<html>
-<head>
+<!DOCTYPE HTML>
+<html>
+<head>
   <link rel="stylesheet" type="text/css" media="all" href="http://example.org/tests/extensions/cookie/test/test1.css" />
   <link rel="stylesheet" type="text/css" media="all" href="http://example.com/tests/extensions/cookie/test/test2.css" />
   <META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
   <script type="text/javascript">
-    function runTest() {
+    function runTest() {
       document.cookie = "can2=has2";
 
       // send a message to our test document, to say we're done loading
-      window.parent.opener.postMessage("message");
+      window.parent.opener.postMessage("message", "http://localhost:8888");
     }
-  </script>
-</head>
+  </script>
+</head>
 <body>
 <img src="http://example.org/tests/extensions/cookie/test/image1.png" onload="runTest()" />
 <img src="http://example.com/tests/extensions/cookie/test/image2.png" onload="runTest()" />
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_loadflags_inner.html
+++ b/extensions/cookie/test/file_loadflags_inner.html
@@ -1,17 +1,17 @@
-<!DOCTYPE HTML>
-<html>
-<head>
+<!DOCTYPE HTML>
+<html>
+<head>
   <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
   <script type="text/javascript">
     function runTest() {
       document.cookie = "can=has";
 
       // send a message to our test document, to say we're done loading
-      window.opener.postMessage("message");
+      window.opener.postMessage("message", "http://localhost:8888");
     }
   </script>
-</head>
+</head>
 <body>
 <img src="http://example.org/tests/extensions/cookie/test/image1.png" onload="runTest()" />
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_localhost_inner.html
+++ b/extensions/cookie/test/file_localhost_inner.html
@@ -1,14 +1,14 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
-  <script type="text/javascript">
+<!DOCTYPE HTML>
+<html>
+<head>
+  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+  <script type="text/javascript">
     document.cookie = "can=has";
 
     // send a message to our test document, to say we're done loading
-    window.opener.postMessage("message");
-  </script>
-<body>
+    window.opener.postMessage("message", "http://localhost:8888");
+  </script>
+<body>
 <iframe name="frame1" src="http://localhost:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_loopback_inner.html
+++ b/extensions/cookie/test/file_loopback_inner.html
@@ -1,14 +1,14 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
-  <script type="text/javascript">
+<!DOCTYPE HTML>
+<html>
+<head>
+  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+  <script type="text/javascript">
     document.cookie = "can=has";
 
     // send a message to our test document, to say we're done loading
-    window.opener.postMessage("message");
-  </script>
-<body>
+    window.opener.postMessage("message", "http://localhost:8888");
+  </script>
+<body>
 <iframe name="frame1" src="http://127.0.0.1:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_subdomain_inner.html
+++ b/extensions/cookie/test/file_subdomain_inner.html
@@ -1,14 +1,14 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
-  <script type="text/javascript">
+<!DOCTYPE HTML>
+<html>
+<head>
+  <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+  <script type="text/javascript">
     document.cookie = "can=has";
 
     // send a message to our test document, to say we're done loading
-    window.opener.postMessage("message");
-  </script>
-<body>
+    window.opener.postMessage("message", "http://localhost:8888");
+  </script>
+<body>
 <iframe name="frame1" src="http://test2.example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
-</body>
-</html>
+</body>
+</html>
--- a/extensions/cookie/test/file_testcommon.js
+++ b/extensions/cookie/test/file_testcommon.js
@@ -58,10 +58,10 @@ function runTest() {
   }
   is(count, gExpectedCookies, "incorrect number of cookies");
 
   gPopup.close();
   cs.removeAll();
   SimpleTest.finish();
 }
 
-document.addEventListener("message", messageReceiver, false);
+window.addEventListener("message", messageReceiver, false);
 
--- a/extensions/cookie/test/file_testloadflags.js
+++ b/extensions/cookie/test/file_testloadflags.js
@@ -102,10 +102,10 @@ function runTest() {
   is(gHeaders, gExpectedHeaders, "incorrect number of request headers");
 
   o.remove();
   gPopup.close();
   cs.removeAll();
   SimpleTest.finish();
 }
 
-document.addEventListener("message", messageReceiver, false);
+window.addEventListener("message", messageReceiver, false);