netwerk/base/src/nsURLHelperOSX.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:af8f21ab0597
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* Mac OS X-specific local file uri parsing */
8 #include "nsURLHelper.h"
9 #include "nsEscape.h"
10 #include "nsIFile.h"
11 #include "nsTArray.h"
12 #include "nsReadableUtils.h"
13 #include <Carbon/Carbon.h>
14
15 static nsTArray<nsCString> *gVolumeList = nullptr;
16
17 static bool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent)
18 {
19 // Return whether the 1st path component in path (escaped) is equal to the name
20 // of a mounted volume. Return the 1st path component (unescaped) in any case.
21 // This needs to be done as quickly as possible, so we cache a list of volume names.
22 // XXX Register an event handler to detect drives being mounted/unmounted?
23
24 if (!gVolumeList) {
25 gVolumeList = new nsTArray<nsCString>;
26 if (!gVolumeList) {
27 return false; // out of memory
28 }
29 }
30
31 // Cache a list of volume names
32 if (!gVolumeList->Length()) {
33 OSErr err;
34 ItemCount volumeIndex = 1;
35
36 do {
37 HFSUniStr255 volName;
38 FSRef rootDirectory;
39 err = ::FSGetVolumeInfo(0, volumeIndex, nullptr, kFSVolInfoNone, nullptr,
40 &volName, &rootDirectory);
41 if (err == noErr) {
42 NS_ConvertUTF16toUTF8 volNameStr(Substring((char16_t *)volName.unicode,
43 (char16_t *)volName.unicode + volName.length));
44 gVolumeList->AppendElement(volNameStr);
45 volumeIndex++;
46 }
47 } while (err == noErr);
48 }
49
50 // Extract the first component of the path
51 nsACString::const_iterator start;
52 path.BeginReading(start);
53 start.advance(1); // path begins with '/'
54 nsACString::const_iterator directory_end;
55 path.EndReading(directory_end);
56 nsACString::const_iterator component_end(start);
57 FindCharInReadable('/', component_end, directory_end);
58
59 nsAutoCString flatComponent((Substring(start, component_end)));
60 NS_UnescapeURL(flatComponent);
61 int32_t foundIndex = gVolumeList->IndexOf(flatComponent);
62 firstPathComponent = flatComponent;
63 return (foundIndex != -1);
64 }
65
66 void
67 net_ShutdownURLHelperOSX()
68 {
69 delete gVolumeList;
70 gVolumeList = nullptr;
71 }
72
73 static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath)
74 {
75 // Use CFURL to do the conversion. We don't want to do this by simply
76 // using SwapSlashColon - we need the charset mapped from MacRoman
77 // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change)
78 // prepended if the path is not on the boot drive.
79
80 CFStringRef pathStrRef = CFStringCreateWithCString(nullptr,
81 PromiseFlatCString(hfsPath).get(),
82 kCFStringEncodingMacRoman);
83 if (!pathStrRef)
84 return NS_ERROR_FAILURE;
85
86 nsresult rv = NS_ERROR_FAILURE;
87 CFURLRef urlRef = CFURLCreateWithFileSystemPath(nullptr,
88 pathStrRef, kCFURLHFSPathStyle, true);
89 if (urlRef) {
90 UInt8 pathBuf[PATH_MAX];
91 if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) {
92 posixPath = (char *)pathBuf;
93 rv = NS_OK;
94 }
95 }
96 CFRelease(pathStrRef);
97 if (urlRef)
98 CFRelease(urlRef);
99 return rv;
100 }
101
102 static void SwapSlashColon(char *s)
103 {
104 while (*s) {
105 if (*s == '/')
106 *s = ':';
107 else if (*s == ':')
108 *s = '/';
109 s++;
110 }
111 }
112
113 nsresult
114 net_GetURLSpecFromActualFile(nsIFile *aFile, nsACString &result)
115 {
116 // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp
117
118 nsresult rv;
119 nsAutoCString ePath;
120
121 // construct URL spec from native file path
122 rv = aFile->GetNativePath(ePath);
123 if (NS_FAILED(rv))
124 return rv;
125
126 nsAutoCString escPath;
127 NS_NAMED_LITERAL_CSTRING(prefix, "file://");
128
129 // Escape the path with the directory mask
130 if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath))
131 escPath.Insert(prefix, 0);
132 else
133 escPath.Assign(prefix + ePath);
134
135 // esc_Directory does not escape the semicolons, so if a filename
136 // contains semicolons we need to manually escape them.
137 // This replacement should be removed in bug #473280
138 escPath.ReplaceSubstring(";", "%3b");
139
140 result = escPath;
141 return NS_OK;
142 }
143
144 nsresult
145 net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result)
146 {
147 // NOTE: See also the implementation in nsURLHelperUnix.cpp
148 // This matches it except for the HFS path handling.
149
150 nsresult rv;
151
152 nsCOMPtr<nsIFile> localFile;
153 rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localFile));
154 if (NS_FAILED(rv))
155 return rv;
156
157 nsAutoCString directory, fileBaseName, fileExtension, path;
158 bool bHFSPath = false;
159
160 rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension);
161 if (NS_FAILED(rv))
162 return rv;
163
164 if (!directory.IsEmpty()) {
165 NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path);
166
167 // The canonical form of file URLs on OSX use POSIX paths:
168 // file:///path-name.
169 // But, we still encounter file URLs that use HFS paths:
170 // file:///volume-name/path-name
171 // Determine that here and normalize HFS paths to POSIX.
172 nsAutoCString possibleVolName;
173 if (pathBeginsWithVolName(directory, possibleVolName)) {
174 // Though we know it begins with a volume name, it could still
175 // be a valid POSIX path if the boot drive is named "Mac HD"
176 // and there is a directory "Mac HD" at its root. If such a
177 // directory doesn't exist, we'll assume this is an HFS path.
178 FSRef testRef;
179 possibleVolName.Insert("/", 0);
180 if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nullptr) != noErr)
181 bHFSPath = true;
182 }
183
184 if (bHFSPath) {
185 // "%2F"s need to become slashes, while all other slashes need to
186 // become colons. If we start out by changing "%2F"s to colons, we
187 // can reply on SwapSlashColon() to do what we need
188 path.ReplaceSubstring("%2F", ":");
189 path.Cut(0, 1); // directory begins with '/'
190 SwapSlashColon((char *)path.get());
191 // At this point, path is an HFS path made using the same
192 // algorithm as nsURLHelperMac. We'll convert to POSIX below.
193 }
194 }
195 if (!fileBaseName.IsEmpty())
196 NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path);
197 if (!fileExtension.IsEmpty()) {
198 path += '.';
199 NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path);
200 }
201
202 NS_UnescapeURL(path);
203 if (path.Length() != strlen(path.get()))
204 return NS_ERROR_FILE_INVALID_PATH;
205
206 if (bHFSPath)
207 convertHFSPathtoPOSIX(path, path);
208
209 // assuming path is encoded in the native charset
210 rv = localFile->InitWithNativePath(path);
211 if (NS_FAILED(rv))
212 return rv;
213
214 NS_ADDREF(*result = localFile);
215 return NS_OK;
216 }

mercurial