1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsURLHelperOSX.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,216 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Mac OS X-specific local file uri parsing */ 1.11 +#include "nsURLHelper.h" 1.12 +#include "nsEscape.h" 1.13 +#include "nsIFile.h" 1.14 +#include "nsTArray.h" 1.15 +#include "nsReadableUtils.h" 1.16 +#include <Carbon/Carbon.h> 1.17 + 1.18 +static nsTArray<nsCString> *gVolumeList = nullptr; 1.19 + 1.20 +static bool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent) 1.21 +{ 1.22 + // Return whether the 1st path component in path (escaped) is equal to the name 1.23 + // of a mounted volume. Return the 1st path component (unescaped) in any case. 1.24 + // This needs to be done as quickly as possible, so we cache a list of volume names. 1.25 + // XXX Register an event handler to detect drives being mounted/unmounted? 1.26 + 1.27 + if (!gVolumeList) { 1.28 + gVolumeList = new nsTArray<nsCString>; 1.29 + if (!gVolumeList) { 1.30 + return false; // out of memory 1.31 + } 1.32 + } 1.33 + 1.34 + // Cache a list of volume names 1.35 + if (!gVolumeList->Length()) { 1.36 + OSErr err; 1.37 + ItemCount volumeIndex = 1; 1.38 + 1.39 + do { 1.40 + HFSUniStr255 volName; 1.41 + FSRef rootDirectory; 1.42 + err = ::FSGetVolumeInfo(0, volumeIndex, nullptr, kFSVolInfoNone, nullptr, 1.43 + &volName, &rootDirectory); 1.44 + if (err == noErr) { 1.45 + NS_ConvertUTF16toUTF8 volNameStr(Substring((char16_t *)volName.unicode, 1.46 + (char16_t *)volName.unicode + volName.length)); 1.47 + gVolumeList->AppendElement(volNameStr); 1.48 + volumeIndex++; 1.49 + } 1.50 + } while (err == noErr); 1.51 + } 1.52 + 1.53 + // Extract the first component of the path 1.54 + nsACString::const_iterator start; 1.55 + path.BeginReading(start); 1.56 + start.advance(1); // path begins with '/' 1.57 + nsACString::const_iterator directory_end; 1.58 + path.EndReading(directory_end); 1.59 + nsACString::const_iterator component_end(start); 1.60 + FindCharInReadable('/', component_end, directory_end); 1.61 + 1.62 + nsAutoCString flatComponent((Substring(start, component_end))); 1.63 + NS_UnescapeURL(flatComponent); 1.64 + int32_t foundIndex = gVolumeList->IndexOf(flatComponent); 1.65 + firstPathComponent = flatComponent; 1.66 + return (foundIndex != -1); 1.67 +} 1.68 + 1.69 +void 1.70 +net_ShutdownURLHelperOSX() 1.71 +{ 1.72 + delete gVolumeList; 1.73 + gVolumeList = nullptr; 1.74 +} 1.75 + 1.76 +static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath) 1.77 +{ 1.78 + // Use CFURL to do the conversion. We don't want to do this by simply 1.79 + // using SwapSlashColon - we need the charset mapped from MacRoman 1.80 + // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change) 1.81 + // prepended if the path is not on the boot drive. 1.82 + 1.83 + CFStringRef pathStrRef = CFStringCreateWithCString(nullptr, 1.84 + PromiseFlatCString(hfsPath).get(), 1.85 + kCFStringEncodingMacRoman); 1.86 + if (!pathStrRef) 1.87 + return NS_ERROR_FAILURE; 1.88 + 1.89 + nsresult rv = NS_ERROR_FAILURE; 1.90 + CFURLRef urlRef = CFURLCreateWithFileSystemPath(nullptr, 1.91 + pathStrRef, kCFURLHFSPathStyle, true); 1.92 + if (urlRef) { 1.93 + UInt8 pathBuf[PATH_MAX]; 1.94 + if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) { 1.95 + posixPath = (char *)pathBuf; 1.96 + rv = NS_OK; 1.97 + } 1.98 + } 1.99 + CFRelease(pathStrRef); 1.100 + if (urlRef) 1.101 + CFRelease(urlRef); 1.102 + return rv; 1.103 +} 1.104 + 1.105 +static void SwapSlashColon(char *s) 1.106 +{ 1.107 + while (*s) { 1.108 + if (*s == '/') 1.109 + *s = ':'; 1.110 + else if (*s == ':') 1.111 + *s = '/'; 1.112 + s++; 1.113 + } 1.114 +} 1.115 + 1.116 +nsresult 1.117 +net_GetURLSpecFromActualFile(nsIFile *aFile, nsACString &result) 1.118 +{ 1.119 + // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp 1.120 + 1.121 + nsresult rv; 1.122 + nsAutoCString ePath; 1.123 + 1.124 + // construct URL spec from native file path 1.125 + rv = aFile->GetNativePath(ePath); 1.126 + if (NS_FAILED(rv)) 1.127 + return rv; 1.128 + 1.129 + nsAutoCString escPath; 1.130 + NS_NAMED_LITERAL_CSTRING(prefix, "file://"); 1.131 + 1.132 + // Escape the path with the directory mask 1.133 + if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath)) 1.134 + escPath.Insert(prefix, 0); 1.135 + else 1.136 + escPath.Assign(prefix + ePath); 1.137 + 1.138 + // esc_Directory does not escape the semicolons, so if a filename 1.139 + // contains semicolons we need to manually escape them. 1.140 + // This replacement should be removed in bug #473280 1.141 + escPath.ReplaceSubstring(";", "%3b"); 1.142 + 1.143 + result = escPath; 1.144 + return NS_OK; 1.145 +} 1.146 + 1.147 +nsresult 1.148 +net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result) 1.149 +{ 1.150 + // NOTE: See also the implementation in nsURLHelperUnix.cpp 1.151 + // This matches it except for the HFS path handling. 1.152 + 1.153 + nsresult rv; 1.154 + 1.155 + nsCOMPtr<nsIFile> localFile; 1.156 + rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localFile)); 1.157 + if (NS_FAILED(rv)) 1.158 + return rv; 1.159 + 1.160 + nsAutoCString directory, fileBaseName, fileExtension, path; 1.161 + bool bHFSPath = false; 1.162 + 1.163 + rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension); 1.164 + if (NS_FAILED(rv)) 1.165 + return rv; 1.166 + 1.167 + if (!directory.IsEmpty()) { 1.168 + NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path); 1.169 + 1.170 + // The canonical form of file URLs on OSX use POSIX paths: 1.171 + // file:///path-name. 1.172 + // But, we still encounter file URLs that use HFS paths: 1.173 + // file:///volume-name/path-name 1.174 + // Determine that here and normalize HFS paths to POSIX. 1.175 + nsAutoCString possibleVolName; 1.176 + if (pathBeginsWithVolName(directory, possibleVolName)) { 1.177 + // Though we know it begins with a volume name, it could still 1.178 + // be a valid POSIX path if the boot drive is named "Mac HD" 1.179 + // and there is a directory "Mac HD" at its root. If such a 1.180 + // directory doesn't exist, we'll assume this is an HFS path. 1.181 + FSRef testRef; 1.182 + possibleVolName.Insert("/", 0); 1.183 + if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nullptr) != noErr) 1.184 + bHFSPath = true; 1.185 + } 1.186 + 1.187 + if (bHFSPath) { 1.188 + // "%2F"s need to become slashes, while all other slashes need to 1.189 + // become colons. If we start out by changing "%2F"s to colons, we 1.190 + // can reply on SwapSlashColon() to do what we need 1.191 + path.ReplaceSubstring("%2F", ":"); 1.192 + path.Cut(0, 1); // directory begins with '/' 1.193 + SwapSlashColon((char *)path.get()); 1.194 + // At this point, path is an HFS path made using the same 1.195 + // algorithm as nsURLHelperMac. We'll convert to POSIX below. 1.196 + } 1.197 + } 1.198 + if (!fileBaseName.IsEmpty()) 1.199 + NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path); 1.200 + if (!fileExtension.IsEmpty()) { 1.201 + path += '.'; 1.202 + NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path); 1.203 + } 1.204 + 1.205 + NS_UnescapeURL(path); 1.206 + if (path.Length() != strlen(path.get())) 1.207 + return NS_ERROR_FILE_INVALID_PATH; 1.208 + 1.209 + if (bHFSPath) 1.210 + convertHFSPathtoPOSIX(path, path); 1.211 + 1.212 + // assuming path is encoded in the native charset 1.213 + rv = localFile->InitWithNativePath(path); 1.214 + if (NS_FAILED(rv)) 1.215 + return rv; 1.216 + 1.217 + NS_ADDREF(*result = localFile); 1.218 + return NS_OK; 1.219 +}