Bug 387706 - Implement HTML5's cross-document messaging API (postMessage), enabling secure cross-origin communication between windows. r+sr=sicking, sr=jst, a=jst
authorjwalden@mit.edu
Tue, 29 Jan 2008 17:31:29 -0800
changeset 10976 cb788a544a7e1057193836890014516e0931a950
parent 10975 bcce79f916a509a155b7a56d8ee213d6e27af99c
child 10977 f19d5fa385ac6096e2aa102e732838d3148c1bc6
push idunknown
push userunknown
push dateunknown
reviewersjst, jst
bugs387706
milestone1.9b3pre
Bug 387706 - Implement HTML5's cross-document messaging API (postMessage), enabling secure cross-origin communication between windows. r+sr=sicking, sr=jst, a=jst
content/events/public/nsIPrivateDOMEvent.h
content/events/src/Makefile.in
content/events/src/nsDOMMessageEvent.cpp
content/events/src/nsDOMMessageEvent.h
content/events/src/nsEventDispatcher.cpp
dom/public/idl/base/nsIDOMWindowInternal.idl
dom/public/idl/events/Makefile.in
dom/public/idl/events/nsIDOMMessageEvent.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsGlobalWindow.h
dom/tests/mochitest/Makefile.in
dom/tests/mochitest/whatwg/Makefile.in
dom/tests/mochitest/whatwg/browserFu.js
dom/tests/mochitest/whatwg/postMessage_chrome_helper.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_override_helper.html
dom/tests/mochitest/whatwg/postMessage_throw_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_chrome.html
dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
dom/tests/mochitest/whatwg/test_postMessage_joined.html
dom/tests/mochitest/whatwg/test_postMessage_onOther.html
dom/tests/mochitest/whatwg/test_postMessage_override.html
dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
dom/tests/mochitest/whatwg/test_postMessage_throw.html
modules/libpref/src/init/all.js
testing/mochitest/harness.xul
testing/mochitest/runtests.pl.in
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -97,10 +97,12 @@ nsresult
 NS_NewDOMSVGEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEvent* aEvent);
 nsresult
 NS_NewDOMSVGZoomEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsGUIEvent* aEvent);
 #endif // MOZ_SVG
 nsresult
 NS_NewDOMXULCommandEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsXULCommandEvent* aEvent);
 nsresult
 NS_NewDOMCommandEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsCommandEvent* aEvent);
+nsresult
+NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
 
 #endif // nsIPrivateDOMEvent_h__
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -75,16 +75,17 @@ CPPSRCS		= \
 		nsDOMTextEvent.cpp \
 		nsDOMMouseEvent.cpp \
 		nsDOMMutationEvent.cpp \
 		nsDOMPopupBlockedEvent.cpp \
 		nsDOMBeforeUnloadEvent.cpp \
 		nsDOMPageTransitionEvent.cpp \
 		nsDOMXULCommandEvent.cpp \
 		nsDOMCommandEvent.cpp \
+		nsDOMMessageEvent.cpp \
 		nsPrivateTextRange.cpp \
 		nsDOMEventGroup.cpp \
 		nsXMLEventsManager.cpp \
 		nsXMLEventsElement.cpp \
 		nsPLDOMEvent.cpp \
 		nsEventDispatcher.cpp \
 		nsIMEStateManager.cpp \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jeff Walden <jwalden+code@mit.edu> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMMessageEvent.h"
+#include "nsContentUtils.h"
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMessageEvent)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMMessageEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMessageEvent)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(MessageEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMPL_ADDREF_INHERITED(nsDOMMessageEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMMessageEvent, nsDOMEvent)
+
+NS_IMETHODIMP
+nsDOMMessageEvent::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMMessageEvent::GetDomain(nsAString& aDomain)
+{
+  aDomain = mDomain;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMMessageEvent::GetUri(nsAString& aURI)
+{
+  aURI = mURI;
+  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& aDomain,
+                                    const nsAString& aURI,
+                                    nsIDOMWindow* aSource)
+{
+  nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mData = aData;
+  mDomain = aDomain;
+  mURI = aURI;
+  mSource = aSource;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMMessageEvent::InitMessageEventNS(const nsAString& aNamespaceURI,
+                                      const nsAString& aType,
+                                      PRBool aCanBubble,
+                                      PRBool aCancelable,
+                                      const nsAString& aData,
+                                      const nsAString& aDomain,
+                                      const nsAString& aURI,
+                                      nsIDOMWindow* aSource)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult,
+                      nsPresContext* aPresContext,
+                      nsEvent* aEvent) 
+{
+  nsDOMMessageEvent* it = new nsDOMMessageEvent(aPresContext, aEvent);
+  if (nsnull == it)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  return CallQueryInterface(it, aInstancePtrResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMMessageEvent.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jeff Walden <jwalden+code@mit.edu> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMMessageEvent_h__
+#define nsDOMMessageEvent_h__
+
+#include "nsIDOMMessageEvent.h"
+#include "nsDOMEvent.h"
+#include "nsCycleCollectionParticipant.h"
+
+/**
+ * Implements the MessageEvent event, used for cross-document messaging.
+ *
+ * See http://www.whatwg.org/specs/web-apps/current-work/#messageevent for
+ * further details.
+ */
+class nsDOMMessageEvent : public nsIDOMMessageEvent,
+                          public nsDOMEvent
+{
+public:
+  nsDOMMessageEvent(nsPresContext* aPresContext, nsEvent* aEvent)
+    : nsDOMEvent(aPresContext, aEvent)
+  {
+  }
+                     
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMMessageEvent, nsDOMEvent)
+
+  NS_DECL_NSIDOMMESSAGEEVENT
+
+  // Forward to base class
+  NS_FORWARD_TO_NSDOMEVENT
+
+private:
+  nsString mData;
+  nsString mDomain;
+  nsString mURI;
+  nsCOMPtr<nsIDOMWindow> mSource;
+};
+
+#endif // nsDOMMessageEvent_h__
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -646,11 +646,13 @@ nsEventDispatcher::CreateEvent(nsPresCon
       aEventType.LowerCaseEqualsLiteral("xulcommandevents"))
     return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("commandevent") ||
       aEventType.LowerCaseEqualsLiteral("commandevents"))
     return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") ||
       aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
     return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nsnull);
+  if (aEventType.LowerCaseEqualsLiteral("messageevent"))
+    return NS_NewDOMMessageEvent(aDOMEvent, aPresContext, nsnull);
 
   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 }
--- a/dom/public/idl/base/nsIDOMWindowInternal.idl
+++ b/dom/public/idl/base/nsIDOMWindowInternal.idl
@@ -39,17 +39,24 @@
 
 #include "nsIDOMWindow2.idl"
 
 interface nsIPrompt;
 interface nsIControllers;
 interface nsIDOMLocation;
 interface nsIVariant;
 
-[scriptable, uuid(0d12a345-3fe2-491e-af0d-bcfd5c4baa03)]
+%{C++
+/* I hate you, Windows. */
+#ifdef PostMessage
+#undef PostMessage
+#endif
+%}
+
+[scriptable, uuid(86f7b733-aff9-469a-9e8c-2996f7db2720)]
 interface nsIDOMWindowInternal : nsIDOMWindow2
 {
   readonly attribute nsIDOMWindowInternal        window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindowInternal        self;
 
   readonly attribute nsIDOMNavigator             navigator;
@@ -195,9 +202,24 @@ interface nsIDOMWindowInternal : nsIDOMW
   DOMString                 atob(in DOMString aAsciiString);
   DOMString                 btoa(in DOMString aBase64Data);
 
   readonly attribute nsIDOMElement               frameElement;
 
   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.
+   * 
+   * See the WHATWG HTML5 specification, section 6.4, for more details.
+   */
+  void postMessage(in DOMString message);
 };
--- a/dom/public/idl/events/Makefile.in
+++ b/dom/public/idl/events/Makefile.in
@@ -66,11 +66,12 @@ XPIDLSRCS =					\
 	nsIDOMMutationEvent.idl			\
 	nsIDOMNSUIEvent.idl			\
 	nsIDOMPopupBlockedEvent.idl		\
 	nsIDOMBeforeUnloadEvent.idl		\
 	nsIDOMNSEventTarget.idl			\
 	nsIDOMSmartCardEvent.idl                \
 	nsIDOMPageTransitionEvent.idl		\
 	nsIDOMCommandEvent.idl			\
+	nsIDOMMessageEvent.idl			\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMMessageEvent.idl
@@ -0,0 +1,97 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jeff Walden <jwalden+code@mit.edu> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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
+ */
+[scriptable, uuid(3CF6163E-0227-49D9-B52B-F061828FB9B8)]
+interface nsIDOMMessageEvent : nsIDOMEvent
+{
+  /**
+   * Custom string data associated with this event.
+   */
+  readonly attribute DOMString data;
+  
+  /**
+   * The domain of the site from which this event originated.
+   */
+  readonly attribute DOMString domain;
+  
+  /**
+   * The URI of the site from which this event was created.
+   */
+  readonly attribute DOMString uri;
+  
+  /**
+   * 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, domain, uri, and source attributes of this appropriately.
+   */
+  void initMessageEvent(in DOMString aType,
+                        in boolean aCanBubble,
+                        in boolean aCancelable,
+                        in DOMString aData,
+                        in DOMString aDomain,
+                        in DOMString aURI,
+                        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,
+   * domain, uri, and source attributes of this appropriately.
+   */
+  void initMessageEventNS(in DOMString aNamespaceURI,
+                          in DOMString aType,
+                          in boolean aCanBubble,
+                          in boolean aCancelable,
+                          in DOMString aData,
+                          in DOMString aDomain,
+                          in DOMString aURI,
+                          in nsIDOMWindow aSource);
+};
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -415,16 +415,20 @@ enum nsDOMClassInfoID {
   eDOMClassInfo_FileException_id,
 
   // DOM modal content window class, almost identical to Window
   eDOMClassInfo_ModalContentWindow_id,
 
   // Data Events
   eDOMClassInfo_DataContainerEvent_id,
 
+  // event used for cross-domain message-passing and for server-sent events in
+  // HTML5
+  eDOMClassInfo_MessageEvent_id,
+
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
 
 /**
  * nsIClassInfo helper macros
  */
 
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -231,16 +231,17 @@
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMCommandEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMSmartCardEvent.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMPageTransitionEvent.h"
+#include "nsIDOMMessageEvent.h"
 #include "nsIDOMNSDocumentStyle.h"
 #include "nsIDOMDocumentRange.h"
 #include "nsIDOMDocumentTraversal.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMLinkStyle.h"
 #include "nsIDOMHTMLDocument.h"
@@ -1222,16 +1223,19 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsWindowSH,
                            DEFAULT_SCRIPTABLE_FLAGS |
                            WINDOW_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DataContainerEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MessageEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that shuld be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -3361,16 +3365,21 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMModalContentWindow)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DataContainerEvent, nsIDOMDataContainerEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataContainerEvent)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(MessageEvent, nsIDOMMessageEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMMessageEvent)
+    DOM_CLASSINFO_EVENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -24,16 +24,17 @@
  *   Travis Bogard <travis@netscape.com>
  *   Brendan Eich <brendan@mozilla.org>
  *   David Hyatt (hyatt@netscape.com)
  *   Dan Rosen <dr@netscape.com>
  *   Vidur Apparao <vidur@netscape.com>
  *   Johnny Stenback <jst@netscape.com>
  *   Mark Hammond <mhammond@skippinet.com.au>
  *   Ryan Jones <sciguyryan@gmail.com>
+ *   Jeff Walden <jwalden+code@mit.edu>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -101,16 +102,17 @@
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMKeyEvent.h"
+#include "nsIDOMMessageEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMPkcs11.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow2.h"
 #include "nsThreadUtils.h"
 #include "nsIEventStateManager.h"
 #include "nsIHttpProtocolHandler.h"
@@ -4965,16 +4967,157 @@ nsGlobalWindow::GetFrames(nsIDOMWindow**
   *aFrames = this;
   NS_ADDREF(*aFrames);
 
   FlushPendingNotifications(Flush_ContentAndNotify);
 
   return NS_OK;
 }
 
+static nsGlobalWindow*
+CallerInnerWindow()
+{
+  nsAXPCNativeCallContext *ncc;
+  nsresult rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);
+  if (NS_FAILED(rv) || !ncc) {
+    NS_ASSERTION(ncc, "Please don't call this method from C++!");
+    return nsnull;
+  }
+
+  JSContext *cx = nsnull;
+  if (NS_FAILED(ncc->GetJSContext(&cx))) {
+    NS_WARNING("couldn't get JS context from native context");
+    return nsnull;
+  }
+
+  JSObject *scope = ::JS_GetScopeChain(cx);
+  if (!scope)
+    return nsnull;
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+  nsContentUtils::XPConnect()->
+    GetWrappedNativeOfJSObject(cx, ::JS_GetGlobalForObject(cx, scope),
+                               getter_AddRefs(wrapper));
+  if (!wrapper)
+    return nsnull;
+
+  // The calling window must be holding a reference, so we can just return a
+  // raw pointer here and let the QI's addref be balanced by the nsCOMPtr
+  // destructor's release.
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
+  return static_cast<nsGlobalWindow*>(win.get());
+}
+
+/* I hate you, Windows. */
+#ifdef PostMessage
+#undef PostMessage
+#endif
+
+NS_IMETHODIMP
+nsGlobalWindow::PostMessage(const nsAString& aMessage)
+{
+  FORWARD_TO_INNER_CREATE(PostMessage, (aMessage));
+
+  //
+  // 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");
+
+  // Obtain the caller's principal, from which we can usually extract a URI
+  // and domain for the event.
+  nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal();
+  if (!callerPrin)
+    return NS_OK;
+  nsCOMPtr<nsIURI> docURI;
+  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(docURI))))
+    return NS_OK;
+
+  // If we hit this, we're probably in chrome context and have the URI-less
+  // system principal, so get the URI off the caller's document.
+  if (!docURI) {
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(callerInnerWin->mDocument);
+    if (!doc)
+      return NS_OK;
+
+    docURI = doc->GetDocumentURI();
+    if (!docURI)
+      return NS_OK;
+  }
+
+  nsCAutoString domain, uri;
+  nsresult rv  = docURI->GetSpec(uri);
+  if (NS_FAILED(rv))
+    return NS_OK;
+
+  // This really shouldn't be necessary -- URLs which don't have a host should
+  // return the empty string -- but nsSimpleURI just errors instead of
+  // truncating domain.  We could just ignore the returned error, but in the
+  // interests of playing it safe in a sensitive API, we check and truncate if
+  // GetHost fails.  Empty hosts are valid for some URI schemes, and any code
+  // which expects a non-empty host should ignore the message we'll dispatch.
+  if (NS_FAILED(docURI->GetHost(domain)))
+    domain.Truncate();
+
+  // Create the event
+  nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(mDocument);
+  if (!docEvent)
+    return NS_OK;
+  nsCOMPtr<nsIDOMEvent> event;
+  docEvent->CreateEvent(NS_LITERAL_STRING("MessageEvent"),
+                        getter_AddRefs(event));
+  if (!event)
+    return NS_ERROR_FAILURE;
+  
+  nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
+  rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
+                                 PR_TRUE /* bubbling */,
+                                 PR_TRUE /* cancelable */,
+                                 aMessage,
+                                 NS_ConvertUTF8toUTF16(domain),
+                                 NS_ConvertUTF8toUTF16(uri),
+                                 nsContentUtils::IsCallerChrome()
+                                 ? nsnull
+                                 : callerInnerWin->GetOuterWindowInternal());
+  if (NS_FAILED(rv))
+    return rv;
+
+
+  // Finally, dispatch the event, ignoring the result to prevent an exception
+  // from revealing anything about the document for this window.
+  PRBool dummy;
+  nsCOMPtr<nsIDOMEventTarget> targetDoc = do_QueryInterface(mDocument);
+  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;
+}
+
 class nsCloseEvent : public nsRunnable {
 public:
   nsCloseEvent (nsGlobalWindow *aWindow)
     : mWindow(aWindow)
   {
   }
  
   NS_IMETHOD Run() {
@@ -7187,43 +7330,17 @@ nsGlobalWindow::SetTimeoutOrInterval(PRB
   // This needs to forward to the inner window, but since the current
   // inner may not be the inner in the calling scope, we need to treat
   // this specially here as we don't want timeouts registered in a
   // dying inner window to get registered and run on the current inner
   // window. To get this right, we need to forward this call to the
   // inner window that's calling window.setTimeout().
 
   if (IsOuterWindow()) {
-    nsAXPCNativeCallContext *ncc = nsnull;
-    nsresult rv = nsContentUtils::XPConnect()->
-      GetCurrentNativeCallContext(&ncc);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!ncc) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-
-    JSContext *cx = nsnull;
-
-    rv = ncc->GetJSContext(&cx);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    JSObject *scope = ::JS_GetScopeChain(cx);
-
-    if (!scope) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-
-    nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-    nsContentUtils::XPConnect()->
-      GetWrappedNativeOfJSObject(cx, ::JS_GetGlobalForObject(cx, scope),
-                                 getter_AddRefs(wrapper));
-    NS_ENSURE_TRUE(wrapper, NS_ERROR_NOT_AVAILABLE);
-
-    nsGlobalWindow *callerInner = FromWrapper(wrapper);
+    nsGlobalWindow* callerInner = CallerInnerWindow();
     NS_ENSURE_TRUE(callerInner, NS_ERROR_NOT_AVAILABLE);
 
     // If the caller and the callee share the same outer window,
     // forward to the callee inner. Else, we forward to the current
     // inner (e.g. someone is calling setTimeout() on a reference to
     // some other window).
 
     if (callerInner->GetOuterWindow() == this &&
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -93,16 +93,21 @@
 #include "prclist.h"
 #include "nsIDOMStorage.h"
 #include "nsIDOMStorageList.h"
 #include "nsIDOMStorageWindow.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIArray.h"
 
+/* I hate you, Windows. */
+#ifdef PostMessage
+#undef PostMessage
+#endif
+
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
 class nsIDOMBarProp;
 class nsIDocument;
 class nsIContent;
 class nsPresContext;
 class nsIDOMEvent;
@@ -206,17 +211,17 @@ private:
 //*****************************************************************************
 // Beware that all scriptable interfaces implemented by
 // nsGlobalWindow will be reachable from JS, if you make this class
 // implement new interfaces you better know what you're
 // doing. Security wise this is very sensitive code. --
 // jst@netscape.com
 
 // nsGlobalWindow inherits PRCList for maintaining a list of all inner
-// widows still in memory for any given outer window. This list is
+// windows still in memory for any given outer window. This list is
 // needed to ensure that mOuterWindow doesn't end up dangling. The
 // nature of PRCList means that the window itself is always in the
 // list, and an outer window's list will also contain all inner window
 // objects that are still in memory (and in reality all inner window
 // object's lists also contain its outer and all other inner windows
 // belonging to the same outer window, but that's an unimportant
 // side effect of inheriting PRCList).
 
--- a/dom/tests/mochitest/Makefile.in
+++ b/dom/tests/mochitest/Makefile.in
@@ -44,12 +44,13 @@ include $(DEPTH)/config/autoconf.mk
 
 DIRS	+= \
 	dom-level0 \
 	dom-level1-core \
 	dom-level2-core \
 	ajax \
 	bugs \
 	chrome \
+	whatwg \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/Makefile.in
@@ -0,0 +1,77 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jeff Walden <jwalden+code@mit.edu>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir	= dom/tests/mochitest/whatwg
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES	= \
+		browserFu.js \
+		test_MessageEvent.html \
+		test_postMessage.html \
+		postMessage_helper.html \
+		test_postMessage_joined.html \
+		postMessage_joined_helper.html \
+		postMessage_joined_helper2.html \
+		test_postMessage_onOther.html \
+		postMessage_onOther.html \
+		test_MessageEvent_dispatchToOther.html \
+		test_postMessage_override.html \
+		postMessage_override_helper.html \
+		test_postMessage_throw.html \
+		postMessage_throw_helper.html \
+		postMessage_chrome_helper.html \
+		test_postMessage_special.xhtml \
+		test_postMessage_idn.xhtml \
+		postMessage_idn_helper.html \
+		$(NULL)
+
+_CHROME_FILES	= \
+		test_postMessage_chrome.html \
+		$(NULL)		
+
+libs:: 	$(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+
+libs:: $(_CHROME_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/browserFu.js
@@ -0,0 +1,6 @@
+/**
+ * File which includes stuff for Mozilla-specific checks which shouldn't happen
+ * in other browsers but which we wish to test.
+ */
+
+var isMozilla = navigator.product === "Gecko" && "buildID" in navigator;
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>postMessage chrome message receiver</title>
+  <script type="application/javascript">
+    var sourcePath = "chrome://mochikit/content/chrome/" +
+                     "dom/tests/mochitest/whatwg/test_postMessage_chrome.html";
+
+    function receiveMessage(evt)
+    {
+      // Content cannot post to chrome without privileges
+      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 + ")";
+      if (evt.uri !== sourcePath)
+        msg += " wrong-uri(" + evt.uri + ")";
+      if (evt.domain !== "mochikit")
+        msg += " wrong-domain(" + evt.domain + ")";
+      if (evt.data !== "post-to-content")
+        msg += " wrong-message(" + evt.data + ")";
+
+      respond(msg);
+    }
+    
+    function respond(msg)
+    {
+      // ...so get privileges and test that this works with privileges
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      window.parent.postMessage(msg);
+    }
+    
+    document.addEventListener("message", receiveMessage, false);
+  </script>
+</head>
+<body>
+<h1 id="domain">example.org</h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_helper.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<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 otherPath = "/tests/dom/tests/mochitest/whatwg/test_postMessage.html";
+    
+    function receiveMessage(evt)
+    {
+      var response = evt.data + "-response";
+
+      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 (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);
+        return;
+      }
+    }
+
+    function receiveSame(evt, response)
+    {
+      var source = evt.source;
+      try
+      {
+        if (evt.domain != "localhost")
+          response += " unexpected-domain(" + evt.domain + ")";
+        if (evt.uri != "http://localhost:8888" + otherPath)
+          response += " unexpected-uri(" + evt.uri + ")";
+          
+        try
+        {
+          var threw = false;
+          var privateVariable = source.privateVariable;
+        }
+        catch (e)
+        {
+          threw = true;
+        }
+        if (threw || privateVariable !== window.parent.privateVariable)
+          response += " accessed-source!!!";
+  
+      }
+      finally
+      {
+        source.postMessage(response);
+      }
+    }
+
+    function receiveCross(evt, response)
+    {
+      var source = evt.source;
+      if (evt.domain != "localhost")
+        response += " unexpected-domain(" + evt.domain + ")";
+      if (evt.uri != "http://localhost:8888" + otherPath)
+        response += " unexpected-uri(" + evt.uri + ")";
+        
+      try
+      {
+        var threw = false;
+        var privateVariable = source.privateVariable;
+      }
+      catch (e)
+      {
+        threw = true;
+      }
+      if (!threw || privateVariable !== undefined)
+        response += " accessed-source!!!";
+
+      source.postMessage(response);
+    }
+
+    window.addEventListener("load", setup, false);
+    document.addEventListener("message", receiveMessage, false);
+  </script>
+</head>
+<body>
+<h1 id="domain"></h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_idn_helper.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>postMessage IDN test page</title>
+  <script type="application/javascript">
+    function receiveMessage(evt)
+    {
+      var response = "idn-response";
+
+      if (!(evt instanceof MessageEvent))
+        response += " not-a-MessageEvent";
+      if (evt.uri !== "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml")
+        response += " wrong-sender-uri(" + evt.uri + ")";
+      if (evt.domain !== "localhost")
+        response += " wrong-sender-domain(" + evt.domain + ")";
+      if (evt.data !== "idn-message")
+        response += " wrong-data(" + evt.data + ")";
+      if (evt.source !== window.parent)
+        response += " wrong-source";
+      if (evt.target !== document)
+        response += " wrong-target";
+      if (evt.type !== "message")
+        response += " wrong-type(" + evt.type + ")";
+
+      evt.source.postMessage(response);
+    }
+    document.addEventListener("message", receiveMessage, false);
+
+    function setup()
+    {
+      var target = document.getElementById("location");
+      target.textContent = document.domain;
+    }
+
+    window.addEventListener("load", setup, false);
+  </script>
+</head>
+<body>
+<h1 id="location">No location!</h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<!--
+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 data = evt.data;
+      if (data === "subframe-test-finished")
+      {
+        target = window.parent;
+        response = "test-passed";
+      }
+      else if (data === "start-test")
+      {
+        target = window.frames.innermost;
+        response = "start-test";
+      }
+      else
+      {
+        target = window.parent;
+        response = "not reached";
+      }
+
+      if (evt.type !== "message")
+        response += " wrong-type(" + evt.type + ")";
+
+      if (evt.target !== document)
+      {
+        response += " wrong-target(" + evt.target + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      if (isMozilla && evt.isTrusted === true)
+      {
+        response += " unexpected-trusted-event";
+      }
+
+      var uri, domain;
+      if (data == "subframe-test-finished")
+      {
+        uri = "http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html";
+        domain = "example.org";
+      }
+      else if (data === "start-test")
+      {
+        uri = "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_joined.html";
+        domain = "localhost";
+      }
+      else
+      {
+        uri = "unreached";
+        domain = "unreached";
+      }
+
+      if (evt.uri !== uri)
+      {
+        response += " wrong-uri(" + evt.uri + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      if (evt.domain !== domain)
+      {
+        response += " wrong-domain(" + evt.domain + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      target.postMessage(response);
+    }
+
+    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("load", setup, false);
+  </script>
+</head>
+<body>
+<p id="location">No location!</p>
+
+<iframe src="http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html"
+        name="innermost"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<!--
+http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
+-->
+<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.data !== "start-test")
+        response += " incorrect-subframe-data(" + evt.data + ")";
+      if (evt.type !== "message")
+        response += " wrong-type(" + evt.type + ")";
+      if (evt.target !== document)
+      {
+        response += " wrong-target(" + evt.target + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      if (isMozilla && evt.isTrusted)
+      {
+        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 + ")";
+      }
+
+      // verify that document.domain was actually joined with this domain
+      try
+      {
+        var passed = evt.source.document.domain === document.domain;
+      }
+      catch (e)
+      {
+      }
+
+      if (!passed)
+        response += " expected-joined-domains";
+
+      if (evt.uri !== "http://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html")
+      {
+        response += " wrong-uri(" + evt.uri + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      if (evt.domain !== "sub1.test1.example.org")
+      {
+        response += " wrong-domain(" + evt.domain + ")";
+        response += " location(" + window.location.href + ")";
+      }
+
+      window.parent.postMessage(response);
+    }
+    
+    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("load", setup, false);
+  </script>
+</head>
+<body>
+<p id="location">No location!</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_onOther.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>postMessage called through another frame</title>
+  <script type="application/javascript">
+    function receiveMessage(evt)
+    {
+      var response = "response-to-sibling-sent-message";
+
+      // 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.
+
+      if (evt.data !== "message-from-sibling")
+        response += " wrong-data(" + evt.data + ")";
+      if (evt.uri !== "http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html")
+        response += " failed-wrong-uri(" + evt.uri + ")";
+      if (evt.domain !== "localhost")
+        response += " failed-wrong-domain(" + evt.domain + ")";
+      if (evt.source !== window.parent.firstFrame)
+        response += " failed-wrong-source";
+
+      window.parent.postMessage(response);
+    }
+
+    function testSiblingPostMessage()
+    {
+      window.parent.secondFrame.postMessage("message-from-sibling");
+    }
+
+    function setup()
+    {
+      var target = document.getElementById("location");
+      target.textContent = document.domain;
+    }
+
+    document.addEventListener("message", receiveMessage, false);
+    window.addEventListener("load", setup, false);
+  </script>
+</head>
+<body>
+<h1 id="location">No location!</h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_override_helper.html
@@ -0,0 +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");
+    };
+
+    var count = 0;
+
+    function receiveMessage(evt)
+    {
+      count++;
+      if (count == 1)
+      {
+        window.dispatchEvent = function(evt)
+        {
+          window.parent.postMessage("FAIL");
+          throw "dispatchEvent threw";
+        };
+      }
+
+      window.parent.postMessage(evt.data);
+    }
+    
+    function setup()
+    {
+      var target = document.getElementById("location");
+      target.textContent = document.domain;
+    }
+
+    document.addEventListener("message", receiveMessage, false);
+
+    window.addEventListener("load", setup, false);
+  </script>
+</head>
+<body>
+<h1 id="location">No location!</h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_throw_helper.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>postMessage throwing page</title>
+  <script type="application/javascript">
+    function receiveMessage(evt)
+    {
+      throw 17;
+    }
+    document.addEventListener("message", receiveMessage, false);
+
+    function setup()
+    {
+      var target = document.getElementById("location");
+      target.textContent = document.domain;
+    }
+
+    window.addEventListener("load", setup, false);
+  </script>
+</head>
+<body>
+<h1 id="location">No location!</h1>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_MessageEvent.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>MessageEvent tests</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" />
+</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>
+
+<button id="target">target</button>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var data = "foobar";
+var domain = "cool.example.com";
+var uri = "http://cool.example.com/bar";
+var bubbles = true, cancelable = true;
+
+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");    
+    }
+  
+    evt.initMessageEvent("message", bubbles, cancelable, data, domain, uri, null);
+    ok(evt.source === null, "null source is fine for a MessageEvent");
+  
+    evt.initMessageEvent("message", bubbles, cancelable, data, domain, uri, window);
+  
+    is(evt.data, data, "unexpected data");
+    is(evt.domain, domain, "unexpected domain");
+    is(evt.uri, uri, "unexpected uri");
+  
+    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)
+  {
+    ok(false, "exception thrown: " + e);
+    return false;
+  }
+}
+
+function recvMsg(evt)
+{
+  is(evt.data, data, "unexpected data");
+  is(evt.domain, domain, "unexpected domain");
+  is(evt.uri, uri, "unexpected uri");
+
+  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)
+  {
+    is(Event.AT_TARGET, evt.eventPhase, "this listener was on the target");
+  }
+  else
+  {
+    is(evt.currentTarget, document, "should have gotten this at the window");
+    is(Event.BUBBLING_PHASE, evt.eventPhase, "wrong phase");
+  }
+
+  count++;
+}
+
+function setup()
+{
+  target = $("target");
+  target.addEventListener("message", recvMsg, false);
+  document.addEventListener("message", recvMsg, false);
+  var res = sendMsg();
+  ok(res === true, "nothing canceled this");
+  is(count, 2, "listener not called twice");
+  SimpleTest.finish();
+}
+
+addLoadEvent(setup);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_MessageEvent_dispatchToOther.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>Dispatching MessageEvent across origins</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <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://example.com/" name="otherDomain"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run()
+{
+  try
+  {
+    var msg = document.createEvent("MessageEvent");
+    msg.initMessageEvent("message", true, true,
+                         "foo", "evil.com", "http://evil.com/", window);
+
+    try
+    {
+      var ex;
+      window.frames.otherDomain.dispatchEvent(msg);
+      ok(false, "should have thrown a security exception per HTML5");
+    }
+    catch (e)
+    {
+      ok(true, "correctly threw an exception (security exception, but " +
+               "what that entails isn't yet defined in the spec)");
+    }
+  }
+  catch (e)
+  {
+    ok(false, "threw exception during execution: " + e);
+  }
+  finally
+  {
+    SimpleTest.finish();
+  }
+}
+
+addLoadEvent(run);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage.html
@@ -0,0 +1,203 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>Basic postMessage tests</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" />
+</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_helper.html"
+        name="otherSameDomain"></iframe>
+<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_helper.html"
+        name="otherCrossDomain"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var otherPath = "/tests/dom/tests/mochitest/whatwg/postMessage_helper.html";
+var path = "/tests/dom/tests/mochitest/whatwg/test_postMessage.html";
+
+var testsCompletedCount = 0;
+
+/** Variable for receivers to attempt to get. */
+window.privateVariable = 17;
+
+/** For sentinel finish, if necessary in deficient browsers */
+var finished = false;
+
+/** Receives MessageEvents to this window. */
+function messageReceiver(evt)
+{
+  try
+  {
+    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;
+  
+    // Check for the message we send to ourselves; it can't be
+    // counted as a test, and it's conceptually distinct from
+    // the other cases, so just return after handling it.
+    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);
+        break;
+  
+      default:
+        ok(false, "unexpected message: " + evt.data);
+        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();
+  }
+}
+
+
+/******************
+ * SELF-RESPONDER *
+ ******************/
+
+function respondToSelf(evt)
+{
+  is(evt.domain, "localhost", "what domain are we on again?");
+  is(evt.uri, "http://localhost:8888" + path, "event has wrong URI");
+  is(evt.source, window, "we posted this message!");
+  
+  evt.source.postMessage("post-to-self-response");
+}
+
+
+/*************
+ * RECEIVERS *
+ *************/
+
+function receiveSelf(evt)
+{
+  is(evt.domain, "localhost", "what domain are we on again?");
+  is(evt.uri, "http://localhost:8888" + path, "event has wrong URI");
+  is(evt.source, window, "we posted this message!");
+}
+
+function receiveOtherSameDomain(evt)
+{
+  is(evt.domain, "localhost", "we should be same domain");
+  is(evt.uri, "http://localhost:8888" + otherPath,
+     "same-domain response event has wrong URI");
+  is(evt.source, window.frames.otherSameDomain,
+     "wrong source for same-domain message!");
+}
+
+function receiveOtherCrossDomain(evt)
+{
+  is(evt.domain, "example.org", "we should be same domain");
+  is(evt.uri, "http://example.org:8000" + otherPath,
+     "same-domain response event has wrong URI");
+
+  // can't use |is| here, because ok tries to get properties on its arguments
+  // for creating a formatted logging message
+  ok(evt.source === window.frames.otherCrossDomain,
+     "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 dispatcher()
+  {
+    try
+    {
+      destination.postMessage(message);
+    }
+    catch (e)
+    {
+      ok(false, "error while calling postMessage: " + e);
+    }
+  }
+
+  return dispatcher;
+}
+
+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]);
+
+/**
+ * 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>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage chrome tests</title>
+  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<!--
+This test runs at the following URL:
+chrome://mochikit/content/chrome/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
+-->
+
+<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://example.org/tests/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html"
+        name="contentDomain"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var pathHead = "chrome://mochikit/content/chrome";
+var path = "/dom/tests/mochitest/whatwg/test_postMessage_chrome.html";
+var otherPath = "/tests/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html";
+
+var testsCompletedCount = 0;
+
+/** 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'");
+
+  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);
+      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.domain, "mochikit", "chrome: protocol's domain is the package");
+  is(evt.uri, pathHead + path, "event has wrong URI");
+  is(evt.source, null, "chrome posters get a null source, for security");
+}
+
+
+/*************
+ * RECEIVERS *
+ *************/
+
+function receiveContent(evt)
+{
+  is(evt.isTrusted, true, "should have sent a trusted event");
+  is(evt.domain, "example.org", "wrong domain for content page");
+  is(evt.uri, "http://example.org" + otherPath,
+     "content response event has wrong URI");
+  is(evt.source, window.frames.contentDomain,
+     "wrong source for same-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)
+{
+  return function() { destination.postMessage(message); };
+}
+
+var allTests =
+  [
+    createMessageDispatcher("post-to-self", window),
+    createMessageDispatcher("post-to-content",
+                            window.frames.contentDomain),
+  ];
+  
+allTests.forEach(addLoadEvent);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage uri/domain values and IDN encoding</title>
+  <script type="text/javascript" src="/MochiKit/packed.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" />
+</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://sub1.ält.example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_idn_helper.html"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var responseReceived = false;
+var idnWindow = null;
+
+function receiveMessage(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");
+  }
+
+  is(evt.uri, "http://sub1.ält.example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_idn_helper.html",
+     "wrong URI -- IDN issue, perhaps?");
+  is(evt.domain, "sub1.ält.example.org",
+     "wrong domain -- IDN issue, perhaps?");
+
+  is(evt.data, "idn-response", "unexpected test result");
+  ok(evt.source === idnWindow, "wrong source");
+
+  responseReceived = true;
+}
+document.addEventListener("message", receiveMessage, false);
+
+var xhtmlns = "http://www.w3.org/1999/xhtml";
+
+function setup()
+{
+  try
+  {
+    var idnFrame = document.getElementsByTagNameNS(xhtmlns, "iframe")[0];
+    idnWindow = idnFrame.contentWindow;
+    idnWindow.postMessage("idn-message");
+
+    ok(responseReceived, "should have gotten a response before returning");
+  }
+  catch (e)
+  {
+    ok(false, "failed to post message: " + e);
+  }
+
+  SimpleTest.finish();
+}
+
+addLoadEvent(setup);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_joined.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage with document.domain setting to join origins</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" />
+</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://sub1.test1.example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper.html"
+        name="container"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+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;
+  
+  // 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");
+  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();
+  }
+}
+
+addLoadEvent(setup);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage called through a different same-origin page</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <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>
+
+
+<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)
+{
+  var fromURI = "http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_onOther.html";
+  ok(evt instanceof MessageEvent, "wrong event type");
+  is(evt.uri, fromURI, "unexpected URI");
+  is(evt.domain, "example.org", "unexpected domain");
+  is(evt.data, "response-to-sibling-sent-message",
+     "unexpected data in message");
+
+  // Handle buggy browsers that might somehow have received a message twice
+  if (finished)
+    return;
+
+  finished = true;
+  SimpleTest.finish();
+}
+
+function postToSecondFrameThroughFirstFrame()
+{
+  try
+  {
+    window.frames.firstFrame.testSiblingPostMessage();
+  }
+  catch (e)
+  {
+    ok(false, "threw exception trying to post through firstFrame: " + e);
+  }
+}
+
+/** 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);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_override.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage override test</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <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://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_override_helper.html"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var count = 0;
+
+function test()
+{
+  try
+  {
+    count++;
+    switch (count)
+    {
+      case 1:
+        window.frames[0].postMessage("PASS 1");
+        break;
+
+      case 2:
+        window.frames[0].postMessage("PASS 2");
+        break;
+  
+      default:
+        ok(false, "unexpected");
+    }
+  }
+  catch (e)
+  {
+    ok(false, "error running test " + count + ": " + e);
+  }
+}
+
+function messageReceiver(evt)
+{
+  var expect;
+
+  switch (count)
+  {
+    case 1:
+      expect = "PASS 1";
+      break;
+
+    case 2:
+      expect = "PASS 2";
+      break;
+
+    default:
+      expect = "unexpected";
+      break;
+  }
+
+  is(evt.data, expect, "uh-oh, we didn't get the right postMessage!");
+}
+
+document.addEventListener("message", messageReceiver, false);
+
+addLoadEvent(test);
+addLoadEvent(test);
+addLoadEvent(SimpleTest.finish);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
@@ -0,0 +1,397 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage from about:blank, data URLs</title>
+  <script type="text/javascript" src="/MochiKit/packed.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" />
+</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>
+
+<pre id="test">
+<script class="testbody" type="application/javascript"><![CDATA[
+/** Test for Bug 387706 **/
+
+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) {
+  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)
+      return null; // Less than three bytes remaining
+
+    // Return the next three bytes in the array, and increment index for our
+    // next invocation
+    return byteArray.slice(index, index += 3);
+  }
+
+  var out = "";
+  var bytes = null;
+  while ((bytes = get3Bytes()))
+  {
+    var bits = 0;
+    for (var i = 0; i < 3; i++)
+      bits = (bits << 8) | bytes[i];
+    for (var j = 18; j >= 0; j -= 6)
+      out += B64_CHARS[(bits>>j) & 0x3F];
+  }
+
+  // Get the remaining bytes
+  bytes = byteArray.slice(index);
+
+  switch (bytes.length)
+  {
+    case 2:
+      out += B64_CHARS[(bytes[0]>>2) & 0x3F] +
+             B64_CHARS[((bytes[0] & 0x03) << 4) | ((bytes[1] >> 4) & 0x0F)] +
+             B64_CHARS[((bytes[1] & 0x0F) << 2)] +
+             "=";
+      break;
+    case 1:
+      out += B64_CHARS[(bytes[0]>>2) & 0x3F] +
+             B64_CHARS[(bytes[0] & 0x03) << 4] +
+             "==";
+      break;
+  }
+
+  return out;
+}
+
+
+var aboutBlankWindow = null;
+var aboutBlank2Window = null;
+var dataWindow = null;
+
+var aboutBlankResponseReceived = false;
+var aboutBlank2ResponseReceived = false;
+var dataResponseReceived = false;
+
+var finished = false;
+
+/** Convert a nullable string to a pretty representation */
+function sourceify(v)
+{
+  if (typeof v == "string")
+    return "'" + v + "'";
+  return String(v);
+}
+
+/** Receives MessageEvents to this window. */
+function messageReceiver(evt)
+{
+  // It's not clear what the security model is for data: URLs and whether they
+  // can access their parents; WebKit denies access, while Gecko currently
+  // allows it.  We work around this problem by using postMessage (surprise!)
+  // to start the round of tests when each iframe loads.
+  if (evt.data === "next-test")
+  {
+    setTimeout(nextTest, 0);
+    return;
+  }
+
+
+  try
+  {
+    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");
+    }
+  
+    if (evt.data === "about:blank-response")
+    {
+      // This isn't clarified in HTML5 yet, but the origin for a document which
+      // has been open()ed is the origin of the calling code, somewhat loosely
+      // 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.uri, "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml",
+         "wrong uri for event from about:blank");
+      ok(evt.domain === "localhost",
+         "wrong domain for event from about:blank; " +
+         "got " + sourceify(evt.domain) + ", " +
+         "expected 'localhost'");
+      is(evt.source, aboutBlankWindow, "wrong source");
+      aboutBlankResponseReceived = true;
+    }
+    else if (evt.data === "about:blank2-response")
+    {
+      is(evt.uri, "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml",
+         "wrong uri for event from about:blank #2");
+      ok(evt.domain === "localhost",
+         "wrong domain for event from about:blank; " +
+         "got " + sourceify(evt.domain) + ", expected 'localhost'");
+      is(evt.source, aboutBlank2Window, "wrong source");
+      aboutBlank2ResponseReceived = true;
+    }
+    else if (evt.data === "data-response")
+    {
+      // Again, HTML5 hasn't yet defined this, but we're going to do the same
+      // thing as with about:blank -- for a data: URL opened from page A, the
+      // uri and domain properties correspond to those of page A.  This happens
+      // to fall out naturally from using the window's security principal to
+      // determine these two properties.
+      //
+      // Mozilla currently gives data: URLs the principal of the opener/parent
+      // window, and at least for now we'll test for that behavior.  If we ever
+      // change how data: URLs are given principals, we can update this test
+      // then.
+      if (isMozilla)
+      {
+        is(evt.uri, "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml",
+           "wrong uri for event from data URL (but note that this URI is " +
+           "the result of Mozilla's current policy that data: URLs inherit " +
+           "the principal of their opener/parent, a policy not currently " +
+           "specified by any standards)");
+        ok(evt.domain === "localhost",
+           "wrong domain for event from data URL; " +
+           "got " + sourceify(evt.domain) + ", expected ''");
+      }
+
+      is(evt.source, dataWindow, "wrong source");
+      dataResponseReceived = true;
+    }
+    else
+    {
+      ok(false, "unexpected message: " + evt.data);
+    }
+  }
+  catch (e)
+  {
+    ok(false, "error processing event with data '" + evt.data + "': " + e);
+  }
+}
+
+function getContents(description, responseText)
+{
+  var contents =
+    "<!DOCTYPE html>\n" +
+    "<html>\n" +
+    "<head>\n" + 
+    "  <title>about:blank</title>\n" +
+    "  <script type='application/javascript'>\n" +
+    "function receive(evt)\n" +
+    "{\n" +
+    "  var response = '" + responseText + "';\n" +
+    "\n" +
+    "  if (evt.source !== window.parent)\n" +
+    "    response += ' wrong-source';\n" +
+    "  if (evt.uri !== 'http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_special.xhtml')\n" +
+    "    response += ' wrong-uri(' + evt.uri + ')';\n" +
+    "  if (evt.domain !== 'localhost')\n" +
+    "    response += ' wrong-domain(' + evt.domain + ')';\n" +
+    "  if (evt.data !== 'from-opener')\n" +
+    "    response += ' wrong-data(' + evt.data + ')';\n" +
+    "\n" +
+    "  window.parent.postMessage(response);\n" +
+    "}\n" +
+    "\n" +
+    "function ready()\n" +
+    "{\n" +
+    "  window.parent.postMessage('next-test');\n" +
+    "}\n" +
+    "\n" +
+    "window.addEventListener('load', ready, false);\n" +
+    "document.addEventListener('message', receive, false);\n" +
+    "  </script>\n" +
+    "</head>\n" +
+    "<body><p>" + description + "</p></body>\n" +
+    "</html>";
+
+  return contents;
+}
+
+var xhtmlns = "http://www.w3.org/1999/xhtml";
+
+function insert(el)
+{
+  var content = $("content");
+  content.parentNode.insertBefore(el, content);
+}
+
+var LOAD_TIMEOUT = 5000;
+
+function setupBlank()
+{
+  var aboutBlankFrame = document.createElementNS(xhtmlns, "iframe");
+  aboutBlankFrame.setAttribute("src", "about:blank");
+  insert(aboutBlankFrame);
+
+  aboutBlankWindow = aboutBlankFrame.contentWindow;
+  var doc = aboutBlankWindow.document;
+  doc.open();
+  doc.write(getContents("This was about:blank #1", "about:blank-response"));
+  doc.close();
+
+  // I don't believe anything guarantees sync parsing, so we have to wait for
+  // the new window to poke us to actually do the test.  :-\
+
+  // Catch recalcitrant browsers that fail inside the iframe document code.
+  setTimeout(blankFailed, LOAD_TIMEOUT);
+}
+
+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();
+  }
+}
+
+function setupBlank2()
+{
+  var aboutBlank2Frame = document.createElementNS(xhtmlns, "iframe");
+  aboutBlank2Frame.addEventListener("load", nextTest, false);
+  aboutBlank2Frame.setAttribute("src", "about:blank");
+
+  insert(aboutBlank2Frame);
+}
+
+// Could use window.btoa here, but that's not standardized, and we want to be
+// able to run these tests against browsers that don't support it.
+var dataURI = "data:text/html;base64," +
+              b64(getContents("A data: URL", "data-response"));
+
+function setupData()
+{
+  var dataFrame = document.createElementNS(xhtmlns, "iframe");
+  dataFrame.setAttribute("src", dataURI);
+  insert(dataFrame);
+
+  dataWindow = dataFrame.contentWindow;
+
+  // ...and wait again for the window to load...
+
+  // Catch recalcitrant browsers that fail inside the iframe document code.
+  setTimeout(dataFailed, LOAD_TIMEOUT);
+}
+
+function dataFailed()
+{
+  if (!dataResponseReceived && !finished)
+  {
+    ok(false,
+       "test timed out (postMessage not accessible on window.parent in " +
+       "the data: iframe?)");
+    finished = true;
+    SimpleTest.finish();
+  }
+}
+
+var count = 0;
+function nextTest()
+{
+  switch (count++)
+  {
+    case 0:
+      testBlank();
+      break;
+
+    case 1:
+      testBlank2();
+      break;
+
+    case 2:
+      testData();
+      break;
+
+    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);
+}
+
+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');";
+  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();
+}
+
+document.addEventListener("message", messageReceiver, false);
+
+addLoadEvent(setupBlank);
+]]></script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_throw.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
+-->
+<head>
+  <title>postMessage with a thrown exception</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <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_throw_helper.html"
+        name="sameDomain"></iframe>
+<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_throw_helper.html"
+        name="crossDomain"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 387706 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function atLoad()
+{
+  try
+  {
+    sameDomain.postMessage("foo");
+    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);
+}
+
+function next()
+{
+  ok(true, "no pending-exception wackiness for same-domain");
+  setTimeout(next2, 0);
+}
+
+function next2()
+{
+  try
+  {
+    crossDomain.postMessage("foo");
+    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);
+}
+
+function next3()
+{
+  ok(true, "no pending-exception wackiness for cross-domain");
+  SimpleTest.finish();
+}
+
+addLoadEvent(atLoad);
+</script>
+</pre>
+</body>
+</html>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -304,16 +304,17 @@ pref("capability.policy.default.Window.c
 pref("capability.policy.default.Window.closed.get", "allAccess");
 pref("capability.policy.default.Window.focus.get", "allAccess");
 pref("capability.policy.default.Window.frames.get", "allAccess");
 pref("capability.policy.default.Window.history.get", "allAccess");
 pref("capability.policy.default.Window.length.get", "allAccess");
 pref("capability.policy.default.Window.location", "allAccess");
 pref("capability.policy.default.Window.opener.get", "allAccess");
 pref("capability.policy.default.Window.parent.get", "allAccess");
+pref("capability.policy.default.Window.postMessage.get", "allAccess");
 pref("capability.policy.default.Window.self.get", "allAccess");
 pref("capability.policy.default.Window.top.get", "allAccess");
 pref("capability.policy.default.Window.window.get", "allAccess");
 
 pref("capability.policy.default.Selection.addSelectionListener", "UniversalXPConnect");
 pref("capability.policy.default.Selection.removeSelectionListener", "UniversalXPConnect");
 
 // Restrictions on the DOM for mail/news - see bugs 66938 and 84545
--- a/testing/mochitest/harness.xul
+++ b/testing/mochitest/harness.xul
@@ -40,18 +40,18 @@
     /** generate our test list **/
     srvScope.makeTags();
     var [links, count] = srvScope.list("chrome://mochikit/content/chrome/",
                                        chromeDir, true);
     var listContent = srvScope.linksToListItems(links);
     var tableContent = srvScope.linksToTableRows(links);
     function populate() {
       $("list-holder").setAttribute("rowspan", 1 + count);
-      $("test-list").innerHTML += listContent.toLowerCase();
-      $("test-table").innerHTML += tableContent.toLowerCase();
+      $("test-list").innerHTML += listContent;
+      $("test-table").innerHTML += tableContent;
       $("wrapper").innerHTML += " "; // redraw the table
     }
     gTestList = eval(srvScope.jsonArrayOfTestFiles(links));
     connect(window, 'onload', populate);
     connect(window, 'onload', hookup);
   ]]>
   </script>
   <vbox>   
--- a/testing/mochitest/runtests.pl.in
+++ b/testing/mochitest/runtests.pl.in
@@ -100,17 +100,18 @@ use constant CHROMETESTS_URL => "http://
 use constant SERVER_STARTUP_TIMEOUT => 45;
 
 
  # Since some tests require cross-domain support in Mochitest, across ports,
  # domains, subdomains, etc. we use a proxy autoconfig hack to map a bunch of
  # servers onto localhost:8888.  We have to grant them the same privileges as
  # localhost:8888 here, since the browser only knows them as the URLs they're
  # pretending to be.
-my @servers = ("localhost:8888", # MUST be first -- see PAC pref-setting code
+my @servers = (
+               "localhost:8888", # MUST be first -- see PAC pref-setting code
                "example.org:80",
                "test1.example.org:80",
                "test2.example.org:80",
                "sub1.test1.example.org:80",
                "sub1.test2.example.org:80",
                "sub2.test1.example.org:80",
                "sub2.test2.example.org:80",
                "example.org:8000",
@@ -123,17 +124,20 @@ my @servers = ("localhost:8888", # MUST 
                "example.com:80",
                "test1.example.com:80",
                "test2.example.com:80",
                "sub1.test1.example.com:80",
                "sub1.test2.example.com:80",
                "sub2.test1.example.com:80",
                "sub2.test2.example.com:80",
                "sectest1.example.org:80",
-               "sub.sectest2.example.org:80");
+               "sub.sectest2.example.org:80",
+               "sub1.xn--lt-uia.example.org:8000", # U+00E4 U+006C U+0074
+               "sub2.xn--lt-uia.example.org:80",   # U+00E4 U+006C U+0074
+              );
 
 
 my $profile = "mochitesttestingprofile";
 my $profile_dir = "$FindBin::Bin/$profile";
 
  # These are generated in mozilla/testing/mochitest/Makefile.in
 #expand my $app = "$FindBin::Bin/" . __BROWSER_PATH__;
 #expand my $dist_bin = "$FindBin::Bin/" . __XPC_BIN_PATH__;