foundation_util.mm revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/foundation_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_path.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/bundle_locations.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/mac_logging.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/sys_string_conversions.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_IOS) 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern "C" { 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CFTypeID SecACLGetTypeID(); 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CFTypeID SecTrustedApplicationGetTypeID(); 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // extern "C" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace mac { 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool g_override_am_i_bundled = false; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool g_override_am_i_bundled_value = false; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool UncachedAmIBundled() { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS) 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // All apps are bundled on iOS 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_override_am_i_bundled) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return g_override_am_i_bundled_value; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ProcessSerialNumber psn = {0, kCurrentProcess}; 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FSRef fsref; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OSStatus pbErr; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) { 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OSSTATUS_DLOG(ERROR, pbErr) << "GetProcessBundleLocation failed"; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FSCatalogInfo info; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OSErr fsErr; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL, NULL, NULL)) != noErr) { 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OSSTATUS_DLOG(ERROR, fsErr) << "FSGetCatalogInfo failed"; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return info.nodeFlags & kFSNodeIsDirectoryMask; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool AmIBundled() { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the return value is not cached, this function will return different 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // values depending on when it's called. This confuses some client code, see 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // http://crbug.com/63183 . 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool result = UncachedAmIBundled(); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(result, UncachedAmIBundled()) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << "The return value of AmIBundled() changed. This will confuse tests. " 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << "Call SetAmIBundled() override manually if your test binary " 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << "delay-loads the framework."; 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetOverrideAmIBundled(bool value) { 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It doesn't make sense not to be bundled on iOS. 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!value) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_override_am_i_bundled = true; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_override_am_i_bundled_value = value; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsBackgroundOnlyProcess() { 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This function really does want to examine NSBundle's idea of the main 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // bundle dictionary. It needs to look at the actual running .app's 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Info.plist to access its LSUIElement property. 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath PathForFrameworkBundleResource(CFStringRef resourceName) { 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSBundle* bundle = base::mac::FrameworkBundle(); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ofType:nil]; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NSStringToFilePath(resourcePath); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OSType creator = kUnknownType; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFBundleGetPackageInfo(bundle, NULL, &creator); 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return creator; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OSType CreatorCodeForApplication() { 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFBundleRef bundle = CFBundleGetMainBundle(); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!bundle) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return kUnknownType; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return CreatorCodeForCFBundleRef(bundle); 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetSearchPathDirectory(NSSearchPathDirectory directory, 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSSearchPathDomainMask domain_mask, 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath* result) { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(result); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSArray* dirs = 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ([dirs count] < 1) { 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *result = NSStringToFilePath([dirs objectAtIndex:0]); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetSearchPathDirectory(directory, NSLocalDomainMask, result); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetSearchPathDirectory(directory, NSUserDomainMask, result); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath GetUserLibraryPath() { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath user_library_path; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DLOG(WARNING) << "Could not get user library path"; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return user_library_path; 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Takes a path to an (executable) binary and tries to provide the path to an 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// application bundle containing it. It takes the outermost bundle that it can 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |exec_name| - path to the binary 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// returns - path to the application bundle, or empty on error 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath GetAppBundlePath(const FilePath& exec_name) { 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char kExt[] = ".app"; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const size_t kExtLength = arraysize(kExt) - 1; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Split the path into components. 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string> components; 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) exec_name.GetComponents(&components); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It's an error if we don't get any components. 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!components.size()) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't prepend '/' to the first component. 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string>::const_iterator it = components.begin(); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string bundle_name = *it; 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(it->length(), 0U); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the first component ends in ".app", we're already done. 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it->length() > kExtLength && 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(bundle_name); 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The first component may be "/" or "//", etc. Only append '/' if it doesn't 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // already end in '/'. 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (bundle_name[bundle_name.length() - 1] != '/') 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bundle_name += '/'; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Go through the remaining components. 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (++it; it != components.end(); ++it) { 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(it->length(), 0U); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bundle_name += *it; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the current component ends in ".app", we're done. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it->length() > kExtLength && 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(bundle_name); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Separate this component from the next one. 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bundle_name += '/'; 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \ 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string TypeNameForCFType(TypeCF##Ref) { \ 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return #TypeCF; \ 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray); 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag); 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean); 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFData); 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate); 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull); 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber); 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet); 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFString); 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL); 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID); 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor); 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun); 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef TYPE_NAME_FOR_CF_TYPE_DEFN 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NSObjectRetain(void* obj) { 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) id<NSObject> nsobj = static_cast<id<NSObject> >(obj); 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [nsobj retain]; 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NSObjectRelease(void* obj) { 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) id<NSObject> nsobj = static_cast<id<NSObject> >(obj); 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [nsobj release]; 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) { 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is a no-op. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // In the traditional GC-less environment, NSMakeCollectable is a no-op, 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and cf_object is autoreleased, balancing out the caller's ownership claim. 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // NSMakeCollectable returns nil when used on a NULL object. 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [NSMakeCollectable(cf_object) autorelease]; 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char* base_bundle_id; 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char* BaseBundleID() { 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base_bundle_id) { 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base_bundle_id; 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(GOOGLE_CHROME_BUILD) 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "com.google.Chrome"; 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "org.chromium.Chromium"; 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetBaseBundleID(const char* new_base_bundle_id) { 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (new_base_bundle_id != base_bundle_id) { 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) free((void*)base_bundle_id); 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL; 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// foundation_util.h. 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)\ 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TypeNS* ns_val = \ 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \ 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ns_val; \ 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} \ 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)\ 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \ 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cf_val; \ 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)\ 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \ 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ns_val; \ 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} \ 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)\ 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFMutable##name##Ref cf_val = \ 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reinterpret_cast<CFMutable##name##Ref>(ns_val); \ 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cf_val; \ 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(Array); 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString); 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar); 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(Data); 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFDate, NSDate); 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary); 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFError, NSError); 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFLocale, NSLocale); 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFNumber, NSNumber); 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer); 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone); 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(Set); 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream); 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_MUTABLE_CAST_DEFN(String); 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_TO_NS_CAST_DEFN(CFURL, NSURL); 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef CF_TO_NS_CAST_DEFN 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef CF_TO_NS_MUTABLE_CAST_DEFN 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CF_CAST_DEFN(TypeCF) \ 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template<> TypeCF##Ref \ 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \ 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (cf_val == NULL) { \ 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; \ 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } \ 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (TypeCF##Ref)(cf_val); \ 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } \ 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; \ 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} \ 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)\ 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template<> TypeCF##Ref \ 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \ 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \ 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(cf_val == NULL || rv); \ 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rv; \ 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFArray); 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFBag); 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFBoolean); 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFData); 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFDate); 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFDictionary); 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFNull); 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFNumber); 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFSet); 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFString); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFURL); 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CFUUID); 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CGColor); 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CTFont); 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(CTRun); 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_IOS) 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(SecACL); 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CF_CAST_DEFN(SecTrustedApplication); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef CF_CAST_DEFN 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string GetValueFromDictionaryErrorMessage( 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFStringRef key, const std::string& expected_type, CFTypeRef value) { 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ScopedCFTypeRef<CFStringRef> actual_type_ref( 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFCopyTypeIDDescription(CFGetTypeID(value))); 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "Expected value for key " + 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SysCFStringRefToUTF8(key) + 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " to be " + 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_type + 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " but it was " + 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SysCFStringRefToUTF8(actual_type_ref) + 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " instead"; 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NSString* FilePathToNSString(const FilePath& path) { 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (path.empty()) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return nil; 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [NSString stringWithUTF8String:path.value().c_str()]; 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath NSStringToFilePath(NSString* str) { 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (![str length]) 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath(); 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FilePath([str fileSystemRepresentation]); 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace mac 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::ostream& operator<<(std::ostream& o, const CFStringRef string) { 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return o << base::SysCFStringRefToUTF8(string); 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::ostream& operator<<(std::ostream& o, const CFErrorRef err) { 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::mac::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err)); 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::mac::ScopedCFTypeRef<CFDictionaryRef> user_info( 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFErrorCopyUserInfo(err)); 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFStringRef errorDesc = NULL; 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (user_info.get()) { 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errorDesc = reinterpret_cast<CFStringRef>( 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey)); 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) o << "Code: " << CFErrorGetCode(err) 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << " Domain: " << CFErrorGetDomain(err) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << " Desc: " << desc.get(); 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if(errorDesc) { 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) o << "(" << errorDesc << ")"; 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return o; 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 402