[ReactNative] Make JavaScript executors bridge modules
Summary: @public This is the first of a few diffs that change the way the executors are handled by the bridge. For they are just promoted to modules, so they are automatically loaded by the bridge. Test Plan: Tested on UIExplorer, Catalyst and MAdMan. Tested all the 3 executors, everything looks fine.
This commit is contained in:
parent
68bb3a7e71
commit
847dff8d75
|
@ -133,8 +133,8 @@ RCT_EXPORT_MODULE();
|
||||||
(void)bridge;
|
(void)bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep on the main thread so the deallocation can happen on the JS thread.
|
RUN_RUNLOOP_WHILE(weakExecutor, 1);
|
||||||
sleep(DEFAULT_TIMEOUT);
|
sleep(1);
|
||||||
XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released");
|
XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,8 @@ RCT_EXPORT_MODULE();
|
||||||
(void)bridge;
|
(void)bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(DEFAULT_TIMEOUT);
|
RUN_RUNLOOP_WHILE(weakContext, 1);
|
||||||
|
sleep(1);
|
||||||
XCTAssertNil(weakContext, @"RCTJavaScriptContext should have been deallocated");
|
XCTAssertNil(weakContext, @"RCTJavaScriptContext should have been deallocated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,10 @@
|
||||||
|
|
||||||
@implementation TestExecutor
|
@implementation TestExecutor
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (void)setUp {}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
|
|
|
@ -13,18 +13,28 @@
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTContextExecutorTests
|
@implementation RCTContextExecutorTests
|
||||||
|
{
|
||||||
|
RCTContextExecutor *_executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
[super setUp];
|
||||||
|
_executor = [[RCTContextExecutor alloc] init];
|
||||||
|
RCTSetExecutorID(_executor);
|
||||||
|
[_executor setUp];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)testNativeLoggingHookExceptionBehavior
|
- (void)testNativeLoggingHookExceptionBehavior
|
||||||
{
|
{
|
||||||
RCTContextExecutor *executor = [[RCTContextExecutor alloc] init];
|
|
||||||
dispatch_semaphore_t doneSem = dispatch_semaphore_create(0);
|
dispatch_semaphore_t doneSem = dispatch_semaphore_create(0);
|
||||||
[executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);"
|
[_executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);"
|
||||||
sourceURL:[NSURL URLWithString:@"file://"]
|
sourceURL:[NSURL URLWithString:@"file://"]
|
||||||
onComplete:^(id error){
|
onComplete:^(id error){
|
||||||
dispatch_semaphore_signal(doneSem);
|
dispatch_semaphore_signal(doneSem);
|
||||||
}];
|
}];
|
||||||
dispatch_semaphore_wait(doneSem, DISPATCH_TIME_FOREVER);
|
dispatch_semaphore_wait(doneSem, DISPATCH_TIME_FOREVER);
|
||||||
[executor invalidate];
|
[_executor invalidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t _get_time_nanoseconds(void)
|
static uint64_t _get_time_nanoseconds(void)
|
||||||
|
@ -91,7 +101,6 @@ static uint64_t _get_time_nanoseconds(void)
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||||
RCTContextExecutor *executor = RCTCreateExecutor([RCTContextExecutor class]);
|
|
||||||
NSString *script = @" \
|
NSString *script = @" \
|
||||||
var modules = { \
|
var modules = { \
|
||||||
module: { \
|
module: { \
|
||||||
|
@ -105,7 +114,7 @@ static uint64_t _get_time_nanoseconds(void)
|
||||||
} \
|
} \
|
||||||
";
|
";
|
||||||
|
|
||||||
[executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(NSError *error) {
|
[_executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(NSError *error) {
|
||||||
NSMutableArray *params = [[NSMutableArray alloc] init];
|
NSMutableArray *params = [[NSMutableArray alloc] init];
|
||||||
id data = @1;
|
id data = @1;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
@ -115,10 +124,10 @@ static uint64_t _get_time_nanoseconds(void)
|
||||||
for (int j = 0; j < runs; j++) {
|
for (int j = 0; j < runs; j++) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
double start = _get_time_nanoseconds();
|
double start = _get_time_nanoseconds();
|
||||||
[executor executeJSCall:@"module"
|
[_executor executeJSCall:@"module"
|
||||||
method:@"method"
|
method:@"method"
|
||||||
arguments:params
|
arguments:params
|
||||||
context:RCTGetExecutorID(executor)
|
context:RCTGetExecutorID(_executor)
|
||||||
callback:^(id json, NSError *__error) {
|
callback:^(id json, NSError *__error) {
|
||||||
RCTAssert([json isEqual:@YES], @"Invalid return");
|
RCTAssert([json isEqual:@YES], @"Invalid return");
|
||||||
}];
|
}];
|
||||||
|
|
|
@ -121,7 +121,7 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
|
RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
|
||||||
{
|
{
|
||||||
UIUserNotificationType types = UIRemoteNotificationTypeNone;
|
UIUserNotificationType types = UIUserNotificationTypeNone;
|
||||||
if (permissions) {
|
if (permissions) {
|
||||||
if ([permissions[@"alert"] boolValue]) {
|
if ([permissions[@"alert"] boolValue]) {
|
||||||
types |= UIUserNotificationTypeAlert;
|
types |= UIUserNotificationTypeAlert;
|
||||||
|
|
|
@ -31,8 +31,11 @@ typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||||
RCTSparseArray *_callbacks;
|
RCTSparseArray *_callbacks;
|
||||||
dispatch_semaphore_t _socketOpenSemaphore;
|
dispatch_semaphore_t _socketOpenSemaphore;
|
||||||
NSMutableDictionary *_injectedObjects;
|
NSMutableDictionary *_injectedObjects;
|
||||||
|
NSURL *_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
return [self initWithURL:[NSURL URLWithString:@"http://localhost:8081/debugger-proxy"]];
|
return [self initWithURL:[NSURL URLWithString:@"http://localhost:8081/debugger-proxy"]];
|
||||||
|
@ -41,23 +44,29 @@ typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||||
- (instancetype)initWithURL:(NSURL *)URL
|
- (instancetype)initWithURL:(NSURL *)URL
|
||||||
{
|
{
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
|
_url = URL;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
_jsQueue = dispatch_queue_create("com.facebook.React.WebSocketExecutor", DISPATCH_QUEUE_SERIAL);
|
_jsQueue = dispatch_queue_create("com.facebook.React.WebSocketExecutor", DISPATCH_QUEUE_SERIAL);
|
||||||
_socket = [[RCTSRWebSocket alloc] initWithURL:URL];
|
_socket = [[RCTSRWebSocket alloc] initWithURL:_url];
|
||||||
_socket.delegate = self;
|
_socket.delegate = self;
|
||||||
_callbacks = [[RCTSparseArray alloc] init];
|
_callbacks = [[RCTSparseArray alloc] init];
|
||||||
_injectedObjects = [[NSMutableDictionary alloc] init];
|
_injectedObjects = [[NSMutableDictionary alloc] init];
|
||||||
[_socket setDelegateDispatchQueue:_jsQueue];
|
[_socket setDelegateDispatchQueue:_jsQueue];
|
||||||
|
|
||||||
NSURL *startDevToolsURL = [NSURL URLWithString:@"/launch-chrome-devtools" relativeToURL:URL];
|
NSURL *startDevToolsURL = [NSURL URLWithString:@"/launch-chrome-devtools" relativeToURL:_url];
|
||||||
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:startDevToolsURL] delegate:nil];
|
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:startDevToolsURL] delegate:nil];
|
||||||
|
|
||||||
if (![self connectToProxy]) {
|
if (![self connectToProxy]) {
|
||||||
RCTLogError(@"Connection to %@ timed out. Are you running node proxy? If \
|
RCTLogError(@"Connection to %@ timed out. Are you running node proxy? If \
|
||||||
you are running on the device, check if you have the right IP \
|
you are running on the device, check if you have the right IP \
|
||||||
address in `RCTWebSocketExecutor.m`.", URL);
|
address in `RCTWebSocketExecutor.m`.", _url);
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
return nil;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSInteger retries = 3;
|
NSInteger retries = 3;
|
||||||
|
@ -70,10 +79,8 @@ typedef void (^RCTWSMessageCallback)(NSError *error, NSDictionary *reply);
|
||||||
RCTLogError(@"Runtime is not ready. Make sure Chrome is running and not "
|
RCTLogError(@"Runtime is not ready. Make sure Chrome is running and not "
|
||||||
"paused on a breakpoint or exception and try reloading again.");
|
"paused on a breakpoint or exception and try reloading again.");
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
return nil;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)connectToProxy
|
- (BOOL)connectToProxy
|
||||||
|
|
|
@ -899,7 +899,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
@implementation RCTBatchedBridge
|
@implementation RCTBatchedBridge
|
||||||
{
|
{
|
||||||
BOOL _loading;
|
BOOL _loading;
|
||||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
__weak id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||||
RCTSparseArray *_modulesByID;
|
RCTSparseArray *_modulesByID;
|
||||||
RCTSparseArray *_queuesByID;
|
RCTSparseArray *_queuesByID;
|
||||||
dispatch_queue_t _methodQueue;
|
dispatch_queue_t _methodQueue;
|
||||||
|
@ -936,13 +936,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize executor to allow enqueueing calls
|
|
||||||
*/
|
|
||||||
Class executorClass = self.executorClass ?: [RCTContextExecutor class];
|
|
||||||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
|
||||||
_latestJSExecutor = _javaScriptExecutor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize and register bridge modules *before* adding the display link
|
* Initialize and register bridge modules *before* adding the display link
|
||||||
* so we don't have threading issues
|
* so we don't have threading issues
|
||||||
|
@ -980,7 +973,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
|
|
||||||
- (Class)executorClass
|
- (Class)executorClass
|
||||||
{
|
{
|
||||||
return _parentBridge.executorClass;
|
return _parentBridge.executorClass ?: [RCTContextExecutor class];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setExecutorClass:(Class)executorClass
|
- (void)setExecutorClass:(Class)executorClass
|
||||||
|
@ -1045,6 +1038,16 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
// Store modules
|
// Store modules
|
||||||
_modulesByName = [modulesByName copy];
|
_modulesByName = [modulesByName copy];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The executor is a bridge module, wait for it to be created and set it before
|
||||||
|
* any other module has access to the bridge
|
||||||
|
*/
|
||||||
|
_javaScriptExecutor = _modulesByName[RCTBridgeModuleNameForClass(self.executorClass)];
|
||||||
|
_latestJSExecutor = _javaScriptExecutor;
|
||||||
|
RCTSetExecutorID(_javaScriptExecutor);
|
||||||
|
|
||||||
|
[_javaScriptExecutor setUp];
|
||||||
|
|
||||||
// Set bridge
|
// Set bridge
|
||||||
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
||||||
if ([module respondsToSelector:@selector(setBridge:)]) {
|
if ([module respondsToSelector:@selector(setBridge:)]) {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#import <JavaScriptCore/JavaScriptCore.h>
|
#import <JavaScriptCore/JavaScriptCore.h>
|
||||||
|
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
typedef void (^RCTJavaScriptCompleteBlock)(NSError *error);
|
typedef void (^RCTJavaScriptCompleteBlock)(NSError *error);
|
||||||
|
@ -20,7 +21,13 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
||||||
* Abstracts away a JavaScript execution context - we may be running code in a
|
* Abstracts away a JavaScript execution context - we may be running code in a
|
||||||
* web view (for debugging purposes), or may be running code in a `JSContext`.
|
* web view (for debugging purposes), or may be running code in a `JSContext`.
|
||||||
*/
|
*/
|
||||||
@protocol RCTJavaScriptExecutor <RCTInvalidating>
|
@protocol RCTJavaScriptExecutor <RCTInvalidating, RCTBridgeModule>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to set up the executor after the bridge has been fully initialized.
|
||||||
|
* Do any expensive setup in this method instead of `-init`.
|
||||||
|
*/
|
||||||
|
- (void)setUp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes given method with arguments on JS thread and calls the given callback
|
* Executes given method with arguments on JS thread and calls the given callback
|
||||||
|
@ -61,14 +68,12 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID";
|
static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID";
|
||||||
__used static id<RCTJavaScriptExecutor> RCTCreateExecutor(Class executorClass)
|
__used static void RCTSetExecutorID(id<RCTJavaScriptExecutor> executor)
|
||||||
{
|
{
|
||||||
static NSUInteger executorID = 0;
|
static NSUInteger executorID = 0;
|
||||||
id<RCTJavaScriptExecutor> executor = [[executorClass alloc] init];
|
|
||||||
if (executor) {
|
if (executor) {
|
||||||
objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN);
|
objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN);
|
||||||
}
|
}
|
||||||
return executor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__used static NSNumber *RCTGetExecutorID(id<RCTJavaScriptExecutor> executor)
|
__used static NSNumber *RCTGetExecutorID(id<RCTJavaScriptExecutor> executor)
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
NSThread *_javaScriptThread;
|
NSThread *_javaScriptThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The one tiny pure native hook that we implement is a native logging hook.
|
* 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
|
* You could even argue that this is not necessary - we could plumb logging
|
||||||
|
@ -233,19 +235,29 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
||||||
JSGlobalContextRef ctx;
|
JSGlobalContextRef ctx;
|
||||||
if (context) {
|
if (context) {
|
||||||
ctx = JSGlobalContextRetain(context);
|
ctx = JSGlobalContextRetain(context);
|
||||||
} else {
|
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];
|
||||||
JSContextGroupRef group = JSContextGroupCreate();
|
}
|
||||||
ctx = JSGlobalContextCreateInGroup(group, NULL);
|
}];
|
||||||
#if FB_JSC_HACK
|
|
||||||
JSContextGroupBindToCurrentThread(group);
|
|
||||||
#endif
|
|
||||||
JSContextGroupRelease(group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
__weak RCTContextExecutor *weakSelf = self;
|
||||||
|
[self executeBlockOnJavaScriptQueue:^{
|
||||||
|
RCTContextExecutor *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!strongSelf->_context) {
|
||||||
|
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
|
||||||
|
JSGlobalContextRetain(ctx);
|
||||||
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];
|
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];
|
||||||
|
}
|
||||||
[strongSelf _addNativeHook:RCTNativeLoggingHook withName:"nativeLoggingHook"];
|
[strongSelf _addNativeHook:RCTNativeLoggingHook withName:"nativeLoggingHook"];
|
||||||
[strongSelf _addNativeHook:RCTNoop withName:"noop"];
|
[strongSelf _addNativeHook:RCTNoop withName:"noop"];
|
||||||
|
|
||||||
#if RCT_DEV
|
#if RCT_DEV
|
||||||
[strongSelf _addNativeHook:RCTConsoleProfile withName:"consoleProfile"];
|
[strongSelf _addNativeHook:RCTConsoleProfile withName:"consoleProfile"];
|
||||||
[strongSelf _addNativeHook:RCTConsoleProfileEnd withName:"consoleProfileEnd"];
|
[strongSelf _addNativeHook:RCTConsoleProfileEnd withName:"consoleProfileEnd"];
|
||||||
|
@ -257,11 +269,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
||||||
object:nil];
|
object:nil];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}];
|
}];
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)toggleProfilingFlag:(NSNotification *)notification
|
- (void)toggleProfilingFlag:(NSNotification *)notification
|
||||||
|
|
|
@ -46,16 +46,14 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
NSRegularExpression *_scriptTagsRegex;
|
NSRegularExpression *_scriptTagsRegex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
@synthesize valid = _valid;
|
@synthesize valid = _valid;
|
||||||
|
|
||||||
- (instancetype)initWithWebView:(UIWebView *)webView
|
- (instancetype)initWithWebView:(UIWebView *)webView
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_objectsToInject = [[NSMutableDictionary alloc] init];
|
_webView = webView;
|
||||||
_webView = webView ?: [[UIWebView alloc] init];
|
|
||||||
_commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL],
|
|
||||||
_scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL],
|
|
||||||
_webView.delegate = self;
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +63,18 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
||||||
return [self initWithWebView:nil];
|
return [self initWithWebView:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
if (!_webView) {
|
||||||
|
_webView = [[UIWebView alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
_objectsToInject = [[NSMutableDictionary alloc] init];
|
||||||
|
_commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL],
|
||||||
|
_scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL],
|
||||||
|
_webView.delegate = self;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
_valid = NO;
|
_valid = NO;
|
||||||
|
|
Loading…
Reference in New Issue