Bug 417075 - Update postMessage and MessageEvent to reflect domain/uri being replaced by origin, optional origin argument. r+sr=sicking, a=beltzner
authorjwalden@mit.edu
Tue, 26 Feb 2008 19:48:54 -0800
changeset 12322 293a55a0a16d303d004f79cd1c41e33c39d12e73
parent 12321 34618990870afed07830b2845885ba4924c410fd
child 12323 694e47d9f54dc98345888a4058cc8ccb48c2942c
push idunknown
push userunknown
push dateunknown
reviewersbeltzner
bugs417075
milestone1.9b4pre
Bug 417075 - Update postMessage and MessageEvent to reflect domain/uri being replaced by origin, optional origin argument. r+sr=sicking, a=beltzner
build/pgo/automation.py.in
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/tests/mochitest/bugs/test_bug409349.html
dom/tests/mochitest/dom-level0/idn_child.html
dom/tests/mochitest/dom-level0/test_crossdomainprops.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_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_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_onOther.html
dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
dom/tests/mochitest/whatwg/test_postMessage_special.xhtml
dom/tests/mochitest/whatwg/test_postMessage_userpass.html
testing/mochitest/runtests.pl.in
--- a/build/pgo/automation.py.in
+++ b/build/pgo/automation.py.in
@@ -211,17 +211,17 @@ user_pref("capability.principal.codebase
 
   # Now actually create the preference to make the proxying happen.
   quotedServers = ", ".join(map(lambda x: "'" + x + "'", allServers))
 
   pacURL = """data:text/plain,
 function FindProxyForURL(url, host)
 {
   var servers = [%(quotedServers)s];
-  var regex = new RegExp('http://(.*?(:\\\\\\\\d+)?)/');
+  var regex = new RegExp('http://(?:[^/@]*@)?(.*?(:\\\\\\\\d+)?)/');
   var matches = regex.exec(url);
   if (!matches)
     return 'DIRECT';
   var hostport = matches[1], port = matches[2];
   if (!port)
     hostport += ':80';
   if (servers.indexOf(hostport) >= 0)
     return 'PROXY localhost:8888';
@@ -267,9 +267,9 @@ def runApp(testURL, env, app, profileDir
 
   args.extend(("-no-remote", "-profile", profileDirectory, testURL))
   proc = Process(cmd, args, env = env)
   print "Application pid: " + str(proc.pid)
   status = proc.wait()
   if status != 0:
     print "FAIL Exited with code " + str(status) + " during test run"
 
-  return start
\ No newline at end of file
+  return start
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -60,64 +60,54 @@ NS_IMPL_RELEASE_INHERITED(nsDOMMessageEv
 NS_IMETHODIMP
 nsDOMMessageEvent::GetData(nsAString& aData)
 {
   aData = mData;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMMessageEvent::GetDomain(nsAString& aDomain)
+nsDOMMessageEvent::GetOrigin(nsAString& aOrigin)
 {
-  aDomain = mDomain;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMMessageEvent::GetUri(nsAString& aURI)
-{
-  aURI = mURI;
+  aOrigin = mOrigin;
   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,
+                                    const nsAString& aOrigin,
                                     nsIDOMWindow* aSource)
 {
   nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mData = aData;
-  mDomain = aDomain;
-  mURI = aURI;
+  mOrigin = aOrigin;
   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,
+                                      const nsAString& aOrigin,
                                       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
@@ -63,14 +63,13 @@ public:
 
   NS_DECL_NSIDOMMESSAGEEVENT
 
   // Forward to base class
   NS_FORWARD_TO_NSDOMEVENT
 
 private:
   nsString mData;
-  nsString mDomain;
-  nsString mURI;
+  nsString mOrigin;
   nsCOMPtr<nsIDOMWindow> mSource;
 };
 
 #endif // nsDOMMessageEvent_h__
--- a/dom/public/idl/base/nsIDOMWindowInternal.idl
+++ b/dom/public/idl/base/nsIDOMWindowInternal.idl
@@ -209,10 +209,11 @@ interface nsIDOMWindowInternal : nsIDOMW
    * 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.
    */
-  [binaryname(PostMessageMoz)] void postMessage(in DOMString message);
+  [binaryname(PostMessageMoz)] void postMessage(in DOMString message,
+                                                [optional] in DOMString origin);
 };
--- a/dom/public/idl/events/nsIDOMMessageEvent.idl
+++ b/dom/public/idl/events/nsIDOMMessageEvent.idl
@@ -40,58 +40,54 @@
 
 /**
  * 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)]
+[scriptable, uuid(ca081997-91f9-40c1-890c-3edf39b6c571)]
 interface nsIDOMMessageEvent : nsIDOMEvent
 {
   /**
    * Custom string data associated with this event.
    */
   readonly attribute DOMString data;
   
   /**
-   * The domain of the site from which this event originated.
+   * 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 domain;
-  
-  /**
-   * The URI of the site from which this event was created.
-   */
-  readonly attribute DOMString uri;
+  readonly attribute DOMString origin;
   
   /**
    * 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.
+   * data, origin, 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 DOMString aOrigin,
                         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.
+   * origin, 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 DOMString aOrigin,
                           in nsIDOMWindow aSource);
 };
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -5101,98 +5101,133 @@ nsGlobalWindow::CallerInnerWindow()
   // destructor's release.
   nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
   if (!win)
     return GetCurrentInnerWindowInternal();
   return static_cast<nsGlobalWindow*>(win.get());
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::PostMessageMoz(const nsAString& aMessage)
-{
-  FORWARD_TO_INNER_CREATE(PostMessageMoz, (aMessage));
+nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrigin)
+{
+  FORWARD_TO_INNER_CREATE(PostMessageMoz, (aMessage, aOrigin));
 
   //
   // 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.
+  // 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.
   nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal();
   if (!callerPrin)
     return NS_OK;
-  nsCOMPtr<nsIURI> docURI;
-  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(docURI))))
+  nsCOMPtr<nsIURI> callerURI;
+  if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerURI))))
     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) {
+  if (!callerURI) {
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(callerInnerWin->mDocument);
     if (!doc)
       return NS_OK;
-
-    docURI = doc->GetDocumentURI();
-    if (!docURI)
+    callerURI = doc->GetDocumentURI();
+    if (!callerURI)
       return NS_OK;
   }
-
-  nsCAutoString domain, uri;
-  nsresult rv  = docURI->GetSpec(uri);
-  if (NS_FAILED(rv))
+  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;
 
-  // 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();
+  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;
+    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<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;
+    return NS_OK;
   
+  nsCAutoString origin;
+  if (NS_FAILED(callerOrigin->GetPrePath(origin)))
+    return NS_OK;
+
   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());
+  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 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;
-  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;
--- a/dom/tests/mochitest/bugs/test_bug409349.html
+++ b/dom/tests/mochitest/bugs/test_bug409349.html
@@ -16,17 +16,17 @@
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
-  is(evt.domain, "127.0.0.1", "wrong sender");
+  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);
--- a/dom/tests/mochitest/dom-level0/idn_child.html
+++ b/dom/tests/mochitest/dom-level0/idn_child.html
@@ -7,17 +7,17 @@
 function run()
 {
   var target = document.getElementById("location");
   target.textContent = location.hostname + ":" + (location.port || 80);
 }
 
 function receiveMessage(evt)
 {
-  if (evt.domain !== "localhost")
+  if (evt.origin !== "http://localhost:8888")
     return;
 
   var message = evt.data + "-response";
 
   var domain = document.domain;
   if (/test$/.test(domain))
   {
     // XXX should really be IDN (bug 414090)
--- a/dom/tests/mochitest/dom-level0/test_crossdomainprops.html
+++ b/dom/tests/mochitest/dom-level0/test_crossdomainprops.html
@@ -22,16 +22,16 @@ function runTest() {
   ok(frames.frame1.myVar == 10, "access same domain inner window variable");
   var otherDomainVar = null;
   try {
     otherDomainVar = frames.frame2.myVar;
   }
   catch (e) {
     otherDomainVar = -1;
   }
-  ok(otherDomainVar == -1, "access other domain inner window variable");
+  is(otherDomainVar, -1, "access other domain inner window variable");
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </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
@@ -66,54 +66,57 @@ var gotPunycodeWhitelist = false;
 var whitelistRegex =
   new RegExp("^http://sub1\\.παράδειγμα\\.δοκιμή/tests/dom/tests/" +
              "mochitest/dom-level0/idn_child\\.html\\?(.+)$");
 
 var noWhitelistRegex =
   new RegExp("^http://sub1\\.exämple\\.test/tests/dom/tests/" +
              "mochitest/dom-level0/idn_child\\.html\\?(.+)$");
 
+var state = 0;
+
+var messages =
+  [
+   "idn-whitelist",
+   "punycode-whitelist",
+   "idn-nowhitelist",
+   "punycode-nowhitelist",
+  ];
+
+
 function receiveMessage(evt)
 {
-  var domain = evt.domain;
+  var origin = evt.origin;
   var match;
-  if (/test$/.test(domain))
+  if (/test$/.test(origin))
   {
     // XXX bug 414090
     // The value of MessageEvent.domain 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).  :-(
     //
     // These two tests should illustrate what currently happens and what should
     // happen once bug 414090 is fixed.
-    todo_is(evt.domain, "sub1.exämple.test", "wrong sender");
-    todo_isnot(evt.domain, "sub1.xn--exmple-cua.test", "wrong sender");
-
-    match = noWhitelistRegex.exec(evt.uri);
-    todo(match, "unexpected sender");
-    if (!match)
-      match = [, evt.uri.substring(evt.uri.indexOf("?") + 1)];
+    todo_is(evt.origin, "http://sub1.exämple.test", "wrong sender");
+    todo_isnot(evt.origin, "http://sub1.xn--exmple-cua.test", "wrong sender");
   }
   else
   {
     // We're receiving data from the Greek IDN name; since that TLD is
     // whitelisted for now, the domain we get isn't going to be punycoded.
-    is(evt.domain, "sub1.παράδειγμα.δοκιμή", "wrong sender");
-
-    match = whitelistRegex.exec(evt.uri);
-    ok(match, "should have matched, unexpected sender");
+    is(evt.origin, "http://sub1.παράδειγμα.δοκιμή", "wrong sender");
   }
 
-  is(match[1] + "-response", evt.data.split(" ")[0],
+  is(messages[state] + "-response", evt.data.split(" ")[0],
      "unexpected data: " + evt.data);
 
-  switch (match[1])
+  switch (messages[state])
   {
     case "idn-whitelist":
       gotIDNWhitelist = true;
       ok(evt.source === window.frames.idnKidWhitelist, "wrong source");
       is(evt.data, "idn-whitelist-response", "wrong response for IDN");
       break;
 
     case "punycode-whitelist":
@@ -133,16 +136,18 @@ function receiveMessage(evt)
       ok(evt.source === window.frames.punycodeKidNoWhitelist, "wrong source");
       is(evt.data, "punycode-nowhitelist-response", "wrong response for punycode");
       break;
 
     default:
       ok(false, "unreached");
       break;
   }
+
+  state++;
 }
 
 function run()
 {
   window.frames.idnKidWhitelist.postMessage("idn-whitelist");
   ok(gotIDNWhitelist, "IDN whitelist message not received");
 
   window.frames.punycodeKidWhitelist.postMessage("punycode-whitelist");
--- 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
@@ -16,17 +16,17 @@
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
-  is(evt.domain, "127.0.0.1", "wrong sender");
+  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);
--- a/dom/tests/mochitest/whatwg/Makefile.in
+++ b/dom/tests/mochitest/whatwg/Makefile.in
@@ -62,16 +62,22 @@ include $(topsrcdir)/config/rules.mk
 		postMessage_throw_helper.html \
 		postMessage_chrome_helper.html \
 		test_postMessage_special.xhtml \
 		test_postMessage_idn.xhtml \
 		postMessage_idn_helper.html \
 		test_postMessage_basehref.html \
 		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 \
 		$(NULL)
 
 _CHROME_FILES	= \
 		test_postMessage_chrome.html \
 		$(NULL)		
 
 libs:: 	$(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_chrome_helper.html
@@ -1,33 +1,28 @@
 <!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.origin !== "chrome://mochikit")
+        msg += " wrong-origin(" + evt.origin + ")";
       if (evt.data !== "post-to-content")
         msg += " wrong-message(" + evt.data + ")";
 
       respond(msg);
     }
     
     function respond(msg)
     {
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_closed_helper.html
@@ -0,0 +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");
+}
+
+document.addEventListener("message", receiveMessage, false);
+
+function setup()
+{
+  var query = location.search.substring(1);
+
+  if (query == "opener")
+    window.opener.postMessage("message");
+}
+
+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
@@ -5,20 +5,18 @@
   <script type="application/javascript">
 function receiveMessage(evt)
 {
   var response = "response-message";
 
   if (window.location.href !== "http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_hash.html#hash")
     response += " kid-at-wrong-uri(" + window.location.href + ")";
 
-  if (evt.domain !== "localhost")
-    response += " wrong-domain(" + evt.domain + ")";
-  if (evt.uri !== "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_hash.html")
-    response += " wrong-uri(" + evt.uri + ")";
+  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 + ")";
 
   window.parent.postMessage(response);
 }
 
--- a/dom/tests/mochitest/whatwg/postMessage_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_helper.html
@@ -6,18 +6,16 @@
   <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 + ")";
@@ -47,20 +45,18 @@
       }
     }
 
     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 + ")";
+        if (evt.origin != "http://localhost:8888")
+          response += " unexpected-origin(" + evt.origin + ")";
           
         try
         {
           var threw = false;
           var privateVariable = source.privateVariable;
         }
         catch (e)
         {
@@ -74,20 +70,18 @@
       {
         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 + ")";
+      if (evt.origin != "http://localhost:8888")
+        response += " unexpected-origin(" + evt.origin + ")";
         
       try
       {
         var threw = false;
         var privateVariable = source.privateVariable;
       }
       catch (e)
       {
--- a/dom/tests/mochitest/whatwg/postMessage_idn_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_idn_helper.html
@@ -4,20 +4,18 @@
   <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.origin !== "http://localhost:8888")
+        response += " wrong-sender-origin(" + evt.origin + ")";
       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 + ")";
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper.html
@@ -36,42 +36,27 @@ http://sub1.test1.example.org/tests/dom/
         response += " location(" + window.location.href + ")";
       }
 
       if (isMozilla && evt.isTrusted === true)
       {
         response += " unexpected-trusted-event";
       }
 
-      var uri, domain;
+      var origin;
       if (data == "subframe-test-finished")
-      {
-        uri = "http://example.org/tests/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html";
-        domain = "example.org";
-      }
+        origin = "http://example.org";
       else if (data === "start-test")
-      {
-        uri = "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_joined.html";
-        domain = "localhost";
-      }
+        origin = "http://localhost:8888";
       else
+        origin = "unreached";
+
+      if (evt.origin !== origin)
       {
-        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 += " wrong-origin(" + evt.origin + ")";
         response += " location(" + window.location.href + ")";
       }
 
       target.postMessage(response);
     }
 
     function setup()
     {
--- a/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
+++ b/dom/tests/mochitest/whatwg/postMessage_joined_helper2.html
@@ -40,25 +40,19 @@ http://example.org/tests/dom/tests/mochi
       }
       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")
+      if (evt.origin !== "http://sub1.test1.example.org")
       {
-        response += " wrong-uri(" + evt.uri + ")";
-        response += " location(" + window.location.href + ")";
-      }
-
-      if (evt.domain !== "sub1.test1.example.org")
-      {
-        response += " wrong-domain(" + evt.domain + ")";
+        response += " wrong-origin(" + evt.origin + ")";
         response += " location(" + window.location.href + ")";
       }
 
       window.parent.postMessage(response);
     }
     
     function setup()
     {
--- a/dom/tests/mochitest/whatwg/postMessage_onOther.html
+++ b/dom/tests/mochitest/whatwg/postMessage_onOther.html
@@ -18,20 +18,18 @@
       // 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.origin !== "http://localhost:8888")
+        response += " failed-wrong-origin(" + evt.origin + ")";
       if (evt.source !== window.parent.firstFrame)
         response += " failed-wrong-source";
 
       window.parent.postMessage(response);
     }
 
     function testSiblingPostMessage()
     {
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>postMessage throwing 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);
+}
+
+document.addEventListener("message", receiveMessage, false);
+
+
+// Aids for identifying origins
+
+function setup()
+{
+  var target = document.getElementById("location");
+  target.textContent = document.domain + ":" + (location.port || 80);
+}
+
+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_userpass_helper.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Username/password page for postMessage tests</title>
+  <script type="application/javascript">
+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.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);
+}
+
+document.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
@@ -21,18 +21,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <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 origin = "http://cool.example.com";
 var bubbles = true, cancelable = true;
 
 var target;
 
 var count = 0;
 
 function sendMsg()
 {
@@ -42,24 +41,23 @@ function sendMsg()
     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);
+    evt.initMessageEvent("message", bubbles, cancelable, data, origin, null);
     ok(evt.source === null, "null source is fine for a MessageEvent");
   
-    evt.initMessageEvent("message", bubbles, cancelable, data, domain, uri, window);
+    evt.initMessageEvent("message", bubbles, cancelable, data, origin, window);
   
     is(evt.data, data, "unexpected data");
-    is(evt.domain, domain, "unexpected domain");
-    is(evt.uri, uri, "unexpected uri");
+    is(evt.origin, origin, "unexpected origin");
   
     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)
@@ -67,18 +65,17 @@ function sendMsg()
     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.origin, origin, "unexpected origin");
 
   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,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function run()
 {
   try
   {
     var msg = document.createEvent("MessageEvent");
     msg.initMessageEvent("message", true, true,
-                         "foo", "evil.com", "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,19 +22,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 
 <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;
 
@@ -98,49 +95,45 @@ function messageReceiver(evt)
 
 
 /******************
  * 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.origin, "http://localhost:8888", "event has wrong origin");
   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.origin, "http://localhost:8888", "event has wrong origin");
   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.origin, "http://localhost:8888",
+     "same-domain response event has wrong origin");
   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");
+  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
   // for creating a formatted logging message
   ok(evt.source === window.frames.otherCrossDomain,
      "wrong source for cross-domain message!");
 }
 
 
--- a/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_basehref.html
@@ -19,19 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="application/javascript">
 /** Test for Bug 414815 **/
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
-  is(evt.domain, "localhost", "wrong sender");
-  is(evt.uri, "http://localhost:8888/tests/dom/tests/mochitest/whatwg/test_postMessage_basehref.html",
-     "wrong sender");
+  is(evt.origin, "http://localhost:8888", "wrong sender");
   ok(evt.source === window, "wrong source");
 
   is(evt.data, "generate-event", "wrong data");
 
   SimpleTest.finish();
 }
 
 document.addEventListener("message", receiveMessage, false);
--- a/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_chrome.html
@@ -24,20 +24,16 @@ chrome://mochikit/content/chrome/dom/tes
 
 
 <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'");
 
@@ -64,32 +60,29 @@ function messageReceiver(evt)
 
 /******************
  * 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.origin, "chrome://mochikit", "wrong origin for chrome: URL");
   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.origin, "http://example.org", "content response event has wrong URI");
   is(evt.source, window.frames.contentDomain,
      "wrong source for same-domain message!");
 }
 
 
 /**************
  * TEST SETUP *
  **************/
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_closed.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>postMessage's interaction with closed windows</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>
+<p><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=417075">Bug 417075</a></p>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<div id="holder"></div>
+
+<pre id="test">
+<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.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");
+
+    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);
+
+function iframeLoaded(evt)
+{
+  var iframe = $("insertedIframe");
+  iframe.removeEventListener("load", iframeLoaded, false);
+
+  var iframeWindow = iframe.contentWindow;
+  $("holder").removeChild($("insertedIframe"));
+  iframeWindow.postMessage("NOT-RECEIVED");
+
+  SimpleTest.finish();
+}
+
+var openedWindow;
+
+function run()
+{
+  openedWindow = window.open("postMessage_closed_helper.html?opener", "foobar");
+}
+
+window.addEventListener("load", run, false);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/whatwg/test_postMessage_hash.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_hash.html
@@ -18,19 +18,17 @@
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 function receiveMessage(evt)
 {
-  is(evt.domain, "localhost", "wrong sender");
-  is(evt.uri, "http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_hash.html#hash",
-     "wrong sender");
+  is(evt.origin, "http://localhost:8888", "wrong origin");
   ok(evt.source === window.frames.kid, "wrong source");
 
   is(evt.data, "response-message", "wrong data");
 
   SimpleTest.finish();
 }
 
 document.addEventListener("message", receiveMessage, false);
--- a/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
+++ b/dom/tests/mochitest/whatwg/test_postMessage_idn.xhtml
@@ -32,20 +32,18 @@ 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.origin, "http://sub1.ält.example.org:8000",
+     "wrong origin -- IDN issue, perhaps?");
 
   is(evt.data, "idn-response", "unexpected test result");
   ok(evt.source === idnWindow, "wrong source");
 
   responseReceived = true;
 }
 document.addEventListener("message", receiveMessage, false);
 
--- a/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
+++ b/dom/tests/mochitest/whatwg/test_postMessage_onOther.html
@@ -26,20 +26,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 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.origin, "http://example.org:8000", "unexpected origin");
   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;
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_origin.xhtml
@@ -0,0 +1,470 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=417075
+-->
+<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=417075">Mozilla Bug 417075</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe src="http://localhost:8888/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="sameDomain"></iframe>
+<iframe src="http://example.com/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="otherDomain"></iframe>
+<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="otherDomainPort"></iframe>
+<iframe src="ftp://localhost:27534/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="localNoExist"></iframe>
+
+<iframe src="http://sub1.παράδειγμα.δοκιμή/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="idnKidWhitelist"></iframe>
+
+<iframe src="http://sub1.exämple.test/tests/dom/tests/mochitest/whatwg/postMessage_origin_helper.xhtml"
+        id="idnKidNoWhitelist"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript"><![CDATA[
+/** Test for Bug 417075 **/
+
+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
+   },
+   {
+     args: ["NOT-RECEIVED", "null"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR
+   },
+   {
+     args: ["NOT-RECEIVED", "a"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR
+   },
+   {
+     args: ["NOT-RECEIVED", "http :"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR
+   },
+   {
+     args: ["NOT-RECEIVED", "http: //"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR,
+
+     throwsNoException: 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
+   },
+   {
+     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
+   },
+   // 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
+   },
+   {
+     args: ["TODO", "http://\nlocalhost:8888"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR,
+
+     returnOrigin: "http://localhost:8888",
+     throwsNoException: true
+   },
+   {
+     args: ["TODO", "http://localhost:8888\0"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR,
+
+     returnOrigin: "http://localhost:8888",
+     throwsNoException: true
+   },
+   {
+     args: ["TODO", "http://localhost:8888\n"],
+     source: "sameDomain",
+     code: DOMException.SYNTAX_ERR,
+
+     returnOrigin: "http://localhost:8888",
+     throwsNoException: true
+   },
+   // 15
+   {
+     args: ONE_PASS,
+     source: "sameDomain",
+     returnOrigin: "http://localhost:8888"
+   },
+   {
+     args: ["PASS", null],
+     source: "sameDomain",
+     returnOrigin: "http://localhost:8888"
+   },
+   {
+     args: ["PASS", "http://example.com"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   {
+     args: ["PASS", "http://example.com/"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   {
+     args: ["PASS", "http://example.com:80"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   // 20
+   {
+     args: ["PASS", "http://example.com:80/"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   {
+     args: ["PASS", "http://example.com:80/foobar"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   {
+     args: ["PASS", "http://example.com/foobar"],
+     source: "otherDomain",
+     returnOrigin: "http://example.com"
+   },
+   {
+     args: ["PASS", "http://example.com:8000"],
+     source: "otherDomain",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "http://example.com:8000/"],
+     source: "otherDomain",
+     expectNoCallback: true
+   },
+   // 25
+   {
+     args: ["PASS", "http://example.org:8000"],
+     source: "otherDomainPort",
+     returnOrigin: "http://example.org:8000"
+   },
+   {
+     args: ["PASS", "http://example.org:8000/"],
+     source: "otherDomainPort",
+     returnOrigin: "http://example.org:8000"
+   },
+   {
+     args: ["PASS", "http://example.org:8000/tests/dom/test/mochitest/whatwg/postMessage_origin_helper.xhtml"],
+     source: "otherDomainPort",
+     returnOrigin: "http://example.org:8000"
+   },
+   {
+     args: ["PASS", "http://example.org:8000/tests/dom/test/mochitest/whatwg/this_file_does_not_exist.xhtml"],
+     source: "otherDomainPort",
+     returnOrigin: "http://example.org:8000"
+   },
+   {
+     args: ["PASS", "http://example.org"],
+     source: "otherDomainPort",
+     expectNoCallback: true
+   },
+   // 30
+   {
+     args: ["PASS", "http://example.org:80"],
+     source: "otherDomainPort",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "http://example.org/"],
+     source: "otherDomainPort",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "http://example.org"],
+     source: "otherDomain",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "ftp://localhost:8888"],
+     source: "sameDomain",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "http://localhost:8888"],
+     source: "sameDomain",
+     returnOrigin: "http://localhost:8888"
+   },
+   // 35
+   {
+     args: ["PASS", "http://localhost:27534"],
+     source: "sameDomain",
+     expectNoCallback: true
+   },
+   {
+     args: ["PASS", "http://sub1.παράδειγμα.δοκιμή"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80/"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.παράδειγμα.δοκιμή:80/foobar"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   // 40
+   {
+     args: ["PASS", "http://sub1.παράδειγμα.δοκιμή/foobar"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80/"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   {
+     args: ["PASS", "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80/foo"],
+     source: "idnKidWhitelist",
+     returnOrigin: "http://sub1.παράδειγμα.δοκιμή"
+   },
+   // 45
+   {
+     args: ["PASS", "http://sub1.exämple.test"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.exämple.test:80"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.exämple.test:80/"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.exämple.test/"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.exämple.test/foobar"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   // 50
+   {
+     args: ["PASS", "http://sub1.xn--exmple-cua.test"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.xn--exmple-cua.test:80"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.xn--exmple-cua.test:80/"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.xn--exmple-cua.test/"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+   {
+     args: ["PASS", "http://sub1.xn--exmple-cua.test/foobar"],
+     source: "idnKidNoWhitelist",
+     returnOrigin: "http://sub1.exämple.test",
+
+     wrongReturnOrigin: true
+   },
+  ];
+
+function allTests()
+{
+  var test, target, called;
+
+  function receive(evt)
+  {
+    var originCheck = test.wrongReturnOrigin ? 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");
+    ok(evt.source === target, "wrong source");
+    called = true;
+  }
+
+  function post(win, args, err)
+  {
+    called = false;
+    win.postMessage.apply(win, args);
+  }
+
+  document.addEventListener("message", receive, false);
+
+  for (var i = 0, sz = tests.length; i < sz; i++)
+  {
+    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);
+    }
+  }
+
+  document.removeEventListener("message", receive, false);
+}
+
+function oddballTests()
+{
+  var called;
+  function receive(evt)
+  {
+    is(evt.origin, "http://localhost:8888", "wrong sender");
+    is(evt.data, "PASS", "wrong data");
+    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);
+    is(called, true, "should have been called");
+  }
+  finally
+  {
+    document.removeEventListener("message", receive, false);
+  }
+}
+
+function run()
+{
+  oddballTests();
+  allTests();
+  SimpleTest.finish();
+}
+
+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
@@ -124,32 +124,25 @@ function messageReceiver(evt)
     {
       // 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.origin, "http://localhost:8888",
+         "wrong origin for event from about:blank");
       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.origin, "http://localhost:8888",
+         "wrong origin for event from about:blank #2");
       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
@@ -157,24 +150,21 @@ function messageReceiver(evt)
       // 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 " +
+        is(evt.origin, "http://localhost:8888",
+           "wrong origin 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);
@@ -195,20 +185,18 @@ function getContents(description, respon
     "  <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.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" +
     "}\n" +
     "\n" +
     "function ready()\n" +
     "{\n" +
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_userpass.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=417075
+-->
+<head>
+  <title>postMessage from a page with username/password in its URI</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=417075">Mozilla Bug 417075</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe src="http://bobhope:password@example.org/tests/dom/tests/mochitest/whatwg/postMessage_userpass_helper.html"
+        name="userPassKid"></iframe>
+
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 417075 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function receiveMessage(evt)
+{
+  is(evt.origin, "http://example.org", "wrong origin");
+  is(evt.data, "child-message", "wrong data");
+  ok(evt.source === window.frames.userPassKid, "wrong source");
+  SimpleTest.finish();
+}
+
+document.addEventListener("message", receiveMessage, false);
+
+function sendMessage(evt)
+{
+  window.frames.userPassKid.postMessage("parent-message");
+}
+
+window.addEventListener("load", sendMessage, false);
+</script>
+</pre>
+</body>
+</html>
--- a/testing/mochitest/runtests.pl.in
+++ b/testing/mochitest/runtests.pl.in
@@ -455,18 +455,20 @@ SERVERPREFEND
   # off the first server because it's the one to which we proxy all the others.
   my $quotedServers = join(", ", map("'" . $_ . "'", @servers[1 .. $#servers]));
 
   my $pacURL = "data:text/plain,";
   $pacURL   .= "function FindProxyForURL(url, host)             ";
   $pacURL   .= "{                                               ";
   $pacURL   .= "  var servers = [$quotedServers];               ";
   $pacURL   .= "  var regex =                                   ";
-  $pacURL   .= "    new RegExp('http://(.*?(:\\\\\\\\d+)?)/');  ";
+  $pacURL   .= "    new RegExp('http://(?:[^/@]*@)?(.*?(:\\\\\\\\d+)?)/');  ";
   $pacURL   .= "  var matches = regex.exec(url);                ";
+  $pacURL   .= "  if (!matches)                                 ";
+  $pacURL   .= "    return 'DIRECT';                            ";
   $pacURL   .= "  var hostport = matches[1], port = matches[2]; ";
   $pacURL   .= "  if (!port)                                    ";
   $pacURL   .= "    hostport += ':80';                          ";
   $pacURL   .= "  if (servers.indexOf(hostport) >= 0)           ";
   $pacURL   .= "    return 'PROXY localhost:8888';              ";
   $pacURL   .= "  return 'DIRECT';                              ";
   $pacURL   .= "}";