1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157

base / apple / bridging.h [blame]

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_APPLE_BRIDGING_H_
#define BASE_APPLE_BRIDGING_H_

#include <CoreText/CoreText.h>
#import <Foundation/Foundation.h>

#include "base/apple/scoped_cftyperef.h"
#include "base/base_export.h"
#include "base/check.h"
#include "base/types/always_false.h"
#include "build/build_config.h"

#if BUILDFLAG(IS_IOS)
#import <UIKit/UIKit.h>
#endif

#if BUILDFLAG(IS_MAC)
#import <AppKit/AppKit.h>
#endif

// These functions convert pointers of bridged CFTypes to NSTypes and
// vice-versa. They come in two flavors: those that transfer ownership
// (`OwnershipCast`) and those that just convert the pointer (`PtrCast`).
//
// Examples:
//
// Ownership transference (as in `CFBridgingRetain`/`Release`):
//   CFStringRef cf_string = CFStringCreateWithCString(...);
//   NSString* ns_string = CFToNSOwnershipCast(cf_string);
//   // At this point, `cf_string` does not need releasing.
//
//   NSString* ns_string = [[NSString alloc] initWithString:...];
//   CFStringRef cf_string = NSToCFOwnershipCast(ns_string);
//   // At this point, `cf_string` must be released.
//
// Pointer conversion (as in `__bridge`):
//   // `cf_data` is some `CFDataRef` from somewhere.
//   NSImage* ns_image = [[NSImage alloc] initWithData:CFToNSPtrCast(cf_data)];
//
//   // `ns_data` is some `NSData *` from somewhere.
//   SecKeyRef sec_key = SecKeyCreateFromData(..., NSToCFPtrCast(ns_data), ...);
//
// The reason to use these functions (rather than using `__bridge` and
// `CFBridgingRetain`/`Release`) is because they are type-safe. The OS-provided
// bridging calls do not type check, while these calls do the appropriate type
// checking via the magic of macros.
//
// Implementation note: Why not templates? Type checking in Core Foundation
// involves functions named in a specific pattern, and only macro token pasting
// works for this purpose.

#define CF_TO_NS_CAST_IMPL(TypeCF, TypeNS)                                  \
  namespace base::apple {                                                   \
  inline BASE_EXPORT TypeNS* _Nullable CFToNSOwnershipCast(                 \
      TypeCF##Ref CF_CONSUMED _Nullable cf_val) {                           \
    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \
    return (__bridge_transfer TypeNS*)cf_val;                               \
  }                                                                         \
  inline BASE_EXPORT CF_RETURNS_RETAINED                                    \
      TypeCF##Ref _Nullable NSToCFOwnershipCast(TypeNS* _Nullable ns_val) { \
    TypeCF##Ref cf_val = (__bridge_retained TypeCF##Ref)ns_val;             \
    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \
    return cf_val;                                                          \
  }                                                                         \
  inline BASE_EXPORT TypeNS* _Nullable CFToNSPtrCast(                       \
      TypeCF##Ref _Nullable cf_val) {                                       \
    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \
    return (__bridge TypeNS*)cf_val;                                        \
  }                                                                         \
  inline BASE_EXPORT TypeCF##Ref _Nullable NSToCFPtrCast(                   \
      TypeNS* _Nullable ns_val) {                                           \
    TypeCF##Ref cf_val = (__bridge TypeCF##Ref)ns_val;                      \
    DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val));          \
    return cf_val;                                                          \
  }                                                                         \
  }

#define CF_TO_NS_MUTABLE_CAST_IMPL(name)                                 \
  CF_TO_NS_CAST_IMPL(CF##name, NS##name)                                 \
  namespace base::apple {                                                \
  inline BASE_EXPORT NSMutable##name* _Nullable CFToNSOwnershipCast(     \
      CFMutable##name##Ref CF_CONSUMED _Nullable cf_val) {               \
    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \
    return (__bridge_transfer NSMutable##name*)cf_val;                   \
  }                                                                      \
  inline BASE_EXPORT CF_RETURNS_RETAINED                                 \
      CFMutable##name##Ref _Nullable NSToCFOwnershipCast(                \
          NSMutable##name* _Nullable ns_val) {                           \
    CFMutable##name##Ref cf_val =                                        \
        (__bridge_retained CFMutable##name##Ref)ns_val;                  \
    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \
    return cf_val;                                                       \
  }                                                                      \
  inline BASE_EXPORT NSMutable##name* _Nullable CFToNSPtrCast(           \
      CFMutable##name##Ref _Nullable cf_val) {                           \
    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \
    return (__bridge NSMutable##name*)cf_val;                            \
  }                                                                      \
  inline BASE_EXPORT CFMutable##name##Ref _Nullable NSToCFPtrCast(       \
      NSMutable##name* _Nullable ns_val) {                               \
    CFMutable##name##Ref cf_val = (__bridge CFMutable##name##Ref)ns_val; \
    DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val));     \
    return cf_val;                                                       \
  }                                                                      \
  }

// List of toll-free bridged types taken from:
// https://web.archive.org/web/20111124025525/http://www.cocoadev.com/index.pl?TollFreeBridged
// https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html#//apple_ref/doc/uid/TP40010677-SW4

// Foundation
CF_TO_NS_MUTABLE_CAST_IMPL(Array)
CF_TO_NS_MUTABLE_CAST_IMPL(AttributedString)
CF_TO_NS_CAST_IMPL(CFCalendar, NSCalendar)
CF_TO_NS_MUTABLE_CAST_IMPL(CharacterSet)
CF_TO_NS_MUTABLE_CAST_IMPL(Data)
CF_TO_NS_CAST_IMPL(CFDate, NSDate)
CF_TO_NS_MUTABLE_CAST_IMPL(Dictionary)
CF_TO_NS_CAST_IMPL(CFError, NSError)
CF_TO_NS_CAST_IMPL(CFLocale, NSLocale)
CF_TO_NS_CAST_IMPL(CFNumber, NSNumber)
CF_TO_NS_CAST_IMPL(CFRunLoopTimer, NSTimer)
CF_TO_NS_CAST_IMPL(CFTimeZone, NSTimeZone)
CF_TO_NS_MUTABLE_CAST_IMPL(Set)
CF_TO_NS_CAST_IMPL(CFReadStream, NSInputStream)
CF_TO_NS_CAST_IMPL(CFWriteStream, NSOutputStream)
CF_TO_NS_MUTABLE_CAST_IMPL(String)
CF_TO_NS_CAST_IMPL(CFURL, NSURL)

// AppKit / UIKit
#if BUILDFLAG(IS_IOS)
CF_TO_NS_CAST_IMPL(CTFont, UIFont)
#else
CF_TO_NS_CAST_IMPL(CTFont, NSFont)
#endif  // BUILDFLAG(IS_IOS)

#undef CF_TO_NS_CAST_IMPL
#undef CF_TO_NS_MUTABLE_CAST_IMPL

namespace base::apple {

template <typename CFT>
id _Nullable CFToNSOwnershipCast(ScopedCFTypeRef<CFT>) {
  static_assert(
      AlwaysFalse<CFT>,
      "Error: Do not pass a ScopedCFTypeRef to CFToNSOwnershipCast. "
      "Call .release() on the ScopedCFTypeRef and pass the result in.");
  return nil;
}

}  // namespace base::apple

#endif  // BASE_APPLE_BRIDGING_H_