diff --git a/CMakeLists.txt b/CMakeLists.txt index 9099da42..95405c6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,7 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) FIND_LIBRARY(COCOA_LIBRARY Cocoa) + FIND_LIBRARY(SECURITY_LIBRARY Security) set( BASE_ARCH_LIBRARIES @@ -208,6 +209,9 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") BASE_ARCH_SOURCES src/base/threading/platform_thread_mac.mm + src/base/strings/sys_string_conversions_mac.mm + src/base/mac/foundation_util.mm + src/base/mac/bundle_locations.mm src/base/mac/mach_logging.cc src/base/mac/scoped_mach_port.cc src/base/time/time_mac.cc diff --git a/DEPS b/DEPS index 7fb266df..a2dfac44 100644 --- a/DEPS +++ b/DEPS @@ -114,6 +114,15 @@ "base/base_paths_win.h", "base/message_loop/message_loop.h" ] + }, + { + "from": "base/strings/sys_string_conversions_mac.mm", + "exclude": [ + "base/debug/debugger.h", + "base/sequence_checker.h", + "base/files/file.h", + "base/tracked_objects.h" + ] } ], "manual_dependency": [ diff --git a/getdep.py b/getdep.py index ad87f990..61cbcf16 100644 --- a/getdep.py +++ b/getdep.py @@ -52,6 +52,10 @@ def enq(new, now): enq(now[:-1] + 'cc', now) depmap[now].append(now[:-1] + 'cc') + if now.endswith(".h") and os.path.exists(self.realpath(now[:-1] + 'mm')): + enq(now[:-1] + 'mm', now) + depmap[now].append(now[:-1] + 'mm') + for dependency in self.parse_cc(now): enq(dependency, now) # print now, dependency diff --git a/src/base/base_paths_mac.mm b/src/base/base_paths_mac.mm new file mode 100644 index 00000000..af4bd1a8 --- /dev/null +++ b/src/base/base_paths_mac.mm @@ -0,0 +1,115 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines base::PathProviderMac which replaces base::PathProviderPosix for Mac +// in base/path_service.cc. + +#include +#import +#include + +#include "base/base_paths.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" +#include "build/build_config.h" + +namespace { + +void GetNSExecutablePath(base::FilePath* path) { + DCHECK(path); + // Executable path can have relative references ("..") depending on + // how the app was launched. + uint32_t executable_length = 0; + _NSGetExecutablePath(NULL, &executable_length); + DCHECK_GT(executable_length, 1u); + std::string executable_path; + int rv = _NSGetExecutablePath( + base::WriteInto(&executable_path, executable_length), + &executable_length); + DCHECK_EQ(rv, 0); + + // _NSGetExecutablePath may return paths containing ./ or ../ which makes + // FilePath::DirName() work incorrectly, convert it to absolute path so that + // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to + // be returned here. + *path = base::MakeAbsoluteFilePath(base::FilePath(executable_path)); +} + +// Returns true if the module for |address| is found. |path| will contain +// the path to the module. Note that |path| may not be absolute. +bool GetModulePathForAddress(base::FilePath* path, + const void* address) WARN_UNUSED_RESULT; + +bool GetModulePathForAddress(base::FilePath* path, const void* address) { + Dl_info info; + if (dladdr(address, &info) == 0) + return false; + *path = base::FilePath(info.dli_fname); + return true; +} + +} // namespace + +namespace base { + +bool PathProviderMac(int key, base::FilePath* result) { + switch (key) { + case base::FILE_EXE: + GetNSExecutablePath(result); + return true; + case base::FILE_MODULE: + return GetModulePathForAddress(result, + reinterpret_cast(&base::PathProviderMac)); + case base::DIR_APP_DATA: { + bool success = base::mac::GetUserDirectory(NSApplicationSupportDirectory, + result); +#if defined(OS_IOS) + // On IOS, this directory does not exist unless it is created explicitly. + if (success && !base::PathExists(*result)) + success = base::CreateDirectory(*result); +#endif // defined(OS_IOS) + return success; + } + case base::DIR_SOURCE_ROOT: + // Go through PathService to catch overrides. + if (!PathService::Get(base::FILE_EXE, result)) + return false; + + // Start with the executable's directory. + *result = result->DirName(); + +#if !defined(OS_IOS) + if (base::mac::AmIBundled()) { + // The bundled app executables (Chromium, TestShell, etc) live five + // levels down, eg: + // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium + *result = result->DirName().DirName().DirName().DirName().DirName(); + } else { + // Unit tests execute two levels deep from the source root, eg: + // src/xcodebuild/{Debug|Release}/base_unittests + *result = result->DirName().DirName(); + } +#endif + return true; + case base::DIR_USER_DESKTOP: +#if defined(OS_IOS) + // iOS does not have desktop directories. + NOTIMPLEMENTED(); + return false; +#else + return base::mac::GetUserDirectory(NSDesktopDirectory, result); +#endif + case base::DIR_CACHE: + return base::mac::GetUserDirectory(NSCachesDirectory, result); + default: + return false; + } +} + +} // namespace base diff --git a/src/base/mac/bundle_locations.h b/src/base/mac/bundle_locations.h new file mode 100644 index 00000000..276290b0 --- /dev/null +++ b/src/base/mac/bundle_locations.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_BUNDLE_LOCATIONS_H_ +#define BASE_MAC_BUNDLE_LOCATIONS_H_ + +#include "base/base_export.h" +#include "base/files/file_path.h" + +#if defined(__OBJC__) +#import +#else // __OBJC__ +class NSBundle; +class NSString; +#endif // __OBJC__ + +namespace base { + +class FilePath; + +namespace mac { + +// This file provides several functions to explicitly request the various +// component bundles of Chrome. Please use these methods rather than calling +// +[NSBundle mainBundle] or CFBundleGetMainBundle(). +// +// Terminology +// - "Outer Bundle" - This is the main bundle for Chrome; it's what +// +[NSBundle mainBundle] returns when Chrome is launched normally. +// +// - "Main Bundle" - This is the bundle from which Chrome was launched. +// This will be the same as the outer bundle except when Chrome is launched +// via an app shortcut, in which case this will return the app shortcut's +// bundle rather than the main Chrome bundle. +// +// - "Framework Bundle" - This is the bundle corresponding to the Chrome +// framework. +// +// Guidelines for use: +// - To access a resource, the Framework bundle should be used. +// - If the choice is between the Outer or Main bundles then please choose +// carefully. Most often the Outer bundle will be the right choice, but for +// cases such as adding an app to the "launch on startup" list, the Main +// bundle is probably the one to use. + +// Methods for retrieving the various bundles. +BASE_EXPORT NSBundle* MainBundle(); +BASE_EXPORT FilePath MainBundlePath(); +BASE_EXPORT NSBundle* OuterBundle(); +BASE_EXPORT FilePath OuterBundlePath(); +BASE_EXPORT NSBundle* FrameworkBundle(); +BASE_EXPORT FilePath FrameworkBundlePath(); + +// Set the bundle that the preceding functions will return, overriding the +// default values. Restore the default by passing in |nil|. +BASE_EXPORT void SetOverrideOuterBundle(NSBundle* bundle); +BASE_EXPORT void SetOverrideFrameworkBundle(NSBundle* bundle); + +// Same as above but accepting a FilePath argument. +BASE_EXPORT void SetOverrideOuterBundlePath(const FilePath& file_path); +BASE_EXPORT void SetOverrideFrameworkBundlePath(const FilePath& file_path); + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_BUNDLE_LOCATIONS_H_ diff --git a/src/base/mac/bundle_locations.mm b/src/base/mac/bundle_locations.mm new file mode 100644 index 00000000..54021b85 --- /dev/null +++ b/src/base/mac/bundle_locations.mm @@ -0,0 +1,83 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/bundle_locations.h" + +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/strings/sys_string_conversions.h" + +namespace base { +namespace mac { + +// NSBundle isn't threadsafe, all functions in this file must be called on the +// main thread. +static NSBundle* g_override_framework_bundle = nil; +static NSBundle* g_override_outer_bundle = nil; + +NSBundle* MainBundle() { + return [NSBundle mainBundle]; +} + +FilePath MainBundlePath() { + NSBundle* bundle = MainBundle(); + return NSStringToFilePath([bundle bundlePath]); +} + +NSBundle* OuterBundle() { + if (g_override_outer_bundle) + return g_override_outer_bundle; + return [NSBundle mainBundle]; +} + +FilePath OuterBundlePath() { + NSBundle* bundle = OuterBundle(); + return NSStringToFilePath([bundle bundlePath]); +} + +NSBundle* FrameworkBundle() { + if (g_override_framework_bundle) + return g_override_framework_bundle; + return [NSBundle mainBundle]; +} + +FilePath FrameworkBundlePath() { + NSBundle* bundle = FrameworkBundle(); + return NSStringToFilePath([bundle bundlePath]); +} + +static void AssignOverrideBundle(NSBundle* new_bundle, + NSBundle** override_bundle) { + if (new_bundle != *override_bundle) { + [*override_bundle release]; + *override_bundle = [new_bundle retain]; + } +} + +static void AssignOverridePath(const FilePath& file_path, + NSBundle** override_bundle) { + NSString* path = base::SysUTF8ToNSString(file_path.value()); + NSBundle* new_bundle = [NSBundle bundleWithPath:path]; + DCHECK(new_bundle) << "Failed to load the bundle at " << file_path.value(); + AssignOverrideBundle(new_bundle, override_bundle); +} + +void SetOverrideOuterBundle(NSBundle* bundle) { + AssignOverrideBundle(bundle, &g_override_outer_bundle); +} + +void SetOverrideFrameworkBundle(NSBundle* bundle) { + AssignOverrideBundle(bundle, &g_override_framework_bundle); +} + +void SetOverrideOuterBundlePath(const FilePath& file_path) { + AssignOverridePath(file_path, &g_override_outer_bundle); +} + +void SetOverrideFrameworkBundlePath(const FilePath& file_path) { + AssignOverridePath(file_path, &g_override_framework_bundle); +} + +} // namespace mac +} // namespace base diff --git a/src/base/mac/foundation_util.h b/src/base/mac/foundation_util.h new file mode 100644 index 00000000..6e8505da --- /dev/null +++ b/src/base/mac/foundation_util.h @@ -0,0 +1,397 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_FOUNDATION_UTIL_H_ +#define BASE_MAC_FOUNDATION_UTIL_H_ + +#include + +#include +#include + +#include "base/base_export.h" +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" + +#if defined(__OBJC__) +#import +@class NSFont; +@class UIFont; +#else // __OBJC__ +#include +class NSBundle; +class NSFont; +class NSString; +class UIFont; +#endif // __OBJC__ + +#if defined(OS_IOS) +#include +#else +#include +#endif + +// Adapted from NSObjCRuntime.h NS_ENUM definition (used in Foundation starting +// with the OS X 10.8 SDK and the iOS 6.0 SDK). +#if __has_extension(cxx_strong_enums) && \ + (defined(OS_IOS) || (defined(MAC_OS_X_VERSION_10_8) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)) +#define CR_FORWARD_ENUM(_type, _name) enum _name : _type _name +#else +#define CR_FORWARD_ENUM(_type, _name) _type _name +#endif + +// Adapted from NSPathUtilities.h and NSObjCRuntime.h. +#if __LP64__ || NS_BUILD_32_LIKE_64 +typedef CR_FORWARD_ENUM(unsigned long, NSSearchPathDirectory); +typedef unsigned long NSSearchPathDomainMask; +#else +typedef CR_FORWARD_ENUM(unsigned int, NSSearchPathDirectory); +typedef unsigned int NSSearchPathDomainMask; +#endif + +typedef struct OpaqueSecTrustRef* SecACLRef; +typedef struct OpaqueSecTrustedApplicationRef* SecTrustedApplicationRef; + +namespace base { + +class FilePath; + +namespace mac { + +// Returns true if the application is running from a bundle +BASE_EXPORT bool AmIBundled(); +BASE_EXPORT void SetOverrideAmIBundled(bool value); + +#if defined(UNIT_TEST) +// This is required because instantiating some tests requires checking the +// directory structure, which sets the AmIBundled cache state. Individual tests +// may or may not be bundled, and this would trip them up if the cache weren't +// cleared. This should not be called from individual tests, just from test +// instantiation code that gets a path from PathService. +BASE_EXPORT void ClearAmIBundledCache(); +#endif + +// Returns true if this process is marked as a "Background only process". +BASE_EXPORT bool IsBackgroundOnlyProcess(); + +// Returns the path to a resource within the framework bundle. +BASE_EXPORT FilePath PathForFrameworkBundleResource(CFStringRef resourceName); + +// Returns the creator code associated with the CFBundleRef at bundle. +OSType CreatorCodeForCFBundleRef(CFBundleRef bundle); + +// Returns the creator code associated with this application, by calling +// CreatorCodeForCFBundleRef for the application's main bundle. If this +// information cannot be determined, returns kUnknownType ('????'). This +// does not respect the override app bundle because it's based on CFBundle +// instead of NSBundle, and because callers probably don't want the override +// app bundle's creator code anyway. +BASE_EXPORT OSType CreatorCodeForApplication(); + +// Searches for directories for the given key in only the given |domain_mask|. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +BASE_EXPORT bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result); + +// Searches for directories for the given key in only the local domain. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +BASE_EXPORT bool GetLocalDirectory(NSSearchPathDirectory directory, + FilePath* result); + +// Searches for directories for the given key in only the user domain. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +BASE_EXPORT bool GetUserDirectory(NSSearchPathDirectory directory, + FilePath* result); + +// Returns the ~/Library directory. +BASE_EXPORT FilePath GetUserLibraryPath(); + +// Takes a path to an (executable) binary and tries to provide the path to an +// application bundle containing it. It takes the outermost bundle that it can +// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). +// |exec_name| - path to the binary +// returns - path to the application bundle, or empty on error +BASE_EXPORT FilePath GetAppBundlePath(const FilePath& exec_name); + +#define TYPE_NAME_FOR_CF_TYPE_DECL(TypeCF) \ +BASE_EXPORT std::string TypeNameForCFType(TypeCF##Ref); + +TYPE_NAME_FOR_CF_TYPE_DECL(CFArray); +TYPE_NAME_FOR_CF_TYPE_DECL(CFBag); +TYPE_NAME_FOR_CF_TYPE_DECL(CFBoolean); +TYPE_NAME_FOR_CF_TYPE_DECL(CFData); +TYPE_NAME_FOR_CF_TYPE_DECL(CFDate); +TYPE_NAME_FOR_CF_TYPE_DECL(CFDictionary); +TYPE_NAME_FOR_CF_TYPE_DECL(CFNull); +TYPE_NAME_FOR_CF_TYPE_DECL(CFNumber); +TYPE_NAME_FOR_CF_TYPE_DECL(CFSet); +TYPE_NAME_FOR_CF_TYPE_DECL(CFString); +TYPE_NAME_FOR_CF_TYPE_DECL(CFURL); +TYPE_NAME_FOR_CF_TYPE_DECL(CFUUID); + +TYPE_NAME_FOR_CF_TYPE_DECL(CGColor); + +TYPE_NAME_FOR_CF_TYPE_DECL(CTFont); +TYPE_NAME_FOR_CF_TYPE_DECL(CTRun); + +#undef TYPE_NAME_FOR_CF_TYPE_DECL + +// Retain/release calls for memory management in C++. +BASE_EXPORT void NSObjectRetain(void* obj); +BASE_EXPORT void NSObjectRelease(void* obj); + +// CFTypeRefToNSObjectAutorelease transfers ownership of a Core Foundation +// object (one derived from CFTypeRef) to the Foundation memory management +// system. In a traditional managed-memory environment, cf_object is +// autoreleased and returned as an NSObject. In a garbage-collected +// environment, cf_object is marked as eligible for garbage collection. +// +// This function should only be used to convert a concrete CFTypeRef type to +// its equivalent "toll-free bridged" NSObject subclass, for example, +// converting a CFStringRef to NSString. +// +// By calling this function, callers relinquish any ownership claim to +// cf_object. In a managed-memory environment, the object's ownership will be +// managed by the innermost NSAutoreleasePool, so after this function returns, +// callers should not assume that cf_object is valid any longer than the +// returned NSObject. +// +// Returns an id, typed here for C++'s sake as a void*. +BASE_EXPORT void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object); + +// Returns the base bundle ID, which can be set by SetBaseBundleID but +// defaults to a reasonable string. This never returns NULL. BaseBundleID +// returns a pointer to static storage that must not be freed. +BASE_EXPORT const char* BaseBundleID(); + +// Sets the base bundle ID to override the default. The implementation will +// make its own copy of new_base_bundle_id. +BASE_EXPORT void SetBaseBundleID(const char* new_base_bundle_id); + +} // namespace mac +} // namespace base + +#if !defined(__OBJC__) +#define OBJC_CPP_CLASS_DECL(x) class x; +#else // __OBJC__ +#define OBJC_CPP_CLASS_DECL(x) +#endif // __OBJC__ + +// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not +// autorelease |cf_val|. This is useful for the case where there is a CFType in +// a call that expects an NSType and the compiler is complaining about const +// casting problems. +// The calls are used like this: +// NSString *foo = CFToNSCast(CFSTR("Hello")); +// CFStringRef foo2 = NSToCFCast(@"Hello"); +// The macro magic below is to enforce safe casting. It could possibly have +// been done using template function specialization, but template function +// specialization doesn't always work intuitively, +// (http://www.gotw.ca/publications/mill17.htm) so the trusty combination +// of macros and function overloading is used instead. + +#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \ +OBJC_CPP_CLASS_DECL(TypeNS) \ +\ +namespace base { \ +namespace mac { \ +BASE_EXPORT TypeNS* CFToNSCast(TypeCF##Ref cf_val); \ +BASE_EXPORT TypeCF##Ref NSToCFCast(TypeNS* ns_val); \ +} \ +} + +#define CF_TO_NS_MUTABLE_CAST_DECL(name) \ +CF_TO_NS_CAST_DECL(CF##name, NS##name) \ +OBJC_CPP_CLASS_DECL(NSMutable##name) \ +\ +namespace base { \ +namespace mac { \ +BASE_EXPORT NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \ +BASE_EXPORT CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \ +} \ +} + +// List of toll-free bridged types taken from: +// http://www.cocoadev.com/index.pl?TollFreeBridged + +CF_TO_NS_MUTABLE_CAST_DECL(Array); +CF_TO_NS_MUTABLE_CAST_DECL(AttributedString); +CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar); +CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet); +CF_TO_NS_MUTABLE_CAST_DECL(Data); +CF_TO_NS_CAST_DECL(CFDate, NSDate); +CF_TO_NS_MUTABLE_CAST_DECL(Dictionary); +CF_TO_NS_CAST_DECL(CFError, NSError); +CF_TO_NS_CAST_DECL(CFLocale, NSLocale); +CF_TO_NS_CAST_DECL(CFNumber, NSNumber); +CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer); +CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone); +CF_TO_NS_MUTABLE_CAST_DECL(Set); +CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream); +CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream); +CF_TO_NS_MUTABLE_CAST_DECL(String); +CF_TO_NS_CAST_DECL(CFURL, NSURL); + +#if defined(OS_IOS) +CF_TO_NS_CAST_DECL(CTFont, UIFont); +#else +CF_TO_NS_CAST_DECL(CTFont, NSFont); +#endif + +#undef CF_TO_NS_CAST_DECL +#undef CF_TO_NS_MUTABLE_CAST_DECL +#undef OBJC_CPP_CLASS_DECL + +namespace base { +namespace mac { + +// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more +// specific CoreFoundation type. The compatibility of the passed +// object is found by comparing its opaque type against the +// requested type identifier. If the supplied object is not +// compatible with the requested return type, CFCast<>() returns +// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer +// to either variant results in NULL being returned without +// triggering any DCHECK. +// +// Example usage: +// CFNumberRef some_number = base::mac::CFCast( +// CFArrayGetValueAtIndex(array, index)); +// +// CFTypeRef hello = CFSTR("hello world"); +// CFStringRef some_string = base::mac::CFCastStrict(hello); + +template +T CFCast(const CFTypeRef& cf_val); + +template +T CFCastStrict(const CFTypeRef& cf_val); + +#define CF_CAST_DECL(TypeCF) \ +template<> BASE_EXPORT TypeCF##Ref \ +CFCast(const CFTypeRef& cf_val);\ +\ +template<> BASE_EXPORT TypeCF##Ref \ +CFCastStrict(const CFTypeRef& cf_val); + +CF_CAST_DECL(CFArray); +CF_CAST_DECL(CFBag); +CF_CAST_DECL(CFBoolean); +CF_CAST_DECL(CFData); +CF_CAST_DECL(CFDate); +CF_CAST_DECL(CFDictionary); +CF_CAST_DECL(CFNull); +CF_CAST_DECL(CFNumber); +CF_CAST_DECL(CFSet); +CF_CAST_DECL(CFString); +CF_CAST_DECL(CFURL); +CF_CAST_DECL(CFUUID); + +CF_CAST_DECL(CGColor); + +CF_CAST_DECL(CTFont); +CF_CAST_DECL(CTFontDescriptor); +CF_CAST_DECL(CTRun); + +CF_CAST_DECL(SecACL); +CF_CAST_DECL(SecTrustedApplication); + +#undef CF_CAST_DECL + +#if defined(__OBJC__) + +// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more +// specific (NSObject-derived) type. The compatibility of the passed +// object is found by checking if it's a kind of the requested type +// identifier. If the supplied object is not compatible with the +// requested return type, ObjCCast<>() returns nil and +// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either +// variant results in nil being returned without triggering any DCHECK. +// +// The strict variant is useful when retrieving a value from a +// collection which only has values of a specific type, e.g. an +// NSArray of NSStrings. The non-strict variant is useful when +// retrieving values from data that you can't fully control. For +// example, a plist read from disk may be beyond your exclusive +// control, so you'd only want to check that the values you retrieve +// from it are of the expected types, but not crash if they're not. +// +// Example usage: +// NSString* version = base::mac::ObjCCast( +// [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); +// +// NSString* str = base::mac::ObjCCastStrict( +// [ns_arr_of_ns_strs objectAtIndex:0]); +template +T* ObjCCast(id objc_val) { + if ([objc_val isKindOfClass:[T class]]) { + return reinterpret_cast(objc_val); + } + return nil; +} + +template +T* ObjCCastStrict(id objc_val) { + T* rv = ObjCCast(objc_val); + DCHECK(objc_val == nil || rv); + return rv; +} + +#endif // defined(__OBJC__) + +// Helper function for GetValueFromDictionary to create the error message +// that appears when a type mismatch is encountered. +BASE_EXPORT std::string GetValueFromDictionaryErrorMessage( + CFStringRef key, const std::string& expected_type, CFTypeRef value); + +// Utility function to pull out a value from a dictionary, check its type, and +// return it. Returns NULL if the key is not present or of the wrong type. +template +T GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key) { + CFTypeRef value = CFDictionaryGetValue(dict, key); + T value_specific = CFCast(value); + + if (value && !value_specific) { + std::string expected_type = TypeNameForCFType(value_specific); + DLOG(WARNING) << GetValueFromDictionaryErrorMessage(key, + expected_type, + value); + } + + return value_specific; +} + +// Converts |path| to an autoreleased NSString. Returns nil if |path| is empty. +BASE_EXPORT NSString* FilePathToNSString(const FilePath& path); + +// Converts |str| to a FilePath. Returns an empty path if |str| is nil. +BASE_EXPORT FilePath NSStringToFilePath(NSString* str); + +#if defined(__OBJC__) +// Converts |range| to an NSRange, returning the new range in |range_out|. +// Returns true if conversion was successful, false if the values of |range| +// could not be converted to NSUIntegers. +BASE_EXPORT bool CFRangeToNSRange(CFRange range, + NSRange* range_out) WARN_UNUSED_RESULT; +#endif // defined(__OBJC__) + +} // namespace mac +} // namespace base + +// Stream operations for CFTypes. They can be used with NSTypes as well +// by using the NSToCFCast methods above. +// e.g. LOG(INFO) << base::mac::NSToCFCast(@"foo"); +// Operator << can not be overloaded for ObjectiveC types as the compiler +// can not distinguish between overloads for id with overloads for void*. +BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, + const CFErrorRef err); +BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, + const CFStringRef str); + +#endif // BASE_MAC_FOUNDATION_UTIL_H_ diff --git a/src/base/mac/foundation_util.mm b/src/base/mac/foundation_util.mm new file mode 100644 index 00000000..bd5d5145 --- /dev/null +++ b/src/base/mac/foundation_util.mm @@ -0,0 +1,469 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/foundation_util.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/mac/bundle_locations.h" +#include "base/mac/mac_logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/sys_string_conversions.h" + +#if !defined(OS_IOS) +extern "C" { +CFTypeID SecACLGetTypeID(); +CFTypeID SecTrustedApplicationGetTypeID(); +Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj); +} // extern "C" +#endif + +namespace base { +namespace mac { + +namespace { + +bool g_cached_am_i_bundled_called = false; +bool g_cached_am_i_bundled_value = false; +bool g_override_am_i_bundled = false; +bool g_override_am_i_bundled_value = false; + +bool UncachedAmIBundled() { +#if defined(OS_IOS) + // All apps are bundled on iOS. + return true; +#else + if (g_override_am_i_bundled) + return g_override_am_i_bundled_value; + + // Yes, this is cheap. + return [[base::mac::OuterBundle() bundlePath] hasSuffix:@".app"]; +#endif +} + +} // namespace + +bool AmIBundled() { + // If the return value is not cached, this function will return different + // values depending on when it's called. This confuses some client code, see + // http://crbug.com/63183 . + if (!g_cached_am_i_bundled_called) { + g_cached_am_i_bundled_called = true; + g_cached_am_i_bundled_value = UncachedAmIBundled(); + } + DCHECK_EQ(g_cached_am_i_bundled_value, UncachedAmIBundled()) + << "The return value of AmIBundled() changed. This will confuse tests. " + << "Call SetAmIBundled() override manually if your test binary " + << "delay-loads the framework."; + return g_cached_am_i_bundled_value; +} + +void SetOverrideAmIBundled(bool value) { +#if defined(OS_IOS) + // It doesn't make sense not to be bundled on iOS. + if (!value) + NOTREACHED(); +#endif + g_override_am_i_bundled = true; + g_override_am_i_bundled_value = value; +} + +BASE_EXPORT void ClearAmIBundledCache() { + g_cached_am_i_bundled_called = false; +} + +bool IsBackgroundOnlyProcess() { + // This function really does want to examine NSBundle's idea of the main + // bundle dictionary. It needs to look at the actual running .app's + // Info.plist to access its LSUIElement property. + NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; + return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; +} + +FilePath PathForFrameworkBundleResource(CFStringRef resourceName) { + NSBundle* bundle = base::mac::FrameworkBundle(); + NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName + ofType:nil]; + return NSStringToFilePath(resourcePath); +} + +OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { + OSType creator = kUnknownType; + CFBundleGetPackageInfo(bundle, NULL, &creator); + return creator; +} + +OSType CreatorCodeForApplication() { + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return kUnknownType; + + return CreatorCodeForCFBundleRef(bundle); +} + +bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result) { + DCHECK(result); + NSArray* dirs = + NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); + if ([dirs count] < 1) { + return false; + } + *result = NSStringToFilePath([dirs objectAtIndex:0]); + return true; +} + +bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSLocalDomainMask, result); +} + +bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSUserDomainMask, result); +} + +FilePath GetUserLibraryPath() { + FilePath user_library_path; + if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { + DLOG(WARNING) << "Could not get user library path"; + } + return user_library_path; +} + +// Takes a path to an (executable) binary and tries to provide the path to an +// application bundle containing it. It takes the outermost bundle that it can +// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). +// |exec_name| - path to the binary +// returns - path to the application bundle, or empty on error +FilePath GetAppBundlePath(const FilePath& exec_name) { + const char kExt[] = ".app"; + const size_t kExtLength = arraysize(kExt) - 1; + + // Split the path into components. + std::vector components; + exec_name.GetComponents(&components); + + // It's an error if we don't get any components. + if (!components.size()) + return FilePath(); + + // Don't prepend '/' to the first component. + std::vector::const_iterator it = components.begin(); + std::string bundle_name = *it; + DCHECK_GT(it->length(), 0U); + // If the first component ends in ".app", we're already done. + if (it->length() > kExtLength && + !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) + return FilePath(bundle_name); + + // The first component may be "/" or "//", etc. Only append '/' if it doesn't + // already end in '/'. + if (bundle_name[bundle_name.length() - 1] != '/') + bundle_name += '/'; + + // Go through the remaining components. + for (++it; it != components.end(); ++it) { + DCHECK_GT(it->length(), 0U); + + bundle_name += *it; + + // If the current component ends in ".app", we're done. + if (it->length() > kExtLength && + !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) + return FilePath(bundle_name); + + // Separate this component from the next one. + bundle_name += '/'; + } + + return FilePath(); +} + +#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \ +std::string TypeNameForCFType(TypeCF##Ref) { \ + return #TypeCF; \ +} + +TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFData); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFString); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL); +TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID); + +TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor); + +TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont); +TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun); + +#undef TYPE_NAME_FOR_CF_TYPE_DEFN + +void NSObjectRetain(void* obj) { + id nsobj = static_cast >(obj); + [nsobj retain]; +} + +void NSObjectRelease(void* obj) { + id nsobj = static_cast >(obj); + [nsobj release]; +} + +void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) { + // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease + // is a no-op. + // + // In the traditional GC-less environment, NSMakeCollectable is a no-op, + // and cf_object is autoreleased, balancing out the caller's ownership claim. + // + // NSMakeCollectable returns nil when used on a NULL object. + return [NSMakeCollectable(cf_object) autorelease]; +} + +static const char* base_bundle_id; + +const char* BaseBundleID() { + if (base_bundle_id) { + return base_bundle_id; + } + +#if defined(GOOGLE_CHROME_BUILD) + return "com.google.Chrome"; +#else + return "org.chromium.Chromium"; +#endif +} + +void SetBaseBundleID(const char* new_base_bundle_id) { + if (new_base_bundle_id != base_bundle_id) { + free((void*)base_bundle_id); + base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL; + } +} + +// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in +// foundation_util.h. +#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ +\ +TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + TypeNS* ns_val = \ + const_cast(reinterpret_cast(cf_val)); \ + return ns_val; \ +} \ +\ +TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ + TypeCF##Ref cf_val = reinterpret_cast(ns_val); \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} + +#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ +CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ +\ +NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + NSMutable##name* ns_val = reinterpret_cast(cf_val); \ + return ns_val; \ +} \ +\ +CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ + CFMutable##name##Ref cf_val = \ + reinterpret_cast(ns_val); \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} + +CF_TO_NS_MUTABLE_CAST_DEFN(Array); +CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString); +CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar); +CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet); +CF_TO_NS_MUTABLE_CAST_DEFN(Data); +CF_TO_NS_CAST_DEFN(CFDate, NSDate); +CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary); +CF_TO_NS_CAST_DEFN(CFError, NSError); +CF_TO_NS_CAST_DEFN(CFLocale, NSLocale); +CF_TO_NS_CAST_DEFN(CFNumber, NSNumber); +CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer); +CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone); +CF_TO_NS_MUTABLE_CAST_DEFN(Set); +CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream); +CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream); +CF_TO_NS_MUTABLE_CAST_DEFN(String); +CF_TO_NS_CAST_DEFN(CFURL, NSURL); + +#if defined(OS_IOS) +CF_TO_NS_CAST_DEFN(CTFont, UIFont); +#else +// The NSFont/CTFont toll-free bridging is broken when it comes to type +// checking, so do some special-casing. +// http://www.openradar.me/15341349 rdar://15341349 +NSFont* CFToNSCast(CTFontRef cf_val) { + NSFont* ns_val = + const_cast(reinterpret_cast(cf_val)); + DCHECK(!cf_val || + CTFontGetTypeID() == CFGetTypeID(cf_val) || + (_CFIsObjC(CTFontGetTypeID(), cf_val) && + [ns_val isKindOfClass:NSClassFromString(@"NSFont")])); + return ns_val; +} + +CTFontRef NSToCFCast(NSFont* ns_val) { + CTFontRef cf_val = reinterpret_cast(ns_val); + DCHECK(!cf_val || + CTFontGetTypeID() == CFGetTypeID(cf_val) || + [ns_val isKindOfClass:NSClassFromString(@"NSFont")]); + return cf_val; +} +#endif + +#undef CF_TO_NS_CAST_DEFN +#undef CF_TO_NS_MUTABLE_CAST_DEFN + +#define CF_CAST_DEFN(TypeCF) \ +template<> TypeCF##Ref \ +CFCast(const CFTypeRef& cf_val) { \ + if (cf_val == NULL) { \ + return NULL; \ + } \ + if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ + return (TypeCF##Ref)(cf_val); \ + } \ + return NULL; \ +} \ +\ +template<> TypeCF##Ref \ +CFCastStrict(const CFTypeRef& cf_val) { \ + TypeCF##Ref rv = CFCast(cf_val); \ + DCHECK(cf_val == NULL || rv); \ + return rv; \ +} + +CF_CAST_DEFN(CFArray); +CF_CAST_DEFN(CFBag); +CF_CAST_DEFN(CFBoolean); +CF_CAST_DEFN(CFData); +CF_CAST_DEFN(CFDate); +CF_CAST_DEFN(CFDictionary); +CF_CAST_DEFN(CFNull); +CF_CAST_DEFN(CFNumber); +CF_CAST_DEFN(CFSet); +CF_CAST_DEFN(CFString); +CF_CAST_DEFN(CFURL); +CF_CAST_DEFN(CFUUID); + +CF_CAST_DEFN(CGColor); + +CF_CAST_DEFN(CTFontDescriptor); +CF_CAST_DEFN(CTRun); + +#if defined(OS_IOS) +CF_CAST_DEFN(CTFont); +#else +// The NSFont/CTFont toll-free bridging is broken when it comes to type +// checking, so do some special-casing. +// http://www.openradar.me/15341349 rdar://15341349 +template<> CTFontRef +CFCast(const CFTypeRef& cf_val) { + if (cf_val == NULL) { + return NULL; + } + if (CFGetTypeID(cf_val) == CTFontGetTypeID()) { + return (CTFontRef)(cf_val); + } + + if (!_CFIsObjC(CTFontGetTypeID(), cf_val)) + return NULL; + + id ns_val = reinterpret_cast(const_cast(cf_val)); + if ([ns_val isKindOfClass:NSClassFromString(@"NSFont")]) { + return (CTFontRef)(cf_val); + } + return NULL; +} + +template<> CTFontRef +CFCastStrict(const CFTypeRef& cf_val) { + CTFontRef rv = CFCast(cf_val); + DCHECK(cf_val == NULL || rv); + return rv; +} +#endif + +#if !defined(OS_IOS) +CF_CAST_DEFN(SecACL); +CF_CAST_DEFN(SecTrustedApplication); +#endif + +#undef CF_CAST_DEFN + +std::string GetValueFromDictionaryErrorMessage( + CFStringRef key, const std::string& expected_type, CFTypeRef value) { + ScopedCFTypeRef actual_type_ref( + CFCopyTypeIDDescription(CFGetTypeID(value))); + return "Expected value for key " + + base::SysCFStringRefToUTF8(key) + + " to be " + + expected_type + + " but it was " + + base::SysCFStringRefToUTF8(actual_type_ref) + + " instead"; +} + +NSString* FilePathToNSString(const FilePath& path) { + if (path.empty()) + return nil; + return [NSString stringWithUTF8String:path.value().c_str()]; +} + +FilePath NSStringToFilePath(NSString* str) { + if (![str length]) + return FilePath(); + return FilePath([str fileSystemRepresentation]); +} + +bool CFRangeToNSRange(CFRange range, NSRange* range_out) { + if (base::IsValueInRangeForNumericTypelocation)>( + range.location) && + base::IsValueInRangeForNumericTypelength)>( + range.length) && + base::IsValueInRangeForNumericTypelocation)>( + range.location + range.length)) { + *range_out = NSMakeRange(range.location, range.length); + return true; + } + return false; +} + +} // namespace mac +} // namespace base + +std::ostream& operator<<(std::ostream& o, const CFStringRef string) { + return o << base::SysCFStringRefToUTF8(string); +} + +std::ostream& operator<<(std::ostream& o, const CFErrorRef err) { + base::ScopedCFTypeRef desc(CFErrorCopyDescription(err)); + base::ScopedCFTypeRef user_info(CFErrorCopyUserInfo(err)); + CFStringRef errorDesc = NULL; + if (user_info.get()) { + errorDesc = reinterpret_cast( + CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey)); + } + o << "Code: " << CFErrorGetCode(err) + << " Domain: " << CFErrorGetDomain(err) + << " Desc: " << desc.get(); + if(errorDesc) { + o << "(" << errorDesc << ")"; + } + return o; +} diff --git a/src/base/mac/mac_logging.cc b/src/base/mac/mac_logging.cc new file mode 100644 index 00000000..d58220fe --- /dev/null +++ b/src/base/mac/mac_logging.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/mac_logging.h" + +#include + +#if !defined(OS_IOS) +#include +#endif + +namespace logging { + +OSStatusLogMessage::OSStatusLogMessage(const char* file_path, + int line, + LogSeverity severity, + OSStatus status) + : LogMessage(file_path, line, severity), + status_(status) { +} + +OSStatusLogMessage::~OSStatusLogMessage() { +#if defined(OS_IOS) + // TODO(ios): Consider using NSError with NSOSStatusErrorDomain to try to + // get a description of the failure. + stream() << ": " << status_; +#else + stream() << ": " + << GetMacOSStatusErrorString(status_) + << " (" + << status_ + << ")"; +#endif +} + +} // namespace logging diff --git a/src/base/mac/mac_logging.h b/src/base/mac/mac_logging.h new file mode 100644 index 00000000..5192b208 --- /dev/null +++ b/src/base/mac/mac_logging.h @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_MAC_LOGGING_H_ +#define BASE_MAC_MAC_LOGGING_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/logging.h" +#include "build/build_config.h" + +#if defined(OS_IOS) +#include +#else +#include +#endif + +// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X +// system routines that report status via an OSStatus or OSErr value. It is +// similar to the PLOG family which operates on errno, but because there is no +// global (or thread-local) OSStatus or OSErr value, the specific error must +// be supplied as an argument to the OSSTATUS_LOG macro. The message logged +// will contain the symbolic constant name corresponding to the status value, +// along with the value itself. +// +// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite +// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr. + +namespace logging { + +class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { + public: + OSStatusLogMessage(const char* file_path, + int line, + LogSeverity severity, + OSStatus status); + ~OSStatusLogMessage(); + + private: + OSStatus status_; + + DISALLOW_COPY_AND_ASSIGN(OSStatusLogMessage); +}; + +} // namespace logging + +#if defined(NDEBUG) +#define MAC_DVLOG_IS_ON(verbose_level) 0 +#else +#define MAC_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level) +#endif + +#define OSSTATUS_LOG_STREAM(severity, status) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(OSStatusLogMessage, status).stream() +#define OSSTATUS_VLOG_STREAM(verbose_level, status) \ + logging::OSStatusLogMessage(__FILE__, __LINE__, \ + -verbose_level, status).stream() + +#define OSSTATUS_LOG(severity, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity)) +#define OSSTATUS_LOG_IF(severity, condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ + LOG_IS_ON(severity) && (condition)) + +#define OSSTATUS_VLOG(verbose_level, status) \ + LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ + VLOG_IS_ON(verbose_level)) +#define OSSTATUS_VLOG_IF(verbose_level, condition, status) \ + LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define OSSTATUS_CHECK(condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \ + << "Check failed: " # condition << ". " + +#define OSSTATUS_DLOG(severity, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity)) +#define OSSTATUS_DLOG_IF(severity, condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ + DLOG_IS_ON(severity) && (condition)) + +#define OSSTATUS_DVLOG(verbose_level, status) \ + LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ + MAC_DVLOG_IS_ON(verbose_level)) +#define OSSTATUS_DVLOG_IF(verbose_level, condition, status) \ + LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ + MAC_DVLOG_IS_ON(verbose_level) && (condition)) + +#define OSSTATUS_DCHECK(condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \ + DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " + +#endif // BASE_MAC_MAC_LOGGING_H_ diff --git a/src/base/strings/sys_string_conversions_mac.mm b/src/base/strings/sys_string_conversions_mac.mm new file mode 100644 index 00000000..9479e787 --- /dev/null +++ b/src/base/strings/sys_string_conversions_mac.mm @@ -0,0 +1,186 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/sys_string_conversions.h" + +#import + +#include + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/strings/string_piece.h" + +namespace base { + +namespace { + +// Convert the supplied CFString into the specified encoding, and return it as +// an STL string of the template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template +static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, + CFStringEncoding encoding) { + CFIndex length = CFStringGetLength(cfstring); + if (length == 0) + return StringType(); + + CFRange whole_string = CFRangeMake(0, length); + CFIndex out_size; + CFIndex converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + NULL, // buffer + 0, // maxBufLen + &out_size); + if (converted == 0 || out_size == 0) + return StringType(); + + // out_size is the number of UInt8-sized units needed in the destination. + // A buffer allocated as UInt8 units might not be properly aligned to + // contain elements of StringType::value_type. Use a container for the + // proper value_type, and convert out_size by figuring the number of + // value_type elements per UInt8. Leave room for a NUL terminator. + typename StringType::size_type elements = + out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; + + std::vector out_buffer(elements); + converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + reinterpret_cast(&out_buffer[0]), + out_size, + NULL); // usedBufLen + if (converted == 0) + return StringType(); + + out_buffer[elements - 1] = '\0'; + return StringType(&out_buffer[0], elements - 1); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// convert it to |out_encoding| and return it as an STL string of the +// |OutStringType| template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template +static OutStringType STLStringToSTLStringWithEncodingsT( + const InStringType& in, + CFStringEncoding in_encoding, + CFStringEncoding out_encoding) { + typename InStringType::size_type in_length = in.length(); + if (in_length == 0) + return OutStringType(); + + base::ScopedCFTypeRef cfstring(CFStringCreateWithBytesNoCopy( + NULL, + reinterpret_cast(in.data()), + in_length * sizeof(typename InStringType::value_type), + in_encoding, + false, + kCFAllocatorNull)); + if (!cfstring) + return OutStringType(); + + return CFStringToSTLStringWithEncodingT(cfstring, + out_encoding); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// return it as a CFStringRef. Returns NULL on failure. +template +static CFStringRef STLStringToCFStringWithEncodingsT( + const StringType& in, + CFStringEncoding in_encoding) { + typename StringType::size_type in_length = in.length(); + if (in_length == 0) + return CFSTR(""); + + return CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(in.data()), + in_length * + sizeof(typename StringType::value_type), + in_encoding, + false); +} + +// Specify the byte ordering explicitly, otherwise CFString will be confused +// when strings don't carry BOMs, as they typically won't. +static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; +#ifdef __BIG_ENDIAN__ +static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE; +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE; +#elif defined(__LITTLE_ENDIAN__) +static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE; +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE; +#endif // __LITTLE_ENDIAN__ + +} // namespace + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return STLStringToSTLStringWithEncodingsT( + wide, kWideStringEncoding, kNarrowStringEncoding); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return STLStringToSTLStringWithEncodingsT( + utf8, kNarrowStringEncoding, kWideStringEncoding); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToUTF8(wide); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysUTF8ToWide(native_mb); +} + +CFStringRef SysUTF8ToCFStringRef(const std::string& utf8) { + return STLStringToCFStringWithEncodingsT(utf8, kNarrowStringEncoding); +} + +CFStringRef SysUTF16ToCFStringRef(const string16& utf16) { + return STLStringToCFStringWithEncodingsT(utf16, kMediumStringEncoding); +} + +NSString* SysUTF8ToNSString(const std::string& utf8) { + return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease( + SysUTF8ToCFStringRef(utf8)); +} + +NSString* SysUTF16ToNSString(const string16& utf16) { + return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease( + SysUTF16ToCFStringRef(utf16)); +} + +std::string SysCFStringRefToUTF8(CFStringRef ref) { + return CFStringToSTLStringWithEncodingT(ref, + kNarrowStringEncoding); +} + +string16 SysCFStringRefToUTF16(CFStringRef ref) { + return CFStringToSTLStringWithEncodingT(ref, + kMediumStringEncoding); +} + +std::string SysNSStringToUTF8(NSString* nsstring) { + if (!nsstring) + return std::string(); + return SysCFStringRefToUTF8(reinterpret_cast(nsstring)); +} + +string16 SysNSStringToUTF16(NSString* nsstring) { + if (!nsstring) + return string16(); + return SysCFStringRefToUTF16(reinterpret_cast(nsstring)); +} + +} // namespace base