Use the same path comparison behavior for nsIFile::Equals on all platforms. b=491245 sr=bsmedberg
authorJosh Aas <joshmoz@gmail.com>
Wed, 08 Jul 2009 11:26:07 -0400
changeset 30071 51bafb458d68eaed953d4d839be8ae839f76b221
parent 30070 96b06aeae4d7b7c6864b26e5c70fb6844d870d84
child 30072 dfa5809bb978da23825c03efd6297a2311927220
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs491245
milestone1.9.2a1pre
Use the same path comparison behavior for nsIFile::Equals on all platforms. b=491245 sr=bsmedberg
xpcom/io/nsLocalFileOSX.h
xpcom/io/nsLocalFileOSX.mm
xpcom/io/nsLocalFileUnix.cpp
xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
xpcom/tests/unit/test_file_equality.js
--- a/xpcom/io/nsLocalFileOSX.h
+++ b/xpcom/io/nsLocalFileOSX.h
@@ -95,17 +95,16 @@ private:
 
 protected:
   nsLocalFile(const nsLocalFile& src);
 
   nsresult SetBaseURL(CFURLRef aCFURLRef); // retains aCFURLRef
 
   nsresult GetFSRefInternal(FSRef& aFSRef);
   nsresult GetPathInternal(nsACString& path); // Returns path WRT mFollowLinks
-  nsresult EqualsInternal(nsISupports* inFile, PRBool *_retval);
   nsresult CopyInternal(nsIFile* newParentDir,
                         const nsAString& newName,
                         PRBool followLinks);
   nsresult FillStatBufferInternal(struct STAT *statBuffer);
 
   static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr);
 
 protected:
--- a/xpcom/io/nsLocalFileOSX.mm
+++ b/xpcom/io/nsLocalFileOSX.mm
@@ -1138,52 +1138,31 @@ NS_IMETHODIMP nsLocalFile::Clone(nsIFile
 
     NS_ADDREF(*_retval);
     
     return NS_OK;
 }
 
 NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
 {
-  return EqualsInternal(inFile, _retval);
-}
-
-nsresult
-nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool *_retval)
-{
+  NS_ENSURE_ARG(inFile);
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
-  
-  nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile));
-  if (!inFile)
-    return NS_OK;
-    
-  nsLocalFile* inLF =
-      static_cast<nsLocalFile*>((nsILocalFileMac*) inMacFile);
+
+  nsCAutoString inPath;
+  nsresult rv = inFile->GetNativePath(inPath);
+  if (NS_FAILED(rv))
+    return rv;
 
-  // If both exist, compare FSRefs
-  FSRef thisFSRef, inFSRef;
-  nsresult rv1 = GetFSRefInternal(thisFSRef);
-  nsresult rv2 = inLF->GetFSRefInternal(inFSRef);
-  if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
-    *_retval = (thisFSRef == inFSRef);
-    return NS_OK;
-  }
-  // If one exists and the other doesn't, not equal  
-  if (rv1 != rv2)
-    return NS_OK;
-    
-  // Arg, we have to get their paths and compare
-  nsCAutoString thisPath, inPath;
-  if (NS_FAILED(GetNativePath(thisPath)))
-    return NS_ERROR_FAILURE;
-  if (NS_FAILED(inMacFile->GetNativePath(inPath)))
-    return NS_ERROR_FAILURE;
-  *_retval = thisPath.Equals(inPath);
-  
+  nsCAutoString thisPath;
+  rv = GetNativePath(thisPath);
+  if (NS_FAILED(rv))
+    return rv;
+
+  *_retval = !strcmp(inPath.get(), thisPath.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
 {
   // Check we are correctly initialized.
   CHECK_mBaseURL();
 
@@ -2145,17 +2124,23 @@ nsresult nsLocalFile::CFStringReftoUTF8(
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 // nsIHashable
 
 NS_IMETHODIMP
 nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult)
 {
-  return EqualsInternal(aOther, aResult);
+  nsCOMPtr<nsIFile> otherFile(do_QueryInterface(aOther));
+  if (!otherFile) {
+    *aResult = PR_FALSE;
+    return NS_OK;
+  }
+
+  return Equals(otherFile, aResult);
 }
 
 NS_IMETHODIMP
 nsLocalFile::GetHashCode(PRUint32 *aResult)
 {
     NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
     CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseURL, kCFURLPOSIXPathStyle);
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -84,26 +84,16 @@
 
 #ifdef MOZ_WIDGET_GTK2
 #include "nsIGnomeVFSService.h"
 #endif
 
 #include "nsNativeCharsetUtils.h"
 #include "nsTraceRefcntImpl.h"
 
-// On some platforms file/directory name comparisons need to
-// be case-blind.
-#if defined(VMS)
-    #define FILE_STRCMP strcasecmp
-    #define FILE_STRNCMP strncasecmp
-#else
-    #define FILE_STRCMP strcmp
-    #define FILE_STRNCMP strncmp
-#endif
-
 #define ENSURE_STAT_CACHE()                     \
     PR_BEGIN_MACRO                              \
         if (!FillStatCache())                   \
              return NSRESULT_FOR_ERRNO();       \
     PR_END_MACRO
 
 #define CHECK_mPath()                           \
     PR_BEGIN_MACRO                              \
@@ -1402,23 +1392,24 @@ nsLocalFile::IsSpecial(PRBool *_retval)
 
 NS_IMETHODIMP
 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
 {
     NS_ENSURE_ARG(inFile);
     NS_ENSURE_ARG_POINTER(_retval);
     *_retval = PR_FALSE;
 
-    nsresult rv;
     nsCAutoString inPath;
-
-    if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
+    nsresult rv = inFile->GetNativePath(inPath);
+    if (NS_FAILED(rv))
         return rv;
 
-    *_retval = !FILE_STRCMP(inPath.get(), mPath.get());
+    // We don't need to worry about "/foo/" vs. "/foo" here
+    // because trailing slashes are stripped on init.
+    *_retval = !strcmp(inPath.get(), mPath.get());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
 {
     CHECK_mPath();
     NS_ENSURE_ARG(inFile);
@@ -1428,17 +1419,17 @@ nsLocalFile::Contains(nsIFile *inFile, P
     nsresult rv;
 
     if (NS_FAILED(rv = inFile->GetNativePath(inPath)))
         return rv;
 
     *_retval = PR_FALSE;
 
     ssize_t len = mPath.Length();
-    if (FILE_STRNCMP(mPath.get(), inPath.get(), len) == 0) {
+    if (strncmp(mPath.get(), inPath.get(), len) == 0) {
         // Now make sure that the |inFile|'s path has a separator at len,
         // which implies that it has more components after len.
         if (inPath[len] == '/')
             *_retval = PR_TRUE;
     }
 
     return NS_OK;
 }
--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
@@ -628,18 +628,22 @@ IndexOfDirectoryOfFile(nsISupportsArray*
         aSearchPath->Count(&count);
         NS_ASSERTION(count, "broken search path! bad count");
         for(PRUint32 i = 0; i < count; i++)
         {
             nsCOMPtr<nsIFile> current;
             aSearchPath->QueryElementAt(i, NS_GET_IID(nsIFile), 
                                         getter_AddRefs(current));
             NS_ASSERTION(current, "broken search path! bad element");
+            // nsIFile::Equals basically compares path strings so normalize
+            // before the comparison.
+            parent->Normalize();
+            current->Normalize();
             PRBool same;
-            if(NS_SUCCEEDED(parent->Equals(current, &same)) && same)
+            if (NS_SUCCEEDED(parent->Equals(current, &same)) && same)
                 return (int) i;
         }
     }
     NS_ERROR("file not in search directory!");
     return -1;
 }        
 
 struct SortData
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/unit/test_file_equality.js
@@ -0,0 +1,71 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is XPCOM unit tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Josh Aas <josh@mozilla.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cr = Components.results;
+const Ci = Components.interfaces;
+
+const CC = Components.Constructor;
+var LocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");
+
+function run_test()
+{
+  test_normalized_vs_non_normalized();
+}
+
+function test_normalized_vs_non_normalized()
+{
+  // get a directory that exists on all platforms
+  var dirProvider = Components.classes["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  var tmp1 = dirProvider.get("TmpD", Ci.nsILocalFile);
+  var exists = tmp1.exists();
+  do_check_true(exists);
+  if (!exists)
+    return;
+
+  // this has the same exact path as tmp1, it should equal tmp1
+  var tmp2 = new LocalFile(tmp1.path);
+  do_check_true(tmp1.equals(tmp2));
+
+  // this is a non-normalized version of tmp1, it should not equal tmp1
+  tmp2.appendRelativePath(".");
+  do_check_false(tmp1.equals(tmp2));
+
+  // normalize and make sure they are equivalent again
+  tmp2.normalize();
+  do_check_true(tmp1.equals(tmp2));
+}