mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 04:35:36 +08:00
[ReactNative] s/ReactKit/React/g
This commit is contained in:
28
React/Executors/RCTContextExecutor.h
Normal file
28
React/Executors/RCTContextExecutor.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
// TODO (#5906496): Might RCTJSCoreExecutor be a better name for this?
|
||||
|
||||
/**
|
||||
* Uses a JavaScriptCore context as the execution engine.
|
||||
*/
|
||||
@interface RCTContextExecutor : NSObject <RCTJavaScriptExecutor>
|
||||
|
||||
/**
|
||||
* Configures the executor to run JavaScript on a custom performer.
|
||||
* You probably don't want to use this; use -init instead.
|
||||
*/
|
||||
- (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread
|
||||
globalContextRef:(JSGlobalContextRef)context;
|
||||
|
||||
@end
|
||||
301
React/Executors/RCTContextExecutor.m
Normal file
301
React/Executors/RCTContextExecutor.m
Normal file
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTContextExecutor.h"
|
||||
|
||||
#import <pthread.h>
|
||||
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTContextExecutor
|
||||
{
|
||||
JSGlobalContextRef _context;
|
||||
NSThread *_javaScriptThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* The one tiny pure native hook that we implement is a native logging hook.
|
||||
* You could even argue that this is not necessary - we could plumb logging
|
||||
* calls through a batched bridge, but having the pure native hook allows
|
||||
* logging to successfully come through even in the event that a batched bridge
|
||||
* crashes.
|
||||
*/
|
||||
|
||||
static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception)
|
||||
{
|
||||
if (argumentCount > 0) {
|
||||
JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
|
||||
if (!string) {
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
NSString *str = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
NSError *error = nil;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:
|
||||
@"( stack: )?([_a-z0-9]*)@?(http://|file:///)[a-z.0-9:/_-]+/([a-z0-9_]+).includeRequire.runModule.bundle(:[0-9]+:[0-9]+)"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
NSString *modifiedString = [regex stringByReplacingMatchesInString:str options:0 range:NSMakeRange(0, [str length]) withTemplate:@"[$4$5] \t$2"];
|
||||
|
||||
modifiedString = [@"RCTJSLog> " stringByAppendingString:modifiedString];
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
fprintf(stderr, "%s\n", [modifiedString UTF8String]); // don't print timestamps and other junk
|
||||
#else
|
||||
// Print normal errors with timestamps to files when not in simulator.
|
||||
RCTLogObjects(@[modifiedString], @"log");
|
||||
#endif
|
||||
JSStringRelease(string);
|
||||
}
|
||||
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
// Do-very-little native hook for testing.
|
||||
static JSValueRef RCTNoop(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception)
|
||||
{
|
||||
static int counter = 0;
|
||||
counter++;
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
static NSString *RCTJSValueToNSString(JSContextRef context, JSValueRef value)
|
||||
{
|
||||
JSStringRef JSString = JSValueToStringCopy(context, value, NULL);
|
||||
CFStringRef string = JSStringCopyCFString(kCFAllocatorDefault, JSString);
|
||||
JSStringRelease(JSString);
|
||||
|
||||
return (__bridge_transfer NSString *)string;
|
||||
}
|
||||
|
||||
static NSString *RCTJSValueToJSONString(JSContextRef context, JSValueRef value, unsigned indent)
|
||||
{
|
||||
JSStringRef JSString = JSValueCreateJSONString(context, value, indent, NULL);
|
||||
CFStringRef string = JSStringCopyCFString(kCFAllocatorDefault, JSString);
|
||||
JSStringRelease(JSString);
|
||||
|
||||
return (__bridge_transfer NSString *)string;
|
||||
}
|
||||
|
||||
static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
||||
{
|
||||
NSString *errorMessage = jsError ? RCTJSValueToNSString(context, jsError) : @"unknown JS error";
|
||||
NSString *details = jsError ? RCTJSValueToJSONString(context, jsError, 2) : @"no details";
|
||||
return [NSError errorWithDomain:@"JS" code:1 userInfo:@{NSLocalizedDescriptionKey: errorMessage, NSLocalizedFailureReasonErrorKey: details}];
|
||||
}
|
||||
|
||||
+ (void)runRunLoopThread
|
||||
{
|
||||
// TODO (#5906496): Investigate exactly what this does and why
|
||||
|
||||
@autoreleasepool {
|
||||
// copy thread name to pthread name
|
||||
pthread_setname_np([[[NSThread currentThread] name] UTF8String]);
|
||||
|
||||
// Set up a dummy runloop source to avoid spinning
|
||||
CFRunLoopSourceContext noSpinCtx = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate(NULL, 0, &noSpinCtx);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), noSpinSource, kCFRunLoopDefaultMode);
|
||||
CFRelease(noSpinSource);
|
||||
|
||||
// run the run loop
|
||||
while (kCFRunLoopRunStopped != CFRunLoopRunInMode(kCFRunLoopDefaultMode, [[NSDate distantFuture] timeIntervalSinceReferenceDate], NO)) {
|
||||
RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
static NSThread *javaScriptThread;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// All JS is single threaded, so a serial queue is our only option.
|
||||
javaScriptThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoopThread) object:nil];
|
||||
[javaScriptThread setName:@"com.facebook.React.JavaScript"];
|
||||
[javaScriptThread setThreadPriority:[[NSThread mainThread] threadPriority]];
|
||||
[javaScriptThread start];
|
||||
});
|
||||
|
||||
return [self initWithJavaScriptThread:javaScriptThread globalContextRef:NULL];
|
||||
}
|
||||
|
||||
- (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread
|
||||
globalContextRef:(JSGlobalContextRef)context
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_javaScriptThread = javaScriptThread;
|
||||
[self executeBlockOnJavaScriptQueue: ^{
|
||||
// Assumes that no other JS tasks are scheduled before.
|
||||
if (context) {
|
||||
_context = JSGlobalContextRetain(context);
|
||||
} else {
|
||||
JSContextGroupRef group = JSContextGroupCreate();
|
||||
_context = JSGlobalContextCreateInGroup(group, NULL);
|
||||
#if FB_JSC_HACK
|
||||
JSContextGroupBindToCurrentThread(group);
|
||||
#endif
|
||||
JSContextGroupRelease(group);
|
||||
}
|
||||
|
||||
[self _addNativeHook:RCTNativeLoggingHook withName:"nativeLoggingHook"];
|
||||
[self _addNativeHook:RCTNoop withName:"noop"];
|
||||
}];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_addNativeHook:(JSObjectCallAsFunctionCallback)hook withName:(const char *)name
|
||||
{
|
||||
JSObjectRef globalObject = JSContextGetGlobalObject(_context);
|
||||
|
||||
JSStringRef JSName = JSStringCreateWithUTF8CString(name);
|
||||
JSObjectSetProperty(_context, globalObject, JSName, JSObjectMakeFunctionWithCallback(_context, JSName, hook), kJSPropertyAttributeNone, NULL);
|
||||
JSStringRelease(JSName);
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _context != NULL;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if ([NSThread currentThread] != _javaScriptThread) {
|
||||
// Yes, block until done. If we're getting called right before dealloc, it's the only safe option.
|
||||
[self performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:YES];
|
||||
} else if (_context != NULL) {
|
||||
JSGlobalContextRelease(_context);
|
||||
_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||
}
|
||||
|
||||
- (void)executeJSCall:(NSString *)name
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)arguments
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
NSError *error;
|
||||
NSString *argsString = RCTJSONStringify(arguments, &error);
|
||||
if (!argsString) {
|
||||
RCTLogError(@"Cannot convert argument to string: %@", error);
|
||||
onComplete(nil, error);
|
||||
return;
|
||||
}
|
||||
NSString *execString = [NSString stringWithFormat:@"require('%@').%@.apply(null, %@);", name, method, argsString];
|
||||
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)execString);
|
||||
JSValueRef result = JSEvaluateScript(_context, execJSString, NULL, NULL, 0, &jsError);
|
||||
JSStringRelease(execJSString);
|
||||
|
||||
if (!result) {
|
||||
onComplete(nil, RCTNSErrorFromJSError(_context, jsError));
|
||||
return;
|
||||
}
|
||||
|
||||
// Looks like making lots of JSC API calls is slower than communicating by using a JSON
|
||||
// string. Also it ensures that data stuctures don't have cycles and non-serializable fields.
|
||||
// see [RCTContextExecutorTests testDeserializationPerf]
|
||||
id objcValue;
|
||||
// We often return `null` from JS when there is nothing for native side. JSONKit takes an extra hundred microseconds
|
||||
// to handle this simple case, so we are adding a shortcut to make executeJSCall method even faster
|
||||
if (!JSValueIsNull(_context, result)) {
|
||||
JSStringRef jsJSONString = JSValueCreateJSONString(_context, result, 0, nil);
|
||||
if (jsJSONString) {
|
||||
NSString *objcJSONString = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsJSONString);
|
||||
JSStringRelease(jsJSONString);
|
||||
|
||||
objcValue = RCTJSONParse(objcJSONString, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
onComplete(objcValue, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)executeApplicationScript:(NSString *)script
|
||||
sourceURL:(NSURL *)url
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(url != nil, @"url should not be nil");
|
||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
|
||||
JSStringRef sourceURL = JSStringCreateWithCFString((__bridge CFStringRef)url.absoluteString);
|
||||
JSValueRef result = JSEvaluateScript(_context, execJSString, NULL, sourceURL, 0, &jsError);
|
||||
JSStringRelease(sourceURL);
|
||||
JSStringRelease(execJSString);
|
||||
|
||||
NSError *error;
|
||||
if (!result) {
|
||||
error = RCTNSErrorFromJSError(_context, jsError);
|
||||
}
|
||||
|
||||
onComplete(error);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
|
||||
{
|
||||
if ([NSThread currentThread] != _javaScriptThread) {
|
||||
[self performSelector:@selector(executeBlockOnJavaScriptQueue:)
|
||||
onThread:_javaScriptThread withObject:block waitUntilDone:NO];
|
||||
} else {
|
||||
block();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)injectJSONText:(NSString *)script
|
||||
asGlobalObjectNamed:(NSString *)objectName
|
||||
callback:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
||||
#if DEBUG
|
||||
RCTAssert(RCTJSONParse(script, NULL) != nil, @"%@ wasn't valid JSON!", script);
|
||||
#endif
|
||||
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script);
|
||||
JSValueRef valueToInject = JSValueMakeFromJSONString(_context, execJSString);
|
||||
JSStringRelease(execJSString);
|
||||
|
||||
if (!valueToInject) {
|
||||
NSString *errorDesc = [NSString stringWithFormat:@"Can't make JSON value from script '%@'", script];
|
||||
RCTLogError(@"%@", errorDesc);
|
||||
|
||||
NSError *error = [NSError errorWithDomain:@"JS" code:2 userInfo:@{NSLocalizedDescriptionKey: errorDesc}];
|
||||
onComplete(error);
|
||||
return;
|
||||
}
|
||||
|
||||
JSObjectRef globalObject = JSContextGetGlobalObject(_context);
|
||||
|
||||
JSStringRef JSName = JSStringCreateWithCFString((__bridge CFStringRef)objectName);
|
||||
JSObjectSetProperty(_context, globalObject, JSName, valueToInject, kJSPropertyAttributeNone, NULL);
|
||||
JSStringRelease(JSName);
|
||||
onComplete(nil);
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
42
React/Executors/RCTWebViewExecutor.h
Normal file
42
React/Executors/RCTWebViewExecutor.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
/**
|
||||
* Uses an embedded web view merely for the purpose of being able to reuse the
|
||||
* existing webkit debugging tools. Fulfills the role of a very constrained
|
||||
* `JSContext`, which we call `RCTJavaScriptExecutor`.
|
||||
*
|
||||
* TODO: To ensure production-identical execution, scrub the window
|
||||
* environment. And ensure main thread operations are actually added to a queue
|
||||
* instead of being executed immediately if already on the main thread.
|
||||
*/
|
||||
@interface RCTWebViewExecutor : NSObject<RCTJavaScriptExecutor>
|
||||
|
||||
// Only one callback stored - will only be invoked for the latest issued
|
||||
// application script request.
|
||||
@property (nonatomic, copy) RCTJavaScriptCompleteBlock onApplicationScriptLoaded;
|
||||
|
||||
/**
|
||||
* Instantiate with a specific webview instance
|
||||
*/
|
||||
- (instancetype)initWithWebView:(UIWebView *)webView NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Invoke this to reclaim the web view for reuse. This is necessary in order to
|
||||
* allow debuggers to remain open, when creating a new `RCTWebViewExecutor`.
|
||||
* This guards against the web view being invalidated, and makes sure the
|
||||
* `delegate` is cleared first.
|
||||
*/
|
||||
- (UIWebView *)invalidateAndReclaimWebView;
|
||||
|
||||
@end
|
||||
192
React/Executors/RCTWebViewExecutor.m
Normal file
192
React/Executors/RCTWebViewExecutor.m
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTWebViewExecutor.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
NSString *description = [[NSString alloc] initWithFormat:fmt arguments:args];
|
||||
RCTLogError(@"%@", description);
|
||||
|
||||
NSError *error = [NSError errorWithDomain:NSStringFromClass([RCTWebViewExecutor class])
|
||||
code:3
|
||||
userInfo:@{NSLocalizedDescriptionKey:description}];
|
||||
callback(nil, error);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@interface RCTWebViewExecutor () <UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTWebViewExecutor
|
||||
{
|
||||
UIWebView *_webView;
|
||||
NSMutableDictionary *_objectsToInject;
|
||||
}
|
||||
|
||||
- (instancetype)initWithWebView:(UIWebView *)webView
|
||||
{
|
||||
if (!webView) {
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Can't init with a nil webview" userInfo:nil];
|
||||
}
|
||||
if ((self = [super init])) {
|
||||
_objectsToInject = [[NSMutableDictionary alloc] init];
|
||||
_webView = webView;
|
||||
_webView.delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithWebView:[[UIWebView alloc] init]];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _webView != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_webView.delegate = nil;
|
||||
_webView = nil;
|
||||
}
|
||||
|
||||
- (UIWebView *)invalidateAndReclaimWebView
|
||||
{
|
||||
UIWebView *webView = _webView;
|
||||
[self invalidate];
|
||||
return webView;
|
||||
}
|
||||
|
||||
- (void)executeJSCall:(NSString *)name
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)arguments
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"");
|
||||
[self executeBlockOnJavaScriptQueue:^{
|
||||
NSError *error;
|
||||
NSString *argsString = RCTJSONStringify(arguments, &error);
|
||||
if (!argsString) {
|
||||
RCTReportError(onComplete, @"Cannot convert argument to string: %@", error);
|
||||
return;
|
||||
}
|
||||
NSString *execString = [NSString stringWithFormat:@"JSON.stringify(require('%@').%@.apply(null, %@));", name, method, argsString];
|
||||
|
||||
NSString *ret = [_webView stringByEvaluatingJavaScriptFromString:execString];
|
||||
if (ret.length == 0) {
|
||||
RCTReportError(onComplete, @"Empty return string: JavaScript error running script: %@", execString);
|
||||
return;
|
||||
}
|
||||
|
||||
id objcValue = RCTJSONParse(ret, &error);
|
||||
if (!objcValue) {
|
||||
RCTReportError(onComplete, @"Cannot parse json response: %@", error);
|
||||
return;
|
||||
}
|
||||
onComplete(objcValue, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* We cannot use the standard eval JS method. Source will not show up in the
|
||||
* debugger. So we have to use this (essentially) async API - and register
|
||||
* ourselves as the webview delegate to be notified when load is complete.
|
||||
*/
|
||||
- (void)executeApplicationScript:(NSString *)script
|
||||
sourceURL:(NSURL *)url
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
if (![NSThread isMainThread]) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[self executeApplicationScript:script sourceURL:url onComplete:onComplete];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
RCTAssert(onComplete != nil, @"");
|
||||
_onApplicationScriptLoaded = onComplete;
|
||||
|
||||
if (_objectsToInject.count > 0) {
|
||||
NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"];
|
||||
[_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) {
|
||||
[scriptWithInjections appendString:objectName];
|
||||
[scriptWithInjections appendString:@" = ("];
|
||||
[scriptWithInjections appendString:blockScript];
|
||||
[scriptWithInjections appendString:@");\n"];
|
||||
}];
|
||||
[_objectsToInject removeAllObjects];
|
||||
[scriptWithInjections appendString:@"/* END NATIVELY INJECTED OBJECTS */\n"];
|
||||
[scriptWithInjections appendString:script];
|
||||
script = scriptWithInjections;
|
||||
}
|
||||
|
||||
NSString *runScript =
|
||||
[NSString
|
||||
stringWithFormat:@"<html><head></head><body><script type='text/javascript'>%@</script></body></html>",
|
||||
script
|
||||
];
|
||||
[_webView loadHTMLString:runScript baseURL:url];
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to avoid `UIWebView` thread locks, all JS executions should be
|
||||
* performed outside of the event loop that notifies the `UIWebViewDelegate`
|
||||
* that the page has loaded. This is only an issue with the remote debug mode of
|
||||
* `UIWebView`. For a production `UIWebView` deployment, this delay is
|
||||
* unnecessary and possibly harmful (or helpful?)
|
||||
*
|
||||
* The delay might not be needed as soon as the following change lands into
|
||||
* iOS7. (Review the patch linked here and search for "crash"
|
||||
* https://bugs.webkit.org/show_bug.cgi?id=125746).
|
||||
*/
|
||||
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
|
||||
{
|
||||
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC);
|
||||
|
||||
dispatch_after(when, dispatch_get_main_queue(), ^{
|
||||
RCTAssertMainThread();
|
||||
block();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* `UIWebViewDelegate` methods. Handle application script load.
|
||||
*/
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
if (_onApplicationScriptLoaded) {
|
||||
_onApplicationScriptLoaded(nil); // TODO(frantic): how to fetch error from UIWebView?
|
||||
}
|
||||
_onApplicationScriptLoaded = nil;
|
||||
}
|
||||
|
||||
- (void)injectJSONText:(NSString *)script
|
||||
asGlobalObjectNamed:(NSString *)objectName
|
||||
callback:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(!_objectsToInject[objectName],
|
||||
@"already injected object named %@", _objectsToInject[objectName]);
|
||||
_objectsToInject[objectName] = script;
|
||||
onComplete(nil);
|
||||
}
|
||||
@end
|
||||
Reference in New Issue
Block a user