| // Copyright 2010-2014, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #import "base/mac_util.h" |
| |
| #import <Cocoa/Cocoa.h> |
| #import <Foundation/Foundation.h> |
| |
| #include <launch.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <IOKit/IOKitLib.h> |
| |
| #include "base/const.h" |
| #include "base/logging.h" |
| #include "base/scoped_cftyperef.h" |
| #include "base/singleton.h" |
| #include "base/util.h" |
| |
| namespace mozc { |
| namespace { |
| const char kServerDirectory[] = |
| "/Library/Input Methods/" kProductPrefix ".app/Contents/Resources"; |
| const unsigned char kPrelauncherPath[] = |
| "/Library/Input Methods/" kProductPrefix ".app/Contents/Resources/" |
| kProductPrefix "Prelauncher.app"; |
| |
| #ifdef GOOGLE_JAPANESE_INPUT_BUILD |
| const char kProjectPrefix[] = |
| "com.google.inputmethod.Japanese."; |
| #elif defined(MOZC_BUILD) |
| const char kProjectPrefix[] = |
| "org.mozc.inputmethod.Japanese."; |
| #else |
| #error Unknown branding |
| #endif |
| |
| // Returns the reference of prelauncher login item. |
| // If the prelauncher login item does not exist this function returns NULL. |
| // Otherwise you must release the reference. |
| LSSharedFileListItemRef GetPrelauncherLoginItem() { |
| LSSharedFileListItemRef prelauncher_item = NULL; |
| scoped_cftyperef<CFURLRef> url( |
| CFURLCreateFromFileSystemRepresentation( |
| kCFAllocatorDefault, kPrelauncherPath, |
| strlen((const char *)kPrelauncherPath), true)); |
| if (!url.get()) { |
| LOG(ERROR) << "CFURLCreateFromFileSystemRepresentation error:" |
| << " Cannot create CFURL object."; |
| return NULL; |
| } |
| |
| scoped_cftyperef<LSSharedFileListRef> login_items( |
| LSSharedFileListCreate( |
| kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, NULL)); |
| if (!login_items.get()) { |
| LOG(ERROR) << "LSSharedFileListCreate error: Cannot get the login items."; |
| return NULL; |
| } |
| |
| scoped_cftyperef<CFArrayRef> login_items_array( |
| LSSharedFileListCopySnapshot(login_items.get(), NULL)); |
| if (!login_items_array.get()) { |
| LOG(ERROR) << "LSSharedFileListCopySnapshot error:" |
| << " Cannot get the login items."; |
| return NULL; |
| } |
| |
| for(CFIndex i = 0; i < CFArrayGetCount(login_items_array.get()); ++i) { |
| LSSharedFileListItemRef item = |
| reinterpret_cast<LSSharedFileListItemRef>(const_cast<void *>( |
| CFArrayGetValueAtIndex(login_items_array.get(), i))); |
| if (!item) { |
| LOG(ERROR) << "CFArrayGetValueAtIndex error:" |
| << " Cannot get the login item."; |
| return NULL; |
| } |
| |
| CFURLRef item_url_ref = NULL; |
| if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr) { |
| if (!item_url_ref) { |
| LOG(ERROR) << "LSSharedFileListItemResolve error:" |
| << " Cannot get the login item url."; |
| return NULL; |
| } |
| if (CFEqual(item_url_ref, url.get())) { |
| prelauncher_item = item; |
| CFRetain(prelauncher_item); |
| } |
| } |
| } |
| |
| return prelauncher_item; |
| } |
| } // anonymous namespace |
| |
| string MacUtil::GetLabelForSuffix(const string &suffix) { |
| return string(kProjectPrefix) + suffix; |
| } |
| |
| string MacUtil::GetApplicationSupportDirectory() { |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| string dir; |
| NSArray *paths = NSSearchPathForDirectoriesInDomains( |
| NSApplicationSupportDirectory, NSUserDomainMask, YES); |
| if ([paths count] > 0) { |
| dir.assign([[paths objectAtIndex:0] fileSystemRepresentation]); |
| } |
| [pool drain]; |
| return dir; |
| } |
| |
| string MacUtil::GetLoggingDirectory() { |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| string dir; |
| NSArray *paths = NSSearchPathForDirectoriesInDomains( |
| NSLibraryDirectory, NSUserDomainMask, YES); |
| if ([paths count] > 0) { |
| dir.assign( |
| [[[[paths objectAtIndex:0] stringByAppendingPathComponent:@"Logs"] |
| stringByAppendingPathComponent:@kProductPrefix] |
| fileSystemRepresentation]); |
| } |
| [pool drain]; |
| return dir; |
| } |
| |
| string MacUtil::GetOSVersionString() { |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| string version; |
| version.assign([[[NSProcessInfo processInfo] operatingSystemVersionString] |
| cStringUsingEncoding : NSUTF8StringEncoding]); |
| [pool drain]; |
| return version; |
| } |
| |
| namespace { |
| |
| class OSVersionCache { |
| public: |
| OSVersionCache() : succeeded_(false) { |
| // TODO(horo): Gestalt function is deprecated in OS X v10.8. |
| // Consider how to get the version after 10.8. |
| if (Gestalt(gestaltSystemVersionMajor, &major_version_) == noErr && |
| Gestalt(gestaltSystemVersionMinor, &minor_version_) == noErr && |
| Gestalt(gestaltSystemVersionBugFix, &fix_version_) == noErr) { |
| succeeded_ = true; |
| } |
| } |
| bool GetOSVersion(int32 *major, int32 *minor, int32 *fix) const { |
| if (!succeeded_) { |
| return false; |
| } |
| *major = major_version_; |
| *minor = minor_version_; |
| *fix = fix_version_; |
| return true; |
| } |
| |
| private: |
| SInt32 major_version_; |
| SInt32 minor_version_; |
| SInt32 fix_version_; |
| bool succeeded_; |
| }; |
| |
| } // namespace |
| |
| bool MacUtil::GetOSVersion(int32 *major, int32 *minor, int32 *fix) { |
| return Singleton<OSVersionCache>::get()->GetOSVersion(major, minor, fix); |
| } |
| |
| bool MacUtil::OSVersionIsGreaterOrEqual(int32 major, int32 minor, int32 fix) { |
| int32 major_version = 0; |
| int32 minor_version = 0; |
| int32 fix_version = 0; |
| |
| if (!GetOSVersion(&major_version, &minor_version, &fix_version)) { |
| return false; |
| } |
| if ((major_version > major) || |
| ((major_version == major) && |
| ((minor_version > minor) || |
| ((minor_version == minor) && (fix_version >= fix))))) { |
| return true; |
| } |
| return false; |
| } |
| |
| string MacUtil::GetServerDirectory() { |
| return kServerDirectory; |
| } |
| |
| string MacUtil::GetResourcesDirectory() { |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| string result; |
| |
| NSBundle *mainBundle = [NSBundle mainBundle]; |
| if (mainBundle) { |
| NSString *resourcePath = [mainBundle resourcePath]; |
| if (resourcePath) { |
| result.assign([resourcePath fileSystemRepresentation]); |
| } |
| } |
| [pool drain]; |
| return result; |
| } |
| |
| string MacUtil::GetSerialNumber() { |
| NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| // Please refer to TN1103 for the details |
| // http://developer.apple.com/library/mac/#technotes/tn/tn1103.html |
| string result; |
| io_service_t platformExpert = IOServiceGetMatchingService( |
| kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); |
| |
| if (platformExpert) { |
| CFTypeRef serialNumberAsCFString = |
| IORegistryEntryCreateCFProperty( |
| platformExpert, CFSTR(kIOPlatformSerialNumberKey), |
| kCFAllocatorDefault, 0); |
| if (serialNumberAsCFString) { |
| const NSString *serialNumberNSString = reinterpret_cast<const NSString *>( |
| serialNumberAsCFString); |
| result.assign([serialNumberNSString UTF8String]); |
| } |
| |
| IOObjectRelease(platformExpert); |
| } |
| |
| [pool drain]; |
| // Return the empty string if failed. |
| return result; |
| } |
| |
| bool MacUtil::StartLaunchdService(const string &service_name, |
| pid_t *pid) { |
| int dummy_pid = 0; |
| if (pid == NULL) { |
| pid = &dummy_pid; |
| } |
| const string label = GetLabelForSuffix(service_name); |
| |
| launch_data_t start_renderer_command = |
| launch_data_alloc(LAUNCH_DATA_DICTIONARY); |
| launch_data_dict_insert(start_renderer_command, |
| launch_data_new_string(label.c_str()), |
| LAUNCH_KEY_STARTJOB); |
| launch_data_t result_data = launch_msg(start_renderer_command); |
| launch_data_free(start_renderer_command); |
| if (result_data == NULL) { |
| LOG(ERROR) << "Failed to launch the specified service"; |
| return false; |
| } |
| launch_data_free(result_data); |
| |
| // Getting PID by using launch_msg API. |
| launch_data_t get_renderer_info = |
| launch_data_alloc(LAUNCH_DATA_DICTIONARY); |
| launch_data_dict_insert(get_renderer_info, |
| launch_data_new_string(label.c_str()), |
| LAUNCH_KEY_GETJOB); |
| launch_data_t renderer_info = launch_msg(get_renderer_info); |
| launch_data_free(get_renderer_info); |
| if (renderer_info == NULL) { |
| LOG(ERROR) << "Unexpected error: launchd doesn't return the data " |
| << "for the service."; |
| return false; |
| } |
| |
| launch_data_t pid_data = launch_data_dict_lookup( |
| renderer_info, LAUNCH_JOBKEY_PID); |
| if (pid_data == NULL) { |
| LOG(ERROR) << |
| "Unexpected error: launchd response doesn't have PID"; |
| launch_data_free(renderer_info); |
| return false; |
| } |
| *pid = launch_data_get_integer(pid_data); |
| launch_data_free(renderer_info); |
| return true; |
| } |
| |
| bool MacUtil::CheckPrelauncherLoginItemStatus() { |
| scoped_cftyperef<LSSharedFileListItemRef> prelauncher_item( |
| GetPrelauncherLoginItem()); |
| return (prelauncher_item.get() != NULL); |
| } |
| |
| void MacUtil::RemovePrelauncherLoginItem() { |
| scoped_cftyperef<LSSharedFileListItemRef> prelauncher_item( |
| GetPrelauncherLoginItem()); |
| |
| if (!prelauncher_item.get()) { |
| DLOG(INFO) << "prelauncher_item not found. Probably not registered yet."; |
| return; |
| } |
| scoped_cftyperef<LSSharedFileListRef> login_items( |
| LSSharedFileListCreate( |
| kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, NULL)); |
| if (!login_items.get()) { |
| LOG(ERROR) << "LSSharedFileListCreate error: Cannot get the login items."; |
| return; |
| } |
| LSSharedFileListItemRemove(login_items.get(), prelauncher_item.get()); |
| } |
| |
| void MacUtil::AddPrelauncherLoginItem() { |
| if (CheckPrelauncherLoginItemStatus()) { |
| return; |
| } |
| scoped_cftyperef<LSSharedFileListRef> login_items( |
| LSSharedFileListCreate( |
| kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, NULL)); |
| if (!login_items.get()) { |
| LOG(ERROR) << "LSSharedFileListCreate error: Cannot get the login items."; |
| return; |
| } |
| scoped_cftyperef<CFURLRef> url( |
| CFURLCreateFromFileSystemRepresentation( |
| kCFAllocatorDefault, kPrelauncherPath, |
| strlen((const char *)kPrelauncherPath), true)); |
| |
| if (!url.get()) { |
| LOG(ERROR) << "CFURLCreateFromFileSystemRepresentation error:" |
| << " Cannot create CFURL object."; |
| return; |
| } |
| scoped_cftyperef<LSSharedFileListItemRef> new_item( |
| LSSharedFileListInsertItemURL( |
| login_items.get(), kLSSharedFileListItemLast, NULL, NULL, url.get(), |
| NULL, NULL)); |
| if (!new_item.get()) { |
| LOG(ERROR) << "LSSharedFileListInsertItemURL error:" |
| << " Cannot insert the prelauncher to the login items."; |
| return; |
| } |
| } |
| |
| bool MacUtil::GetFrontmostWindowNameAndOwner(string *name, string *owner) { |
| DCHECK(name); |
| DCHECK(owner); |
| scoped_cftyperef<CFArrayRef> window_list( |
| ::CGWindowListCopyWindowInfo( |
| kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, |
| kCGNullWindowID)); |
| const CFIndex window_count = CFArrayGetCount(window_list.get()); |
| for (CFIndex i = 0; i < window_count; ++i) { |
| const NSDictionary *window_data = static_cast<const NSDictionary *>( |
| CFArrayGetValueAtIndex(window_list.get(), i)); |
| if ([[window_data objectForKey:(id)kCGWindowSharingState] intValue] == |
| kCGWindowSharingNone) { |
| // Skips not shared window. |
| continue; |
| } |
| NSString *window_name = [window_data objectForKey:(id)kCGWindowName]; |
| NSString *owner_name = |
| [window_data objectForKey:(id)kCGWindowOwnerName]; |
| NSNumber *window_layer = [window_data objectForKey:(id)kCGWindowLayer]; |
| |
| if ((window_name == nil) || (owner_name == nil) || (window_layer == nil)) { |
| continue; |
| } |
| // Ignores the windows which aren't normal window level. |
| if ([window_layer intValue] != kCGNormalWindowLevel) { |
| continue; |
| } |
| |
| // Hack to ignore the window (name == "" and owner == "Google Chrome") |
| // Chrome browser seems to create a window which has no name in front of the |
| // actual frontmost Chrome window. |
| if ([window_name isEqualToString:@""] && |
| [owner_name isEqualToString:@"Google Chrome"]) { |
| continue; |
| } |
| name->assign([window_name UTF8String]); |
| owner->assign([owner_name UTF8String]); |
| return true; |
| } |
| return false; |
| } |
| |
| bool MacUtil::IsSuppressSuggestionWindow(const string &name, |
| const string &owner) { |
| // TODO(horo): Make a function to check the name, then share it with the |
| // Windows client. |
| // Currently we don't support "Firefox", because in Firefox "activateServer:" |
| // of IMKStateSetting Protocol is not called when the user changes the |
| // browsing tab. |
| return (("Google Chrome" == owner) || |
| ("Safari" == owner)) && |
| (("Google" == name) || |
| Util::EndsWith( |
| name, |
| " - Google \xE6\xA4\x9C\xE7\xB4\xA2") || // " - Google 検索" |
| Util::EndsWith(name, " - Google Search")); |
| } |
| |
| } // namespace mozc |