Bug 1195415 - Add asciiHostPort field to nsIURI, and use it in the implementation of nsPrincipal::GetOriginForURI, r=bholley
authorMichael Layzell <michael@thelayzells.com>
Mon, 17 Aug 2015 15:34:17 -0400
changeset 258288 75ba313634f34e4b5e7fd9e5e6862680c1f69ddd
parent 258287 f195230a7c5da263c0d5dbcce7dc8b31380c728f
child 258289 cf099fe29296dd3f75114c4d4394d81ea4383bfb
push id29249
push userryanvm@gmail.com
push dateWed, 19 Aug 2015 11:17:27 +0000
treeherdermozilla-central@706b23a03d1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1195415
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1195415 - Add asciiHostPort field to nsIURI, and use it in the implementation of nsPrincipal::GetOriginForURI, r=bholley
caps/nsNullPrincipalURI.cpp
caps/nsPrincipal.cpp
image/decoders/icon/nsIconURI.cpp
image/nsIIconURI.idl
modules/libjar/nsIJARURI.idl
modules/libjar/nsJARURI.cpp
netwerk/base/nsIFileURL.idl
netwerk/base/nsIURI.idl
netwerk/base/nsIURL.idl
netwerk/base/nsSimpleURI.cpp
netwerk/base/nsStandardURL.cpp
netwerk/test/unit/test_bug1195415.js
netwerk/test/unit/xpcshell.ini
xpcom/io/nsBinaryStream.cpp
--- a/caps/nsNullPrincipalURI.cpp
+++ b/caps/nsNullPrincipalURI.cpp
@@ -82,16 +82,22 @@ NS_INTERFACE_MAP_END
 NS_IMETHODIMP
 nsNullPrincipalURI::GetAsciiHost(nsACString &_host)
 {
   _host.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNullPrincipalURI::GetAsciiHostPort(nsACString &_hostport)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsNullPrincipalURI::GetAsciiSpec(nsACString &_spec)
 {
   nsAutoCString buffer;
   (void)GetSpec(buffer);
   NS_EscapeURL(buffer, esc_OnlyNonASCII | esc_AlwaysCopy, _spec);
   return NS_OK;
 }
 
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -106,29 +106,29 @@ nsPrincipal::GetOriginForURI(nsIURI* aUR
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
   if (!origin) {
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoCString host;
+  nsAutoCString hostPort;
 
   // chrome: URLs don't have a meaningful origin, so make
   // sure we just get the full spec for them.
   // XXX this should be removed in favor of the solution in
   // bug 160042.
   bool isChrome;
   nsresult rv = origin->SchemeIs("chrome", &isChrome);
   if (NS_SUCCEEDED(rv) && !isChrome) {
-    rv = origin->GetAsciiHost(host);
+    rv = origin->GetAsciiHostPort(hostPort);
     // Some implementations return an empty string, treat it as no support
     // for asciiHost by that implementation.
-    if (host.IsEmpty()) {
+    if (hostPort.IsEmpty()) {
       rv = NS_ERROR_FAILURE;
     }
   }
 
   // We want the invariant that prinA.origin == prinB.origin i.f.f.
   // prinA.equals(prinB). However, this requires that we impose certain constraints
   // on the behavior and origin semantics of principals, and in particular, forbid
   // creating origin strings for principals whose equality constraints are not
@@ -148,36 +148,17 @@ nsPrincipal::GetOriginForURI(nsIURI* aUR
     // These URIs could technically contain a '^', but they never should.
     if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) {
       aOrigin.Truncate();
       return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
-  int32_t port;
   if (NS_SUCCEEDED(rv) && !isChrome) {
-    rv = origin->GetPort(&port);
-  }
-
-  if (NS_SUCCEEDED(rv) && !isChrome) {
-    nsAutoCString hostPort;
-    if (host.FindChar(':') != -1) {
-      hostPort.Assign("[");
-      hostPort.Append(host);
-      hostPort.Append("]");
-    } else {
-      hostPort.Assign(host);
-    }
-
-    if (port != -1) {
-      hostPort.Append(':');
-      hostPort.AppendInt(port, 10);
-    }
-
     rv = origin->GetScheme(aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
     aOrigin.AppendLiteral("://");
     aOrigin.Append(hostPort);
   }
   else {
     // If we reached this branch, we can only create an origin if we have a nsIStandardURL.
     // So, we query to a nsIStandardURL, and fail if we aren't an instance of an nsIStandardURL
--- a/image/decoders/icon/nsIconURI.cpp
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -461,16 +461,22 @@ nsMozIconURI::Resolve(const nsACString& 
 
 NS_IMETHODIMP
 nsMozIconURI::GetAsciiSpec(nsACString& aSpecA)
 {
   return GetSpec(aSpecA);
 }
 
 NS_IMETHODIMP
+nsMozIconURI::GetAsciiHostPort(nsACString& aHostPortA)
+{
+  return GetHostPort(aHostPortA);
+}
+
+NS_IMETHODIMP
 nsMozIconURI::GetAsciiHost(nsACString& aHostA)
 {
   return GetHost(aHostA);
 }
 
 NS_IMETHODIMP
 nsMozIconURI::GetOriginCharset(nsACString& result)
 {
--- a/image/nsIIconURI.idl
+++ b/image/nsIIconURI.idl
@@ -40,17 +40,17 @@
    *   Description: The state of the icon.
    *
    *   Parameter:   contentType
    *   Values:      <mime-type>
    *   Description: The mime type we want an icon for. This is ignored by
    *                stock images.
    */
 
-[scriptable, uuid(da53adda-cbe3-41bc-a57d-fdd7a0ff448b)]
+[scriptable, uuid(f8fe5ef2-5f2b-43f3-857d-5b64d192c427)]
 interface nsIMozIconURI : nsIURI
 {
   /// iconFile: the file URL contained within this moz-icon url, or null.
   attribute nsIURL iconURL;
 
   /// imageSize: The image area in square pixels, defaults to 16 if unspecified.
   attribute unsigned long imageSize;
 
--- a/modules/libjar/nsIJARURI.idl
+++ b/modules/libjar/nsIJARURI.idl
@@ -10,17 +10,17 @@
  * JAR URLs have the following syntax
  *
  * jar:<jar-file-uri>!/<jar-entry>
  *
  * EXAMPLE: jar:http://www.big.com/blue.jar!/ocean.html
  *
  * The nsIURL methods operate on the <jar-entry> part of the spec.
  */
-[scriptable, uuid(1ee60719-c056-43b3-8f54-6a6e7ba0ca6c)]
+[scriptable, uuid(646a508c-f786-4e14-be6d-8dda2a633c60)]
 interface nsIJARURI : nsIURL {
 
     /**
      * Returns the root URI (the one for the actual JAR file) for this JAR
      * (e.g., http://www.big.com/blue.jar).
      */
     readonly attribute nsIURI JARFile;
 
--- a/modules/libjar/nsJARURI.cpp
+++ b/modules/libjar/nsJARURI.cpp
@@ -425,16 +425,22 @@ nsJARURI::SetPath(const nsACString &aPat
 NS_IMETHODIMP
 nsJARURI::GetAsciiSpec(nsACString &aSpec)
 {
     // XXX Shouldn't this like... make sure it returns ASCII or something?
     return GetSpec(aSpec);
 }
 
 NS_IMETHODIMP
+nsJARURI::GetAsciiHostPort(nsACString &aHostPort)
+{
+    return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 nsJARURI::GetAsciiHost(nsACString &aHost)
 {
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsJARURI::GetOriginCharset(nsACString &aOriginCharset)
 {
--- a/netwerk/base/nsIFileURL.idl
+++ b/netwerk/base/nsIFileURL.idl
@@ -7,17 +7,17 @@
 
 interface nsIFile;
 
 /**
  * nsIFileURL provides access to the underlying nsIFile object corresponding to
  * an URL.  The URL scheme need not be file:, since other local protocols may
  * map URLs to files (e.g., resource:).
  */
-[scriptable, uuid(7750029c-1b0a-414e-8359-a77f24a2a0a6)]
+[scriptable, uuid(e91ac988-27c2-448b-b1a1-3822e1ef1987)]
 interface nsIFileURL : nsIURL
 {
     /**
      * Get/Set nsIFile corresponding to this URL.
      *
      *  - Getter returns a reference to an immutable object.  Callers must clone
      *    before attempting to modify the returned nsIFile object.  NOTE: this
      *    constraint might not be enforced at runtime, so beware!!
--- a/netwerk/base/nsIURI.idl
+++ b/netwerk/base/nsIURI.idl
@@ -62,17 +62,17 @@
  * nsIIOService.newURI.
  *
  * NOTE: nsBinaryInputStream::ReadObject contains a hackaround to intercept the
  * old (pre-gecko6) nsIURI IID and swap in the current IID instead, in order
  * for sessionstore to work after an upgrade.  If this IID is revved further,
  * we will need to add additional checks there for all intermediate IIDs, until
  * nsPrincipal is fixed to serialize its URIs as nsISupports (bug 662693).
  */
-[scriptable, uuid(395fe045-7d18-4adb-a3fd-af98c8a1af11)]
+[scriptable, uuid(92073a54-6d78-4f30-913a-b871813208c6)]
 interface nsIURI : nsISupports
 {
     /************************************************************************
      * The URI is broken down into the following principal components:
      */
 
     /**
      * Returns a string representation of the URI. Setting the spec causes
@@ -190,16 +190,23 @@ interface nsIURI : nsISupports
     /**
      * The URI spec with an ASCII compatible encoding.  Host portion follows
      * the IDNA draft spec.  Other parts are URL-escaped per the rules of
      * RFC2396.  The result is strictly ASCII.
      */
     readonly attribute ACString asciiSpec;
 
     /**
+     * The host:port (or simply the host, if port == -1), with an ASCII compatible
+     * encoding.  Host portion follows the IDNA draft spec.  The result is strictly
+     * ASCII.
+     */
+    readonly attribute ACString asciiHostPort;
+
+    /**
      * The URI host with an ASCII compatible encoding.  Follows the IDNA
      * draft spec for converting internationalized domain names (UTF-8) to
      * ASCII for compatibility with existing internet infrasture.
      */
     readonly attribute ACString asciiHost;
 
     /**
      * The charset of the document from which this URI originated.  An empty
--- a/netwerk/base/nsIURL.idl
+++ b/netwerk/base/nsIURL.idl
@@ -14,17 +14,17 @@
  *            \          \                       /
  *             \          -----------------------
  *              \                   |          /
  *               \               fileName     /
  *                ----------------------------
  *                            |
  *                        filePath
  */
-[scriptable, uuid(1419aa16-f134-4154-9886-00c7c5147a13)]
+[scriptable, uuid(86adcd89-0b70-47a2-b0fe-5bb2c5f37e31)]
 interface nsIURL : nsIURI
 {
     /*************************************************************************
      * The URL path is broken down into the following principal components:
      */
 
     /**
      * Returns a path including the directory and file portions of a
--- a/netwerk/base/nsSimpleURI.cpp
+++ b/netwerk/base/nsSimpleURI.cpp
@@ -292,16 +292,17 @@ nsSimpleURI::SetPassword(const nsACStrin
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::GetHostPort(nsACString &result)
 {
     // Note: Audit all callers before changing this to return an empty
     // string -- CAPS and UI code may depend on this throwing.
+    // Note: If this is changed, change GetAsciiHostPort as well.
     return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::SetHostPort(const nsACString &result)
 {
     NS_ENSURE_STATE(mMutable);
     
@@ -523,16 +524,23 @@ nsSimpleURI::GetAsciiSpec(nsACString &re
     nsAutoCString buf;
     nsresult rv = GetSpec(buf);
     if (NS_FAILED(rv)) return rv;
     NS_EscapeURL(buf, esc_OnlyNonASCII|esc_AlwaysCopy, result);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSimpleURI::GetAsciiHostPort(nsACString &result)
+{
+    // XXX This behavior mimics GetHostPort.
+    return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
 nsSimpleURI::GetAsciiHost(nsACString &result)
 {
     result.Truncate();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::GetOriginCharset(nsACString &result)
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1085,35 +1085,50 @@ nsStandardURL::GetAsciiSpec(nsACString &
 
     // try to guess the capacity required for result...
     result.SetCapacity(mSpec.Length() + std::min<uint32_t>(32, mSpec.Length()/10));
 
     result = Substring(mSpec, 0, mScheme.mLen + 3);
 
     NS_EscapeURL(Userpass(true), esc_OnlyNonASCII | esc_AlwaysCopy, result);
 
-    // get escaped host
-    nsAutoCString escHostport;
-    if (mHost.mLen > 0) {
-        // this doesn't fail
-        (void) GetAsciiHost(escHostport);
-
-        // escHostport = "hostA" + ":port"
-        uint32_t pos = mHost.mPos + mHost.mLen;
-        if (pos < mPath.mPos)
-            escHostport += Substring(mSpec, pos, mPath.mPos - pos);
-    }
-    result += escHostport;
+    // get the hostport
+    nsAutoCString hostport;
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetAsciiHostPort(hostport)));
+    result += hostport;
 
     NS_EscapeURL(Path(), esc_OnlyNonASCII | esc_AlwaysCopy, result);
     return NS_OK;
 }
 
 // result is ASCII
 NS_IMETHODIMP
+nsStandardURL::GetAsciiHostPort(nsACString &result)
+{
+    if (mHostEncoding == eEncoding_ASCII) {
+        result = Hostport();
+        return NS_OK;
+    }
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetAsciiHost(result)));
+
+    // As our mHostEncoding is not eEncoding_ASCII, we know that
+    // the our host is not ipv6, and we can avoid looking at it.
+    MOZ_ASSERT(result.FindChar(':') == -1, "The host must not be ipv6");
+
+    // hostport = "hostA" + ":port"
+    uint32_t pos = mHost.mPos + mHost.mLen;
+    if (pos < mPath.mPos)
+        result += Substring(mSpec, pos, mPath.mPos - pos);
+
+    return NS_OK;
+}
+
+// result is ASCII
+NS_IMETHODIMP
 nsStandardURL::GetAsciiHost(nsACString &result)
 {
     if (mHostEncoding == eEncoding_ASCII) {
         result = Host();
         return NS_OK;
     }
 
     // perhaps we have it cached...
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug1195415.js
@@ -0,0 +1,116 @@
+// Test for bug 1195415
+
+function run_test() {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+        getService(Ci.nsIIOService);
+  var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
+        getService(Ci.nsIScriptSecurityManager);
+
+  // NON-UNICODE
+  var uri = ios.newURI("http://foo.com/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "foo.com");
+  uri.port = 90;
+  var prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "foo.com:90");
+  do_check_eq(prin.origin, "http://foo.com:90");
+
+  uri = ios.newURI("http://foo.com:10/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "foo.com:10");
+  uri.port = 500;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "foo.com:500");
+  do_check_eq(prin.origin, "http://foo.com:500");
+
+  uri = ios.newURI("http://foo.com:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "foo.com:5000");
+  uri.port = 20;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "foo.com:20");
+  do_check_eq(prin.origin, "http://foo.com:20");
+
+  uri = ios.newURI("http://foo.com:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "foo.com:5000");
+  uri.port = -1;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "foo.com");
+  do_check_eq(prin.origin, "http://foo.com");
+
+  uri = ios.newURI("http://foo.com:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "foo.com:5000");
+  uri.port = 80;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "foo.com");
+  do_check_eq(prin.origin, "http://foo.com");
+
+  // UNICODE
+  uri = ios.newURI("http://jos\u00e9.example.net.ch/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch");
+  uri.port = 90;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:90");
+  do_check_eq(prin.origin, "http://xn--jos-dma.example.net.ch:90");
+
+  uri = ios.newURI("http://jos\u00e9.example.net.ch:10/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:10");
+  uri.port = 500;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:500");
+  do_check_eq(prin.origin, "http://xn--jos-dma.example.net.ch:500");
+
+  uri = ios.newURI("http://jos\u00e9.example.net.ch:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:5000");
+  uri.port = 20;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:20");
+  do_check_eq(prin.origin, "http://xn--jos-dma.example.net.ch:20");
+
+  uri = ios.newURI("http://jos\u00e9.example.net.ch:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:5000");
+  uri.port = -1;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch");
+  do_check_eq(prin.origin, "http://xn--jos-dma.example.net.ch");
+
+  uri = ios.newURI("http://jos\u00e9.example.net.ch:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch:5000");
+  uri.port = 80;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "xn--jos-dma.example.net.ch");
+  do_check_eq(prin.origin, "http://xn--jos-dma.example.net.ch");
+
+  // ipv6
+  uri = ios.newURI("http://[123:45::678]/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "[123:45::678]");
+  uri.port = 90;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:90");
+  do_check_eq(prin.origin, "http://[123:45::678]:90");
+
+  uri = ios.newURI("http://[123:45::678]:10/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:10");
+  uri.port = 500;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:500");
+  do_check_eq(prin.origin, "http://[123:45::678]:500");
+
+  uri = ios.newURI("http://[123:45::678]:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:5000");
+  uri.port = 20;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:20");
+  do_check_eq(prin.origin, "http://[123:45::678]:20");
+
+  uri = ios.newURI("http://[123:45::678]:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:5000");
+  uri.port = -1;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "[123:45::678]");
+  do_check_eq(prin.origin, "http://[123:45::678]");
+
+  uri = ios.newURI("http://[123:45::678]:5000/file.txt", null, null);
+  do_check_eq(uri.asciiHostPort, "[123:45::678]:5000");
+  uri.port = 80;
+  prin = ssm.createCodebasePrincipal(uri, {});
+  do_check_eq(uri.asciiHostPort, "[123:45::678]");
+  do_check_eq(prin.origin, "http://[123:45::678]");
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -322,8 +322,9 @@ skip-if = os == "android"
 [test_multipart_streamconv_application_package.js]
 [test_safeoutputstream_append.js]
 [test_packaged_app_service.js]
 [test_suspend_channel_before_connect.js]
 [test_inhibit_caching.js]
 [test_dns_disable_ipv4.js]
 [test_dns_disable_ipv6.js]
 [test_packaged_app_service_paths.js]
+[test_bug1195415.js]
--- a/xpcom/io/nsBinaryStream.cpp
+++ b/xpcom/io/nsBinaryStream.cpp
@@ -912,19 +912,26 @@ nsBinaryInputStream::ReadObject(bool aIs
   };
 
   // hackaround for bug 682031
   static const nsIID oldURIiid3 = {
     0x12120b20, 0x0929, 0x40e9,
     { 0x88, 0xcf, 0x6e, 0x08, 0x76, 0x6e, 0x8b, 0x23 }
   };
 
+  // hackaround for bug 1195415
+  static const nsIID oldURIiid4 = {
+    0x395fe045, 0x7d18, 0x4adb,
+    { 0xa3, 0xfd, 0xaf, 0x98, 0xc8, 0xa1, 0xaf, 0x11 }
+  };
+
   if (iid.Equals(oldURIiid) ||
       iid.Equals(oldURIiid2) ||
-      iid.Equals(oldURIiid3)) {
+      iid.Equals(oldURIiid3) ||
+      iid.Equals(oldURIiid4)) {
     const nsIID newURIiid = NS_IURI_IID;
     iid = newURIiid;
   }
   // END HACK
 
   nsCOMPtr<nsISupports> object = do_CreateInstance(cid, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;