Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsURLHelperOSX.cpp
blob1324716648a19d526194168ef5fb6956b5dd2b90
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
14 * License.
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.
23 * Contributor(s):
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"
44 #include "nsEscape.h"
45 #include "nsILocalFile.h"
46 #include "nsVoidArray.h"
47 #include "nsReadableUtils.h"
48 #include <Files.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?
59 if (!gVolumeList) {
60 gVolumeList = new nsCStringArray;
61 if (!gVolumeList) {
62 return PR_FALSE; // out of memory
66 // Cache a list of volume names
67 if (!gVolumeList->Count()) {
68 OSErr err;
69 ItemCount volumeIndex = 1;
71 do {
72 HFSUniStr255 volName;
73 FSRef rootDirectory;
74 err = ::FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, &volName, &rootDirectory);
75 if (err == noErr) {
76 NS_ConvertUTF16toUTF8 volNameStr(Substring((PRUnichar *)volName.unicode,
77 (PRUnichar *)volName.unicode + volName.length));
78 gVolumeList->AppendCString(volNameStr);
79 volumeIndex++;
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);
100 void
101 net_ShutdownURLHelperOSX()
103 delete gVolumeList;
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);
117 if (!pathStrRef)
118 return NS_ERROR_FAILURE;
120 nsresult rv = NS_ERROR_FAILURE;
121 CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL,
122 pathStrRef, kCFURLHFSPathStyle, true);
123 if (urlRef) {
124 UInt8 pathBuf[PATH_MAX];
125 if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) {
126 posixPath = (char *)pathBuf;
127 rv = NS_OK;
130 CFRelease(pathStrRef);
131 if (urlRef)
132 CFRelease(urlRef);
133 return rv;
136 static void SwapSlashColon(char *s)
138 while (*s) {
139 if (*s == '/')
140 *s = ':';
141 else if (*s == ':')
142 *s = '/';
143 s++;
147 nsresult
148 net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result)
150 // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp
152 nsresult rv;
153 nsCAutoString ePath;
155 // construct URL spec from native file path
156 rv = aFile->GetNativePath(ePath);
157 if (NS_FAILED(rv))
158 return rv;
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);
166 else
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() != '/') {
179 PRBool dir;
180 rv = aFile->IsDirectory(&dir);
181 if (NS_SUCCEEDED(rv) && dir)
182 escPath += '/';
185 result = escPath;
186 return NS_OK;
189 nsresult
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.
195 nsresult rv;
197 nsCOMPtr<nsILocalFile> localFile;
198 rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(localFile));
199 if (NS_FAILED(rv))
200 return rv;
202 nsCAutoString directory, fileBaseName, fileExtension, path;
203 PRBool bHFSPath = PR_FALSE;
205 rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension);
206 if (NS_FAILED(rv))
207 return rv;
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.
223 FSRef testRef;
224 possibleVolName.Insert("/", 0);
225 if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nsnull) != noErr)
226 bHFSPath = PR_TRUE;
229 if (bHFSPath) {
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()) {
243 path += '.';
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;
251 if (bHFSPath)
252 convertHFSPathtoPOSIX(path, path);
254 // assuming path is encoded in the native charset
255 rv = localFile->InitWithNativePath(path);
256 if (NS_FAILED(rv))
257 return rv;
259 NS_ADDREF(*result = localFile);
260 return NS_OK;