Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / xpcom / io / CocoaFileUtils.mm
blob9665358ac9ee03b725dd266501ac33118b7883d8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:set ts=2 sts=2 sw=2 et cin:
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/. */
7 #include "CocoaFileUtils.h"
8 #include "nsCocoaUtils.h"
9 #include <Cocoa/Cocoa.h>
10 #include "nsObjCExceptions.h"
11 #include "nsDebug.h"
12 #include "nsString.h"
13 #include "mozilla/MacStringHelpers.h"
15 namespace CocoaFileUtils {
17 nsresult RevealFileInFinder(CFURLRef url) {
18   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
20   if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
22   NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
23   BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path]
24                                   inFileViewerRootedAtPath:@""];
25   [ap release];
27   return (success ? NS_OK : NS_ERROR_FAILURE);
29   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
32 nsresult OpenURL(CFURLRef url) {
33   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
35   if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
37   NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
38   BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
39   [ap release];
41   return (success ? NS_OK : NS_ERROR_FAILURE);
43   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
46 nsresult GetFileCreatorCode(CFURLRef url, OSType* creatorCode) {
47   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
49   if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode)) return NS_ERROR_INVALID_ARG;
51   nsAutoreleasePool localPool;
53   NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
54   if (!resolvedPath) {
55     return NS_ERROR_FAILURE;
56   }
58   NSDictionary* dict =
59       [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
60                                                        error:nil];
61   if (!dict) {
62     return NS_ERROR_FAILURE;
63   }
65   NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
66   if (!creatorNum) {
67     return NS_ERROR_FAILURE;
68   }
70   *creatorCode = [creatorNum unsignedLongValue];
71   return NS_OK;
73   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
76 nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode) {
77   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
79   if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
81   NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
82   NSDictionary* dict = [NSDictionary
83       dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode]
84                     forKey:NSFileHFSCreatorCode];
85   BOOL success =
86       [[NSFileManager defaultManager] setAttributes:dict
87                                        ofItemAtPath:[(NSURL*)url path]
88                                               error:nil];
89   [ap release];
90   return (success ? NS_OK : NS_ERROR_FAILURE);
92   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
95 nsresult GetFileTypeCode(CFURLRef url, OSType* typeCode) {
96   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
98   if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode)) return NS_ERROR_INVALID_ARG;
100   nsAutoreleasePool localPool;
102   NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
103   if (!resolvedPath) {
104     return NS_ERROR_FAILURE;
105   }
107   NSDictionary* dict =
108       [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
109                                                        error:nil];
110   if (!dict) {
111     return NS_ERROR_FAILURE;
112   }
114   NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
115   if (!typeNum) {
116     return NS_ERROR_FAILURE;
117   }
119   *typeCode = [typeNum unsignedLongValue];
120   return NS_OK;
122   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
125 nsresult SetFileTypeCode(CFURLRef url, OSType typeCode) {
126   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
128   if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
130   NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
131   NSDictionary* dict = [NSDictionary
132       dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode]
133                     forKey:NSFileHFSTypeCode];
134   BOOL success =
135       [[NSFileManager defaultManager] setAttributes:dict
136                                        ofItemAtPath:[(NSURL*)url path]
137                                               error:nil];
138   [ap release];
139   return (success ? NS_OK : NS_ERROR_FAILURE);
141   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
144 // Can be called off of the main thread.
145 void AddOriginMetadataToFile(const CFStringRef filePath,
146                              const CFURLRef sourceURL,
147                              const CFURLRef referrerURL) {
148   typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef,
149                                               CFTypeRef);
150   static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
152   static bool did_symbol_lookup = false;
153   if (!did_symbol_lookup) {
154     did_symbol_lookup = true;
156     CFBundleRef metadata_bundle =
157         ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
158     if (!metadata_bundle) {
159       return;
160     }
162     mdItemSetAttributeFunc =
163         (MDItemSetAttribute_type)::CFBundleGetFunctionPointerForName(
164             metadata_bundle, CFSTR("MDItemSetAttribute"));
165   }
166   if (!mdItemSetAttributeFunc) {
167     return;
168   }
170   MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
171   if (!mdItem) {
172     return;
173   }
175   CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
176   if (!list) {
177     ::CFRelease(mdItem);
178     return;
179   }
181   // The first item in the list is the source URL of the downloaded file.
182   if (sourceURL) {
183     ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
184   }
186   // If the referrer is known, store that in the second position.
187   if (referrerURL) {
188     ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
189   }
191   mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
193   ::CFRelease(list);
194   ::CFRelease(mdItem);
197 // Can be called off of the main thread.
198 static CFMutableDictionaryRef CreateQuarantineDictionary(
199     const CFURLRef aFileURL, const bool aCreateProps) {
200   CFDictionaryRef quarantineProps = NULL;
201   if (aCreateProps) {
202     quarantineProps = ::CFDictionaryCreate(NULL, NULL, NULL, 0,
203                                            &kCFTypeDictionaryKeyCallBacks,
204                                            &kCFTypeDictionaryValueCallBacks);
205   } else {
206     Boolean success = ::CFURLCopyResourcePropertyForKey(
207         aFileURL, kCFURLQuarantinePropertiesKey, &quarantineProps, NULL);
208     // If there aren't any quarantine properties then the user probably
209     // set up an exclusion and we don't need to add metadata.
210     if (!success || !quarantineProps) {
211       return NULL;
212     }
213   }
215   // We don't know what to do if the props aren't a dictionary.
216   if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
217     ::CFRelease(quarantineProps);
218     return NULL;
219   }
221   // Make a mutable copy of the properties.
222   CFMutableDictionaryRef mutQuarantineProps = ::CFDictionaryCreateMutableCopy(
223       kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
224   ::CFRelease(quarantineProps);
226   return mutQuarantineProps;
229 // Can be called off of the main thread.
230 void AddQuarantineMetadataToFile(const CFStringRef filePath,
231                                  const CFURLRef sourceURL,
232                                  const CFURLRef referrerURL,
233                                  const bool isFromWeb,
234                                  const bool createProps /* = false */) {
235   CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(
236       kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
238   CFMutableDictionaryRef mutQuarantineProps =
239       CreateQuarantineDictionary(fileURL, createProps);
240   if (!mutQuarantineProps) {
241     ::CFRelease(fileURL);
242     return;
243   }
245   // Add metadata that the OS couldn't infer.
247   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
248     CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload
249                                  : kLSQuarantineTypeOtherDownload;
250     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
251   }
253   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) &&
254       referrerURL) {
255     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey,
256                            referrerURL);
257   }
259   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) &&
260       sourceURL) {
261     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey,
262                            sourceURL);
263   }
265   // Set quarantine properties on file.
266   ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey,
267                                    mutQuarantineProps, NULL);
269   ::CFRelease(fileURL);
270   ::CFRelease(mutQuarantineProps);
273 // Can be called off of the main thread.
274 void CopyQuarantineReferrerUrl(const CFStringRef aFilePath,
275                                nsAString& aReferrer) {
276   CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(
277       kCFAllocatorDefault, aFilePath, kCFURLPOSIXPathStyle, false);
279   CFMutableDictionaryRef mutQuarantineProps =
280       CreateQuarantineDictionary(fileURL, false);
281   ::CFRelease(fileURL);
282   if (!mutQuarantineProps) {
283     return;
284   }
286   CFTypeRef referrerRef =
287       ::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey);
288   if (referrerRef && ::CFGetTypeID(referrerRef) == ::CFURLGetTypeID()) {
289     // URL string must be copied prior to releasing the dictionary.
290     mozilla::CopyCocoaStringToXPCOMString(
291         (NSString*)::CFURLGetString(static_cast<CFURLRef>(referrerRef)),
292         aReferrer);
293   }
295   ::CFRelease(mutQuarantineProps);
298 CFURLRef GetTemporaryFolderCFURLRef() {
299   NSString* tempDir = ::NSTemporaryDirectory();
300   return tempDir == nil
301              ? NULL
302              : (CFURLRef)[NSURL fileURLWithPath:tempDir isDirectory:YES];
305 CFURLRef GetProductDirectoryCFURLRef(bool aLocal) {
306   NSSearchPathDirectory folderType =
307       aLocal ? NSCachesDirectory : NSLibraryDirectory;
308   NSFileManager* manager = [NSFileManager defaultManager];
309   return static_cast<CFURLRef>(
310       [[manager URLsForDirectory:folderType
311                        inDomains:NSUserDomainMask] firstObject]);
314 }  // namespace CocoaFileUtils