Provide image baselines for struct-image-07-t.svg
[chromium-blink-merge.git] / base / mac_util.mm
blob2975b4e4a0e9ed62f0bfd535cad44db7f9cc6b17
1 // Copyright (c) 2008-2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/mac_util.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/scoped_cftyperef.h"
13 #include "base/sys_string_conversions.h"
15 namespace mac_util {
17 std::string PathFromFSRef(const FSRef& ref) {
18   scoped_cftyperef<CFURLRef> url(
19       CFURLCreateFromFSRef(kCFAllocatorDefault, &ref));
20   NSString *path_string = [(NSURL *)url.get() path];
21   return [path_string fileSystemRepresentation];
24 bool FSRefFromPath(const std::string& path, FSRef* ref) {
25   OSStatus status = FSPathMakeRef((const UInt8*)path.c_str(),
26                                   ref, nil);
27   return status == noErr;
30 // Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
31 bool AmIBundled() {
32   ProcessSerialNumber psn = {0, kCurrentProcess};
34   FSRef fsref;
35   if (GetProcessBundleLocation(&psn, &fsref) != noErr)
36     return false;
38   FSCatalogInfo info;
39   if (FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
40                        NULL, NULL, NULL) != noErr) {
41     return false;
42   }
44   return info.nodeFlags & kFSNodeIsDirectoryMask;
47 bool IsBackgroundOnlyProcess() {
48   // This function really does want to examine NSBundle's idea of the main
49   // bundle dictionary, and not the overriden MainAppBundle.  It needs to look
50   // at the actual running .app's Info.plist to access its LSUIElement
51   // property.
52   NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary];
53   return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO;
56 // No threading worries since NSBundle isn't thread safe.
57 static NSBundle* g_override_app_bundle = nil;
59 NSBundle* MainAppBundle() {
60   if (g_override_app_bundle)
61     return g_override_app_bundle;
62   return [NSBundle mainBundle];
65 FilePath MainAppBundlePath() {
66   NSBundle* bundle = MainAppBundle();
67   return FilePath([[bundle bundlePath] fileSystemRepresentation]);
70 void SetOverrideAppBundle(NSBundle* bundle) {
71   if (bundle != g_override_app_bundle) {
72     [g_override_app_bundle release];
73     g_override_app_bundle = [bundle retain];
74   }
77 void SetOverrideAppBundlePath(const FilePath& file_path) {
78   NSString* path = base::SysUTF8ToNSString(file_path.value());
79   NSBundle* bundle = [NSBundle bundleWithPath:path];
80   CHECK(bundle) << "Failed to load the bundle at " << file_path.value();
82   SetOverrideAppBundle(bundle);
85 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
86   OSType creator = kUnknownType;
87   CFBundleGetPackageInfo(bundle, NULL, &creator);
88   return creator;
91 OSType CreatorCodeForApplication() {
92   CFBundleRef bundle = CFBundleGetMainBundle();
93   if (!bundle)
94     return kUnknownType;
96   return CreatorCodeForCFBundleRef(bundle);
99 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
100   DCHECK(result);
101   NSArray* dirs =
102       NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
103   if ([dirs count] < 1) {
104     return false;
105   }
106   NSString* path = [dirs objectAtIndex:0];
107   *result = FilePath([path fileSystemRepresentation]);
108   return true;
111 FilePath GetUserLibraryPath() {
112   FilePath user_library_path;
113   if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
114     LOG(WARNING) << "Could not get user library path";
115   }
116   return user_library_path;
119 CGColorSpaceRef GetSRGBColorSpace() {
120   // Leaked.  That's OK, it's scoped to the lifetime of the application.
121   static CGColorSpaceRef g_color_space_sRGB =
122       CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
123   LOG_IF(ERROR, !g_color_space_sRGB) << "Couldn't get the sRGB color space";
124   return g_color_space_sRGB;
127 CGColorSpaceRef GetSystemColorSpace() {
128   // Leaked.  That's OK, it's scoped to the lifetime of the application.
129   // Try to get the main display's color space.
130   static CGColorSpaceRef g_system_color_space =
131       CGDisplayCopyColorSpace(CGMainDisplayID());
133   if (!g_system_color_space) {
134     // Use a generic RGB color space.  This is better than nothing.
135     g_system_color_space = CGColorSpaceCreateDeviceRGB();
137     if (g_system_color_space) {
138       LOG(WARNING) <<
139           "Couldn't get the main display's color space, using generic";
140     } else {
141       LOG(ERROR) << "Couldn't get any color space";
142     }
143   }
145   return g_system_color_space;
148 // a count of currently outstanding requests for full screen mode from browser
149 // windows, plugins, etc.
150 static int g_full_screen_requests = 0;
152 // Add a request for full screen mode.  If the menu bar is not currently
153 // hidden, hide it.  Must be called on main thread.
154 void RequestFullScreen() {
155   DCHECK_GE(g_full_screen_requests, 0);
156   if (g_full_screen_requests == 0)
157     SetSystemUIMode(kUIModeAllSuppressed, kUIOptionAutoShowMenuBar);
158   ++g_full_screen_requests;
161 // Release a request for full screen mode.  If there are no other outstanding
162 // requests, show the menu bar. Must be called on main thread.
163 void ReleaseFullScreen() {
164   DCHECK_GT(g_full_screen_requests, 0);
165   --g_full_screen_requests;
166   if (g_full_screen_requests == 0)
167     SetSystemUIMode(kUIModeNormal, 0);
170 void SetCursorVisibility(bool visible) {
171   if (visible)
172     [NSCursor unhide];
173   else
174     [NSCursor hide];
177 void GrabWindowSnapshot(NSWindow* window,
178     std::vector<unsigned char>* png_representation) {
179   // Make sure to grab the "window frame" view so we get current tab +
180   // tabstrip.
181   NSView* view = [[window contentView] superview];
182   NSBitmapImageRep* rep =
183       [view bitmapImageRepForCachingDisplayInRect:[view bounds]];
184   [view cacheDisplayInRect:[view bounds] toBitmapImageRep:rep];
185   NSData* data = [rep representationUsingType:NSPNGFileType properties:nil];
186   const unsigned char* buf = static_cast<const unsigned char*>([data bytes]);
187   NSUInteger length = [data length];
188   if (buf != NULL && length > 0){
189     png_representation->assign(buf, buf + length);
190     DCHECK(png_representation->size() > 0);
191   }
194 void ActivateProcess(pid_t pid) {
195   ProcessSerialNumber process;
196   OSStatus status = GetProcessForPID(pid, &process);
197   if (status == noErr) {
198     SetFrontProcess(&process);
199   } else {
200     LOG(WARNING) << "Unable to get process for pid " << pid;
201   }
204 // Takes a path to an (executable) binary and tries to provide the path to an
205 // application bundle containing it. It takes the outermost bundle that it can
206 // find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
207 //   |exec_name| - path to the binary
208 //   returns - path to the application bundle, or empty on error
209 FilePath GetAppBundlePath(const FilePath& exec_name) {
210   const char kExt[] = ".app";
211   const size_t kExtLength = arraysize(kExt) - 1;
213   // Split the path into components.
214   std::vector<std::string> components;
215   exec_name.GetComponents(&components);
217   // It's an error if we don't get any components.
218   if (!components.size())
219     return FilePath();
221   // Don't prepend '/' to the first component.
222   std::vector<std::string>::const_iterator it = components.begin();
223   std::string bundle_name = *it;
224   DCHECK(it->length() > 0);
225   // If the first component ends in ".app", we're already done.
226   if (it->length() > kExtLength &&
227       !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
228     return FilePath(bundle_name);
230   // The first component may be "/" or "//", etc. Only append '/' if it doesn't
231   // already end in '/'.
232   if (bundle_name[bundle_name.length() - 1] != '/')
233     bundle_name += '/';
235   // Go through the remaining components.
236   for (++it; it != components.end(); ++it) {
237     DCHECK(it->length() > 0);
239     bundle_name += *it;
241     // If the current component ends in ".app", we're done.
242     if (it->length() > kExtLength &&
243         !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
244       return FilePath(bundle_name);
246     // Separate this component from the next one.
247     bundle_name += '/';
248   }
250   return FilePath();
253 bool SetFileBackupExclusion(const FilePath& file_path, bool exclude) {
254   NSString* filePath =
255       [NSString stringWithUTF8String:file_path.value().c_str()];
257   // If being asked to exclude something in a tmp directory, just lie and say it
258   // was done.  TimeMachine will already ignore tmp directories.  This keeps the
259   // temporary profiles used by unittests from being added to the exclude list.
260   // Otherwise, as /Library/Preferences/com.apple.TimeMachine.plist grows the
261   // bots slow down due to reading/writing all the temporary profiles used over
262   // time.
264   NSString* tmpDir = NSTemporaryDirectory();
265   // Make sure the temp dir is terminated with a slash
266   if (tmpDir && ![tmpDir hasSuffix:@"/"])
267     tmpDir = [tmpDir stringByAppendingString:@"/"];
268   // '/var' is a link to '/private/var', make sure to check both forms.
269   NSString* privateTmpDir = nil;
270   if ([tmpDir hasPrefix:@"/var/"])
271     privateTmpDir = [@"/private" stringByAppendingString:tmpDir];
273   if ((tmpDir && [filePath hasPrefix:tmpDir]) ||
274       (privateTmpDir && [filePath hasPrefix:privateTmpDir]) ||
275       [filePath hasPrefix:@"/tmp/"] ||
276       [filePath hasPrefix:@"/var/tmp/"] ||
277       [filePath hasPrefix:@"/private/tmp/"] ||
278       [filePath hasPrefix:@"/private/var/tmp/"]) {
279     return true;
280   }
282   NSURL* url = [NSURL fileURLWithPath:filePath];
283   // Note that we always set CSBackupSetItemExcluded's excludeByPath param
284   // to true.  This prevents a problem with toggling the setting: if the file
285   // is excluded with excludeByPath set to true then excludeByPath must
286   // also be true when un-excluding the file, otherwise the un-excluding
287   // will be ignored.
288   bool success =
289       CSBackupSetItemExcluded((CFURLRef)url, exclude, true) == noErr;
290   if (!success)
291     LOG(WARNING) << "Failed to set backup excluson for file '"
292                  << file_path.value().c_str() << "'.  Continuing.";
293   return success;
296 CFTypeRef GetValueFromDictionary(CFDictionaryRef dict,
297                                  CFStringRef key,
298                                  CFTypeID expected_type) {
299   CFTypeRef value = CFDictionaryGetValue(dict, key);
300   if (!value)
301     return value;
303   if (CFGetTypeID(value) != expected_type) {
304     scoped_cftyperef<CFStringRef> expected_type_ref(
305         CFCopyTypeIDDescription(expected_type));
306     scoped_cftyperef<CFStringRef> actual_type_ref(
307         CFCopyTypeIDDescription(CFGetTypeID(value)));
308     LOG(WARNING) << "Expected value for key "
309                  << base::SysCFStringRefToUTF8(key)
310                  << " to be "
311                  << base::SysCFStringRefToUTF8(expected_type_ref)
312                  << " but it was "
313                  << base::SysCFStringRefToUTF8(actual_type_ref)
314                  << " instead";
315     return NULL;
316   }
318   return value;
321 }  // namespace mac_util