netwerk/base/src/nsURLHelperOSX.cpp

changeset 0
6474c204b198
     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 +}

mercurial