1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 et cindent: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Alec Flett <alecf@netscape.com>
25 * Darin Fisher <darin@netscape.com>
26 * Conrad Carlen <ccarlen@netscape.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 /* Mac OS X-specific local file uri parsing */
43 #include "nsURLHelper.h"
45 #include "nsILocalFile.h"
46 #include "nsVoidArray.h"
47 #include "nsReadableUtils.h"
50 static nsCStringArray
*gVolumeList
= nsnull
;
52 static PRBool
pathBeginsWithVolName(const nsACString
& path
, nsACString
& firstPathComponent
)
54 // Return whether the 1st path component in path (escaped) is equal to the name
55 // of a mounted volume. Return the 1st path component (unescaped) in any case.
56 // This needs to be done as quickly as possible, so we cache a list of volume names.
57 // XXX Register an event handler to detect drives being mounted/unmounted?
60 gVolumeList
= new nsCStringArray
;
62 return PR_FALSE
; // out of memory
66 // Cache a list of volume names
67 if (!gVolumeList
->Count()) {
69 ItemCount volumeIndex
= 1;
74 err
= ::FSGetVolumeInfo(0, volumeIndex
, NULL
, kFSVolInfoNone
, NULL
, &volName
, &rootDirectory
);
76 NS_ConvertUTF16toUTF8
volNameStr(Substring((PRUnichar
*)volName
.unicode
,
77 (PRUnichar
*)volName
.unicode
+ volName
.length
));
78 gVolumeList
->AppendCString(volNameStr
);
81 } while (err
== noErr
);
84 // Extract the first component of the path
85 nsACString::const_iterator start
;
86 path
.BeginReading(start
);
87 start
.advance(1); // path begins with '/'
88 nsACString::const_iterator directory_end
;
89 path
.EndReading(directory_end
);
90 nsACString::const_iterator
component_end(start
);
91 FindCharInReadable('/', component_end
, directory_end
);
93 nsCAutoString
flatComponent((Substring(start
, component_end
)));
94 NS_UnescapeURL(flatComponent
);
95 PRInt32 foundIndex
= gVolumeList
->IndexOf(flatComponent
);
96 firstPathComponent
= flatComponent
;
97 return (foundIndex
!= -1);
101 net_ShutdownURLHelperOSX()
104 gVolumeList
= nsnull
;
107 static nsresult
convertHFSPathtoPOSIX(const nsACString
& hfsPath
, nsACString
& posixPath
)
109 // Use CFURL to do the conversion. We don't want to do this by simply
110 // using SwapSlashColon - we need the charset mapped from MacRoman
111 // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change)
112 // prepended if the path is not on the boot drive.
114 CFStringRef pathStrRef
= CFStringCreateWithCString(NULL
,
115 PromiseFlatCString(hfsPath
).get(),
116 kCFStringEncodingMacRoman
);
118 return NS_ERROR_FAILURE
;
120 nsresult rv
= NS_ERROR_FAILURE
;
121 CFURLRef urlRef
= CFURLCreateWithFileSystemPath(NULL
,
122 pathStrRef
, kCFURLHFSPathStyle
, true);
124 UInt8 pathBuf
[PATH_MAX
];
125 if (CFURLGetFileSystemRepresentation(urlRef
, true, pathBuf
, sizeof(pathBuf
))) {
126 posixPath
= (char *)pathBuf
;
130 CFRelease(pathStrRef
);
136 static void SwapSlashColon(char *s
)
148 net_GetURLSpecFromFile(nsIFile
*aFile
, nsACString
&result
)
150 // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp
155 // construct URL spec from native file path
156 rv
= aFile
->GetNativePath(ePath
);
160 nsCAutoString escPath
;
161 NS_NAMED_LITERAL_CSTRING(prefix
, "file://");
163 // Escape the path with the directory mask
164 if (NS_EscapeURL(ePath
.get(), ePath
.Length(), esc_Directory
+esc_Forced
, escPath
))
165 escPath
.Insert(prefix
, 0);
167 escPath
.Assign(prefix
+ ePath
);
169 // esc_Directory does not escape the semicolons, so if a filename
170 // contains semicolons we need to manually escape them.
171 escPath
.ReplaceSubstring(";", "%3b");
173 // if this file references a directory, then we need to ensure that the
174 // URL ends with a slash. this is important since it affects the rules
175 // for relative URL resolution when this URL is used as a base URL.
176 // if the file does not exist, then we make no assumption about its type,
177 // and simply leave the URL unmodified.
178 if (escPath
.Last() != '/') {
180 rv
= aFile
->IsDirectory(&dir
);
181 if (NS_SUCCEEDED(rv
) && dir
)
190 net_GetFileFromURLSpec(const nsACString
&aURL
, nsIFile
**result
)
192 // NOTE: See also the implementation in nsURLHelperUnix.cpp
193 // This matches it except for the HFS path handling.
197 nsCOMPtr
<nsILocalFile
> localFile
;
198 rv
= NS_NewNativeLocalFile(EmptyCString(), PR_TRUE
, getter_AddRefs(localFile
));
202 nsCAutoString directory
, fileBaseName
, fileExtension
, path
;
203 PRBool bHFSPath
= PR_FALSE
;
205 rv
= net_ParseFileURL(aURL
, directory
, fileBaseName
, fileExtension
);
209 if (!directory
.IsEmpty()) {
210 NS_EscapeURL(directory
, esc_Directory
|esc_AlwaysCopy
, path
);
212 // The canonical form of file URLs on OSX use POSIX paths:
213 // file:///path-name.
214 // But, we still encounter file URLs that use HFS paths:
215 // file:///volume-name/path-name
216 // Determine that here and normalize HFS paths to POSIX.
217 nsCAutoString possibleVolName
;
218 if (pathBeginsWithVolName(directory
, possibleVolName
)) {
219 // Though we know it begins with a volume name, it could still
220 // be a valid POSIX path if the boot drive is named "Mac HD"
221 // and there is a directory "Mac HD" at its root. If such a
222 // directory doesn't exist, we'll assume this is an HFS path.
224 possibleVolName
.Insert("/", 0);
225 if (::FSPathMakeRef((UInt8
*)possibleVolName
.get(), &testRef
, nsnull
) != noErr
)
230 // "%2F"s need to become slashes, while all other slashes need to
231 // become colons. If we start out by changing "%2F"s to colons, we
232 // can reply on SwapSlashColon() to do what we need
233 path
.ReplaceSubstring("%2F", ":");
234 path
.Cut(0, 1); // directory begins with '/'
235 SwapSlashColon((char *)path
.get());
236 // At this point, path is an HFS path made using the same
237 // algorithm as nsURLHelperMac. We'll convert to POSIX below.
240 if (!fileBaseName
.IsEmpty())
241 NS_EscapeURL(fileBaseName
, esc_FileBaseName
|esc_AlwaysCopy
, path
);
242 if (!fileExtension
.IsEmpty()) {
244 NS_EscapeURL(fileExtension
, esc_FileExtension
|esc_AlwaysCopy
, path
);
247 NS_UnescapeURL(path
);
248 if (path
.Length() != strlen(path
.get()))
249 return NS_ERROR_FILE_INVALID_PATH
;
252 convertHFSPathtoPOSIX(path
, path
);
254 // assuming path is encoded in the native charset
255 rv
= localFile
->InitWithNativePath(path
);
259 NS_ADDREF(*result
= localFile
);