Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: base/mac_util.mm

Issue 6046009: Move base/mac_util.h to base/mac and use the base::mac namespace.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/mac_util.h ('k') | base/mac_util_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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.
4
5 #include "base/mac_util.h"
6
7 #import <Cocoa/Cocoa.h>
8
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/message_loop.h"
13 #include "base/scoped_nsobject.h"
14 #include "base/sys_string_conversions.h"
15
16 using base::mac::ScopedCFTypeRef;
17
18 namespace {
19
20 // a count of currently outstanding requests for full screen mode from browser
21 // windows, plugins, etc.
22 int g_full_screen_requests[mac_util::kNumFullScreenModes] = { 0, 0, 0};
23
24 // Sets the appropriate SystemUIMode based on the current full screen requests.
25 // Since only one SystemUIMode can be active at a given time, full screen
26 // requests are ordered by priority. If there are no outstanding full screen
27 // requests, reverts to normal mode. If the correct SystemUIMode is already
28 // set, does nothing.
29 void SetUIMode() {
30 // Get the current UI mode.
31 SystemUIMode current_mode;
32 GetSystemUIMode(&current_mode, NULL);
33
34 // Determine which mode should be active, based on which requests are
35 // currently outstanding. More permissive requests take precedence. For
36 // example, plugins request |kFullScreenModeAutoHideAll|, while browser
37 // windows request |kFullScreenModeHideDock| when the fullscreen overlay is
38 // down. Precedence goes to plugins in this case, so AutoHideAll wins over
39 // HideDock.
40 SystemUIMode desired_mode = kUIModeNormal;
41 SystemUIOptions desired_options = 0;
42 if (g_full_screen_requests[mac_util::kFullScreenModeAutoHideAll] > 0) {
43 desired_mode = kUIModeAllHidden;
44 desired_options = kUIOptionAutoShowMenuBar;
45 } else if (g_full_screen_requests[mac_util::kFullScreenModeHideDock] > 0) {
46 desired_mode = kUIModeContentHidden;
47 } else if (g_full_screen_requests[mac_util::kFullScreenModeHideAll] > 0) {
48 desired_mode = kUIModeAllHidden;
49 }
50
51 if (current_mode != desired_mode)
52 SetSystemUIMode(desired_mode, desired_options);
53 }
54
55 bool WasLaunchedAsLoginItem() {
56 ProcessSerialNumber psn = { 0, kCurrentProcess };
57
58 scoped_nsobject<NSDictionary> process_info(
59 mac_util::CFToNSCast(ProcessInformationCopyDictionary(&psn,
60 kProcessDictionaryIncludeAllInformationMask)));
61
62 long long temp = [[process_info objectForKey:@"ParentPSN"] longLongValue];
63 ProcessSerialNumber parent_psn =
64 { (temp >> 32) & 0x00000000FFFFFFFFLL, temp & 0x00000000FFFFFFFFLL };
65
66 scoped_nsobject<NSDictionary> parent_info(
67 mac_util::CFToNSCast(ProcessInformationCopyDictionary(&parent_psn,
68 kProcessDictionaryIncludeAllInformationMask)));
69
70 // Check that creator process code is that of loginwindow.
71 BOOL result =
72 [[parent_info objectForKey:@"FileCreator"] isEqualToString:@"lgnw"];
73
74 return result == YES;
75 }
76
77 // Looks into Shared File Lists corresponding to Login Items for the item
78 // representing the current application. If such an item is found, returns
79 // retained reference to it. Caller is responsible for releasing the reference.
80 LSSharedFileListItemRef GetLoginItemForApp() {
81 ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
82 NULL, kLSSharedFileListSessionLoginItems, NULL));
83
84 if (!login_items.get()) {
85 LOG(ERROR) << "Couldn't get a Login Items list.";
86 return NULL;
87 }
88
89 scoped_nsobject<NSArray> login_items_array(
90 mac_util::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
91
92 NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
93
94 for(NSUInteger i = 0; i < [login_items_array count]; ++i) {
95 LSSharedFileListItemRef item = reinterpret_cast<LSSharedFileListItemRef>(
96 [login_items_array objectAtIndex:i]);
97 CFURLRef item_url_ref = NULL;
98
99 if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr) {
100 ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
101 if (CFEqual(item_url, url)) {
102 CFRetain(item);
103 return item;
104 }
105 }
106 }
107
108 return NULL;
109 }
110
111 #if !defined(MAC_OS_X_VERSION_10_6) || \
112 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
113 // kLSSharedFileListLoginItemHidden is supported on
114 // 10.5, but missing from the 10.5 headers.
115 // http://openradar.appspot.com/6482251
116 static NSString* kLSSharedFileListLoginItemHidden =
117 @"com.apple.loginitem.HideOnLaunch";
118 #endif
119
120 bool IsHiddenLoginItem(LSSharedFileListItemRef item) {
121 ScopedCFTypeRef<CFBooleanRef> hidden(reinterpret_cast<CFBooleanRef>(
122 LSSharedFileListItemCopyProperty(item,
123 reinterpret_cast<CFStringRef>(kLSSharedFileListLoginItemHidden))));
124
125 return hidden && hidden == kCFBooleanTrue;
126 }
127
128 } // end namespace
129
130 namespace mac_util {
131
132 std::string PathFromFSRef(const FSRef& ref) {
133 ScopedCFTypeRef<CFURLRef> url(
134 CFURLCreateFromFSRef(kCFAllocatorDefault, &ref));
135 NSString *path_string = [(NSURL *)url.get() path];
136 return [path_string fileSystemRepresentation];
137 }
138
139 bool FSRefFromPath(const std::string& path, FSRef* ref) {
140 OSStatus status = FSPathMakeRef((const UInt8*)path.c_str(),
141 ref, nil);
142 return status == noErr;
143 }
144
145 static bool g_override_am_i_bundled = false;
146 static bool g_override_am_i_bundled_value = false;
147
148 // Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
149 static bool UncachedAmIBundled() {
150 if (g_override_am_i_bundled)
151 return g_override_am_i_bundled_value;
152
153 ProcessSerialNumber psn = {0, kCurrentProcess};
154
155 FSRef fsref;
156 OSStatus pbErr;
157 if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) {
158 LOG(ERROR) << "GetProcessBundleLocation failed: error " << pbErr;
159 return false;
160 }
161
162 FSCatalogInfo info;
163 OSErr fsErr;
164 if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
165 NULL, NULL, NULL)) != noErr) {
166 LOG(ERROR) << "FSGetCatalogInfo failed: error " << fsErr;
167 return false;
168 }
169
170 return info.nodeFlags & kFSNodeIsDirectoryMask;
171 }
172
173 bool AmIBundled() {
174 // If the return value is not cached, this function will return different
175 // values depending on when it's called. This confuses some client code, see
176 // http://crbug.com/63183 .
177 static bool result = UncachedAmIBundled();
178 DCHECK_EQ(result, UncachedAmIBundled())
179 << "The return value of AmIBundled() changed. This will confuse tests. "
180 << "Call SetAmIBundled() override manually if your test binary "
181 << "delay-loads the framework.";
182 return result;
183 }
184
185 void SetOverrideAmIBundled(bool value) {
186 g_override_am_i_bundled = true;
187 g_override_am_i_bundled_value = value;
188 }
189
190 bool IsBackgroundOnlyProcess() {
191 // This function really does want to examine NSBundle's idea of the main
192 // bundle dictionary, and not the overriden MainAppBundle. It needs to look
193 // at the actual running .app's Info.plist to access its LSUIElement
194 // property.
195 NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary];
196 return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO;
197 }
198
199 // No threading worries since NSBundle isn't thread safe.
200 static NSBundle* g_override_app_bundle = nil;
201
202 NSBundle* MainAppBundle() {
203 if (g_override_app_bundle)
204 return g_override_app_bundle;
205 return [NSBundle mainBundle];
206 }
207
208 FilePath MainAppBundlePath() {
209 NSBundle* bundle = MainAppBundle();
210 return FilePath([[bundle bundlePath] fileSystemRepresentation]);
211 }
212
213 void SetOverrideAppBundle(NSBundle* bundle) {
214 if (bundle != g_override_app_bundle) {
215 [g_override_app_bundle release];
216 g_override_app_bundle = [bundle retain];
217 }
218 }
219
220 void SetOverrideAppBundlePath(const FilePath& file_path) {
221 NSString* path = base::SysUTF8ToNSString(file_path.value());
222 NSBundle* bundle = [NSBundle bundleWithPath:path];
223 CHECK(bundle) << "Failed to load the bundle at " << file_path.value();
224
225 SetOverrideAppBundle(bundle);
226 }
227
228 OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
229 OSType creator = kUnknownType;
230 CFBundleGetPackageInfo(bundle, NULL, &creator);
231 return creator;
232 }
233
234 OSType CreatorCodeForApplication() {
235 CFBundleRef bundle = CFBundleGetMainBundle();
236 if (!bundle)
237 return kUnknownType;
238
239 return CreatorCodeForCFBundleRef(bundle);
240 }
241
242 bool GetSearchPathDirectory(NSSearchPathDirectory directory,
243 NSSearchPathDomainMask domain_mask,
244 FilePath* result) {
245 DCHECK(result);
246 NSArray* dirs =
247 NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
248 if ([dirs count] < 1) {
249 return false;
250 }
251 NSString* path = [dirs objectAtIndex:0];
252 *result = FilePath([path fileSystemRepresentation]);
253 return true;
254 }
255
256 bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
257 return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
258 }
259
260 bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
261 return GetSearchPathDirectory(directory, NSUserDomainMask, result);
262 }
263
264 FilePath GetUserLibraryPath() {
265 FilePath user_library_path;
266 if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
267 LOG(WARNING) << "Could not get user library path";
268 }
269 return user_library_path;
270 }
271
272 CGColorSpaceRef GetSRGBColorSpace() {
273 // Leaked. That's OK, it's scoped to the lifetime of the application.
274 static CGColorSpaceRef g_color_space_sRGB =
275 CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
276 LOG_IF(ERROR, !g_color_space_sRGB) << "Couldn't get the sRGB color space";
277 return g_color_space_sRGB;
278 }
279
280 CGColorSpaceRef GetSystemColorSpace() {
281 // Leaked. That's OK, it's scoped to the lifetime of the application.
282 // Try to get the main display's color space.
283 static CGColorSpaceRef g_system_color_space =
284 CGDisplayCopyColorSpace(CGMainDisplayID());
285
286 if (!g_system_color_space) {
287 // Use a generic RGB color space. This is better than nothing.
288 g_system_color_space = CGColorSpaceCreateDeviceRGB();
289
290 if (g_system_color_space) {
291 LOG(WARNING) <<
292 "Couldn't get the main display's color space, using generic";
293 } else {
294 LOG(ERROR) << "Couldn't get any color space";
295 }
296 }
297
298 return g_system_color_space;
299 }
300
301 // Add a request for full screen mode. Must be called on the main thread.
302 void RequestFullScreen(FullScreenMode mode) {
303 DCHECK_LT(mode, kNumFullScreenModes);
304 if (mode >= kNumFullScreenModes)
305 return;
306
307 DCHECK_GE(g_full_screen_requests[mode], 0);
308 g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] + 1, 1);
309 SetUIMode();
310 }
311
312 // Release a request for full screen mode. Must be called on the main thread.
313 void ReleaseFullScreen(FullScreenMode mode) {
314 DCHECK_LT(mode, kNumFullScreenModes);
315 if (mode >= kNumFullScreenModes)
316 return;
317
318 DCHECK_GT(g_full_screen_requests[mode], 0);
319 g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] - 1, 0);
320 SetUIMode();
321 }
322
323 // Switches full screen modes. Releases a request for |from_mode| and adds a
324 // new request for |to_mode|. Must be called on the main thread.
325 void SwitchFullScreenModes(FullScreenMode from_mode, FullScreenMode to_mode) {
326 DCHECK_LT(from_mode, kNumFullScreenModes);
327 DCHECK_LT(to_mode, kNumFullScreenModes);
328 if (from_mode >= kNumFullScreenModes || to_mode >= kNumFullScreenModes)
329 return;
330
331 DCHECK_GT(g_full_screen_requests[from_mode], 0);
332 DCHECK_GE(g_full_screen_requests[to_mode], 0);
333 g_full_screen_requests[from_mode] =
334 std::max(g_full_screen_requests[from_mode] - 1, 0);
335 g_full_screen_requests[to_mode] =
336 std::max(g_full_screen_requests[to_mode] + 1, 1);
337 SetUIMode();
338 }
339
340 void SetCursorVisibility(bool visible) {
341 if (visible)
342 [NSCursor unhide];
343 else
344 [NSCursor hide];
345 }
346
347 bool ShouldWindowsMiniaturizeOnDoubleClick() {
348 // We use an undocumented method in Cocoa; if it doesn't exist, default to
349 // |true|. If it ever goes away, we can do (using an undocumented pref key):
350 // NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
351 // return ![defaults objectForKey:@"AppleMiniaturizeOnDoubleClick"] ||
352 // [defaults boolForKey:@"AppleMiniaturizeOnDoubleClick"];
353 BOOL methodImplemented =
354 [NSWindow respondsToSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
355 DCHECK(methodImplemented);
356 return !methodImplemented ||
357 [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
358 }
359
360 void GrabWindowSnapshot(NSWindow* window,
361 std::vector<unsigned char>* png_representation,
362 int* width, int* height) {
363 // Make sure to grab the "window frame" view so we get current tab +
364 // tabstrip.
365 NSView* view = [[window contentView] superview];
366 NSBitmapImageRep* rep =
367 [view bitmapImageRepForCachingDisplayInRect:[view bounds]];
368 [view cacheDisplayInRect:[view bounds] toBitmapImageRep:rep];
369 NSData* data = [rep representationUsingType:NSPNGFileType properties:nil];
370 const unsigned char* buf = static_cast<const unsigned char*>([data bytes]);
371 NSUInteger length = [data length];
372 if (buf != NULL && length > 0){
373 *width = static_cast<int>([rep pixelsWide]);
374 *height = static_cast<int>([rep pixelsHigh]);
375 png_representation->assign(buf, buf + length);
376 DCHECK(png_representation->size() > 0);
377 }
378 }
379
380 void ActivateProcess(pid_t pid) {
381 ProcessSerialNumber process;
382 OSStatus status = GetProcessForPID(pid, &process);
383 if (status == noErr) {
384 SetFrontProcess(&process);
385 } else {
386 LOG(WARNING) << "Unable to get process for pid " << pid;
387 }
388 }
389
390 // Takes a path to an (executable) binary and tries to provide the path to an
391 // application bundle containing it. It takes the outermost bundle that it can
392 // find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
393 // |exec_name| - path to the binary
394 // returns - path to the application bundle, or empty on error
395 FilePath GetAppBundlePath(const FilePath& exec_name) {
396 const char kExt[] = ".app";
397 const size_t kExtLength = arraysize(kExt) - 1;
398
399 // Split the path into components.
400 std::vector<std::string> components;
401 exec_name.GetComponents(&components);
402
403 // It's an error if we don't get any components.
404 if (!components.size())
405 return FilePath();
406
407 // Don't prepend '/' to the first component.
408 std::vector<std::string>::const_iterator it = components.begin();
409 std::string bundle_name = *it;
410 DCHECK(it->length() > 0);
411 // If the first component ends in ".app", we're already done.
412 if (it->length() > kExtLength &&
413 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
414 return FilePath(bundle_name);
415
416 // The first component may be "/" or "//", etc. Only append '/' if it doesn't
417 // already end in '/'.
418 if (bundle_name[bundle_name.length() - 1] != '/')
419 bundle_name += '/';
420
421 // Go through the remaining components.
422 for (++it; it != components.end(); ++it) {
423 DCHECK(it->length() > 0);
424
425 bundle_name += *it;
426
427 // If the current component ends in ".app", we're done.
428 if (it->length() > kExtLength &&
429 !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
430 return FilePath(bundle_name);
431
432 // Separate this component from the next one.
433 bundle_name += '/';
434 }
435
436 return FilePath();
437 }
438
439 bool SetFileBackupExclusion(const FilePath& file_path, bool exclude) {
440 NSString* filePath =
441 [NSString stringWithUTF8String:file_path.value().c_str()];
442
443 // If being asked to exclude something in a tmp directory, just lie and say it
444 // was done. TimeMachine will already ignore tmp directories. This keeps the
445 // temporary profiles used by unittests from being added to the exclude list.
446 // Otherwise, as /Library/Preferences/com.apple.TimeMachine.plist grows the
447 // bots slow down due to reading/writing all the temporary profiles used over
448 // time.
449
450 NSString* tmpDir = NSTemporaryDirectory();
451 // Make sure the temp dir is terminated with a slash
452 if (tmpDir && ![tmpDir hasSuffix:@"/"])
453 tmpDir = [tmpDir stringByAppendingString:@"/"];
454 // '/var' is a link to '/private/var', make sure to check both forms.
455 NSString* privateTmpDir = nil;
456 if ([tmpDir hasPrefix:@"/var/"])
457 privateTmpDir = [@"/private" stringByAppendingString:tmpDir];
458
459 if ((tmpDir && [filePath hasPrefix:tmpDir]) ||
460 (privateTmpDir && [filePath hasPrefix:privateTmpDir]) ||
461 [filePath hasPrefix:@"/tmp/"] ||
462 [filePath hasPrefix:@"/var/tmp/"] ||
463 [filePath hasPrefix:@"/private/tmp/"] ||
464 [filePath hasPrefix:@"/private/var/tmp/"]) {
465 return true;
466 }
467
468 NSURL* url = [NSURL fileURLWithPath:filePath];
469 // Note that we always set CSBackupSetItemExcluded's excludeByPath param
470 // to true. This prevents a problem with toggling the setting: if the file
471 // is excluded with excludeByPath set to true then excludeByPath must
472 // also be true when un-excluding the file, otherwise the un-excluding
473 // will be ignored.
474 bool success =
475 CSBackupSetItemExcluded((CFURLRef)url, exclude, true) == noErr;
476 if (!success)
477 LOG(WARNING) << "Failed to set backup excluson for file '"
478 << file_path.value().c_str() << "'. Continuing.";
479 return success;
480 }
481
482 CFTypeRef GetValueFromDictionary(CFDictionaryRef dict,
483 CFStringRef key,
484 CFTypeID expected_type) {
485 CFTypeRef value = CFDictionaryGetValue(dict, key);
486 if (!value)
487 return value;
488
489 if (CFGetTypeID(value) != expected_type) {
490 ScopedCFTypeRef<CFStringRef> expected_type_ref(
491 CFCopyTypeIDDescription(expected_type));
492 ScopedCFTypeRef<CFStringRef> actual_type_ref(
493 CFCopyTypeIDDescription(CFGetTypeID(value)));
494 LOG(WARNING) << "Expected value for key "
495 << base::SysCFStringRefToUTF8(key)
496 << " to be "
497 << base::SysCFStringRefToUTF8(expected_type_ref)
498 << " but it was "
499 << base::SysCFStringRefToUTF8(actual_type_ref)
500 << " instead";
501 return NULL;
502 }
503
504 return value;
505 }
506
507 void SetProcessName(CFStringRef process_name) {
508 if (!process_name || CFStringGetLength(process_name) == 0) {
509 NOTREACHED() << "SetProcessName given bad name.";
510 return;
511 }
512
513 if (![NSThread isMainThread]) {
514 NOTREACHED() << "Should only set process name from main thread.";
515 return;
516 }
517
518 // Warning: here be dragons! This is SPI reverse-engineered from WebKit's
519 // plugin host, and could break at any time (although realistically it's only
520 // likely to break in a new major release).
521 // When 10.7 is available, check that this still works, and update this
522 // comment for 10.8.
523
524 // Private CFType used in these LaunchServices calls.
525 typedef CFTypeRef PrivateLSASN;
526 typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
527 typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
528 CFStringRef,
529 CFStringRef,
530 CFDictionaryRef*);
531
532 static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
533 NULL;
534 static LSSetApplicationInformationItemType
535 ls_set_application_information_item_func = NULL;
536 static CFStringRef ls_display_name_key = NULL;
537
538 static bool did_symbol_lookup = false;
539 if (!did_symbol_lookup) {
540 did_symbol_lookup = true;
541 CFBundleRef launch_services_bundle =
542 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
543 if (!launch_services_bundle) {
544 LOG(ERROR) << "Failed to look up LaunchServices bundle";
545 return;
546 }
547
548 ls_get_current_application_asn_func =
549 reinterpret_cast<LSGetCurrentApplicationASNType>(
550 CFBundleGetFunctionPointerForName(
551 launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
552 if (!ls_get_current_application_asn_func)
553 LOG(ERROR) << "Could not find _LSGetCurrentApplicationASN";
554
555 ls_set_application_information_item_func =
556 reinterpret_cast<LSSetApplicationInformationItemType>(
557 CFBundleGetFunctionPointerForName(
558 launch_services_bundle,
559 CFSTR("_LSSetApplicationInformationItem")));
560 if (!ls_set_application_information_item_func)
561 LOG(ERROR) << "Could not find _LSSetApplicationInformationItem";
562
563 CFStringRef* key_pointer = reinterpret_cast<CFStringRef*>(
564 CFBundleGetDataPointerForName(launch_services_bundle,
565 CFSTR("_kLSDisplayNameKey")));
566 ls_display_name_key = key_pointer ? *key_pointer : NULL;
567 if (!ls_display_name_key)
568 LOG(ERROR) << "Could not find _kLSDisplayNameKey";
569
570 // Internally, this call relies on the Mach ports that are started up by the
571 // Carbon Process Manager. In debug builds this usually happens due to how
572 // the logging layers are started up; but in release, it isn't started in as
573 // much of a defined order. So if the symbols had to be loaded, go ahead
574 // and force a call to make sure the manager has been initialized and hence
575 // the ports are opened.
576 ProcessSerialNumber psn;
577 GetCurrentProcess(&psn);
578 }
579 if (!ls_get_current_application_asn_func ||
580 !ls_set_application_information_item_func ||
581 !ls_display_name_key) {
582 return;
583 }
584
585 PrivateLSASN asn = ls_get_current_application_asn_func();
586 // Constant used by WebKit; what exactly it means is unknown.
587 const int magic_session_constant = -2;
588 OSErr err =
589 ls_set_application_information_item_func(magic_session_constant, asn,
590 ls_display_name_key,
591 process_name,
592 NULL /* optional out param */);
593 LOG_IF(ERROR, err) << "Call to set process name failed, err " << err;
594 }
595
596 // Converts a NSImage to a CGImageRef. Normally, the system frameworks can do
597 // this fine, especially on 10.6. On 10.5, however, CGImage cannot handle
598 // converting a PDF-backed NSImage into a CGImageRef. This function will
599 // rasterize the PDF into a bitmap CGImage. The caller is responsible for
600 // releasing the return value.
601 CGImageRef CopyNSImageToCGImage(NSImage* image) {
602 // This is based loosely on http://www.cocoadev.com/index.pl?CGImageRef .
603 NSSize size = [image size];
604 ScopedCFTypeRef<CGContextRef> context(
605 CGBitmapContextCreate(NULL, // Allow CG to allocate memory.
606 size.width,
607 size.height,
608 8, // bitsPerComponent
609 0, // bytesPerRow - CG will calculate by default.
610 [[NSColorSpace genericRGBColorSpace] CGColorSpace],
611 kCGBitmapByteOrder32Host |
612 kCGImageAlphaPremultipliedFirst));
613 if (!context.get())
614 return NULL;
615
616 [NSGraphicsContext saveGraphicsState];
617 [NSGraphicsContext setCurrentContext:
618 [NSGraphicsContext graphicsContextWithGraphicsPort:context.get()
619 flipped:NO]];
620 [image drawInRect:NSMakeRect(0,0, size.width, size.height)
621 fromRect:NSZeroRect
622 operation:NSCompositeCopy
623 fraction:1.0];
624 [NSGraphicsContext restoreGraphicsState];
625
626 return CGBitmapContextCreateImage(context);
627 }
628
629 bool CheckLoginItemStatus(bool* is_hidden) {
630 ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
631 if (!item.get())
632 return false;
633
634 if (is_hidden)
635 *is_hidden = IsHiddenLoginItem(item);
636
637 return true;
638 }
639
640 void AddToLoginItems(bool hide_on_startup) {
641 ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
642 if (item.get() && (IsHiddenLoginItem(item) == hide_on_startup)) {
643 return; // Already is a login item with required hide flag.
644 }
645
646 ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
647 NULL, kLSSharedFileListSessionLoginItems, NULL));
648
649 if (!login_items.get()) {
650 LOG(ERROR) << "Couldn't get a Login Items list.";
651 return;
652 }
653
654 // Remove the old item, it has wrong hide flag, we'll create a new one.
655 if (item.get()) {
656 LSSharedFileListItemRemove(login_items, item);
657 }
658
659 NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
660
661 BOOL hide = hide_on_startup ? YES : NO;
662 NSDictionary* properties =
663 [NSDictionary
664 dictionaryWithObject:[NSNumber numberWithBool:hide]
665 forKey:(NSString*)kLSSharedFileListLoginItemHidden];
666
667 ScopedCFTypeRef<LSSharedFileListItemRef> new_item;
668 new_item.reset(LSSharedFileListInsertItemURL(
669 login_items, kLSSharedFileListItemLast, NULL, NULL,
670 reinterpret_cast<CFURLRef>(url),
671 reinterpret_cast<CFDictionaryRef>(properties), NULL));
672
673 if (!new_item.get()) {
674 LOG(ERROR) << "Couldn't insert current app into Login Items list.";
675 }
676 }
677
678 void RemoveFromLoginItems() {
679 ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
680 if (!item.get())
681 return;
682
683 ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
684 NULL, kLSSharedFileListSessionLoginItems, NULL));
685
686 if (!login_items.get()) {
687 LOG(ERROR) << "Couldn't get a Login Items list.";
688 return;
689 }
690
691 LSSharedFileListItemRemove(login_items, item);
692 }
693
694 bool WasLaunchedAsHiddenLoginItem() {
695 if (!WasLaunchedAsLoginItem())
696 return false;
697
698 ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
699 if (!item.get()) {
700 LOG(ERROR) << "Process launched at Login but can't access Login Item List.";
701 return false;
702 }
703 return IsHiddenLoginItem(item);
704 }
705
706 void NSObjectRetain(void* obj) {
707 id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
708 [nsobj retain];
709 }
710
711 void NSObjectRelease(void* obj) {
712 id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
713 [nsobj release];
714 }
715
716 } // namespace mac_util
OLDNEW
« no previous file with comments | « base/mac_util.h ('k') | base/mac_util_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698