diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index f82cdeccc2b..ca9f8bd9928 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -16,11 +16,6 @@ #import #import -RCT_EXTERN BOOL RCTImageLoadingInstrumentationEnabled(void); -RCT_EXTERN BOOL RCTImageLoadingPerfInstrumentationEnabled(void); -RCT_EXTERN void RCTEnableImageLoadingInstrumentation(BOOL enabled); -RCT_EXTERN void RCTEnableImageLoadingPerfInstrumentation(BOOL enabled); - @interface RCTImageLoader : NSObject - (instancetype)init; - (instancetype)initWithRedirectDelegate:(id)redirectDelegate NS_DESIGNATED_INITIALIZER; diff --git a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h index 76dcd71452a..c785369c0bf 100644 --- a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h +++ b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h @@ -10,6 +10,11 @@ #import #import +RCT_EXTERN BOOL RCTImageLoadingInstrumentationEnabled(void); +RCT_EXTERN BOOL RCTImageLoadingPerfInstrumentationEnabled(void); +RCT_EXTERN void RCTEnableImageLoadingInstrumentation(BOOL enabled); +RCT_EXTERN void RCTEnableImageLoadingPerfInstrumentation(BOOL enabled); + @protocol RCTImageLoaderWithAttributionProtocol /** diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm index 90c490d083f..27cd06de7d1 100644 --- a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm @@ -13,7 +13,9 @@ #import #import #import +#import #import +#import #import #import "RCTConversions.h" @@ -100,6 +102,12 @@ // Loading actually starts a little before this, but this is the first time we know // the image is loading and can fire an event from this component std::static_pointer_cast(_eventEmitter)->onLoadStart(); + + // TODO (T58941612): Tracking for visibility should be done directly on this class. + // For now, we consolidate instrumentation logic in the image loader, so that pre-Fabric gets the same treatment. + auto instrumentation = std::static_pointer_cast( + _imageLocalData->getImageRequest().getSharedImageInstrumentation()); + instrumentation->trackNativeImageView(self); } } @@ -161,6 +169,8 @@ // Apply trilinear filtering to smooth out mis-sized images. self->_imageView.layer.minificationFilter = kCAFilterTrilinear; self->_imageView.layer.magnificationFilter = kCAFilterTrilinear; + + _imageLocalData->getImageRequest().getImageInstrumentation().didSetImage(); } - (void)didReceiveProgress:(float)progress fromObserver:(void const *)observer diff --git a/ReactCommon/fabric/imagemanager/BUCK b/ReactCommon/fabric/imagemanager/BUCK index 4508ecb28f3..05853e26a6b 100644 --- a/ReactCommon/fabric/imagemanager/BUCK +++ b/ReactCommon/fabric/imagemanager/BUCK @@ -64,6 +64,7 @@ rn_xplat_cxx_library( ios_exported_headers = subdir_glob( [ ("", "*.h"), + ("platform/ios", "RCTImageInstrumentationProxy.h"), ("platform/ios", "RCTImagePrimitivesConversions.h"), ], prefix = "react/imagemanager", diff --git a/ReactCommon/fabric/imagemanager/ImageInstrumentation.h b/ReactCommon/fabric/imagemanager/ImageInstrumentation.h new file mode 100644 index 00000000000..577d18d97fe --- /dev/null +++ b/ReactCommon/fabric/imagemanager/ImageInstrumentation.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace facebook { +namespace react { + +/* + * A base class for performing image loading instrumentation. + * The actual instrumentation is app, platform, and image loader-specific. + */ +class ImageInstrumentation { + public: + virtual ~ImageInstrumentation() noexcept = default; + + /** + * Mark that the image content is set on the native image component on screen. + */ + virtual void didSetImage() const = 0; + + /** + * Mark that the image view starts to be visible on screen. + */ + virtual void didEnterVisibilityRange() const = 0; + + /** + * Mark that the image view is no longer visible on screen. + */ + virtual void didExitVisibilityRange() const = 0; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/ImageRequest.h b/ReactCommon/fabric/imagemanager/ImageRequest.h index f29b0a3b2b2..76195165ce6 100644 --- a/ReactCommon/fabric/imagemanager/ImageRequest.h +++ b/ReactCommon/fabric/imagemanager/ImageRequest.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -24,11 +25,12 @@ namespace react { */ class ImageRequest final { public: - /* * The default constructor */ - ImageRequest(const ImageSource &imageSource); + ImageRequest( + const ImageSource &imageSource, + std::shared_ptr instrumentation); /* * The move constructor. @@ -61,6 +63,20 @@ class ImageRequest final { */ const ImageResponseObserverCoordinator &getObserverCoordinator() const; + /* + * Returns stored image instrumentation object as a shared pointer. + * Retain this *or* `ImageRequest` to ensure a correct lifetime of the object. + */ + const std::shared_ptr + &getSharedImageInstrumentation() const; + + /* + * Returns the image instrumentation object specific to this request. + * Use this if a correct lifetime of the object is ensured in some other way + * (e.g. by retaining an `ImageRequest`). + */ + const ImageInstrumentation &getImageInstrumentation() const; + private: /* * Image source associated with the request. @@ -72,6 +88,11 @@ class ImageRequest final { */ std::shared_ptr coordinator_{}; + /* + * Image instrumentation specific to the request. + */ + std::shared_ptr instrumentation_; + /* * Function we can call to cancel image request (see destructor). */ diff --git a/ReactCommon/fabric/imagemanager/platform/cxx/ImageManager.cpp b/ReactCommon/fabric/imagemanager/platform/cxx/ImageManager.cpp index 7a4b8211f2f..245c3b5e7a7 100644 --- a/ReactCommon/fabric/imagemanager/platform/cxx/ImageManager.cpp +++ b/ReactCommon/fabric/imagemanager/platform/cxx/ImageManager.cpp @@ -24,7 +24,7 @@ ImageRequest ImageManager::requestImage( const ImageSource &imageSource, SurfaceId surfaceId) const { // Not implemented. - return ImageRequest(imageSource); + return ImageRequest(imageSource, nullptr); } } // namespace react diff --git a/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp b/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp index 744e592a28f..8f41d93231c 100644 --- a/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp +++ b/ReactCommon/fabric/imagemanager/platform/cxx/ImageRequest.cpp @@ -10,8 +10,10 @@ namespace facebook { namespace react { -ImageRequest::ImageRequest(const ImageSource &imageSource) - : imageSource_(imageSource) { +ImageRequest::ImageRequest( + const ImageSource &imageSource, + std::shared_ptr instrumentation) + : imageSource_(imageSource), instrumentation_(instrumentation) { // Not implemented. } diff --git a/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp b/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp index 5f2e8f2ed45..96911824679 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp +++ b/ReactCommon/fabric/imagemanager/platform/ios/ImageRequest.cpp @@ -10,17 +10,21 @@ namespace facebook { namespace react { -ImageRequest::ImageRequest(const ImageSource &imageSource) - : imageSource_(imageSource) { +ImageRequest::ImageRequest( + const ImageSource &imageSource, + std::shared_ptr instrumentation) + : imageSource_(imageSource), instrumentation_(instrumentation) { coordinator_ = std::make_shared(); } ImageRequest::ImageRequest(ImageRequest &&other) noexcept : imageSource_(std::move(other.imageSource_)), - coordinator_(std::move(other.coordinator_)) { + coordinator_(std::move(other.coordinator_)), + instrumentation_(std::move(other.instrumentation_)) { other.moved_ = true; other.coordinator_ = nullptr; other.cancelRequest_ = nullptr; + other.instrumentation_ = nullptr; } ImageRequest::~ImageRequest() { @@ -44,5 +48,14 @@ const std::shared_ptr return coordinator_; } +const std::shared_ptr + &ImageRequest::getSharedImageInstrumentation() const { + return instrumentation_; +} + +const ImageInstrumentation &ImageRequest::getImageInstrumentation() const { + return *instrumentation_; +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h new file mode 100644 index 00000000000..3a2f2b477f7 --- /dev/null +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include +#include + +NS_ASSUME_NONNULL_BEGIN + +namespace facebook { +namespace react { + +class RCTImageInstrumentationProxy final : public ImageInstrumentation { + public: + RCTImageInstrumentationProxy(id imageLoader); + + void didSetImage() const override; + void didEnterVisibilityRange() const override; + void didExitVisibilityRange() const override; + + void trackNativeImageView(UIView *imageView) const; + + private: + __weak id imageLoader_; +}; + +} // namespace react +} // namespace facebook + +NS_ASSUME_NONNULL_END diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm new file mode 100644 index 00000000000..0e4b67ee615 --- /dev/null +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm @@ -0,0 +1,57 @@ +/* + * 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 "RCTImageInstrumentationProxy.h" + +namespace facebook { +namespace react { + +RCTImageInstrumentationProxy::RCTImageInstrumentationProxy(id imageLoader) + : imageLoader_(imageLoader) +{ +} + +void RCTImageInstrumentationProxy::didSetImage() const +{ + if (!RCTImageLoadingPerfInstrumentationEnabled()) { + return; + } + + // TODO (T58941612): Not yet supported. + if (imageLoader_) { + } +} + +void RCTImageInstrumentationProxy::didEnterVisibilityRange() const +{ + if (!RCTImageLoadingPerfInstrumentationEnabled()) { + return; + } + + // TODO (T58941612): Not yet supported. +} + +void RCTImageInstrumentationProxy::didExitVisibilityRange() const +{ + if (!RCTImageLoadingPerfInstrumentationEnabled()) { + return; + } + + // TODO (T58941612): Not yet supported. +} + +void RCTImageInstrumentationProxy::trackNativeImageView(UIView *imageView) const +{ + if (!RCTImageLoadingPerfInstrumentationEnabled()) { + return; + } + + // TODO (T58941612): Not yet supported. +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm index 01e295f2043..244f0c5b381 100644 --- a/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm +++ b/ReactCommon/fabric/imagemanager/platform/ios/RCTImageManager.mm @@ -12,9 +12,11 @@ #import #import + #import #import +#import "RCTImageInstrumentationProxy.h" #import "RCTImagePrimitivesConversions.h" using namespace facebook::react; @@ -39,7 +41,7 @@ using namespace facebook::react; { SystraceSection s("RCTImageManager::requestImage"); - auto imageRequest = ImageRequest(imageSource); + auto imageRequest = ImageRequest(imageSource, std::make_unique(_imageLoader)); auto weakObserverCoordinator = (std::weak_ptr)imageRequest.getSharedObserverCoordinator();