Cleanup ASTextNode and add locking

For now we use a big recursive lock. This needs to be revisited as we revisit the whole ASDK locking strategy.
This commit is contained in:
Michael Schneider 2016-06-15 16:53:15 -07:00
parent 22fa715682
commit 3d72a6b7e5

View File

@ -11,22 +11,19 @@
#import "ASTextNode.h" #import "ASTextNode.h"
#import "ASTextNode+Beta.h" #import "ASTextNode+Beta.h"
#include <mutex>
#import <AsyncDisplayKit/_ASDisplayLayer.h> #import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> #import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h> #import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h> #import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h> #import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import "ASTextKitCoreTextAdditions.h" #import "ASTextKitCoreTextAdditions.h"
#import "ASTextKitComponents.h"
#import "ASTextKitFontSizeAdjuster.h"
#import "ASTextKitRenderer.h"
#import "ASTextKitRenderer+Positioning.h" #import "ASTextKitRenderer+Positioning.h"
#import "ASTextKitShadower.h" #import "ASTextKitShadower.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASEqualityHelpers.h"
#import "ASLayout.h" #import "ASLayout.h"
static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15; static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15;
@ -38,15 +35,13 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
@interface ASTextNodeDrawParameters : NSObject @interface ASTextNodeDrawParameters : NSObject
@property (nonatomic, assign, readonly) CGRect bounds; @property (nonatomic, assign, readonly) CGRect bounds;
@property (nonatomic, strong, readonly) UIColor *backgroundColor; @property (nonatomic, strong, readonly) UIColor *backgroundColor;
@end @end
@implementation ASTextNodeDrawParameters @implementation ASTextNodeDrawParameters
- (instancetype)initWithBounds:(CGRect)bounds - (instancetype)initWithBounds:(CGRect)bounds backgroundColor:(UIColor *)backgroundColor
backgroundColor:(UIColor *)backgroundColor
{ {
if (self = [super init]) { if (self = [super init]) {
_bounds = bounds; _bounds = bounds;
@ -76,7 +71,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
NSRange _highlightRange; NSRange _highlightRange;
ASHighlightOverlayLayer *_activeHighlightLayer; ASHighlightOverlayLayer *_activeHighlightLayer;
ASDN::Mutex _rendererLock; std::recursive_mutex _textLock;
CGSize _constrainedSize; CGSize _constrainedSize;
@ -160,6 +155,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (NSString *)description - (NSString *)description
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSString *truncationString = [_composedTruncationText string]; NSString *truncationString = [_composedTruncationText string];
if (plainString.length > 50) if (plainString.length > 50)
@ -227,7 +224,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds - (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds
{ {
ASDN::MutexLocker l(_rendererLock); std::lock_guard<std::recursive_mutex> l(_textLock);
if (_renderer == nil) { if (_renderer == nil) {
CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size; CGSize constrainedSize = _constrainedSize.width != -INFINITY ? _constrainedSize : bounds.size;
_renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes] _renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:[self _rendererAttributes]
@ -250,9 +248,28 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
}; };
} }
- (void)_invalidateRendererIfNeeded
{
[self _invalidateRendererIfNeededForBoundsSize:self.threadSafeBounds.size];
}
- (void)_invalidateRendererIfNeededForBoundsSize:(CGSize)boundsSize
{
if ([self _needInvalidateRendererForBoundsSize:boundsSize]) {
// Our bounds of frame have changed to a size that is not identical to our constraining size,
// so our previous layout information is invalid, and TextKit may draw at the
// incorrect origin.
{
std::lock_guard<std::recursive_mutex> l(_textLock);
_constrainedSize = CGSizeMake(-INFINITY, -INFINITY);
}
[self _invalidateRenderer];
}
}
- (void)_invalidateRenderer - (void)_invalidateRenderer
{ {
ASDN::MutexLocker l(_rendererLock); std::lock_guard<std::recursive_mutex> l(_textLock);
if (_renderer) { if (_renderer) {
// Destruction of the layout managers/containers/text storage is quite // Destruction of the layout managers/containers/text storage is quite
@ -267,27 +284,13 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
} }
} }
- (void)_invalidateRendererIfNeeded
{
[self _invalidateRendererIfNeededForBoundsSize:self.threadSafeBounds.size];
}
- (void)_invalidateRendererIfNeededForBoundsSize:(CGSize)boundsSize
{
if ([self _needInvalidateRendererForBoundsSize:boundsSize]) {
// Our bounds of frame have changed to a size that is not identical to our constraining size,
// so our previous layout information is invalid, and TextKit may draw at the
// incorrect origin.
_constrainedSize = CGSizeMake(-INFINITY, -INFINITY);
[self _invalidateRenderer];
}
}
#pragma mark - Layout and Sizing #pragma mark - Layout and Sizing
- (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize - (BOOL)_needInvalidateRendererForBoundsSize:(CGSize)boundsSize
{ {
if (!_renderer) { std::lock_guard<std::recursive_mutex> l(_textLock);
if (_renderer == nil) {
return YES; return YES;
} }
@ -322,6 +325,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
[super calculatedLayoutDidChange]; [super calculatedLayoutDidChange];
ASLayout *layout = self.calculatedLayout; ASLayout *layout = self.calculatedLayout;
std::lock_guard<std::recursive_mutex> l(_textLock);
if (layout != nil) { if (layout != nil) {
_constrainedSize = layout.size; _constrainedSize = layout.size;
_renderer.constrainedSize = layout.size; _renderer.constrainedSize = layout.size;
@ -333,6 +338,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width);
ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height);
std::lock_guard<std::recursive_mutex> l(_textLock);
_constrainedSize = constrainedSize; _constrainedSize = constrainedSize;
// Instead of invalidating the renderer, in case this is a new call with a different constrained size, // Instead of invalidating the renderer, in case this is a new call with a different constrained size,
@ -341,7 +348,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
[self setNeedsDisplay]; [self setNeedsDisplay];
CGSize size = [[self _renderer] size]; CGSize size = [self _renderer].size;
if (_attributedText.length > 0) { if (_attributedText.length > 0) {
CGFloat screenScale = ASScreenScale(); CGFloat screenScale = ASScreenScale();
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale; self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
@ -359,6 +366,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)setAttributedText:(NSAttributedString *)attributedText - (void)setAttributedText:(NSAttributedString *)attributedText
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (attributedText == nil) { if (attributedText == nil) {
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
} }
@ -388,16 +397,18 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
[self setNeedsDisplay]; [self setNeedsDisplay];
self.accessibilityLabel = _attributedText.string;
// We're an accessibility element by default if there is a string. // Accessiblity
self.isAccessibilityElement = _attributedText.length != 0; self.accessibilityLabel = _attributedText.string;
self.isAccessibilityElement = (_attributedText.length != 0); // We're an accessibility element by default if there is a string.
} }
#pragma mark - Text Layout #pragma mark - Text Layout
- (void)setExclusionPaths:(NSArray *)exclusionPaths - (void)setExclusionPaths:(NSArray *)exclusionPaths
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (ASObjectIsEqual(exclusionPaths, _exclusionPaths)) { if (ASObjectIsEqual(exclusionPaths, _exclusionPaths)) {
return; return;
} }
@ -410,6 +421,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (NSArray *)exclusionPaths - (NSArray *)exclusionPaths
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return _exclusionPaths; return _exclusionPaths;
} }
@ -417,6 +430,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing - (void)drawRect:(CGRect)bounds withParameters:(ASTextNodeDrawParameters *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextRef context = UIGraphicsGetCurrentContext();
ASDisplayNodeAssert(context, @"This is no good without a context."); ASDisplayNodeAssert(context, @"This is no good without a context.");
@ -437,7 +452,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
} }
// Draw shadow // Draw shadow
[[renderer shadower] setShadowInContext:context]; [renderer.shadower setShadowInContext:context];
// Draw text // Draw text
bounds.origin = textOrigin; bounds.origin = textOrigin;
@ -470,6 +485,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
forHighlighting:(BOOL)highlighting forHighlighting:(BOOL)highlighting
{ {
ASDisplayNodeAssertMainThread();
ASTextKitRenderer *renderer = [self _renderer]; ASTextKitRenderer *renderer = [self _renderer];
NSRange visibleRange = renderer.firstVisibleRange; NSRange visibleRange = renderer.firstVisibleRange;
NSAttributedString *attributedString = _attributedText; NSAttributedString *attributedString = _attributedText;
@ -562,6 +579,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{ {
ASDisplayNodeAssertMainThread();
if (gestureRecognizer == _longPressGestureRecognizer) { if (gestureRecognizer == _longPressGestureRecognizer) {
// Don't allow long press on truncation message // Don't allow long press on truncation message
if ([self _pendingTruncationTap]) { if ([self _pendingTruncationTap]) {
@ -593,6 +612,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (NSRange)highlightRange - (NSRange)highlightRange
{ {
ASDisplayNodeAssertMainThread();
return _highlightRange; return _highlightRange;
} }
@ -603,6 +624,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated - (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated
{ {
ASDisplayNodeAssertMainThread();
[self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated]; [self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated];
} }
@ -672,7 +695,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count]; NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count];
for (NSValue *rectValue in highlightRects) { for (NSValue *rectValue in highlightRects) {
UIEdgeInsets shadowPadding = _renderer.shadower.shadowPadding; UIEdgeInsets shadowPadding = _renderer.shadower.shadowPadding;
CGRect rendererRect = [[self class] _adjustRendererRect:rectValue.CGRectValue forShadowPadding:shadowPadding]; CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.CGRectValue, shadowPadding);
CGRect highlightedRect = [self.layer convertRect:rendererRect toLayer:highlightTargetLayer]; CGRect highlightedRect = [self.layer convertRect:rendererRect toLayer:highlightTargetLayer];
// We set our overlay layer's frame to the bounds of the highlight target layer. // We set our overlay layer's frame to the bounds of the highlight target layer.
@ -709,6 +732,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)_clearHighlightIfNecessary - (void)_clearHighlightIfNecessary
{ {
ASDisplayNodeAssertMainThread();
if ([self _pendingLinkTap] || [self _pendingTruncationTap]) { if ([self _pendingLinkTap] || [self _pendingTruncationTap]) {
[self setHighlightRange:NSMakeRange(0, 0) animated:YES]; [self setHighlightRange:NSMakeRange(0, 0) animated:YES];
} }
@ -726,29 +751,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
#pragma mark - Text rects #pragma mark - Text rects
+ (CGRect)_adjustRendererRect:(CGRect)rendererRect forShadowPadding:(UIEdgeInsets)shadowPadding static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) {
{
rendererRect.origin.x -= shadowPadding.left; rendererRect.origin.x -= shadowPadding.left;
rendererRect.origin.y -= shadowPadding.top; rendererRect.origin.y -= shadowPadding.top;
return rendererRect; return rendererRect;
} }
- (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption
{
NSArray *rects = [[self _renderer] rectsForTextRange:textRange measureOption:measureOption];
NSMutableArray *adjustedRects = [NSMutableArray array];
for (NSValue *rectValue in rects) {
CGRect rect = [rectValue CGRectValue];
rect = [self.class _adjustRendererRect:rect forShadowPadding:self.shadowPadding];
NSValue *adjustedRectValue = [NSValue valueWithCGRect:rect];
[adjustedRects addObject:adjustedRectValue];
}
return adjustedRects;
}
- (NSArray *)rectsForTextRange:(NSRange)textRange - (NSArray *)rectsForTextRange:(NSRange)textRange
{ {
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionCapHeight]; return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionCapHeight];
@ -759,22 +767,46 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionBlock]; return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionBlock];
} }
- (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption
{
std::lock_guard<std::recursive_mutex> l(_textLock);
NSArray *rects = [[self _renderer] rectsForTextRange:textRange measureOption:measureOption];
NSMutableArray *adjustedRects = [NSMutableArray array];
for (NSValue *rectValue in rects) {
CGRect rect = [rectValue CGRectValue];
rect = ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding);
NSValue *adjustedRectValue = [NSValue valueWithCGRect:rect];
[adjustedRects addObject:adjustedRectValue];
}
return adjustedRects;
}
- (CGRect)trailingRect - (CGRect)trailingRect
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
CGRect rect = [[self _renderer] trailingRect]; CGRect rect = [[self _renderer] trailingRect];
return [self.class _adjustRendererRect:rect forShadowPadding:self.shadowPadding]; return ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding);
} }
- (CGRect)frameForTextRange:(NSRange)textRange - (CGRect)frameForTextRange:(NSRange)textRange
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
CGRect frame = [[self _renderer] frameForTextRange:textRange]; CGRect frame = [[self _renderer] frameForTextRange:textRange];
return [self.class _adjustRendererRect:frame forShadowPadding:self.shadowPadding]; return ASTextNodeAdjustRenderRectForShadowPadding(frame, self.shadowPadding);
} }
#pragma mark - Placeholders #pragma mark - Placeholders
- (void)setPlaceholderColor:(UIColor *)placeholderColor - (void)setPlaceholderColor:(UIColor *)placeholderColor
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
_placeholderColor = placeholderColor; _placeholderColor = placeholderColor;
// prevent placeholders if we don't have a color // prevent placeholders if we don't have a color
@ -790,6 +822,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
return nil; return nil;
} }
std::lock_guard<std::recursive_mutex> l(_textLock);
UIGraphicsBeginImageContext(size); UIGraphicsBeginImageContext(size);
[self.placeholderColor setFill]; [self.placeholderColor setFill];
@ -818,6 +852,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{ {
ASDisplayNodeAssertMainThread();
if (!_passthroughNonlinkTouches) { if (!_passthroughNonlinkTouches) {
return [super pointInside:point withEvent:event]; return [super pointInside:point withEvent:event];
} }
@ -846,9 +882,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ {
[super touchesBegan:touches withEvent:event];
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
[super touchesBegan:touches withEvent:event];
CGPoint point = [[touches anyObject] locationInView:self.view]; CGPoint point = [[touches anyObject] locationInView:self.view];
@ -878,6 +913,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{ {
ASDisplayNodeAssertMainThread();
[super touchesCancelled:touches withEvent:event]; [super touchesCancelled:touches withEvent:event];
[self _clearHighlightIfNecessary]; [self _clearHighlightIfNecessary];
@ -885,6 +921,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{ {
ASDisplayNodeAssertMainThread();
[super touchesEnded:touches withEvent:event]; [super touchesEnded:touches withEvent:event];
if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) { if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) {
@ -903,6 +940,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ {
ASDisplayNodeAssertMainThread();
[super touchesMoved:touches withEvent:event]; [super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject]; UITouch *touch = [touches anyObject];
@ -928,6 +966,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer - (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
{ {
ASDisplayNodeAssertMainThread();
// Respond to long-press when it begins, not when it ends. // Respond to long-press when it begins, not when it ends.
if (longPressRecognizer.state == UIGestureRecognizerStateBegan) { if (longPressRecognizer.state == UIGestureRecognizerStateBegan) {
if ([self.delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) { if ([self.delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) {
@ -939,11 +979,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (BOOL)_pendingLinkTap - (BOOL)_pendingLinkTap
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil; return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil;
} }
- (BOOL)_pendingTruncationTap - (BOOL)_pendingTruncationTap
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName]; return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName];
} }
@ -951,11 +995,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (CGColorRef)shadowColor - (CGColorRef)shadowColor
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return _shadowColor; return _shadowColor;
} }
- (void)setShadowColor:(CGColorRef)shadowColor - (void)setShadowColor:(CGColorRef)shadowColor
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (_shadowColor != shadowColor) { if (_shadowColor != shadowColor) {
if (shadowColor != NULL) { if (shadowColor != NULL) {
CGColorRetain(shadowColor); CGColorRetain(shadowColor);
@ -968,11 +1016,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (CGSize)shadowOffset - (CGSize)shadowOffset
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return _shadowOffset; return _shadowOffset;
} }
- (void)setShadowOffset:(CGSize)shadowOffset - (void)setShadowOffset:(CGSize)shadowOffset
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) { if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) {
_shadowOffset = shadowOffset; _shadowOffset = shadowOffset;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -982,11 +1034,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (CGFloat)shadowOpacity - (CGFloat)shadowOpacity
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return _shadowOpacity; return _shadowOpacity;
} }
- (void)setShadowOpacity:(CGFloat)shadowOpacity - (void)setShadowOpacity:(CGFloat)shadowOpacity
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (_shadowOpacity != shadowOpacity) { if (_shadowOpacity != shadowOpacity) {
_shadowOpacity = shadowOpacity; _shadowOpacity = shadowOpacity;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -996,11 +1052,15 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (CGFloat)shadowRadius - (CGFloat)shadowRadius
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return _shadowRadius; return _shadowRadius;
} }
- (void)setShadowRadius:(CGFloat)shadowRadius - (void)setShadowRadius:(CGFloat)shadowRadius
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (_shadowRadius != shadowRadius) { if (_shadowRadius != shadowRadius) {
_shadowRadius = shadowRadius; _shadowRadius = shadowRadius;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -1015,6 +1075,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
- (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer - (UIEdgeInsets)shadowPaddingWithRenderer:(ASTextKitRenderer *)renderer
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return renderer.shadower.shadowPadding; return renderer.shadower.shadowPadding;
} }
@ -1032,6 +1094,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText - (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) { if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) {
return; return;
} }
@ -1042,6 +1106,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage - (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) { if (ASObjectIsEqual(_additionalTruncationMessage, additionalTruncationMessage)) {
return; return;
} }
@ -1052,6 +1118,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)setTruncationMode:(NSLineBreakMode)truncationMode - (void)setTruncationMode:(NSLineBreakMode)truncationMode
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (_truncationMode != truncationMode) { if (_truncationMode != truncationMode) {
_truncationMode = truncationMode; _truncationMode = truncationMode;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -1061,12 +1129,16 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (BOOL)isTruncated - (BOOL)isTruncated
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
ASTextKitRenderer *renderer = [self _renderer]; ASTextKitRenderer *renderer = [self _renderer];
return renderer.firstVisibleRange.length < _attributedText.length; return renderer.firstVisibleRange.length < _attributedText.length;
} }
- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors - (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) { if ([_pointSizeScaleFactors isEqualToArray:pointSizeScaleFactors] == NO) {
_pointSizeScaleFactors = pointSizeScaleFactors; _pointSizeScaleFactors = pointSizeScaleFactors;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -1075,6 +1147,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines - (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
if (_maximumNumberOfLines != maximumNumberOfLines) { if (_maximumNumberOfLines != maximumNumberOfLines) {
_maximumNumberOfLines = maximumNumberOfLines; _maximumNumberOfLines = maximumNumberOfLines;
[self _invalidateRenderer]; [self _invalidateRenderer];
@ -1084,6 +1158,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (NSUInteger)lineCount - (NSUInteger)lineCount
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
return [[self _renderer] lineCount]; return [[self _renderer] lineCount];
} }
@ -1091,6 +1167,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
- (void)_updateComposedTruncationText - (void)_updateComposedTruncationText
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
_composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]]; _composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]];
} }
@ -1107,6 +1185,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
*/ */
- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange - (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
// Check if we even have an additional truncation message. // Check if we even have an additional truncation message.
if (!_additionalTruncationMessage) { if (!_additionalTruncationMessage) {
return NSMakeRange(NSNotFound, 0); return NSMakeRange(NSNotFound, 0);
@ -1118,8 +1198,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length; NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length;
// We get the location of the truncation token, then add the length of the // We get the location of the truncation token, then add the length of the
// truncation attributed string +1 for the space between. // truncation attributed string +1 for the space between.
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength); return NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength);
return range;
} }
/** /**
@ -1129,6 +1208,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
*/ */
- (NSAttributedString *)_composedTruncationText - (NSAttributedString *)_composedTruncationText
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
//If we have neither return the default //If we have neither return the default
if (!_additionalTruncationMessage && !_truncationAttributedText) { if (!_additionalTruncationMessage && !_truncationAttributedText) {
return _composedTruncationText; return _composedTruncationText;
@ -1157,6 +1238,8 @@ static NSAttributedString *DefaultTruncationAttributedString()
*/ */
- (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString - (NSAttributedString *)_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString
{ {
std::lock_guard<std::recursive_mutex> l(_textLock);
truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString); truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString);
NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy]; NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy];
// Grab the attributes from the full string // Grab the attributes from the full string