xref: /aosp_15_r20/external/cronet/base/apple/bridging.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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