From 2f1a9c3fed92aa4ecf36455d9e940b0c4c3f1ff9 Mon Sep 17 00:00:00 2001 From: "ccarlen%netscape.com" Date: Tue, 17 Jun 2003 13:55:47 +0000 Subject: [PATCH] Bug 197379 - file:// URLs using HFS paths don't work with Mach-O Mozilla. Creating a new version of nsURLHelper for OSX which handles this. Also, some correctness and commenting of corresponding code in nsURLHelperUnix.cpp. r=alecf/sr=sfraser git-svn-id: svn://10.0.0.236/trunk@143792 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/netwerk/base/src/Makefile.in | 4 + mozilla/netwerk/base/src/nsURLHelperOSX.cpp | 244 +++++++++++++++++++ mozilla/netwerk/base/src/nsURLHelperUnix.cpp | 17 +- 3 files changed, 258 insertions(+), 7 deletions(-) create mode 100644 mozilla/netwerk/base/src/nsURLHelperOSX.cpp diff --git a/mozilla/netwerk/base/src/Makefile.in b/mozilla/netwerk/base/src/Makefile.in index 58b079dfeb8..884d4fc198b 100644 --- a/mozilla/netwerk/base/src/Makefile.in +++ b/mozilla/netwerk/base/src/Makefile.in @@ -76,10 +76,14 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) CPPSRCS += nsURLHelperWin.cpp CPPSRCS += nsNativeConnectionHelper.cpp CPPSRCS += nsAutodialWin.cpp +else +ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) + CPPSRCS += nsURLHelperOSX.cpp else CPPSRCS += nsURLHelperUnix.cpp endif endif +endif EXTRA_COMPONENTS = \ $(srcdir)/nsProxyAutoConfig.js \ diff --git a/mozilla/netwerk/base/src/nsURLHelperOSX.cpp b/mozilla/netwerk/base/src/nsURLHelperOSX.cpp new file mode 100644 index 00000000000..63b43ebd5ef --- /dev/null +++ b/mozilla/netwerk/base/src/nsURLHelperOSX.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape 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/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alec Flett + * Darin Fisher + * Conrad Carlen + * + * 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 NPL, 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 NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* Unix-specific local file uri parsing */ +#include "nsURLHelper.h" +#include "nsEscape.h" +#include "nsILocalFile.h" +#include "nsVoidArray.h" +#include "nsReadableUtils.h" +#include + +static PRBool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent) +{ + // Return whether the 1st path component in the given path is equal to the name + // of a mounted volume. Return the 1st path component in any case. + // This needs to be done as quickly as possible, so we cache a list of volume names. + // XXX Register an event handler to detect drives being mounted/unmounted? + + static nsCStringArray gVolumeList; // We will leak this - one for the life of the app :-/ + + // Cache a list of volume names + if (!gVolumeList.Count()) { + OSErr err; + ItemCount volumeIndex = 1; + + do { + HFSUniStr255 volName; + FSRef rootDirectory; + err = ::FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, &volName, &rootDirectory); + if (err == noErr) { + nsCString volNameStr = NS_ConvertUCS2toUTF8(Substring((PRUnichar *)volName.unicode, + (PRUnichar *)volName.unicode + volName.length)); + gVolumeList.AppendCString(volNameStr); + volumeIndex++; + } + } while (err == noErr); + } + + // Extract the first component of the path + nsACString::const_iterator start; + path.BeginReading(start); + start.advance(1); // path begins with '/' + nsACString::const_iterator directory_end; + path.EndReading(directory_end); + nsACString::const_iterator component_end(start); + FindCharInReadable('/', component_end, directory_end); + + firstPathComponent = Substring(start, component_end); + PRInt32 foundIndex = gVolumeList.IndexOf(nsCAutoString(firstPathComponent)); + return (foundIndex != -1); +} + +static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath) +{ + // Use CFURL to do the conversion. We don't want to do this by simply + // using SwapSlashColon - we need the charset mapped from MacRoman + // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change) + // prepended if the path is not on the boot drive. + + CFStringRef pathStrRef = CFStringCreateWithCString(NULL, + PromiseFlatCString(hfsPath).get(), + kCFStringEncodingMacRoman); + if (!pathStrRef) + return NS_ERROR_FAILURE; + + nsresult rv = NS_ERROR_FAILURE; + CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, + pathStrRef, kCFURLHFSPathStyle, true); + if (urlRef) { + UInt8 pathBuf[PATH_MAX]; + if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) { + posixPath = (char *)pathBuf; + rv = NS_OK; + } + } + CFRelease(pathStrRef); + if (urlRef) + CFRelease(urlRef); + return rv; +} + +static void SwapSlashColon(char *s) +{ + while (*s) { + if (*s == '/') + *s = ':'; + else if (*s == ':') + *s = '/'; + s++; + } +} + +nsresult +net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result) +{ + // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp + + nsresult rv; + nsCAutoString ePath; + + // construct URL spec from native file path + rv = aFile->GetNativePath(ePath); + if (NS_FAILED(rv)) + return rv; + + nsCAutoString escPath; + NS_NAMED_LITERAL_CSTRING(prefix, "file://"); + + // Escape the path with the directory mask + if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath)) + escPath.Insert(prefix, 0); + else + escPath.Assign(prefix + ePath); + + // esc_Directory does not escape the semicolons, so if a filename + // contains semicolons we need to manually escape them. + escPath.ReplaceSubstring(";", "%3b"); + + // XXX this should be unnecessary + if (escPath.Last() != '/') { + PRBool dir; + rv = aFile->IsDirectory(&dir); + if (NS_FAILED(rv)) + NS_WARNING(PromiseFlatCString( + NS_LITERAL_CSTRING("Cannot tell if ") + escPath + + NS_LITERAL_CSTRING(" is a directory or file")).get()); + else if (dir) { + // make sure we have a trailing slash + escPath += "/"; + } + } + + result = escPath; + return NS_OK; +} + +nsresult +net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result) +{ + // NOTE: See also the implementation in nsURLHelperUnix.cpp + // This matches it except for the HFS path handling. + + nsresult rv; + + nsCOMPtr localFile; + rv = NS_NewNativeLocalFile(nsCString(), PR_TRUE, getter_AddRefs(localFile)); + if (NS_FAILED(rv)) + return rv; + + nsCAutoString directory, fileBaseName, fileExtension, path; + PRBool bHFSPath = PR_FALSE; + + rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension); + if (NS_FAILED(rv)) + return rv; + + if (!directory.IsEmpty()) { + // The canonical form of file URLs on OSX use POSIX paths: + // file:///path-name. + // But, we still encounter file URLs that use HFS paths: + // file:///volume-name/path-name + // Determine that here and normalize HFS paths to POSIX. + nsCAutoString possibleVolName; + if (pathBeginsWithVolName(directory, possibleVolName)) { + // Though we know it begins with a volume name, it could still + // be a valid POSIX path if the boot drive is named "Mac HD" + // and there is a directory "Mac HD" at its root. If such a + // directory doesn't exist, we'll assume this is an HFS path. + FSRef testRef; + possibleVolName.Insert("/", 0); + if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nsnull) != noErr) + bHFSPath = PR_TRUE; + } + + NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path); + + if (bHFSPath) { + // "%2F"s need to become slashes, while all other slashes need to + // become colons. If we start out by changing "%2F"s to colons, we + // can reply on SwapSlashColon() to do what we need + path.ReplaceSubstring("%2F", ":"); + path.Cut(0, 1); // directory begins with '/' + SwapSlashColon((char *)path.get()); + // At this point, path is an HFS path made using the same + // algorithm as nsURLHelperMac. We'll convert to POSIX below. + } + } + if (!fileBaseName.IsEmpty()) + NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path); + if (!fileExtension.IsEmpty()) { + path += '.'; + NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path); + } + + NS_UnescapeURL(path); + + if (bHFSPath) + convertHFSPathtoPOSIX(path, path); + + // assuming path is encoded in the native charset + rv = localFile->InitWithNativePath(path); + if (NS_FAILED(rv)) + return rv; + + NS_ADDREF(*result = localFile); + return NS_OK; +} diff --git a/mozilla/netwerk/base/src/nsURLHelperUnix.cpp b/mozilla/netwerk/base/src/nsURLHelperUnix.cpp index fd93eac5bdd..fa340a4d2ed 100644 --- a/mozilla/netwerk/base/src/nsURLHelperUnix.cpp +++ b/mozilla/netwerk/base/src/nsURLHelperUnix.cpp @@ -45,6 +45,8 @@ nsresult net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result) { + // NOTE: This is identical to the implementation in nsURLHelperOSX.cpp + nsresult rv; nsCAutoString ePath; @@ -66,7 +68,7 @@ net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result) escPath.ReplaceSubstring(";", "%3b"); // XXX this should be unnecessary - if (escPath[escPath.Length() - 1] != '/') { + if (escPath.Last() != '/') { PRBool dir; rv = aFile->IsDirectory(&dir); if (NS_FAILED(rv)) @@ -86,14 +88,15 @@ net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result) nsresult net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result) { + // NOTE: See also the implementation in nsURLHelperOSX.cpp, + // which is based on this. + nsresult rv; - nsCOMPtr localFile( - do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); - if (NS_FAILED(rv)) { - NS_ERROR("Only nsILocalFile supported right now"); - return rv; - } + nsCOMPtr localFile; + rv = NS_NewNativeLocalFile(nsCString(), PR_TRUE, getter_AddRefs(localFile)); + if (NS_FAILED(rv)) + return rv; nsCAutoString directory, fileBaseName, fileExtension, path;