Bug 308590 patch 3: Add nsIURI::EqualsExceptRef interface & impls. r=bz sr=biesi
authorDaniel Holbert <dholbert@cs.stanford.edu>
Sat, 21 May 2011 18:12:45 -0700
changeset 70212 add8d8d67ba80f21adcc4971be3a26dfbb86c16b
parent 70211 e9c7616c4f722cebd8d2985bb59d35bc419a1d9f
child 70213 835e9789e934e5b9c1cda65ab10547dd5bcc52a4
push id11
push userffxbld
push dateThu, 11 Aug 2011 21:43:38 +0000
treeherdermozilla-release@cf0a29826586 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, biesi
bugs308590
milestone6.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 308590 patch 3: Add nsIURI::EqualsExceptRef interface & impls. r=bz sr=biesi
caps/src/nsNullPrincipalURI.cpp
content/base/src/nsFileDataProtocolHandler.cpp
modules/libjar/nsIJARURI.idl
modules/libjar/nsJARURI.cpp
modules/libjar/nsJARURI.h
modules/libpr0n/decoders/icon/nsIIconURI.idl
modules/libpr0n/decoders/icon/nsIconURI.cpp
netwerk/base/public/nsIFileURL.idl
netwerk/base/public/nsIURI.idl
netwerk/base/public/nsIURL.idl
netwerk/base/src/nsSimpleNestedURI.cpp
netwerk/base/src/nsSimpleNestedURI.h
netwerk/base/src/nsSimpleURI.cpp
netwerk/base/src/nsSimpleURI.h
netwerk/base/src/nsStandardURL.cpp
netwerk/base/src/nsStandardURL.h
netwerk/test/unit/test_URIs.js
--- a/caps/src/nsNullPrincipalURI.cpp
+++ b/caps/src/nsNullPrincipalURI.cpp
@@ -252,16 +252,24 @@ nsNullPrincipalURI::Equals(nsIURI *aOthe
   if (NS_SUCCEEDED(rv)) {
     *_equals = (mScheme == otherURI->mScheme && mPath == otherURI->mPath);
     NS_RELEASE(otherURI);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNullPrincipalURI::EqualsExceptRef(nsIURI *aOther, PRBool *_equals)
+{
+  // GetRef/SetRef not supported by nsNullPrincipalURI, so
+  // EqualsExceptRef() is the same as Equals().
+  return Equals(aOther, _equals);
+}
+
+NS_IMETHODIMP
 nsNullPrincipalURI::Resolve(const nsACString &aRelativePath,
                             nsACString &_resolvedURI)
 {
   _resolvedURI = aRelativePath;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsFileDataProtocolHandler.cpp
+++ b/content/base/src/nsFileDataProtocolHandler.cpp
@@ -142,19 +142,21 @@ public:
   // For use only from deserialization
   nsFileDataURI() : nsSimpleURI() {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIURIWITHPRINCIPAL
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
-  // Override Clone() and Equals()
+  // Override Clone() and EqualsInternal()
   NS_IMETHOD Clone(nsIURI** aClone);
-  NS_IMETHOD Equals(nsIURI* aOther, PRBool *aResult);
+  virtual nsresult EqualsInternal(nsIURI* aOther,
+                                  RefHandlingEnum aRefHandlingMode,
+                                  PRBool* aResult);
 
   // Override StartClone to hand back a nsFileDataURI
   virtual nsSimpleURI* StartClone()
   { return new nsFileDataURI(); }
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
@@ -231,18 +233,20 @@ nsFileDataURI::Clone(nsIURI** aClone)
   nsFileDataURI* fileDataURI = static_cast<nsFileDataURI*>(simpleClone.get());
 
   fileDataURI->mPrincipal = mPrincipal;
 
   simpleClone.forget(aClone);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsFileDataURI::Equals(nsIURI* aOther, PRBool *aResult)
+/* virtual */ nsresult
+nsFileDataURI::EqualsInternal(nsIURI* aOther,
+                              nsSimpleURI::RefHandlingEnum aRefHandlingMode,
+                              PRBool* aResult)
 {
   if (!aOther) {
     *aResult = PR_FALSE;
     return NS_OK;
   }
   
   nsRefPtr<nsFileDataURI> otherFileDataUri;
   aOther->QueryInterface(kFILEDATAURICID, getter_AddRefs(otherFileDataUri));
@@ -253,17 +257,18 @@ nsFileDataURI::Equals(nsIURI* aOther, PR
 
   nsresult rv = mPrincipal->Equals(otherFileDataUri->mPrincipal, aResult);
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (!*aResult) {
     return NS_OK;
   }
 
-  return nsSimpleURI::Equals(otherFileDataUri, aResult);
+  return nsSimpleURI::EqualsInternal(otherFileDataUri, aRefHandlingMode,
+                                     aResult);
 }
 
 // nsIClassInfo methods:
 NS_IMETHODIMP 
 nsFileDataURI::GetInterfaces(PRUint32 *count, nsIID * **array)
 {
   *count = 0;
   *array = nsnull;
--- a/modules/libjar/nsIJARURI.idl
+++ b/modules/libjar/nsIJARURI.idl
@@ -42,17 +42,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(b0922a89-f87b-4cb5-8612-305a285fcca7)]
+[scriptable, uuid(ba32f809-5adf-4191-866a-1cddf60548f7)]
 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
@@ -453,36 +453,50 @@ nsJARURI::GetOriginCharset(nsACString &a
 {
     aOriginCharset = mCharsetHint;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARURI::Equals(nsIURI *other, PRBool *result)
 {
-    nsresult rv;
+    return EqualsInternal(other, eHonorRef, result);
+}
 
+NS_IMETHODIMP
+nsJARURI::EqualsExceptRef(nsIURI *other, PRBool *result)
+{
+    return EqualsInternal(other, eIgnoreRef, result);
+}
+
+// Helper method:
+/* virtual */ nsresult
+nsJARURI::EqualsInternal(nsIURI *other,
+                         nsJARURI::RefHandlingEnum refHandlingMode,
+                         PRBool *result)
+{
     *result = PR_FALSE;
 
-    if (other == nsnull)
+    if (!other)
         return NS_OK;	// not equal
 
     nsRefPtr<nsJARURI> otherJAR;
     other->QueryInterface(NS_GET_IID(nsJARURI), getter_AddRefs(otherJAR));
     if (!otherJAR)
         return NS_OK;   // not equal
 
     PRBool equal;
-    rv = mJARFile->Equals(otherJAR->mJARFile, &equal);
+    nsresult rv = mJARFile->Equals(otherJAR->mJARFile, &equal);
     if (NS_FAILED(rv) || !equal) {
         return rv;   // not equal
     }
 
-    rv = mJAREntry->Equals(otherJAR->mJAREntry, result);
-    return rv;
+    return refHandlingMode == eHonorRef ?
+        mJAREntry->Equals(otherJAR->mJAREntry, result) :
+        mJAREntry->EqualsExceptRef(otherJAR->mJAREntry, result);
 }
 
 NS_IMETHODIMP
 nsJARURI::SchemeIs(const char *i_Scheme, PRBool *o_Equals)
 {
     NS_ENSURE_ARG_POINTER(o_Equals);
     if (!i_Scheme) return NS_ERROR_INVALID_ARG;
 
--- a/modules/libjar/nsJARURI.h
+++ b/modules/libjar/nsJARURI.h
@@ -89,16 +89,27 @@ public:
     nsresult FormatSpec(const nsACString &entryPath, nsACString &result,
                         PRBool aIncludeScheme = PR_TRUE);
     nsresult CreateEntryURL(const nsACString& entryFilename,
                             const char* charset,
                             nsIURL** url);
     nsresult SetSpecWithBase(const nsACString& aSpec, nsIURI* aBaseURL);
 
 protected:
+    // enum used in a few places to specify how .ref attribute should be handled
+    enum RefHandlingEnum {
+        eIgnoreRef,
+        eHonorRef
+    };
+
+    // Helper to share code between Equals methods.
+    virtual nsresult EqualsInternal(nsIURI* other,
+                                    RefHandlingEnum refHandlingMode,
+                                    PRBool* result);
+
     nsCOMPtr<nsIURI> mJARFile;
     // mJarEntry stored as a URL so that we can easily access things
     // like extensions, refs, etc.
     nsCOMPtr<nsIURL> mJAREntry;
     nsCString        mCharsetHint;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsJARURI, NS_THIS_JARURI_IMPL_CID)
--- a/modules/libpr0n/decoders/icon/nsIIconURI.idl
+++ b/modules/libpr0n/decoders/icon/nsIIconURI.idl
@@ -68,17 +68,17 @@
    *   Values:      [normal | disabled]
    *   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(E2D046D2-3729-440D-B0DB-F8D817BC2571)]
+[scriptable, uuid(5d8a4518-bebf-4e7e-bad0-3e6737b3d7f4)]
 interface nsIMozIconURI : nsIURI 
 {
   /**
    * iconFile
    *
    * the file URL contained within this moz-icon url, or null.
    */
   attribute nsIURL iconURL;
--- a/modules/libpr0n/decoders/icon/nsIconURI.cpp
+++ b/modules/libpr0n/decoders/icon/nsIconURI.cpp
@@ -411,16 +411,24 @@ nsMozIconURI::Equals(nsIURI *other, PRBo
   if (!PL_strcasecmp(spec1.get(), spec2.get()))
     *result = PR_TRUE;
   else
     *result = PR_FALSE;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsMozIconURI::EqualsExceptRef(nsIURI *other, PRBool *result)
+{
+  // GetRef/SetRef not supported by nsMozIconURI, so
+  // EqualsExceptRef() is the same as Equals().
+  return Equals(other, result);
+}
+
+NS_IMETHODIMP
 nsMozIconURI::SchemeIs(const char *i_Scheme, PRBool *o_Equals)
 {
   NS_ENSURE_ARG_POINTER(o_Equals);
   if (!i_Scheme) return NS_ERROR_INVALID_ARG;
   
   *o_Equals = PL_strcasecmp("moz-icon", i_Scheme) ? PR_FALSE : PR_TRUE;
   return NS_OK;
 }
--- a/netwerk/base/public/nsIFileURL.idl
+++ b/netwerk/base/public/nsIFileURL.idl
@@ -40,17 +40,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(d26b2e2e-1dd1-11b2-88f3-8545a7ba7949)]
+[scriptable, uuid(e693cb4f-d835-4068-9e14-76de8bd9df1d)]
 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/public/nsIURI.idl
+++ b/netwerk/base/public/nsIURI.idl
@@ -90,17 +90,17 @@
  * 
  * Unescaping URI segments is unadvised unless there is intimate
  * knowledge of the underlying charset or there is no plan to display (or
  * otherwise enforce a charset on) the resulting URI substring.
  *
  * The correct way to create an nsIURI from a string is via
  * nsIIOService.newURI.
  */
-[scriptable, uuid(70d94a92-2b13-4e03-9f84-c1684aeea0f2)]
+[scriptable, uuid(d6d04c36-0fa4-4db3-be05-4a18397103e2)]
 interface nsIURI : nsISupports
 {
     /************************************************************************
      * The URI is broken down into the following principal components:
      */
 
     /**
      * Returns a string representation of the URI. Setting the spec causes
@@ -194,16 +194,25 @@ interface nsIURI : nsISupports
     /**
      * URI equivalence test (not a strict string comparison).
      *
      * eg. http://foo.com:80/ == http://foo.com/
      */
     boolean equals(in nsIURI other);
 
     /**
+     * URI equivalence test (not a strict string comparison), ignoring
+     * the value of the .ref member.
+     *
+     * eg. http://foo.com/# == http://foo.com/
+     *     http://foo.com/#aaa == http://foo.com/#bbb
+     */
+    boolean equalsExceptRef(in nsIURI other);
+
+    /**
      * An optimization to do scheme checks without requiring the users of nsIURI
      * to GetScheme, thereby saving extra allocating and freeing. Returns true if
      * the schemes match (case ignored).
      */
     boolean schemeIs(in string scheme);
 
     /**
      * Clones the current URI.
--- a/netwerk/base/public/nsIURL.idl
+++ b/netwerk/base/public/nsIURL.idl
@@ -49,17 +49,17 @@
  *            \          \                       /
  *             \          -----------------------
  *              \                   |          /
  *               \               fileName     /
  *                ----------------------------
  *                            |
  *                        filePath
  */
-[scriptable, uuid(d55f7915-6ff7-4b17-8795-d3b447bfe1f8)]
+[scriptable, uuid(55e824ca-f1bb-4452-9e14-fcfa1ff091ce)]
 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/src/nsSimpleNestedURI.cpp
+++ b/netwerk/base/src/nsSimpleNestedURI.cpp
@@ -125,37 +125,40 @@ nsSimpleNestedURI::GetInnerURI(nsIURI** 
 }
 
 NS_IMETHODIMP
 nsSimpleNestedURI::GetInnermostURI(nsIURI** uri)
 {
     return NS_ImplGetInnermostURI(this, uri);
 }
 
-// nsIURI overrides
-
-NS_IMETHODIMP
-nsSimpleNestedURI::Equals(nsIURI* other, PRBool *result)
+// nsSimpleURI overrides
+/* virtual */ nsresult
+nsSimpleNestedURI::EqualsInternal(nsIURI* other,
+                                  nsSimpleURI::RefHandlingEnum refHandlingMode,
+                                  PRBool* result)
 {
     *result = PR_FALSE;
     NS_ENSURE_TRUE(mInnerURI, NS_ERROR_NOT_INITIALIZED);
     
     if (other) {
         PRBool correctScheme;
         nsresult rv = other->SchemeIs(mScheme.get(), &correctScheme);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (correctScheme) {
             nsCOMPtr<nsINestedURI> nest = do_QueryInterface(other);
             if (nest) {
                 nsCOMPtr<nsIURI> otherInner;
                 rv = nest->GetInnerURI(getter_AddRefs(otherInner));
                 NS_ENSURE_SUCCESS(rv, rv);
 
-                return otherInner->Equals(mInnerURI, result);
+                return (refHandlingMode == eHonorRef) ?
+                    otherInner->Equals(mInnerURI, result) :
+                    otherInner->EqualsExceptRef(mInnerURI, result);
             }
         }
     }
 
     return NS_OK;
 }
 
 /* virtual */ nsSimpleURI*
--- a/netwerk/base/src/nsSimpleNestedURI.h
+++ b/netwerk/base/src/nsSimpleNestedURI.h
@@ -68,18 +68,20 @@ public:
     nsSimpleNestedURI(nsIURI* innerURI);
 
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSINESTEDURI
     NS_DECL_NSIIPCSERIALIZABLE
 
     // Overrides for various methods nsSimpleURI implements follow.
   
-    // nsIURI overrides
-    NS_IMETHOD Equals(nsIURI* other, PRBool *result);
+    // nsSimpleURI overrides
+    virtual nsresult EqualsInternal(nsIURI* other,
+                                    RefHandlingEnum refHandlingMode,
+                                    PRBool* result);
     virtual nsSimpleURI* StartClone();
 
     // nsISerializable overrides
     NS_IMETHOD Read(nsIObjectInputStream* aStream);
     NS_IMETHOD Write(nsIObjectOutputStream* aStream);
 
     // Override the nsIClassInfo method GetClassIDNoAlloc to make sure our
     // nsISerializable impl works right.
--- a/netwerk/base/src/nsSimpleURI.cpp
+++ b/netwerk/base/src/nsSimpleURI.cpp
@@ -339,16 +339,32 @@ nsSimpleURI::SetRef(const nsACString &re
 {
     // XXXdholbert Implement this.
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsSimpleURI::Equals(nsIURI* other, PRBool *result)
 {
+    return EqualsInternal(other, eHonorRef, result);
+}
+
+NS_IMETHODIMP
+nsSimpleURI::EqualsExceptRef(nsIURI* other, PRBool *result)
+{
+    return EqualsInternal(other, eIgnoreRef, result);
+}
+
+/* virtual */ nsresult
+nsSimpleURI::EqualsInternal(nsIURI* other,
+                            nsSimpleURI::RefHandlingEnum refHandlingMode,
+                            PRBool* result)
+{
+    // XXXdholbert Currently ignoring refHandlingMode.  I'll make use of it
+    // in next patch, when I add support for .ref to this class.
     PRBool eq = PR_FALSE;
     if (other) {
         nsSimpleURI* otherUrl;
         nsresult rv =
             other->QueryInterface(kThisSimpleURIImplementationCID,
                                   (void**)&otherUrl);
         if (NS_SUCCEEDED(rv)) {
             eq = PRBool((0 == strcmp(mScheme.get(), otherUrl->mScheme.get())) && 
--- a/netwerk/base/src/nsSimpleURI.h
+++ b/netwerk/base/src/nsSimpleURI.h
@@ -69,16 +69,28 @@ public:
     NS_DECL_NSIMUTABLE
 
     // nsSimpleURI methods:
 
     nsSimpleURI();
     virtual ~nsSimpleURI();
 
 protected:
+    // enum used in a few places to specify how .ref attribute should be handled
+    enum RefHandlingEnum {
+        eIgnoreRef,
+        eHonorRef
+    };
+
+    // Helper to share code between Equals methods.  Subclasses can override
+    // for custom Equals behavior.
+    virtual nsresult EqualsInternal(nsIURI* other,
+                                    RefHandlingEnum refHandlingMode,
+                                    PRBool* result);
+
     virtual nsSimpleURI* StartClone();
 
     nsCString mScheme;
     nsCString mPath;
     PRBool mMutable;
 };
 
 #endif // nsSimpleURI_h__
--- a/netwerk/base/src/nsStandardURL.cpp
+++ b/netwerk/base/src/nsStandardURL.cpp
@@ -1572,16 +1572,30 @@ nsStandardURL::SetPath(const nsACString 
         mRef.mLen = -1;
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStandardURL::Equals(nsIURI *unknownOther, PRBool *result)
 {
+    return EqualsInternal(unknownOther, eHonorRef, result);
+}
+
+NS_IMETHODIMP
+nsStandardURL::EqualsExceptRef(nsIURI *unknownOther, PRBool *result)
+{
+    return EqualsInternal(unknownOther, eIgnoreRef, result);
+}
+
+nsresult
+nsStandardURL::EqualsInternal(nsIURI *unknownOther,
+                              nsStandardURL::RefHandlingEnum refHandlingMode,
+                              PRBool *result)
+{
     NS_ENSURE_ARG_POINTER(unknownOther);
     NS_PRECONDITION(result, "null pointer");
 
     nsRefPtr<nsStandardURL> other;
     nsresult rv = unknownOther->QueryInterface(kThisImplCID,
                                                getter_AddRefs(other));
     if (NS_FAILED(rv)) {
         *result = PR_FALSE;
@@ -1597,26 +1611,31 @@ nsStandardURL::Equals(nsIURI *unknownOth
 
     // Next check parts of a URI that, if different, automatically make the
     // URIs different
     if (!SegmentIs(mScheme, other->mSpec.get(), other->mScheme) ||
         // Check for host manually, since conversion to file will
         // ignore the host!
         !SegmentIs(mHost, other->mSpec.get(), other->mHost) ||
         !SegmentIs(mQuery, other->mSpec.get(), other->mQuery) ||
-        !SegmentIs(mRef, other->mSpec.get(), other->mRef) ||
         !SegmentIs(mUsername, other->mSpec.get(), other->mUsername) ||
         !SegmentIs(mPassword, other->mSpec.get(), other->mPassword) ||
         Port() != other->Port() ||
         !SegmentIs(mParam, other->mSpec.get(), other->mParam)) {
         // No need to compare files or other URI parts -- these are different
         // beasties
         *result = PR_FALSE;
         return NS_OK;
     }
+
+    if (refHandlingMode == eHonorRef &&
+        !SegmentIs(mRef, other->mSpec.get(), other->mRef)) {
+        *result = PR_FALSE;
+        return NS_OK;
+    }
     
     // Then check for exact identity of URIs.  If we have it, they're equal
     if (SegmentIs(mDirectory, other->mSpec.get(), other->mDirectory) &&
         SegmentIs(mBasename, other->mSpec.get(), other->mBasename) &&
         SegmentIs(mExtension, other->mSpec.get(), other->mExtension)) {
         *result = PR_TRUE;
         return NS_OK;
     }
--- a/netwerk/base/src/nsStandardURL.h
+++ b/netwerk/base/src/nsStandardURL.h
@@ -148,16 +148,28 @@ public: /* internal -- HPUX compiler can
         
         const char* mCharset;  // Caller should keep this alive for
                                // the life of the segment encoder
         nsCOMPtr<nsIUnicodeEncoder> mEncoder;
     };
     friend class nsSegmentEncoder;
 
 protected:
+    // enum used in a few places to specify how .ref attribute should be handled
+    enum RefHandlingEnum {
+        eIgnoreRef,
+        eHonorRef
+    };
+
+    // Helper to share code between Equals and EqualsExceptRef
+    // NOTE: *not* virtual, because no one needs to override this so far...
+    nsresult EqualsInternal(nsIURI* unknownOther,
+                            RefHandlingEnum refHandlingMode,
+                            PRBool* result);
+
     virtual nsStandardURL* StartClone();
 
     // Helper for subclass implementation of GetFile().  Subclasses that map
     // URIs to files in a special way should implement this method.  It should
     // ensure that our mFile is initialized, if it's possible.
     // returns NS_ERROR_NO_INTERFACE if the url does not map to a file
     virtual nsresult EnsureFile();
 
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -129,16 +129,57 @@ function do_info(text, stack) {
   if (!stack)
     stack = Components.stack.caller;
 
   dump( "\n" +
        "TEST-INFO | " + stack.filename + " | [" + stack.name + " : " +
        stack.lineNumber + "] " + text + "\n");
 }
 
+// Checks that the URIs satisfy equals(), in both possible orderings.
+// Also checks URI.equalsExceptRef(), because equal URIs should also be equal
+// when we ignore the ref.
+// 
+// The third argument is optional. If the client passes a third argument
+// (e.g. todo_check_true), we'll use that in lieu of do_check_true.
+function do_check_uri_eq(aURI1, aURI2, aCheckTrueFunc) {
+  if (!aCheckTrueFunc) {
+    aCheckTrueFunc = do_check_true;
+  }
+
+  do_info("(uri equals check: '" + aURI1.spec + "' == '" + aURI2.spec + "')");
+  aCheckTrueFunc(aURI1.equals(aURI2));
+  do_info("(uri equals check: '" + aURI2.spec + "' == '" + aURI1.spec + "')");
+  aCheckTrueFunc(aURI2.equals(aURI1));
+
+  // (Only take the extra step of testing 'equalsExceptRef' when we expect the
+  // URIs to really be equal.  In 'todo' cases, the URIs may or may not be
+  // equal when refs are ignored - there's no way of knowing in general.)
+  if (aCheckTrueFunc == do_check_true) {
+    do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc);
+  }
+}
+
+// Checks that the URIs satisfy equalsExceptRef(), in both possible orderings.
+//
+// The third argument is optional. If the client passes a third argument
+// (e.g. todo_check_true), we'll use that in lieu of do_check_true.
+function do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc) {
+  if (!aCheckTrueFunc) {
+    aCheckTrueFunc = do_check_true;
+  }
+
+  do_info("(uri equalsExceptRef check: '" +
+          aURI1.spec + "' == '" + aURI2.spec + "')");
+  aCheckTrueFunc(aURI1.equalsExceptRef(aURI2));
+  do_info("(uri equalsExceptRef check: '" +
+          aURI2.spec + "' == '" + aURI1.spec + "')");
+  aCheckTrueFunc(aURI2.equalsExceptRef(aURI1));
+}
+
 // Checks that the given property on aURI matches the corresponding property
 // in the test bundle (or matches some function of that corresponding property,
 // if aTestFunctor is passed in).
 function do_check_property(aTest, aURI, aPropertyName, aTestFunctor) {
   var expectedVal = aTestFunctor ?
     aTestFunctor(aTest[aPropertyName]) :
     aTest[aPropertyName];
 
@@ -149,17 +190,17 @@ function do_check_property(aTest, aURI, 
 }
 
 // Test that a given URI parses correctly into its various components.
 function do_test_uri_basic(aTest) {
   var URI = NetUtil.newURI(aTest.spec);
 
   // Sanity-check
   do_info("testing " + aTest.spec + " equals a clone of itself");
-  do_check_true(URI.equals(URI.clone()));
+  do_check_uri_eq(URI, URI.clone());
   do_info("testing " + aTest.spec + " instanceof nsIURL");
   do_check_eq(URI instanceof Ci.nsIURL, aTest.nsIURL);
   do_info("testing " + aTest.spec + " instanceof nsINestedURI");
   do_check_eq(URI instanceof Ci.nsINestedURI,
               aTest.nsINestedURI);
 
   // Check the various components
   do_check_property(aTest, URI, "scheme");
@@ -175,21 +216,20 @@ function do_test_uri_basic(aTest) {
 }
 
 // Test that a given URI parses correctly when we add a given ref to the end
 function do_test_uri_with_hash_suffix(aTest, aSuffix) {
   do_info("making sure caller is using suffix that starts with '#'");
   do_check_eq(aSuffix[0], "#");
 
   var testURI = NetUtil.newURI(aTest.spec + aSuffix);
+  var origURI = NetUtil.newURI(aTest.spec);
 
   do_info("testing " + aTest.spec +
           " doesn't equal self with '" + aSuffix + "' appended");
-
-  var origURI = NetUtil.newURI(aTest.spec);
   if (aTest.spec == "file://") {
     do_info("TODO: bug 656853");
     todo_check_false(origURI.equals(testURI));
     return;  // bail out early since file:// doesn't handle hash refs at all
   }
 
   do_check_false(origURI.equals(testURI));
 
@@ -199,16 +239,20 @@ function do_test_uri_with_hash_suffix(aT
                     function(aStr) { return aStr + aSuffix; });
 
   // XXXdholbert Only URLs (not URIs) support 'ref', until bug 308590's fix
   // lands, so the following only checks 'ref' on URLs.
   if (aTest.nsIURL) {
     var URL = testURI.QueryInterface(Ci.nsIURL);
     do_check_property(aTest, URL, "ref",
                       function(aStr) { return aSuffix.substr(1); });
+
+    do_info("testing " + aTest.spec +
+          " is equalExceptRef to self with '" + aSuffix + "' appended");
+    do_check_uri_eqExceptRef(origURI, testURI);
   }
 }
 
 // Tests various ways of setting & clearing a ref on a URI.
 function do_test_mutate_ref(aTest, aSuffix) {
   do_info("making sure caller is using suffix that starts with '#'");
   do_check_eq(aSuffix[0], "#");
 
@@ -221,66 +265,72 @@ function do_test_mutate_ref(aTest, aSuff
   var refURIWithSuffix    = NetUtil.newURI(aTest.spec + aSuffix).QueryInterface(Ci.nsIURL);
   var refURIWithoutSuffix = NetUtil.newURI(aTest.spec).QueryInterface(Ci.nsIURL);
 
   var testURI             = NetUtil.newURI(aTest.spec).QueryInterface(Ci.nsIURL);
 
   if (aTest.spec == "file://") {
     do_info("TODO: bug 656853");
     testURI.ref = aSuffix;
-    todo_check_true(testURI.equals(refURIWithSuffix));
+    do_check_uri_eq(testURI, refURIWithSuffix, todo_check_true);
     return; // bail out early since file:// doesn't handle hash refs at all
   }
 
   // First: Try setting .ref to our suffix
   do_info("testing that setting .ref on " + aTest.spec +
           " to '" + aSuffix + "' does what we expect");
   testURI.ref = aSuffix;
-  do_check_true(testURI.equals(refURIWithSuffix));
+  do_check_uri_eq(testURI, refURIWithSuffix);
+  do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
 
   // Now try setting .ref but leave off the initial hash (expect same result)
   var suffixLackingHash = aSuffix.substr(1);
   if (suffixLackingHash) { // (skip this our suffix was *just* a #)
     do_info("testing that setting .ref on " + aTest.spec +
             " to '" + suffixLackingHash + "' does what we expect");
     testURI.ref = suffixLackingHash;
-    do_check_true(testURI.equals(refURIWithSuffix));
+    do_check_uri_eq(testURI, refURIWithSuffix);
+    do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
   }
 
   // Now, clear .ref (should get us back the original spec)
   do_info("testing that clearing .ref on " + testURI.spec +
           " does what we expect");
   testURI.ref = "";
-  do_check_true(testURI.equals(refURIWithoutSuffix));
+  do_check_uri_eq(testURI, refURIWithoutSuffix);
+  do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
   // Now try setting .spec directly (including suffix) and then clearing .ref
   var specWithSuffix = aTest.spec + aSuffix;
   do_info("testing that setting spec to " +
           specWithSuffix + " and then clearing ref does what we expect");
   testURI.spec = specWithSuffix
   testURI.ref = "";
   if (aTest.spec == "http://" && aSuffix == "#") {
     do_info("TODO: bug 657033");
-    todo_check_true(testURI.equals(refURIWithoutSuffix));
+    do_check_uri_eq(testURI, refURIWithoutSuffix, todo_check_true);
+    do_check_uri_eqExceptRef(testURI, refURIWithSuffix, todo_check_true);
   } else {
-    do_check_true(testURI.equals(refURIWithoutSuffix));
+    do_check_uri_eq(testURI, refURIWithoutSuffix);
+    do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
   }
 
   // XXX nsIJARURI throws an exception in SetPath(), so skip it for next part.
   if (!(testURI instanceof Ci.nsIJARURI)) {
     // Now try setting .path directly (including suffix) and then clearing .ref
     // (same as above, but with now with .path instead of .spec)
     testURI = NetUtil.newURI(aTest.spec).QueryInterface(Ci.nsIURL);
 
     var pathWithSuffix = aTest.path + aSuffix;
     do_info("testing that setting path to " +
             pathWithSuffix + " and then clearing ref does what we expect");
     testURI.path = pathWithSuffix;
     testURI.ref = "";
-    do_check_true(testURI.equals(refURIWithoutSuffix));
+    do_check_uri_eq(testURI, refURIWithoutSuffix);
+    do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
 
     // Also: make sure that clearing .path also clears .ref
     testURI.path = pathWithSuffix;
     do_info("testing that clearing path from " + 
             pathWithSuffix + " also clears .ref");
     testURI.path = "";
     do_check_eq(testURI.ref, "");
   }