Files
react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
T
Valentin Shergin 7c83eaba80 Fabric: Fixed crash in [RCTParagraphComponentView accessibilityElements]
Summary:
Now we check for `_state` being nullptr before dereferencing this.

`accessibilityElements` (as a bunch of other methods that have the same check) can be called by OS any moment (we cannot predict when), so at some very rare moments, it can be called when the view is not fully initialized yet. To prevent crashing we just need to return some default value indicating that the view is empty.

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: sammy-SC

Differential Revision: D24095654

fbshipit-source-id: d8a37f269c5bf7718acb0b512e7a9313e199398e
2020-10-04 19:12:22 -07:00

207 lines
6.5 KiB
Plaintext

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTParagraphComponentView.h"
#import "RCTParagraphComponentAccessibilityProvider.h"
#import <react/renderer/components/text/ParagraphComponentDescriptor.h>
#import <react/renderer/components/text/ParagraphProps.h>
#import <react/renderer/components/text/ParagraphState.h>
#import <react/renderer/components/text/RawTextComponentDescriptor.h>
#import <react/renderer/components/text/TextComponentDescriptor.h>
#import <react/renderer/graphics/Geometry.h>
#import <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
#import <react/renderer/textlayoutmanager/RCTTextLayoutManager.h>
#import <react/renderer/textlayoutmanager/TextLayoutManager.h>
#import <react/utils/ManagedObjectWrapper.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
using namespace facebook::react;
@implementation RCTParagraphComponentView {
ParagraphShadowNode::ConcreteState::Shared _state;
ParagraphAttributes _paragraphAttributes;
RCTParagraphComponentAccessibilityProvider *_accessibilityProvider;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const ParagraphProps>();
_props = defaultProps;
self.isAccessibilityElement = YES;
self.opaque = NO;
self.contentMode = UIViewContentModeRedraw;
}
return self;
}
- (NSString *)description
{
NSString *superDescription = [super description];
// Cutting the last `>` character.
if (superDescription.length > 0 && [superDescription characterAtIndex:superDescription.length - 1] == '>') {
superDescription = [superDescription substringToIndex:superDescription.length - 1];
}
return [NSString stringWithFormat:@"%@; attributedText = %@>", superDescription, self.attributedText];
}
- (NSAttributedString *_Nullable)attributedText
{
if (!_state) {
return nil;
}
return RCTNSAttributedStringFromAttributedString(_state->getData().attributedString);
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<ParagraphComponentDescriptor>();
}
+ (std::vector<facebook::react::ComponentDescriptorProvider>)supplementalComponentDescriptorProviders
{
return {concreteComponentDescriptorProvider<RawTextComponentDescriptor>(),
concreteComponentDescriptorProvider<TextComponentDescriptor>()};
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &paragraphProps = std::static_pointer_cast<const ParagraphProps>(props);
assert(paragraphProps);
_paragraphAttributes = paragraphProps->paragraphAttributes;
[super updateProps:props oldProps:oldProps];
}
- (void)updateState:(State::Shared const &)state oldState:(State::Shared const &)oldState
{
_state = std::static_pointer_cast<ParagraphShadowNode::ConcreteState const>(state);
[self setNeedsDisplay];
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_state.reset();
}
- (void)drawRect:(CGRect)rect
{
if (!_state) {
return;
}
auto textLayoutManager = _state->getData().layoutManager;
assert(textLayoutManager && "TextLayoutManager must not be `nullptr`.");
if (!textLayoutManager) {
return;
}
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
[nativeTextLayoutManager drawAttributedString:_state->getData().attributedString
paragraphAttributes:_paragraphAttributes
frame:frame];
}
#pragma mark - Accessibility
- (NSString *)accessibilityLabel
{
NSString *superAccessibilityLabel = RCTNSStringFromStringNilIfEmpty(_props->accessibilityLabel);
if (superAccessibilityLabel) {
return superAccessibilityLabel;
}
if (!_state) {
return nil;
}
return RCTNSStringFromString(_state->getData().attributedString.getString());
}
- (NSArray *)accessibilityElements
{
if (!_state) {
return [NSArray new];
}
auto &data = _state->getData();
if (![_accessibilityProvider isUpToDate:data.attributedString]) {
RCTTextLayoutManager *textLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(data.layoutManager->getNativeTextLayoutManager());
CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
_accessibilityProvider = [[RCTParagraphComponentAccessibilityProvider alloc] initWithString:data.attributedString
layoutManager:textLayoutManager
paragraphAttributes:data.paragraphAttributes
frame:frame
view:self];
}
self.isAccessibilityElement = NO;
return _accessibilityProvider.accessibilityElements;
}
- (UIAccessibilityTraits)accessibilityTraits
{
return [super accessibilityTraits] | UIAccessibilityTraitStaticText;
}
- (SharedTouchEventEmitter)touchEventEmitterAtPoint:(CGPoint)point
{
if (!_state) {
return _eventEmitter;
}
auto textLayoutManager = _state->getData().layoutManager;
assert(textLayoutManager && "TextLayoutManager must not be `nullptr`.");
if (!textLayoutManager) {
return _eventEmitter;
}
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
auto eventEmitter = [nativeTextLayoutManager getEventEmitterWithAttributeString:_state->getData().attributedString
paragraphAttributes:_paragraphAttributes
frame:frame
atPoint:point];
if (!eventEmitter) {
return _eventEmitter;
}
assert(std::dynamic_pointer_cast<const TouchEventEmitter>(eventEmitter));
return std::static_pointer_cast<const TouchEventEmitter>(eventEmitter);
}
@end
Class<RCTComponentViewProtocol> RCTParagraphCls(void)
{
return RCTParagraphComponentView.class;
}