diff --git a/BUCK b/BUCK
new file mode 100644
index 0000000000..ea67fb9dd5
--- /dev/null
+++ b/BUCK
@@ -0,0 +1,553 @@
+load("//Config:utils.bzl",
+ "library_configs",
+)
+
+load("//Config:configs.bzl",
+ "app_binary_configs",
+ "share_extension_configs",
+ "widget_extension_configs",
+ "notification_content_extension_configs",
+ "notification_service_extension_configs",
+ "intents_extension_configs",
+ "watch_extension_binary_configs",
+ "watch_binary_configs",
+ "info_plist_substitutions",
+ "app_info_plist_substitutions",
+ "share_extension_info_plist_substitutions",
+ "widget_extension_info_plist_substitutions",
+ "notification_content_extension_info_plist_substitutions",
+ "notification_service_extension_info_plist_substitutions",
+ "intents_extension_info_plist_substitutions",
+ "watch_extension_info_plist_substitutions",
+ "watch_info_plist_substitutions",
+ "DEVELOPMENT_LANGUAGE",
+)
+
+load("//Config:buck_rule_macros.bzl",
+ "apple_lib",
+ "framework_binary_dependencies",
+ "framework_bundle_dependencies",
+ "glob_map",
+ "glob_sub_map",
+ "merge_maps",
+)
+
+framework_dependencies = [
+ "//submodules/MtProtoKit:MtProtoKit",
+ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
+ "//submodules/Postbox:Postbox",
+ "//submodules/TelegramApi:TelegramApi",
+ "//submodules/SyncCore:SyncCore",
+ "//submodules/TelegramCore:TelegramCore",
+ "//submodules/AsyncDisplayKit:AsyncDisplayKit",
+ "//submodules/Display:Display",
+ "//submodules/TelegramUI:TelegramUI",
+]
+
+resource_dependencies = [
+ "//submodules/LegacyComponents:LegacyComponentsResources",
+ "//submodules/TelegramUI:TelegramUIAssets",
+ "//submodules/TelegramUI:TelegramUIResources",
+ #"//submodules/WalletUI:WalletUIAssets",
+ #"//submodules/WalletUI:WalletUIResources",
+ "//submodules/PasswordSetupUI:PasswordSetupUIResources",
+ "//submodules/PasswordSetupUI:PasswordSetupUIAssets",
+ "//submodules/OverlayStatusController:OverlayStatusControllerResources",
+ "//:AppResources",
+ "//:AppStringResources",
+ "//:InfoPlistStringResources",
+ "//:AppIntentVocabularyResources",
+ "//:Icons",
+ "//:AdditionalIcons",
+ "//:LaunchScreen",
+]
+
+build_phase_scripts = [
+]
+
+apple_resource(
+ name = "AppResources",
+ files = glob([
+ "Telegram-iOS/Resources/**/*",
+ ], exclude = ["Telegram-iOS/Resources/**/.*"]),
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "AppStringResources",
+ files = [],
+ variants = glob([
+ "Telegram-iOS/*.lproj/Localizable.strings",
+ ]),
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "AppIntentVocabularyResources",
+ files = [],
+ variants = glob([
+ "Telegram-iOS/*.lproj/AppIntentVocabulary.plist",
+ ]),
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "InfoPlistStringResources",
+ files = [],
+ variants = glob([
+ "Telegram-iOS/*.lproj/InfoPlist.strings",
+ ]),
+ visibility = ["PUBLIC"],
+)
+
+apple_asset_catalog(
+ name = "Icons",
+ dirs = [
+ "Telegram-iOS/Icons.xcassets",
+ "Telegram-iOS/AppIcons.xcassets",
+ ],
+ app_icon = "AppIconLLC",
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "AdditionalIcons",
+ files = glob([
+ "Telegram-iOS/*.png",
+ ]),
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "LaunchScreen",
+ files = [
+ "Telegram-iOS/Base.lproj/LaunchScreen.xib",
+ ],
+ visibility = ["PUBLIC"],
+)
+
+apple_library(
+ name = "AppLibrary",
+ visibility = [
+ "//:",
+ "//...",
+ ],
+ configs = library_configs(),
+ swift_version = native.read_config("swift", "version"),
+ srcs = [
+ "Telegram-iOS/main.m",
+ "Telegram-iOS/Application.swift"
+ ],
+ deps = [
+ ]
+ + framework_binary_dependencies(framework_dependencies),
+)
+
+apple_binary(
+ name = "AppBinary",
+ visibility = [
+ "//:",
+ "//...",
+ ],
+ configs = app_binary_configs(),
+ swift_version = native.read_config("swift", "version"),
+ srcs = [
+ "SupportFiles/Empty.swift",
+ ],
+ deps = [
+ ":AppLibrary",
+ ]
+ + resource_dependencies,
+)
+
+apple_bundle(
+ name = "Telegram",
+ visibility = [
+ "//:",
+ ],
+ extension = "app",
+ binary = ":AppBinary",
+ product_name = "Telegram",
+ info_plist = "Telegram-iOS/Info.plist",
+ info_plist_substitutions = app_info_plist_substitutions(),
+ deps = [
+ ":ShareExtension",
+ ":WidgetExtension",
+ ":NotificationContentExtension",
+ ":NotificationServiceExtension",
+ ":IntentsExtension",
+ ":WatchApp#watch",
+ ]
+ + framework_bundle_dependencies(framework_dependencies),
+)
+
+# Share Extension
+
+apple_binary(
+ name = "ShareBinary",
+ srcs = glob([
+ "Share/**/*.swift",
+ ]),
+ configs = share_extension_configs(),
+ linker_flags = [
+ "-e",
+ "_NSExtensionMain",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "/usr/lib/swift",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/../../Frameworks",
+ ],
+ deps = [
+ "//submodules/TelegramUI:TelegramUI#shared",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/UIKit.framework",
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ ],
+)
+
+apple_bundle(
+ name = "ShareExtension",
+ binary = ":ShareBinary",
+ extension = "appex",
+ info_plist = "Share/Info.plist",
+ info_plist_substitutions = share_extension_info_plist_substitutions(),
+ deps = [
+ ],
+ xcode_product_type = "com.apple.product-type.app-extension",
+)
+
+# Widget
+
+apple_binary(
+ name = "WidgetBinary",
+ srcs = glob([
+ "Widget/**/*.swift",
+ ]),
+ configs = widget_extension_configs(),
+ swift_compiler_flags = [
+ "-application-extension",
+ ],
+ linker_flags = [
+ "-e",
+ "_NSExtensionMain",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "/usr/lib/swift",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/../../Frameworks",
+ ],
+ deps = [
+ "//submodules/BuildConfig:BuildConfig",
+ "//submodules/WidgetItems:WidgetItems",
+ "//submodules/AppLockState:AppLockState",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/UIKit.framework",
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ "$SDKROOT/System/Library/Frameworks/NotificationCenter.framework",
+ ],
+)
+
+apple_bundle(
+ name = "WidgetExtension",
+ binary = ":WidgetBinary",
+ extension = "appex",
+ info_plist = "Widget/Info.plist",
+ info_plist_substitutions = widget_extension_info_plist_substitutions(),
+ deps = [
+ ],
+ xcode_product_type = "com.apple.product-type.app-extension",
+)
+
+# Notification Content
+
+apple_binary(
+ name = "NotificationContentBinary",
+ srcs = glob([
+ "NotificationContent/**/*.swift",
+ ]),
+ configs = notification_content_extension_configs(),
+ swift_compiler_flags = [
+ "-application-extension",
+ ],
+ linker_flags = [
+ "-e",
+ "_NSExtensionMain",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "/usr/lib/swift",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/../../Frameworks",
+ ],
+ deps = [
+ "//submodules/TelegramUI:TelegramUI#shared",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/UIKit.framework",
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ "$SDKROOT/System/Library/Frameworks/UserNotificationsUI.framework",
+ ],
+)
+
+apple_bundle(
+ name = "NotificationContentExtension",
+ binary = ":NotificationContentBinary",
+ extension = "appex",
+ info_plist = "NotificationContent/Info.plist",
+ info_plist_substitutions = notification_content_extension_info_plist_substitutions(),
+ deps = [
+ ],
+ xcode_product_type = "com.apple.product-type.app-extension",
+)
+
+#Notification Service
+
+apple_binary(
+ name = "NotificationServiceBinary",
+ srcs = glob([
+ "NotificationService/**/*.m",
+ "NotificationService/**/*.swift",
+ ]),
+ headers = glob([
+ "NotificationService/**/*.h",
+ ]),
+ bridging_header = "NotificationService/NotificationService-Bridging-Header.h",
+ configs = notification_service_extension_configs(),
+ swift_compiler_flags = [
+ "-application-extension",
+ ],
+ linker_flags = [
+ "-e",
+ "_NSExtensionMain",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "/usr/lib/swift",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/../../Frameworks",
+ ],
+ deps = [
+ "//submodules/BuildConfig:BuildConfig",
+ "//submodules/MtProtoKit:MtProtoKit#shared",
+ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
+ "//submodules/EncryptionProvider:EncryptionProvider",
+ "//submodules/Database/ValueBox:ValueBox",
+ "//submodules/Database/PostboxDataTypes:PostboxDataTypes",
+ "//submodules/Database/MessageHistoryReadStateTable:MessageHistoryReadStateTable",
+ "//submodules/Database/MessageHistoryMetadataTable:MessageHistoryMetadataTable",
+ "//submodules/Database/PreferencesTable:PreferencesTable",
+ "//submodules/Database/PeerTable:PeerTable",
+ "//submodules/sqlcipher:sqlcipher",
+ "//submodules/AppLockState:AppLockState",
+ "//submodules/NotificationsPresentationData:NotificationsPresentationData",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ "$SDKROOT/System/Library/Frameworks/UserNotifications.framework",
+ ],
+)
+
+apple_bundle(
+ name = "NotificationServiceExtension",
+ binary = ":NotificationServiceBinary",
+ extension = "appex",
+ info_plist = "NotificationService/Info.plist",
+ info_plist_substitutions = notification_service_extension_info_plist_substitutions(),
+ deps = [
+ ],
+ xcode_product_type = "com.apple.product-type.app-extension",
+)
+
+# Intents
+
+apple_binary(
+ name = "IntentsBinary",
+ srcs = glob([
+ "SiriIntents/**/*.swift",
+ ]),
+ configs = intents_extension_configs(),
+ swift_compiler_flags = [
+ "-application-extension",
+ ],
+ linker_flags = [
+ "-e",
+ "_NSExtensionMain",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "/usr/lib/swift",
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@executable_path/../../Frameworks",
+ ],
+ deps = [
+ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
+ "//submodules/Postbox:Postbox#shared",
+ "//submodules/TelegramApi:TelegramApi#shared",
+ "//submodules/SyncCore:SyncCore#shared",
+ "//submodules/TelegramCore:TelegramCore#shared",
+ "//submodules/BuildConfig:BuildConfig",
+ "//submodules/OpenSSLEncryptionProvider:OpenSSLEncryptionProvider",
+ "//submodules/AppLockState:AppLockState",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ "$SDKROOT/System/Library/Frameworks/Intents.framework",
+ "$SDKROOT/System/Library/Frameworks/Contacts.framework",
+ ],
+)
+
+apple_bundle(
+ name = "IntentsExtension",
+ binary = ":IntentsBinary",
+ extension = "appex",
+ info_plist = "SiriIntents/Info.plist",
+ info_plist_substitutions = intents_extension_info_plist_substitutions(),
+ deps = [
+ ],
+ xcode_product_type = "com.apple.product-type.app-extension",
+)
+
+# Watch
+
+apple_resource(
+ name = "WatchAppStringResources",
+ files = [],
+ variants = glob([
+ "Telegram-iOS/*.lproj/Localizable.strings",
+ ]),
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "WatchAppExtensionResources",
+ files = glob([
+ "Watch/Extension/Resources/**/*",
+ ], exclude = ["Watch/Extension/Resources/**/.*"]),
+ visibility = ["PUBLIC"],
+)
+
+apple_binary(
+ name = "WatchAppExtensionBinary",
+ srcs = glob([
+ "Watch/Extension/**/*.m",
+ "Watch/SSignalKit/**/*.m",
+ "Watch/Bridge/**/*.m",
+ "Watch/WatchCommonWatch/**/*.m",
+ ]),
+ headers = merge_maps([
+ glob_map(glob([
+ "Watch/Extension/*.h",
+ "Watch/Bridge/*.h",
+ ])),
+ glob_sub_map("Watch/Extension/", glob([
+ "Watch/Extension/SSignalKit/*.h",
+ ])),
+ glob_sub_map("Watch/", glob([
+ "Watch/WatchCommonWatch/*.h",
+ ])),
+ ]),
+ compiler_flags = [
+ "-DTARGET_OS_WATCH=1",
+ ],
+ linker_flags = [
+ "-e",
+ "_WKExtensionMain",
+ "-lWKExtensionMainLegacy",
+ ],
+ configs = watch_extension_binary_configs(),
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/UserNotifications.framework",
+ "$SDKROOT/System/Library/Frameworks/CoreLocation.framework",
+ "$SDKROOT/System/Library/Frameworks/CoreGraphics.framework",
+ ],
+ deps = [
+ ":WatchAppStringResources",
+ ":WatchAppExtensionResources",
+ ],
+)
+
+apple_bundle(
+ name = "WatchAppExtension",
+ binary = ":WatchAppExtensionBinary",
+ extension = "appex",
+ info_plist = "Watch/Extension/Info.plist",
+ info_plist_substitutions = watch_extension_info_plist_substitutions(),
+ xcode_product_type = "com.apple.product-type.watchkit2-extension",
+)
+
+apple_resource(
+ name = "WatchAppResources",
+ dirs = [],
+ files = glob(["Watch/Extension/Resources/*.png"])
+)
+
+apple_asset_catalog(
+ name = "WatchAppAssets",
+ dirs = [
+ "Watch/App/Assets.xcassets",
+ ],
+ app_icon = "AppIcon",
+ visibility = ["PUBLIC"],
+)
+
+apple_resource(
+ name = "WatchAppInterface",
+ files = [
+ "Watch/App/Base.lproj/Interface.storyboard",
+ ],
+ visibility = ["PUBLIC"],
+)
+
+apple_binary(
+ name = "WatchAppBinary",
+ configs = watch_binary_configs(),
+ deps = [
+ ":WatchAppResources",
+ ":WatchAppAssets",
+ ":WatchAppInterface",
+ ":WatchAppStringResources",
+ ],
+)
+
+apple_bundle(
+ name = "WatchApp",
+ binary = ":WatchAppBinary",
+ visibility = [
+ "//:",
+ ],
+ extension = "app",
+ info_plist = "Watch/App/Info.plist",
+ info_plist_substitutions = watch_info_plist_substitutions(),
+ xcode_product_type = "com.apple.product-type.application.watchapp2",
+ deps = [
+ ":WatchAppExtension",
+ ],
+)
+
+# Package
+
+apple_package(
+ name = "AppPackage",
+ bundle = ":Telegram",
+)
+
+xcode_workspace_config(
+ name = "workspace",
+ workspace_name = "Telegram_Buck",
+ src_target = ":Telegram",
+)
diff --git a/Telegram/NotificationService/Serialization.m b/Telegram/NotificationService/Serialization.m
index f7eea7807e..b14ed21e17 100644
--- a/Telegram/NotificationService/Serialization.m
+++ b/Telegram/NotificationService/Serialization.m
@@ -3,7 +3,7 @@
@implementation Serialization
- (NSUInteger)currentLayer {
- return 110;
+ return 111;
}
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift
index efc0b4076a..9083112cb9 100644
--- a/Telegram/SiriIntents/IntentHandler.swift
+++ b/Telegram/SiriIntents/IntentHandler.swift
@@ -7,6 +7,7 @@ import SwiftSignalKit
import BuildConfig
import Contacts
import OpenSSLEncryptionProvider
+import AppLockState
private var accountCache: Account?
@@ -57,6 +58,8 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
private let resolvePersonsDisposable = MetaDisposable()
private let actionDisposable = MetaDisposable()
+ private var appGroupUrl: URL?
+
override init() {
super.init()
@@ -79,6 +82,8 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
return
}
+ self.appGroupUrl = appGroupUrl
+
let rootPath = rootPathForBasePath(appGroupUrl.path)
performAppGroupUpgrades(appGroupPath: appGroupUrl.path, rootPath: rootPath)
@@ -179,6 +184,14 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
private func resolve(persons: [INPerson]?, with completion: @escaping ([ResolveResult]) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ completion([.skip])
+ return
+ }
+ }
+
let account = self.accountPromise.get()
guard let initialPersons = persons, !initialPersons.isEmpty else {
completion([.needsValue])
@@ -278,6 +291,14 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
@available(iOSApplicationExtension 11.0, *)
public func resolveRecipients(for intent: INSendMessageIntent, with completion: @escaping ([INSendMessageRecipientResolutionResult]) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ completion([INSendMessageRecipientResolutionResult.notRequired()])
+ return
+ }
+ }
+
if let peerId = intent.conversationIdentifier.flatMap(Int64.init) {
let account = self.accountPromise.get()
@@ -321,6 +342,13 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ completion(INStringResolutionResult.notRequired())
+ return
+ }
+ }
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion(INStringResolutionResult.notRequired())
return
@@ -333,6 +361,15 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
+ let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
@@ -344,6 +381,16 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func handle(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
+ let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
+
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@@ -394,6 +441,16 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func handle(intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self))
+ let response = INSearchForMessagesIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
+
self.actionDisposable.set((self.accountPromise.get()
|> take(1)
|> castError(IntentHandlingError.self)
@@ -455,6 +512,16 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func handle(intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INSetMessageAttributeIntent.self))
+ let response = INSetMessageAttributeIntentResponse(code: .failure, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
+
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@@ -514,6 +581,14 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
// MARK: - INStartAudioCallIntentHandling
public func resolveContacts(for intent: INStartAudioCallIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ completion([INPersonResolutionResult.notRequired()])
+ return
+ }
+ }
+
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion([INPersonResolutionResult.notRequired()])
return
@@ -529,6 +604,16 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}
public func handle(intent: INStartAudioCallIntent, completion: @escaping (INStartAudioCallIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INStartAudioCallIntent.self))
+ let response = INStartAudioCallIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
+
self.actionDisposable.set((self.accountPromise.get()
|> castError(IntentHandlingError.self)
|> take(1)
@@ -572,6 +657,16 @@ public class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
}*/
public func handle(intent: INSearchCallHistoryIntent, completion: @escaping (INSearchCallHistoryIntentResponse) -> Void) {
+ if let appGroupUrl = self.appGroupUrl {
+ let rootPath = rootPathForBasePath(appGroupUrl.path)
+ if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) {
+ let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchCallHistoryIntent.self))
+ let response = INSearchCallHistoryIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
+ completion(response)
+ return
+ }
+ }
+
self.actionDisposable.set((self.accountPromise.get()
|> take(1)
|> castError(IntentHandlingError.self)
diff --git a/Telegram/Telegram-iOS/Telegram-iOS-Hockeyapp.entitlements b/Telegram/Telegram-iOS/Telegram-iOS-Hockeyapp.entitlements
index f4771f658b..97391bb537 100644
--- a/Telegram/Telegram-iOS/Telegram-iOS-Hockeyapp.entitlements
+++ b/Telegram/Telegram-iOS/Telegram-iOS-Hockeyapp.entitlements
@@ -34,7 +34,5 @@
com.apple.developer.pushkit.unrestricted-voip
- application-identifier
- X834Q8SBVP.org.telegram.Telegram-iOS
diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
index 061741053b..a0af4e214f 100644
--- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings
+++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings
@@ -5344,3 +5344,25 @@ Any member of this group will be able to see messages in the channel.";
"PeerInfo.AddToContacts" = "Add to Contacts";
"PeerInfo.BioExpand" = "more";
+
+"External.OpenIn" = "Open in %@";
+
+"ChatList.EmptyChatList" = "You have no\nconversations yet.";
+"ChatList.EmptyChatFilterList" = "No chats currently\nmatch this filter.";
+
+"ChatList.EmptyChatListNewMessage" = "New Message";
+"ChatList.EmptyChatListEditFilter" = "Edit Filter";
+
+"Stats.Overview" = "OVERVIEW";
+"Stats.Followers" = "Followers";
+"Stats.EnabledNotifications" = "Enabled Notifications";
+"Stats.ViewsPerPost" = "Views Per Post";
+"Stats.SharesPerPost" = "Shares Per Post";
+
+"Stats.GrowthTitle" = "GROWTH";
+"Stats.FollowersTitle" = "FOLLOWERS";
+"Stats.NotificationsTitle" = "NOTIFICATIONS";
+"Stats.InteractionsTitle" = "INTERACTIONS";
+"Stats.ViewsBySourceTitle" = "VIEWS BY SOURCE";
+"Stats.FollowersBySourceTitle" = "FOLLOWERS BY SOURCE";
+"Stats.LanguagesTitle" = "LANGUAGES";
diff --git a/build-system/verify.sh b/build-system/verify.sh
index 89e33bef02..d260604e30 100644
--- a/build-system/verify.sh
+++ b/build-system/verify.sh
@@ -21,22 +21,22 @@ if [ -z "$BUILD_NUMBER" ]; then
exit 1
fi
-export ENTITLEMENTS_APP="Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements"
+export ENTITLEMENTS_APP="Telegram/Telegram-iOS/Telegram-iOS-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_APP="match Development ph.telegra.Telegraph"
export DISTRIBUTION_PROVISIONING_PROFILE_APP="match AppStore ph.telegra.Telegraph"
-export ENTITLEMENTS_EXTENSION_SHARE="Share/Share-AppStoreLLC.entitlements"
+export ENTITLEMENTS_EXTENSION_SHARE="Telegram/Share/Share-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_SHARE="match Development ph.telegra.Telegraph.Share"
export DISTRIBUTION_PROVISIONING_PROFILE_EXTENSION_SHARE="match AppStore ph.telegra.Telegraph.Share"
-export ENTITLEMENTS_EXTENSION_WIDGET="Widget/Widget-AppStoreLLC.entitlements"
+export ENTITLEMENTS_EXTENSION_WIDGET="Telegram/Widget/Widget-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_WIDGET="match Development ph.telegra.Telegraph.Widget"
export DISTRIBUTION_PROVISIONING_PROFILE_EXTENSION_WIDGET="match AppStore ph.telegra.Telegraph.Widget"
-export ENTITLEMENTS_EXTENSION_NOTIFICATIONSERVICE="NotificationService/NotificationService-AppStoreLLC.entitlements"
+export ENTITLEMENTS_EXTENSION_NOTIFICATIONSERVICE="Telegram/NotificationService/NotificationService-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONSERVICE="match Development ph.telegra.Telegraph.NotificationService"
export DISTRIBUTION_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONSERVICE="match AppStore ph.telegra.Telegraph.NotificationService"
-export ENTITLEMENTS_EXTENSION_NOTIFICATIONCONTENT="NotificationContent/NotificationContent-AppStoreLLC.entitlements"
+export ENTITLEMENTS_EXTENSION_NOTIFICATIONCONTENT="Telegram/NotificationContent/NotificationContent-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONCONTENT="match Development ph.telegra.Telegraph.NotificationContent"
export DISTRIBUTION_PROVISIONING_PROFILE_EXTENSION_NOTIFICATIONCONTENT="match AppStore ph.telegra.Telegraph.NotificationContent"
-export ENTITLEMENTS_EXTENSION_INTENTS="SiriIntents/SiriIntents-AppStoreLLC.entitlements"
+export ENTITLEMENTS_EXTENSION_INTENTS="Telegram/SiriIntents/SiriIntents-AppStoreLLC.entitlements"
export DEVELOPMENT_PROVISIONING_PROFILE_EXTENSION_INTENTS="match Development ph.telegra.Telegraph.SiriIntents"
export DISTRIBUTION_PROVISIONING_PROFILE_EXTENSION_INTENTS="match AppStore ph.telegra.Telegraph.SiriIntents"
export DEVELOPMENT_PROVISIONING_PROFILE_WATCH_APP="match Development ph.telegra.Telegraph.watchkitapp"
diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift
index 23ff45ff11..0d69cfc183 100644
--- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift
+++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift
@@ -5,7 +5,7 @@ import Postbox
public enum ContactMultiselectionControllerMode {
case groupCreation
- case peerSelection(searchChatList: Bool, searchGroups: Bool)
+ case peerSelection(searchChatList: Bool, searchGroups: Bool, searchChannels: Bool)
case channelCreation
}
diff --git a/submodules/AsyncDisplayKit/Source/ASAbstractLayoutController+FrameworkPrivate.h b/submodules/AsyncDisplayKit/Source/ASAbstractLayoutController+FrameworkPrivate.h
deleted file mode 100644
index f836fa0b84..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASAbstractLayoutController+FrameworkPrivate.h
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// ASAbstractLayoutController+FrameworkPrivate.h
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-//
-// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode.
-// These methods must never be called or overridden by other classes.
-//
-
-#include
-
-@interface ASAbstractLayoutController (FrameworkPrivate)
-
-+ (std::vector>)defaultTuningParameters;
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASAsciiArtBoxCreator.mm b/submodules/AsyncDisplayKit/Source/ASAsciiArtBoxCreator.mm
deleted file mode 100644
index 78eb572ead..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASAsciiArtBoxCreator.mm
+++ /dev/null
@@ -1,186 +0,0 @@
-//
-// ASAsciiArtBoxCreator.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-#import
-
-static const NSUInteger kDebugBoxPadding = 2;
-
-typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation)
-{
- PIDebugBoxPaddingLocationFront,
- PIDebugBoxPaddingLocationEnd,
- PIDebugBoxPaddingLocationBoth
-};
-
-@interface NSString(PIDebugBox)
-
-@end
-
-@implementation NSString(PIDebugBox)
-
-+ (instancetype)debugbox_stringWithString:(NSString *)stringToRepeat repeatedCount:(NSUInteger)repeatCount NS_RETURNS_RETAINED
-{
- NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[stringToRepeat length] * repeatCount];
- for (NSUInteger index = 0; index < repeatCount; index++) {
- [string appendString:stringToRepeat];
- }
- return [string copy];
-}
-
-- (NSString *)debugbox_stringByAddingPadding:(NSString *)padding count:(NSUInteger)count location:(PIDebugBoxPaddingLocation)location
-{
- NSString *paddingString = [NSString debugbox_stringWithString:padding repeatedCount:count];
- switch (location) {
- case PIDebugBoxPaddingLocationFront:
- return [NSString stringWithFormat:@"%@%@", paddingString, self];
- case PIDebugBoxPaddingLocationEnd:
- return [NSString stringWithFormat:@"%@%@", self, paddingString];
- case PIDebugBoxPaddingLocationBoth:
- return [NSString stringWithFormat:@"%@%@%@", paddingString, self, paddingString];
- }
- return [self copy];
-}
-
-@end
-
-@implementation ASAsciiArtBoxCreator
-
-+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
-{
- if ([children count] == 0) {
- return parent;
- }
-
- NSMutableArray *childrenLines = [NSMutableArray array];
-
- // split the children into lines
- NSUInteger lineCountPerChild = 0;
- for (NSString *child in children) {
- NSArray *lines = [child componentsSeparatedByString:@"\n"];
- lineCountPerChild = MAX(lineCountPerChild, [lines count]);
- }
-
- for (NSString *child in children) {
- NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
- NSUInteger topPadding = ceil((CGFloat)(lineCountPerChild - [lines count])/2.0);
- NSUInteger bottomPadding = (lineCountPerChild - [lines count])/2.0;
- NSUInteger lineLength = [lines[0] length];
-
- for (NSUInteger index = 0; index < topPadding; index++) {
- [lines insertObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength] atIndex:0];
- }
- for (NSUInteger index = 0; index < bottomPadding; index++) {
- [lines addObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength]];
- }
- [childrenLines addObject:lines];
- }
-
- NSMutableArray *concatenatedLines = [NSMutableArray array];
- NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding];
- for (NSUInteger index = 0; index < lineCountPerChild; index++) {
- NSMutableString *line = [[NSMutableString alloc] init];
- [line appendFormat:@"|%@",padding];
- for (NSArray *childLines in childrenLines) {
- [line appendFormat:@"%@%@", childLines[index], padding];
- }
- [line appendString:@"|"];
- [concatenatedLines addObject:line];
- }
-
- // surround the lines in a box
- NSUInteger totalLineLength = [concatenatedLines[0] length];
- if (totalLineLength < [parent length]) {
- NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - totalLineLength;
- NSUInteger leftPadding = ceil((CGFloat)difference/2.0);
- NSUInteger rightPadding = difference/2;
-
- NSString *leftString = [@"|" debugbox_stringByAddingPadding:@" " count:leftPadding location:PIDebugBoxPaddingLocationEnd];
- NSString *rightString = [@"|" debugbox_stringByAddingPadding:@" " count:rightPadding location:PIDebugBoxPaddingLocationFront];
-
- NSMutableArray *paddedLines = [NSMutableArray array];
- for (NSString *line in concatenatedLines) {
- NSString *paddedLine = [line stringByReplacingOccurrencesOfString:@"|" withString:leftString options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)];
- paddedLine = [paddedLine stringByReplacingOccurrencesOfString:@"|" withString:rightString options:NSCaseInsensitiveSearch range:NSMakeRange([paddedLine length] - 1, 1)];
- [paddedLines addObject:paddedLine];
- }
- concatenatedLines = paddedLines;
- // totalLineLength += difference;
- }
- concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent];
- return [concatenatedLines componentsJoinedByString:@"\n"];
-
-}
-
-+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
-{
- if ([children count] == 0) {
- return parent;
- }
-
- NSMutableArray *childrenLines = [NSMutableArray array];
-
- NSUInteger maxChildLength = 0;
- for (NSString *child in children) {
- NSArray *lines = [child componentsSeparatedByString:@"\n"];
- maxChildLength = MAX(maxChildLength, [lines[0] length]);
- }
-
- NSUInteger rightPadding = 0;
- NSUInteger leftPadding = 0;
-
- if (maxChildLength < [parent length]) {
- NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - maxChildLength;
- leftPadding = ceil((CGFloat)difference/2.0);
- rightPadding = difference/2;
- }
-
- NSString *rightPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightPadding + kDebugBoxPadding];
- NSString *leftPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftPadding + kDebugBoxPadding];
-
- for (NSString *child in children) {
- NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
-
- NSUInteger leftLinePadding = ceil((CGFloat)(maxChildLength - [lines[0] length])/2.0);
- NSUInteger rightLinePadding = (maxChildLength - [lines[0] length])/2.0;
-
- for (NSString *line in lines) {
- NSString *rightLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightLinePadding];
- rightLinePaddingString = [NSString stringWithFormat:@"%@%@|", rightLinePaddingString, rightPaddingString];
-
- NSString *leftLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftLinePadding];
- leftLinePaddingString = [NSString stringWithFormat:@"|%@%@", leftLinePaddingString, leftPaddingString];
-
- NSString *paddingLine = [NSString stringWithFormat:@"%@%@%@", leftLinePaddingString, line, rightLinePaddingString];
- [childrenLines addObject:paddingLine];
- }
- }
-
- childrenLines = [self appendTopAndBottomToBoxString:childrenLines parent:parent];
- return [childrenLines componentsJoinedByString:@"\n"];
-}
-
-+ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent
-{
- NSUInteger totalLineLength = [boxStrings[0] length];
- [boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]];
-
- NSUInteger leftPadding = ceil(((CGFloat)(totalLineLength - [parent length]))/2.0);
- NSUInteger rightPadding = (totalLineLength - [parent length])/2;
-
- NSString *topLine = [parent debugbox_stringByAddingPadding:@"-" count:leftPadding location:PIDebugBoxPaddingLocationFront];
- topLine = [topLine debugbox_stringByAddingPadding:@"-" count:rightPadding location:PIDebugBoxPaddingLocationEnd];
- [boxStrings insertObject:topLine atIndex:0];
-
- return boxStrings;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASAssert.mm b/submodules/AsyncDisplayKit/Source/ASAssert.mm
deleted file mode 100644
index 6a78b06eaf..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASAssert.mm
+++ /dev/null
@@ -1,58 +0,0 @@
-//
-// ASAssert.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-
-#if AS_TLS_AVAILABLE
-
-static _Thread_local int tls_mainThreadAssertionsDisabledCount;
-BOOL ASMainThreadAssertionsAreDisabled() {
- return tls_mainThreadAssertionsDisabledCount > 0;
-}
-
-void ASPushMainThreadAssertionsDisabled() {
- tls_mainThreadAssertionsDisabledCount += 1;
-}
-
-void ASPopMainThreadAssertionsDisabled() {
- tls_mainThreadAssertionsDisabledCount -= 1;
- ASDisplayNodeCAssert(tls_mainThreadAssertionsDisabledCount >= 0, @"Attempt to pop thread assertion-disabling without corresponding push.");
-}
-
-#else
-
-#import
-
-static pthread_key_t ASMainThreadAssertionsDisabledKey() {
- static pthread_key_t k;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- pthread_key_create(&k, NULL);
- });
- return k;
-}
-
-BOOL ASMainThreadAssertionsAreDisabled() {
- return (nullptr != pthread_getspecific(ASMainThreadAssertionsDisabledKey()));
-}
-
-void ASPushMainThreadAssertionsDisabled() {
- const auto key = ASMainThreadAssertionsDisabledKey();
- const auto oldVal = (intptr_t)pthread_getspecific(key);
- pthread_setspecific(key, (void *)(oldVal + 1));
-}
-
-void ASPopMainThreadAssertionsDisabled() {
- const auto key = ASMainThreadAssertionsDisabledKey();
- const auto oldVal = (intptr_t)pthread_getspecific(key);
- pthread_setspecific(key, (void *)(oldVal - 1));
- ASDisplayNodeCAssert(oldVal > 0, @"Attempt to pop thread assertion-disabling without corresponding push.");
-}
-
-#endif // AS_TLS_AVAILABLE
diff --git a/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.h b/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.h
deleted file mode 100644
index a77452622b..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.h
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// ASCGImageBuffer.h
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-AS_SUBCLASSING_RESTRICTED
-@interface ASCGImageBuffer : NSObject
-
-/// Init a zero-filled buffer with the given length.
-- (instancetype)initWithLength:(NSUInteger)length;
-
-@property (readonly) void *mutableBytes NS_RETURNS_INNER_POINTER;
-
-/// Don't do any drawing or call any methods after calling this.
-- (CGDataProviderRef)createDataProviderAndInvalidate CF_RETURNS_RETAINED;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.mm b/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.mm
deleted file mode 100644
index 6f05300e23..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASCGImageBuffer.mm
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-// ASCGImageBuffer.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import "ASCGImageBuffer.h"
-
-#import
-#import
-#import
-#import
-
-/**
- * The behavior of this class is modeled on the private function
- * _CGDataProviderCreateWithCopyOfData, which is the function used
- * by CGBitmapContextCreateImage.
- *
- * If the buffer is larger than a page, we use mmap and mark it as
- * read-only when they are finished drawing. Then we wrap the VM
- * in an NSData
- */
-@implementation ASCGImageBuffer {
- BOOL _createdData;
- BOOL _isVM;
- NSUInteger _length;
-}
-
-- (instancetype)initWithLength:(NSUInteger)length
-{
- if (self = [super init]) {
- _length = length;
- _isVM = (length >= vm_page_size);
- if (_isVM) {
- _mutableBytes = mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, VM_MAKE_TAG(VM_MEMORY_COREGRAPHICS_DATA), 0);
- if (_mutableBytes == MAP_FAILED) {
- NSAssert(NO, @"Failed to map for CG image data.");
- _isVM = NO;
- }
- }
-
- // Check the VM flag again because we may have failed above.
- if (!_isVM) {
- _mutableBytes = calloc(1, length);
- }
- }
- return self;
-}
-
-- (void)dealloc
-{
- if (!_createdData) {
- [ASCGImageBuffer deallocateBuffer:_mutableBytes length:_length isVM:_isVM];
- }
-}
-
-- (CGDataProviderRef)createDataProviderAndInvalidate
-{
- NSAssert(!_createdData, @"Should not create data provider from buffer multiple times.");
- _createdData = YES;
-
- // Mark the pages as read-only.
- if (_isVM) {
- __unused kern_return_t result = vm_protect(mach_task_self(), (vm_address_t)_mutableBytes, _length, true, VM_PROT_READ);
- NSAssert(result == noErr, @"Error marking buffer as read-only: %@", [NSError errorWithDomain:NSMachErrorDomain code:result userInfo:nil]);
- }
-
- // Wrap in an NSData
- BOOL isVM = _isVM;
- NSData *d = [[NSData alloc] initWithBytesNoCopy:_mutableBytes length:_length deallocator:^(void * _Nonnull bytes, NSUInteger length) {
- [ASCGImageBuffer deallocateBuffer:bytes length:length isVM:isVM];
- }];
- return CGDataProviderCreateWithCFData((__bridge CFDataRef)d);
-}
-
-+ (void)deallocateBuffer:(void *)buf length:(NSUInteger)length isVM:(BOOL)isVM
-{
- if (isVM) {
- __unused kern_return_t result = vm_deallocate(mach_task_self(), (vm_address_t)buf, length);
- NSAssert(result == noErr, @"Failed to unmap cg image buffer: %@", [NSError errorWithDomain:NSMachErrorDomain code:result userInfo:nil]);
- } else {
- free(buf);
- }
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASCollections.mm b/submodules/AsyncDisplayKit/Source/ASCollections.mm
deleted file mode 100644
index 592dee2e88..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASCollections.mm
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// ASCollections.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-/**
- * A private allocator that signals to our retain callback to skip the retain.
- * It behaves the same as the default allocator, but acts as a signal that we
- * are creating a transfer array so we should skip the retain.
- */
-static CFAllocatorRef gTransferAllocator;
-
-static const void *ASTransferRetain(CFAllocatorRef allocator, const void *val) {
- if (allocator == gTransferAllocator) {
- // Transfer allocator. Ignore retain and pass through.
- return val;
- } else {
- // Other allocator. Retain like normal.
- // This happens when they make a mutable copy.
- return (&kCFTypeArrayCallBacks)->retain(allocator, val);
- }
-}
-
-@implementation NSArray (ASCollections)
-
-+ (NSArray *)arrayByTransferring:(__strong id *)pointers count:(NSUInteger)count NS_RETURNS_RETAINED
-{
- // Custom callbacks that point to our ASTransferRetain callback.
- static CFArrayCallBacks callbacks;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- callbacks = kCFTypeArrayCallBacks;
- callbacks.retain = ASTransferRetain;
- CFAllocatorContext ctx;
- CFAllocatorGetContext(NULL, &ctx);
- gTransferAllocator = CFAllocatorCreate(NULL, &ctx);
- });
-
- // NSZeroArray fast path.
- if (count == 0) {
- return @[]; // Does not actually call +array when optimized.
- }
-
- // NSSingleObjectArray fast path. Retain/release here is worth it.
- if (count == 1) {
- NSArray *result = [[NSArray alloc] initWithObjects:pointers count:1];
- pointers[0] = nil;
- return result;
- }
-
- NSArray *result = (__bridge_transfer NSArray *)CFArrayCreate(gTransferAllocator, (const void **)(void *)pointers, count, &callbacks);
- memset(pointers, 0, count * sizeof(id));
- return result;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASConfiguration.mm b/submodules/AsyncDisplayKit/Source/ASConfiguration.mm
deleted file mode 100644
index 34f11bd366..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASConfiguration.mm
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// ASConfiguration.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-
-/// Not too performance-sensitive here.
-
-@implementation ASConfiguration
-
-- (instancetype)initWithDictionary:(NSDictionary *)dictionary
-{
- if (self = [super init]) {
- if (dictionary != nil) {
- const auto featureStrings = ASDynamicCast(dictionary[@"experimental_features"], NSArray);
- const auto version = ASDynamicCast(dictionary[@"version"], NSNumber).integerValue;
- if (version != ASConfigurationSchemaCurrentVersion) {
- NSLog(@"Texture warning: configuration schema is old version (%ld vs %ld)", (long)version, (long)ASConfigurationSchemaCurrentVersion);
- }
- self.experimentalFeatures = ASExperimentalFeaturesFromArray(featureStrings);
- } else {
- self.experimentalFeatures = kNilOptions;
- }
- }
- return self;
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- ASConfiguration *config = [[ASConfiguration alloc] initWithDictionary:nil];
- config.experimentalFeatures = self.experimentalFeatures;
- config.delegate = self.delegate;
- return config;
-}
-
-@end
-
-//#define AS_FIXED_CONFIG_JSON "{ \"version\" : 1, \"experimental_features\": [ \"exp_text_node\" ] }"
-
-#ifdef AS_FIXED_CONFIG_JSON
-
-@implementation ASConfiguration (UserProvided)
-
-+ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED
-{
- NSData *data = [@AS_FIXED_CONFIG_JSON dataUsingEncoding:NSUTF8StringEncoding];
- NSError *error;
- NSDictionary *d = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
- if (!d) {
- NSAssert(NO, @"Error parsing fixed config string '%s': %@", AS_FIXED_CONFIG_JSON, error);
- return nil;
- } else {
- return [[ASConfiguration alloc] initWithDictionary:d];
- }
-}
-
-@end
-
-#endif // AS_FIXED_CONFIG_JSON
diff --git a/submodules/AsyncDisplayKit/Source/ASConfigurationInternal.mm b/submodules/AsyncDisplayKit/Source/ASConfigurationInternal.mm
deleted file mode 100644
index 2fb190103a..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASConfigurationInternal.mm
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// ASConfigurationInternal.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-#import
-#import
-
-static ASConfigurationManager *ASSharedConfigurationManager;
-static dispatch_once_t ASSharedConfigurationManagerOnceToken;
-
-NS_INLINE ASConfigurationManager *ASConfigurationManagerGet() {
- dispatch_once(&ASSharedConfigurationManagerOnceToken, ^{
- ASSharedConfigurationManager = [[ASConfigurationManager alloc] init];
- });
- return ASSharedConfigurationManager;
-}
-
-@implementation ASConfigurationManager {
- ASConfiguration *_config;
- dispatch_queue_t _delegateQueue;
- BOOL _frameworkInitialized;
- _Atomic(ASExperimentalFeatures) _activatedExperiments;
-}
-
-+ (ASConfiguration *)defaultConfiguration NS_RETURNS_RETAINED
-{
- ASConfiguration *config = [[ASConfiguration alloc] init];
- // TODO(wsdwsd0829): Fix #788 before enabling it.
- // config.experimentalFeatures = ASExperimentalInterfaceStateCoalescing;
- return config;
-}
-
-- (instancetype)init
-{
- if (self = [super init]) {
- _delegateQueue = dispatch_queue_create("org.TextureGroup.Texture.ConfigNotifyQueue", DISPATCH_QUEUE_SERIAL);
- if ([ASConfiguration respondsToSelector:@selector(textureConfiguration)]) {
- _config = [[ASConfiguration textureConfiguration] copy];
- } else {
- _config = [ASConfigurationManager defaultConfiguration];
- }
- }
- return self;
-}
-
-- (void)frameworkDidInitialize
-{
- ASDisplayNodeAssertMainThread();
- if (_frameworkInitialized) {
- ASDisplayNodeFailAssert(@"Framework initialized twice.");
- return;
- }
- _frameworkInitialized = YES;
-
- const auto delegate = _config.delegate;
- if ([delegate respondsToSelector:@selector(textureDidInitialize)]) {
- [delegate textureDidInitialize];
- }
-}
-
-- (BOOL)activateExperimentalFeature:(ASExperimentalFeatures)requested
-{
- if (_config == nil) {
- return NO;
- }
-
- NSAssert(__builtin_popcountl(requested) == 1, @"Cannot activate multiple features at once with this method.");
-
- // We need to call out, whether it's enabled or not.
- // A/B testing requires even "control" users to be activated.
- ASExperimentalFeatures enabled = requested & _config.experimentalFeatures;
- ASExperimentalFeatures prevTriggered = atomic_fetch_or(&_activatedExperiments, requested);
- ASExperimentalFeatures newlyTriggered = requested & ~prevTriggered;
-
- // Notify delegate if needed.
- if (newlyTriggered != 0) {
- __unsafe_unretained id del = _config.delegate;
- dispatch_async(_delegateQueue, ^{
- [del textureDidActivateExperimentalFeatures:newlyTriggered];
- });
- }
-
- return (enabled != 0);
-}
-
-// Define this even when !DEBUG, since we may run our tests in release mode.
-+ (void)test_resetWithConfiguration:(ASConfiguration *)configuration
-{
- ASConfigurationManager *inst = ASConfigurationManagerGet();
- inst->_config = configuration ?: [self defaultConfiguration];
- atomic_store(&inst->_activatedExperiments, 0);
-}
-
-@end
-
-BOOL _ASActivateExperimentalFeature(ASExperimentalFeatures feature)
-{
- return [ASConfigurationManagerGet() activateExperimentalFeature:feature];
-}
-
-void ASNotifyInitialized()
-{
- [ASConfigurationManagerGet() frameworkDidInitialize];
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASControlNode+Private.h b/submodules/AsyncDisplayKit/Source/ASControlNode+Private.h
deleted file mode 100644
index 02f54a20ec..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASControlNode+Private.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// ASControlNode+Private.h
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-@interface ASControlNode (Private)
-
-#if TARGET_OS_TV
-- (void)_pressDown;
-#endif
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASControlNode.mm b/submodules/AsyncDisplayKit/Source/ASControlNode.mm
deleted file mode 100644
index dcdb9ec829..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASControlNode.mm
+++ /dev/null
@@ -1,499 +0,0 @@
-//
-// ASControlNode.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import "ASControlNode+Private.h"
-#import
-#import
-#import
-#import
-#import
-#import
-
-// UIControl allows dragging some distance outside of the control itself during
-// tracking. This value depends on the device idiom (25 or 70 points), so
-// so replicate that effect with the same values here for our own controls.
-#define kASControlNodeExpandedInset (([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? -25.0f : -70.0f)
-
-// Initial capacities for dispatch tables.
-#define kASControlNodeEventDispatchTableInitialCapacity 4
-#define kASControlNodeActionDispatchTableInitialCapacity 4
-
-@interface ASControlNode ()
-{
-@private
- // Control Attributes
- BOOL _enabled;
- BOOL _highlighted;
-
- // Tracking
- BOOL _tracking;
- BOOL _touchInside;
-
- // Target action pairs stored in an array for each event type
- // ASControlEvent -> [ASTargetAction0, ASTargetAction1]
- NSMutableDictionary, NSMutableArray *> *_controlEventDispatchTable;
-}
-
-// Read-write overrides.
-@property (getter=isTracking) BOOL tracking;
-@property (getter=isTouchInside) BOOL touchInside;
-
-/**
- @abstract Returns a key to be used in _controlEventDispatchTable that identifies the control event.
- @param controlEvent A control event.
- @result A key for use in _controlEventDispatchTable.
- */
-id _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEvent);
-
-/**
- @abstract Enumerates the ASControlNode events included mask, invoking the block for each event.
- @param mask An ASControlNodeEvent mask.
- @param block The block to be invoked for each ASControlNodeEvent included in mask.
- */
-void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent));
-
-/**
- @abstract Returns the expanded bounds used to determine if a touch is considered 'inside' during tracking.
- @param controlNode A control node.
- @result The expanded bounds of the node.
- */
-CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode);
-
-
-@end
-
-@implementation ASControlNode
-{
-}
-
-#pragma mark - Lifecycle
-
-- (instancetype)init
-{
- if (!(self = [super init]))
- return nil;
-
- _enabled = YES;
-
- // As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on.
- self.userInteractionEnabled = NO;
-
- return self;
-}
-
-#if TARGET_OS_TV
-- (void)didLoad
-{
- [super didLoad];
-
- // On tvOS all controls, such as buttons, interact with the focus system even if they don't have a target set on them.
- // Here we add our own internal tap gesture to handle this behaviour.
- self.userInteractionEnabled = YES;
- UITapGestureRecognizer *tapGestureRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_pressDown)];
- tapGestureRec.allowedPressTypes = @[@(UIPressTypeSelect)];
- [self.view addGestureRecognizer:tapGestureRec];
-}
-#endif
-
-- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
-{
- [super setUserInteractionEnabled:userInteractionEnabled];
- self.isAccessibilityElement = userInteractionEnabled;
-}
-
-- (void)__exitHierarchy
-{
- [super __exitHierarchy];
-
- // If a control node is exit the hierarchy and is tracking we have to cancel it
- if (self.tracking) {
- [self _cancelTrackingWithEvent:nil];
- }
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
-
-#pragma mark - ASDisplayNode Overrides
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // If we're not interested in touches, we have nothing to do.
- if (!self.enabled) {
- return;
- }
-
- // Check if the tracking should start
- UITouch *theTouch = [touches anyObject];
- if (![self beginTrackingWithTouch:theTouch withEvent:event]) {
- return;
- }
-
- // If we get more than one touch down on us, cancel.
- // Additionally, if we're already tracking a touch, a second touch beginning is cause for cancellation.
- if (touches.count > 1 || self.tracking) {
- [self _cancelTrackingWithEvent:event];
- } else {
- // Otherwise, begin tracking.
- self.tracking = YES;
-
- // No need to check bounds on touchesBegan as we wouldn't get the call if it wasn't in our bounds.
- self.touchInside = YES;
- self.highlighted = YES;
-
- // Send the appropriate touch-down control event depending on how many times we've been tapped.
- ASControlNodeEvent controlEventMask = (theTouch.tapCount == 1) ? ASControlNodeEventTouchDown : ASControlNodeEventTouchDownRepeat;
- [self sendActionsForControlEvents:controlEventMask withEvent:event];
- }
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // If we're not interested in touches, we have nothing to do.
- if (!self.enabled) {
- return;
- }
-
- NSParameterAssert(touches.count == 1);
- UITouch *theTouch = [touches anyObject];
-
- // Check if tracking should continue
- if (!self.tracking || ![self continueTrackingWithTouch:theTouch withEvent:event]) {
- self.tracking = NO;
- return;
- }
-
- CGPoint touchLocation = [theTouch locationInView:self.view];
-
- // Update our touchInside state.
- BOOL dragIsInsideBounds = [self pointInside:touchLocation withEvent:nil];
-
- // Update our highlighted state.
- CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self);
- BOOL dragIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation);
- self.touchInside = dragIsInsideExpandedBounds;
- self.highlighted = dragIsInsideExpandedBounds;
-
- [self sendActionsForControlEvents:(dragIsInsideBounds ? ASControlNodeEventTouchDragInside : ASControlNodeEventTouchDragOutside)
- withEvent:event];
-}
-
-- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // If we're not interested in touches, we have nothing to do.
- if (!self.enabled) {
- return;
- }
-
- // Note that we've cancelled tracking.
- [self _cancelTrackingWithEvent:event];
-}
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // If we're not interested in touches, we have nothing to do.
- if (!self.enabled) {
- return;
- }
-
- // On iPhone 6s, iOS 9.2 (and maybe other versions) sometimes calls -touchesEnded:withEvent:
- // twice on the view for one call to -touchesBegan:withEvent:. On ASControlNode, it used to
- // trigger an action twice unintentionally. Now, we ignore that event if we're not in a tracking
- // state in order to have a correct behavior.
- // It might be related to that issue: http://www.openradar.me/22910171
- if (!self.tracking) {
- return;
- }
-
- NSParameterAssert([touches count] == 1);
- UITouch *theTouch = [touches anyObject];
- CGPoint touchLocation = [theTouch locationInView:self.view];
-
- // Update state.
- self.tracking = NO;
- self.touchInside = NO;
- self.highlighted = NO;
-
- // Note that we've ended tracking.
- [self endTrackingWithTouch:theTouch withEvent:event];
-
- // Send the appropriate touch-up control event.
- CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self);
- BOOL touchUpIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation);
-
- [self sendActionsForControlEvents:(touchUpIsInsideExpandedBounds ? ASControlNodeEventTouchUpInside : ASControlNodeEventTouchUpOutside)
- withEvent:event];
-}
-
-- (void)_cancelTrackingWithEvent:(UIEvent *)event
-{
- // We're no longer tracking and there is no touch to be inside.
- self.tracking = NO;
- self.touchInside = NO;
- self.highlighted = NO;
-
- // Send the cancel event.
- [self sendActionsForControlEvents:ASControlNodeEventTouchCancel withEvent:event];
-}
-
-#pragma clang diagnostic pop
-
-- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
-{
- ASDisplayNodeAssertMainThread();
-
- // If not enabled we should not care about receving touches
- if (! self.enabled) {
- return nil;
- }
-
- return [super hitTest:point withEvent:event];
-}
-
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
-{
- // If we're interested in touches, this is a tap (the only gesture we care about) and passed -hitTest for us, then no, you may not begin. Sir.
- if (self.enabled && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && gestureRecognizer.view != self.view) {
- UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)gestureRecognizer;
- // Allow double-tap gestures
- return tapRecognizer.numberOfTapsRequired != 1;
- }
-
- // Otherwise, go ahead. :]
- return YES;
-}
-
-- (BOOL)supportsLayerBacking
-{
- return super.supportsLayerBacking && !self.userInteractionEnabled;
-}
-
-#pragma mark - Action Messages
-
-- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask
-{
- NSParameterAssert(action);
- NSParameterAssert(controlEventMask != 0);
-
- // ASControlNode cannot be layer backed if adding a target
- ASDisplayNodeAssert(!self.isLayerBacked, @"ASControlNode is layer backed, will never be able to call target in target:action: pair.");
-
- ASLockScopeSelf();
-
- if (!_controlEventDispatchTable) {
- _controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
- }
-
- // Create new target action pair
- ASControlTargetAction *targetAction = [[ASControlTargetAction alloc] init];
- targetAction.action = action;
- targetAction.target = target;
-
- // Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask
- _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^
- (ASControlNodeEvent controlEvent)
- {
- // Do we already have an event table for this control event?
- id eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent);
- NSMutableArray *eventTargetActionArray = _controlEventDispatchTable[eventKey];
-
- if (!eventTargetActionArray) {
- eventTargetActionArray = [[NSMutableArray alloc] init];
- }
-
- // Remove any prior target-action pair for this event, as UIKit does.
- [eventTargetActionArray removeObject:targetAction];
-
- // Register the new target-action as the last one to be sent.
- [eventTargetActionArray addObject:targetAction];
-
- if (eventKey) {
- [_controlEventDispatchTable setObject:eventTargetActionArray forKey:eventKey];
- }
- });
-
- self.userInteractionEnabled = YES;
-}
-
-- (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent
-{
- NSParameterAssert(target);
- NSParameterAssert(controlEvent != 0 && controlEvent != ASControlNodeEventAllEvents);
-
- ASLockScopeSelf();
-
- // Grab the event target action array for this event.
- NSMutableArray *eventTargetActionArray = _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)];
- if (!eventTargetActionArray) {
- return nil;
- }
-
- NSMutableArray *actions = [[NSMutableArray alloc] init];
-
- // Collect all actions for this target.
- for (ASControlTargetAction *targetAction in eventTargetActionArray) {
- if ((target == nil && targetAction.createdWithNoTarget) || (target != nil && target == targetAction.target)) {
- [actions addObject:NSStringFromSelector(targetAction.action)];
- }
- }
-
- return actions;
-}
-
-- (NSSet *)allTargets
-{
- ASLockScopeSelf();
-
- NSMutableSet *targets = [[NSMutableSet alloc] init];
-
- // Look at each event...
- for (NSMutableArray *eventTargetActionArray in [_controlEventDispatchTable objectEnumerator]) {
- // and each event's targets...
- for (ASControlTargetAction *targetAction in eventTargetActionArray) {
- [targets addObject:targetAction.target];
- }
- }
-
- return targets;
-}
-
-- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask
-{
- NSParameterAssert(controlEventMask != 0);
-
- ASLockScopeSelf();
-
- // Enumerate the events in the mask, removing the target-action pair for each control event included in controlEventMask.
- _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^
- (ASControlNodeEvent controlEvent)
- {
- // Grab the dispatch table for this event (if we have it).
- id eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent);
- NSMutableArray *eventTargetActionArray = _controlEventDispatchTable[eventKey];
- if (!eventTargetActionArray) {
- return;
- }
-
- NSPredicate *filterPredicate = [NSPredicate predicateWithBlock:^BOOL(ASControlTargetAction *_Nullable evaluatedObject, NSDictionary * _Nullable bindings) {
- if (!target || evaluatedObject.target == target) {
- if (!action) {
- return NO;
- } else if (evaluatedObject.action == action) {
- return NO;
- }
- }
-
- return YES;
- }];
- [eventTargetActionArray filterUsingPredicate:filterPredicate];
-
- if (eventTargetActionArray.count == 0) {
- // If there are no targets for this event anymore, remove it.
- [_controlEventDispatchTable removeObjectForKey:eventKey];
- }
- });
-}
-
-#pragma mark -
-
-- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)event
-{
- ASDisplayNodeAssertMainThread(); //We access self.view below, it's not safe to call this off of main.
- NSParameterAssert(controlEvents != 0);
-
- NSMutableArray *resolvedEventTargetActionArray = [[NSMutableArray alloc] init];
-
- {
- ASLockScopeSelf();
-
- // Enumerate the events in the mask, invoking the target-action pairs for each.
- _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^
- (ASControlNodeEvent controlEvent)
- {
- // Iterate on each target action pair
- for (ASControlTargetAction *targetAction in _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]) {
- ASControlTargetAction *resolvedTargetAction = [[ASControlTargetAction alloc] init];
- resolvedTargetAction.action = targetAction.action;
- resolvedTargetAction.target = targetAction.target;
-
- // NSNull means that a nil target was set, so start at self and travel the responder chain
- if (!resolvedTargetAction.target && targetAction.createdWithNoTarget) {
- // if the target cannot perform the action, travel the responder chain to try to find something that does
- resolvedTargetAction.target = [self.view targetForAction:resolvedTargetAction.action withSender:self];
- }
-
- if (resolvedTargetAction.target) {
- [resolvedEventTargetActionArray addObject:resolvedTargetAction];
- }
- }
- });
- }
-
- //We don't want to hold the lock while calling out, we could potentially walk up the ownership tree causing a deadlock.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- for (ASControlTargetAction *targetAction in resolvedEventTargetActionArray) {
- [targetAction.target performSelector:targetAction.action withObject:self withObject:event];
- }
-#pragma clang diagnostic pop
-}
-
-#pragma mark - Convenience
-
-id _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEvent)
-{
- return @(controlEvent);
-}
-
-void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent))
-{
- if (block == nil) {
- return;
- }
- // Start with our first event (touch down) and work our way up to the last event (PrimaryActionTriggered)
- for (ASControlNodeEvent thisEvent = ASControlNodeEventTouchDown; thisEvent <= ASControlNodeEventPrimaryActionTriggered; thisEvent <<= 1) {
- // If it's included in the mask, invoke the block.
- if ((mask & thisEvent) == thisEvent)
- block(thisEvent);
- }
-}
-
-CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode) {
- return CGRectInset(UIEdgeInsetsInsetRect(controlNode.view.bounds, controlNode.hitTestSlop), kASControlNodeExpandedInset, kASControlNodeExpandedInset);
-}
-
-#pragma mark - For Subclasses
-
-- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent
-{
- return YES;
-}
-
-- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent
-{
- return YES;
-}
-
-- (void)cancelTrackingWithEvent:(UIEvent *)touchEvent
-{
- // Subclass hook
-}
-
-- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent
-{
- // Subclass hook
-}
-
-#pragma mark - Debug
-- (ASDisplayNode *)debugHighlightOverlay
-{
- return nil;
-}
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASControlTargetAction.mm b/submodules/AsyncDisplayKit/Source/ASControlTargetAction.mm
deleted file mode 100644
index 41cc113314..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASControlTargetAction.mm
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// ASControlTargetAction.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-@implementation ASControlTargetAction
-{
- __weak id _target;
- BOOL _createdWithNoTarget;
-}
-
-- (void)setTarget:(id)target {
- _target = target;
-
- if (!target) {
- _createdWithNoTarget = YES;
- }
-}
-
-- (id)target {
- return _target;
-}
-
-- (BOOL)isEqual:(id)object {
- if (![object isKindOfClass:[ASControlTargetAction class]]) {
- return NO;
- }
-
- ASControlTargetAction *otherObject = (ASControlTargetAction *)object;
-
- BOOL areTargetsEqual;
-
- if (self.target != nil && otherObject.target != nil && self.target == otherObject.target) {
- areTargetsEqual = YES;
- }
- else if (self.target == nil && otherObject.target == nil && self.createdWithNoTarget && otherObject.createdWithNoTarget) {
- areTargetsEqual = YES;
- }
- else {
- areTargetsEqual = NO;
- }
-
- if (!areTargetsEqual) {
- return NO;
- }
-
- if (self.action && otherObject.action && self.action == otherObject.action) {
- return YES;
- }
- else {
- return NO;
- }
-}
-
-- (NSUInteger)hash {
- return [self.target hash];
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDimension.mm b/submodules/AsyncDisplayKit/Source/ASDimension.mm
deleted file mode 100644
index d1a42462df..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDimension.mm
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// ASDimension.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-
-#import
-
-#pragma mark - ASDimension
-
-ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0};
-
-ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
-{
- if (dimension.length > 0) {
-
- // Handle points
- if ([dimension hasSuffix:@"pt"]) {
- return ASDimensionMake(ASDimensionUnitPoints, ASCGFloatFromString(dimension));
- }
-
- // Handle auto
- if ([dimension isEqualToString:@"auto"]) {
- return ASDimensionAuto;
- }
-
- // Handle percent
- if ([dimension hasSuffix:@"%"]) {
- return ASDimensionMake(ASDimensionUnitFraction, (ASCGFloatFromString(dimension) / 100.0));
- }
- }
-
- return ASDimensionAuto;
-}
-
-NSString *NSStringFromASDimension(ASDimension dimension)
-{
- switch (dimension.unit) {
- case ASDimensionUnitPoints:
- return [NSString stringWithFormat:@"%.0fpt", dimension.value];
- case ASDimensionUnitFraction:
- return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0];
- case ASDimensionUnitAuto:
- return @"Auto";
- }
-}
-
-#pragma mark - ASLayoutSize
-
-ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto};
-
-#pragma mark - ASSizeRange
-
-ASSizeRange const ASSizeRangeZero = {};
-
-ASSizeRange const ASSizeRangeUnconstrained = { {0, 0}, { INFINITY, INFINITY }};
-
-struct _Range {
- CGFloat min;
- CGFloat max;
-
- /**
- Intersects another dimension range. If the other range does not overlap, this size range "wins" by returning a
- single point within its own range that is closest to the non-overlapping range.
- */
- _Range intersect(const _Range &other) const
- {
- CGFloat newMin = MAX(min, other.min);
- CGFloat newMax = MIN(max, other.max);
- if (newMin <= newMax) {
- return {newMin, newMax};
- } else {
- // No intersection. If we're before the other range, return our max; otherwise our min.
- if (min < other.min) {
- return {max, max};
- } else {
- return {min, min};
- }
- }
- }
-};
-
-ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange)
-{
- const auto w = _Range({sizeRange.min.width, sizeRange.max.width}).intersect({otherSizeRange.min.width, otherSizeRange.max.width});
- const auto h = _Range({sizeRange.min.height, sizeRange.max.height}).intersect({otherSizeRange.min.height, otherSizeRange.max.height});
- return {{w.min, h.min}, {w.max, h.max}};
-}
-
-NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
-{
- // 17 field length copied from iOS 10.3 impl of NSStringFromCGSize.
- if (CGSizeEqualToSize(sizeRange.min, sizeRange.max)) {
- return [NSString stringWithFormat:@"{{%.*g, %.*g}}",
- 17, sizeRange.min.width,
- 17, sizeRange.min.height];
- }
- return [NSString stringWithFormat:@"{{%.*g, %.*g}, {%.*g, %.*g}}",
- 17, sizeRange.min.width,
- 17, sizeRange.min.height,
- 17, sizeRange.max.width,
- 17, sizeRange.max.height];
-}
-
-#if YOGA
-#pragma mark - Yoga - ASEdgeInsets
-ASEdgeInsets const ASEdgeInsetsZero = {};
-
-ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets)
-{
- ASEdgeInsets asEdgeInsets = ASEdgeInsetsZero;
- asEdgeInsets.top = ASDimensionMake(edgeInsets.top);
- asEdgeInsets.left = ASDimensionMake(edgeInsets.left);
- asEdgeInsets.bottom = ASDimensionMake(edgeInsets.bottom);
- asEdgeInsets.right = ASDimensionMake(edgeInsets.right);
- return asEdgeInsets;
-}
-#endif
diff --git a/submodules/AsyncDisplayKit/Source/ASDimensionInternal.mm b/submodules/AsyncDisplayKit/Source/ASDimensionInternal.mm
deleted file mode 100644
index 8af3555252..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDimensionInternal.mm
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// ASDimensionInternal.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#pragma mark - ASLayoutElementSize
-
-NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size)
-{
- return [NSString stringWithFormat:
- @"",
- NSStringFromASLayoutSize(ASLayoutSizeMake(size.width, size.height)),
- NSStringFromASLayoutSize(ASLayoutSizeMake(size.minWidth, size.minHeight)),
- NSStringFromASLayoutSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight))];
-}
-
-ASDISPLAYNODE_INLINE void ASLayoutElementSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax)
-{
- NSCAssert(!isnan(minVal), @"minVal must not be NaN");
- NSCAssert(!isnan(maxVal), @"maxVal must not be NaN");
- // Avoid use of min/max primitives since they're harder to reason
- // about in the presence of NaN (in exactVal)
- // Follow CSS: min overrides max overrides exact.
-
- // Begin with the min/max range
- *outMin = minVal;
- *outMax = maxVal;
- if (maxVal <= minVal) {
- // min overrides max and exactVal is irrelevant
- *outMax = minVal;
- return;
- }
- if (isnan(exactVal)) {
- // no exact value, so leave as a min/max range
- return;
- }
- if (exactVal > maxVal) {
- // clip to max value
- *outMin = maxVal;
- } else if (exactVal < minVal) {
- // clip to min value
- *outMax = minVal;
- } else {
- // use exact value
- *outMin = *outMax = exactVal;
- }
-}
-
-ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange)
-{
- CGSize resolvedExact = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.width, size.height), parentSize, {NAN, NAN});
- CGSize resolvedMin = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min);
- CGSize resolvedMax = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max);
-
- CGSize rangeMin, rangeMax;
- ASLayoutElementSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width);
- ASLayoutElementSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height);
- return {rangeMin, rangeMax};
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASDispatch.h b/submodules/AsyncDisplayKit/Source/ASDispatch.h
deleted file mode 100644
index e20941806f..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDispatch.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// ASDispatch.h
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-
-/**
- * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs.
- *
- * Note: The actual number of threads may be lower than threadCount, if libdispatch
- * decides the system can't handle it. In reality this rarely happens.
- */
-AS_EXTERN void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i));
-
-/**
- * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs.
- *
- * Note: The actual number of threads may be lower than threadCount, if libdispatch
- * decides the system can't handle it. In reality this rarely happens.
- */
-AS_EXTERN void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i));
diff --git a/submodules/AsyncDisplayKit/Source/ASDispatch.mm b/submodules/AsyncDisplayKit/Source/ASDispatch.mm
deleted file mode 100644
index edc2feba46..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDispatch.mm
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// ASDispatch.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import "ASDispatch.h"
-#import
-
-
-// Prefer C atomics in this file because ObjC blocks can't capture C++ atomics well.
-#import
-
-/**
- * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs.
- *
- * Note: The actual number of threads may be lower than threadCount, if libdispatch
- * decides the system can't handle it. In reality this rarely happens.
- */
-void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) {
- if (threadCount == 0) {
- if (ASActivateExperimentalFeature(ASExperimentalDispatchApply)) {
- dispatch_apply(iterationCount, queue, work);
- return;
- }
- threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2;
- }
- dispatch_group_t group = dispatch_group_create();
- __block atomic_size_t counter = ATOMIC_VAR_INIT(0);
- for (NSUInteger t = 0; t < threadCount; t++) {
- dispatch_group_async(group, queue, ^{
- size_t i;
- while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) {
- work(i);
- }
- });
- }
- dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
-};
-
-/**
- * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs.
- *
- * Note: The actual number of threads may be lower than threadCount, if libdispatch
- * decides the system can't handle it. In reality this rarely happens.
- */
-void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) {
- if (threadCount == 0) {
- threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2;
- }
- __block atomic_size_t counter = ATOMIC_VAR_INIT(0);
- for (NSUInteger t = 0; t < threadCount; t++) {
- dispatch_async(queue, ^{
- size_t i;
- while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) {
- work(i);
- }
- });
- }
-};
-
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Ancestry.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Ancestry.mm
deleted file mode 100644
index ea1376ed54..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Ancestry.mm
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// ASDisplayNode+Ancestry.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-
-AS_SUBCLASSING_RESTRICTED
-@interface ASNodeAncestryEnumerator : NSEnumerator
-@end
-
-@implementation ASNodeAncestryEnumerator {
- ASDisplayNode *_lastNode; // This needs to be strong because enumeration will not retain the current batch of objects
- BOOL _initialState;
-}
-
-- (instancetype)initWithNode:(ASDisplayNode *)node
-{
- if (self = [super init]) {
- _initialState = YES;
- _lastNode = node;
- }
- return self;
-}
-
-- (id)nextObject
-{
- if (_initialState) {
- _initialState = NO;
- return _lastNode;
- }
-
- ASDisplayNode *nextNode = _lastNode.supernode;
- if (nextNode == nil && ASDisplayNodeThreadIsMain()) {
- CALayer *layer = _lastNode.nodeLoaded ? _lastNode.layer.superlayer : nil;
- while (layer != nil) {
- nextNode = ASLayerToDisplayNode(layer);
- if (nextNode != nil) {
- break;
- }
- layer = layer.superlayer;
- }
- }
- _lastNode = nextNode;
- return nextNode;
-}
-
-@end
-
-@implementation ASDisplayNode (Ancestry)
-
-- (id)supernodes
-{
- NSEnumerator *result = [[ASNodeAncestryEnumerator alloc] initWithNode:self];
- [result nextObject]; // discard first object (self)
- return result;
-}
-
-- (id)supernodesIncludingSelf
-{
- return [[ASNodeAncestryEnumerator alloc] initWithNode:self];
-}
-
-- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf
-{
- id chain = includeSelf ? self.supernodesIncludingSelf : self.supernodes;
- for (ASDisplayNode *ancestor in chain) {
- if ([ancestor isKindOfClass:supernodeClass]) {
- return ancestor;
- }
- }
- return nil;
-}
-
-- (NSString *)ancestryDescription
-{
- NSMutableArray *strings = [NSMutableArray array];
- for (ASDisplayNode *node in self.supernodes) {
- [strings addObject:ASObjectDescriptionMakeTiny(node)];
- }
- return strings.description;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+AsyncDisplay.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+AsyncDisplay.mm
deleted file mode 100644
index 068f5509a7..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+AsyncDisplay.mm
+++ /dev/null
@@ -1,493 +0,0 @@
-//
-// ASDisplayNode+AsyncDisplay.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-#import
-#import "ASDisplayNodeInternal.h"
-#import
-#import
-#import
-#import "ASSignpost.h"
-#import
-
-using AS::MutexLocker;
-
-@interface ASDisplayNode () <_ASDisplayLayerDelegate>
-@end
-
-@implementation ASDisplayNode (AsyncDisplay)
-
-#if ASDISPLAYNODE_DELAY_DISPLAY
- #define ASDN_DELAY_FOR_DISPLAY() usleep( (long)(0.1 * USEC_PER_SEC) )
-#else
- #define ASDN_DELAY_FOR_DISPLAY()
-#endif
-
-#define CHECK_CANCELLED_AND_RETURN_NIL(expr) if (isCancelledBlock()) { \
- expr; \
- return nil; \
- } \
-
-- (NSObject *)drawParameters
-{
- __instanceLock__.lock();
- BOOL implementsDrawParameters = _flags.implementsDrawParameters;
- __instanceLock__.unlock();
-
- if (implementsDrawParameters) {
- return [self drawParametersForAsyncLayer:self.asyncLayer];
- } else {
- return nil;
- }
-}
-
-- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks
-{
- // Skip subtrees that are hidden or zero alpha.
- if (self.isHidden || self.alpha <= 0.0) {
- return;
- }
-
- __instanceLock__.lock();
- BOOL rasterizingFromAscendent = (_hierarchyState & ASHierarchyStateRasterized);
- __instanceLock__.unlock();
-
- // if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers
- if (rasterizingFromAscendent) {
- [self __layout];
- }
-
- // Capture these outside the display block so they are retained.
- UIColor *backgroundColor = self.backgroundColor;
- CGRect bounds = self.bounds;
- CGFloat cornerRadius = self.cornerRadius;
- BOOL clipsToBounds = self.clipsToBounds;
-
- CGRect frame;
-
- // If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint.
- if (self.rasterizesSubtree) {
- frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height);
- } else {
- CGPoint position = self.position;
- CGPoint anchorPoint = self.anchorPoint;
-
- // Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot.
- CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform);
- CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform);
- CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x,
- position.y - scaledBoundsSize.height * anchorPoint.y);
- frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
- }
-
- // Get the display block for this node.
- asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:NO isCancelledBlock:isCancelledBlock rasterizing:YES];
-
- // We'll display something if there is a display block, clipping, translation and/or a background color.
- BOOL shouldDisplay = displayBlock || backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds;
-
- // If we should display, then push a transform, draw the background color, and draw the contents.
- // The transform is popped in a block added after the recursion into subnodes.
- if (shouldDisplay) {
- dispatch_block_t pushAndDisplayBlock = ^{
- // Push transform relative to parent.
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextSaveGState(context);
-
- CGContextTranslateCTM(context, frame.origin.x, frame.origin.y);
-
- //support cornerRadius
- if (rasterizingFromAscendent && clipsToBounds) {
- if (cornerRadius) {
- [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius] addClip];
- } else {
- CGContextClipToRect(context, bounds);
- }
- }
-
- // Fill background if any.
- CGColorRef backgroundCGColor = backgroundColor.CGColor;
- if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) {
- CGContextSetFillColorWithColor(context, backgroundCGColor);
- CGContextFillRect(context, bounds);
- }
-
- // If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store).
- if (displayBlock) {
- UIImage *image = (UIImage *)displayBlock();
- if (image) {
- BOOL opaque = ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage));
- CGBlendMode blendMode = opaque ? kCGBlendModeCopy : kCGBlendModeNormal;
- [image drawInRect:bounds blendMode:blendMode alpha:1];
- }
- }
- };
- [displayBlocks addObject:pushAndDisplayBlock];
- }
-
- // Recursively capture displayBlocks for all descendants.
- for (ASDisplayNode *subnode in self.subnodes) {
- [subnode _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks];
- }
-
- // If we pushed a transform, pop it by adding a display block that does nothing other than that.
- if (shouldDisplay) {
- // Since this block is pure, we can store it statically.
- static dispatch_block_t popBlock;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- popBlock = ^{
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextRestoreGState(context);
- };
- });
- [displayBlocks addObject:popBlock];
- }
-}
-
-- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous
- isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock
- rasterizing:(BOOL)rasterizing
-{
- ASDisplayNodeAssertMainThread();
-
- asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil;
- ASDisplayNodeFlags flags;
-
- __instanceLock__.lock();
-
- flags = _flags;
-
- // We always create a graphics context, unless a -display method is used, OR if we are a subnode drawing into a rasterized parent.
- BOOL shouldCreateGraphicsContext = (flags.implementsImageDisplay == NO && rasterizing == NO);
- BOOL shouldBeginRasterizing = (rasterizing == NO && flags.rasterizesSubtree);
- BOOL usesImageDisplay = flags.implementsImageDisplay;
- BOOL usesDrawRect = flags.implementsDrawRect;
-
- if (usesImageDisplay == NO && usesDrawRect == NO && shouldBeginRasterizing == NO) {
- // Early exit before requesting more expensive properties like bounds and opaque from the layer.
- __instanceLock__.unlock();
- return nil;
- }
-
- BOOL opaque = self.opaque;
- CGRect bounds = self.bounds;
- UIColor *backgroundColor = self.backgroundColor;
- CGColorRef borderColor = self.borderColor;
- CGFloat borderWidth = self.borderWidth;
- CGFloat contentsScaleForDisplay = _contentsScaleForDisplay;
-
- __instanceLock__.unlock();
-
- // Capture drawParameters from delegate on main thread, if this node is displaying itself rather than recursively rasterizing.
- id drawParameters = (shouldBeginRasterizing == NO ? [self drawParameters] : nil);
-
- // Only the -display methods should be called if we can't size the graphics buffer to use.
- if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) {
- return nil;
- }
-
- ASDisplayNodeAssert(contentsScaleForDisplay != 0.0, @"Invalid contents scale");
- ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized),
- @"Rasterized descendants should never display unless being drawn into the rasterized container.");
-
- if (shouldBeginRasterizing) {
- // Collect displayBlocks for all descendants.
- NSMutableArray *displayBlocks = [[NSMutableArray alloc] init];
- [self _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks];
- CHECK_CANCELLED_AND_RETURN_NIL();
-
- // If [UIColor clearColor] or another semitransparent background color is used, include alpha channel when rasterizing.
- // Unlike CALayer drawing, we include the backgroundColor as a base during rasterization.
- opaque = opaque && CGColorGetAlpha(backgroundColor.CGColor) == 1.0f;
-
- displayBlock = ^id{
- CHECK_CANCELLED_AND_RETURN_NIL();
-
- ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
-
- for (dispatch_block_t block in displayBlocks) {
- CHECK_CANCELLED_AND_RETURN_NIL(ASGraphicsEndImageContext());
- block();
- }
-
- UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
-
- ASDN_DELAY_FOR_DISPLAY();
- return image;
- };
- } else {
- displayBlock = ^id{
- CHECK_CANCELLED_AND_RETURN_NIL();
-
- if (shouldCreateGraphicsContext) {
- ASGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
- CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); );
- }
-
- CGContextRef currentContext = UIGraphicsGetCurrentContext();
- UIImage *image = nil;
-
- if (shouldCreateGraphicsContext && !currentContext) {
- //ASDisplayNodeAssert(NO, @"Failed to create a CGContext (size: %@)", NSStringFromCGSize(bounds.size));
- return nil;
- }
-
- // For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or
- // _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs.
- [self __willDisplayNodeContentWithRenderingContext:currentContext drawParameters:drawParameters];
-
- if (usesImageDisplay) { // If we are using a display method, we'll get an image back directly.
- image = [self.class displayWithParameters:drawParameters isCancelled:isCancelledBlock];
- } else if (usesDrawRect) { // If we're using a draw method, this will operate on the currentContext.
- [self.class drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing];
- }
-
- [self __didDisplayNodeContentWithRenderingContext:currentContext image:&image drawParameters:drawParameters backgroundColor:backgroundColor borderWidth:borderWidth borderColor:borderColor];
-
- if (shouldCreateGraphicsContext) {
- CHECK_CANCELLED_AND_RETURN_NIL( ASGraphicsEndImageContext(); );
- image = ASGraphicsGetImageAndEndCurrentContext();
- }
-
- ASDN_DELAY_FOR_DISPLAY();
- return image;
- };
- }
-
- /**
- If we're profiling, wrap the display block with signpost start and end.
- Color the interval red if cancelled, green otherwise.
- */
-#if AS_KDEBUG_ENABLE
- __unsafe_unretained id ptrSelf = self;
- displayBlock = ^{
- ASSignpostStartCustom(ASSignpostLayerDisplay, ptrSelf, 0);
- id result = displayBlock();
- ASSignpostEndCustom(ASSignpostLayerDisplay, ptrSelf, 0, isCancelledBlock() ? ASSignpostColorRed : ASSignpostColorGreen);
- return result;
- };
-#endif
-
- return displayBlock;
-}
-
-- (void)__willDisplayNodeContentWithRenderingContext:(CGContextRef)context drawParameters:(id _Nullable)drawParameters
-{
- if (context) {
- __instanceLock__.lock();
- ASCornerRoundingType cornerRoundingType = _cornerRoundingType;
- CGFloat cornerRadius = _cornerRadius;
- ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext;
- __instanceLock__.unlock();
-
- if (cornerRoundingType == ASCornerRoundingTypePrecomposited && cornerRadius > 0.0) {
- ASDisplayNodeAssert(context == UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
- // TODO: This clip path should be removed if we are rasterizing.
- CGRect boundingBox = CGContextGetClipBoundingBox(context);
- [[UIBezierPath bezierPathWithRoundedRect:boundingBox cornerRadius:cornerRadius] addClip];
- }
-
- if (willDisplayNodeContentWithRenderingContext) {
- willDisplayNodeContentWithRenderingContext(context, drawParameters);
- }
- }
-
-}
-- (void)__didDisplayNodeContentWithRenderingContext:(CGContextRef)context image:(UIImage **)image drawParameters:(id _Nullable)drawParameters backgroundColor:(UIColor *)backgroundColor borderWidth:(CGFloat)borderWidth borderColor:(CGColorRef)borderColor
-{
- if (context == NULL && *image == NULL) {
- return;
- }
-
- __instanceLock__.lock();
- ASCornerRoundingType cornerRoundingType = _cornerRoundingType;
- CGFloat cornerRadius = _cornerRadius;
- CGFloat contentsScale = _contentsScaleForDisplay;
- ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext;
- __instanceLock__.unlock();
-
- if (context != NULL) {
- if (didDisplayNodeContentWithRenderingContext) {
- didDisplayNodeContentWithRenderingContext(context, drawParameters);
- }
- }
-
- if (cornerRoundingType == ASCornerRoundingTypePrecomposited && cornerRadius > 0.0f) {
- CGRect bounds = CGRectZero;
- if (context == NULL) {
- bounds = self.threadSafeBounds;
- bounds.size.width *= contentsScale;
- bounds.size.height *= contentsScale;
- CGFloat white = 0.0f, alpha = 0.0f;
- [backgroundColor getWhite:&white alpha:&alpha];
- ASGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale);
- [*image drawInRect:bounds];
- } else {
- bounds = CGContextGetClipBoundingBox(context);
- }
-
- ASDisplayNodeAssert(UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self);
-
- UIBezierPath *roundedHole = [UIBezierPath bezierPathWithRect:bounds];
- [roundedHole appendPath:[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius * contentsScale]];
- roundedHole.usesEvenOddFillRule = YES;
-
- UIBezierPath *roundedPath = nil;
- if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0
- CGFloat strokeThickness = borderWidth * contentsScale;
- CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f;
- roundedPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(bounds, strokeInset, strokeInset)
- cornerRadius:_cornerRadius * contentsScale];
- roundedPath.lineWidth = strokeThickness;
- [[UIColor colorWithCGColor:borderColor] setStroke];
- }
-
- // Punch out the corners by copying the backgroundColor over them.
- // This works for everything from clearColor to opaque colors.
- [backgroundColor setFill];
- [roundedHole fillWithBlendMode:kCGBlendModeCopy alpha:1.0f];
-
- [roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil.
-
- if (*image) {
- *image = ASGraphicsGetImageAndEndCurrentContext();
- }
- }
-}
-
-- (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously
-{
- ASDisplayNodeAssertMainThread();
-
- __instanceLock__.lock();
-
- if (_hierarchyState & ASHierarchyStateRasterized) {
- __instanceLock__.unlock();
- return;
- }
-
- CALayer *layer = _layer;
- BOOL rasterizesSubtree = _flags.rasterizesSubtree;
-
- __instanceLock__.unlock();
-
- // for async display, capture the current displaySentinel value to bail early when the job is executed if another is
- // enqueued
- // for sync display, do not support cancellation
-
- // FIXME: what about the degenerate case where we are calling setNeedsDisplay faster than the jobs are dequeuing
- // from the displayQueue? Need to not cancel early fails from displaySentinel changes.
- asdisplaynode_iscancelled_block_t isCancelledBlock = nil;
- if (asynchronously) {
- uint displaySentinelValue = ++_displaySentinel;
- __weak ASDisplayNode *weakSelf = self;
- isCancelledBlock = ^BOOL{
- __strong ASDisplayNode *self = weakSelf;
- return self == nil || (displaySentinelValue != self->_displaySentinel.load());
- };
- } else {
- isCancelledBlock = ^BOOL{
- return NO;
- };
- }
-
- // Set up displayBlock to call either display or draw on the delegate and return a UIImage contents
- asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:asynchronously isCancelledBlock:isCancelledBlock rasterizing:NO];
-
- if (!displayBlock) {
- return;
- }
-
- ASDisplayNodeAssert(layer, @"Expect _layer to be not nil");
-
- // This block is called back on the main thread after rendering at the completion of the current async transaction, or immediately if !asynchronously
- asyncdisplaykit_async_transaction_operation_completion_block_t completionBlock = ^(id value, BOOL canceled){
- ASDisplayNodeCAssertMainThread();
- if (!canceled && !isCancelledBlock()) {
- UIImage *image = (UIImage *)value;
- BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
- if (stretchable) {
- ASDisplayNodeSetResizableContents(layer, image);
- } else {
- layer.contentsScale = self.contentsScale;
- layer.contents = (id)image.CGImage;
- }
- [self didDisplayAsyncLayer:self.asyncLayer];
-
- if (rasterizesSubtree) {
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- [node didDisplayAsyncLayer:node.asyncLayer];
- });
- }
- }
- };
-
- // Call willDisplay immediately in either case
- [self willDisplayAsyncLayer:self.asyncLayer asynchronously:asynchronously];
-
- if (rasterizesSubtree) {
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- [node willDisplayAsyncLayer:node.asyncLayer asynchronously:asynchronously];
- });
- }
-
- if (asynchronously) {
- // Async rendering operations are contained by a transaction, which allows them to proceed and concurrently
- // while synchronizing the final application of the results to the layer's contents property (completionBlock).
-
- // First, look to see if we are expected to join a parent's transaction container.
- CALayer *containerLayer = layer.asyncdisplaykit_parentTransactionContainer ? : layer;
-
- // In the case that a transaction does not yet exist (such as for an individual node outside of a container),
- // this call will allocate the transaction and add it to _ASAsyncTransactionGroup.
- // It will automatically commit the transaction at the end of the runloop.
- _ASAsyncTransaction *transaction = containerLayer.asyncdisplaykit_asyncTransaction;
-
- // Adding this displayBlock operation to the transaction will start it IMMEDIATELY.
- // The only function of the transaction commit is to gate the calling of the completionBlock.
- [transaction addOperationWithBlock:displayBlock priority:self.drawingPriority queue:[_ASDisplayLayer displayQueue] completion:completionBlock];
- } else {
- UIImage *contents = (UIImage *)displayBlock();
- completionBlock(contents, NO);
- }
-}
-
-- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer
-{
- _displaySentinel.fetch_add(1);
-}
-
-- (ASDisplayNodeContextModifier)willDisplayNodeContentWithRenderingContext
-{
- MutexLocker l(__instanceLock__);
- return _willDisplayNodeContentWithRenderingContext;
-}
-
-- (ASDisplayNodeContextModifier)didDisplayNodeContentWithRenderingContext
-{
- MutexLocker l(__instanceLock__);
- return _didDisplayNodeContentWithRenderingContext;
-}
-
-- (void)setWillDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier
-{
- MutexLocker l(__instanceLock__);
- _willDisplayNodeContentWithRenderingContext = contextModifier;
-}
-
-- (void)setDidDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier;
-{
- MutexLocker l(__instanceLock__);
- _didDisplayNodeContentWithRenderingContext = contextModifier;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Convenience.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Convenience.mm
deleted file mode 100644
index 29d761be5a..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Convenience.mm
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// ASDisplayNode+Convenience.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-
-#import
-#import "ASResponderChainEnumerator.h"
-
-@implementation ASDisplayNode (Convenience)
-
-- (__kindof UIViewController *)closestViewController
-{
- ASDisplayNodeAssertMainThread();
-
- // Careful not to trigger node loading here.
- if (!self.nodeLoaded) {
- return nil;
- }
-
- // Get the closest view.
- UIView *view = ASFindClosestViewOfLayer(self.layer);
- // Travel up the responder chain to find a view controller.
- for (UIResponder *responder in [view asdk_responderChainEnumerator]) {
- UIViewController *vc = ASDynamicCast(responder, UIViewController);
- if (vc != nil) {
- return vc;
- }
- }
- return nil;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Deprecated.h b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Deprecated.h
deleted file mode 100644
index b05390ea47..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Deprecated.h
+++ /dev/null
@@ -1,142 +0,0 @@
-//
-// ASDisplayNode+Deprecated.h
-// Texture
-//
-// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
-// This source code is licensed under the BSD-style license found in the
-// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
-// grant of patent rights can be found in the PATENTS file in the same directory.
-//
-// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
-// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#pragma once
-
-#import
-
-@interface ASDisplayNode (Deprecated)
-
-/**
- * @abstract The name of this node, which will be displayed in `description`. The default value is nil.
- *
- * @deprecated Deprecated in version 2.0: Use .debugName instead. This value will display in
- * results of the -asciiArtString method (@see ASLayoutElementAsciiArtProtocol).
- */
-@property (nullable, nonatomic, copy) NSString *name ASDISPLAYNODE_DEPRECATED_MSG("Use .debugName instead.");
-
-/**
- * @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
- * a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
- * size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
- * image size.
- *
- * @return Try to create a CGSize for preferredFrameSize of this node from the width and height property of this node. It will return CGSizeZero if width and height dimensions are not of type ASDimensionUnitPoints.
- *
- * @deprecated Deprecated in version 2.0: Just calls through to set the height and width property of the node. Convert to use sizing properties instead: height, minHeight, maxHeight, width, minWidth, maxWidth.
- */
-@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED_MSG("Use .style.preferredSize instead OR set individual values with .style.height and .style.width.");
-
-/**
- * @abstract Asks the node to measure and return the size that best fits its subnodes.
- *
- * @param constrainedSize The maximum size the receiver should fit in.
- *
- * @return A new size that fits the receiver's subviews.
- *
- * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
- * constraint and the result.
- *
- * @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
- * -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
- * be expensive if result is not cached.
- *
- * @see measureWithSizeRange:
- * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
- *
- * @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout
- */
-- (CGSize)measure:(CGSize)constrainedSize/* ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout.")*/;
-
-ASLayoutElementStyleForwardingDeclaration
-
-/**
- * @abstract Called whenever the visiblity of the node changed.
- *
- * @discussion Subclasses may use this to monitor when they become visible.
- *
- * @deprecated @see didEnterVisibleState @see didExitVisibleState
- */
-- (void)visibilityDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead.");
-
-/**
- * @abstract Called whenever the visiblity of the node changed.
- *
- * @discussion Subclasses may use this to monitor when they become visible.
- *
- * @deprecated @see didEnterVisibleState @see didExitVisibleState
- */
-- (void)visibleStateDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead.");
-
-/**
- * @abstract Called whenever the the node has entered or exited the display state.
- *
- * @discussion Subclasses may use this to monitor when a node should be rendering its content.
- *
- * @note This method can be called from any thread and should therefore be thread safe.
- *
- * @deprecated @see didEnterDisplayState @see didExitDisplayState
- */
-- (void)displayStateDidChange:(BOOL)inDisplayState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterDisplayState / -didExitDisplayState instead.");
-
-/**
- * @abstract Called whenever the the node has entered or left the load state.
- *
- * @discussion Subclasses may use this to monitor data for a node should be loaded, either from a local or remote source.
- *
- * @note This method can be called from any thread and should therefore be thread safe.
- *
- * @deprecated @see didEnterPreloadState @see didExitPreloadState
- */
-- (void)loadStateDidChange:(BOOL)inLoadState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState / -didExitPreloadState instead.");
-
-/**
- * @abstract Cancels all performing layout transitions. Can be called on any thread.
- *
- * @deprecated Deprecated in version 2.0: Use cancelLayoutTransition
- */
-- (void)cancelLayoutTransitionsInProgress ASDISPLAYNODE_DEPRECATED_MSG("Use -cancelLayoutTransition instead.");
-
-/**
- * @abstract A boolean that shows whether the node automatically inserts and removes nodes based on the presence or
- * absence of the node and its subnodes is completely determined in its layoutSpecThatFits: method.
- *
- * @discussion If flag is YES the node no longer require addSubnode: or removeFromSupernode method calls. The presence
- * or absence of subnodes is completely determined in its layoutSpecThatFits: method.
- *
- * @deprecated Deprecated in version 2.0: Use automaticallyManagesSubnodes
- */
-@property (nonatomic, assign) BOOL usesImplicitHierarchyManagement ASDISPLAYNODE_DEPRECATED_MSG("Set .automaticallyManagesSubnodes instead.");
-
-/**
- * @abstract Indicates that the node should fetch any external data, such as images.
- *
- * @discussion Subclasses may override this method to be notified when they should begin to preload. Fetching
- * should be done asynchronously. The node is also responsible for managing the memory of any data.
- * The data may be remote and accessed via the network, but could also be a local database query.
- */
-- (void)fetchData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState instead.");
-
-/**
- * Provides an opportunity to clear any fetched data (e.g. remote / network or database-queried) on the current node.
- *
- * @discussion This will not clear data recursively for all subnodes. Either call -recursivelyClearPreloadedData or
- * selectively clear fetched data.
- */
-- (void)clearFetchedData ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didExitPreloadState instead.");
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm
deleted file mode 100644
index 32934a599c..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+Layout.mm
+++ /dev/null
@@ -1,1036 +0,0 @@
-//
-// ASDisplayNode+Layout.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-#import "ASDisplayNodeInternal.h"
-#import
-#import
-#import
-#import
-#import "ASLayoutElementStylePrivate.h"
-#import
-
-using AS::MutexLocker;
-
-#pragma mark - ASDisplayNode (ASLayoutElement)
-
-@implementation ASDisplayNode (ASLayoutElement)
-
-#pragma mark
-
-- (BOOL)implementsLayoutMethod
-{
- MutexLocker l(__instanceLock__);
- return (_methodOverrides & (ASDisplayNodeMethodOverrideLayoutSpecThatFits |
- ASDisplayNodeMethodOverrideCalcLayoutThatFits |
- ASDisplayNodeMethodOverrideCalcSizeThatFits)) != 0 || _layoutSpecBlock != nil;
-}
-
-
-- (ASLayoutElementStyle *)style
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_style];
-}
-
-- (ASLayoutElementStyle *)_locked_style
-{
- if (_style == nil) {
- _style = [[ASLayoutElementStyle alloc] init];
- }
- return _style;
-}
-
-- (ASLayoutElementType)layoutElementType
-{
- return ASLayoutElementTypeDisplayNode;
-}
-
-- (NSArray> *)sublayoutElements
-{
- return self.subnodes;
-}
-
-#pragma mark Measurement Pass
-
-- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
-{
- return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
-}
-
-- (CGSize)measure:(CGSize)constrainedSize {
- return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
-}
-
-- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
-{
- ASScopedLockSelfOrToRoot();
-
- // If one or multiple layout transitions are in flight it still can happen that layout information is requested
- // on other threads. As the pending and calculated layout to be updated in the layout transition in here just a
- // layout calculation wil be performed without side effect
- if ([self _isLayoutTransitionInvalid]) {
- return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];
- }
-
- ASLayout *layout = nil;
- NSUInteger version = _layoutVersion;
- if (_calculatedDisplayNodeLayout.isValid(constrainedSize, parentSize, version)) {
- ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout.layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout.layout should not be nil! %@", self);
- layout = _calculatedDisplayNodeLayout.layout;
- } else if (_pendingDisplayNodeLayout.isValid(constrainedSize, parentSize, version)) {
- ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout.layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout.layout should not be nil! %@", self);
- layout = _pendingDisplayNodeLayout.layout;
- } else {
- // Create a pending display node layout for the layout pass
- layout = [self calculateLayoutThatFits:constrainedSize
- restrictedToSize:self.style.size
- relativeToParentSize:parentSize];
- _pendingDisplayNodeLayout = ASDisplayNodeLayout(layout, constrainedSize, parentSize,version);
- ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self);
- }
-
- return layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
-}
-
-#pragma mark ASLayoutElementStyleExtensibility
-
-ASLayoutElementStyleExtensibilityForwarding
-
-#pragma mark ASPrimitiveTraitCollection
-
-- (ASPrimitiveTraitCollection)primitiveTraitCollection
-{
- return _primitiveTraitCollection.load();
-}
-
-- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
-{
- if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection.load()) == NO) {
- _primitiveTraitCollection = traitCollection;
- ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
-
- [self asyncTraitCollectionDidChange];
- }
-}
-
-- (ASTraitCollection *)asyncTraitCollection
-{
- return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection];
-}
-
-#pragma mark - ASLayoutElementAsciiArtProtocol
-
-- (NSString *)asciiArtString
-{
- return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
-}
-
-- (NSString *)asciiArtName
-{
- NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding];
- if (_debugName) {
- [result appendFormat:@" (%@)", _debugName];
- }
- return result;
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (ASLayout)
-
-@implementation ASDisplayNode (ASLayout)
-
-- (ASLayoutEngineType)layoutEngineType
-{
-#if YOGA
- MutexLocker l(__instanceLock__);
- YGNodeRef yogaNode = _style.yogaNode;
- BOOL hasYogaParent = (_yogaParent != nil);
- BOOL hasYogaChildren = (_yogaChildren.count > 0);
- if (yogaNode != NULL && (hasYogaParent || hasYogaChildren)) {
- return ASLayoutEngineTypeYoga;
- }
-#endif
-
- return ASLayoutEngineTypeLayoutSpec;
-}
-
-- (ASLayout *)calculatedLayout
-{
- MutexLocker l(__instanceLock__);
- return _calculatedDisplayNodeLayout.layout;
-}
-
-- (CGSize)calculatedSize
-{
- MutexLocker l(__instanceLock__);
- if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) {
- return _pendingDisplayNodeLayout.layout.size;
- }
- return _calculatedDisplayNodeLayout.layout.size;
-}
-
-- (ASSizeRange)constrainedSizeForCalculatedLayout
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_constrainedSizeForCalculatedLayout];
-}
-
-- (ASSizeRange)_locked_constrainedSizeForCalculatedLayout
-{
- ASAssertLocked(__instanceLock__);
- if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) {
- return _pendingDisplayNodeLayout.constrainedSize;
- }
- return _calculatedDisplayNodeLayout.constrainedSize;
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (ASLayoutElementStylability)
-
-@implementation ASDisplayNode (ASLayoutElementStylability)
-
-- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock
-{
- styleBlock(self.style);
- return self;
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (ASLayoutInternal)
-
-@implementation ASDisplayNode (ASLayoutInternal)
-
-/**
- * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid.
- *
- * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know
- * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen.
- */
-- (void)_u_setNeedsLayoutFromAbove
-{
- ASDisplayNodeAssertThreadAffinity(self);
- ASAssertUnlocked(__instanceLock__);
-
- // Mark the node for layout in the next layout pass
- [self setNeedsLayout];
-
- __instanceLock__.lock();
- // Escalate to the root; entire tree must allow adjustments so the layout fits the new child.
- // Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack)
- ASDisplayNode *supernode = _supernode;
- __instanceLock__.unlock();
-
- if (supernode) {
- // Threading model requires that we unlock before calling a method on our parent.
- [supernode _u_setNeedsLayoutFromAbove];
- } else {
- // Let the root node method know that the size was invalidated
- [self _rootNodeDidInvalidateSize];
- }
-}
-
-// TODO It would be easier to work with if we could `ASAssertUnlocked` here, but we
-// cannot due to locking to root in `_u_measureNodeWithBoundsIfNecessary`.
-- (void)_rootNodeDidInvalidateSize
-{
- ASDisplayNodeAssertThreadAffinity(self);
- __instanceLock__.lock();
-
- // We are the root node and need to re-flow the layout; at least one child needs a new size.
- CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size);
-
- // Figure out constrainedSize to use
- ASSizeRange constrainedSize = ASSizeRangeMake(boundsSizeForLayout);
- if (_pendingDisplayNodeLayout.layout != nil) {
- constrainedSize = _pendingDisplayNodeLayout.constrainedSize;
- } else if (_calculatedDisplayNodeLayout.layout != nil) {
- constrainedSize = _calculatedDisplayNodeLayout.constrainedSize;
- }
-
- __instanceLock__.unlock();
-
- // Perform a measurement pass to get the full tree layout, adapting to the child's new size.
- ASLayout *layout = [self layoutThatFits:constrainedSize];
-
- // Check if the returned layout has a different size than our current bounds.
- if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) {
- // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc).
- [self displayNodeDidInvalidateSizeNewSize:layout.size];
- }
-}
-
-// TODO
-// We should remove this logic, which is relatively new, and instead
-// rely on the parent / host of the root node to do this size change. That's always been the
-// expectation with other node containers like ASTableView, ASCollectionView, ASViewController, etc.
-// E.g. in ASCellNode the _interactionDelegate is a Table or Collection that will resize in this
-// case. By resizing without participating with the parent, we could get cases where our parent size
-// does not match, especially if there is a size constraint that is applied at that level.
-//
-// In general a node should never need to set its own size, instead allowing its parent to do so -
-// even in the root case. Anyhow this is a separate / pre-existing issue, but I think it could be
-// causing real issues in cases of resizing nodes.
-- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- // The default implementation of display node changes the size of itself to the new size
- CGRect oldBounds = self.bounds;
- CGSize oldSize = oldBounds.size;
- CGSize newSize = size;
-
- if (! CGSizeEqualToSize(oldSize, newSize)) {
- self.bounds = (CGRect){ oldBounds.origin, newSize };
-
- // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
- // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
- CGPoint anchorPoint = self.anchorPoint;
- CGPoint oldPosition = self.position;
- CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x;
- CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y;
- self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta);
- }
-}
-
-- (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds
-{
- // ASAssertUnlocked(__instanceLock__);
- ASScopedLockSelfOrToRoot();
-
- // Check if we are a subnode in a layout transition.
- // In this case no measurement is needed as it's part of the layout transition
- if ([self _locked_isLayoutTransitionInvalid]) {
- return;
- }
-
- CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
-
- // Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout
- // If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below).
- BOOL pendingLayoutIsPreferred = NO;
- if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) {
- NSUInteger calculatedVersion = _calculatedDisplayNodeLayout.version;
- NSUInteger pendingVersion = _pendingDisplayNodeLayout.version;
- if (pendingVersion > calculatedVersion) {
- pendingLayoutIsPreferred = YES; // Newer _pending
- } else if (pendingVersion == calculatedVersion
- && !ASSizeRangeEqualToSizeRange(_pendingDisplayNodeLayout.constrainedSize,
- _calculatedDisplayNodeLayout.constrainedSize)) {
- pendingLayoutIsPreferred = YES; // _pending with a different constrained size
- }
- }
- BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout.isValid(_layoutVersion)
- && (_calculatedDisplayNodeLayout.requestedLayoutFromAbove
- || CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout)));
- if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
- return;
- }
- // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
- [self cancelLayoutTransition];
-
- BOOL didCreateNewContext = NO;
- ASLayoutElementContext *context = ASLayoutElementGetCurrentContext();
- if (context == nil) {
- context = [[ASLayoutElementContext alloc] init];
- ASLayoutElementPushContext(context);
- didCreateNewContext = YES;
- }
-
- // Figure out previous and pending layouts for layout transition
- ASDisplayNodeLayout nextLayout = _pendingDisplayNodeLayout;
- #define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout.layout.size, boundsSizeForLayout)
-
- // nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied.
- // If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
- BOOL pendingLayoutApplicable = NO;
- if (nextLayout.layout == nil) {
- } else if (!nextLayout.isValid(_layoutVersion)) {
- } else if (layoutSizeDifferentFromBounds) {
- } else {
- pendingLayoutApplicable = YES;
- }
-
- if (!pendingLayoutApplicable) {
- // Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
- NSUInteger version = _layoutVersion;
- ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
- ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
- restrictedToSize:self.style.size
- relativeToParentSize:boundsSizeForLayout];
- nextLayout = ASDisplayNodeLayout(layout, constrainedSize, boundsSizeForLayout, version);
- // Now that the constrained size of pending layout might have been reused, the layout is useless
- // Release it and any orphaned subnodes it retains
- _pendingDisplayNodeLayout.layout = nil;
- }
-
- if (didCreateNewContext) {
- ASLayoutElementPopContext();
- }
-
- // If our new layout's desired size for self doesn't match current size, ask our parent to update it.
- // This can occur for either pre-calculated or newly-calculated layouts.
- if (nextLayout.requestedLayoutFromAbove == NO
- && CGSizeEqualToSize(boundsSizeForLayout, nextLayout.layout.size) == NO) {
- // The layout that we have specifies that this node (self) would like to be a different size
- // than it currently is. Because that size has been computed within the constrainedSize, we
- // expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
- // However, in some cases apps may manually interfere with this (setting a different bounds).
- // In this case, we need to detect that we've already asked to be resized to match this
- // particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
- nextLayout.requestedLayoutFromAbove = YES;
-
- {
- __instanceLock__.unlock();
- [self _u_setNeedsLayoutFromAbove];
- __instanceLock__.lock();
- }
-
- // Update the layout's version here because _u_setNeedsLayoutFromAbove calls __setNeedsLayout which in turn increases _layoutVersion
- // Failing to do this will cause the layout to be invalid immediately
- nextLayout.version = _layoutVersion;
- }
-
- // Prepare to transition to nextLayout
- ASDisplayNodeAssertNotNil(nextLayout.layout, @"nextLayout.layout should not be nil! %@", self);
- _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
- pendingLayout:nextLayout
- previousLayout:_calculatedDisplayNodeLayout];
-
- // If a parent is currently executing a layout transition, perform our layout application after it.
- if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) {
- // If no transition, apply our new layout immediately (common case).
- [self _completePendingLayoutTransition];
- }
-}
-
-- (ASSizeRange)_constrainedSizeForLayoutPass
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_constrainedSizeForLayoutPass];
-}
-
-- (ASSizeRange)_locked_constrainedSizeForLayoutPass
-{
- // TODO: The logic in -_u_setNeedsLayoutFromAbove seems correct and doesn't use this method.
- // logic seems correct. For what case does -this method need to do the CGSizeEqual checks?
- // IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK
-
- ASAssertLocked(__instanceLock__);
-
- CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size);
-
- // Checkout if constrained size of pending or calculated display node layout can be used
- if (_pendingDisplayNodeLayout.requestedLayoutFromAbove
- || CGSizeEqualToSize(_pendingDisplayNodeLayout.layout.size, boundsSizeForLayout)) {
- // We assume the size from the last returned layoutThatFits: layout was applied so use the pending display node
- // layout constrained size
- return _pendingDisplayNodeLayout.constrainedSize;
- } else if (_calculatedDisplayNodeLayout.layout != nil
- && (_calculatedDisplayNodeLayout.requestedLayoutFromAbove
- || CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout))) {
- // We assume the _calculatedDisplayNodeLayout is still valid and the frame is not different
- return _calculatedDisplayNodeLayout.constrainedSize;
- } else {
- // In this case neither the _pendingDisplayNodeLayout or the _calculatedDisplayNodeLayout constrained size can
- // be reused, so the current bounds is used. This is usual the case if a frame was set manually that differs to
- // the one returned from layoutThatFits: or layoutThatFits: was never called
- return ASSizeRangeMake(boundsSizeForLayout);
- }
-}
-
-- (void)_layoutSublayouts
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // ASAssertUnlocked(__instanceLock__);
-
- ASLayout *layout;
- {
- MutexLocker l(__instanceLock__);
- if (_calculatedDisplayNodeLayout.version < _layoutVersion) {
- return;
- }
- layout = _calculatedDisplayNodeLayout.layout;
- }
-
- for (ASDisplayNode *node in self.subnodes) {
- CGRect frame = [layout frameForElement:node];
- if (CGRectIsNull(frame)) {
- // There is no frame for this node in our layout.
- // This currently can happen if we get a CA layout pass
- // while waiting for the client to run animateLayoutTransition:
- } else {
- node.frame = frame;
- }
- }
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (ASAutomatic Subnode Management)
-
-@implementation ASDisplayNode (ASAutomaticSubnodeManagement)
-
-#pragma mark Automatically Manages Subnodes
-
-- (BOOL)automaticallyManagesSubnodes
-{
- MutexLocker l(__instanceLock__);
- return _automaticallyManagesSubnodes;
-}
-
-- (void)setAutomaticallyManagesSubnodes:(BOOL)automaticallyManagesSubnodes
-{
- MutexLocker l(__instanceLock__);
- _automaticallyManagesSubnodes = automaticallyManagesSubnodes;
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (ASLayoutTransition)
-
-@implementation ASDisplayNode (ASLayoutTransition)
-
-- (BOOL)_isLayoutTransitionInvalid
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_isLayoutTransitionInvalid];
-}
-
-- (BOOL)_locked_isLayoutTransitionInvalid
-{
- ASAssertLocked(__instanceLock__);
- if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
- ASLayoutElementContext *context = ASLayoutElementGetCurrentContext();
- if (context == nil || _pendingTransitionID != context.transitionID) {
- return YES;
- }
- }
- return NO;
-}
-
-/// Starts a new transition and returns the transition id
-- (int32_t)_startNewTransition
-{
- static std::atomic gNextTransitionID;
- int32_t newTransitionID = gNextTransitionID.fetch_add(1) + 1;
- _transitionID = newTransitionID;
- return newTransitionID;
-}
-
-/// Returns NO if there was no transition to cancel/finish.
-- (BOOL)_finishOrCancelTransition
-{
- int32_t oldValue = _transitionID.exchange(ASLayoutElementContextInvalidTransitionID);
- return oldValue != ASLayoutElementContextInvalidTransitionID;
-}
-
-#pragma mark Layout Transition
-
-- (void)transitionLayoutWithAnimation:(BOOL)animated
- shouldMeasureAsync:(BOOL)shouldMeasureAsync
- measurementCompletion:(void(^)())completion
-{
- ASDisplayNodeAssertMainThread();
- [self transitionLayoutWithSizeRange:[self _constrainedSizeForLayoutPass]
- animated:animated
- shouldMeasureAsync:shouldMeasureAsync
- measurementCompletion:completion];
-}
-
-- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize
- animated:(BOOL)animated
- shouldMeasureAsync:(BOOL)shouldMeasureAsync
- measurementCompletion:(void(^)())completion
-{
- ASDisplayNodeAssertMainThread();
-
- if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) {
- // Using CGSizeZero for the sizeRange can cause negative values in client layout code.
- // Most likely called transitionLayout: without providing a size, before first layout pass.
- return;
- }
-
- {
- MutexLocker l(__instanceLock__);
-
- // Check if we are a subnode in a layout transition.
- // In this case no measurement is needed as we're part of the layout transition.
- if ([self _locked_isLayoutTransitionInvalid]) {
- return;
- }
-
- if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
- ASDisplayNodeAssert(NO, @"Can't start a transition when one of the supernodes is performing one.");
- return;
- }
- }
-
- // Invalidate calculated layout because this method acts as an animated "setNeedsLayout" for nodes.
- // If the user has reconfigured the node and calls this, we should never return a stale layout
- // for subsequent calls to layoutThatFits: regardless of size range. We choose this method rather than
- // -setNeedsLayout because that method also triggers a CA layout invalidation, which isn't necessary at this time.
- // See https://github.com/TextureGroup/Texture/issues/463
- [self invalidateCalculatedLayout];
-
- // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling
- int32_t transitionID = [self _startNewTransition];
- // NOTE: This block captures self. It's cheaper than hitting the weak table.
- asdisplaynode_iscancelled_block_t isCancelled = ^{
- BOOL result = (_transitionID != transitionID);
- if (result) {
- }
- return result;
- };
-
- // Move all subnodes in layout pending state for this transition
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- ASDisplayNodeAssert(node->_transitionID == ASLayoutElementContextInvalidTransitionID, @"Can't start a transition when one of the subnodes is performing one.");
- node.hierarchyState |= ASHierarchyStateLayoutPending;
- node->_pendingTransitionID = transitionID;
- });
-
- // Transition block that executes the layout transition
- void (^transitionBlock)(void) = ^{
- if (isCancelled()) {
- return;
- }
-
- // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition
- NSUInteger newLayoutVersion = _layoutVersion;
- ASLayout *newLayout;
- {
- ASScopedLockSelfOrToRoot();
-
- ASLayoutElementContext *ctx = [[ASLayoutElementContext alloc] init];
- ctx.transitionID = transitionID;
- ASLayoutElementPushContext(ctx);
-
- BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO);
- self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x
- newLayout = [self calculateLayoutThatFits:constrainedSize
- restrictedToSize:self.style.size
- relativeToParentSize:constrainedSize.max];
- if (automaticallyManagesSubnodesDisabled) {
- self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x
- }
-
- ASLayoutElementPopContext();
- }
-
- if (isCancelled()) {
- return;
- }
-
- ASPerformBlockOnMainThread(^{
- if (isCancelled()) {
- return;
- }
- ASLayoutTransition *pendingLayoutTransition;
- _ASTransitionContext *pendingLayoutTransitionContext;
- {
- // Grab __instanceLock__ here to make sure this transition isn't invalidated
- // right after it passed the validation test and before it proceeds
- MutexLocker l(__instanceLock__);
-
- // Update calculated layout
- const auto previousLayout = _calculatedDisplayNodeLayout;
- const auto pendingLayout = ASDisplayNodeLayout(newLayout,
- constrainedSize,
- constrainedSize.max,
- newLayoutVersion);
- [self _locked_setCalculatedDisplayNodeLayout:pendingLayout];
-
- // Setup pending layout transition for animation
- _pendingLayoutTransition = pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
- pendingLayout:pendingLayout
- previousLayout:previousLayout];
- // Setup context for pending layout transition. we need to hold a strong reference to the context
- _pendingLayoutTransitionContext = pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated
- layoutDelegate:_pendingLayoutTransition
- completionDelegate:self];
- }
-
- // Apply complete layout transitions for all subnodes
- {
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- [node _completePendingLayoutTransition];
- node.hierarchyState &= (~ASHierarchyStateLayoutPending);
- });
- }
-
- // Measurement pass completion
- // Give the subclass a change to hook into before calling the completion block
- [self _layoutTransitionMeasurementDidFinish];
- if (completion) {
- completion();
- }
-
- // Apply the subnode insertion immediately to be able to animate the nodes
- [pendingLayoutTransition applySubnodeInsertionsAndMoves];
-
- // Kick off animating the layout transition
- {
- [self animateLayoutTransition:pendingLayoutTransitionContext];
- }
-
- // Mark transaction as finished
- [self _finishOrCancelTransition];
- });
- };
-
- // Start transition based on flag on current or background thread
- if (shouldMeasureAsync) {
- ASPerformBlockOnBackgroundThread(transitionBlock);
- } else {
- transitionBlock();
- }
-}
-
-- (void)cancelLayoutTransition
-{
- if ([self _finishOrCancelTransition]) {
- // Tell subnodes to exit layout pending state and clear related properties
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- node.hierarchyState &= (~ASHierarchyStateLayoutPending);
- });
- }
-}
-
-- (void)setDefaultLayoutTransitionDuration:(NSTimeInterval)defaultLayoutTransitionDuration
-{
- MutexLocker l(__instanceLock__);
- _defaultLayoutTransitionDuration = defaultLayoutTransitionDuration;
-}
-
-- (NSTimeInterval)defaultLayoutTransitionDuration
-{
- MutexLocker l(__instanceLock__);
- return _defaultLayoutTransitionDuration;
-}
-
-- (void)setDefaultLayoutTransitionDelay:(NSTimeInterval)defaultLayoutTransitionDelay
-{
- MutexLocker l(__instanceLock__);
- _defaultLayoutTransitionDelay = defaultLayoutTransitionDelay;
-}
-
-- (NSTimeInterval)defaultLayoutTransitionDelay
-{
- MutexLocker l(__instanceLock__);
- return _defaultLayoutTransitionDelay;
-}
-
-- (void)setDefaultLayoutTransitionOptions:(UIViewAnimationOptions)defaultLayoutTransitionOptions
-{
- MutexLocker l(__instanceLock__);
- _defaultLayoutTransitionOptions = defaultLayoutTransitionOptions;
-}
-
-- (UIViewAnimationOptions)defaultLayoutTransitionOptions
-{
- MutexLocker l(__instanceLock__);
- return _defaultLayoutTransitionOptions;
-}
-
-#pragma mark
-
-/*
- * Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out
- * animation is provided.
- */
-- (void)animateLayoutTransition:(id)context
-{
- if ([context isAnimated] == NO) {
- [self _layoutSublayouts];
- [context completeTransition:YES];
- return;
- }
-
- ASDisplayNode *node = self;
-
- NSAssert(node.isNodeLoaded == YES, @"Invalid node state");
-
- NSArray *removedSubnodes = [context removedSubnodes];
- NSMutableArray *insertedSubnodes = [[context insertedSubnodes] mutableCopy];
- const auto movedSubnodes = [[NSMutableArray alloc] init];
-
- const auto insertedSubnodeContexts = [[NSMutableArray<_ASAnimatedTransitionContext *> alloc] init];
- const auto removedSubnodeContexts = [[NSMutableArray<_ASAnimatedTransitionContext *> alloc] init];
-
- for (ASDisplayNode *subnode in [context subnodesForKey:ASTransitionContextToLayoutKey]) {
- if ([insertedSubnodes containsObject:subnode] == NO) {
- // This is an existing subnode, check if it is resized, moved or both
- CGRect fromFrame = [context initialFrameForNode:subnode];
- CGRect toFrame = [context finalFrameForNode:subnode];
- if (CGSizeEqualToSize(fromFrame.size, toFrame.size) == NO) {
- [insertedSubnodes addObject:subnode];
- }
- if (CGPointEqualToPoint(fromFrame.origin, toFrame.origin) == NO) {
- [movedSubnodes addObject:subnode];
- }
- }
- }
-
- // Create contexts for inserted and removed subnodes
- for (ASDisplayNode *insertedSubnode in insertedSubnodes) {
- [insertedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:insertedSubnode alpha:insertedSubnode.alpha]];
- }
- for (ASDisplayNode *removedSubnode in removedSubnodes) {
- [removedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:removedSubnode alpha:removedSubnode.alpha]];
- }
-
- // Fade out inserted subnodes
- for (ASDisplayNode *insertedSubnode in insertedSubnodes) {
- insertedSubnode.frame = [context finalFrameForNode:insertedSubnode];
- insertedSubnode.alpha = 0;
- }
-
- // Adjust groupOpacity for animation
- BOOL originAllowsGroupOpacity = node.allowsGroupOpacity;
- node.allowsGroupOpacity = YES;
-
- [UIView animateWithDuration:self.defaultLayoutTransitionDuration delay:self.defaultLayoutTransitionDelay options:self.defaultLayoutTransitionOptions animations:^{
- // Fade removed subnodes and views out
- for (ASDisplayNode *removedSubnode in removedSubnodes) {
- removedSubnode.alpha = 0;
- }
-
- // Fade inserted subnodes in
- for (_ASAnimatedTransitionContext *insertedSubnodeContext in insertedSubnodeContexts) {
- insertedSubnodeContext.node.alpha = insertedSubnodeContext.alpha;
- }
-
- // Update frame of self and moved subnodes
- CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size;
- CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size;
- BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO);
- if (isResized == YES) {
- CGPoint position = node.frame.origin;
- node.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height);
- }
- for (ASDisplayNode *movedSubnode in movedSubnodes) {
- movedSubnode.frame = [context finalFrameForNode:movedSubnode];
- }
- } completion:^(BOOL finished) {
- // Restore all removed subnode alpha values
- for (_ASAnimatedTransitionContext *removedSubnodeContext in removedSubnodeContexts) {
- removedSubnodeContext.node.alpha = removedSubnodeContext.alpha;
- }
-
- // Restore group opacity
- node.allowsGroupOpacity = originAllowsGroupOpacity;
-
- // Subnode removals are automatically performed
- [context completeTransition:finished];
- }];
-}
-
-/**
- * Hook for subclasses to clean up nodes after the transition happened. Furthermore this can be used from subclasses
- * to manually perform deletions.
- */
-- (void)didCompleteLayoutTransition:(id)context
-{
- ASDisplayNodeAssertMainThread();
-
- __instanceLock__.lock();
- ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition;
- __instanceLock__.unlock();
-
- [pendingLayoutTransition applySubnodeRemovals];
-}
-
-/**
- * Completes the pending layout transition immediately without going through the the Layout Transition Animation API
- */
-- (void)_completePendingLayoutTransition
-{
- __instanceLock__.lock();
- ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition;
- __instanceLock__.unlock();
-
- if (pendingLayoutTransition != nil) {
- [self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout];
- [self _completeLayoutTransition:pendingLayoutTransition];
- [self _pendingLayoutTransitionDidComplete];
- }
-}
-
-/**
- * Can be directly called to commit the given layout transition immediately to complete without calling through to the
- * Layout Transition Animation API
- */
-- (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition
-{
- // Layout transition is not supported for nodes that do not have automatic subnode management enabled
- if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) {
- return;
- }
-
- // Trampoline to the main thread if necessary
- if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) {
- // Committing the layout transition will result in subnode insertions and removals, both of which must be called without the lock held
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
- [layoutTransition commitTransition];
- } else {
- // Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded
- ASPerformBlockOnMainThread(^{
- [layoutTransition commitTransition];
- });
- }
-}
-
-- (void)_assertSubnodeState
-{
- // Verify that any orphaned nodes are removed.
- // This can occur in rare cases if main thread layout is flushed while a background layout is calculating.
-
- if (self.automaticallyManagesSubnodes == NO) {
- return;
- }
-
- MutexLocker l(__instanceLock__);
- NSArray *sublayouts = _calculatedDisplayNodeLayout.layout.sublayouts;
- unowned ASLayout *cSublayouts[sublayouts.count];
- [sublayouts getObjects:cSublayouts range:NSMakeRange(0, AS_ARRAY_SIZE(cSublayouts))];
-
- // Fast-path if we are in the correct state (likely).
- if (_subnodes.count == AS_ARRAY_SIZE(cSublayouts)) {
- NSUInteger i = 0;
- BOOL matches = YES;
- for (ASDisplayNode *subnode in _subnodes) {
- if (subnode != cSublayouts[i].layoutElement) {
- matches = NO;
- }
- i++;
- }
- if (matches) {
- return;
- }
- }
-
- NSArray *layoutNodes = ASArrayByFlatMapping(sublayouts, ASLayout *layout, (ASDisplayNode *)layout.layoutElement);
- NSIndexSet *insertions, *deletions;
- [_subnodes asdk_diffWithArray:layoutNodes insertions:&insertions deletions:&deletions];
- if (insertions.count > 0) {
- NSLog(@"Warning: node's layout includes subnode that has not been added: node = %@, subnodes = %@, subnodes in layout = %@", self, _subnodes, layoutNodes);
- }
-
- // Remove any nodes that are in the tree but should not be.
- // Go in reverse order so we don't shift our indexes.
- if (deletions) {
- for (NSUInteger i = deletions.lastIndex; i != NSNotFound; i = [deletions indexLessThanIndex:i]) {
- NSLog(@"Automatically removing orphaned subnode %@, from parent %@", _subnodes[i], self);
- [_subnodes[i] removeFromSupernode];
- }
- }
-}
-
-- (void)_pendingLayoutTransitionDidComplete
-{
- // This assertion introduces a breaking behavior for nodes that has ASM enabled but also manually manage some subnodes.
- // Let's gate it behind YOGA flag.
-#if YOGA
- [self _assertSubnodeState];
-#endif
-
- // Subclass hook
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
- [self calculatedLayoutDidChange];
-
- // Grab lock after calling out to subclass
- MutexLocker l(__instanceLock__);
-
- // We generate placeholders at -layoutThatFits: time so that a node is guaranteed to have a placeholder ready to go.
- // This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously.
- // First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync.
- if (_placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) {
-
- // Zero-sized nodes do not require a placeholder.
- CGSize layoutSize = _calculatedDisplayNodeLayout.layout.size;
- if (layoutSize.width * layoutSize.height <= 0.0) {
- return;
- }
-
- // If we've displayed our contents, we don't need a placeholder.
- // Contents is a thread-affined property and can't be read off main after loading.
- if (self.isNodeLoaded) {
- ASPerformBlockOnMainThread(^{
- if (self.contents == nil) {
- _placeholderImage = [self placeholderImage];
- }
- });
- } else {
- if (self.contents == nil) {
- _placeholderImage = [self placeholderImage];
- }
- }
- }
-
- // Cleanup pending layout transition
- _pendingLayoutTransition = nil;
-}
-
-- (void)_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout
-{
- MutexLocker l(__instanceLock__);
- [self _locked_setCalculatedDisplayNodeLayout:displayNodeLayout];
-}
-
-- (void)_locked_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout
-{
- ASAssertLocked(__instanceLock__);
- ASDisplayNodeAssertTrue(displayNodeLayout.layout.layoutElement == self);
- ASDisplayNodeAssertTrue(displayNodeLayout.layout.size.width >= 0.0);
- ASDisplayNodeAssertTrue(displayNodeLayout.layout.size.height >= 0.0);
-
- _calculatedDisplayNodeLayout = displayNodeLayout;
-}
-
-@end
-
-#pragma mark -
-#pragma mark - ASDisplayNode (YogaLayout)
-
-@implementation ASDisplayNode (YogaLayout)
-
-- (BOOL)locked_shouldLayoutFromYogaRoot {
-#if YOGA
- YGNodeRef yogaNode = _style.yogaNode;
- BOOL hasYogaParent = (_yogaParent != nil);
- BOOL hasYogaChildren = (_yogaChildren.count > 0);
- BOOL usesYoga = (yogaNode != NULL && (hasYogaParent || hasYogaChildren));
- if (usesYoga) {
- if ([self shouldHaveYogaMeasureFunc] == NO) {
- return YES;
- } else {
- return NO;
- }
- } else {
- return NO;
- }
-#else
- return NO;
-#endif
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+LayoutSpec.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+LayoutSpec.mm
deleted file mode 100644
index 132bc666f8..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+LayoutSpec.mm
+++ /dev/null
@@ -1,145 +0,0 @@
-//
-// ASDisplayNode+LayoutSpec.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-
-#import "_ASScopeTimer.h"
-#import "ASDisplayNodeInternal.h"
-#import
-#import
-#import "ASLayoutSpec+Subclasses.h"
-#import "ASLayoutSpecPrivate.h"
-#import
-
-
-@implementation ASDisplayNode (ASLayoutSpec)
-
-- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
-{
- // For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together.
- ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits),
- @"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:");
- AS::MutexLocker l(__instanceLock__);
- _layoutSpecBlock = layoutSpecBlock;
-}
-
-- (ASLayoutSpecBlock)layoutSpecBlock
-{
- AS::MutexLocker l(__instanceLock__);
- return _layoutSpecBlock;
-}
-
-- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize
-{
- AS::UniqueLock l(__instanceLock__);
-
- // Manual size calculation via calculateSizeThatFits:
- if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) {
- CGSize size = [self calculateSizeThatFits:constrainedSize.max];
- ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size));
- return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
- }
-
- // Size calcualtion with layout elements
- BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
- if (measureLayoutSpec) {
- _layoutSpecNumberOfPasses++;
- }
-
- // Get layout element from the node
- id layoutElement = [self _locked_layoutElementThatFits:constrainedSize];
-#if ASEnableVerboseLogging
- for (NSString *asciiLine in [[layoutElement asciiArtString] componentsSeparatedByString:@"\n"]) {
- as_log_verbose(ASLayoutLog(), "%@", asciiLine);
- }
-#endif
-
-
- // Certain properties are necessary to set on an element of type ASLayoutSpec
- if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
- ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement;
-
-#if AS_DEDUPE_LAYOUT_SPEC_TREE
- NSHashTable *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree];
- if (duplicateElements.count > 0) {
- ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements);
- // Use an empty layout spec to avoid crashes
- layoutSpec = [[ASLayoutSpec alloc] init];
- }
-#endif
-
- ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);
-
- layoutSpec.isMutable = NO;
- }
-
- // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection
- {
- AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
- ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection);
- }
-
- BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation;
- if (measureLayoutComputation) {
- _layoutComputationNumberOfPasses++;
- }
-
- // Layout element layout creation
- ASLayout *layout = ({
- AS::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation);
- [layoutElement layoutThatFits:constrainedSize];
- });
- ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout);
-
- // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct.
- BOOL isFinalLayoutElement = (layout.layoutElement != self);
- if (isFinalLayoutElement) {
- layout.position = CGPointZero;
- layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]];
- }
- ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout);
-
- // PR #1157: Reduces accuracy of _unflattenedLayout for debugging/Weaver
- if ([ASDisplayNode shouldStoreUnflattenedLayouts]) {
- _unflattenedLayout = layout;
- }
- layout = [layout filteredNodeLayoutTree];
-
- return layout;
-}
-
-- (id)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize
-{
- ASAssertLocked(__instanceLock__);
-
- BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
-
- if (_layoutSpecBlock != NULL) {
- return ({
- AS::MutexLocker l(__instanceLock__);
- AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
- _layoutSpecBlock(self, constrainedSize);
- });
- } else {
- return ({
- AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
- [self layoutSpecThatFits:constrainedSize];
- });
- }
-}
-
-- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
-{
- __ASDisplayNodeCheckForLayoutMethodOverrides;
-
- ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported.");
- return [[ASLayoutSpec alloc] init];
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode+UIViewBridge.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode+UIViewBridge.mm
deleted file mode 100644
index 084800a1ed..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode+UIViewBridge.mm
+++ /dev/null
@@ -1,1325 +0,0 @@
-//
-// ASDisplayNode+UIViewBridge.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import "_ASPendingState.h"
-#import
-#import "ASDisplayNodeInternal.h"
-#import
-#import
-#import "ASPendingStateController.h"
-
-/**
- * The following macros are conveniences to help in the common tasks related to the bridging that ASDisplayNode does to UIView and CALayer.
- * In general, a property can either be:
- * - Always sent to the layer or view's layer
- * use _getFromLayer / _setToLayer
- * - Bridged to the view if view-backed or the layer if layer-backed
- * use _getFromViewOrLayer / _setToViewOrLayer / _messageToViewOrLayer
- * - Only applicable if view-backed
- * use _setToViewOnly / _getFromViewOnly
- * - Has differing types on views and layers, or custom ASDisplayNode-specific behavior is desired
- * manually implement
- *
- * _bridge_prologue_write is defined to take the node's property lock. Add it at the beginning of any bridged property setters.
- * _bridge_prologue_read is defined to take the node's property lock and enforce thread affinity. Add it at the beginning of any bridged property getters.
- */
-
-#define DISPLAYNODE_USE_LOCKS 1
-
-#if DISPLAYNODE_USE_LOCKS
-#define _bridge_prologue_read AS::MutexLocker l(__instanceLock__); ASDisplayNodeAssertThreadAffinity(self)
-#define _bridge_prologue_write AS::MutexLocker l(__instanceLock__)
-#else
-#define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self)
-#define _bridge_prologue_write
-#endif
-
-/// Returns YES if the property set should be applied to view/layer immediately.
-/// Side Effect: Registers the node with the shared ASPendingStateController if
-/// the property cannot be immediately applied and the node does not already have pending changes.
-/// This function must be called with the node's lock already held (after _bridge_prologue_write).
-/// *warning* the lock should *not* be released until the pending state is updated if this method
-/// returns NO. Otherwise, the pending state can be scheduled and flushed *before* you get a chance
-/// to apply it.
-ASDISPLAYNODE_INLINE BOOL ASDisplayNodeShouldApplyBridgedWriteToView(ASDisplayNode *node) {
- BOOL loaded = _loaded(node);
- if (ASDisplayNodeThreadIsMain()) {
- return loaded;
- } else {
- if (loaded && !ASDisplayNodeGetPendingState(node).hasChanges) {
- [[ASPendingStateController sharedInstance] registerNode:node];
- }
- return NO;
- }
-};
-
-#define _getFromViewOrLayer(layerProperty, viewAndPendingViewStateProperty) _loaded(self) ? \
- (_view ? _view.viewAndPendingViewStateProperty : _layer.layerProperty )\
- : ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty
-
-#define _setToViewOrLayer(layerProperty, layerValueExpr, viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
- if (shouldApply) { (_view ? _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr) : _layer.layerProperty = (layerValueExpr)); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
-
-#define _setToViewOnly(viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
-if (shouldApply) { _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); }
-
-#define _getFromViewOnly(viewAndPendingViewStateProperty) _loaded(self) ? _view.viewAndPendingViewStateProperty : ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty
-
-#define _getFromLayer(layerProperty) _loaded(self) ? _layer.layerProperty : ASDisplayNodeGetPendingState(self).layerProperty
-
-#define _setToLayer(layerProperty, layerValueExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \
-if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNodeGetPendingState(self).layerProperty = (layerValueExpr); }
-
-/**
- * This category implements certain frequently-used properties and methods of UIView and CALayer so that ASDisplayNode clients can just call the view/layer methods on the node,
- * with minimal loss in performance. Unlike UIView and CALayer methods, these can be called from a non-main thread until the view or layer is created.
- * This allows text sizing in -calculateSizeThatFits: (essentially a simplified layout) to happen off the main thread
- * without any CALayer or UIView actually existing while still being able to set and read properties from ASDisplayNode instances.
- */
-@implementation ASDisplayNode (UIViewBridge)
-
-#if TARGET_OS_TV
-// Focus Engine
-- (BOOL)canBecomeFocused
-{
- return NO;
-}
-
-- (void)setNeedsFocusUpdate
-{
- ASDisplayNodeAssertMainThread();
- [_view setNeedsFocusUpdate];
-}
-
-- (void)updateFocusIfNeeded
-{
- ASDisplayNodeAssertMainThread();
- [_view updateFocusIfNeeded];
-}
-
-- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
-{
- return NO;
-}
-
-- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
-{
-
-}
-
-- (UIView *)preferredFocusedView
-{
- if (self.nodeLoaded) {
- return _view;
- }
- else {
- return nil;
- }
-}
-#endif
-
-- (BOOL)canBecomeFirstResponder
-{
- ASDisplayNodeAssertMainThread();
- return [self __canBecomeFirstResponder];
-}
-
-- (BOOL)canResignFirstResponder
-{
- ASDisplayNodeAssertMainThread();
- return [self __canResignFirstResponder];
-}
-
-- (BOOL)isFirstResponder
-{
- ASDisplayNodeAssertMainThread();
- return [self __isFirstResponder];
-}
-
-- (BOOL)becomeFirstResponder
-{
- ASDisplayNodeAssertMainThread();
- return [self __becomeFirstResponder];
-}
-
-- (BOOL)resignFirstResponder
-{
- ASDisplayNodeAssertMainThread();
- return [self __resignFirstResponder];
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
-{
- ASDisplayNodeAssertMainThread();
- return !self.layerBacked && [self.view canPerformAction:action withSender:sender];
-}
-
-- (CGFloat)alpha
-{
- _bridge_prologue_read;
- return _getFromViewOrLayer(opacity, alpha);
-}
-
-- (void)setAlpha:(CGFloat)newAlpha
-{
- _bridge_prologue_write;
- _setToViewOrLayer(opacity, newAlpha, alpha, newAlpha);
-}
-
-- (CGFloat)cornerRadius
-{
- AS::MutexLocker l(__instanceLock__);
- return _cornerRadius;
-}
-
-- (void)setCornerRadius:(CGFloat)newCornerRadius
-{
- [self updateCornerRoundingWithType:self.cornerRoundingType cornerRadius:newCornerRadius];
-}
-
-- (ASCornerRoundingType)cornerRoundingType
-{
- AS::MutexLocker l(__instanceLock__);
- return _cornerRoundingType;
-}
-
-- (void)setCornerRoundingType:(ASCornerRoundingType)newRoundingType
-{
- [self updateCornerRoundingWithType:newRoundingType cornerRadius:self.cornerRadius];
-}
-
-- (NSString *)contentsGravity
-{
- _bridge_prologue_read;
- return _getFromLayer(contentsGravity);
-}
-
-- (void)setContentsGravity:(NSString *)newContentsGravity
-{
- _bridge_prologue_write;
- _setToLayer(contentsGravity, newContentsGravity);
-}
-
-- (CGRect)contentsRect
-{
- _bridge_prologue_read;
- return _getFromLayer(contentsRect);
-}
-
-- (void)setContentsRect:(CGRect)newContentsRect
-{
- _bridge_prologue_write;
- _setToLayer(contentsRect, newContentsRect);
-}
-
-- (CGRect)contentsCenter
-{
- _bridge_prologue_read;
- return _getFromLayer(contentsCenter);
-}
-
-- (void)setContentsCenter:(CGRect)newContentsCenter
-{
- _bridge_prologue_write;
- _setToLayer(contentsCenter, newContentsCenter);
-}
-
-- (CGFloat)contentsScale
-{
- _bridge_prologue_read;
- return _getFromLayer(contentsScale);
-}
-
-- (void)setContentsScale:(CGFloat)newContentsScale
-{
- _bridge_prologue_write;
- _setToLayer(contentsScale, newContentsScale);
-}
-
-- (CGFloat)rasterizationScale
-{
- _bridge_prologue_read;
- return _getFromLayer(rasterizationScale);
-}
-
-- (void)setRasterizationScale:(CGFloat)newRasterizationScale
-{
- _bridge_prologue_write;
- _setToLayer(rasterizationScale, newRasterizationScale);
-}
-
-- (CGRect)bounds
-{
- _bridge_prologue_read;
- return _getFromViewOrLayer(bounds, bounds);
-}
-
-- (void)setBounds:(CGRect)newBounds
-{
- _bridge_prologue_write;
- _setToViewOrLayer(bounds, newBounds, bounds, newBounds);
- self.threadSafeBounds = newBounds;
-}
-
-- (CGRect)frame
-{
- _bridge_prologue_read;
-
- // Frame is only defined when transform is identity.
-//#if DEBUG
-// // Checking if the transform is identity is expensive, so disable when unnecessary. We have assertions on in Release, so DEBUG is the only way I know of.
-// ASDisplayNodeAssert(CATransform3DIsIdentity(self.transform), @"-[ASDisplayNode frame] - self.transform must be identity in order to use the frame property. (From Apple's UIView documentation: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.)");
-//#endif
-
- CGPoint position = self.position;
- CGRect bounds = self.bounds;
- CGPoint anchorPoint = self.anchorPoint;
- CGPoint origin = CGPointMake(position.x - bounds.size.width * anchorPoint.x,
- position.y - bounds.size.height * anchorPoint.y);
- return CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height);
-}
-
-- (void)setFrame:(CGRect)rect
-{
- BOOL setToView = NO;
- BOOL setToLayer = NO;
- CGRect newBounds = CGRectZero;
- CGPoint newPosition = CGPointZero;
- BOOL nodeLoaded = NO;
- BOOL isMainThread = ASDisplayNodeThreadIsMain();
- {
- _bridge_prologue_write;
-
- // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame:
- struct ASDisplayNodeFlags flags = _flags;
- BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), flags.layerBacked);
-
- nodeLoaded = _loaded(self);
- if (!specialPropertiesHandling) {
- BOOL canReadProperties = isMainThread || !nodeLoaded;
- if (canReadProperties) {
- // We don't have to set frame directly, and we can read current properties.
- // Compute a new bounds and position and set them on self.
- CALayer *layer = _layer;
- CGPoint origin = (nodeLoaded ? layer.bounds.origin : self.bounds.origin);
- CGPoint anchorPoint = (nodeLoaded ? layer.anchorPoint : self.anchorPoint);
-
- ASBoundsAndPositionForFrame(rect, origin, anchorPoint, &newBounds, &newPosition);
-
- if (ASIsCGRectValidForLayout(newBounds) == NO || ASIsCGPositionValidForLayout(newPosition) == NO) {
- ASDisplayNodeAssertNonFatal(NO, @"-[ASDisplayNode setFrame:] - The new frame (%@) is invalid and unsafe to be set.", NSStringFromCGRect(rect));
- return;
- }
-
- if (nodeLoaded) {
- setToLayer = YES;
- } else {
- self.bounds = newBounds;
- self.position = newPosition;
- }
- } else {
- // We don't have to set frame directly, but we can't read properties.
- // Store the frame in our pending state, and it'll get decomposed into
- // bounds and position when the pending state is applied.
- _ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
- if (nodeLoaded && !pendingState.hasChanges) {
- [[ASPendingStateController sharedInstance] registerNode:self];
- }
- pendingState.frame = rect;
- }
- } else {
- if (nodeLoaded && isMainThread) {
- // We do have to set frame directly, and we're on main thread with a loaded node.
- // Just set the frame on the view.
- // NOTE: Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform.
- setToView = YES;
- } else {
- // We do have to set frame directly, but either the node isn't loaded or we're on a non-main thread.
- // Set the frame on the pending state, and it'll call setFrame: when applied.
- _ASPendingState *pendingState = ASDisplayNodeGetPendingState(self);
- if (nodeLoaded && !pendingState.hasChanges) {
- [[ASPendingStateController sharedInstance] registerNode:self];
- }
- pendingState.frame = rect;
- }
- }
- }
-
- if (setToView) {
- ASDisplayNodeAssertTrue(nodeLoaded && isMainThread);
- _view.frame = rect;
- } else if (setToLayer) {
- ASDisplayNodeAssertTrue(nodeLoaded && isMainThread);
- _layer.bounds = newBounds;
- _layer.position = newPosition;
- }
-}
-
-- (void)setNeedsDisplay
-{
- BOOL isRasterized = NO;
- BOOL shouldApply = NO;
- id viewOrLayer = nil;
- {
- _bridge_prologue_write;
- isRasterized = _hierarchyState & ASHierarchyStateRasterized;
- shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
- viewOrLayer = _view ?: _layer;
-
- if (isRasterized == NO && shouldApply == NO) {
- // We can't release the lock before applying to pending state, or it may be flushed before it can be applied.
- [ASDisplayNodeGetPendingState(self) setNeedsDisplay];
- }
- }
-
- if (isRasterized) {
- ASPerformBlockOnMainThread(^{
- // The below operation must be performed on the main thread to ensure against an extremely rare deadlock, where a parent node
- // begins materializing the view / layer hierarchy (locking itself or a descendant) while this node walks up
- // the tree and requires locking that node to access .rasterizesSubtree.
- // For this reason, this method should be avoided when possible. Use _hierarchyState & ASHierarchyStateRasterized.
- ASDisplayNodeAssertMainThread();
- ASDisplayNode *rasterizedContainerNode = self.supernode;
- while (rasterizedContainerNode) {
- if (rasterizedContainerNode.rasterizesSubtree) {
- break;
- }
- rasterizedContainerNode = rasterizedContainerNode.supernode;
- }
- [rasterizedContainerNode setNeedsDisplay];
- });
- } else {
- if (shouldApply) {
- // If not rasterized, and the node is loaded (meaning we certainly have a view or layer), send a
- // message to the view/layer first. This is because __setNeedsDisplay calls as scheduleNodeForDisplay,
- // which may call -displayIfNeeded. We want to ensure the needsDisplay flag is set now, and then cleared.
- [viewOrLayer setNeedsDisplay];
- }
- [self __setNeedsDisplay];
- }
-}
-
-- (void)setNeedsLayout
-{
- BOOL shouldApply = NO;
- BOOL loaded = NO;
- id viewOrLayer = nil;
- {
- _bridge_prologue_write;
- shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
- loaded = _loaded(self);
- viewOrLayer = _view ?: _layer;
- if (shouldApply == NO && loaded) {
- // The node is loaded but we're not on main.
- // We will call [self __setNeedsLayout] when we apply the pending state.
- // We need to call it on main if the node is loaded to support automatic subnode management.
- // We can't release the lock before applying to pending state, or it may be flushed before it can be applied.
- [ASDisplayNodeGetPendingState(self) setNeedsLayout];
- }
- }
-
- if (shouldApply) {
- // The node is loaded and we're on main.
- // Quite the opposite of setNeedsDisplay, we must call __setNeedsLayout before messaging
- // the view or layer to ensure that measurement and implicitly added subnodes have been handled.
- [self __setNeedsLayout];
- [viewOrLayer setNeedsLayout];
- } else if (loaded == NO) {
- // The node is not loaded and we're not on main.
- [self __setNeedsLayout];
- }
-}
-
-- (void)layoutIfNeeded
-{
- BOOL shouldApply = NO;
- BOOL loaded = NO;
- id viewOrLayer = nil;
- {
- _bridge_prologue_write;
- shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
- loaded = _loaded(self);
- viewOrLayer = _view ?: _layer;
- if (shouldApply == NO && loaded) {
- // The node is loaded but we're not on main.
- // We will call layoutIfNeeded on the view or layer when we apply the pending state. __layout will in turn be called on us (see -[_ASDisplayLayer layoutSublayers]).
- // We need to call it on main if the node is loaded to support automatic subnode management.
- // We can't release the lock before applying to pending state, or it may be flushed before it can be applied.
- [ASDisplayNodeGetPendingState(self) layoutIfNeeded];
- }
- }
-
- if (shouldApply) {
- // The node is loaded and we're on main.
- // Message the view or layer which in turn will call __layout on us (see -[_ASDisplayLayer layoutSublayers]).
- [viewOrLayer layoutIfNeeded];
- } else if (loaded == NO) {
- // The node is not loaded and we're not on main.
- [self __layout];
- }
-}
-
-- (BOOL)isOpaque
-{
- _bridge_prologue_read;
- return _getFromLayer(opaque);
-}
-
-- (void)setOpaque:(BOOL)newOpaque
-{
- _bridge_prologue_write;
-
- BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
-
- if (shouldApply) {
- BOOL oldOpaque = _layer.opaque;
- _layer.opaque = newOpaque;
- if (oldOpaque != newOpaque) {
- [self setNeedsDisplay];
- }
- } else {
- // NOTE: If we're in the background, we cannot read the current value of self.opaque (if loaded).
- // When the pending state is applied to the view on main, we will call `setNeedsDisplay` if
- // the new opaque value doesn't match the one on the layer.
- ASDisplayNodeGetPendingState(self).opaque = newOpaque;
- }
-}
-
-- (BOOL)isUserInteractionEnabled
-{
- _bridge_prologue_read;
- if (_flags.layerBacked) return NO;
- return _getFromViewOnly(userInteractionEnabled);
-}
-
-- (void)setUserInteractionEnabled:(BOOL)enabled
-{
- _bridge_prologue_write;
- _setToViewOnly(userInteractionEnabled, enabled);
-}
-#if TARGET_OS_IOS
-- (BOOL)isExclusiveTouch
-{
- _bridge_prologue_read;
- return _getFromViewOnly(exclusiveTouch);
-}
-
-- (void)setExclusiveTouch:(BOOL)exclusiveTouch
-{
- _bridge_prologue_write;
- _setToViewOnly(exclusiveTouch, exclusiveTouch);
-}
-#endif
-- (BOOL)clipsToBounds
-{
- _bridge_prologue_read;
- return _getFromViewOrLayer(masksToBounds, clipsToBounds);
-}
-
-- (void)setClipsToBounds:(BOOL)clips
-{
- _bridge_prologue_write;
- _setToViewOrLayer(masksToBounds, clips, clipsToBounds, clips);
-}
-
-- (CGPoint)anchorPoint
-{
- _bridge_prologue_read;
- return _getFromLayer(anchorPoint);
-}
-
-- (void)setAnchorPoint:(CGPoint)newAnchorPoint
-{
- _bridge_prologue_write;
- _setToLayer(anchorPoint, newAnchorPoint);
-}
-
-- (CGPoint)position
-{
- _bridge_prologue_read;
- return _getFromLayer(position);
-}
-
-- (void)setPosition:(CGPoint)newPosition
-{
- _bridge_prologue_write;
- _setToLayer(position, newPosition);
-}
-
-- (CGFloat)zPosition
-{
- _bridge_prologue_read;
- return _getFromLayer(zPosition);
-}
-
-- (void)setZPosition:(CGFloat)newPosition
-{
- _bridge_prologue_write;
- _setToLayer(zPosition, newPosition);
-}
-
-- (CATransform3D)transform
-{
- _bridge_prologue_read;
- return _getFromLayer(transform);
-}
-
-- (void)setTransform:(CATransform3D)newTransform
-{
- _bridge_prologue_write;
- _setToLayer(transform, newTransform);
-}
-
-- (CATransform3D)subnodeTransform
-{
- _bridge_prologue_read;
- return _getFromLayer(sublayerTransform);
-}
-
-- (void)setSubnodeTransform:(CATransform3D)newSubnodeTransform
-{
- _bridge_prologue_write;
- _setToLayer(sublayerTransform, newSubnodeTransform);
-}
-
-- (id)contents
-{
- _bridge_prologue_read;
- return _getFromLayer(contents);
-}
-
-- (void)setContents:(id)newContents
-{
- _bridge_prologue_write;
- _setToLayer(contents, newContents);
-}
-
-- (BOOL)isHidden
-{
- _bridge_prologue_read;
- return _getFromViewOrLayer(hidden, hidden);
-}
-
-- (void)setHidden:(BOOL)flag
-{
- _bridge_prologue_write;
- _setToViewOrLayer(hidden, flag, hidden, flag);
-}
-
-- (BOOL)needsDisplayOnBoundsChange
-{
- _bridge_prologue_read;
- return _getFromLayer(needsDisplayOnBoundsChange);
-}
-
-- (void)setNeedsDisplayOnBoundsChange:(BOOL)flag
-{
- _bridge_prologue_write;
- _setToLayer(needsDisplayOnBoundsChange, flag);
-}
-
-- (BOOL)autoresizesSubviews
-{
- _bridge_prologue_read;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- return _getFromViewOnly(autoresizesSubviews);
-}
-
-- (void)setAutoresizesSubviews:(BOOL)flag
-{
- _bridge_prologue_write;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- _setToViewOnly(autoresizesSubviews, flag);
-}
-
-- (UIViewAutoresizing)autoresizingMask
-{
- _bridge_prologue_read;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- return _getFromViewOnly(autoresizingMask);
-}
-
-- (void)setAutoresizingMask:(UIViewAutoresizing)mask
-{
- _bridge_prologue_write;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- _setToViewOnly(autoresizingMask, mask);
-}
-
-- (UIViewContentMode)contentMode
-{
- _bridge_prologue_read;
- if (_loaded(self)) {
- if (_flags.layerBacked) {
- return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
- } else {
- return _view.contentMode;
- }
- } else {
- return ASDisplayNodeGetPendingState(self).contentMode;
- }
-}
-
-- (void)setContentMode:(UIViewContentMode)contentMode
-{
- _bridge_prologue_write;
- BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
- if (shouldApply) {
- if (_flags.layerBacked) {
- _layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
- } else {
- _view.contentMode = contentMode;
- }
- } else {
- ASDisplayNodeGetPendingState(self).contentMode = contentMode;
- }
-}
-
-- (void)setAccessibilityCustomActions:(NSArray *)accessibilityCustomActions
-{
- _bridge_prologue_write;
- BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
- if (shouldApply) {
- if (_flags.layerBacked) {
- } else {
- _view.accessibilityCustomActions = accessibilityCustomActions;
- }
- } else {
- ASDisplayNodeGetPendingState(self).accessibilityCustomActions = accessibilityCustomActions;
- }
-}
-
-- (UIColor *)backgroundColor
-{
- _bridge_prologue_read;
- return [UIColor colorWithCGColor:_getFromLayer(backgroundColor)];
-}
-
-- (void)setBackgroundColor:(UIColor *)newBackgroundColor
-{
- _bridge_prologue_write;
-
- CGColorRef newBackgroundCGColor = CGColorRetain([newBackgroundColor CGColor]);
- BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
-
- if (shouldApply) {
- CGColorRef oldBackgroundCGColor = CGColorRetain(_layer.backgroundColor);
-
- BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked);
- if (specialPropertiesHandling) {
- _view.backgroundColor = newBackgroundColor;
- } else {
- _layer.backgroundColor = newBackgroundCGColor;
- }
-
- if (!CGColorEqualToColor(oldBackgroundCGColor, newBackgroundCGColor)) {
- [self setNeedsDisplay];
- }
-
- CGColorRelease(oldBackgroundCGColor);
- } else {
- // NOTE: If we're in the background, we cannot read the current value of bgcolor (if loaded).
- // When the pending state is applied to the view on main, we will call `setNeedsDisplay` if
- // the new background color doesn't match the one on the layer.
- ASDisplayNodeGetPendingState(self).backgroundColor = newBackgroundCGColor;
- }
- CGColorRelease(newBackgroundCGColor);
-}
-
-- (UIColor *)tintColor
-{
- _bridge_prologue_read;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- return _getFromViewOnly(tintColor);
-}
-
-- (void)setTintColor:(UIColor *)color
-{
- _bridge_prologue_write;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- _setToViewOnly(tintColor, color);
-}
-
-- (void)tintColorDidChange
-{
- // ignore this, allow subclasses to be notified
-}
-
-- (CGColorRef)shadowColor
-{
- _bridge_prologue_read;
- return _getFromLayer(shadowColor);
-}
-
-- (void)setShadowColor:(CGColorRef)colorValue
-{
- _bridge_prologue_write;
- _setToLayer(shadowColor, colorValue);
-}
-
-- (CGFloat)shadowOpacity
-{
- _bridge_prologue_read;
- return _getFromLayer(shadowOpacity);
-}
-
-- (void)setShadowOpacity:(CGFloat)opacity
-{
- _bridge_prologue_write;
- _setToLayer(shadowOpacity, opacity);
-}
-
-- (CGSize)shadowOffset
-{
- _bridge_prologue_read;
- return _getFromLayer(shadowOffset);
-}
-
-- (void)setShadowOffset:(CGSize)offset
-{
- _bridge_prologue_write;
- _setToLayer(shadowOffset, offset);
-}
-
-- (CGFloat)shadowRadius
-{
- _bridge_prologue_read;
- return _getFromLayer(shadowRadius);
-}
-
-- (void)setShadowRadius:(CGFloat)radius
-{
- _bridge_prologue_write;
- _setToLayer(shadowRadius, radius);
-}
-
-- (CGFloat)borderWidth
-{
- _bridge_prologue_read;
- return _getFromLayer(borderWidth);
-}
-
-- (void)setBorderWidth:(CGFloat)width
-{
- _bridge_prologue_write;
- _setToLayer(borderWidth, width);
-}
-
-- (CGColorRef)borderColor
-{
- _bridge_prologue_read;
- return _getFromLayer(borderColor);
-}
-
-- (void)setBorderColor:(CGColorRef)colorValue
-{
- _bridge_prologue_write;
- _setToLayer(borderColor, colorValue);
-}
-
-- (BOOL)allowsGroupOpacity
-{
- _bridge_prologue_read;
- return _getFromLayer(allowsGroupOpacity);
-}
-
-- (void)setAllowsGroupOpacity:(BOOL)allowsGroupOpacity
-{
- _bridge_prologue_write;
- _setToLayer(allowsGroupOpacity, allowsGroupOpacity);
-}
-
-- (BOOL)allowsEdgeAntialiasing
-{
- _bridge_prologue_read;
- return _getFromLayer(allowsEdgeAntialiasing);
-}
-
-- (void)setAllowsEdgeAntialiasing:(BOOL)allowsEdgeAntialiasing
-{
- _bridge_prologue_write;
- _setToLayer(allowsEdgeAntialiasing, allowsEdgeAntialiasing);
-}
-
-- (unsigned int)edgeAntialiasingMask
-{
- _bridge_prologue_read;
- return _getFromLayer(edgeAntialiasingMask);
-}
-
-- (void)setEdgeAntialiasingMask:(unsigned int)edgeAntialiasingMask
-{
- _bridge_prologue_write;
- _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask);
-}
-
-- (UISemanticContentAttribute)semanticContentAttribute
-{
- _bridge_prologue_read;
- return _getFromViewOnly(semanticContentAttribute);
-}
-
-- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute
-{
- _bridge_prologue_write;
- _setToViewOnly(semanticContentAttribute, semanticContentAttribute);
-#if YOGA
- [self semanticContentAttributeDidChange:semanticContentAttribute];
-#endif
-}
-
-- (UIEdgeInsets)layoutMargins
-{
- _bridge_prologue_read;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- UIEdgeInsets margins = _getFromViewOnly(layoutMargins);
-
- if (!AS_AT_LEAST_IOS11 && self.insetsLayoutMarginsFromSafeArea) {
- UIEdgeInsets safeArea = self.safeAreaInsets;
- margins = ASConcatInsets(margins, safeArea);
- }
-
- return margins;
-}
-
-- (void)setLayoutMargins:(UIEdgeInsets)layoutMargins
-{
- _bridge_prologue_write;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- _setToViewOnly(layoutMargins, layoutMargins);
-}
-
-- (BOOL)preservesSuperviewLayoutMargins
-{
- _bridge_prologue_read;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- return _getFromViewOnly(preservesSuperviewLayoutMargins);
-}
-
-- (void)setPreservesSuperviewLayoutMargins:(BOOL)preservesSuperviewLayoutMargins
-{
- _bridge_prologue_write;
- ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes.");
- _setToViewOnly(preservesSuperviewLayoutMargins, preservesSuperviewLayoutMargins);
-}
-
-- (void)layoutMarginsDidChange
-{
- ASDisplayNodeAssertMainThread();
-
- if (self.automaticallyRelayoutOnLayoutMarginsChanges) {
- [self setNeedsLayout];
- }
-}
-
-- (UIEdgeInsets)safeAreaInsets
-{
- _bridge_prologue_read;
-
- if (AS_AVAILABLE_IOS(11.0)) {
- if (!_flags.layerBacked && _loaded(self)) {
- return self.view.safeAreaInsets;
- }
- }
- return _fallbackSafeAreaInsets;
-}
-
-- (BOOL)insetsLayoutMarginsFromSafeArea
-{
- _bridge_prologue_read;
-
- return [self _locked_insetsLayoutMarginsFromSafeArea];
-}
-
-- (void)setInsetsLayoutMarginsFromSafeArea:(BOOL)insetsLayoutMarginsFromSafeArea
-{
- ASDisplayNodeAssertThreadAffinity(self);
- BOOL shouldNotifyAboutUpdate;
- {
- _bridge_prologue_write;
-
- _fallbackInsetsLayoutMarginsFromSafeArea = insetsLayoutMarginsFromSafeArea;
-
- if (AS_AVAILABLE_IOS(11.0)) {
- if (!_flags.layerBacked) {
- _setToViewOnly(insetsLayoutMarginsFromSafeArea, insetsLayoutMarginsFromSafeArea);
- }
- }
-
- shouldNotifyAboutUpdate = _loaded(self) && (!AS_AT_LEAST_IOS11 || _flags.layerBacked);
- }
-
- if (shouldNotifyAboutUpdate) {
- [self layoutMarginsDidChange];
- }
-}
-
-- (void)safeAreaInsetsDidChange
-{
- ASDisplayNodeAssertMainThread();
-
- if (self.automaticallyRelayoutOnSafeAreaChanges) {
- [self setNeedsLayout];
- }
-
- [self _fallbackUpdateSafeAreaOnChildren];
-}
-
-@end
-
-@implementation ASDisplayNode (InternalPropertyBridge)
-
-- (CGFloat)layerCornerRadius
-{
- _bridge_prologue_read;
- return _getFromLayer(cornerRadius);
-}
-
-- (void)setLayerCornerRadius:(CGFloat)newLayerCornerRadius
-{
- _bridge_prologue_write;
- _setToLayer(cornerRadius, newLayerCornerRadius);
-}
-
-- (BOOL)_locked_insetsLayoutMarginsFromSafeArea
-{
- ASAssertLocked(__instanceLock__);
- if (AS_AVAILABLE_IOS(11.0)) {
- if (!_flags.layerBacked) {
- return _getFromViewOnly(insetsLayoutMarginsFromSafeArea);
- }
- }
- return _fallbackInsetsLayoutMarginsFromSafeArea;
-}
-
-@end
-
-#pragma mark - UIViewBridgeAccessibility
-
-// ASDK supports accessibility for view or layer backed nodes. To be able to provide support for layer backed
-// nodes, properties for all of the UIAccessibility protocol defined properties need to be provided an held in sync
-// between node and view
-
-// Helper function with following logic:
-// - If the node is not loaded yet use the property from the pending state
-// - In case the node is loaded
-// - Check if the node has a view and get the value from the view if loaded or from the pending state
-// - If view is not available, e.g. the node is layer backed return the property value
-#define _getAccessibilityFromViewOrProperty(nodeProperty, viewAndPendingViewStateProperty) _loaded(self) ? \
-(_view ? _view.viewAndPendingViewStateProperty : nodeProperty )\
-: ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty
-
-// Helper function to set property values on pending state or view and property if loaded
-#define _setAccessibilityToViewAndProperty(nodeProperty, nodeValueExpr, viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) \
-nodeProperty = nodeValueExpr; _setToViewOnly(viewAndPendingViewStateProperty, viewAndPendingViewStateExpr)
-
-@implementation ASDisplayNode (UIViewBridgeAccessibility)
-
-// iOS 11 only properties. Add this to silence "unimplemented selector" warnings
-// in old SDKs. If the caller doesn't respect our API_AVAILABLE attributes, then they
-// get an appropriate "unrecognized selector" runtime error.
-#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_11_0
-@dynamic accessibilityAttributedLabel, accessibilityAttributedHint, accessibilityAttributedValue;
-#endif
-
-- (BOOL)isAccessibilityElement
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_isAccessibilityElement, isAccessibilityElement);
-}
-
-- (void)setIsAccessibilityElement:(BOOL)isAccessibilityElement
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_isAccessibilityElement, isAccessibilityElement, isAccessibilityElement, isAccessibilityElement);
-}
-
-- (NSString *)accessibilityLabel
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityLabel, accessibilityLabel);
-}
-
-- (void)setAccessibilityLabel:(NSString *)accessibilityLabel
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityLabel, accessibilityLabel, accessibilityLabel);
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
- if (AS_AVAILABLE_IOS_TVOS(11, 11)) {
- NSAttributedString *accessibilityAttributedLabel = accessibilityLabel ? [[NSAttributedString alloc] initWithString:accessibilityLabel] : nil;
- _setAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel);
- }
-#endif
-}
-
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
-- (NSAttributedString *)accessibilityAttributedLabel
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel);
-}
-
-- (void)setAccessibilityAttributedLabel:(NSAttributedString *)accessibilityAttributedLabel
-{
- _bridge_prologue_write;
- { _setAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel); }
- { _setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityAttributedLabel.string, accessibilityLabel, accessibilityAttributedLabel.string); }
-}
-#endif
-
-- (NSString *)accessibilityHint
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityHint, accessibilityHint);
-}
-
-- (void)setAccessibilityHint:(NSString *)accessibilityHint
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityHint, accessibilityHint, accessibilityHint, accessibilityHint);
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
- if (AS_AVAILABLE_IOS_TVOS(11, 11)) {
- NSAttributedString *accessibilityAttributedHint = accessibilityHint ? [[NSAttributedString alloc] initWithString:accessibilityHint] : nil;
- _setAccessibilityToViewAndProperty(_accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint);
- }
-#endif
-}
-
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
-- (NSAttributedString *)accessibilityAttributedHint
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityAttributedHint, accessibilityAttributedHint);
-}
-
-- (void)setAccessibilityAttributedHint:(NSAttributedString *)accessibilityAttributedHint
-{
- _bridge_prologue_write;
- { _setAccessibilityToViewAndProperty(_accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint); }
-
- { _setAccessibilityToViewAndProperty(_accessibilityHint, accessibilityAttributedHint.string, accessibilityHint, accessibilityAttributedHint.string); }
-}
-#endif
-
-- (NSString *)accessibilityValue
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityValue, accessibilityValue);
-}
-
-- (void)setAccessibilityValue:(NSString *)accessibilityValue
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityValue, accessibilityValue, accessibilityValue, accessibilityValue);
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
- if (AS_AVAILABLE_IOS_TVOS(11, 11)) {
- NSAttributedString *accessibilityAttributedValue = accessibilityValue ? [[NSAttributedString alloc] initWithString:accessibilityValue] : nil;
- _setAccessibilityToViewAndProperty(_accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue);
- }
-#endif
-}
-
-#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0
-- (NSAttributedString *)accessibilityAttributedValue
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityAttributedValue, accessibilityAttributedValue);
-}
-
-- (void)setAccessibilityAttributedValue:(NSAttributedString *)accessibilityAttributedValue
-{
- _bridge_prologue_write;
- { _setAccessibilityToViewAndProperty(_accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue); }
- { _setAccessibilityToViewAndProperty(_accessibilityValue, accessibilityAttributedValue.string, accessibilityValue, accessibilityAttributedValue.string); }
-}
-#endif
-
-- (UIAccessibilityTraits)accessibilityTraits
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityTraits, accessibilityTraits);
-}
-
-- (void)setAccessibilityTraits:(UIAccessibilityTraits)accessibilityTraits
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityTraits, accessibilityTraits, accessibilityTraits, accessibilityTraits);
-}
-
-- (CGRect)accessibilityFrame
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityFrame, accessibilityFrame);
-}
-
-- (void)setAccessibilityFrame:(CGRect)accessibilityFrame
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityFrame, accessibilityFrame, accessibilityFrame, accessibilityFrame);
-}
-
-- (NSString *)accessibilityLanguage
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityLanguage, accessibilityLanguage);
-}
-
-- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityLanguage, accessibilityLanguage, accessibilityLanguage, accessibilityLanguage);
-}
-
-- (BOOL)accessibilityElementsHidden
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityElementsHidden, accessibilityElementsHidden);
-}
-
-- (void)setAccessibilityElementsHidden:(BOOL)accessibilityElementsHidden
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden);
-}
-
-- (BOOL)accessibilityViewIsModal
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityViewIsModal, accessibilityViewIsModal);
-}
-
-- (void)setAccessibilityViewIsModal:(BOOL)accessibilityViewIsModal
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityViewIsModal, accessibilityViewIsModal, accessibilityViewIsModal, accessibilityViewIsModal);
-}
-
-- (BOOL)shouldGroupAccessibilityChildren
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren);
-}
-
-- (void)setShouldGroupAccessibilityChildren:(BOOL)shouldGroupAccessibilityChildren
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren);
-}
-
-- (NSString *)accessibilityIdentifier
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityIdentifier, accessibilityIdentifier);
-}
-
-- (void)setAccessibilityIdentifier:(NSString *)accessibilityIdentifier
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityIdentifier, accessibilityIdentifier, accessibilityIdentifier, accessibilityIdentifier);
-}
-
-- (void)setAccessibilityNavigationStyle:(UIAccessibilityNavigationStyle)accessibilityNavigationStyle
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityNavigationStyle, accessibilityNavigationStyle, accessibilityNavigationStyle, accessibilityNavigationStyle);
-}
-
-- (UIAccessibilityNavigationStyle)accessibilityNavigationStyle
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityNavigationStyle, accessibilityNavigationStyle);
-}
-
-#if TARGET_OS_TV
-- (void)setAccessibilityHeaderElements:(NSArray *)accessibilityHeaderElements
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityHeaderElements, accessibilityHeaderElements, accessibilityHeaderElements, accessibilityHeaderElements);
-}
-
-- (NSArray *)accessibilityHeaderElements
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityHeaderElements, accessibilityHeaderElements);
-}
-#endif
-
-- (void)setAccessibilityActivationPoint:(CGPoint)accessibilityActivationPoint
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityActivationPoint, accessibilityActivationPoint, accessibilityActivationPoint, accessibilityActivationPoint);
-}
-
-- (CGPoint)accessibilityActivationPoint
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityActivationPoint, accessibilityActivationPoint);
-}
-
-- (void)setAccessibilityPath:(UIBezierPath *)accessibilityPath
-{
- _bridge_prologue_write;
- _setAccessibilityToViewAndProperty(_accessibilityPath, accessibilityPath, accessibilityPath, accessibilityPath);
-}
-
-- (UIBezierPath *)accessibilityPath
-{
- _bridge_prologue_read;
- return _getAccessibilityFromViewOrProperty(_accessibilityPath, accessibilityPath);
-}
-
-- (NSInteger)accessibilityElementCount
-{
- _bridge_prologue_read;
- return _getFromViewOnly(accessibilityElementCount);
-}
-
-@end
-
-
-#pragma mark - ASAsyncTransactionContainer
-
-@implementation ASDisplayNode (ASAsyncTransactionContainer)
-
-- (BOOL)asyncdisplaykit_isAsyncTransactionContainer
-{
- _bridge_prologue_read;
- return _getFromViewOrLayer(asyncdisplaykit_isAsyncTransactionContainer, asyncdisplaykit_isAsyncTransactionContainer);
-}
-
-- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)asyncTransactionContainer
-{
- _bridge_prologue_write;
- _setToViewOrLayer(asyncdisplaykit_asyncTransactionContainer, asyncTransactionContainer, asyncdisplaykit_asyncTransactionContainer, asyncTransactionContainer);
-}
-
-- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState
-{
- ASDisplayNodeAssertMainThread();
- return [_layer asyncdisplaykit_asyncTransactionContainerState];
-}
-
-- (void)asyncdisplaykit_cancelAsyncTransactions
-{
- ASDisplayNodeAssertMainThread();
- [_layer asyncdisplaykit_cancelAsyncTransactions];
-}
-
-- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction
-{
- _layer.asyncdisplaykit_currentAsyncTransaction = transaction;
-}
-
-- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction
-{
- return _layer.asyncdisplaykit_currentAsyncTransaction;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNode.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNode.mm
deleted file mode 100644
index e8607e273c..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNode.mm
+++ /dev/null
@@ -1,3803 +0,0 @@
-//
-// ASDisplayNode.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import "ASDisplayNodeInternal.h"
-
-#import
-#import
-#import
-#import "ASLayoutSpec+Subclasses.h"
-
-#import
-#include
-
-#import
-#import "_ASAsyncTransactionContainer+Private.h"
-#import
-#import
-#import
-#import "_ASPendingState.h"
-#import "_ASScopeTimer.h"
-#import
-#import
-#import
-#import
-#import
-#import
-#import
-#import
-#import "ASLayoutElementStylePrivate.h"
-#import
-#import "ASLayoutSpecPrivate.h"
-#import
-#import
-#import "ASSignpost.h"
-#import
-#import "ASWeakProxy.h"
-#import "ASResponderChainEnumerator.h"
-
-// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
-#if TIME_DISPLAYNODE_OPS
- #define TIME_SCOPED(outVar) AS::ScopeTimer t(outVar)
-#else
- #define TIME_SCOPED(outVar)
-#endif
-// This is trying to merge non-rangeManaged with rangeManaged, so both range-managed and standalone nodes wait before firing their exit-visibility handlers, as UIViewController transitions now do rehosting at both start & end of animation.
-// Enable this will mitigate interface updating state when coalescing disabled.
-// TODO(wsdwsd0829): Rework enabling code to ensure that interface state behavior is not altered when ASCATransactionQueue is disabled.
-#define ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR 0
-
-using AS::MutexLocker;
-
-static ASDisplayNodeNonFatalErrorBlock _nonFatalErrorBlock = nil;
-
-// Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from an informal delegate to a protocol.
-// We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10
-@protocol CALayerDelegate;
-
-@interface ASDisplayNode ()
-/**
- * See ASDisplayNodeInternal.h for ivars
- */
-
-@end
-
-@implementation ASDisplayNode
-
-@dynamic layoutElementType;
-
-@synthesize threadSafeBounds = _threadSafeBounds;
-
-static std::atomic_bool suppressesInvalidCollectionUpdateExceptions = ATOMIC_VAR_INIT(NO);
-static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO);
-
-BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
-{
- return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector);
-}
-
-// For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties
-// like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only.
-BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked)
-{
- return isSynchronous && !isLayerBacked;
-}
-
-_ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node)
-{
- ASLockScope(node);
- _ASPendingState *result = node->_pendingViewState;
- if (result == nil) {
- result = [[_ASPendingState alloc] init];
- node->_pendingViewState = result;
- }
- return result;
-}
-
-void StubImplementationWithNoArgs(id receiver) {}
-void StubImplementationWithSizeRange(id receiver, ASSizeRange sr) {}
-void StubImplementationWithTwoInterfaceStates(id receiver, ASInterfaceState s0, ASInterfaceState s1) {}
-
-/**
- * Returns ASDisplayNodeFlags for the given class/instance. instance MAY BE NIL.
- *
- * @param c the class, required
- * @param instance the instance, which may be nil. (If so, the class is inspected instead)
- * @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads
- * +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this
- * method on each -init and passing the instance value. While this may seem like an unlikely scenario,
- * it turns our our own internal tests use a dynamic class, so it's worth capturing this edge case.
- *
- * @return ASDisplayNode flags.
- */
-static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance)
-{
- ASDisplayNodeCAssertNotNil(c, @"class is required");
-
- struct ASDisplayNodeFlags flags = {0};
-
- flags.isInHierarchy = NO;
- flags.displaysAsynchronously = YES;
- flags.shouldAnimateSizeChanges = YES;
- flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
- flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
- if (instance) {
- flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
- } else {
- flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
- }
-
-
- return flags;
-}
-
-/**
- * Returns ASDisplayNodeMethodOverrides for the given class
- *
- * @param c the class, required.
- *
- * @return ASDisplayNodeMethodOverrides.
- */
-static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
-{
- ASDisplayNodeCAssertNotNil(c, @"class is required");
-
- ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone;
-
- // Handling touches
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) {
- overrides |= ASDisplayNodeMethodOverrideTouchesBegan;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesMoved:withEvent:))) {
- overrides |= ASDisplayNodeMethodOverrideTouchesMoved;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesCancelled:withEvent:))) {
- overrides |= ASDisplayNodeMethodOverrideTouchesCancelled;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesEnded:withEvent:))) {
- overrides |= ASDisplayNodeMethodOverrideTouchesEnded;
- }
-
- // Responder chain
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(canBecomeFirstResponder))) {
- overrides |= ASDisplayNodeMethodOverrideCanBecomeFirstResponder;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(becomeFirstResponder))) {
- overrides |= ASDisplayNodeMethodOverrideBecomeFirstResponder;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(canResignFirstResponder))) {
- overrides |= ASDisplayNodeMethodOverrideCanResignFirstResponder;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(resignFirstResponder))) {
- overrides |= ASDisplayNodeMethodOverrideResignFirstResponder;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(isFirstResponder))) {
- overrides |= ASDisplayNodeMethodOverrideIsFirstResponder;
- }
-
- // Layout related methods
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) {
- overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:)) ||
- ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:
- restrictedToSize:
- relativeToParentSize:))) {
- overrides |= ASDisplayNodeMethodOverrideCalcLayoutThatFits;
- }
- if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) {
- overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits;
- }
-
- return overrides;
-}
-
-+ (void)initialize
-{
-#if ASDISPLAYNODE_ASSERTIONS_ENABLED
- if (self != [ASDisplayNode class]) {
-
- // Subclasses should never override these. Use unused to prevent warnings
- __unused NSString *classString = NSStringFromClass(self);
-
- //ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString);
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString);
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString);
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString);
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString);
- } else {
- // Check if subnodes where modified during the creation of the layout
- __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) {
- NSArray *oldSubnodes = _self.subnodes;
- ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange);
- NSArray *subnodes = _self.subnodes;
- ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
- for (NSInteger i = 0; i < oldSubnodes.count; i++) {
- ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
- }
- return layoutElement;
- });
- }
-#endif
-
- // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
- // when each instance is constructed. These values don't change for each class, so there is significant performance benefit
- // in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here.
- // Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path
- // (recalculating for each instance) to make sure we are always correct.
-
- BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:));
- BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:));
- struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil);
- ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self);
-
- __unused Class initializeSelf = self;
-
- IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) {
- ASDisplayNodeAssert(node.class == initializeSelf, @"Node class %@ does not have a matching _staticInitialize method; check to ensure [super initialize] is called within any custom +initialize implementations! Overridden methods will not be called unless they are also implemented by superclass %@", node.class, initializeSelf);
- node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags;
- node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides;
- });
-
- class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@");
-
- // Add stub implementations for global methods that the client didn't
- // implement in a category. We do this instead of hard-coding empty
- // implementations to avoid a linker warning when it merges categories.
- // Note: addMethod will not do anything if a method already exists.
- if (self == ASDisplayNode.class) {
- IMP noArgsImp = (IMP)StubImplementationWithNoArgs;
- class_addMethod(self, @selector(baseDidInit), noArgsImp, "v@:");
- class_addMethod(self, @selector(baseWillDealloc), noArgsImp, "v@:");
- class_addMethod(self, @selector(didLoad), noArgsImp, "v@:");
- class_addMethod(self, @selector(layoutDidFinish), noArgsImp, "v@:");
- class_addMethod(self, @selector(didEnterPreloadState), noArgsImp, "v@:");
- class_addMethod(self, @selector(didExitPreloadState), noArgsImp, "v@:");
- class_addMethod(self, @selector(didEnterDisplayState), noArgsImp, "v@:");
- class_addMethod(self, @selector(didExitDisplayState), noArgsImp, "v@:");
- class_addMethod(self, @selector(didEnterVisibleState), noArgsImp, "v@:");
- class_addMethod(self, @selector(didExitVisibleState), noArgsImp, "v@:");
- class_addMethod(self, @selector(hierarchyDisplayDidFinish), noArgsImp, "v@:");
- class_addMethod(self, @selector(asyncTraitCollectionDidChange), noArgsImp, "v@:");
- class_addMethod(self, @selector(calculatedLayoutDidChange), noArgsImp, "v@:");
-
- auto type0 = "v@:" + std::string(@encode(ASSizeRange));
- class_addMethod(self, @selector(willCalculateLayout:), (IMP)StubImplementationWithSizeRange, type0.c_str());
-
- auto interfaceStateType = std::string(@encode(ASInterfaceState));
- auto type1 = "v@:" + interfaceStateType + interfaceStateType;
- class_addMethod(self, @selector(interfaceStateDidChange:fromState:), (IMP)StubImplementationWithTwoInterfaceStates, type1.c_str());
- }
-}
-
-#if !AS_INITIALIZE_FRAMEWORK_MANUALLY
-+ (void)load
-{
- ASInitializeFrameworkMainThread();
-}
-#endif
-
-+ (Class)viewClass
-{
- return [_ASDisplayView class];
-}
-
-+ (Class)layerClass
-{
- return [_ASDisplayLayer class];
-}
-
-#pragma mark - Lifecycle
-
-- (void)_staticInitialize
-{
- ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden");
-}
-
-- (void)_initializeInstance
-{
- [self _staticInitialize];
-
-#if ASEVENTLOG_ENABLE
- _eventLog = [[ASEventLog alloc] initWithObject:self];
-#endif
-
- _viewClass = [self.class viewClass];
- _layerClass = [self.class layerClass];
- BOOL isSynchronous = ![_viewClass isSubclassOfClass:[_ASDisplayView class]]
- || ![_layerClass isSubclassOfClass:[_ASDisplayLayer class]];
- setFlag(Synchronous, isSynchronous);
-
-
- _contentsScaleForDisplay = ASScreenScale();
- _drawingPriority = ASDefaultTransactionPriority;
-
- _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
-
- _layoutVersion = 1;
-
- _defaultLayoutTransitionDuration = 0.2;
- _defaultLayoutTransitionDelay = 0.0;
- _defaultLayoutTransitionOptions = UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionNone;
-
- _flags.canClearContentsOfLayer = YES;
- _flags.canCallSetNeedsDisplayOfLayer = YES;
-
- _fallbackSafeAreaInsets = UIEdgeInsetsZero;
- _fallbackInsetsLayoutMarginsFromSafeArea = YES;
- _isViewControllerRoot = NO;
-
- _automaticallyRelayoutOnSafeAreaChanges = NO;
- _automaticallyRelayoutOnLayoutMarginsChanges = NO;
-
- [self baseDidInit];
- ASDisplayNodeLogEvent(self, @"init");
-}
-
-- (instancetype)init
-{
- if (!(self = [super init]))
- return nil;
-
- [self _initializeInstance];
-
- return self;
-}
-
-- (instancetype)initWithViewClass:(Class)viewClass
-{
- if (!(self = [self init]))
- return nil;
-
- ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView");
-
- _viewClass = viewClass;
- setFlag(Synchronous, ![viewClass isSubclassOfClass:[_ASDisplayView class]]);
-
- return self;
-}
-
-- (instancetype)initWithLayerClass:(Class)layerClass
-{
- if (!(self = [self init])) {
- return nil;
- }
-
- ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer");
-
- _layerClass = layerClass;
- _flags.layerBacked = YES;
- setFlag(Synchronous, ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]);
-
- return self;
-}
-
-- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
-{
- return [self initWithViewBlock:viewBlock didLoadBlock:nil];
-}
-
-- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
-{
- if (!(self = [self init])) {
- return nil;
- }
-
- [self setViewBlock:viewBlock];
- if (didLoadBlock != nil) {
- [self onDidLoad:didLoadBlock];
- }
-
- return self;
-}
-
-- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
-{
- return [self initWithLayerBlock:layerBlock didLoadBlock:nil];
-}
-
-- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
-{
- if (!(self = [self init])) {
- return nil;
- }
-
- [self setLayerBlock:layerBlock];
- if (didLoadBlock != nil) {
- [self onDidLoad:didLoadBlock];
- }
-
- return self;
-}
-
-ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
-
-- (void)setViewBlock:(ASDisplayNodeViewBlock)viewBlock
-{
- ASDisplayNodeAssertFalse(self.nodeLoaded);
- ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView");
-
- _viewBlock = viewBlock;
- setFlag(Synchronous, YES);
-}
-
-- (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
-{
- ASDisplayNodeAssertFalse(self.nodeLoaded);
- ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer");
-
- _layerBlock = layerBlock;
- _flags.layerBacked = YES;
- setFlag(Synchronous, YES);
-}
-
-- (ASDisplayNodeMethodOverrides)methodOverrides
-{
- return _methodOverrides;
-}
-
-- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
-{
- AS::UniqueLock l(__instanceLock__);
-
- if ([self _locked_isNodeLoaded]) {
- ASDisplayNodeAssertThreadAffinity(self);
- l.unlock();
- body(self);
- return;
- } else if (_onDidLoadBlocks == nil) {
- _onDidLoadBlocks = [NSMutableArray arrayWithObject:body];
- } else {
- [_onDidLoadBlocks addObject:body];
- }
-}
-
-- (void)dealloc
-{
- _flags.isDeallocating = YES;
- [self baseWillDealloc];
-
- // Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes.
- //ASDisplayNodeAssert(checkFlag(Synchronous) || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self);
-
- self.asyncLayer.asyncDelegate = nil;
- _view.asyncdisplaykit_node = nil;
- _layer.asyncdisplaykit_node = nil;
-
- // Remove any subnodes so they lose their connection to the now deallocated parent. This can happen
- // because subnodes do not retain their supernode, but subnodes can legitimately remain alive if another
- // thing outside the view hierarchy system (e.g. async display, controller code, etc). keeps a retained
- // reference to subnodes.
-
- for (ASDisplayNode *subnode in _subnodes)
- [subnode _setSupernode:nil];
-
- [self scheduleIvarsForMainThreadDeallocation];
-
- // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
- [self _setSupernode:nil];
-}
-
-#pragma mark - Loading
-
-- (BOOL)_locked_shouldLoadViewOrLayer
-{
- ASAssertLocked(__instanceLock__);
- return !_flags.isDeallocating && !(_hierarchyState & ASHierarchyStateRasterized);
-}
-
-- (UIView *)_locked_viewToLoad
-{
- ASAssertLocked(__instanceLock__);
-
- UIView *view = nil;
- if (_viewBlock) {
- view = _viewBlock();
- ASDisplayNodeAssertNotNil(view, @"View block returned nil");
- ASDisplayNodeAssert(![view isKindOfClass:[_ASDisplayView class]], @"View block should return a synchronously displayed view");
- _viewBlock = nil;
- _viewClass = [view class];
- } else {
- view = [[_viewClass alloc] init];
- }
-
- // Special handling of wrapping UIKit components
- if (checkFlag(Synchronous)) {
- [self checkResponderCompatibility];
-
- // UIImageView layers. More details on the flags
- if ([_viewClass isSubclassOfClass:[UIImageView class]]) {
- _flags.canClearContentsOfLayer = NO;
- _flags.canCallSetNeedsDisplayOfLayer = NO;
- }
-
- // UIActivityIndicator
- if ([_viewClass isSubclassOfClass:[UIActivityIndicatorView class]]
- || [_viewClass isSubclassOfClass:[UIVisualEffectView class]]) {
- self.opaque = NO;
- }
-
- // CAEAGLLayer
- if([[view.layer class] isSubclassOfClass:[CAEAGLLayer class]]){
- _flags.canClearContentsOfLayer = NO;
- }
- }
-
- return view;
-}
-
-- (CALayer *)_locked_layerToLoad
-{
- ASAssertLocked(__instanceLock__);
- ASDisplayNodeAssert(_flags.layerBacked, @"_layerToLoad is only for layer-backed nodes");
-
- CALayer *layer = nil;
- if (_layerBlock) {
- layer = _layerBlock();
- ASDisplayNodeAssertNotNil(layer, @"Layer block returned nil");
- ASDisplayNodeAssert(![layer isKindOfClass:[_ASDisplayLayer class]], @"Layer block should return a synchronously displayed layer");
- _layerBlock = nil;
- _layerClass = [layer class];
- } else {
- layer = [[_layerClass alloc] init];
- }
-
- return layer;
-}
-
-- (void)_locked_loadViewOrLayer
-{
- ASAssertLocked(__instanceLock__);
-
- if (_flags.layerBacked) {
- TIME_SCOPED(_debugTimeToCreateView);
- _layer = [self _locked_layerToLoad];
- static int ASLayerDelegateAssociationKey;
-
- /**
- * CALayer's .delegate property is documented to be weak, but the implementation is actually assign.
- * Because our layer may survive longer than the node (e.g. if someone else retains it, or if the node
- * begins deallocation on a background thread and it waiting for the -dealloc call to reach main), the only
- * way to avoid a dangling pointer is to use a weak proxy.
- */
- ASWeakProxy *instance = [ASWeakProxy weakProxyWithTarget:self];
- _layer.delegate = (id)instance;
- objc_setAssociatedObject(_layer, &ASLayerDelegateAssociationKey, instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- } else {
- TIME_SCOPED(_debugTimeToCreateView);
- _view = [self _locked_viewToLoad];
- _view.asyncdisplaykit_node = self;
- _layer = _view.layer;
- }
- _layer.asyncdisplaykit_node = self;
-
- self._locked_asyncLayer.asyncDelegate = self;
-}
-
-- (void)_didLoad
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- ASDisplayNodeLogEvent(self, @"didLoad");
- TIME_SCOPED(_debugTimeForDidLoad);
-
- [self didLoad];
-
- __instanceLock__.lock();
- const auto onDidLoadBlocks = ASTransferStrong(_onDidLoadBlocks);
- __instanceLock__.unlock();
-
- for (ASDisplayNodeDidLoadBlock block in onDidLoadBlocks) {
- block(self);
- }
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del nodeDidLoad];
- }];
-}
-
-- (BOOL)isNodeLoaded
-{
- if (ASDisplayNodeThreadIsMain()) {
- // Because the view and layer can only be created and destroyed on Main, that is also the only thread
- // where the state of this property can change. As an optimization, we can avoid locking.
- return _loaded(self);
- } else {
- MutexLocker l(__instanceLock__);
- return [self _locked_isNodeLoaded];
- }
-}
-
-- (BOOL)_locked_isNodeLoaded
-{
- ASAssertLocked(__instanceLock__);
- return _loaded(self);
-}
-
-#pragma mark - Misc Setter / Getter
-
-- (UIView *)view
-{
- AS::UniqueLock l(__instanceLock__);
-
- ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes");
- BOOL isLayerBacked = _flags.layerBacked;
- if (isLayerBacked) {
- return nil;
- }
-
- if (_view != nil) {
- return _view;
- }
-
- if (![self _locked_shouldLoadViewOrLayer]) {
- return nil;
- }
-
- // Loading a view needs to happen on the main thread
- ASDisplayNodeAssertMainThread();
- [self _locked_loadViewOrLayer];
-
- // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
- // but automatic subnode management would require us to modify the node tree
- // in the background on a loaded node, which isn't currently supported.
- if (_pendingViewState.hasSetNeedsLayout) {
- // Need to unlock before calling setNeedsLayout to avoid deadlocks.
- l.unlock();
- [self __setNeedsLayout];
- l.lock();
- }
-
- [self _locked_applyPendingStateToViewOrLayer];
-
- // The following methods should not be called with a lock
- l.unlock();
-
- // No need for the lock as accessing the subviews or layers are always happening on main
- [self _addSubnodeViewsAndLayers];
-
- // A subclass hook should never be called with a lock
- [self _didLoad];
-
- return _view;
-}
-
-- (CALayer *)layer
-{
- AS::UniqueLock l(__instanceLock__);
- if (_layer != nil) {
- return _layer;
- }
-
- if (![self _locked_shouldLoadViewOrLayer]) {
- return nil;
- }
-
- // Loading a layer needs to happen on the main thread
- ASDisplayNodeAssertMainThread();
- [self _locked_loadViewOrLayer];
- CALayer *layer = _layer;
-
- // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
- // but automatic subnode management would require us to modify the node tree
- // in the background on a loaded node, which isn't currently supported.
- if (_pendingViewState.hasSetNeedsLayout) {
- // Need to unlock before calling setNeedsLayout to avoid deadlocks.
- l.unlock();
- [self __setNeedsLayout];
- l.lock();
- }
-
- [self _locked_applyPendingStateToViewOrLayer];
-
- // The following methods should not be called with a lock
- l.unlock();
-
- // No need for the lock as accessing the subviews or layers are always happening on main
- [self _addSubnodeViewsAndLayers];
-
- // A subclass hook should never be called with a lock
- [self _didLoad];
-
- return layer;
-}
-
-// Returns nil if the layer is not an _ASDisplayLayer; will not create the layer if nil.
-- (_ASDisplayLayer *)asyncLayer
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_asyncLayer];
-}
-
-- (_ASDisplayLayer *)_locked_asyncLayer
-{
- ASAssertLocked(__instanceLock__);
- return [_layer isKindOfClass:[_ASDisplayLayer class]] ? (_ASDisplayLayer *)_layer : nil;
-}
-
-- (BOOL)isSynchronous
-{
- return checkFlag(Synchronous);
-}
-
-- (void)setLayerBacked:(BOOL)isLayerBacked
-{
- // Only call this if assertions are enabled – it could be expensive.
- ASDisplayNodeAssert(!isLayerBacked || self.supportsLayerBacking, @"Node %@ does not support layer backing.", self);
-
- MutexLocker l(__instanceLock__);
- if (_flags.layerBacked == isLayerBacked) {
- return;
- }
-
- if ([self _locked_isNodeLoaded]) {
- ASDisplayNodeFailAssert(@"Cannot change layerBacked after view/layer has loaded.");
- return;
- }
-
- _flags.layerBacked = isLayerBacked;
-}
-
-- (BOOL)isLayerBacked
-{
- MutexLocker l(__instanceLock__);
- return _flags.layerBacked;
-}
-
-- (BOOL)supportsLayerBacking
-{
- MutexLocker l(__instanceLock__);
- return !checkFlag(Synchronous) && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class];
-}
-
-- (BOOL)shouldAnimateSizeChanges
-{
- MutexLocker l(__instanceLock__);
- return _flags.shouldAnimateSizeChanges;
-}
-
-- (void)setShouldAnimateSizeChanges:(BOOL)shouldAnimateSizeChanges
-{
- MutexLocker l(__instanceLock__);
- _flags.shouldAnimateSizeChanges = shouldAnimateSizeChanges;
-}
-
-- (CGRect)threadSafeBounds
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_threadSafeBounds];
-}
-
-- (CGRect)_locked_threadSafeBounds
-{
- ASAssertLocked(__instanceLock__);
- return _threadSafeBounds;
-}
-
-- (void)setThreadSafeBounds:(CGRect)newBounds
-{
- MutexLocker l(__instanceLock__);
- _threadSafeBounds = newBounds;
-}
-
-- (void)nodeViewDidAddGestureRecognizer
-{
- MutexLocker l(__instanceLock__);
- _flags.viewEverHadAGestureRecognizerAttached = YES;
-}
-
-- (UIEdgeInsets)fallbackSafeAreaInsets
-{
- MutexLocker l(__instanceLock__);
- return _fallbackSafeAreaInsets;
-}
-
-- (void)setFallbackSafeAreaInsets:(UIEdgeInsets)insets
-{
- BOOL needsManualUpdate;
- BOOL updatesLayoutMargins;
-
- {
- MutexLocker l(__instanceLock__);
- ASDisplayNodeAssertThreadAffinity(self);
-
- if (UIEdgeInsetsEqualToEdgeInsets(insets, _fallbackSafeAreaInsets)) {
- return;
- }
-
- _fallbackSafeAreaInsets = insets;
- needsManualUpdate = !AS_AT_LEAST_IOS11 || _flags.layerBacked;
- updatesLayoutMargins = needsManualUpdate && [self _locked_insetsLayoutMarginsFromSafeArea];
- }
-
- if (needsManualUpdate) {
- [self safeAreaInsetsDidChange];
- }
-
- if (updatesLayoutMargins) {
- [self layoutMarginsDidChange];
- }
-}
-
-- (void)_fallbackUpdateSafeAreaOnChildren
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- UIEdgeInsets insets = self.safeAreaInsets;
- CGRect bounds = self.bounds;
-
- for (ASDisplayNode *child in self.subnodes) {
- if (AS_AT_LEAST_IOS11 && !child.layerBacked) {
- // In iOS 11 view-backed nodes already know what their safe area is.
- continue;
- }
-
- if (child.viewControllerRoot) {
- // Its safe area is controlled by a view controller. Don't override it.
- continue;
- }
-
- CGRect childFrame = child.frame;
- UIEdgeInsets childInsets = UIEdgeInsetsMake(MAX(insets.top - (CGRectGetMinY(childFrame) - CGRectGetMinY(bounds)), 0),
- MAX(insets.left - (CGRectGetMinX(childFrame) - CGRectGetMinX(bounds)), 0),
- MAX(insets.bottom - (CGRectGetMaxY(bounds) - CGRectGetMaxY(childFrame)), 0),
- MAX(insets.right - (CGRectGetMaxX(bounds) - CGRectGetMaxX(childFrame)), 0));
-
- child.fallbackSafeAreaInsets = childInsets;
- }
-}
-
-- (BOOL)isViewControllerRoot
-{
- MutexLocker l(__instanceLock__);
- return _isViewControllerRoot;
-}
-
-- (void)setViewControllerRoot:(BOOL)flag
-{
- MutexLocker l(__instanceLock__);
- _isViewControllerRoot = flag;
-}
-
-- (BOOL)automaticallyRelayoutOnSafeAreaChanges
-{
- MutexLocker l(__instanceLock__);
- return _automaticallyRelayoutOnSafeAreaChanges;
-}
-
-- (void)setAutomaticallyRelayoutOnSafeAreaChanges:(BOOL)flag
-{
- MutexLocker l(__instanceLock__);
- _automaticallyRelayoutOnSafeAreaChanges = flag;
-}
-
-- (BOOL)automaticallyRelayoutOnLayoutMarginsChanges
-{
- MutexLocker l(__instanceLock__);
- return _automaticallyRelayoutOnLayoutMarginsChanges;
-}
-
-- (void)setAutomaticallyRelayoutOnLayoutMarginsChanges:(BOOL)flag
-{
- MutexLocker l(__instanceLock__);
- _automaticallyRelayoutOnLayoutMarginsChanges = flag;
-}
-
-#pragma mark - UIResponder
-
-#define HANDLE_NODE_RESPONDER_METHOD(__sel) \
- /* All responder methods should be called on the main thread */ \
- ASDisplayNodeAssertMainThread(); \
- if (checkFlag(Synchronous)) { \
- /* If the view is not a _ASDisplayView subclass (Synchronous) just call through to the view as we
- expect it's a non _ASDisplayView subclass that will respond */ \
- return [_view __sel]; \
- } else { \
- if (ASSubclassOverridesSelector([_ASDisplayView class], _viewClass, @selector(__sel))) { \
- /* If the subclass overwrites canBecomeFirstResponder just call through
- to it as we expect it will handle it */ \
- return [_view __sel]; \
- } else { \
- /* Call through to _ASDisplayView's superclass to get it handled */ \
- return [(_ASDisplayView *)_view __##__sel]; \
- } \
- } \
-
-- (void)checkResponderCompatibility
-{
-#if ASDISPLAYNODE_ASSERTIONS_ENABLED
- // There are certain cases we cannot handle and are not supported:
- // 1. If the _view class is not a subclass of _ASDisplayView
- if (checkFlag(Synchronous)) {
- // 2. At least one UIResponder methods are overwritten in the node subclass
- NSString *message = @"Overwritting %@ and having a backing view that is not an _ASDisplayView is not supported.";
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(canBecomeFirstResponder)), ([NSString stringWithFormat:message, @"canBecomeFirstResponder"]));
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(becomeFirstResponder)), ([NSString stringWithFormat:message, @"becomeFirstResponder"]));
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(canResignFirstResponder)), ([NSString stringWithFormat:message, @"canResignFirstResponder"]));
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(resignFirstResponder)), ([NSString stringWithFormat:message, @"resignFirstResponder"]));
- ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(isFirstResponder)), ([NSString stringWithFormat:message, @"isFirstResponder"]));
- }
-#endif
-}
-
-- (BOOL)__canBecomeFirstResponder
-{
- if (_view == nil) {
- // By default we return NO if not view is created yet
- return NO;
- }
-
- HANDLE_NODE_RESPONDER_METHOD(canBecomeFirstResponder);
-}
-
-- (BOOL)__becomeFirstResponder
-{
- // Note: This implicitly loads the view if it hasn't been loaded yet.
- [self view];
-
- if (![self canBecomeFirstResponder]) {
- return NO;
- }
-
- HANDLE_NODE_RESPONDER_METHOD(becomeFirstResponder);
-}
-
-- (BOOL)__canResignFirstResponder
-{
- if (_view == nil) {
- // By default we return YES if no view is created yet
- return YES;
- }
-
- HANDLE_NODE_RESPONDER_METHOD(canResignFirstResponder);
-}
-
-- (BOOL)__resignFirstResponder
-{
- // Note: This implicitly loads the view if it hasn't been loaded yet.
- [self view];
-
- if (![self canResignFirstResponder]) {
- return NO;
- }
-
- HANDLE_NODE_RESPONDER_METHOD(resignFirstResponder);
-}
-
-- (BOOL)__isFirstResponder
-{
- if (_view == nil) {
- // If no view is created yet we can just return NO as it's unlikely it's the first responder
- return NO;
- }
-
- HANDLE_NODE_RESPONDER_METHOD(isFirstResponder);
-}
-
-#pragma mark
-
-- (NSString *)debugName
-{
- MutexLocker l(__instanceLock__);
- return _debugName;
-}
-
-- (void)setDebugName:(NSString *)debugName
-{
- MutexLocker l(__instanceLock__);
- if (!ASObjectIsEqual(_debugName, debugName)) {
- _debugName = [debugName copy];
- }
-}
-
-#pragma mark - Layout
-
-#pragma mark
-
-- (BOOL)canLayoutAsynchronous
-{
- return !self.isNodeLoaded;
-}
-
-#pragma mark Layout Pass
-
-- (void)__setNeedsLayout
-{
- [self invalidateCalculatedLayout];
-}
-
-- (void)invalidateCalculatedLayout
-{
- MutexLocker l(__instanceLock__);
-
- _layoutVersion++;
-
- _unflattenedLayout = nil;
-
-#if YOGA
- [self invalidateCalculatedYogaLayout];
-#endif
-}
-
-- (void)__layout
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // ASAssertUnlocked(__instanceLock__);
-
- BOOL loaded = NO;
- {
- AS::UniqueLock l(__instanceLock__);
- loaded = [self _locked_isNodeLoaded];
- CGRect bounds = _threadSafeBounds;
-
- if (CGRectEqualToRect(bounds, CGRectZero)) {
- // Performing layout on a zero-bounds view often results in frame calculations
- // with negative sizes after applying margins, which will cause
- // layoutThatFits: on subnodes to assert.
- return;
- }
-
- // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as
- // this is supposed to happen within the layout transition process
- if (_transitionID != ASLayoutElementContextInvalidTransitionID) {
- return;
- }
-
- // This method will confirm that the layout is up to date (and update if needed).
- // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
- l.unlock();
- [self _u_measureNodeWithBoundsIfNecessary:bounds];
- l.lock();
-
- [self _locked_layoutPlaceholderIfNecessary];
- }
-
- [self _layoutSublayouts];
-
- // Per API contract, `-layout` and `-layoutDidFinish` are called only if the node is loaded.
- if (loaded) {
- ASPerformBlockOnMainThread(^{
- [self layout];
- [self _layoutClipCornersIfNeeded];
- [self _layoutDidFinish];
- });
- }
-
- [self _fallbackUpdateSafeAreaOnChildren];
-}
-
-- (void)_layoutDidFinish
-{
- ASDisplayNodeAssertMainThread();
- // ASAssertUnlocked(__instanceLock__);
- ASDisplayNodeAssertTrue(self.isNodeLoaded);
- [self layoutDidFinish];
-}
-
-#pragma mark Calculation
-
-- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
- restrictedToSize:(ASLayoutElementSize)size
- relativeToParentSize:(CGSize)parentSize
-{
-#if AS_KDEBUG_ENABLE
- // We only want one calculateLayout signpost interval per thread.
- // Currently there is no fallback for profiling i386, since it's not useful.
- static _Thread_local NSInteger tls_callDepth;
- if (tls_callDepth++ == 0) {
- ASSignpostStart(ASSignpostCalculateLayout);
- }
-#endif
-
- ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
- const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
- ASLayout *result = [self calculateLayoutThatFits:resolvedRange];
-
-#if AS_KDEBUG_ENABLE
- if (--tls_callDepth == 0) {
- ASSignpostEnd(ASSignpostCalculateLayout);
- }
-#endif
-
- return result;
-}
-
-- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
-{
- __ASDisplayNodeCheckForLayoutMethodOverrides;
-
- switch (self.layoutEngineType) {
- case ASLayoutEngineTypeLayoutSpec:
- return [self calculateLayoutLayoutSpec:constrainedSize];
-#if YOGA
- case ASLayoutEngineTypeYoga:
- return [self calculateLayoutYoga:constrainedSize];
-#endif
- // If YOGA is not defined but for some reason the layout type engine is Yoga
- // we explicitly fallthrough here
- default:
- break;
- }
-
- // If this case is reached a layout type engine was defined for a node that is currently
- // not supported.
- ASDisplayNodeAssert(NO, @"No layout type determined");
- return nil;
-}
-
-- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
-{
- __ASDisplayNodeCheckForLayoutMethodOverrides;
-
- ASDisplayNodeLogEvent(self, @"calculateSizeThatFits: with constrainedSize: %@", NSStringFromCGSize(constrainedSize));
-
- return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero;
-}
-
-- (void)layout
-{
- // Hook for subclasses
- ASDisplayNodeAssertMainThread();
- // ASAssertUnlocked(__instanceLock__);
- ASDisplayNodeAssertTrue(self.isNodeLoaded);
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del nodeDidLayout];
- }];
-}
-
-#pragma mark Layout Transition
-
-- (void)_layoutTransitionMeasurementDidFinish
-{
- // Hook for subclasses - No-Op in ASDisplayNode
-}
-
-#pragma mark <_ASTransitionContextCompletionDelegate>
-
-/**
- * After completeTransition: is called on the ASContextTransitioning object in animateLayoutTransition: this
- * delegate method will be called that start the completion process of the transition
- */
-- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
-{
- ASDisplayNodeAssertMainThread();
-
- [self didCompleteLayoutTransition:context];
-
- _pendingLayoutTransitionContext = nil;
-
- [self _pendingLayoutTransitionDidComplete];
-}
-
-#pragma mark - Display
-
-NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
-NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";
-
-- (BOOL)displaysAsynchronously
-{
- MutexLocker l(__instanceLock__);
- return [self _locked_displaysAsynchronously];
-}
-
-/**
- * Core implementation of -displaysAsynchronously.
- */
-- (BOOL)_locked_displaysAsynchronously
-{
- ASAssertLocked(__instanceLock__);
- return checkFlag(Synchronous) == NO && _flags.displaysAsynchronously;
-}
-
-- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- MutexLocker l(__instanceLock__);
-
- // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
- if (checkFlag(Synchronous)) {
- return;
- }
-
- if (_flags.displaysAsynchronously == displaysAsynchronously) {
- return;
- }
-
- _flags.displaysAsynchronously = displaysAsynchronously;
-
- self._locked_asyncLayer.displaysAsynchronously = displaysAsynchronously;
-}
-
-- (BOOL)rasterizesSubtree
-{
- MutexLocker l(__instanceLock__);
- return _flags.rasterizesSubtree;
-}
-
-- (void)enableSubtreeRasterization
-{
- MutexLocker l(__instanceLock__);
- // Already rasterized from self.
- if (_flags.rasterizesSubtree) {
- return;
- }
-
- // If rasterized from above, bail.
- if (ASHierarchyStateIncludesRasterized(_hierarchyState)) {
- ASDisplayNodeFailAssert(@"Subnode of a rasterized node should not have redundant -enableSubtreeRasterization.");
- return;
- }
-
- // Ensure not loaded.
- if ([self _locked_isNodeLoaded]) {
- ASDisplayNodeFailAssert(@"Cannot call %@ on loaded node: %@", NSStringFromSelector(_cmd), self);
- return;
- }
-
- // Ensure no loaded subnodes
- ASDisplayNode *loadedSubnode = ASDisplayNodeFindFirstSubnode(self, ^BOOL(ASDisplayNode * _Nonnull node) {
- return node.nodeLoaded;
- });
- if (loadedSubnode != nil) {
- ASDisplayNodeFailAssert(@"Cannot call %@ on node %@ with loaded subnode %@", NSStringFromSelector(_cmd), self, loadedSubnode);
- return;
- }
-
- _flags.rasterizesSubtree = YES;
-
- // Tell subnodes that now they're in a rasterized hierarchy (while holding lock!)
- for (ASDisplayNode *subnode in _subnodes) {
- [subnode enterHierarchyState:ASHierarchyStateRasterized];
- }
-}
-
-- (CGFloat)contentsScaleForDisplay
-{
- MutexLocker l(__instanceLock__);
-
- return _contentsScaleForDisplay;
-}
-
-- (void)setContentsScaleForDisplay:(CGFloat)contentsScaleForDisplay
-{
- MutexLocker l(__instanceLock__);
-
- if (_contentsScaleForDisplay == contentsScaleForDisplay) {
- return;
- }
-
- _contentsScaleForDisplay = contentsScaleForDisplay;
-}
-
-- (void)displayImmediately
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(!checkFlag(Synchronous), @"this method is designed for asynchronous mode only");
-
- [self.asyncLayer displayImmediately];
-}
-
-- (void)recursivelyDisplayImmediately
-{
- for (ASDisplayNode *child in self.subnodes) {
- [child recursivelyDisplayImmediately];
- }
- [self displayImmediately];
-}
-
-- (void)__setNeedsDisplay
-{
- BOOL shouldScheduleForDisplay = NO;
- {
- MutexLocker l(__instanceLock__);
- BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
- // FIXME: This should not need to recursively display, so create a non-recursive variant.
- // The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
- if (_layer != nil && !checkFlag(Synchronous) && nowDisplay && [self _implementsDisplay]) {
- shouldScheduleForDisplay = YES;
- }
- }
-
- if (shouldScheduleForDisplay) {
- [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
- }
-}
-
-+ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node
-{
- static dispatch_once_t onceToken;
- static ASRunLoopQueue *renderQueue;
- dispatch_once(&onceToken, ^{
- renderQueue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain()
- retainObjects:NO
- handler:^(ASDisplayNode * _Nonnull dequeuedItem, BOOL isQueueDrained) {
- [dequeuedItem _recursivelyTriggerDisplayAndBlock:NO];
- if (isQueueDrained) {
- CFTimeInterval timestamp = CACurrentMediaTime();
- [[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification
- object:nil
- userInfo:@{ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp: @(timestamp)}];
- }
- }];
- });
-
- [renderQueue enqueue:node];
-}
-
-/// Helper method to summarize whether or not the node run through the display process
-- (BOOL)_implementsDisplay
-{
- MutexLocker l(__instanceLock__);
-
- return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.rasterizesSubtree;
-}
-
-// Track that a node will be displayed as part of the current node hierarchy.
-// The node sending the message should usually be passed as the parameter, similar to the delegation pattern.
-- (void)_pendingNodeWillDisplay:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertMainThread();
-
- // No lock needed as _pendingDisplayNodes is main thread only
- if (!_pendingDisplayNodes) {
- _pendingDisplayNodes = [[ASWeakSet alloc] init];
- }
-
- [_pendingDisplayNodes addObject:node];
-}
-
-// Notify that a node that was pending display finished
-// The node sending the message should usually be passed as the parameter, similar to the delegation pattern.
-- (void)_pendingNodeDidDisplay:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertMainThread();
-
- // No lock for _pendingDisplayNodes needed as it's main thread only
- [_pendingDisplayNodes removeObject:node];
-
- if (_pendingDisplayNodes.isEmpty) {
-
- [self hierarchyDisplayDidFinish];
- [self enumerateInterfaceStateDelegates:^(id delegate) {
- [delegate hierarchyDisplayDidFinish];
- }];
-
- BOOL placeholderShouldPersist = [self placeholderShouldPersist];
-
- __instanceLock__.lock();
- if (_placeholderLayer.superlayer && !placeholderShouldPersist) {
- void (^cleanupBlock)() = ^{
- [_placeholderLayer removeFromSuperlayer];
- };
-
- if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
- [CATransaction begin];
- [CATransaction setCompletionBlock:cleanupBlock];
- [CATransaction setAnimationDuration:_placeholderFadeDuration];
- _placeholderLayer.opacity = 0.0;
- [CATransaction commit];
- } else {
- cleanupBlock();
- }
- }
- __instanceLock__.unlock();
- }
-}
-
-// Helper method to determine if it's safe to call setNeedsDisplay on a layer without throwing away the content.
-// For details look at the comment on the canCallSetNeedsDisplayOfLayer flag
-- (BOOL)_canCallSetNeedsDisplayOfLayer
-{
- MutexLocker l(__instanceLock__);
- return _flags.canCallSetNeedsDisplayOfLayer;
-}
-
-void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
-{
- // This recursion must handle layers in various states:
- // 1. Just added to hierarchy, CA hasn't yet called -display
- // 2. Previously in a hierarchy (such as a working window owned by an Intelligent Preloading class, like ASTableView / ASCollectionView / ASViewController)
- // 3. Has no content to display at all
- // Specifically for case 1), we need to explicitly trigger a -display call now.
- // Otherwise, there is no opportunity to block the main thread after CoreAnimation's transaction commit
- // (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders).
-
- ASDisplayNode *node = [layer asyncdisplaykit_node];
-
- if (node.isSynchronous && [node _canCallSetNeedsDisplayOfLayer]) {
- // Layers for UIKit components that are wrapped within a node needs to be set to be displayed as the contents of
- // the layer get's cleared and would not be recreated otherwise.
- // We do not call this for _ASDisplayLayer as an optimization.
- [layer setNeedsDisplay];
- }
-
- if ([node _implementsDisplay]) {
- // For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue].
- // At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm.
- [layer displayIfNeeded];
- }
-
- // Kick off the recursion first, so that all necessary display calls are sent and the displayQueue is full of parallelizable work.
- // NOTE: The docs report that `sublayers` returns a copy but it actually doesn't.
- for (CALayer *sublayer in [layer.sublayers copy]) {
- recursivelyTriggerDisplayForLayer(sublayer, shouldBlock);
- }
-
- if (shouldBlock) {
- // As the recursion unwinds, verify each transaction is complete and block if it is not.
- // While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
- BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
- if (waitUntilComplete) {
- for (_ASAsyncTransaction *transaction in [layer.asyncdisplaykit_asyncLayerTransactions copy]) {
- // Even if none of the layers have had a chance to start display earlier, they will still be allowed to saturate a multicore CPU while blocking main.
- // This significantly reduces time on the main thread relative to UIKit.
- [transaction waitUntilComplete];
- }
- }
- }
-}
-
-- (void)_recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock
-{
- ASDisplayNodeAssertMainThread();
-
- CALayer *layer = self.layer;
- // -layoutIfNeeded is recursive, and even walks up to superlayers to check if they need layout,
- // so we should call it outside of starting the recursion below. If our own layer is not marked
- // as dirty, we can assume layout has run on this subtree before.
- if ([layer needsLayout]) {
- [layer layoutIfNeeded];
- }
- recursivelyTriggerDisplayForLayer(layer, shouldBlock);
-}
-
-- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously
-{
- [self _recursivelyTriggerDisplayAndBlock:synchronously];
-}
-
-- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
-{
- MutexLocker l(__instanceLock__);
- _flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay;
-}
-
-- (BOOL)shouldBypassEnsureDisplay
-{
- MutexLocker l(__instanceLock__);
- return _flags.shouldBypassEnsureDisplay;
-}
-
-- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale
-{
- {
- MutexLocker l(__instanceLock__);
- if (contentsScale == _contentsScaleForDisplay) {
- return;
- }
-
- _contentsScaleForDisplay = contentsScale;
- }
-
- [self setNeedsDisplay];
-}
-
-- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale
-{
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
- [node setNeedsDisplayAtScale:contentsScale];
- });
-}
-
-- (void)_layoutClipCornersIfNeeded
-{
- ASDisplayNodeAssertMainThread();
- if (_clipCornerLayers[0] == nil) {
- return;
- }
-
- CGSize boundsSize = self.bounds.size;
- for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) {
- BOOL isTop = (idx == 0 || idx == 1);
- BOOL isRight = (idx == 1 || idx == 2);
- if (_clipCornerLayers[idx]) {
- // Note the Core Animation coordinates are reversed for y; 0 is at the bottom.
- _clipCornerLayers[idx].position = CGPointMake(isRight ? boundsSize.width : 0.0, isTop ? boundsSize.height : 0.0);
- [_layer addSublayer:_clipCornerLayers[idx]];
- }
- }
-}
-
-- (void)_updateClipCornerLayerContentsWithRadius:(CGFloat)radius backgroundColor:(UIColor *)backgroundColor
-{
- ASPerformBlockOnMainThread(^{
- for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) {
- // Layers are, in order: Top Left, Top Right, Bottom Right, Bottom Left.
- // anchorPoint is Bottom Left at 0,0 and Top Right at 1,1.
- BOOL isTop = (idx == 0 || idx == 1);
- BOOL isRight = (idx == 1 || idx == 2);
-
- CGSize size = CGSizeMake(radius + 1, radius + 1);
- ASGraphicsBeginImageContextWithOptions(size, NO, self.contentsScaleForDisplay);
-
- CGContextRef ctx = UIGraphicsGetCurrentContext();
- if (isRight == YES) {
- CGContextTranslateCTM(ctx, -radius + 1, 0);
- }
- if (isTop == YES) {
- CGContextTranslateCTM(ctx, 0, -radius + 1);
- }
- UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, radius * 2, radius * 2) cornerRadius:radius];
- [roundedRect setUsesEvenOddFillRule:YES];
- [roundedRect appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1)]];
- [backgroundColor setFill];
- [roundedRect fill];
-
- // No lock needed, as _clipCornerLayers is only modified on the main thread.
- CALayer *clipCornerLayer = _clipCornerLayers[idx];
- clipCornerLayer.contents = (id)(ASGraphicsGetImageAndEndCurrentContext().CGImage);
- clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height);
- clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 1.0 : 0.0);
- }
- [self _layoutClipCornersIfNeeded];
- });
-}
-
-- (void)_setClipCornerLayersVisible:(BOOL)visible
-{
-}
-
-- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType cornerRadius:(CGFloat)newCornerRadius
-{
- __instanceLock__.lock();
- CGFloat oldCornerRadius = _cornerRadius;
- ASCornerRoundingType oldRoundingType = _cornerRoundingType;
-
- _cornerRadius = newCornerRadius;
- _cornerRoundingType = newRoundingType;
- __instanceLock__.unlock();
-
- ASPerformBlockOnMainThread(^{
- ASDisplayNodeAssertMainThread();
-
- if (oldRoundingType != newRoundingType || oldCornerRadius != newCornerRadius) {
- if (oldRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) {
- if (newRoundingType == ASCornerRoundingTypePrecomposited) {
- self.layerCornerRadius = 0.0;
- if (oldCornerRadius > 0.0) {
- [self displayImmediately];
- } else {
- [self setNeedsDisplay]; // Async display is OK if we aren't replacing an existing .cornerRadius.
- }
- }
- else if (newRoundingType == ASCornerRoundingTypeClipping) {
- self.layerCornerRadius = 0.0;
- [self _setClipCornerLayersVisible:YES];
- } else if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) {
- self.layerCornerRadius = newCornerRadius;
- }
- }
- else if (oldRoundingType == ASCornerRoundingTypePrecomposited) {
- if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) {
- self.layerCornerRadius = newCornerRadius;
- [self setNeedsDisplay];
- }
- else if (newRoundingType == ASCornerRoundingTypePrecomposited) {
- // Corners are already precomposited, but the radius has changed.
- // Default to async re-display. The user may force a synchronous display if desired.
- [self setNeedsDisplay];
- }
- else if (newRoundingType == ASCornerRoundingTypeClipping) {
- [self _setClipCornerLayersVisible:YES];
- [self setNeedsDisplay];
- }
- }
- else if (oldRoundingType == ASCornerRoundingTypeClipping) {
- if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) {
- self.layerCornerRadius = newCornerRadius;
- [self _setClipCornerLayersVisible:NO];
- }
- else if (newRoundingType == ASCornerRoundingTypePrecomposited) {
- [self _setClipCornerLayersVisible:NO];
- [self displayImmediately];
- }
- else if (newRoundingType == ASCornerRoundingTypeClipping) {
- // Clip corners already exist, but the radius has changed.
- [self _updateClipCornerLayerContentsWithRadius:newCornerRadius backgroundColor:self.backgroundColor];
- }
- }
- }
- });
-}
-
-- (void)recursivelySetDisplaySuspended:(BOOL)flag
-{
- _recursivelySetDisplaySuspended(self, nil, flag);
-}
-
-// TODO: Replace this with ASDisplayNodePerformBlockOnEveryNode or a variant with a condition / test block.
-static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, BOOL flag)
-{
- // If there is no layer, but node whose its view is loaded, then we can traverse down its layer hierarchy. Otherwise we must stick to the node hierarchy to avoid loading views prematurely. Note that for nodes that haven't loaded their views, they can't possibly have subviews/sublayers, so we don't need to traverse the layer hierarchy for them.
- if (!layer && node && node.nodeLoaded) {
- layer = node.layer;
- }
-
- // If we don't know the node, but the layer is an async layer, get the node from the layer.
- if (!node && layer && [layer isKindOfClass:[_ASDisplayLayer class]]) {
- node = layer.asyncdisplaykit_node;
- }
-
- // Set the flag on the node. If this is a pure layer (no node) then this has no effect (plain layers don't support preventing/cancelling display).
- node.displaySuspended = flag;
-
- if (layer && !node.rasterizesSubtree) {
- // If there is a layer, recurse down the layer hierarchy to set the flag on descendants. This will cover both layer-based and node-based children.
- for (CALayer *sublayer in layer.sublayers) {
- _recursivelySetDisplaySuspended(nil, sublayer, flag);
- }
- } else {
- // If there is no layer (view not loaded yet) or this node rasterizes descendants (there won't be a layer tree to traverse), recurse down the subnode hierarchy to set the flag on descendants. This covers only node-based children, but for a node whose view is not loaded it can't possibly have nodeless children.
- for (ASDisplayNode *subnode in node.subnodes) {
- _recursivelySetDisplaySuspended(subnode, nil, flag);
- }
- }
-}
-
-- (BOOL)displaySuspended
-{
- MutexLocker l(__instanceLock__);
- return _flags.displaySuspended;
-}
-
-- (void)setDisplaySuspended:(BOOL)flag
-{
- ASDisplayNodeAssertThreadAffinity(self);
- __instanceLock__.lock();
-
- // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
- if (checkFlag(Synchronous) || _flags.displaySuspended == flag) {
- __instanceLock__.unlock();
- return;
- }
-
- _flags.displaySuspended = flag;
-
- self._locked_asyncLayer.displaySuspended = flag;
-
- ASDisplayNode *supernode = _supernode;
- __instanceLock__.unlock();
-
- if ([self _implementsDisplay]) {
- // Display start and finish methods needs to happen on the main thread
- ASPerformBlockOnMainThread(^{
- if (flag) {
- [supernode subnodeDisplayDidFinish:self];
- } else {
- [supernode subnodeDisplayWillStart:self];
- }
- });
- }
-}
-
-#pragma mark <_ASDisplayLayerDelegate>
-
-- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously
-{
- // Subclass hook.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [self displayWillStart];
-#pragma clang diagnostic pop
-
- [self displayWillStartAsynchronously:asynchronously];
-}
-
-- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer
-{
- // Subclass hook.
- [self displayDidFinish];
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-implementations"
-- (void)displayWillStart {}
-#pragma clang diagnostic pop
-- (void)displayWillStartAsynchronously:(BOOL)asynchronously
-{
- ASDisplayNodeAssertMainThread();
-
- ASDisplayNodeLogEvent(self, @"displayWillStart");
- // in case current node takes longer to display than it's subnodes, treat it as a dependent node
- [self _pendingNodeWillDisplay:self];
-
- __instanceLock__.lock();
- ASDisplayNode *supernode = _supernode;
- __instanceLock__.unlock();
-
- [supernode subnodeDisplayWillStart:self];
-}
-
-- (void)displayDidFinish
-{
- ASDisplayNodeAssertMainThread();
-
- ASDisplayNodeLogEvent(self, @"displayDidFinish");
- [self _pendingNodeDidDisplay:self];
-
- __instanceLock__.lock();
- ASDisplayNode *supernode = _supernode;
- __instanceLock__.unlock();
-
- [supernode subnodeDisplayDidFinish:self];
-}
-
-- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode
-{
- // Subclass hook
- [self _pendingNodeWillDisplay:subnode];
-}
-
-- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode
-{
- // Subclass hook
- [self _pendingNodeDidDisplay:subnode];
-}
-
-#pragma mark
-
-// We are only the delegate for the layer when we are layer-backed, as UIView performs this function normally
-- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event
-{
- if (event == kCAOnOrderIn) {
- [self __enterHierarchy];
- } else if (event == kCAOnOrderOut) {
- [self __exitHierarchy];
- }
-
- ASDisplayNodeAssert(_flags.layerBacked, @"We shouldn't get called back here unless we are layer-backed.");
- return (id)kCFNull;
-}
-
-#pragma mark - Error Handling
-
-+ (void)setNonFatalErrorBlock:(ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock
-{
- if (_nonFatalErrorBlock != nonFatalErrorBlock) {
- _nonFatalErrorBlock = [nonFatalErrorBlock copy];
- }
-}
-
-+ (ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock
-{
- return _nonFatalErrorBlock;
-}
-
-#pragma mark - Converting to and from the Node's Coordinate System
-
-- (CATransform3D)_transformToAncestor:(ASDisplayNode *)ancestor
-{
- CATransform3D transform = CATransform3DIdentity;
- ASDisplayNode *currentNode = self;
- while (currentNode.supernode) {
- if (currentNode == ancestor) {
- return transform;
- }
-
- CGPoint anchorPoint = currentNode.anchorPoint;
- CGRect bounds = currentNode.bounds;
- CGPoint position = currentNode.position;
- CGPoint origin = CGPointMake(position.x - bounds.size.width * anchorPoint.x,
- position.y - bounds.size.height * anchorPoint.y);
-
- transform = CATransform3DTranslate(transform, origin.x, origin.y, 0);
- transform = CATransform3DTranslate(transform, -bounds.origin.x, -bounds.origin.y, 0);
- currentNode = currentNode.supernode;
- }
- return transform;
-}
-
-static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNode *referenceNode, ASDisplayNode *targetNode)
-{
- ASDisplayNode *ancestor = ASDisplayNodeFindClosestCommonAncestor(referenceNode, targetNode);
-
- // Transform into global (away from reference coordinate space)
- CATransform3D transformToGlobal = [referenceNode _transformToAncestor:ancestor];
-
- // Transform into local (via inverse transform from target to ancestor)
- CATransform3D transformToLocal = CATransform3DInvert([targetNode _transformToAncestor:ancestor]);
-
- return CATransform3DConcat(transformToGlobal, transformToLocal);
-}
-
-- (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- /**
- * When passed node=nil, all methods in this family use the UIView-style
- * behavior – that is, convert from/to window coordinates if there's a window,
- * otherwise return the point untransformed.
- */
- if (node == nil && self.nodeLoaded) {
- CALayer *layer = self.layer;
- if (UIWindow *window = ASFindWindowOfLayer(layer)) {
- return [layer convertPoint:point fromLayer:window.layer];
- } else {
- return point;
- }
- }
-
- // Get root node of the accessible node hierarchy, if node not specified
- node = node ? : ASDisplayNodeUltimateParentOfNode(self);
-
- // Calculate transform to map points between coordinate spaces
- CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self);
- CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
- ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
-
- // Apply to point
- return CGPointApplyAffineTransform(point, flattenedTransform);
-}
-
-- (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- if (node == nil && self.nodeLoaded) {
- CALayer *layer = self.layer;
- if (UIWindow *window = ASFindWindowOfLayer(layer)) {
- return [layer convertPoint:point toLayer:window.layer];
- } else {
- return point;
- }
- }
-
- // Get root node of the accessible node hierarchy, if node not specified
- node = node ? : ASDisplayNodeUltimateParentOfNode(self);
-
- // Calculate transform to map points between coordinate spaces
- CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node);
- CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
- ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
-
- // Apply to point
- return CGPointApplyAffineTransform(point, flattenedTransform);
-}
-
-- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- if (node == nil && self.nodeLoaded) {
- CALayer *layer = self.layer;
- if (UIWindow *window = ASFindWindowOfLayer(layer)) {
- return [layer convertRect:rect fromLayer:window.layer];
- } else {
- return rect;
- }
- }
-
- // Get root node of the accessible node hierarchy, if node not specified
- node = node ? : ASDisplayNodeUltimateParentOfNode(self);
-
- // Calculate transform to map points between coordinate spaces
- CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self);
- CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
- ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
-
- // Apply to rect
- return CGRectApplyAffineTransform(rect, flattenedTransform);
-}
-
-- (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- if (node == nil && self.nodeLoaded) {
- CALayer *layer = self.layer;
- if (UIWindow *window = ASFindWindowOfLayer(layer)) {
- return [layer convertRect:rect toLayer:window.layer];
- } else {
- return rect;
- }
- }
-
- // Get root node of the accessible node hierarchy, if node not specified
- node = node ? : ASDisplayNodeUltimateParentOfNode(self);
-
- // Calculate transform to map points between coordinate spaces
- CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node);
- CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
- ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
-
- // Apply to rect
- return CGRectApplyAffineTransform(rect, flattenedTransform);
-}
-
-#pragma mark - Managing the Node Hierarchy
-
-ASDISPLAYNODE_INLINE bool shouldDisableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASDisplayNode *to) {
- if (!from || !to) return NO;
- if (from.isSynchronous) return NO;
- if (to.isSynchronous) return NO;
- if (from.isInHierarchy != to.isInHierarchy) return NO;
- return YES;
-}
-
-/// Returns incremented value of i if i is not NSNotFound
-ASDISPLAYNODE_INLINE NSInteger incrementIfFound(NSInteger i) {
- return i == NSNotFound ? NSNotFound : i + 1;
-}
-
-/// Returns if a node is a member of a rasterized tree
-ASDISPLAYNODE_INLINE BOOL canUseViewAPI(ASDisplayNode *node, ASDisplayNode *subnode) {
- return (subnode.isLayerBacked == NO && node.isLayerBacked == NO);
-}
-
-/// Returns if node is a member of a rasterized tree
-ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- return (node.rasterizesSubtree || (node.hierarchyState & ASHierarchyStateRasterized));
-}
-
-// NOTE: This method must be dealloc-safe (should not retain self).
-- (ASDisplayNode *)supernode
-{
- MutexLocker l(__instanceLock__);
- return _supernode;
-}
-
-- (void)_setSupernode:(ASDisplayNode *)newSupernode
-{
- BOOL supernodeDidChange = NO;
- ASDisplayNode *oldSupernode = nil;
- {
- MutexLocker l(__instanceLock__);
- if (_supernode != newSupernode) {
- oldSupernode = _supernode; // Access supernode properties outside of lock to avoid remote chance of deadlock,
- // in case supernode implementation must access one of our properties.
- _supernode = newSupernode;
- supernodeDidChange = YES;
- }
- }
-
- if (supernodeDidChange) {
- ASDisplayNodeLogEvent(self, @"supernodeDidChange: %@, oldValue = %@", ASObjectDescriptionMakeTiny(newSupernode), ASObjectDescriptionMakeTiny(oldSupernode));
- // Hierarchy state
- ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
- : oldSupernode.hierarchyState);
-
- // Rasterized state
- BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.rasterizesSubtree
- : oldSupernode.rasterizesSubtree);
- if (parentWasOrIsRasterized) {
- stateToEnterOrExit |= ASHierarchyStateRasterized;
- }
- if (newSupernode) {
-
- // Now that we have a supernode, propagate its traits to self.
- // This should be done before possibly forcing self to load so we have traits in -didLoad
- ASTraitCollectionPropagateDown(self, newSupernode.primitiveTraitCollection);
-
- if (!parentWasOrIsRasterized && newSupernode.nodeLoaded) {
- // Trigger the subnode to load its layer, which will create its view if it needs one.
- // By doing this prior to the downward propagation of newSupernode's interface state,
- // we can guarantee that -didEnterVisibleState is only called with .isNodeLoaded = YES.
- [self layer];
- }
-
- [self enterHierarchyState:stateToEnterOrExit];
-
- // If a node was added to a supernode, the supernode could be in a layout pending state. All of the hierarchy state
- // properties related to the transition need to be copied over as well as propagated down the subtree.
- // This is especially important as with automatic subnode management, adding subnodes can happen while a transition
- // is in fly
- if (ASHierarchyStateIncludesLayoutPending(stateToEnterOrExit)) {
- int32_t pendingTransitionId = newSupernode->_pendingTransitionID;
- if (pendingTransitionId != ASLayoutElementContextInvalidTransitionID) {
- {
- _pendingTransitionID = pendingTransitionId;
-
- // Propagate down the new pending transition id
- ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
- node->_pendingTransitionID = pendingTransitionId;
- });
- }
- }
- }
- } else {
- // If a node will be removed from the supernode it should go out from the layout pending state to remove all
- // layout pending state related properties on the node
- stateToEnterOrExit |= ASHierarchyStateLayoutPending;
-
- [self exitHierarchyState:stateToEnterOrExit];
-
- // We only need to explicitly exit hierarchy here if we were rasterized.
- // Otherwise we will exit the hierarchy when our view/layer does so
- // which has some nice carry-over machinery to handle cases where we are removed from a hierarchy
- // and then added into it again shortly after.
- __instanceLock__.lock();
- BOOL isInHierarchy = _flags.isInHierarchy;
- __instanceLock__.unlock();
-
- if (parentWasOrIsRasterized && isInHierarchy) {
- [self __exitHierarchy];
- }
- }
- }
-}
-
-- (NSArray *)subnodes
-{
- MutexLocker l(__instanceLock__);
- if (_cachedSubnodes == nil) {
- _cachedSubnodes = [_subnodes copy];
- } else {
- ASDisplayNodeAssert(ASObjectIsEqual(_cachedSubnodes, _subnodes), @"Expected _subnodes and _cachedSubnodes to have the same contents.");
- }
- return _cachedSubnodes ?: @[];
-}
-
-/*
- * Central private helper method that should eventually be called if submethods add, insert or replace subnodes
- * This method is called with thread affinity and without lock held.
- *
- * @param subnode The subnode to insert
- * @param subnodeIndex The index in _subnodes to insert it
- * @param viewSublayerIndex The index in layer.sublayers (not view.subviews) at which to insert the view (use if we can use the view API) otherwise pass NSNotFound
- * @param sublayerIndex The index in layer.sublayers at which to insert the layer (use if either parent or subnode is layer-backed) otherwise pass NSNotFound
- * @param oldSubnode Remove this subnode before inserting; ok to be nil if no removal is desired
- */
-- (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- if (subnode == nil || subnode == self) {
- ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode");
- return;
- }
-
- if (subnodeIndex == NSNotFound) {
- ASDisplayNodeFailAssert(@"Try to insert node on an index that was not found");
- return;
- }
-
- if (self.layerBacked && !subnode.layerBacked) {
- ASDisplayNodeFailAssert(@"Cannot add a view-backed node as a subnode of a layer-backed node. Supernode: %@, subnode: %@", self, subnode);
- return;
- }
-
- BOOL isRasterized = subtreeIsRasterized(self);
- if (isRasterized && subnode.nodeLoaded) {
- ASDisplayNodeFailAssert(@"Cannot add loaded node %@ to rasterized subtree of node %@", ASObjectDescriptionMakeTiny(subnode), ASObjectDescriptionMakeTiny(self));
- return;
- }
-
- __instanceLock__.lock();
- NSUInteger subnodesCount = _subnodes.count;
- __instanceLock__.unlock();
- if (subnodeIndex > subnodesCount || subnodeIndex < 0) {
- ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %ld. Count is %ld", (long)subnodeIndex, (long)subnodesCount);
- return;
- }
-
- // Disable appearance methods during move between supernodes, but make sure we restore their state after we do our thing
- ASDisplayNode *oldParent = subnode.supernode;
- BOOL disableNotifications = shouldDisableNotificationsForMovingBetweenParents(oldParent, self);
- if (disableNotifications) {
- [subnode __incrementVisibilityNotificationsDisabled];
- }
-
- [subnode _removeFromSupernode];
- [oldSubnode _removeFromSupernode];
-
- __instanceLock__.lock();
- if (_subnodes == nil) {
- _subnodes = [[NSMutableArray alloc] init];
- }
- [_subnodes insertObject:subnode atIndex:subnodeIndex];
- _cachedSubnodes = nil;
- __instanceLock__.unlock();
-
- // This call will apply our .hierarchyState to the new subnode.
- // If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
- [subnode _setSupernode:self];
-
- // If this subnode will be rasterized, enter hierarchy if needed
- // TODO: Move this into _setSupernode: ?
- if (isRasterized) {
- if (self.inHierarchy) {
- [subnode __enterHierarchy];
- }
- } else if (self.nodeLoaded) {
- // If not rasterizing, and node is loaded insert the subview/sublayer now.
- [self _insertSubnodeSubviewOrSublayer:subnode atIndex:sublayerIndex];
- } // Otherwise we will insert subview/sublayer when we get loaded
-
- ASDisplayNodeAssert(disableNotifications == shouldDisableNotificationsForMovingBetweenParents(oldParent, self), @"Invariant violated");
- if (disableNotifications) {
- [subnode __decrementVisibilityNotificationsDisabled];
- }
-}
-
-/*
- * Inserts the view or layer of the given node at the given index
- *
- * @param subnode The subnode to insert
- * @param idx The index in _view.subviews or _layer.sublayers at which to insert the subnode.view or
- * subnode.layer of the subnode
- */
-- (void)_insertSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(self.nodeLoaded, @"_insertSubnodeSubviewOrSublayer:atIndex: should never be called before our own view is created");
-
- ASDisplayNodeAssert(idx != NSNotFound, @"Try to insert node on an index that was not found");
- if (idx == NSNotFound) {
- return;
- }
-
- // Because the view and layer can only be created and destroyed on Main, that is also the only thread
- // where the view and layer can change. We can avoid locking.
-
- // If we can use view API, do. Due to an apple bug, -insertSubview:atIndex: actually wants a LAYER index,
- // which we pass in.
- if (canUseViewAPI(self, subnode)) {
- [_view insertSubview:subnode.view atIndex:idx];
- } else {
- [_layer insertSublayer:subnode.layer atIndex:(unsigned int)idx];
- }
-}
-
-- (void)addSubnode:(ASDisplayNode *)subnode
-{
- ASDisplayNodeLogEvent(self, @"addSubnode: %@ with automaticallyManagesSubnodes: %@",
- subnode, self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _addSubnode:subnode];
-}
-
-- (void)_addSubnode:(ASDisplayNode *)subnode
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- ASDisplayNodeAssert(subnode, @"Cannot insert a nil subnode");
-
- // Don't add if it's already a subnode
- ASDisplayNode *oldParent = subnode.supernode;
- if (!subnode || subnode == self || oldParent == self) {
- return;
- }
-
- NSUInteger subnodesIndex;
- NSUInteger sublayersIndex;
- {
- MutexLocker l(__instanceLock__);
- subnodesIndex = _subnodes.count;
- sublayersIndex = _layer.sublayers.count;
- }
-
- [self _insertSubnode:subnode atSubnodeIndex:subnodesIndex sublayerIndex:sublayersIndex andRemoveSubnode:nil];
-}
-
-- (void)_addSubnodeViewsAndLayers
-{
- ASDisplayNodeAssertMainThread();
-
- TIME_SCOPED(_debugTimeToAddSubnodeViews);
-
- for (ASDisplayNode *node in self.subnodes) {
- [self _addSubnodeSubviewOrSublayer:node];
- }
-}
-
-- (void)_addSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode
-{
- ASDisplayNodeAssertMainThread();
-
- // Due to a bug in Apple's framework we have to use the layer index to insert a subview
- // so just use the count of the sublayers to add the subnode
- NSInteger idx = _layer.sublayers.count; // No locking is needed as it's main thread only
- [self _insertSubnodeSubviewOrSublayer:subnode atIndex:idx];
-}
-
-- (void)replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode
-{
- ASDisplayNodeLogEvent(self, @"replaceSubnode: %@ withSubnode: %@ with automaticallyManagesSubnodes: %@",
- oldSubnode, replacementSubnode, self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _replaceSubnode:oldSubnode withSubnode:replacementSubnode];
-}
-
-- (void)_replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode
-{
- ASDisplayNodeAssertThreadAffinity(self);
-
- if (replacementSubnode == nil) {
- ASDisplayNodeFailAssert(@"Invalid subnode to replace");
- return;
- }
-
- if (oldSubnode.supernode != self) {
- ASDisplayNodeFailAssert(@"Old Subnode to replace must be a subnode");
- return;
- }
-
- ASDisplayNodeAssert(!(self.nodeLoaded && !oldSubnode.nodeLoaded), @"We have view loaded, but child node does not.");
-
- NSInteger subnodeIndex;
- NSInteger sublayerIndex = NSNotFound;
- {
- MutexLocker l(__instanceLock__);
- ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
-
- subnodeIndex = [_subnodes indexOfObjectIdenticalTo:oldSubnode];
-
- // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
- // hierarchy and none of this could possibly work.
- if (subtreeIsRasterized(self) == NO) {
- if (_layer) {
- sublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:oldSubnode.layer];
- ASDisplayNodeAssert(sublayerIndex != NSNotFound, @"Somehow oldSubnode's supernode is self, yet we could not find it in our layers to replace");
- if (sublayerIndex == NSNotFound) {
- return;
- }
- }
- }
- }
-
- [self _insertSubnode:replacementSubnode atSubnodeIndex:subnodeIndex sublayerIndex:sublayerIndex andRemoveSubnode:oldSubnode];
-}
-
-- (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
-{
- ASDisplayNodeLogEvent(self, @"insertSubnode: %@ belowSubnode: %@ with automaticallyManagesSubnodes: %@",
- subnode, below, self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _insertSubnode:subnode belowSubnode:below];
-}
-
-- (void)_insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- if (subnode == nil) {
- ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
- return;
- }
-
- if (below.supernode != self) {
- ASDisplayNodeFailAssert(@"Node to insert below must be a subnode");
- return;
- }
-
- NSInteger belowSubnodeIndex;
- NSInteger belowSublayerIndex = NSNotFound;
- {
- MutexLocker l(__instanceLock__);
- ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
-
- belowSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:below];
-
- // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
- // hierarchy and none of this could possibly work.
- if (subtreeIsRasterized(self) == NO) {
- if (_layer) {
- belowSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:below.layer];
- ASDisplayNodeAssert(belowSublayerIndex != NSNotFound, @"Somehow below's supernode is self, yet we could not find it in our layers to reference");
- if (belowSublayerIndex == NSNotFound)
- return;
- }
-
- ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes");
-
- // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to
- // insert it will mess up our calculation
- if (subnode.supernode == self) {
- NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode];
- if (currentIndexInSubnodes < belowSubnodeIndex) {
- belowSubnodeIndex--;
- }
- if (_layer) {
- NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer];
- if (currentIndexInSublayers < belowSublayerIndex) {
- belowSublayerIndex--;
- }
- }
- }
- }
- }
-
- ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find below in subnodes");
-
- [self _insertSubnode:subnode atSubnodeIndex:belowSubnodeIndex sublayerIndex:belowSublayerIndex andRemoveSubnode:nil];
-}
-
-- (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
-{
- ASDisplayNodeLogEvent(self, @"insertSubnode: %@ abodeSubnode: %@ with automaticallyManagesSubnodes: %@",
- subnode, above, self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _insertSubnode:subnode aboveSubnode:above];
-}
-
-- (void)_insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- if (subnode == nil) {
- ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
- return;
- }
-
- if (above.supernode != self) {
- ASDisplayNodeFailAssert(@"Node to insert above must be a subnode");
- return;
- }
-
- NSInteger aboveSubnodeIndex;
- NSInteger aboveSublayerIndex = NSNotFound;
- {
- MutexLocker l(__instanceLock__);
- ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
-
- aboveSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:above];
-
- // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
- // hierarchy and none of this could possibly work.
- if (subtreeIsRasterized(self) == NO) {
- if (_layer) {
- aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer];
- ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace");
- if (aboveSublayerIndex == NSNotFound)
- return;
- }
-
- ASDisplayNodeAssert(aboveSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes");
-
- // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to
- // insert it will mess up our calculation
- if (subnode.supernode == self) {
- NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode];
- if (currentIndexInSubnodes <= aboveSubnodeIndex) {
- aboveSubnodeIndex--;
- }
- if (_layer) {
- NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer];
- if (currentIndexInSublayers <= aboveSublayerIndex) {
- aboveSublayerIndex--;
- }
- }
- }
- }
- }
-
- [self _insertSubnode:subnode atSubnodeIndex:incrementIfFound(aboveSubnodeIndex) sublayerIndex:incrementIfFound(aboveSublayerIndex) andRemoveSubnode:nil];
-}
-
-- (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
-{
- ASDisplayNodeLogEvent(self, @"insertSubnode: %@ atIndex: %td with automaticallyManagesSubnodes: %@",
- subnode, idx, self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _insertSubnode:subnode atIndex:idx];
-}
-
-- (void)_insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- if (subnode == nil) {
- ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
- return;
- }
-
- NSInteger sublayerIndex = NSNotFound;
- {
- MutexLocker l(__instanceLock__);
-
- if (idx > _subnodes.count || idx < 0) {
- ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %ld. Count is %ld", (long)idx, (long)_subnodes.count);
- return;
- }
-
- // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
- // hierarchy and none of this could possibly work.
- if (subtreeIsRasterized(self) == NO) {
- // Account for potentially having other subviews
- if (_layer && idx == 0) {
- sublayerIndex = 0;
- } else if (_layer) {
- ASDisplayNode *positionInRelationTo = (_subnodes.count > 0 && idx > 0) ? _subnodes[idx - 1] : nil;
- if (positionInRelationTo) {
- sublayerIndex = incrementIfFound([_layer.sublayers indexOfObjectIdenticalTo:positionInRelationTo.layer]);
- }
- }
- }
- }
-
- [self _insertSubnode:subnode atSubnodeIndex:idx sublayerIndex:sublayerIndex andRemoveSubnode:nil];
-}
-
-- (void)_removeSubnode:(ASDisplayNode *)subnode
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- // Don't call self.supernode here because that will retain/autorelease the supernode. This method -_removeSupernode: is often called while tearing down a node hierarchy, and the supernode in question might be in the middle of its -dealloc. The supernode is never messaged, only compared by value, so this is safe.
- // The particular issue that triggers this edge case is when a node calls -removeFromSupernode on a subnode from within its own -dealloc method.
- if (!subnode || subnode.supernode != self) {
- return;
- }
-
- __instanceLock__.lock();
- [_subnodes removeObjectIdenticalTo:subnode];
- _cachedSubnodes = nil;
- __instanceLock__.unlock();
-
- [subnode _setSupernode:nil];
-}
-
-- (void)removeFromSupernode
-{
- ASDisplayNodeLogEvent(self, @"removeFromSupernode with automaticallyManagesSubnodes: %@",
- self.automaticallyManagesSubnodes ? @"YES" : @"NO");
- [self _removeFromSupernode];
-}
-
-- (void)_removeFromSupernode
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- __instanceLock__.lock();
- __weak ASDisplayNode *supernode = _supernode;
- __weak UIView *view = _view;
- __weak CALayer *layer = _layer;
- __instanceLock__.unlock();
-
- [self _removeFromSupernode:supernode view:view layer:layer];
-}
-
-- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode
-{
- ASDisplayNodeAssertThreadAffinity(self);
- // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
- // ASAssertUnlocked(__instanceLock__);
-
- __instanceLock__.lock();
-
- // Only remove if supernode is still the expected supernode
- if (!ASObjectIsEqual(_supernode, supernode)) {
- __instanceLock__.unlock();
- return;
- }
-
- __weak UIView *view = _view;
- __weak CALayer *layer = _layer;
- __instanceLock__.unlock();
-
- [self _removeFromSupernode:supernode view:view layer:layer];
-}
-
-- (void)_removeFromSupernode:(ASDisplayNode *)supernode view:(UIView *)view layer:(CALayer *)layer
-{
- // Note: we continue even if supernode is nil to ensure view/layer are removed from hierarchy.
-
- if (supernode != nil) {
- }
-
- // Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView
- // will trigger us to clear our _supernode pointer in willMoveToSuperview:nil.
- // This may result in removing the last strong reference, triggering deallocation after this method.
- [supernode _removeSubnode:self];
-
- if (view != nil) {
- [view removeFromSuperview];
- } else if (layer != nil) {
- [layer removeFromSuperlayer];
- }
-}
-
-#pragma mark - Visibility API
-
-- (BOOL)__visibilityNotificationsDisabled
-{
- // Currently, this method is only used by the testing infrastructure to verify this internal feature.
- MutexLocker l(__instanceLock__);
- return _flags.visibilityNotificationsDisabled > 0;
-}
-
-- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled
-{
- MutexLocker l(__instanceLock__);
- return (_hierarchyState & ASHierarchyStateTransitioningSupernodes);
-}
-
-- (void)__incrementVisibilityNotificationsDisabled
-{
- __instanceLock__.lock();
- const size_t maxVisibilityIncrement = (1ULL< 0, @"Can't decrement past 0");
- if (_flags.visibilityNotificationsDisabled > 0) {
- _flags.visibilityNotificationsDisabled--;
- }
- BOOL visibilityNotificationsDisabled = (_flags.visibilityNotificationsDisabled == 0);
- __instanceLock__.unlock();
-
- if (visibilityNotificationsDisabled) {
- // Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state.
- // FIXME: This system should be revisited when refactoring and consolidating the implementation of the
- // addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases,
- // multiple nodes in the subtree below may have a non-zero visibilityNotification count and still have
- // the ASHierarchyState bit cleared (the only value checked when reading this state).
- [self exitHierarchyState:ASHierarchyStateTransitioningSupernodes];
- }
-}
-
-#pragma mark - Placeholder
-
-- (void)_locked_layoutPlaceholderIfNecessary
-{
- ASAssertLocked(__instanceLock__);
- if ([self _locked_shouldHavePlaceholderLayer]) {
- [self _locked_setupPlaceholderLayerIfNeeded];
- }
- // Update the placeholderLayer size in case the node size has changed since the placeholder was added.
- _placeholderLayer.frame = self.threadSafeBounds;
-}
-
-- (BOOL)_locked_shouldHavePlaceholderLayer
-{
- ASAssertLocked(__instanceLock__);
- return (_placeholderEnabled && [self _implementsDisplay]);
-}
-
-- (void)_locked_setupPlaceholderLayerIfNeeded
-{
- ASDisplayNodeAssertMainThread();
- ASAssertLocked(__instanceLock__);
-
- if (!_placeholderLayer) {
- _placeholderLayer = [CALayer layer];
- // do not set to CGFLOAT_MAX in the case that something needs to be overtop the placeholder
- _placeholderLayer.zPosition = 9999.0;
- }
-
- if (_placeholderLayer.contents == nil) {
- if (!_placeholderImage) {
- _placeholderImage = [self placeholderImage];
- }
- if (_placeholderImage) {
- BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(_placeholderImage.capInsets, UIEdgeInsetsZero);
- if (stretchable) {
- ASDisplayNodeSetResizableContents(_placeholderLayer, _placeholderImage);
- } else {
- _placeholderLayer.contentsScale = self.contentsScale;
- _placeholderLayer.contents = (id)_placeholderImage.CGImage;
- }
- }
- }
-}
-
-- (UIImage *)placeholderImage
-{
- // Subclass hook
- return nil;
-}
-
-- (BOOL)placeholderShouldPersist
-{
- // Subclass hook
- return NO;
-}
-
-#pragma mark - Hierarchy State
-
-- (BOOL)isInHierarchy
-{
- MutexLocker l(__instanceLock__);
- return _flags.isInHierarchy;
-}
-
-- (void)__enterHierarchy
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"Should not cause recursive __enterHierarchy");
- ASDisplayNodeLogEvent(self, @"enterHierarchy");
-
- // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
- __instanceLock__.lock();
-
- if (!_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
- _flags.isEnteringHierarchy = YES;
- _flags.isInHierarchy = YES;
-
- // Don't call -willEnterHierarchy while holding __instanceLock__.
- // This method and subsequent ones (i.e -interfaceState and didEnter(.*)State)
- // don't expect that they are called while the lock is being held.
- // More importantly, didEnter(.*)State methods are meant to be overriden by clients.
- // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long.
- __instanceLock__.unlock();
- [self willEnterHierarchy];
- for (ASDisplayNode *subnode in self.subnodes) {
- [subnode __enterHierarchy];
- }
- __instanceLock__.lock();
-
- _flags.isEnteringHierarchy = NO;
-
- // If we don't have contents finished drawing by the time we are on screen, immediately add the placeholder (if it is enabled and we do have something to draw).
- if (self.contents == nil && [self _implementsDisplay]) {
- CALayer *layer = self.layer;
- [layer setNeedsDisplay];
-
- if ([self _locked_shouldHavePlaceholderLayer]) {
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
- [self _locked_setupPlaceholderLayerIfNeeded];
- _placeholderLayer.opacity = 1.0;
- [CATransaction commit];
- [layer addSublayer:_placeholderLayer];
- }
- }
- }
-
- __instanceLock__.unlock();
-
- [self didEnterHierarchy];
-}
-
-- (void)__exitHierarchy
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"Should not cause recursive __exitHierarchy");
- ASDisplayNodeLogEvent(self, @"exitHierarchy");
-
- // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
- __instanceLock__.lock();
-
- if (_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
- _flags.isExitingHierarchy = YES;
- _flags.isInHierarchy = NO;
-
- // Don't call -didExitHierarchy while holding __instanceLock__.
- // This method and subsequent ones (i.e -interfaceState and didExit(.*)State)
- // don't expect that they are called while the lock is being held.
- // More importantly, didExit(.*)State methods are meant to be overriden by clients.
- // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long.
- __instanceLock__.unlock();
- [self didExitHierarchy];
- for (ASDisplayNode *subnode in self.subnodes) {
- [subnode __exitHierarchy];
- }
- __instanceLock__.lock();
-
- _flags.isExitingHierarchy = NO;
- }
-
- __instanceLock__.unlock();
-}
-
-- (void)enterHierarchyState:(ASHierarchyState)hierarchyState
-{
- if (hierarchyState == ASHierarchyStateNormal) {
- return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
- }
-
- ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) {
- node.hierarchyState |= hierarchyState;
- });
-}
-
-- (void)exitHierarchyState:(ASHierarchyState)hierarchyState
-{
- if (hierarchyState == ASHierarchyStateNormal) {
- return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
- }
- ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) {
- node.hierarchyState &= (~hierarchyState);
- });
-}
-
-- (ASHierarchyState)hierarchyState
-{
- MutexLocker l(__instanceLock__);
- return _hierarchyState;
-}
-
-- (void)setHierarchyState:(ASHierarchyState)newState
-{
- ASHierarchyState oldState = ASHierarchyStateNormal;
- {
- MutexLocker l(__instanceLock__);
- if (_hierarchyState == newState) {
- return;
- }
- oldState = _hierarchyState;
- _hierarchyState = newState;
- }
-
- // Entered rasterization state.
- if (newState & ASHierarchyStateRasterized) {
- ASDisplayNodeAssert(checkFlag(Synchronous) == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with subtree rasterization enabled. Node: %@", self);
- }
-
- // Entered or exited range managed state.
- if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) {
- if (newState & ASHierarchyStateRangeManaged) {
- [self enterInterfaceState:self.supernode.pendingInterfaceState];
- } else {
- // The case of exiting a range-managed state should be fairly rare. Adding or removing the node
- // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields),
- // but because we might be about to be added to a view hierarchy, exiting the interface state now
- // would cause inefficient churn. The tradeoff is that we may not clear contents / fetched data
- // for nodes that are removed from a managed state and then retained but not used (bad idea anyway!)
- }
- }
-
- if ((newState & ASHierarchyStateLayoutPending) != (oldState & ASHierarchyStateLayoutPending)) {
- if (newState & ASHierarchyStateLayoutPending) {
- // Entering layout pending state
- } else {
- // Leaving layout pending state, reset related properties
- MutexLocker l(__instanceLock__);
- _pendingTransitionID = ASLayoutElementContextInvalidTransitionID;
- _pendingLayoutTransition = nil;
- }
- }
-
- ASDisplayNodeLogEvent(self, @"setHierarchyState: %@", NSStringFromASHierarchyStateChange(oldState, newState));
-}
-
-- (void)willEnterHierarchy
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
- ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
- ASAssertUnlocked(__instanceLock__);
-
- if (![self supportsRangeManagedInterfaceState]) {
- self.interfaceState = ASInterfaceStateInHierarchy;
- } else if (ASCATransactionQueueGet().enabled) {
- __instanceLock__.lock();
- ASInterfaceState state = _preExitingInterfaceState;
- _preExitingInterfaceState = ASInterfaceStateNone;
- __instanceLock__.unlock();
- // Layer thrash happened, revert to before exiting.
- if (state != ASInterfaceStateNone) {
- self.interfaceState = state;
- }
- }
-}
-
-- (void)didEnterHierarchy {
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"You should never call -didEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
- ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
- ASDisplayNodeAssert(_flags.isInHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
- ASAssertUnlocked(__instanceLock__);
-}
-
-- (void)didExitHierarchy
-{
- ASDisplayNodeAssertMainThread();
- ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
- ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
- ASAssertUnlocked(__instanceLock__);
-
- // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for
- // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail.
- // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the
- // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed).
- // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer
- // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call.
-
-#if !ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR
- if (![self supportsRangeManagedInterfaceState]) {
- self.interfaceState = ASInterfaceStateNone;
- return;
- }
-#endif
- if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) {
- void(^exitVisibleInterfaceState)(void) = ^{
- // This block intentionally retains self.
- __instanceLock__.lock();
- unsigned isStillInHierarchy = _flags.isInHierarchy;
- BOOL isVisible = ASInterfaceStateIncludesVisible(_pendingInterfaceState);
- ASInterfaceState newState = (_pendingInterfaceState & ~ASInterfaceStateVisible);
- // layer may be thrashed, we need to remember the state so we can reset if it enters in same runloop later.
- _preExitingInterfaceState = _pendingInterfaceState;
- __instanceLock__.unlock();
- if (!isStillInHierarchy && isVisible) {
-#if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR
- if (![self supportsRangeManagedInterfaceState]) {
- newState = ASInterfaceStateNone;
- }
-#endif
- self.interfaceState = newState;
- }
- };
-
- if (!ASCATransactionQueueGet().enabled) {
- dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState);
- } else {
- exitVisibleInterfaceState();
- }
- }
-}
-
-#pragma mark - Interface State
-
-/**
- * We currently only set interface state on nodes in table/collection views. For other nodes, if they are
- * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`.
- */
-- (BOOL)supportsRangeManagedInterfaceState
-{
- MutexLocker l(__instanceLock__);
- return ASHierarchyStateIncludesRangeManaged(_hierarchyState);
-}
-
-- (void)enterInterfaceState:(ASInterfaceState)interfaceState
-{
- if (interfaceState == ASInterfaceStateNone) {
- return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
- }
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
- node.interfaceState |= interfaceState;
- });
-}
-
-- (void)exitInterfaceState:(ASInterfaceState)interfaceState
-{
- if (interfaceState == ASInterfaceStateNone) {
- return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
- }
- ASDisplayNodeLogEvent(self, @"%s %@", sel_getName(_cmd), NSStringFromASInterfaceState(interfaceState));
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
- node.interfaceState &= (~interfaceState);
- });
-}
-
-- (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState
-{
- // Instead of each node in the recursion assuming it needs to schedule itself for display,
- // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set).
- // If our range manager intends for us to be displayed right now, and didn't before, get started!
- BOOL shouldScheduleDisplay = [self supportsRangeManagedInterfaceState] && [self shouldScheduleDisplayWithNewInterfaceState:newInterfaceState];
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
- node.interfaceState = newInterfaceState;
- });
- if (shouldScheduleDisplay) {
- [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
- }
-}
-
-- (ASInterfaceState)interfaceState
-{
- MutexLocker l(__instanceLock__);
- return _interfaceState;
-}
-
-- (void)setInterfaceState:(ASInterfaceState)newState
-{
- if (!ASCATransactionQueueGet().enabled) {
- [self applyPendingInterfaceState:newState];
- } else {
- MutexLocker l(__instanceLock__);
- if (_pendingInterfaceState != newState) {
- _pendingInterfaceState = newState;
- [ASCATransactionQueueGet() enqueue:self];
- }
- }
-}
-
-- (ASInterfaceState)pendingInterfaceState
-{
- MutexLocker l(__instanceLock__);
- return _pendingInterfaceState;
-}
-
-- (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState
-{
- //This method is currently called on the main thread. The assert has been added here because all of the
- //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main.
- ASDisplayNodeAssertMainThread();
-
- // This method manages __instanceLock__ itself, to ensure the lock is not held while didEnter/Exit(.*)State methods are called, thus avoid potential deadlocks
- ASAssertUnlocked(__instanceLock__);
-
- ASInterfaceState oldState = ASInterfaceStateNone;
- ASInterfaceState newState = ASInterfaceStateNone;
- {
- MutexLocker l(__instanceLock__);
- // newPendingState will not be used when ASCATransactionQueue is enabled
- // and use _pendingInterfaceState instead for interfaceState update.
- if (!ASCATransactionQueueGet().enabled) {
- _pendingInterfaceState = newPendingState;
- }
- oldState = _interfaceState;
- newState = _pendingInterfaceState;
- if (newState == oldState) {
- return;
- }
- _interfaceState = newState;
- _preExitingInterfaceState = ASInterfaceStateNone;
- }
-
- // It should never be possible for a node to be visible but not be allowed / expected to display.
- ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState));
-
- // TODO: Trigger asynchronous measurement if it is not already cached or being calculated.
- // if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
- // }
-
- // For the Preload and Display ranges, we don't want to call -clear* if not being managed by a range controller.
- // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop.
- // Still, the interfaceState should be updated to the current state of the node; just don't act on the transition.
-
- // Entered or exited data loading state.
- BOOL nowPreload = ASInterfaceStateIncludesPreload(newState);
- BOOL wasPreload = ASInterfaceStateIncludesPreload(oldState);
-
- if (nowPreload != wasPreload) {
- if (nowPreload) {
- [self _didEnterPreloadState];
- } else {
- // We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller.
- // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop.
- if ([self supportsRangeManagedInterfaceState]) {
- [self _didExitPreloadState];
- }
- }
- }
-
- // Entered or exited contents rendering state.
- BOOL nowDisplay = ASInterfaceStateIncludesDisplay(newState);
- BOOL wasDisplay = ASInterfaceStateIncludesDisplay(oldState);
-
- if (nowDisplay != wasDisplay) {
- if ([self supportsRangeManagedInterfaceState]) {
- if (nowDisplay) {
- // Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
- [self setDisplaySuspended:NO];
- } else {
- [self setDisplaySuspended:YES];
- //schedule clear contents on next runloop
- dispatch_async(dispatch_get_main_queue(), ^{
- __instanceLock__.lock();
- ASInterfaceState interfaceState = _interfaceState;
- __instanceLock__.unlock();
- if (ASInterfaceStateIncludesDisplay(interfaceState) == NO) {
- [self clearContents];
- }
- });
- }
- } else {
- // NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
- // internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
- if (!ASInterfaceStateIncludesVisible(newState)) {
- // Check _implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
- if ([self _implementsDisplay]) {
- if (nowDisplay) {
- [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
- } else {
- [[self asyncLayer] cancelAsyncDisplay];
- //schedule clear contents on next runloop
- dispatch_async(dispatch_get_main_queue(), ^{
- __instanceLock__.lock();
- ASInterfaceState interfaceState = _interfaceState;
- __instanceLock__.unlock();
- if (ASInterfaceStateIncludesDisplay(interfaceState) == NO) {
- [self clearContents];
- }
- });
- }
- }
- }
- }
-
- if (nowDisplay) {
- [self _didEnterDisplayState];
- } else {
- [self _didExitDisplayState];
- }
- }
-
- // Became visible or invisible. When range-managed, this represents literal visibility - at least one pixel
- // is onscreen. If not range-managed, we can't guarantee more than the node being present in an onscreen window.
- BOOL nowVisible = ASInterfaceStateIncludesVisible(newState);
- BOOL wasVisible = ASInterfaceStateIncludesVisible(oldState);
-
- if (nowVisible != wasVisible) {
- if (nowVisible) {
- [self _didEnterVisibleState];
- } else {
- [self _didExitVisibleState];
- }
- }
-
- // Log this change, unless it's just the node going from {} -> {Measure} because that change happens
- // for all cell nodes and it isn't currently meaningful.
- BOOL measureChangeOnly = ((oldState | newState) == ASInterfaceStateMeasureLayout);
- if (!measureChangeOnly) {
- }
-
- ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@", NSStringFromASInterfaceStateChange(oldState, newState));
- [self _interfaceStateDidChange:newState fromState:oldState];
-}
-
-- (void)prepareForCATransactionCommit
-{
- // Apply _pendingInterfaceState actual _interfaceState, note that ASInterfaceStateNone is not used.
- [self applyPendingInterfaceState:ASInterfaceStateNone];
-}
-
-- (void)_interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
-{
- ASAssertUnlocked(__instanceLock__);
- ASDisplayNodeAssertMainThread();
- [self interfaceStateDidChange:newState fromState:oldState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del interfaceStateDidChange:newState fromState:oldState];
- }];
-}
-
-- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState
-{
- BOOL willDisplay = ASInterfaceStateIncludesDisplay(newInterfaceState);
- BOOL nowDisplay = ASInterfaceStateIncludesDisplay(self.interfaceState);
- return willDisplay && (willDisplay != nowDisplay);
-}
-
-- (void)addInterfaceStateDelegate:(id )interfaceStateDelegate
-{
- MutexLocker l(__instanceLock__);
- _hasHadInterfaceStateDelegates = YES;
- for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) {
- if (_interfaceStateDelegates[i] == nil) {
- _interfaceStateDelegates[i] = interfaceStateDelegate;
- return;
- }
- }
- ASDisplayNodeFailAssert(@"Exceeded interface state delegate limit: %d", AS_MAX_INTERFACE_STATE_DELEGATES);
-}
-
-- (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate
-{
- MutexLocker l(__instanceLock__);
- for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) {
- if (_interfaceStateDelegates[i] == interfaceStateDelegate) {
- _interfaceStateDelegates[i] = nil;
- break;
- }
- }
-}
-
-- (BOOL)isVisible
-{
- MutexLocker l(__instanceLock__);
- return ASInterfaceStateIncludesVisible(_interfaceState);
-}
-
-- (void)_didEnterVisibleState
-{
- ASDisplayNodeAssertMainThread();
-
-#if ASDISPLAYNODE_ASSERTIONS_ENABLED
- // Rasterized node's loading state is merged with root node of rasterized tree.
- if (!(self.hierarchyState & ASHierarchyStateRasterized)) {
- ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded before entering visible state.");
- }
-#endif
-
- ASAssertUnlocked(__instanceLock__);
- [self didEnterVisibleState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didEnterVisibleState];
- }];
-
-#if AS_ENABLE_TIPS
- [ASTipsController.shared nodeDidAppear:self];
-#endif
-}
-
-- (void)_didExitVisibleState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- [self didExitVisibleState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didExitVisibleState];
- }];
-}
-
-- (BOOL)isInDisplayState
-{
- MutexLocker l(__instanceLock__);
- return ASInterfaceStateIncludesDisplay(_interfaceState);
-}
-
-- (void)_didEnterDisplayState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- [self didEnterDisplayState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didEnterDisplayState];
- }];
-}
-
-- (void)_didExitDisplayState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- [self didExitDisplayState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didExitDisplayState];
- }];
-}
-
-- (BOOL)isInPreloadState
-{
- MutexLocker l(__instanceLock__);
- return ASInterfaceStateIncludesPreload(_interfaceState);
-}
-
-- (void)setNeedsPreload
-{
- if (self.isInPreloadState) {
- [self recursivelyPreload];
- }
-}
-
-- (void)recursivelyPreload
-{
- ASPerformBlockOnMainThread(^{
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
- [node didEnterPreloadState];
- });
- });
-}
-
-- (void)recursivelyClearPreloadedData
-{
- ASPerformBlockOnMainThread(^{
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
- [node didExitPreloadState];
- });
- });
-}
-
-- (void)_didEnterPreloadState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- [self didEnterPreloadState];
-
- // If this node has ASM enabled and is not yet visible, force a layout pass to apply its applicable pending layout, if any,
- // so that its subnodes are inserted/deleted and start preloading right away.
- //
- // - If it has an up-to-date layout (and subnodes), calling -layoutIfNeeded will be fast.
- //
- // - If it doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur
- // (see -__layout and -_u_measureNodeWithBoundsIfNecessary:). This scenario is uncommon,
- // and running a measurement pass here is a fine trade-off because preloading any time after this point would be late.
-
- if (self.automaticallyManagesSubnodes && !ASActivateExperimentalFeature(ASExperimentalDidEnterPreloadSkipASMLayout)) {
- [self layoutIfNeeded];
- }
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didEnterPreloadState];
- }];
-}
-
-- (void)_didExitPreloadState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
- [self didExitPreloadState];
- [self enumerateInterfaceStateDelegates:^(id del) {
- [del didExitPreloadState];
- }];
-}
-
-- (void)clearContents
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
-
- MutexLocker l(__instanceLock__);
- if (_flags.canClearContentsOfLayer) {
- // No-op if these haven't been created yet, as that guarantees they don't have contents that needs to be released.
- _layer.contents = nil;
- }
-
- _placeholderLayer.contents = nil;
- _placeholderImage = nil;
-}
-
-- (void)recursivelyClearContents
-{
- ASPerformBlockOnMainThread(^{
- ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
- [node clearContents];
- });
- });
-}
-
-- (void)enumerateInterfaceStateDelegates:(void (NS_NOESCAPE ^)(id))block
-{
- ASAssertUnlocked(__instanceLock__);
-
- id dels[AS_MAX_INTERFACE_STATE_DELEGATES];
- int count = 0;
- {
- ASLockScopeSelf();
- // Fast path for non-delegating nodes.
- if (!_hasHadInterfaceStateDelegates) {
- return;
- }
-
- for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) {
- if ((dels[count] = _interfaceStateDelegates[i])) {
- count++;
- }
- }
- }
- for (int i = 0; i < count; i++) {
- block(dels[i]);
- }
-}
-
-#pragma mark - Gesture Recognizing
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // Subclass hook
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // Subclass hook
-}
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // Subclass hook
-}
-
-- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // Subclass hook
-}
-
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
-{
- // This method is only implemented on UIView on iOS 6+.
- ASDisplayNodeAssertMainThread();
-
- // No locking needed as it's main thread only
- UIView *view = _view;
- if (view == nil) {
- return YES;
- }
-
- // If we reach the base implementation, forward up the view hierarchy.
- UIView *superview = view.superview;
- return [superview gestureRecognizerShouldBegin:gestureRecognizer];
-}
-
-- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
-{
- ASDisplayNodeAssertMainThread();
- return [_view hitTest:point withEvent:event];
-}
-
-- (void)setHitTestSlop:(UIEdgeInsets)hitTestSlop
-{
- MutexLocker l(__instanceLock__);
- _hitTestSlop = hitTestSlop;
-}
-
-- (UIEdgeInsets)hitTestSlop
-{
- MutexLocker l(__instanceLock__);
- return _hitTestSlop;
-}
-
-- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
-{
- ASDisplayNodeAssertMainThread();
- UIEdgeInsets slop = self.hitTestSlop;
- if (_view && UIEdgeInsetsEqualToEdgeInsets(slop, UIEdgeInsetsZero)) {
- // Safer to use UIView's -pointInside:withEvent: if we can.
- return [_view pointInside:point withEvent:event];
- } else {
- return CGRectContainsPoint(UIEdgeInsetsInsetRect(self.bounds, slop), point);
- }
-}
-
-
-#pragma mark - Pending View State
-
-- (void)_locked_applyPendingStateToViewOrLayer
-{
- ASDisplayNodeAssertMainThread();
- ASAssertLocked(__instanceLock__);
- ASDisplayNodeAssert(self.nodeLoaded, @"must have a view or layer");
-
- TIME_SCOPED(_debugTimeToApplyPendingState);
-
- // If no view/layer properties were set before the view/layer were created, _pendingViewState will be nil and the default values
- // for the view/layer are still valid.
- [self _locked_applyPendingViewState];
-
- if (_flags.displaySuspended) {
- self._locked_asyncLayer.displaySuspended = YES;
- }
- if (!_flags.displaysAsynchronously) {
- self._locked_asyncLayer.displaysAsynchronously = NO;
- }
-}
-
-- (void)applyPendingViewState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertUnlocked(__instanceLock__);
-
- AS::UniqueLock l(__instanceLock__);
- // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
- // but automatic subnode management would require us to modify the node tree
- // in the background on a loaded node, which isn't currently supported.
- if (_pendingViewState.hasSetNeedsLayout) {
- // Need to unlock before calling setNeedsLayout to avoid deadlocks.
- l.unlock();
- [self __setNeedsLayout];
- l.lock();
- }
-
- [self _locked_applyPendingViewState];
-}
-
-- (void)_locked_applyPendingViewState
-{
- ASDisplayNodeAssertMainThread();
- ASAssertLocked(__instanceLock__);
- ASDisplayNodeAssert([self _locked_isNodeLoaded], @"Expected node to be loaded before applying pending state.");
-
- if (_flags.layerBacked) {
- [_pendingViewState applyToLayer:_layer];
- } else {
- BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked);
- [_pendingViewState applyToView:_view withSpecialPropertiesHandling:specialPropertiesHandling];
- }
-
- // _ASPendingState objects can add up very quickly when adding
- // many nodes. This is especially an issue in large collection views
- // and table views. This needs to be weighed against the cost of
- // reallocing a _ASPendingState. So in range managed nodes we
- // delete the pending state, otherwise we just clear it.
- if (ASHierarchyStateIncludesRangeManaged(_hierarchyState)) {
- _pendingViewState = nil;
- } else {
- [_pendingViewState clearChanges];
- }
-}
-
-// This method has proved helpful in a few rare scenarios, similar to a category extension on UIView, but assumes knowledge of _ASDisplayView.
-// It's considered private API for now and its use should not be encouraged.
-- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy
-{
- ASDisplayNode *supernode = self.supernode;
- while (supernode) {
- if ([supernode isKindOfClass:supernodeClass])
- return supernode;
- supernode = supernode.supernode;
- }
- if (!checkViewHierarchy) {
- return nil;
- }
-
- UIView *view = self.view.superview;
- while (view) {
- ASDisplayNode *viewNode = ((_ASDisplayView *)view).asyncdisplaykit_node;
- if (viewNode) {
- if ([viewNode isKindOfClass:supernodeClass])
- return viewNode;
- }
-
- view = view.superview;
- }
-
- return nil;
-}
-
-#pragma mark - Performance Measurement
-
-- (void)setMeasurementOptions:(ASDisplayNodePerformanceMeasurementOptions)measurementOptions
-{
- MutexLocker l(__instanceLock__);
- _measurementOptions = measurementOptions;
-}
-
-- (ASDisplayNodePerformanceMeasurementOptions)measurementOptions
-{
- MutexLocker l(__instanceLock__);
- return _measurementOptions;
-}
-
-- (ASDisplayNodePerformanceMeasurements)performanceMeasurements
-{
- MutexLocker l(__instanceLock__);
- ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecNumberOfPasses = -1, .layoutSpecTotalTime = NAN, .layoutComputationNumberOfPasses = -1, .layoutComputationTotalTime = NAN };
- if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
- measurements.layoutSpecNumberOfPasses = _layoutSpecNumberOfPasses;
- measurements.layoutSpecTotalTime = _layoutSpecTotalTime;
- }
- if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation) {
- measurements.layoutComputationNumberOfPasses = _layoutComputationNumberOfPasses;
- measurements.layoutComputationTotalTime = _layoutComputationTotalTime;
- }
- return measurements;
-}
-
-#pragma mark - Accessibility
-
-- (void)setIsAccessibilityContainer:(BOOL)isAccessibilityContainer
-{
- MutexLocker l(__instanceLock__);
- _isAccessibilityContainer = isAccessibilityContainer;
-}
-
-- (BOOL)isAccessibilityContainer
-{
- MutexLocker l(__instanceLock__);
- return _isAccessibilityContainer;
-}
-
-- (NSString *)defaultAccessibilityLabel
-{
- return nil;
-}
-
-- (NSString *)defaultAccessibilityHint
-{
- return nil;
-}
-
-- (NSString *)defaultAccessibilityValue
-{
- return nil;
-}
-
-- (NSString *)defaultAccessibilityIdentifier
-{
- return nil;
-}
-
-- (UIAccessibilityTraits)defaultAccessibilityTraits
-{
- return UIAccessibilityTraitNone;
-}
-
-#pragma mark - Debugging (Private)
-
-#if ASEVENTLOG_ENABLE
-- (ASEventLog *)eventLog
-{
- return _eventLog;
-}
-#endif
-
-- (NSMutableArray *)propertiesForDescription
-{
- NSMutableArray *result = [NSMutableArray array];
- ASPushMainThreadAssertionsDisabled();
-
- NSString *debugName = self.debugName;
- if (debugName.length > 0) {
- [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(debugName) }];
- }
-
- NSString *axId = self.accessibilityIdentifier;
- if (axId.length > 0) {
- [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(axId) }];
- }
-
- ASPopMainThreadAssertionsDisabled();
- return result;
-}
-
-- (NSMutableArray *)propertiesForDebugDescription
-{
- NSMutableArray *result = [NSMutableArray array];
-
- if (self.debugName.length > 0) {
- [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}];
- }
- if (self.accessibilityIdentifier.length > 0) {
- [result addObject:@{ @"axId": ASStringWithQuotesIfMultiword(self.accessibilityIdentifier) }];
- }
-
- CGRect windowFrame = [self _frameInWindow];
- if (CGRectIsNull(windowFrame) == NO) {
- [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
- }
-
- // Attempt to find view controller.
- // Note that the convenience method asdk_associatedViewController has an assertion
- // that it's run on main. Since this is a debug method, let's bypass the assertion
- // and run up the chain ourselves.
- if (_view != nil) {
- for (UIResponder *responder in [_view asdk_responderChainEnumerator]) {
- UIViewController *vc = ASDynamicCast(responder, UIViewController);
- if (vc) {
- [result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }];
- break;
- }
- }
- }
-
- if (_view != nil) {
- [result addObject:@{ @"alpha" : @(_view.alpha) }];
- [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }];
- } else if (_layer != nil) {
- [result addObject:@{ @"alpha" : @(_layer.opacity) }];
- [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_layer.frame] }];
- } else if (_pendingViewState != nil) {
- [result addObject:@{ @"alpha" : @(_pendingViewState.alpha) }];
- [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_pendingViewState.frame] }];
- }
-#ifndef MINIMAL_ASDK
- // Check supernode so that if we are a cell node we don't find self.
- ASCellNode *cellNode = [self supernodeOfClass:[ASCellNode class] includingSelf:NO];
- if (cellNode != nil) {
- [result addObject:@{ @"cellNode" : ASObjectDescriptionMakeTiny(cellNode) }];
- }
-#endif
-
- [result addObject:@{ @"interfaceState" : NSStringFromASInterfaceState(self.interfaceState)} ];
-
- if (_view != nil) {
- [result addObject:@{ @"view" : ASObjectDescriptionMakeTiny(_view) }];
- } else if (_layer != nil) {
- [result addObject:@{ @"layer" : ASObjectDescriptionMakeTiny(_layer) }];
- } else if (_viewClass != nil) {
- [result addObject:@{ @"viewClass" : _viewClass }];
- } else if (_layerClass != nil) {
- [result addObject:@{ @"layerClass" : _layerClass }];
- } else if (_viewBlock != nil) {
- [result addObject:@{ @"viewBlock" : _viewBlock }];
- } else if (_layerBlock != nil) {
- [result addObject:@{ @"layerBlock" : _layerBlock }];
- }
-
-#if TIME_DISPLAYNODE_OPS
- NSString *creationTypeString = [NSString stringWithFormat:@"cr8:%.2lfms dl:%.2lfms ap:%.2lfms ad:%.2lfms", 1000 * _debugTimeToCreateView, 1000 * _debugTimeForDidLoad, 1000 * _debugTimeToApplyPendingState, 1000 * _debugTimeToAddSubnodeViews];
- [result addObject:@{ @"creationTypeString" : creationTypeString }];
-#endif
-
- return result;
-}
-
-- (NSString *)description
-{
- return ASObjectDescriptionMake(self, [self propertiesForDescription]);
-}
-
-- (NSString *)debugDescription
-{
- ASPushMainThreadAssertionsDisabled();
- const auto result = ASObjectDescriptionMake(self, [self propertiesForDebugDescription]);
- ASPopMainThreadAssertionsDisabled();
- return result;
-}
-
-// This should only be called for debugging. It's not thread safe and it doesn't assert.
-// NOTE: Returns CGRectNull if the node isn't in a hierarchy.
-- (CGRect)_frameInWindow
-{
- if (self.isNodeLoaded == NO || self.isInHierarchy == NO) {
- return CGRectNull;
- }
-
- if (self.layerBacked) {
- CALayer *rootLayer = _layer;
- CALayer *nextLayer = nil;
- while ((nextLayer = rootLayer.superlayer) != nil) {
- rootLayer = nextLayer;
- }
-
- return [_layer convertRect:self.threadSafeBounds toLayer:rootLayer];
- } else {
- return [_view convertRect:self.threadSafeBounds toView:nil];
- }
-}
-
-@end
-
-#pragma mark - ASDisplayNode (Debugging)
-
-@implementation ASDisplayNode (Debugging)
-
-+ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore
-{
- storesUnflattenedLayouts.store(shouldStore);
-}
-
-+ (BOOL)shouldStoreUnflattenedLayouts
-{
- return storesUnflattenedLayouts.load();
-}
-
-- (ASLayout *)unflattenedCalculatedLayout
-{
- MutexLocker l(__instanceLock__);
- return _unflattenedLayout;
-}
-
-+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses
-{
- suppressesInvalidCollectionUpdateExceptions.store(suppresses);
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-implementations"
-+ (BOOL)suppressesInvalidCollectionUpdateExceptions
-{
- return suppressesInvalidCollectionUpdateExceptions.load();
-}
-#pragma clang diagnostic pop
-
-- (NSString *)displayNodeRecursiveDescription
-{
- return [self _recursiveDescriptionHelperWithIndent:@""];
-}
-
-- (NSString *)_recursiveDescriptionHelperWithIndent:(NSString *)indent
-{
- NSMutableString *subtree = [[[indent stringByAppendingString:self.debugDescription] stringByAppendingString:@"\n"] mutableCopy];
- for (ASDisplayNode *n in self.subnodes) {
- [subtree appendString:[n _recursiveDescriptionHelperWithIndent:[indent stringByAppendingString:@" | "]]];
- }
- return subtree;
-}
-
-- (NSString *)detailedLayoutDescription
-{
- ASPushMainThreadAssertionsDisabled();
- MutexLocker l(__instanceLock__);
- const auto props = [[NSMutableArray alloc] init];
-
- [props addObject:@{ @"layoutVersion": @(_layoutVersion.load()) }];
- [props addObject:@{ @"bounds": [NSValue valueWithCGRect:self.bounds] }];
-
- if (_calculatedDisplayNodeLayout.layout) {
- [props addObject:@{ @"calculatedLayout": _calculatedDisplayNodeLayout.layout }];
- [props addObject:@{ @"calculatedVersion": @(_calculatedDisplayNodeLayout.version) }];
- [props addObject:@{ @"calculatedConstrainedSize" : NSStringFromASSizeRange(_calculatedDisplayNodeLayout.constrainedSize) }];
- if (_calculatedDisplayNodeLayout.requestedLayoutFromAbove) {
- [props addObject:@{ @"calculatedRequestedLayoutFromAbove": @"YES" }];
- }
- }
- if (_pendingDisplayNodeLayout.layout) {
- [props addObject:@{ @"pendingLayout": _pendingDisplayNodeLayout.layout }];
- [props addObject:@{ @"pendingVersion": @(_pendingDisplayNodeLayout.version) }];
- [props addObject:@{ @"pendingConstrainedSize" : NSStringFromASSizeRange(_pendingDisplayNodeLayout.constrainedSize) }];
- if (_pendingDisplayNodeLayout.requestedLayoutFromAbove) {
- [props addObject:@{ @"pendingRequestedLayoutFromAbove": (id)kCFNull }];
- }
- }
-
- ASPopMainThreadAssertionsDisabled();
- return ASObjectDescriptionMake(self, props);
-}
-
-@end
-
-#pragma mark - ASDisplayNode UIKit / CA Categories
-
-// We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to
-
-static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
-
-@implementation UIView (ASDisplayNodeInternal)
-
-- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
-{
- ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node];
- objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the view.
-}
-
-- (ASDisplayNode *)asyncdisplaykit_node
-{
- ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
- return weakProxy.target;
-}
-
-@end
-
-@implementation CALayer (ASDisplayNodeInternal)
-
-- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
-{
- ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node];
- objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the layer.
-}
-
-- (ASDisplayNode *)asyncdisplaykit_node
-{
- ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
- return weakProxy.target;
-}
-
-@end
-
-@implementation UIView (AsyncDisplayKit)
-
-- (void)addSubnode:(ASDisplayNode *)subnode
-{
- if (subnode.layerBacked) {
- // Call -addSubnode: so that we use the asyncdisplaykit_node path if possible.
- [self.layer addSubnode:subnode];
- } else {
- ASDisplayNode *selfNode = self.asyncdisplaykit_node;
- if (selfNode) {
- [selfNode addSubnode:subnode];
- } else {
- if (subnode.supernode) {
- [subnode removeFromSupernode];
- }
- [self addSubview:subnode.view];
- }
- }
-}
-
-@end
-
-@implementation CALayer (AsyncDisplayKit)
-
-- (void)addSubnode:(ASDisplayNode *)subnode
-{
- ASDisplayNode *selfNode = self.asyncdisplaykit_node;
- if (selfNode) {
- [selfNode addSubnode:subnode];
- } else {
- if (subnode.supernode) {
- [subnode removeFromSupernode];
- }
- [self addSublayer:subnode.layer];
- }
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNodeExtras.mm b/submodules/AsyncDisplayKit/Source/ASDisplayNodeExtras.mm
deleted file mode 100644
index b3a56f4697..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNodeExtras.mm
+++ /dev/null
@@ -1,338 +0,0 @@
-//
-// ASDisplayNodeExtras.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import "ASDisplayNodeInternal.h"
-#import
-#import
-
-#import
-#import
-
-void ASPerformMainThreadDeallocation(id _Nullable __strong * _Nonnull objectPtr) {
- /**
- * UIKit components must be deallocated on the main thread. We use this shared
- * run loop queue to gradually deallocate them across many turns of the main run loop.
- */
- static ASRunLoopQueue *queue;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:nil];
- queue.batchSize = 10;
- });
-
- if (objectPtr != NULL && *objectPtr != nil) {
- // TODO: If ASRunLoopQueue supported an "unsafe_unretained" mode, we could
- // transfer the caller's +1 into it and save the retain/release pair.
-
- // Lock queue while enqueuing and releasing, so that there's no risk
- // that the queue will release before we get a chance to release.
- [queue lock];
- [queue enqueue:*objectPtr]; // Retain, +1
- *objectPtr = nil; // Release, +0
- [queue unlock]; // (After queue drains), release, -1
- }
-}
-
-void _ASSetDebugNames(Class _Nonnull owningClass, NSString * _Nonnull names, ASDisplayNode * _Nullable object, ...)
-{
- NSString *owningClassName = NSStringFromClass(owningClass);
- NSArray *nameArray = [names componentsSeparatedByString:@", "];
- va_list args;
- va_start(args, object);
- NSInteger i = 0;
- for (ASDisplayNode *node = object; node != nil; node = va_arg(args, id), i++) {
- NSMutableString *symbolName = [nameArray[i] mutableCopy];
- // Remove any `self.` or `_` prefix
- [symbolName replaceOccurrencesOfString:@"self." withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, symbolName.length)];
- [symbolName replaceOccurrencesOfString:@"_" withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, symbolName.length)];
- node.debugName = [NSString stringWithFormat:@"%@.%@", owningClassName, symbolName];
- }
- ASDisplayNodeCAssert(nameArray.count == i, @"Malformed call to ASSetDebugNames: %@", names);
- va_end(args);
-}
-
-ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window)
-{
- ASDisplayNodeCAssert(![displayNode isLayerBacked], @"displayNode must not be layer backed as it may have a nil window");
- if (displayNode && [displayNode supportsRangeManagedInterfaceState]) {
- // Directly clear the visible bit if we are not in a window. This means that the interface state is,
- // if not already, about to be set to invisible as it is not possible for an element to be visible
- // while outside of a window.
- ASInterfaceState interfaceState = displayNode.pendingInterfaceState;
- return (window == nil ? (interfaceState &= (~ASInterfaceStateVisible)) : interfaceState);
- } else {
- // For not range managed nodes we might be on our own to try to guess if we're visible.
- return (window == nil ? ASInterfaceStateNone : (ASInterfaceStateVisible | ASInterfaceStateDisplay));
- }
-}
-
-ASDisplayNode *ASLayerToDisplayNode(CALayer *layer)
-{
- return layer.asyncdisplaykit_node;
-}
-
-ASDisplayNode *ASViewToDisplayNode(UIView *view)
-{
- return view.asyncdisplaykit_node;
-}
-
-void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node))
-{
- if (!node) {
- ASDisplayNodeCAssertNotNil(layer, @"Cannot recursively perform with nil node and nil layer");
- ASDisplayNodeCAssertMainThread();
- node = ASLayerToDisplayNode(layer);
- }
-
- if (node) {
- block(node);
- }
- if (traverseSublayers && !layer && [node isNodeLoaded] && ASDisplayNodeThreadIsMain()) {
- layer = node.layer;
- }
-
- if (traverseSublayers && layer && node.rasterizesSubtree == NO) {
- /// NOTE: The docs say `sublayers` returns a copy, but it does not.
- /// See: http://stackoverflow.com/questions/14854480/collection-calayerarray-0x1ed8faa0-was-mutated-while-being-enumerated
- for (CALayer *sublayer in [[layer sublayers] copy]) {
- ASDisplayNodePerformBlockOnEveryNode(sublayer, nil, traverseSublayers, block);
- }
- } else if (node) {
- for (ASDisplayNode *subnode in [node subnodes]) {
- ASDisplayNodePerformBlockOnEveryNode(nil, subnode, traverseSublayers, block);
- }
- }
-}
-
-void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node))
-{
- // Queue used to keep track of subnodes while traversing this layout in a BFS fashion.
- std::queue queue;
- queue.push(node);
-
- while (!queue.empty()) {
- node = queue.front();
- queue.pop();
-
- block(node);
-
- // Add all subnodes to process in next step
- for (ASDisplayNode *subnode in node.subnodes) {
- queue.push(subnode);
- }
- }
-}
-
-void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node))
-{
- for (ASDisplayNode *subnode in node.subnodes) {
- ASDisplayNodePerformBlockOnEveryNode(nil, subnode, YES, block);
- }
-}
-
-ASDisplayNode *ASDisplayNodeFindFirstSupernode(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node))
-{
- // This function has historically started with `self` but the name suggests
- // that it wouldn't. Perhaps we should change the behavior.
- for (ASDisplayNode *ancestor in node.supernodesIncludingSelf) {
- if (block(ancestor)) {
- return ancestor;
- }
- }
- return nil;
-}
-
-__kindof ASDisplayNode *ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c)
-{
- // This function has historically started with `self` but the name suggests
- // that it wouldn't. Perhaps we should change the behavior.
- return [start supernodeOfClass:c includingSelf:YES];
-}
-
-static void _ASCollectDisplayNodes(NSMutableArray *array, CALayer *layer)
-{
- ASDisplayNode *node = ASLayerToDisplayNode(layer);
-
- if (nil != node) {
- [array addObject:node];
- }
-
- for (CALayer *sublayer in layer.sublayers)
- _ASCollectDisplayNodes(array, sublayer);
-}
-
-NSArray *ASCollectDisplayNodes(ASDisplayNode *node)
-{
- NSMutableArray *list = [[NSMutableArray alloc] init];
- for (CALayer *sublayer in node.layer.sublayers) {
- _ASCollectDisplayNodes(list, sublayer);
- }
- return list;
-}
-
-#pragma mark - Find all subnodes
-
-static void _ASDisplayNodeFindAllSubnodes(NSMutableArray *array, ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node))
-{
- if (!node)
- return;
-
- for (ASDisplayNode *subnode in node.subnodes) {
- if (block(subnode)) {
- [array addObject:subnode];
- }
-
- _ASDisplayNodeFindAllSubnodes(array, subnode, block);
- }
-}
-
-NSArray *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node))
-{
- NSMutableArray *list = [[NSMutableArray alloc] init];
- _ASDisplayNodeFindAllSubnodes(list, start, block);
- return list;
-}
-
-NSArray<__kindof ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c)
-{
- return ASDisplayNodeFindAllSubnodes(start, ^(ASDisplayNode *n) {
- return [n isKindOfClass:c];
- });
-}
-
-#pragma mark - Find first subnode
-
-static ASDisplayNode *_ASDisplayNodeFindFirstNode(ASDisplayNode *startNode, BOOL includeStartNode, BOOL (^block)(ASDisplayNode *node))
-{
- for (ASDisplayNode *subnode in startNode.subnodes) {
- ASDisplayNode *foundNode = _ASDisplayNodeFindFirstNode(subnode, YES, block);
- if (foundNode) {
- return foundNode;
- }
- }
-
- if (includeStartNode && block(startNode))
- return startNode;
-
- return nil;
-}
-
-__kindof ASDisplayNode *ASDisplayNodeFindFirstNode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node))
-{
- return _ASDisplayNodeFindFirstNode(startNode, YES, block);
-}
-
-__kindof ASDisplayNode *ASDisplayNodeFindFirstSubnode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node))
-{
- return _ASDisplayNodeFindFirstNode(startNode, NO, block);
-}
-
-__kindof ASDisplayNode *ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c)
-{
- return ASDisplayNodeFindFirstSubnode(start, ^(ASDisplayNode *n) {
- return [n isKindOfClass:c];
- });
-}
-
-static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possibleAncestor, ASDisplayNode *possibleDescendant)
-{
- ASDisplayNode *supernode = possibleDescendant;
- while (supernode) {
- if (supernode == possibleAncestor) {
- return YES;
- }
- supernode = supernode.supernode;
- }
-
- return NO;
-}
-
-UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer)
-{
- UIView *view = ASFindClosestViewOfLayer(layer);
- if (UIWindow *window = ASDynamicCast(view, UIWindow)) {
- return window;
- } else {
- return view.window;
- }
-}
-
-UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer)
-{
- while (layer != nil) {
- if (UIView *view = ASDynamicCast(layer.delegate, UIView)) {
- return view;
- }
- layer = layer.superlayer;
- }
- return nil;
-}
-
-ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2)
-{
- ASDisplayNode *possibleAncestor = node1;
- while (possibleAncestor) {
- if (_ASDisplayNodeIsAncestorOfDisplayNode(possibleAncestor, node2)) {
- break;
- }
- possibleAncestor = possibleAncestor.supernode;
- }
-
- ASDisplayNodeCAssertNotNil(possibleAncestor, @"Could not find a common ancestor between node1: %@ and node2: %@", node1, node2);
- return possibleAncestor;
-}
-
-ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node)
-{
- // node <- supernode on each loop
- // previous <- node on each loop where node is not nil
- // previous is the final non-nil value of supernode, i.e. the root node
- ASDisplayNode *previousNode = node;
- while ((node = [node supernode])) {
- previousNode = node;
- }
- return previousNode;
-}
-
-#pragma mark - Placeholders
-
-UIColor *ASDisplayNodeDefaultPlaceholderColor()
-{
- static UIColor *defaultPlaceholderColor;
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- defaultPlaceholderColor = [UIColor colorWithWhite:0.95 alpha:1.0];
- });
- return defaultPlaceholderColor;
-}
-
-UIColor *ASDisplayNodeDefaultTintColor()
-{
- static UIColor *defaultTintColor;
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0];
- });
- return defaultTintColor;
-}
-
-#pragma mark - Hierarchy Notifications
-
-void ASDisplayNodeDisableHierarchyNotifications(ASDisplayNode *node)
-{
- [node __incrementVisibilityNotificationsDisabled];
-}
-
-void ASDisplayNodeEnableHierarchyNotifications(ASDisplayNode *node)
-{
- [node __decrementVisibilityNotificationsDisabled];
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNodeInternal.h b/submodules/AsyncDisplayKit/Source/ASDisplayNodeInternal.h
deleted file mode 100644
index 88b268c696..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNodeInternal.h
+++ /dev/null
@@ -1,407 +0,0 @@
-//
-// ASDisplayNodeInternal.h
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-//
-// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode.
-// These methods must never be called or overridden by other classes.
-//
-
-#import
-#import
-#import
-#import
-#import "ASLayoutTransition.h"
-#import
-#import
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol _ASDisplayLayerDelegate;
-@class _ASDisplayLayer;
-@class _ASPendingState;
-@class ASNodeController;
-struct ASDisplayNodeFlags;
-
-BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
-BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked);
-
-/// Get the pending view state for the node, creating one if needed.
-_ASPendingState * ASDisplayNodeGetPendingState(ASDisplayNode * node);
-
-typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
-{
- ASDisplayNodeMethodOverrideNone = 0,
- ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
- ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
- ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2,
- ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3,
- ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4,
- ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5,
- ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6,
- ASDisplayNodeMethodOverrideCanBecomeFirstResponder= 1 << 7,
- ASDisplayNodeMethodOverrideBecomeFirstResponder = 1 << 8,
- ASDisplayNodeMethodOverrideCanResignFirstResponder= 1 << 9,
- ASDisplayNodeMethodOverrideResignFirstResponder = 1 << 10,
- ASDisplayNodeMethodOverrideIsFirstResponder = 1 << 11,
-};
-
-typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags)
-{
- Synchronous = 1 << 0,
- YogaLayoutInProgress = 1 << 1,
-};
-
-// Can be called without the node's lock. Client is responsible for thread safety.
-#define _loaded(node) (node->_layer != nil)
-
-#define checkFlag(flag) ((_atomicFlags.load() & flag) != 0)
-// Returns the old value of the flag as a BOOL.
-#define setFlag(flag, x) (((x ? _atomicFlags.fetch_or(flag) \
- : _atomicFlags.fetch_and(~flag)) & flag) != 0)
-
-AS_EXTERN NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
-AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;
-
-// Allow 2^n increments of begin disabling hierarchy notifications
-#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4
-
-#define TIME_DISPLAYNODE_OPS 0 // If you're using this information frequently, try: (DEBUG || PROFILE)
-
-#define NUM_CLIP_CORNER_LAYERS 4
-
-@interface ASDisplayNode () <_ASTransitionContextCompletionDelegate>
-{
-@package
- AS::RecursiveMutex __instanceLock__;
-
- _ASPendingState *_pendingViewState;
- ASInterfaceState _pendingInterfaceState;
- ASInterfaceState _preExitingInterfaceState;
-
- UIView *_view;
- CALayer *_layer;
-
- std::atomic _atomicFlags;
-
- struct ASDisplayNodeFlags {
- // public properties
- unsigned viewEverHadAGestureRecognizerAttached:1;
- unsigned layerBacked:1;
- unsigned displaysAsynchronously:1;
- unsigned rasterizesSubtree:1;
- unsigned shouldBypassEnsureDisplay:1;
- unsigned displaySuspended:1;
- unsigned shouldAnimateSizeChanges:1;
-
- // Wrapped view handling
-
- // The layer contents should not be cleared in case the node is wrapping a UIImageView.UIImageView is specifically
- // optimized for performance and does not use the usual way to provide the contents of the CALayer via the
- // CALayerDelegate method that backs the UIImageView.
- unsigned canClearContentsOfLayer:1;
-
- // Prevent calling setNeedsDisplay on a layer that backs a UIImageView. Usually calling setNeedsDisplay on a CALayer
- // triggers a recreation of the contents of layer unfortunately calling it on a CALayer that backs a UIImageView
- // it goes through the normal flow to assign the contents to a layer via the CALayerDelegate methods. Unfortunately
- // UIImageView does not do recreate the layer contents the usual way, it actually does not implement some of the
- // methods at all instead it throws away the contents of the layer and nothing will show up.
- unsigned canCallSetNeedsDisplayOfLayer:1;
-
- unsigned implementsDrawRect:1;
- unsigned implementsImageDisplay:1;
- unsigned implementsDrawParameters:1;
-
- // internal state
- unsigned isEnteringHierarchy:1;
- unsigned isExitingHierarchy:1;
- unsigned isInHierarchy:1;
- unsigned visibilityNotificationsDisabled:VISIBILITY_NOTIFICATIONS_DISABLED_BITS;
- unsigned isDeallocating:1;
- } _flags;
-
-@protected
- ASDisplayNode * __weak _supernode;
- NSMutableArray *_subnodes;
-
- ASNodeController *_strongNodeController;
- __weak ASNodeController *_weakNodeController;
-
- // Set this to nil whenever you modify _subnodes
- NSArray *_cachedSubnodes;
-
- std::atomic_uint _displaySentinel;
-
- // This is the desired contentsScale, not the scale at which the layer's contents should be displayed
- CGFloat _contentsScaleForDisplay;
- ASDisplayNodeMethodOverrides _methodOverrides;
-
- UIEdgeInsets _hitTestSlop;
-
-#if ASEVENTLOG_ENABLE
- ASEventLog *_eventLog;
-#endif
-
-
- // Layout support
- ASLayoutElementStyle *_style;
- std::atomic _primitiveTraitCollection;
-
- // Layout Spec
- ASLayoutSpecBlock _layoutSpecBlock;
- NSString *_debugName;
-
-#if YOGA
- // Only ASDisplayNodes are supported in _yogaChildren currently. This means that it is necessary to
- // create ASDisplayNodes to make a stack layout when using Yoga.
- // However, the implementation is mostly ready for id , with a few areas requiring updates.
- NSMutableArray *_yogaChildren;
- __weak ASDisplayNode *_yogaParent;
- ASLayout *_yogaCalculatedLayout;
-#endif
-
- // Automatically manages subnodes
- BOOL _automaticallyManagesSubnodes; // Main thread only
-
- // Layout Transition
- _ASTransitionContext *_pendingLayoutTransitionContext;
- NSTimeInterval _defaultLayoutTransitionDuration;
- NSTimeInterval _defaultLayoutTransitionDelay;
- UIViewAnimationOptions _defaultLayoutTransitionOptions;
-
- std::atomic _transitionID;
- std::atomic _pendingTransitionID;
- ASLayoutTransition *_pendingLayoutTransition;
- ASDisplayNodeLayout _calculatedDisplayNodeLayout;
- ASDisplayNodeLayout _pendingDisplayNodeLayout;
-
- /// Sentinel for layout data. Incremented when we get -setNeedsLayout / -invalidateCalculatedLayout.
- /// Starts at 1.
- std::atomic _layoutVersion;
-
-
- // Layout Spec performance measurement
- ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
- NSTimeInterval _layoutSpecTotalTime;
- NSInteger _layoutSpecNumberOfPasses;
- NSTimeInterval _layoutComputationTotalTime;
- NSInteger _layoutComputationNumberOfPasses;
-
-
- // View Loading
- ASDisplayNodeViewBlock _viewBlock;
- ASDisplayNodeLayerBlock _layerBlock;
- NSMutableArray *_onDidLoadBlocks;
- Class _viewClass; // nil -> _ASDisplayView
- Class _layerClass; // nil -> _ASDisplayLayer
-
-
- // Placeholder support
- UIImage *_placeholderImage;
- BOOL _placeholderEnabled;
- CALayer *_placeholderLayer;
-
- // keeps track of nodes/subnodes that have not finished display, used with placeholders
- ASWeakSet *_pendingDisplayNodes;
-
-
- // Corner Radius support
- CGFloat _cornerRadius;
- ASCornerRoundingType _cornerRoundingType;
- CALayer *_clipCornerLayers[NUM_CLIP_CORNER_LAYERS];
-
- ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext;
- ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext;
-
-
- // Accessibility support
- BOOL _isAccessibilityElement;
- NSString *_accessibilityLabel;
- NSAttributedString *_accessibilityAttributedLabel;
- NSString *_accessibilityHint;
- NSAttributedString *_accessibilityAttributedHint;
- NSString *_accessibilityValue;
- NSAttributedString *_accessibilityAttributedValue;
- UIAccessibilityTraits _accessibilityTraits;
- CGRect _accessibilityFrame;
- NSString *_accessibilityLanguage;
- BOOL _accessibilityElementsHidden;
- BOOL _accessibilityViewIsModal;
- BOOL _shouldGroupAccessibilityChildren;
- NSString *_accessibilityIdentifier;
- UIAccessibilityNavigationStyle _accessibilityNavigationStyle;
- NSArray *_accessibilityHeaderElements;
- CGPoint _accessibilityActivationPoint;
- UIBezierPath *_accessibilityPath;
- BOOL _isAccessibilityContainer;
-
-
- // Safe Area support
- // These properties are used on iOS 10 and lower, where safe area is not supported by UIKit.
- UIEdgeInsets _fallbackSafeAreaInsets;
- BOOL _fallbackInsetsLayoutMarginsFromSafeArea;
-
- BOOL _automaticallyRelayoutOnSafeAreaChanges;
- BOOL _automaticallyRelayoutOnLayoutMarginsChanges;
-
- BOOL _isViewControllerRoot;
-
-
-#pragma mark - ASDisplayNode (Debugging)
- ASLayout *_unflattenedLayout;
-
-#if TIME_DISPLAYNODE_OPS
-@public
- NSTimeInterval _debugTimeToCreateView;
- NSTimeInterval _debugTimeToApplyPendingState;
- NSTimeInterval _debugTimeToAddSubnodeViews;
- NSTimeInterval _debugTimeForDidLoad;
-#endif
-
- /// Fast path: tells whether we've ever had an interface state delegate before.
- BOOL _hasHadInterfaceStateDelegates;
- __weak id _interfaceStateDelegates[AS_MAX_INTERFACE_STATE_DELEGATES];
-}
-
-+ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node;
-
-/// The _ASDisplayLayer backing the node, if any.
-@property (nullable, nonatomic, readonly) _ASDisplayLayer *asyncLayer;
-
-/// Bitmask to check which methods an object overrides.
-- (ASDisplayNodeMethodOverrides)methodOverrides;
-
-/**
- * Invoked before a call to setNeedsLayout to the underlying view
- */
-- (void)__setNeedsLayout;
-
-/**
- * Invoked after a call to setNeedsDisplay to the underlying view
- */
-- (void)__setNeedsDisplay;
-
-/**
- * Called whenever the node needs to layout its subnodes and, if it's already loaded, its subviews. Executes the layout pass for the node
- *
- * This method is thread-safe but asserts thread affinity.
- */
-- (void)__layout;
-
-/**
- * Internal method to add / replace / insert subnode and remove from supernode without checking if
- * node has automaticallyManagesSubnodes set to YES.
- */
-- (void)_addSubnode:(ASDisplayNode *)subnode;
-- (void)_replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode;
-- (void)_insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below;
-- (void)_insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above;
-- (void)_insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx;
-- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode;
-- (void)_removeFromSupernode;
-
-// Private API for helper functions / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this.
-- (BOOL)__visibilityNotificationsDisabled;
-- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled;
-- (void)__incrementVisibilityNotificationsDisabled;
-- (void)__decrementVisibilityNotificationsDisabled;
-
-// Helper methods for UIResponder forwarding
-- (BOOL)__canBecomeFirstResponder;
-- (BOOL)__becomeFirstResponder;
-- (BOOL)__canResignFirstResponder;
-- (BOOL)__resignFirstResponder;
-- (BOOL)__isFirstResponder;
-
-/// Helper method to summarize whether or not the node run through the display process
-- (BOOL)_implementsDisplay;
-
-/// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
-- (void)displayImmediately;
-
-/// Refreshes any precomposited or drawn clip corners, setting up state as required to transition radius or rounding type.
-- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType cornerRadius:(CGFloat)newCornerRadius;
-
-/// Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses.
-- (instancetype)initWithViewClass:(Class)viewClass;
-
-/// Alternative initialiser for backing with a custom layer class. Supports asynchronous display with _ASDisplayLayer subclasses.
-- (instancetype)initWithLayerClass:(Class)layerClass;
-
-@property (nonatomic) CGFloat contentsScaleForDisplay;
-
-- (void)applyPendingViewState;
-
-/**
- * Makes a local copy of the interface state delegates then calls the block on each.
- *
- * Lock is not held during block invocation. Method must not be called with the lock held.
- */
-- (void)enumerateInterfaceStateDelegates:(void(NS_NOESCAPE ^)(id delegate))block;
-
-/**
- * // TODO: NOT YET IMPLEMENTED
- *
- * @abstract Prevents interface state changes from affecting the node, until disabled.
- *
- * @discussion Useful to avoid flashing after removing a node from the hierarchy and re-adding it.
- * Removing a node from the hierarchy will cause it to exit the Display state, clearing its contents.
- * For some animations, it's desirable to be able to remove a node without causing it to re-display.
- * Once re-enabled, the interface state will be updated to the same value it would have been.
- *
- * @see ASInterfaceState
- */
-@property (nonatomic) BOOL interfaceStateSuspended;
-
-/**
- * This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
- * but it's considered private API for now and its use should not be encouraged.
- * @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode.
- * If YES, this method must be called on the main thread and the node must not be layer-backed.
- */
-- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
-
-/**
- * Whether this node rasterizes its descendants. See -enableSubtreeRasterization.
- */
-@property (readonly) BOOL rasterizesSubtree;
-
-/**
- * Called if a gesture recognizer was attached to an _ASDisplayView
- */
-- (void)nodeViewDidAddGestureRecognizer;
-
-// Recalculates fallbackSafeAreaInsets for the subnodes
-- (void)_fallbackUpdateSafeAreaOnChildren;
-
-@end
-
-@interface ASDisplayNode (InternalPropertyBridge)
-
-@property (nonatomic) CGFloat layerCornerRadius;
-
-- (BOOL)_locked_insetsLayoutMarginsFromSafeArea;
-
-@end
-
-@interface ASDisplayNode (ASLayoutElementPrivate)
-
-/**
- * Returns the internal style object or creates a new if no exists. Need to be called with lock held.
- */
-- (ASLayoutElementStyle *)_locked_style;
-
-/**
- * Returns the current layout element. Need to be called with lock held.
- */
-- (id)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/submodules/AsyncDisplayKit/Source/ASDisplayNodeLayout.h b/submodules/AsyncDisplayKit/Source/ASDisplayNodeLayout.h
deleted file mode 100644
index af905a813a..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASDisplayNodeLayout.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// ASDisplayNodeLayout.h
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#pragma once
-
-#import
-#import
-
-@class ASLayout;
-
-/*
- * Represents a connection between an ASLayout and a ASDisplayNode
- * ASDisplayNode uses this to store additional information that are necessary besides the layout
- */
-struct ASDisplayNodeLayout {
- ASLayout *layout;
- ASSizeRange constrainedSize;
- CGSize parentSize;
- BOOL requestedLayoutFromAbove;
- NSUInteger version;
-
- /*
- * Create a new display node layout with
- * @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize:
- * @param constrainedSize Constrained size used to create the layout
- * @param parentSize Parent size used to create the layout
- * @param version The version of the source layout data – see ASDisplayNode's _layoutVersion.
- */
- ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version)
- : layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), version(version) {};
-
- /*
- * Creates a layout without any layout associated. By default this display node layout is dirty.
- */
- ASDisplayNodeLayout()
- : layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), version(0) {};
-
- /**
- * Returns whether this is valid for a given version
- */
- BOOL isValid(NSUInteger versionArg) {
- return layout != nil && version >= versionArg;
- }
-
- /**
- * Returns whether this is valid for a given constrained size, parent size, and version
- */
- BOOL isValid(ASSizeRange theConstrainedSize, CGSize theParentSize, NSUInteger versionArg) {
- return isValid(versionArg)
- && CGSizeEqualToSize(parentSize, theParentSize)
- && ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize);
- }
-};
diff --git a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm b/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm
deleted file mode 100644
index 87baeb09ac..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm
+++ /dev/null
@@ -1,1172 +0,0 @@
-//
-// ASEditableTextNode.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-#import
-
-#import
-#import
-#import
-#import "ASTextNodeWordKerner.h"
-#import
-
-@implementation ASEditableTextNodeTargetForAction
-
-- (instancetype)initWithTarget:(id _Nullable)target {
- self = [super init];
- if (self != nil) {
- _target = target;
- }
- return self;
-}
-
-@end
-
-/**
- @abstract Object to hold UITextView's pending UITextInputTraits
-**/
-@interface _ASTextInputTraitsPendingState : NSObject
-
-@property UITextAutocapitalizationType autocapitalizationType;
-@property UITextAutocorrectionType autocorrectionType;
-@property UITextSpellCheckingType spellCheckingType;
-@property UIKeyboardAppearance keyboardAppearance;
-@property UIKeyboardType keyboardType;
-@property UIReturnKeyType returnKeyType;
-@property BOOL enablesReturnKeyAutomatically;
-@property (getter=isSecureTextEntry) BOOL secureTextEntry;
-
-@end
-
-@implementation _ASTextInputTraitsPendingState
-
-- (instancetype)init
-{
- if (!(self = [super init]))
- return nil;
-
- // set default values, as defined in Apple's comments in UITextInputTraits.h
- _autocapitalizationType = UITextAutocapitalizationTypeSentences;
- _autocorrectionType = UITextAutocorrectionTypeDefault;
- _spellCheckingType = UITextSpellCheckingTypeDefault;
- _keyboardAppearance = UIKeyboardAppearanceDefault;
- _keyboardType = UIKeyboardTypeDefault;
- _returnKeyType = UIReturnKeyDefault;
-
- return self;
-}
-
-@end
-
-/**
- @abstract As originally reported in rdar://14729288, when scrollEnabled = NO,
- UITextView does not calculate its contentSize. This makes it difficult
- for a client to embed a UITextView inside a different scroll view with
- other content (setting scrollEnabled = NO on the UITextView itself,
- because the containing scroll view will handle the gesture)...
- because accessing contentSize is typically necessary to perform layout.
- Apple later closed the issue as expected behavior. This works around
- the issue by ensuring that contentSize is always calculated, while
- still providing control over the UITextView's scrolling.
-
- See issue: https://github.com/facebook/AsyncDisplayKit/issues/1063
- */
-
-@interface ASPanningOverriddenUITextView : ASTextKitComponentsTextView
-{
- BOOL _shouldBlockPanGesture;
-}
-
-@property (nonatomic, copy) bool (^shouldCopy)();
-@property (nonatomic, copy) bool (^shouldPaste)();
-@property (nonatomic, copy) ASEditableTextNodeTargetForAction *(^targetForActionImpl)(SEL);
-@property (nonatomic, copy) bool (^shouldReturn)();
-@property (nonatomic, copy) void (^backspaceWhileEmpty)();
-
-@property (nonatomic, strong) NSString * _Nullable initialPrimaryLanguage;
-@property (nonatomic) bool initializedPrimaryInputLanguage;
-
-@end
-
-@implementation ASPanningOverriddenUITextView
-
-#if TARGET_OS_IOS
- // tvOS doesn't support self.scrollsToTop
-- (BOOL)scrollEnabled
-{
- return _shouldBlockPanGesture;
-}
-
-- (void)setScrollEnabled:(BOOL)scrollEnabled
-{
- _shouldBlockPanGesture = !scrollEnabled;
- self.scrollsToTop = scrollEnabled;
-
- [super setScrollEnabled:YES];
-}
-
-- (void)setContentSize:(CGSize)contentSize {
- [super setContentSize:contentSize];
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
-{
- if (_targetForActionImpl) {
- ASEditableTextNodeTargetForAction *result = _targetForActionImpl(action);
- if (result) {
- return result.target != nil;
- }
- }
-
- if (action == @selector(paste:)) {
- NSArray *items = [UIMenuController sharedMenuController].menuItems;
- if (((UIMenuItem *)items.firstObject).action == @selector(toggleBoldface:)) {
- return false;
- }
- return true;
- }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wundeclared-selector"
- static SEL promptForReplaceSelector;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- promptForReplaceSelector = NSSelectorFromString(@"_promptForReplace:");
- });
- if (action == promptForReplaceSelector) {
- return false;
- }
-#pragma clang diagnostic pop
-
- if (action == @selector(toggleUnderline:)) {
- return false;
- }
-
- return [super canPerformAction:action withSender:sender];
-}
-
-- (id)targetForAction:(SEL)action withSender:(id)__unused sender
-{
- if (_targetForActionImpl) {
- ASEditableTextNodeTargetForAction *result = _targetForActionImpl(action);
- if (result) {
- return result.target;
- }
- }
- return [super targetForAction:action withSender:sender];
-}
-
-- (void)copy:(id)sender {
- if (_shouldCopy == nil || _shouldCopy()) {
- [super copy:sender];
- }
-}
-
-- (void)paste:(id)sender
-{
- if (_shouldPaste == nil || _shouldPaste()) {
- [super paste:sender];
- }
-}
-
-- (NSArray *)keyCommands {
- UIKeyCommand *plainReturn = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:kNilOptions action:@selector(handlePlainReturn:)];
- return @[
- plainReturn
- ];
-}
-
-- (void)handlePlainReturn:(id)__unused sender {
- if (_shouldReturn) {
- _shouldReturn();
- }
-}
-
-- (void)deleteBackward {
- bool notify = self.text.length == 0;
- [super deleteBackward];
- if (notify) {
- if (_backspaceWhileEmpty) {
- _backspaceWhileEmpty();
- }
- }
-}
-
-- (UIKeyboardAppearance)keyboardAppearance {
- return [super keyboardAppearance];
-}
-
-- (UITextInputMode *)textInputMode {
- if (!_initializedPrimaryInputLanguage) {
- _initializedPrimaryInputLanguage = true;
- if (_initialPrimaryLanguage != nil) {
- for (UITextInputMode *inputMode in [UITextInputMode activeInputModes]) {
- NSString *primaryLanguage = inputMode.primaryLanguage;
- if (primaryLanguage != nil && [primaryLanguage isEqualToString:_initialPrimaryLanguage]) {
- return inputMode;
- }
- }
- }
- }
- return [super textInputMode];
-}
-
-#endif
-
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
-{
- // Never allow our pans to begin when _shouldBlockPanGesture is true.
- if (_shouldBlockPanGesture && gestureRecognizer == self.panGestureRecognizer)
- return NO;
-
- // Otherwise, proceed as usual.
- if ([UITextView instancesRespondToSelector:_cmd])
- return [super gestureRecognizerShouldBegin:gestureRecognizer];
- return YES;
-}
-
-@end
-
-#pragma mark -
-@interface ASEditableTextNode ()
-{
- @private
- // Configuration.
- NSDictionary *_typingAttributes;
-
- // Core.
- id __weak _delegate;
- BOOL _delegateDidUpdateEnqueued;
-
- // TextKit.
- AS::RecursiveMutex _textKitLock;
- ASTextKitComponents *_textKitComponents;
- ASTextKitComponents *_placeholderTextKitComponents;
- // Forwards NSLayoutManagerDelegate methods related to word kerning
- ASTextNodeWordKerner *_wordKerner;
-
- // UITextInputTraits
- AS::RecursiveMutex _textInputTraitsLock;
- _ASTextInputTraitsPendingState *_textInputTraits;
-
- // Misc. State.
- BOOL _displayingPlaceholder; // Defaults to YES.
- BOOL _isPreservingSelection;
- BOOL _isPreservingText;
- BOOL _selectionChangedForEditedText;
- NSRange _previousSelectedRange;
-}
-
-@property (nonatomic, readonly) _ASTextInputTraitsPendingState *textInputTraits;
-
-@end
-
-@implementation ASEditableTextNode
-
-#pragma mark - NSObject Overrides
-- (instancetype)init
-{
- return [self initWithTextKitComponents:[ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]
- placeholderTextKitComponents:[ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]];
-}
-
-- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents
- placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents
-{
- if (!(self = [super init]))
- return nil;
-
- _displayingPlaceholder = YES;
- _scrollEnabled = YES;
-
- // Create the scaffolding for the text view.
- _textKitComponents = textKitComponents;
- _textKitComponents.layoutManager.delegate = self;
- _wordKerner = [[ASTextNodeWordKerner alloc] init];
- _textContainerInset = UIEdgeInsetsZero;
-
- // Create the placeholder scaffolding.
- _placeholderTextKitComponents = placeholderTextKitComponents;
- _placeholderTextKitComponents.layoutManager.delegate = self;
-
- return self;
-}
-
-#pragma mark - ASDisplayNode Overrides
-- (void)didLoad
-{
- [super didLoad];
-
- void (^configureTextView)(UITextView *) = ^(UITextView *textView) {
- if (!_displayingPlaceholder || textView != _textKitComponents.textView) {
- // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder.
- textView.backgroundColor = self.backgroundColor;
- textView.opaque = self.opaque;
- } else if (_displayingPlaceholder && textView == _textKitComponents.textView) {
- // The default backgroundColor for a textView is white. Due to the reason described above, make sure the editable textView starts out transparent.
- textView.backgroundColor = nil;
- textView.opaque = NO;
- }
- textView.textContainerInset = self.textContainerInset;
-
- // Configure textView with UITextInputTraits
- {
- AS::MutexLocker l(_textInputTraitsLock);
- if (_textInputTraits) {
- textView.autocapitalizationType = _textInputTraits.autocapitalizationType;
- textView.autocorrectionType = _textInputTraits.autocorrectionType;
- textView.spellCheckingType = _textInputTraits.spellCheckingType;
- textView.keyboardType = _textInputTraits.keyboardType;
- textView.keyboardAppearance = _textInputTraits.keyboardAppearance;
- textView.returnKeyType = _textInputTraits.returnKeyType;
- textView.enablesReturnKeyAutomatically = _textInputTraits.enablesReturnKeyAutomatically;
- textView.secureTextEntry = _textInputTraits.isSecureTextEntry;
- }
- }
-
- [self.view addSubview:textView];
- };
-
- AS::MutexLocker l(_textKitLock);
-
- // Create and configure the placeholder text view.
- _placeholderTextKitComponents.textView = [[ASTextKitComponentsTextView alloc] initWithFrame:CGRectZero textContainer:_placeholderTextKitComponents.textContainer];
- _placeholderTextKitComponents.textView.userInteractionEnabled = NO;
- _placeholderTextKitComponents.textView.accessibilityElementsHidden = YES;
- configureTextView(_placeholderTextKitComponents.textView);
-
- // Create and configure our text view.
- ASPanningOverriddenUITextView *textView = [[ASPanningOverriddenUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer];
- textView.initialPrimaryLanguage = _initialPrimaryLanguage;
- __weak ASEditableTextNode *weakSelf = self;
- textView.shouldCopy = ^bool{
- __strong ASEditableTextNode *strongSelf = weakSelf;
- if (strongSelf != nil) {
- if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeShouldCopy:)]) {
- return [strongSelf->_delegate editableTextNodeShouldCopy:self];
- }
- }
- return true;
- };
- textView.shouldPaste = ^bool{
- __strong ASEditableTextNode *strongSelf = weakSelf;
- if (strongSelf != nil) {
- if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeShouldPaste:)]) {
- return [strongSelf->_delegate editableTextNodeShouldPaste:self];
- }
- }
- return true;
- };
- textView.targetForActionImpl = ^id(SEL action) {
- __strong ASEditableTextNode *strongSelf = weakSelf;
- if (strongSelf != nil) {
- if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeTargetForAction:)]) {
- return [strongSelf->_delegate editableTextNodeTargetForAction:action];
- }
- }
- return nil;
- };
- textView.shouldReturn = ^bool {
- __strong ASEditableTextNode *strongSelf = weakSelf;
- if (strongSelf != nil) {
- if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeShouldReturn:)]) {
- return [strongSelf->_delegate editableTextNodeShouldReturn:strongSelf];
- }
- }
- return true;
- };
- textView.backspaceWhileEmpty = ^{
- __strong ASEditableTextNode *strongSelf = weakSelf;
- if (strongSelf != nil) {
- if ([strongSelf->_delegate respondsToSelector:@selector(editableTextNodeBackspaceWhileEmpty:)]) {
- [strongSelf->_delegate editableTextNodeBackspaceWhileEmpty:strongSelf];
- }
- }
- };
- _textKitComponents.textView = textView;
- _textKitComponents.textView.scrollEnabled = _scrollEnabled;
- _textKitComponents.textView.delegate = self;
- #if TARGET_OS_IOS
- _textKitComponents.textView.editable = YES;
- #endif
- _textKitComponents.textView.typingAttributes = _typingAttributes;
- _textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string;
- configureTextView(_textKitComponents.textView);
-
- [self _updateDisplayingPlaceholder];
-
- // once view is loaded, setters set directly on view
- _textInputTraits = nil;
-
- UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
- tapRecognizer.cancelsTouchesInView = false;
- tapRecognizer.delaysTouchesBegan = false;
- tapRecognizer.delaysTouchesEnded = false;
- tapRecognizer.delegate = self;
- [self.view addGestureRecognizer:tapRecognizer];
-}
-
-- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
- return true;
-}
-
-- (void)tapGesture:(UITapGestureRecognizer *)recognizer {
- static Class promptClass = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- promptClass = NSClassFromString([[NSString alloc] initWithFormat:@"%@AutocorrectInlinePrompt", @"UI"]);
- });
-
- if (recognizer.state == UIGestureRecognizerStateEnded) {
- UIView *result = [self hitTest:[recognizer locationInView:self.view] withEvent:nil];
- if (result != nil && [result class] == promptClass) {
- [self dropAutocorrection];
- }
- }
-}
-
-- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
-{
- ASTextKitComponents *displayedComponents = [self isDisplayingPlaceholder] ? _placeholderTextKitComponents : _textKitComponents;
-
- CGSize textSize;
-
- if (_maximumLinesToDisplay > 0) {
- textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width
- forMaxNumberOfLines: _maximumLinesToDisplay];
- } else {
- textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width];
- }
-
- CGFloat width = std::ceil(constrainedSize.width);
- CGFloat height = std::ceil(textSize.height + _textContainerInset.top + _textContainerInset.bottom);
- return CGSizeMake(std::fmin(width, constrainedSize.width), std::fmin(height, constrainedSize.height));
-}
-
-- (void)layout
-{
- ASDisplayNodeAssertMainThread();
-
- [super layout];
- [self _layoutTextView];
-}
-
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
- [super setBackgroundColor:backgroundColor];
-
- AS::MutexLocker l(_textKitLock);
-
- // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder.
- // The backgroundColor/opaque will be propagated to the editable textView when editing begins.
- if (!_displayingPlaceholder) {
- _textKitComponents.textView.backgroundColor = backgroundColor;
- }
- _placeholderTextKitComponents.textView.backgroundColor = backgroundColor;
-}
-
-- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset
-{
- AS::MutexLocker l(_textKitLock);
-
- _textContainerInset = textContainerInset;
- _textKitComponents.textView.textContainerInset = textContainerInset;
- _placeholderTextKitComponents.textView.textContainerInset = textContainerInset;
-}
-
-- (void)setOpaque:(BOOL)opaque
-{
- [super setOpaque:opaque];
-
- AS::MutexLocker l(_textKitLock);
-
- // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder.
- // The backgroundColor/opaque will be propagated to the editable textView when editing begins.
- if (!_displayingPlaceholder) {
- _textKitComponents.textView.opaque = opaque;
- }
- _placeholderTextKitComponents.textView.opaque = opaque;
-}
-
-- (void)setLayerBacked:(BOOL)layerBacked
-{
- ASDisplayNodeAssert(!layerBacked, @"Cannot set layerBacked to YES on ASEditableTextNode – instances must be view-backed in order to ensure touch events can be passed to the internal UITextView during editing.");
- [super setLayerBacked:layerBacked];
-}
-
-- (BOOL)supportsLayerBacking
-{
- return NO;
-}
-
-#pragma mark - Configuration
-@synthesize delegate = _delegate;
-
-- (void)setScrollEnabled:(BOOL)scrollEnabled
-{
- AS::MutexLocker l(_textKitLock);
- _scrollEnabled = scrollEnabled;
- [_textKitComponents.textView setScrollEnabled:_scrollEnabled];
-}
-
-- (UITextView *)textView
-{
- ASDisplayNodeAssertMainThread();
- [self view];
- ASDisplayNodeAssert(_textKitComponents.textView != nil, @"UITextView must be created in -[ASEditableTextNode didLoad]");
- return _textKitComponents.textView;
-}
-
-- (void)setMaximumLinesToDisplay:(NSUInteger)maximumLines
-{
- _maximumLinesToDisplay = maximumLines;
- [self setNeedsLayout];
-}
-
-#pragma mark -
-@dynamic typingAttributes;
-
-- (NSDictionary *)typingAttributes
-{
- return _typingAttributes;
-}
-
-- (void)setTypingAttributes:(NSDictionary *)typingAttributes
-{
- if (ASObjectIsEqual(typingAttributes, _typingAttributes))
- return;
-
- _typingAttributes = [typingAttributes copy];
-
- AS::MutexLocker l(_textKitLock);
-
- _textKitComponents.textView.typingAttributes = _typingAttributes;
-}
-
-#pragma mark -
-@dynamic selectedRange;
-
-- (NSRange)selectedRange
-{
- AS::MutexLocker l(_textKitLock);
- return _textKitComponents.textView.selectedRange;
-}
-
-- (void)setSelectedRange:(NSRange)selectedRange
-{
- AS::MutexLocker l(_textKitLock);
- _textKitComponents.textView.selectedRange = selectedRange;
-}
-
-- (CGRect)selectionRect {
- UITextRange *range = [_textKitComponents.textView selectedTextRange];
- if (range != nil) {
- return [_textKitComponents.textView firstRectForRange:range];
- } else {
- return [_textKitComponents.textView bounds];
- }
-}
-
-#pragma mark - Placeholder
-- (BOOL)isDisplayingPlaceholder
-{
- return _displayingPlaceholder;
-}
-
-#pragma mark -
-@dynamic attributedPlaceholderText;
-- (NSAttributedString *)attributedPlaceholderText
-{
- AS::MutexLocker l(_textKitLock);
-
- return [_placeholderTextKitComponents.textStorage copy];
-}
-
-- (void)setAttributedPlaceholderText:(NSAttributedString *)attributedPlaceholderText
-{
- AS::MutexLocker l(_textKitLock);
-
- if (ASObjectIsEqual(_placeholderTextKitComponents.textStorage, attributedPlaceholderText))
- return;
-
- [_placeholderTextKitComponents.textStorage setAttributedString:attributedPlaceholderText ? : [[NSAttributedString alloc] initWithString:@""]];
- _textKitComponents.textView.accessibilityHint = attributedPlaceholderText.string;
-}
-
-#pragma mark - Modifying User Text
-@dynamic attributedText;
-- (NSAttributedString *)attributedText
-{
- // Per contract in our header, this value is nil when the placeholder is displayed.
- if ([self isDisplayingPlaceholder])
- return nil;
-
- AS::MutexLocker l(_textKitLock);
-
- return [_textKitComponents.textStorage copy];
-}
-
-- (void)setAttributedText:(NSAttributedString *)attributedText
-{
- AS::MutexLocker l(_textKitLock);
-
- // If we (_cmd) are called while the text view itself is updating (-textViewDidUpdate:), you cannot update the text storage and expect perfect propagation to the text view.
- // Thus, we always update the textview directly if it's been created already.
- if (ASObjectIsEqual((_textKitComponents.textView.attributedText ? : _textKitComponents.textStorage), attributedText))
- return;
-
- // If the cursor isn't at the end of the text, we need to preserve the selected range to avoid moving the cursor.
- NSRange selectedRange = _textKitComponents.textView.selectedRange;
- BOOL preserveSelectedRange = (selectedRange.location != _textKitComponents.textStorage.length);
-
- NSAttributedString *attributedStringToDisplay = nil;
-
- if (attributedText)
- attributedStringToDisplay = attributedText;
- // Otherwise, note that we don't simply nil out attributed text. Because the insertion point is guided by the attributes at index 0, we need to attribute an empty string to ensure the insert point obeys our typing attributes.
- else
- attributedStringToDisplay = [[NSAttributedString alloc] initWithString:@"" attributes:self.typingAttributes];
-
- // Always prefer updating the text view directly if it's been created (see above).
- if (_textKitComponents.textView)
- [_textKitComponents.textView setAttributedText:attributedStringToDisplay];
- else
- [_textKitComponents.textStorage setAttributedString:attributedStringToDisplay];
-
- // Calculated size depends on the seeded text.
- [self setNeedsLayout];
-
- // Update if placeholder is shown.
- [self _updateDisplayingPlaceholder];
-
- // Preserve cursor range, if necessary.
- if (preserveSelectedRange) {
- _isPreservingSelection = YES; // Used in -textViewDidChangeSelection: to avoid informing our delegate about our preservation.
- [_textKitComponents.textView setSelectedRange:selectedRange];
- _isPreservingSelection = NO;
- }
-}
-
-- (void)setInitialPrimaryLanguage:(NSString *)initialPrimaryLanguage {
- _initialPrimaryLanguage = initialPrimaryLanguage;
- ((ASPanningOverriddenUITextView *)_textKitComponents.textView).initialPrimaryLanguage = initialPrimaryLanguage;
-}
-
-- (void)resetInitialPrimaryLanguage {
- ((ASPanningOverriddenUITextView *)_textKitComponents.textView).initializedPrimaryInputLanguage = false;
-}
-
-- (void)dropAutocorrection {
- _isPreservingSelection = YES; // Used in -textViewDidChangeSelection: to avoid informing our delegate about our preservation.
- _isPreservingText = YES;
-
- UITextView *textView = _textKitComponents.textView;
-
- NSRange rangeCopy = textView.selectedRange;
- NSRange fakeRange = rangeCopy;
- if (fakeRange.location != 0) {
- fakeRange.location--;
- }
- [textView unmarkText];
- [textView setSelectedRange:fakeRange];
- [textView setSelectedRange:rangeCopy];
-
- //[_textKitComponents.textView.inputDelegate textWillChange:_textKitComponents.textView];
- //[_textKitComponents.textView.inputDelegate textDidChange:_textKitComponents.textView];
-
- _isPreservingSelection = NO;
- _isPreservingText = NO;
-}
-
-- (bool)isCurrentlyEmoji {
- NSString *value = [[UITextInputMode currentInputMode] primaryLanguage];
- if ([value isEqualToString:@"emoji"]) {
- return true;
- } else {
- return false;
- }
-}
-
-#pragma mark - Core
-- (void)_updateDisplayingPlaceholder
-{
- AS::MutexLocker l(_textKitLock);
-
- // Show the placeholder if necessary.
- _displayingPlaceholder = (_textKitComponents.textStorage.length == 0);
- _placeholderTextKitComponents.textView.hidden = !_displayingPlaceholder;
-
- // If hiding the placeholder, propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and was kept transparent so it doesn't obscure the placeholder text. Now that we're editing it and the placeholder is hidden, we can make it opaque to avoid unnecessary blending.
- if (!_displayingPlaceholder) {
- _textKitComponents.textView.opaque = self.isOpaque;
- _textKitComponents.textView.backgroundColor = self.backgroundColor;
- } else {
- _textKitComponents.textView.opaque = NO;
- _textKitComponents.textView.backgroundColor = nil;
- }
-}
-
-- (void)_layoutTextView
-{
- AS::MutexLocker l(_textKitLock);
-
- // Layout filling our bounds.
- _textKitComponents.textView.frame = self.bounds;
- _placeholderTextKitComponents.textView.frame = self.bounds;
-
- // Note that both of these won't be necessary once we can disable scrolling, pending rdar://14729288
- // When we resize to fit (above) the prior layout becomes invalid. For whatever reason, UITextView doesn't invalidate its layout when its frame changes on its own, so we have to do so ourselves.
- [_textKitComponents.layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, [_textKitComponents.textStorage length]) actualCharacterRange:NULL];
-
- // When you type beyond UITextView's bounds it scrolls you down a line. We need to remain at the top.
- [_textKitComponents.textView setContentOffset:CGPointZero animated:NO];
- [_textKitComponents.layoutManager ensureGlyphsForCharacterRange:NSMakeRange(0, [_textKitComponents.textStorage length])];
- NSRange range = [self selectedRange];
- range.location = range.location + range.length - 1;
- range.length = 1;
- [self.textView scrollRangeToVisible:range];
-
- CGPoint bottomOffset = CGPointMake(0, self.textView.contentSize.height - self.textView.bounds.size.height);
- //[self.textView setContentOffset:bottomOffset animated:NO];
-}
-
-#pragma mark - Keyboard
-@dynamic textInputMode;
-- (UITextInputMode *)textInputMode
-{
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView textInputMode];
-}
-
-- (BOOL)isFirstResponder
-{
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView isFirstResponder];
-}
-
-- (BOOL)canBecomeFirstResponder {
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView canBecomeFirstResponder];
-}
-
-- (BOOL)becomeFirstResponder
-{
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView becomeFirstResponder];
-}
-
-- (BOOL)canResignFirstResponder {
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView canResignFirstResponder];
-}
-
-- (BOOL)resignFirstResponder
-{
- AS::MutexLocker l(_textKitLock);
- return [_textKitComponents.textView resignFirstResponder];
-}
-
-#pragma mark - UITextInputTraits
-
-- (_ASTextInputTraitsPendingState *)textInputTraits
-{
- if (!_textInputTraits) {
- _textInputTraits = [[_ASTextInputTraitsPendingState alloc] init];
- }
- return _textInputTraits;
-}
-
-- (void)setAutocapitalizationType:(UITextAutocapitalizationType)autocapitalizationType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setAutocapitalizationType:autocapitalizationType];
- } else {
- [self.textInputTraits setAutocapitalizationType:autocapitalizationType];
- }
-}
-
-- (UITextAutocapitalizationType)autocapitalizationType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView autocapitalizationType];
- } else {
- return [self.textInputTraits autocapitalizationType];
- }
-}
-
-- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setAutocorrectionType:autocorrectionType];
- } else {
- [self.textInputTraits setAutocorrectionType:autocorrectionType];
- }
-}
-
-- (UITextAutocorrectionType)autocorrectionType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView autocorrectionType];
- } else {
- return [self.textInputTraits autocorrectionType];
- }
-}
-
-- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setSpellCheckingType:spellCheckingType];
- } else {
- [self.textInputTraits setSpellCheckingType:spellCheckingType];
- }
-}
-
-- (UITextSpellCheckingType)spellCheckingType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView spellCheckingType];
- } else {
- return [self.textInputTraits spellCheckingType];
- }
-}
-
-- (void)setEnablesReturnKeyAutomatically:(BOOL)enablesReturnKeyAutomatically
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically];
- } else {
- [self.textInputTraits setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically];
- }
-}
-
-- (BOOL)enablesReturnKeyAutomatically
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView enablesReturnKeyAutomatically];
- } else {
- return [self.textInputTraits enablesReturnKeyAutomatically];
- }
-}
-
-- (void)setKeyboardAppearance:(UIKeyboardAppearance)setKeyboardAppearance
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setKeyboardAppearance:setKeyboardAppearance];
- } else {
- [self.textInputTraits setKeyboardAppearance:setKeyboardAppearance];
- }
-}
-
-- (UIKeyboardAppearance)keyboardAppearance
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView keyboardAppearance];
- } else {
- return [self.textInputTraits keyboardAppearance];
- }
-}
-
-- (void)setKeyboardType:(UIKeyboardType)keyboardType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setKeyboardType:keyboardType];
- } else {
- [self.textInputTraits setKeyboardType:keyboardType];
- }
-}
-
-- (UIKeyboardType)keyboardType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView keyboardType];
- } else {
- return [self.textInputTraits keyboardType];
- }
-}
-
-- (void)setReturnKeyType:(UIReturnKeyType)returnKeyType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setReturnKeyType:returnKeyType];
- } else {
- [self.textInputTraits setReturnKeyType:returnKeyType];
- }
-}
-
-- (UIReturnKeyType)returnKeyType
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView returnKeyType];
- } else {
- return [self.textInputTraits returnKeyType];
- }
-}
-
-- (void)setSecureTextEntry:(BOOL)secureTextEntry
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- [self.textView setSecureTextEntry:secureTextEntry];
- } else {
- [self.textInputTraits setSecureTextEntry:secureTextEntry];
- }
-}
-
-- (BOOL)isSecureTextEntry
-{
- AS::MutexLocker l(_textInputTraitsLock);
- if (self.isNodeLoaded) {
- return [self.textView isSecureTextEntry];
- } else {
- return [self.textInputTraits isSecureTextEntry];
- }
-}
-
-#pragma mark - UITextView Delegate
-- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
-{
- // Delegateify.
- return [self _delegateShouldBeginEditing];
-}
-
-- (void)textViewDidBeginEditing:(UITextView *)textView
-{
- // Delegateify.
- [self _delegateDidBeginEditing];
-}
-
-- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
-{
- if (_isPreservingText) {
- return false;
- }
- // Delegateify.
- return [self _delegateShouldChangeTextInRange:range replacementText:text];
-}
-
-- (void)textViewDidChange:(UITextView *)textView
-{
- AS::MutexLocker l(_textKitLock);
-
- // Note we received a text changed event.
- // This is used by _delegateDidChangeSelectionFromSelectedRange:toSelectedRange: to distinguish between selection changes that happen because of editing or pure selection changes.
- _selectionChangedForEditedText = YES;
-
- // Update if the placeholder is visible.
- [self _updateDisplayingPlaceholder];
-
- // Invalidate, as our calculated size depends on the textview's seeded text.
- [self invalidateCalculatedLayout];
-
- // Delegateify.
- [self _delegateDidUpdateText];
-}
-
-- (void)textViewDidChangeSelection:(UITextView *)textView
-{
- // Typing attributes get reset when selection changes. Reapply them so they actually obey our header.
- _textKitComponents.textView.typingAttributes = _typingAttributes;
-
- // If we're only changing selection to preserve it, don't notify about anything.
- if (_isPreservingSelection)
- return;
-
- // Note if we receive a -textDidChange: between now and when we delegatify.
- // This is used by _delegateDidChangeSelectionFromSelectedRange:toSelectedRange: to distinguish between selection changes that happen because of editing or pure selection changes.
- _selectionChangedForEditedText = NO;
-
- NSRange fromSelectedRange = _previousSelectedRange;
- NSRange toSelectedRange = self.selectedRange;
- _previousSelectedRange = toSelectedRange;
-
- // Delegateify.
- [self _delegateDidChangeSelectionFromSelectedRange:fromSelectedRange toSelectedRange:toSelectedRange];
-}
-
-- (void)textViewDidEndEditing:(UITextView *)textView
-{
- // Delegateify.
- [self _delegateDidFinishEditing];
-}
-
-#pragma mark - NSLayoutManager Delegate
-
-- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)properties characterIndexes:(const NSUInteger *)characterIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange
-{
- return [_wordKerner layoutManager:layoutManager shouldGenerateGlyphs:glyphs properties:properties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange];
-}
-
-- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)defaultAction forControlCharacterAtIndex:(NSUInteger)characterIndex
-{
- return [_wordKerner layoutManager:layoutManager shouldUseAction:defaultAction forControlCharacterAtIndex:characterIndex];
-}
-
-- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)characterIndex
-{
- return [_wordKerner layoutManager:layoutManager boundingBoxForControlGlyphAtIndex:glyphIndex forTextContainer:textContainer proposedLineFragment:proposedRect glyphPosition:glyphPosition characterIndex:characterIndex];
-}
-
-- (BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldSetLineFragmentRect:(inout CGRect *)lineFragmentRect lineFragmentUsedRect:(inout CGRect *)lineFragmentUsedRect baselineOffset:(inout CGFloat *)baselineOffset inTextContainer:(NSTextContainer *)textContainer forGlyphRange:(NSRange)glyphRange {
- CGFloat fontLineHeight;
- UIFont *baseFont = _baseFont;
- if (_typingAttributes[NSFontAttributeName] != nil) {
- baseFont = _typingAttributes[NSFontAttributeName];
- }
- if (baseFont == nil) {
- fontLineHeight = 20.0;
- } else {
- CGFloat fontAscent = baseFont.ascender;
- CGFloat fontDescent = ABS(baseFont.descender);
- fontLineHeight = floor(fontAscent + fontDescent);
- }
- CGFloat lineHeight = fontLineHeight * 1.0;
- CGFloat baselineNudge = (lineHeight - fontLineHeight) * 0.6f;
-
- CGRect rect = *lineFragmentRect;
- rect.size.height = lineHeight;
-
- CGRect usedRect = *lineFragmentUsedRect;
- usedRect.size.height = MAX(lineHeight, usedRect.size.height);
-
- *lineFragmentRect = rect;
- *lineFragmentUsedRect = usedRect;
- *baselineOffset = *baselineOffset + baselineNudge;
-
- return true;
-}
-
-#pragma mark - Geometry
-- (CGRect)frameForTextRange:(NSRange)textRange
-{
- AS::MutexLocker l(_textKitLock);
-
- // Bail on invalid range.
- if (NSMaxRange(textRange) > [_textKitComponents.textStorage length]) {
- ASDisplayNodeAssert(NO, @"Invalid range");
- return CGRectZero;
- }
-
- // Force glyph generation and layout.
- [_textKitComponents.layoutManager ensureLayoutForTextContainer:_textKitComponents.textContainer];
-
- NSRange glyphRange = [_textKitComponents.layoutManager glyphRangeForCharacterRange:textRange actualCharacterRange:NULL];
- CGRect textRect = [_textKitComponents.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textKitComponents.textContainer];
- return [_textKitComponents.textView convertRect:textRect toView:self.view];
-}
-
-#pragma mark -
-- (BOOL)_delegateShouldBeginEditing
-{
- if ([_delegate respondsToSelector:@selector(editableTextNodeShouldBeginEditing:)]) {
- return [_delegate editableTextNodeShouldBeginEditing:self];
- }
- return YES;
-}
-
-- (void)_delegateDidBeginEditing
-{
- if ([_delegate respondsToSelector:@selector(editableTextNodeDidBeginEditing:)])
- [_delegate editableTextNodeDidBeginEditing:self];
-}
-
-- (BOOL)_delegateShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
-{
- if ([_delegate respondsToSelector:@selector(editableTextNode:shouldChangeTextInRange:replacementText:)]) {
- return [_delegate editableTextNode:self shouldChangeTextInRange:range replacementText:text];
- }
-
- return YES;
-}
-
-- (void)_delegateDidChangeSelectionFromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange
-{
- // There are two reasons we're invoking the delegate on the next run of the runloop.
- // 1. UITextView invokes its delegate methods when it's in the middle of text-processing. For example, -textViewDidChange: is invoked before you can truly rely on the changes being propagated throughout the Text Kit hierarchy.
- // 2. This delegate method (-textViewDidChangeSelection:) is called both before -textViewDidChange: and before the layout manager/etc. has necessarily generated+laid out its glyphs. Because of the former, we need to wait until -textViewDidChange: has had an opportunity to be called so can accurately determine whether this selection change is due to editing (_selectionChangedForEditedText).
- // Thus, to avoid calling out to client code in the middle of UITextView's processing, we call the delegate on the next run of the runloop, when all such internal processing is surely done.
- dispatch_async(dispatch_get_main_queue(), ^{
- if ([_delegate respondsToSelector:@selector(editableTextNodeDidChangeSelection:fromSelectedRange:toSelectedRange:dueToEditing:)])
- [_delegate editableTextNodeDidChangeSelection:self fromSelectedRange:fromSelectedRange toSelectedRange:toSelectedRange dueToEditing:_selectionChangedForEditedText];
- });
-}
-
-- (void)_delegateDidUpdateText
-{
- // Note that because -editableTextNodeDidUpdateText: passes no state, the current state of the receiver will be accessed. Thus, it's not useful to enqueue a second delegation call if the first hasn't happened yet -- doing so will result in the delegate receiving -editableTextNodeDidUpdateText: when the "updated text" has already been processed. This may sound innocuous, but because our delegation may cause additional updates to the textview's string, and because such updates discard spelling suggestions and autocompletions (like double-space to `.`), it can actually be quite dangerous!
- if (_delegateDidUpdateEnqueued)
- return;
-
- _delegateDidUpdateEnqueued = YES;
-
- // UITextView invokes its delegate methods when it's in the middle of text-processing. For example, -textViewDidChange: is invoked before you can truly rely on the changes being propagated throughout the Text Kit hierarchy.
- // Thus, to avoid calling out to client code in the middle of UITextView's processing, we call the delegate on the next run of the runloop, when all such internal processing is surely done.
- dispatch_async(dispatch_get_main_queue(), ^{
- _delegateDidUpdateEnqueued = NO;
- if ([_delegate respondsToSelector:@selector(editableTextNodeDidUpdateText:)])
- [_delegate editableTextNodeDidUpdateText:self];
- });
-}
-
-- (void)_delegateDidFinishEditing
-{
- if ([_delegate respondsToSelector:@selector(editableTextNodeDidFinishEditing:)])
- [_delegate editableTextNodeDidFinishEditing:self];
-}
-
-#pragma mark - UIAccessibilityContainer
-
-- (NSInteger)accessibilityElementCount
-{
- if (!self.isNodeLoaded) {
- ASDisplayNodeFailAssert(@"Cannot access accessibilityElementCount since ASEditableTextNode is not loaded");
- return 0;
- }
- return 1;
-}
-
-- (NSArray *)accessibilityElements
-{
- if (!self.isNodeLoaded) {
- ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since ASEditableTextNode is not loaded");
- return @[];
- }
- return @[self.textView];
-}
-
-- (id)accessibilityElementAtIndex:(NSInteger)index
-{
- if (!self.isNodeLoaded) {
- ASDisplayNodeFailAssert(@"Cannot access accessibilityElementAtIndex: since ASEditableTextNode is not loaded");
- return nil;
- }
- return self.textView;
-}
-
-- (NSInteger)indexOfAccessibilityElement:(id)element
-{
- return 0;
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm b/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm
deleted file mode 100644
index 653db2c370..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASExperimentalFeatures.mm
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// ASExperimentalFeatures.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-
-NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags)
-{
- NSArray *allNames = ASCreateOnce((@[@"exp_graphics_contexts",
- @"exp_text_node",
- @"exp_interface_state_coalesce",
- @"exp_unfair_lock",
- @"exp_infer_layer_defaults",
- @"exp_collection_teardown",
- @"exp_framesetter_cache",
- @"exp_skip_clear_data",
- @"exp_did_enter_preload_skip_asm_layout",
- @"exp_disable_a11y_cache",
- @"exp_dispatch_apply",
- @"exp_image_downloader_priority",
- @"exp_text_drawing"]));
- if (flags == ASExperimentalFeatureAll) {
- return allNames;
- }
-
- // Go through all names, testing each bit.
- NSUInteger i = 0;
- return ASArrayByFlatMapping(allNames, NSString *name, ({
- (flags & (1 << i++)) ? name : nil;
- }));
-}
-
-// O(N^2) but with counts this small, it's probably faster
-// than hashing the strings.
-ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray *array)
-{
- NSArray *allNames = ASExperimentalFeaturesGetNames(ASExperimentalFeatureAll);
- ASExperimentalFeatures result = 0;
- for (NSString *str in array) {
- NSUInteger i = [allNames indexOfObject:str];
- if (i != NSNotFound) {
- result |= (1 << i);
- }
- }
- return result;
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASGraphicsContext.mm b/submodules/AsyncDisplayKit/Source/ASGraphicsContext.mm
deleted file mode 100644
index b181ee2728..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASGraphicsContext.mm
+++ /dev/null
@@ -1,167 +0,0 @@
-//
-// ASGraphicsContext.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import "ASCGImageBuffer.h"
-#import
-#import
-#import
-#import
-#import
-#import
-
-/**
- * Our version of the private CGBitmapGetAlignedBytesPerRow function.
- *
- * In both 32-bit and 64-bit, this function rounds up to nearest multiple of 32
- * in iOS 9, 10, and 11. We'll try to catch if this ever changes by asserting that
- * the bytes-per-row for a 1x1 context from the system is 32.
- */
-static size_t ASGraphicsGetAlignedBytesPerRow(size_t baseValue) {
- // Add 31 then zero out low 5 bits.
- return (baseValue + 31) & ~0x1F;
-}
-
-/**
- * A key used to associate CGContextRef -> NSMutableData, nonatomic retain.
- *
- * That way the data will be released when the context dies. If they pull an image,
- * we will retain the data object (in a CGDataProvider) before releasing the context.
- */
-static UInt8 __contextDataAssociationKey;
-
-#pragma mark - Graphics Contexts
-
-void ASGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
-{
- if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) {
- UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
- return;
- }
-
- // We use "reference contexts" to get device-specific options that UIKit
- // uses.
- static dispatch_once_t onceToken;
- static CGContextRef refCtxOpaque;
- static CGContextRef refCtxTransparent;
- dispatch_once(&onceToken, ^{
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 1);
- refCtxOpaque = CGContextRetain(UIGraphicsGetCurrentContext());
- ASDisplayNodeCAssert(CGBitmapContextGetBytesPerRow(refCtxOpaque) == 32, @"Expected bytes per row to be aligned to 32. Has CGBitmapGetAlignedBytesPerRow implementation changed?");
- UIGraphicsEndImageContext();
-
- // Make transparent ref context.
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, 1);
- refCtxTransparent = CGContextRetain(UIGraphicsGetCurrentContext());
- UIGraphicsEndImageContext();
- });
-
- // These options are taken from UIGraphicsBeginImageContext.
- CGContextRef refCtx = opaque ? refCtxOpaque : refCtxTransparent;
- CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(refCtx);
-
- if (scale == 0) {
- scale = ASScreenScale();
- }
- size_t intWidth = (size_t)ceil(size.width * scale);
- size_t intHeight = (size_t)ceil(size.height * scale);
- size_t bitsPerComponent = CGBitmapContextGetBitsPerComponent(refCtx);
- size_t bytesPerRow = CGBitmapContextGetBitsPerPixel(refCtx) * intWidth / 8;
- bytesPerRow = ASGraphicsGetAlignedBytesPerRow(bytesPerRow);
- size_t bufferSize = bytesPerRow * intHeight;
- CGColorSpaceRef colorspace = CGBitmapContextGetColorSpace(refCtx);
-
- // We create our own buffer, and wrap the context around that. This way we can prevent
- // the copy that usually gets made when you form a CGImage from the context.
- ASCGImageBuffer *buffer = [[ASCGImageBuffer alloc] initWithLength:bufferSize];
-
- CGContextRef context = CGBitmapContextCreate(buffer.mutableBytes, intWidth, intHeight, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo);
-
- // Transfer ownership of the data to the context. So that if the context
- // is destroyed before we create an image from it, the data will be released.
- objc_setAssociatedObject((__bridge id)context, &__contextDataAssociationKey, buffer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-
- // Set the CTM to account for iOS orientation & specified scale.
- // If only we could use CGContextSetBaseCTM. It doesn't
- // seem like there are any consequences for our use case
- // but we'll be on the look out. The internet hinted that it
- // affects shadowing but I tested and shadowing works.
- CGContextTranslateCTM(context, 0, intHeight);
- CGContextScaleCTM(context, scale, -scale);
-
- // Save the state so we can restore it and recover our scale in GetImageAndEnd
- CGContextSaveGState(context);
-
- // Transfer context ownership to the UIKit stack.
- UIGraphicsPushContext(context);
- CGContextRelease(context);
-}
-
-UIImage * _Nullable ASGraphicsGetImageAndEndCurrentContext() NS_RETURNS_RETAINED
-{
- if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) {
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- return image;
- }
-
- // Pop the context and make sure we have one.
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (context == NULL) {
- ASDisplayNodeCFailAssert(@"Can't end image context without having begun one.");
- return nil;
- }
-
- // Read the device-specific ICC-based color space to use for the image.
- // For DeviceRGB contexts (e.g. UIGraphics), CGBitmapContextCreateImage
- // generates an image in a device-specific color space (for wide color support).
- // We replicate that behavior, even though at this time CA does not
- // require the image to be in this space. Plain DeviceRGB images seem
- // to be treated exactly the same, but better safe than sorry.
- static CGColorSpaceRef imageColorSpace;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
- UIImage *refImage = UIGraphicsGetImageFromCurrentImageContext();
- imageColorSpace = CGColorSpaceRetain(CGImageGetColorSpace(refImage.CGImage));
- ASDisplayNodeCAssertNotNil(imageColorSpace, nil);
- UIGraphicsEndImageContext();
- });
-
- // Retrieve our buffer and create a CGDataProvider from it.
- ASCGImageBuffer *buffer = objc_getAssociatedObject((__bridge id)context, &__contextDataAssociationKey);
- ASDisplayNodeCAssertNotNil(buffer, nil);
- CGDataProviderRef provider = [buffer createDataProviderAndInvalidate];
-
- // Create the CGImage. Options taken from CGBitmapContextCreateImage.
- CGImageRef cgImg = CGImageCreate(CGBitmapContextGetWidth(context), CGBitmapContextGetHeight(context), CGBitmapContextGetBitsPerComponent(context), CGBitmapContextGetBitsPerPixel(context), CGBitmapContextGetBytesPerRow(context), imageColorSpace, CGBitmapContextGetBitmapInfo(context), provider, NULL, true, kCGRenderingIntentDefault);
- CGDataProviderRelease(provider);
-
- // We saved our GState right after setting the CTM so that we could restore it
- // here and get the original scale back.
- CGContextRestoreGState(context);
- CGFloat scale = CGContextGetCTM(context).a;
-
- // Note: popping from the UIKit stack will probably destroy the context.
- context = NULL;
- UIGraphicsPopContext();
-
- UIImage *result = [[UIImage alloc] initWithCGImage:cgImg scale:scale orientation:UIImageOrientationUp];
- CGImageRelease(cgImg);
- return result;
-}
-
-void ASGraphicsEndImageContext()
-{
- if (!ASActivateExperimentalFeature(ASExperimentalGraphicsContexts)) {
- UIGraphicsEndImageContext();
- return;
- }
-
- UIGraphicsPopContext();
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASHashing.mm b/submodules/AsyncDisplayKit/Source/ASHashing.mm
deleted file mode 100644
index 17bf66bd82..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASHashing.mm
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// ASHashing.mm
-// Texture
-//
-// Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1;
-
-/**
- * The hashing algorithm copied from CoreFoundation CFHashBytes function.
- * https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html
- */
-NSUInteger ASHashBytes(void *bytesarg, size_t length) {
- /* The ELF hash algorithm, used in the ELF object file format */
- uint8_t *bytes = (uint8_t *)bytesarg;
- UInt32 H = 0, T1, T2;
- SInt32 rem = (SInt32)length;
- while (3 < rem) {
- ELF_STEP(bytes[length - rem]);
- ELF_STEP(bytes[length - rem + 1]);
- ELF_STEP(bytes[length - rem + 2]);
- ELF_STEP(bytes[length - rem + 3]);
- rem -= 4;
- }
- switch (rem) {
- case 3: ELF_STEP(bytes[length - 3]);
- case 2: ELF_STEP(bytes[length - 2]);
- case 1: ELF_STEP(bytes[length - 1]);
- case 0: ;
- }
- return H;
-}
-
-#undef ELF_STEP
diff --git a/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm b/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm
deleted file mode 100644
index a9926ccca4..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASInternalHelpers.mm
+++ /dev/null
@@ -1,233 +0,0 @@
-//
-// ASInternalHelpers.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-
-#import
-#import
-
-#import
-#import
-#import
-
-static NSNumber *allowsGroupOpacityFromUIKitOrNil;
-static NSNumber *allowsEdgeAntialiasingFromUIKitOrNil;
-
-BOOL ASDefaultAllowsGroupOpacity()
-{
- static BOOL groupOpacity;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSNumber *groupOpacityObj = allowsGroupOpacityFromUIKitOrNil ?: [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIViewGroupOpacity"];
- groupOpacity = groupOpacityObj ? groupOpacityObj.boolValue : YES;
- });
- return groupOpacity;
-}
-
-BOOL ASDefaultAllowsEdgeAntialiasing()
-{
- static BOOL edgeAntialiasing;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSNumber *antialiasingObj = allowsEdgeAntialiasingFromUIKitOrNil ?: [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIViewEdgeAntialiasing"];
- edgeAntialiasing = antialiasingObj ? antialiasingObj.boolValue : NO;
- });
- return edgeAntialiasing;
-}
-
-void ASInitializeFrameworkMainThread(void)
-{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- ASDisplayNodeCAssertMainThread();
- // Ensure these values are cached on the main thread before needed in the background.
- if (ASActivateExperimentalFeature(ASExperimentalLayerDefaults)) {
- // Nop. We will gather default values on-demand in ASDefaultAllowsGroupOpacity and ASDefaultAllowsEdgeAntialiasing
- } else {
- CALayer *layer = [[[UIView alloc] init] layer];
- allowsGroupOpacityFromUIKitOrNil = @(layer.allowsGroupOpacity);
- allowsEdgeAntialiasingFromUIKitOrNil = @(layer.allowsEdgeAntialiasing);
- }
- ASNotifyInitialized();
- });
-}
-
-BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector)
-{
- if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself.
- Method superclassMethod = class_getInstanceMethod(superclass, selector);
- Method subclassMethod = class_getInstanceMethod(subclass, selector);
- return (superclassMethod != subclassMethod);
-}
-
-BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector)
-{
- if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself.
- Method superclassMethod = class_getClassMethod(superclass, selector);
- Method subclassMethod = class_getClassMethod(subclass, selector);
- return (superclassMethod != subclassMethod);
-}
-
-IMP ASReplaceMethodWithBlock(Class c, SEL origSEL, id block)
-{
- NSCParameterAssert(block);
-
- // Get original method
- Method origMethod = class_getInstanceMethod(c, origSEL);
- NSCParameterAssert(origMethod);
-
- // Convert block to IMP trampoline and replace method implementation
- IMP newIMP = imp_implementationWithBlock(block);
-
- // Try adding the method if not yet in the current class
- if (!class_addMethod(c, origSEL, newIMP, method_getTypeEncoding(origMethod))) {
- return method_setImplementation(origMethod, newIMP);
- } else {
- return method_getImplementation(origMethod);
- }
-}
-
-void ASPerformBlockOnMainThread(void (^block)(void))
-{
- if (block == nil){
- return;
- }
- if (ASDisplayNodeThreadIsMain()) {
- block();
- } else {
- dispatch_async(dispatch_get_main_queue(), block);
- }
-}
-
-void ASPerformBlockOnBackgroundThread(void (^block)(void))
-{
- if (block == nil){
- return;
- }
- if (ASDisplayNodeThreadIsMain()) {
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
- } else {
- block();
- }
-}
-
-void ASPerformBackgroundDeallocation(id __strong _Nullable * _Nonnull object)
-{
- [[ASDeallocQueue sharedDeallocationQueue] releaseObjectInBackground:object];
-}
-
-Class _Nullable ASGetClassFromType(const char * _Nullable type)
-{
- // Class types all start with @"
- if (type == NULL || strncmp(type, "@\"", 2) != 0) {
- return Nil;
- }
-
- // Ensure length >= 3
- size_t typeLength = strlen(type);
- if (typeLength < 3) {
- ASDisplayNodeCFailAssert(@"Got invalid type-encoding: %s", type);
- return Nil;
- }
-
- // Copy type[2..(end-1)]. So @"UIImage" -> UIImage
- size_t resultLength = typeLength - 3;
- char className[resultLength + 1];
- strncpy(className, type + 2, resultLength);
- className[resultLength] = '\0';
- return objc_getClass(className);
-}
-
-CGFloat ASScreenScale()
-{
- static CGFloat __scale = 0.0;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
- __scale = CGContextGetCTM(UIGraphicsGetCurrentContext()).a;
- UIGraphicsEndImageContext();
- });
- return __scale;
-}
-
-CGSize ASFloorSizeValues(CGSize s)
-{
- return CGSizeMake(ASFloorPixelValue(s.width), ASFloorPixelValue(s.height));
-}
-
-// See ASCeilPixelValue for a more thoroguh explanation of (f + FLT_EPSILON),
-// but here is some quick math:
-//
-// Imagine a layout that comes back with a height of 100.66666666663
-// for a 3x deice:
-// 100.66666666663 * 3 = 301.99999999988995
-// floor(301.99999999988995) = 301
-// 301 / 3 = 100.333333333
-//
-// If we add FLT_EPSILON to normalize the garbage at the end we get:
-// po (100.66666666663 + FLT_EPSILON) * 3 = 302.00000035751782
-// floor(302.00000035751782) = 302
-// 302/3 = 100.66666666
-CGFloat ASFloorPixelValue(CGFloat f)
-{
- CGFloat scale = ASScreenScale();
- return floor((f + FLT_EPSILON) * scale) / scale;
-}
-
-CGPoint ASCeilPointValues(CGPoint p)
-{
- return CGPointMake(ASCeilPixelValue(p.x), ASCeilPixelValue(p.y));
-}
-
-CGSize ASCeilSizeValues(CGSize s)
-{
- return CGSizeMake(ASCeilPixelValue(s.width), ASCeilPixelValue(s.height));
-}
-
-// With 3x devices layouts will often to compute to pixel bounds but
-// include garbage values beyond the precision of a float/double.
-// This garbage can result in a pixel value being rounded up when it isn't
-// necessary.
-//
-// For example, imagine a layout that comes back with a height of 100.666666666669
-// for a 3x device:
-// 100.666666666669 * 3 = 302.00000000000699
-// ceil(302.00000000000699) = 303
-// 303/3 = 101
-//
-// If we use FLT_EPSILON to get rid of the garbage at the end of the value,
-// things work as expected:
-// (100.666666666669 - FLT_EPSILON) * 3 = 301.99999964237912
-// ceil(301.99999964237912) = 302
-// 302/3 = 100.666666666
-//
-// For even more conversation around this, see:
-// https://github.com/TextureGroup/Texture/issues/838
-CGFloat ASCeilPixelValue(CGFloat f)
-{
- CGFloat scale = ASScreenScale();
- return ceil((f - FLT_EPSILON) * scale) / scale;
-}
-
-CGFloat ASRoundPixelValue(CGFloat f)
-{
- CGFloat scale = ASScreenScale();
- return round(f * scale) / scale;
-}
-
-@implementation NSIndexPath (ASInverseComparison)
-
-- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath
-{
- return [otherIndexPath compare:self];
-}
-
-@end
diff --git a/submodules/AsyncDisplayKit/Source/ASLayout.mm b/submodules/AsyncDisplayKit/Source/ASLayout.mm
deleted file mode 100644
index 6a6a96f24c..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASLayout.mm
+++ /dev/null
@@ -1,378 +0,0 @@
-//
-// ASLayout.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-
-#import
-#import
-
-#import
-#import
-#import "ASLayoutSpecUtilities.h"
-#import "ASLayoutSpec+Subclasses.h"
-
-#import
-#import
-#import
-
-NSString *const ASThreadDictMaxConstraintSizeKey = @"kASThreadDictMaxConstraintSizeKey";
-
-CGPoint const ASPointNull = {NAN, NAN};
-
-BOOL ASPointIsNull(CGPoint point)
-{
- return isnan(point.x) && isnan(point.y);
-}
-
-/**
- * Creates an defined number of " |" indent blocks for the recursive description.
- */
-ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString * descriptionIndents(NSUInteger indents)
-{
- NSMutableString *description = [NSMutableString string];
- for (NSUInteger i = 0; i < indents; i++) {
- [description appendString:@" |"];
- }
- if (indents > 0) {
- [description appendString:@" "];
- }
- return description;
-}
-
-ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutIsDisplayNodeType(ASLayout *layout)
-{
- return layout.type == ASLayoutElementTypeDisplayNode;
-}
-
-@interface ASLayout ()
-{
- ASLayoutElementType _layoutElementType;
- std::atomic_bool _retainSublayoutElements;
-}
-@end
-
-@implementation ASLayout
-
-@dynamic frame, type;
-
-static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(NO);
-
-+ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain
-{
- static_retainsSublayoutLayoutElements.store(shouldRetain);
-}
-
-+ (BOOL)shouldRetainSublayoutLayoutElements
-{
- return static_retainsSublayoutLayoutElements.load();
-}
-
-- (instancetype)initWithLayoutElement:(id)layoutElement
- size:(CGSize)size
- position:(CGPoint)position
- sublayouts:(nullable NSArray *)sublayouts
-{
- NSParameterAssert(layoutElement);
-
- self = [super init];
- if (self) {
-#if ASDISPLAYNODE_ASSERTIONS_ENABLED
- for (ASLayout *sublayout in sublayouts) {
- ASDisplayNodeAssert(ASPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout.");
- }
-#endif
-
- _layoutElement = layoutElement;
-
- // Read this now to avoid @c weak overhead later.
- _layoutElementType = layoutElement.layoutElementType;
-
- if (!ASIsCGSizeValidForSize(size)) {
- //ASDisplayNodeFailAssert(@"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutElement);
- size = CGSizeZero;
- } else {
- size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
- }
- _size = size;
-
- if (ASPointIsNull(position) == NO) {
- _position = ASCeilPointValues(position);
- } else {
- _position = position;
- }
-
- _sublayouts = [sublayouts copy] ?: @[];
-
- if ([ASLayout shouldRetainSublayoutLayoutElements]) {
- [self retainSublayoutElements];
- }
- }
-
- return self;
-}
-
-#pragma mark - Class Constructors
-
-+ (instancetype)layoutWithLayoutElement:(id)layoutElement
- size:(CGSize)size
- position:(CGPoint)position
- sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED
-{
- return [[self alloc] initWithLayoutElement:layoutElement
- size:size
- position:position
- sublayouts:sublayouts];
-}
-
-+ (instancetype)layoutWithLayoutElement:(id)layoutElement
- size:(CGSize)size
- sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED
-{
- return [self layoutWithLayoutElement:layoutElement
- size:size
- position:ASPointNull
- sublayouts:sublayouts];
-}
-
-+ (instancetype)layoutWithLayoutElement:(id)layoutElement size:(CGSize)size NS_RETURNS_RETAINED
-{
- return [self layoutWithLayoutElement:layoutElement
- size:size
- position:ASPointNull
- sublayouts:nil];
-}
-
-- (void)dealloc
-{
- if (_retainSublayoutElements.load()) {
- for (ASLayout *sublayout in _sublayouts) {
- // We retained this, so there's no risk of it deallocating on us.
- if (CFTypeRef cfElement = (__bridge CFTypeRef)sublayout->_layoutElement) {
- CFRelease(cfElement);
- }
- }
- }
-}
-
-#pragma mark - Sublayout Elements Caching
-
-- (void)retainSublayoutElements
-{
- if (_retainSublayoutElements.exchange(true)) {
- return;
- }
-
- for (ASLayout *sublayout in _sublayouts) {
- // CFBridgingRetain atomically casts and retains. We need the atomicity.
- CFBridgingRetain(sublayout->_layoutElement);
- }
-}
-
-#pragma mark - Layout Flattening
-
-- (BOOL)isFlattened
-{
- // A layout is flattened if its position is null, and all of its sublayouts are of type displaynode with no sublayouts.
- if (!ASPointIsNull(_position)) {
- return NO;
- }
-
- for (ASLayout *sublayout in _sublayouts) {
- if (ASLayoutIsDisplayNodeType(sublayout) == NO || sublayout->_sublayouts.count > 0) {
- return NO;
- }
- }
-
- return YES;
-}
-
-- (ASLayout *)filteredNodeLayoutTree NS_RETURNS_RETAINED
-{
- if ([self isFlattened]) {
- // All flattened layouts must retain sublayout elements until they are applied.
- [self retainSublayoutElements];
- return self;
- }
-
- struct Context {
- unowned ASLayout *layout;
- CGPoint absolutePosition;
- };
-
- // Queue used to keep track of sublayouts while traversing this layout in a DFS fashion.
- std::deque queue;
- for (ASLayout *sublayout in _sublayouts) {
- queue.push_back({sublayout, sublayout.position});
- }
-
- std::vector flattenedSublayouts;
-
- while (!queue.empty()) {
- const Context context = std::move(queue.front());
- queue.pop_front();
-
- unowned ASLayout *layout = context.layout;
- // Direct ivar access to avoid retain/release, use existing +1.
- const NSUInteger sublayoutsCount = layout->_sublayouts.count;
- const CGPoint absolutePosition = context.absolutePosition;
-
- if (ASLayoutIsDisplayNodeType(layout)) {
- if (sublayoutsCount > 0 || CGPointEqualToPoint(ASCeilPointValues(absolutePosition), layout.position) == NO) {
- // Only create a new layout if the existing one can't be reused, which means it has either some sublayouts or an invalid absolute position.
- const auto newLayout = [ASLayout layoutWithLayoutElement:layout->_layoutElement
- size:layout.size
- position:absolutePosition
- sublayouts:@[]];
- flattenedSublayouts.push_back(newLayout);
- } else {
- flattenedSublayouts.push_back(layout);
- }
- } else if (sublayoutsCount > 0) {
- // Fast-reverse-enumerate the sublayouts array by copying it into a C-array and push_front'ing each into the queue.
- unowned ASLayout *rawSublayouts[sublayoutsCount];
- [layout->_sublayouts getObjects:rawSublayouts range:NSMakeRange(0, sublayoutsCount)];
- for (NSInteger i = sublayoutsCount - 1; i >= 0; i--) {
- queue.push_front({rawSublayouts[i], absolutePosition + rawSublayouts[i].position});
- }
- }
- }
-
- NSArray *array = [NSArray arrayByTransferring:flattenedSublayouts.data() count:flattenedSublayouts.size()];
- // flattenedSublayouts is now all nils.
-
- ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size sublayouts:array];
- // All flattened layouts must retain sublayout elements until they are applied.
- [layout retainSublayoutElements];
- return layout;
-}
-
-#pragma mark - Equality Checking
-
-- (BOOL)isEqual:(id)object
-{
- if (self == object) return YES;
-
- ASLayout *layout = ASDynamicCast(object, ASLayout);
- if (layout == nil) {
- return NO;
- }
-
- if (!CGSizeEqualToSize(_size, layout.size)) return NO;
-
- if (!((ASPointIsNull(self.position) && ASPointIsNull(layout.position))
- || CGPointEqualToPoint(self.position, layout.position))) return NO;
- if (_layoutElement != layout.layoutElement) return NO;
-
- if (!ASObjectIsEqual(_sublayouts, layout.sublayouts)) {
- return NO;
- }
-
- return YES;
-}
-
-#pragma mark - Accessors
-
-- (ASLayoutElementType)type
-{
- return _layoutElementType;
-}
-
-- (CGRect)frameForElement:(id)layoutElement
-{
- for (ASLayout *l in _sublayouts) {
- if (l->_layoutElement == layoutElement) {
- return l.frame;
- }
- }
- return CGRectNull;
-}
-
-- (CGRect)frame
-{
- CGRect subnodeFrame = CGRectZero;
- CGPoint adjustedOrigin = _position;
- if (isfinite(adjustedOrigin.x) == NO) {
- ASDisplayNodeAssert(0, @"Layout has an invalid position");
- adjustedOrigin.x = 0;
- }
- if (isfinite(adjustedOrigin.y) == NO) {
- ASDisplayNodeAssert(0, @"Layout has an invalid position");
- adjustedOrigin.y = 0;
- }
- subnodeFrame.origin = adjustedOrigin;
-
- CGSize adjustedSize = _size;
- if (isfinite(adjustedSize.width) == NO) {
- ASDisplayNodeAssert(0, @"Layout has an invalid size");
- adjustedSize.width = 0;
- }
- if (isfinite(adjustedSize.height) == NO) {
- ASDisplayNodeAssert(0, @"Layout has an invalid position");
- adjustedSize.height = 0;
- }
- subnodeFrame.size = adjustedSize;
-
- return subnodeFrame;
-}
-
-#pragma mark - Description
-
-- (NSMutableArray *)propertiesForDescription
-{
- NSMutableArray *result = [NSMutableArray array];
- [result addObject:@{ @"size" : [NSValue valueWithCGSize:self.size] }];
-
- if (id layoutElement = self.layoutElement) {
- [result addObject:@{ @"layoutElement" : layoutElement }];
- }
-
- const auto pos = self.position;
- if (!ASPointIsNull(pos)) {
- [result addObject:@{ @"position" : [NSValue valueWithCGPoint:pos] }];
- }
- return result;
-}
-
-- (NSString *)description
-{
- return ASObjectDescriptionMake(self, [self propertiesForDescription]);
-}
-
-- (NSString *)recursiveDescription
-{
- return [self _recursiveDescriptionForLayout:self level:0];
-}
-
-- (NSString *)_recursiveDescriptionForLayout:(ASLayout *)layout level:(NSUInteger)level
-{
- NSMutableString *description = [NSMutableString string];
- [description appendString:descriptionIndents(level)];
- [description appendString:[layout description]];
- for (ASLayout *sublayout in layout.sublayouts) {
- [description appendString:@"\n"];
- [description appendString:[self _recursiveDescriptionForLayout:sublayout level:level + 1]];
- }
- return description;
-}
-
-@end
-
-ASLayout *ASCalculateLayout(id layoutElement, const ASSizeRange sizeRange, const CGSize parentSize)
-{
- NSCParameterAssert(layoutElement != nil);
-
- return [layoutElement layoutThatFits:sizeRange parentSize:parentSize];
-}
-
-ASLayout *ASCalculateRootLayout(id rootLayoutElement, const ASSizeRange sizeRange)
-{
- ASLayout *layout = ASCalculateLayout(rootLayoutElement, sizeRange, sizeRange.max);
- // Here could specific verfication happen
- return layout;
-}
diff --git a/submodules/AsyncDisplayKit/Source/ASLayoutElement.mm b/submodules/AsyncDisplayKit/Source/ASLayoutElement.mm
deleted file mode 100644
index 887f609e99..0000000000
--- a/submodules/AsyncDisplayKit/Source/ASLayoutElement.mm
+++ /dev/null
@@ -1,843 +0,0 @@
-//
-// ASLayoutElement.mm
-// Texture
-//
-// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
-// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
-// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
-//
-
-#import
-#import
-#import
-#import
-#import
-#import
-#import
-
-#import
-#include
-
-using AS::MutexLocker;
-
-#if YOGA
- #import YOGA_HEADER_PATH
- #import
-#endif
-
-#pragma mark - ASLayoutElementContext
-
-@implementation ASLayoutElementContext
-
-- (instancetype)init
-{
- if (self = [super init]) {
- _transitionID = ASLayoutElementContextDefaultTransitionID;
- }
- return self;
-}
-
-@end
-
-CGFloat const ASLayoutElementParentDimensionUndefined = NAN;
-CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined};
-
-int32_t const ASLayoutElementContextInvalidTransitionID = 0;
-int32_t const ASLayoutElementContextDefaultTransitionID = ASLayoutElementContextInvalidTransitionID + 1;
-
-#if AS_TLS_AVAILABLE
-
-static _Thread_local __unsafe_unretained ASLayoutElementContext *tls_context;
-
-void ASLayoutElementPushContext(ASLayoutElementContext *context)
-{
- // NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
- ASDisplayNodeCAssertNil(tls_context, @"Nested ASLayoutElementContexts aren't supported.");
-
- tls_context = (__bridge ASLayoutElementContext *)(__bridge_retained CFTypeRef)context;
-}
-
-ASLayoutElementContext *ASLayoutElementGetCurrentContext()
-{
- // Don't retain here. Caller will retain if it wants to!
- return tls_context;
-}
-
-void ASLayoutElementPopContext()
-{
- ASDisplayNodeCAssertNotNil(tls_context, @"Attempt to pop context when there wasn't a context!");
- CFRelease((__bridge CFTypeRef)tls_context);
- tls_context = nil;
-}
-
-#else
-
-static pthread_key_t ASLayoutElementContextKey() {
- static pthread_key_t k;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- pthread_key_create(&k, NULL);
- });
- return k;
-}
-void ASLayoutElementPushContext(ASLayoutElementContext *context)
-{
- // NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
- ASDisplayNodeCAssertNil(pthread_getspecific(ASLayoutElementContextKey()), @"Nested ASLayoutElementContexts aren't supported.");
-
- const auto cfCtx = (__bridge_retained CFTypeRef)context;
- pthread_setspecific(ASLayoutElementContextKey(), cfCtx);
-}
-
-ASLayoutElementContext *ASLayoutElementGetCurrentContext()
-{
- // Don't retain here. Caller will retain if it wants to!
- const auto ctxPtr = pthread_getspecific(ASLayoutElementContextKey());
- return (__bridge ASLayoutElementContext *)ctxPtr;
-}
-
-void ASLayoutElementPopContext()
-{
- const auto ctx = (CFTypeRef)pthread_getspecific(ASLayoutElementContextKey());
- ASDisplayNodeCAssertNotNil(ctx, @"Attempt to pop context when there wasn't a context!");
- CFRelease(ctx);
- pthread_setspecific(ASLayoutElementContextKey(), NULL);
-}
-
-#endif // AS_TLS_AVAILABLE
-
-#pragma mark - ASLayoutElementStyle
-
-NSString * const ASLayoutElementStyleWidthProperty = @"ASLayoutElementStyleWidthProperty";
-NSString * const ASLayoutElementStyleMinWidthProperty = @"ASLayoutElementStyleMinWidthProperty";
-NSString * const ASLayoutElementStyleMaxWidthProperty = @"ASLayoutElementStyleMaxWidthProperty";
-
-NSString * const ASLayoutElementStyleHeightProperty = @"ASLayoutElementStyleHeightProperty";
-NSString * const ASLayoutElementStyleMinHeightProperty = @"ASLayoutElementStyleMinHeightProperty";
-NSString * const ASLayoutElementStyleMaxHeightProperty = @"ASLayoutElementStyleMaxHeightProperty";
-
-NSString * const ASLayoutElementStyleSpacingBeforeProperty = @"ASLayoutElementStyleSpacingBeforeProperty";
-NSString * const ASLayoutElementStyleSpacingAfterProperty = @"ASLayoutElementStyleSpacingAfterProperty";
-NSString * const ASLayoutElementStyleFlexGrowProperty = @"ASLayoutElementStyleFlexGrowProperty";
-NSString * const ASLayoutElementStyleFlexShrinkProperty = @"ASLayoutElementStyleFlexShrinkProperty";
-NSString * const ASLayoutElementStyleFlexBasisProperty = @"ASLayoutElementStyleFlexBasisProperty";
-NSString * const ASLayoutElementStyleAlignSelfProperty = @"ASLayoutElementStyleAlignSelfProperty";
-NSString * const ASLayoutElementStyleAscenderProperty = @"ASLayoutElementStyleAscenderProperty";
-NSString * const ASLayoutElementStyleDescenderProperty = @"ASLayoutElementStyleDescenderProperty";
-
-NSString * const ASLayoutElementStyleLayoutPositionProperty = @"ASLayoutElementStyleLayoutPositionProperty";
-
-#if YOGA
-NSString * const ASYogaFlexWrapProperty = @"ASLayoutElementStyleLayoutFlexWrapProperty";
-NSString * const ASYogaFlexDirectionProperty = @"ASYogaFlexDirectionProperty";
-NSString * const ASYogaDirectionProperty = @"ASYogaDirectionProperty";
-NSString * const ASYogaSpacingProperty = @"ASYogaSpacingProperty";
-NSString * const ASYogaJustifyContentProperty = @"ASYogaJustifyContentProperty";
-NSString * const ASYogaAlignItemsProperty = @"ASYogaAlignItemsProperty";
-NSString * const ASYogaPositionTypeProperty = @"ASYogaPositionTypeProperty";
-NSString * const ASYogaPositionProperty = @"ASYogaPositionProperty";
-NSString * const ASYogaMarginProperty = @"ASYogaMarginProperty";
-NSString * const ASYogaPaddingProperty = @"ASYogaPaddingProperty";
-NSString * const ASYogaBorderProperty = @"ASYogaBorderProperty";
-NSString * const ASYogaAspectRatioProperty = @"ASYogaAspectRatioProperty";
-#endif
-
-#define ASLayoutElementStyleSetSizeWithScope(x) \
- __instanceLock__.lock(); \
- ASLayoutElementSize newSize = _size.load(); \
- { x } \
- _size.store(newSize); \
- __instanceLock__.unlock();
-
-#define ASLayoutElementStyleCallDelegate(propertyName)\
-do {\
- [self propertyDidChange:propertyName];\
- [_delegate style:self propertyDidChange:propertyName];\
-} while(0)
-
-@implementation ASLayoutElementStyle {
- AS::RecursiveMutex __instanceLock__;
- ASLayoutElementStyleExtensions _extensions;
-
- std::atomic _size;
- std::atomic _spacingBefore;
- std::atomic _spacingAfter;
- std::atomic _flexGrow;
- std::atomic _flexShrink;
- std::atomic _flexBasis;
- std::atomic _alignSelf;
- std::atomic _ascender;
- std::atomic _descender;
- std::atomic _layoutPosition;
-
-#if YOGA
- YGNodeRef _yogaNode;
- std::atomic _flexWrap;
- std::atomic _flexDirection;
- std::atomic _direction;
- std::atomic _justifyContent;
- std::atomic _alignItems;
- std::atomic