From fdcdca4cfa5d6d5f03c34d274173ed7843ba91ea Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Sat, 16 Nov 2019 00:10:25 -0800 Subject: [PATCH] iOS: Introduced RCTImageURLLoaderWithAttribution Summary: Changelog: [iOS] [Changed] - New internal image attribution support, but files importing RCTImageLoader.h must be converted to ObjC++ This new interface is the same as RCTImageURLLoader, but with additional support to pass in optional attribution information. The attribution info is not strictly defined (we may do so in the future though), and it's up to the hosting application and RCTImageURLLoader classes to handle it. Reviewed By: sammy-SC Differential Revision: D18492882 fbshipit-source-id: c3870c60e6c2e7c65758fc3235ebf5db369e07dc --- Libraries/Image/RCTImageLoader.mm | 79 ++++++++++++++++--- Libraries/Image/RCTImageLoaderProtocol.h | 1 + .../RCTImageLoaderWithAttributionProtocol.h | 29 +++++++ .../Image/RCTImageURLLoaderWithAttribution.h | 38 +++++++++ .../Image/{RCTImageView.m => RCTImageView.mm} | 0 ...geViewManager.m => RCTImageViewManager.mm} | 0 6 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 Libraries/Image/RCTImageLoaderWithAttributionProtocol.h create mode 100644 Libraries/Image/RCTImageURLLoaderWithAttribution.h rename Libraries/Image/{RCTImageView.m => RCTImageView.mm} (100%) rename Libraries/Image/{RCTImageViewManager.m => RCTImageViewManager.mm} (100%) diff --git a/Libraries/Image/RCTImageLoader.mm b/Libraries/Image/RCTImageLoader.mm index 6052cebcbe0..5bb3b124867 100644 --- a/Libraries/Image/RCTImageLoader.mm +++ b/Libraries/Image/RCTImageLoader.mm @@ -15,6 +15,7 @@ #import #import #import +#import #import #import #import @@ -22,13 +23,15 @@ #import "RCTImagePlugins.h" +using namespace facebook::react; + static NSInteger RCTImageBytesForImage(UIImage *image) { NSInteger singleImageBytes = image.size.width * image.size.height * image.scale * image.scale * 4; return image.images ? image.images.count * singleImageBytes : singleImageBytes; } -@interface RCTImageLoader() +@interface RCTImageLoader() @end @@ -288,11 +291,32 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, scale:1 clipped:YES resizeMode:RCTResizeModeStretch + attribution:{} progressBlock:nil partialLoadBlock:nil completionBlock:callback]; } +- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest + size:(CGSize)size + scale:(CGFloat)scale + clipped:(BOOL)clipped + resizeMode:(RCTResizeMode)resizeMode + progressBlock:(RCTImageLoaderProgressBlock)progressBlock + partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock +{ + return [self loadImageWithURLRequest:imageURLRequest + size:size + scale:scale + clipped:clipped + resizeMode:resizeMode + attribution:{} + progressBlock:progressBlock + partialLoadBlock:partialLoadBlock + completionBlock:completionBlock]; +} + - (void)dequeueTasks { dispatch_async(_URLRequestQueue, ^{ @@ -363,6 +387,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode + attribution:(const ImageURLLoaderAttribution &)attribution progressBlock:(RCTImageLoaderProgressBlock)progressHandler partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadHandler completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSURLResponse *response))completionBlock @@ -383,6 +408,9 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, request = mutableRequest; } + // Create a copy here so the value is retained when accessed in the blocks below. + ImageURLLoaderAttribution attributionCopy(attribution); + // Find suitable image URL loader id loadHandler = [self imageURLLoaderForURL:request.URL]; BOOL requiresScheduling = [loadHandler respondsToSelector:@selector(requiresScheduling)] ? @@ -417,13 +445,25 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, // If the loader doesn't require scheduling we call it directly on // the main queue. if (loadHandler && !requiresScheduling) { + if ([loadHandler conformsToProtocol:@protocol(RCTImageURLLoaderWithAttribution)]) { + return [(id)loadHandler loadImageForURL:request.URL + size:size + scale:scale + resizeMode:resizeMode + attribution:attributionCopy + progressHandler:progressHandler + partialLoadHandler:partialLoadHandler + completionHandler:^(NSError *error, UIImage *image) { + completionHandler(error, image, nil); + }]; + } return [loadHandler loadImageForURL:request.URL size:size scale:scale resizeMode:resizeMode progressHandler:progressHandler partialLoadHandler:partialLoadHandler - completionHandler:^(NSError *error, UIImage *image){ + completionHandler:^(NSError *error, UIImage *image) { completionHandler(error, image, nil); }]; } @@ -441,15 +481,29 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, } if (loadHandler) { - dispatch_block_t cancelLoadLocal = [loadHandler loadImageForURL:request.URL - size:size - scale:scale - resizeMode:resizeMode - progressHandler:progressHandler - partialLoadHandler:partialLoadHandler - completionHandler:^(NSError *error, UIImage *image) { - completionHandler(error, image, nil); - }]; + dispatch_block_t cancelLoadLocal; + if ([loadHandler conformsToProtocol:@protocol(RCTImageURLLoaderWithAttribution)]) { + cancelLoadLocal = [(id)loadHandler loadImageForURL:request.URL + size:size + scale:scale + resizeMode:resizeMode + attribution:attributionCopy + progressHandler:progressHandler + partialLoadHandler:partialLoadHandler + completionHandler:^(NSError *error, UIImage *image) { + completionHandler(error, image, nil); + }]; + } else { + cancelLoadLocal = [loadHandler loadImageForURL:request.URL + size:size + scale:scale + resizeMode:resizeMode + progressHandler:progressHandler + partialLoadHandler:partialLoadHandler + completionHandler:^(NSError *error, UIImage *image) { + completionHandler(error, image, nil); + }]; + } [cancelLoadLock lock]; cancelLoad = cancelLoadLocal; [cancelLoadLock unlock]; @@ -607,6 +661,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, scale:(CGFloat)scale clipped:(BOOL)clipped resizeMode:(RCTResizeMode)resizeMode + attribution:(const ImageURLLoaderAttribution &)attribution progressBlock:(RCTImageLoaderProgressBlock)progressBlock partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock completionBlock:(RCTImageLoaderCompletionBlock)completionBlock @@ -673,6 +728,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, size:size scale:scale resizeMode:resizeMode + attribution:attribution progressBlock:progressBlock partialLoadBlock:partialLoadBlock completionBlock:completionHandler]; @@ -830,6 +886,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, size:CGSizeZero scale:1 resizeMode:RCTResizeModeStretch + attribution:{} progressBlock:NULL partialLoadBlock:NULL completionBlock:completion]; diff --git a/Libraries/Image/RCTImageLoaderProtocol.h b/Libraries/Image/RCTImageLoaderProtocol.h index 801f77f4090..6b17cc7bcb9 100644 --- a/Libraries/Image/RCTImageLoaderProtocol.h +++ b/Libraries/Image/RCTImageLoaderProtocol.h @@ -113,4 +113,5 @@ * protocol. This method should be called in bridgeDidInitializeModule. */ - (void)setImageCache:(id)cache; + @end diff --git a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h new file mode 100644 index 00000000000..2e9d20ec543 --- /dev/null +++ b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h @@ -0,0 +1,29 @@ +/* + * 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 + +#import +#import + +@protocol RCTImageLoaderWithAttributionProtocol + +/** + * Same as the variant in RCTImageURLLoaderProtocol, but allows passing attribution + * information that each image URL loader can process. + */ +- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest + size:(CGSize)size + scale:(CGFloat)scale + clipped:(BOOL)clipped + resizeMode:(RCTResizeMode)resizeMode + attribution:(const facebook::react::ImageURLLoaderAttribution &)attribution + progressBlock:(RCTImageLoaderProgressBlock)progressBlock + partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; + +@end diff --git a/Libraries/Image/RCTImageURLLoaderWithAttribution.h b/Libraries/Image/RCTImageURLLoaderWithAttribution.h new file mode 100644 index 00000000000..9de3e31f420 --- /dev/null +++ b/Libraries/Image/RCTImageURLLoaderWithAttribution.h @@ -0,0 +1,38 @@ +/* + * 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 + +namespace facebook { +namespace react { + +struct ImageURLLoaderAttribution { + int32_t surfaceId = 0; +}; + +} // namespace react +} // namespace facebook + +/** + * Same as the RCTImageURLLoader interface, but allows passing in optional `attribution` information. + * This is useful for per-app logging and other instrumentation. + */ +@protocol RCTImageURLLoaderWithAttribution + +/** + * Same as the RCTImageURLLoader variant above, but allows optional `attribution` information. + */ +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(RCTResizeMode)resizeMode + attribution:(const facebook::react::ImageURLLoaderAttribution &)attribution + progressHandler:(RCTImageLoaderProgressBlock)progressHandler + partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler + completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; + +@end diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.mm similarity index 100% rename from Libraries/Image/RCTImageView.m rename to Libraries/Image/RCTImageView.mm diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.mm similarity index 100% rename from Libraries/Image/RCTImageViewManager.m rename to Libraries/Image/RCTImageViewManager.mm