1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_APPLE_BRIDGING_H_
6 #define BASE_APPLE_BRIDGING_H_
7
8 #include <CoreText/CoreText.h>
9 #import <Foundation/Foundation.h>
10
11 #include "base/apple/scoped_cftyperef.h"
12 #include "base/base_export.h"
13 #include "base/check.h"
14 #include "base/types/always_false.h"
15 #include "build/build_config.h"
16
17 #if BUILDFLAG(IS_IOS)
18 #import <UIKit/UIKit.h>
19 #endif
20
21 #if BUILDFLAG(IS_MAC)
22 #import <AppKit/AppKit.h>
23 #endif
24
25 // These functions convert pointers of bridged CFTypes to NSTypes and
26 // vice-versa. They come in two flavors: those that transfer ownership
27 // (`OwnershipCast`) and those that just convert the pointer (`PtrCast`).
28 //
29 // Examples:
30 //
31 // Ownership transference (as in `CFBridgingRetain`/`Release`):
32 // CFStringRef cf_string = CFStringCreateWithCString(...);
33 // NSString* ns_string = CFToNSOwnershipCast(cf_string);
34 // // At this point, `cf_string` does not need releasing.
35 //
36 // NSString* ns_string = [[NSString alloc] initWithString:...];
37 // CFStringRef cf_string = NSToCFOwnershipCast(ns_string);
38 // // At this point, `cf_string` must be released.
39 //
40 // Pointer conversion (as in `__bridge`):
41 // // `cf_data` is some `CFDataRef` from somewhere.
42 // NSImage* ns_image = [[NSImage alloc] initWithData:CFToNSPtrCast(cf_data)];
43 //
44 // // `ns_data` is some `NSData *` from somewhere.
45 // SecKeyRef sec_key = SecKeyCreateFromData(..., NSToCFPtrCast(ns_data), ...);
46 //
47 // The reason to use these functions (rather than using `__bridge` and
48 // `CFBridgingRetain`/`Release`) is because they are type-safe. The OS-provided
49 // bridging calls do not type check, while these calls do the appropriate type
50 // checking via the magic of macros.
51 //
52 // Implementation note: Why not templates? Type checking in Core Foundation
53 // involves functions named in a specific pattern, and only macro token pasting
54 // works for this purpose.
55
56 #define CF_TO_NS_CAST_IMPL(TypeCF, TypeNS) \
57 namespace base::apple { \
58 inline BASE_EXPORT TypeNS* _Nullable CFToNSOwnershipCast( \
59 TypeCF##Ref CF_CONSUMED _Nullable cf_val) { \
60 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
61 return (__bridge_transfer TypeNS*)cf_val; \
62 } \
63 inline BASE_EXPORT CF_RETURNS_RETAINED \
64 TypeCF##Ref _Nullable NSToCFOwnershipCast(TypeNS* _Nullable ns_val) { \
65 TypeCF##Ref cf_val = (__bridge_retained TypeCF##Ref)ns_val; \
66 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
67 return cf_val; \
68 } \
69 inline BASE_EXPORT TypeNS* _Nullable CFToNSPtrCast( \
70 TypeCF##Ref _Nullable cf_val) { \
71 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
72 return (__bridge TypeNS*)cf_val; \
73 } \
74 inline BASE_EXPORT TypeCF##Ref _Nullable NSToCFPtrCast( \
75 TypeNS* _Nullable ns_val) { \
76 TypeCF##Ref cf_val = (__bridge TypeCF##Ref)ns_val; \
77 DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
78 return cf_val; \
79 } \
80 }
81
82 #define CF_TO_NS_MUTABLE_CAST_IMPL(name) \
83 CF_TO_NS_CAST_IMPL(CF##name, NS##name) \
84 namespace base::apple { \
85 inline BASE_EXPORT NSMutable##name* _Nullable CFToNSOwnershipCast( \
86 CFMutable##name##Ref CF_CONSUMED _Nullable cf_val) { \
87 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
88 return (__bridge_transfer NSMutable##name*)cf_val; \
89 } \
90 inline BASE_EXPORT CF_RETURNS_RETAINED \
91 CFMutable##name##Ref _Nullable NSToCFOwnershipCast( \
92 NSMutable##name* _Nullable ns_val) { \
93 CFMutable##name##Ref cf_val = \
94 (__bridge_retained CFMutable##name##Ref)ns_val; \
95 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
96 return cf_val; \
97 } \
98 inline BASE_EXPORT NSMutable##name* _Nullable CFToNSPtrCast( \
99 CFMutable##name##Ref _Nullable cf_val) { \
100 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
101 return (__bridge NSMutable##name*)cf_val; \
102 } \
103 inline BASE_EXPORT CFMutable##name##Ref _Nullable NSToCFPtrCast( \
104 NSMutable##name* _Nullable ns_val) { \
105 CFMutable##name##Ref cf_val = (__bridge CFMutable##name##Ref)ns_val; \
106 DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
107 return cf_val; \
108 } \
109 }
110
111 // List of toll-free bridged types taken from:
112 // https://web.archive.org/web/20111124025525/http://www.cocoadev.com/index.pl?TollFreeBridged
113 // https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html#//apple_ref/doc/uid/TP40010677-SW4
114
115 // Foundation
116 CF_TO_NS_MUTABLE_CAST_IMPL(Array)
CF_TO_NS_MUTABLE_CAST_IMPL(AttributedString)117 CF_TO_NS_MUTABLE_CAST_IMPL(AttributedString)
118 CF_TO_NS_CAST_IMPL(CFCalendar, NSCalendar)
119 CF_TO_NS_MUTABLE_CAST_IMPL(CharacterSet)
120 CF_TO_NS_MUTABLE_CAST_IMPL(Data)
121 CF_TO_NS_CAST_IMPL(CFDate, NSDate)
122 CF_TO_NS_MUTABLE_CAST_IMPL(Dictionary)
123 CF_TO_NS_CAST_IMPL(CFError, NSError)
124 CF_TO_NS_CAST_IMPL(CFLocale, NSLocale)
125 CF_TO_NS_CAST_IMPL(CFNumber, NSNumber)
126 CF_TO_NS_CAST_IMPL(CFRunLoopTimer, NSTimer)
127 CF_TO_NS_CAST_IMPL(CFTimeZone, NSTimeZone)
128 CF_TO_NS_MUTABLE_CAST_IMPL(Set)
129 CF_TO_NS_CAST_IMPL(CFReadStream, NSInputStream)
130 CF_TO_NS_CAST_IMPL(CFWriteStream, NSOutputStream)
131 CF_TO_NS_MUTABLE_CAST_IMPL(String)
132 CF_TO_NS_CAST_IMPL(CFURL, NSURL)
133
134 // AppKit / UIKit
135 #if BUILDFLAG(IS_IOS)
136 CF_TO_NS_CAST_IMPL(CTFont, UIFont)
137 #else
138 CF_TO_NS_CAST_IMPL(CTFont, NSFont)
139 #endif // BUILDFLAG(IS_IOS)
140
141 #undef CF_TO_NS_CAST_IMPL
142 #undef CF_TO_NS_MUTABLE_CAST_IMPL
143
144 namespace base::apple {
145
146 template <typename CFT>
147 id _Nullable CFToNSOwnershipCast(ScopedCFTypeRef<CFT>) {
148 static_assert(
149 AlwaysFalse<CFT>,
150 "Error: Do not pass a ScopedCFTypeRef to CFToNSOwnershipCast. "
151 "Call .release() on the ScopedCFTypeRef and pass the result in.");
152 return nil;
153 }
154
155 } // namespace base::apple
156
157 #endif // BASE_APPLE_BRIDGING_H_
158