add .gitignore

This commit is contained in:
JoyWayer
2023-12-27 20:38:37 +08:00
parent b106a628a5
commit f6343426d6
515 changed files with 104217 additions and 199 deletions

View File

@@ -0,0 +1,63 @@
<!doctype html>
<html><head>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<style type='text/css'>
html { font-family:Helvetica; color:#222; }
h1 { color:steelblue; font-size:24px; margin-top:24px; }
button { margin:0 3px 10px; font-size:12px; }
.logLine { border-bottom:1px solid #ccc; padding:4px 2px; font-family:courier; font-size:11px; }
</style>
</head><body>
<h1>WebViewJavascriptBridge Demo</h1>
<script>
window.onerror = function(err) {
log('window.onerror: ' + err)
}
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
setupWebViewJavascriptBridge(function(bridge) {
var uniqueId = 1
function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
}
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'Javascript Says':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
document.body.appendChild(document.createElement('br'))
var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
callbackButton.innerHTML = 'Fire testObjcCallback'
callbackButton.onclick = function(e) {
var result='123';
e.preventDefault()
log('JS calling handler "testObjcCallback"')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
result='12334';
})
log('JS got response', result)
}
})
</script>
<div id='buttons'></div> <div id='log'></div>
</body></html>

View File

@@ -0,0 +1,14 @@
//
// ExampleWKWebViewController.h
// ExampleApp-iOS
//
// Created by Marcus Westin on 1/13/14.
// Copyright (c) 2014 Marcus Westin. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@interface ExampleWKWebViewController : UINavigationController<WKNavigationDelegate>
@end

View File

@@ -0,0 +1,85 @@
//
// ExampleWKWebViewController.m
// ExampleApp-iOS
//
// Created by Marcus Westin on 1/13/14.
// Copyright (c) 2014 Marcus Westin. All rights reserved.
//
#import "ExampleWKWebViewController.h"
#import "WebViewJavascriptBridge.h"
@interface ExampleWKWebViewController ()
@property WebViewJavascriptBridge* bridge;
@end
@implementation ExampleWKWebViewController
- (void)viewWillAppear:(BOOL)animated {
if (_bridge) { return; }
WKWebView* webView = [[NSClassFromString(@"WKWebView") alloc] initWithFrame:self.view.bounds];
webView.navigationDelegate = self;
[self.view addSubview:webView];
[WebViewJavascriptBridge enableLogging];
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
[_bridge setWebViewDelegate:self];
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
responseCallback(@"Response from testObjcCallback");
}];
[_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];
[self renderButtons:webView];
[self loadExamplePage:webView];
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"webViewDidStartLoad");
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"webViewDidFinishLoad");
}
- (void)renderButtons:(WKWebView*)webView {
UIFont* font = [UIFont fontWithName:@"HelveticaNeue" size:12.0];
UIButton *callbackButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[callbackButton setTitle:@"Call handler" forState:UIControlStateNormal];
[callbackButton addTarget:self action:@selector(callHandler:) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:callbackButton aboveSubview:webView];
callbackButton.frame = CGRectMake(10, 400, 100, 35);
callbackButton.titleLabel.font = font;
UIButton* reloadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[reloadButton setTitle:@"Reload webview" forState:UIControlStateNormal];
[reloadButton addTarget:webView action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
[self.view insertSubview:reloadButton aboveSubview:webView];
reloadButton.frame = CGRectMake(110, 400, 100, 35);
reloadButton.titleLabel.font = font;
}
- (void)callHandler:(id)sender {
id data = @{ @"greetingFromObjC": @"Hi there, JS!" };
[_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
NSLog(@"testJavascriptHandler responded: %@", response);
}];
}
- (void)loadExamplePage:(WKWebView*)webView {
NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString* appHtml = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
NSURL *baseURL = [NSURL fileURLWithPath:htmlPath];
[webView loadHTMLString:[NSString stringWithFormat:@"%@",appHtml] baseURL:baseURL];
// NSString *htmlPath=[FuncPublic getFilePath:[NSString stringWithFormat:@"%@/%@/index.html",[FuncPublic filename:@"gamedir"],[FuncPublic filename:@"gamestart"]] PathType:2];
// NSString* appHtml = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
// NSURL *baseURL =[NSURL fileURLWithPath:[FuncPublic getFilePath:[NSString stringWithFormat:@"%@/%@",[FuncPublic filename:@"gamedir"],[FuncPublic filename:@"gamestart"]] PathType:2]];
// [webView loadHTMLString:[NSString stringWithFormat:@"%@",appHtml] baseURL:baseURL];
}
@end

View File

@@ -0,0 +1,34 @@
//
// WKWebViewJavascriptBridge.h
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1)
#define supportsWKWebView
#endif
#if defined supportsWKWebView
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#import <WebKit/WebKit.h>
@interface WKWebViewJavascriptBridge : NSObject<WKNavigationDelegate, WebViewJavascriptBridgeBaseDelegate>
+ (instancetype)bridgeForWebView:(WKWebView*)webView;
+ (void)enableLogging;
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)removeHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)reset;
- (void)setWebViewDelegate:(id)webViewDelegate;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end
#endif

View File

@@ -0,0 +1,198 @@
//
// WKWebViewJavascriptBridge.m
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import "WKWebViewJavascriptBridge.h"
#if defined supportsWKWebView
@implementation WKWebViewJavascriptBridge {
__weak WKWebView* _webView;
__weak id<WKNavigationDelegate> _webViewDelegate;
long _uniqueId;
WebViewJavascriptBridgeBase *_base;
}
/* API
*****/
+ (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; }
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
WKWebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _setupInstance:webView];
[bridge reset];
return bridge;
}
- (void)send:(id)data {
[self send:data responseCallback:nil];
}
- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:nil];
}
- (void)callHandler:(NSString *)handlerName {
[self callHandler:handlerName data:nil responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data {
[self callHandler:handlerName data:data responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
- (void)removeHandler:(NSString *)handlerName {
[_base.messageHandlers removeObjectForKey:handlerName];
}
- (void)reset {
[_base reset];
}
- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate {
_webViewDelegate = webViewDelegate;
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[_base disableJavscriptAlertBoxSafetyTimeout];
}
/* Internals
***********/
- (void)dealloc {
_base = nil;
_webView = nil;
_webViewDelegate = nil;
_webView.navigationDelegate = nil;
}
/* WKWebView Specific Internals
******************************/
- (void) _setupInstance:(WKWebView*)webView {
_webView = webView;
_webView.navigationDelegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void)WKFlushMessageQueue {
[_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
if (error != nil) {
NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
}
[_base flushMessageQueue:result];
}];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) {
[strongDelegate webView:webView didFinishNavigation:navigation];
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)]) {
[strongDelegate webView:webView decidePolicyForNavigationResponse:navigationResponse decisionHandler:decisionHandler];
}
else {
decisionHandler(WKNavigationResponsePolicyAllow);
}
}
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)]) {
[strongDelegate webView:webView didReceiveAuthenticationChallenge:challenge completionHandler:completionHandler];
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (webView != _webView) { return; }
NSURL *url = navigationAction.request.URL;
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
[self WKFlushMessageQueue];
} else {
[_base logUnkownMessage:url];
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) {
[strongDelegate webView:webView didStartProvisionalNavigation:navigation];
}
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailNavigation:withError:)]) {
[strongDelegate webView:webView didFailNavigation:navigation withError:error];
}
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) {
[strongDelegate webView:webView didFailProvisionalNavigation:navigation withError:error];
}
}
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
[_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
return NULL;
}
@end
#endif

View File

@@ -0,0 +1,50 @@
//
// WebViewJavascriptBridge.h
// ExampleApp-iOS
//
// Created by Marcus Westin on 6/14/13.
// Copyright (c) 2013 Marcus Westin. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1)
#define supportsWKWebView
#endif
#if defined supportsWKWebView
#import <WebKit/WebKit.h>
#endif
#if defined __MAC_OS_X_VERSION_MAX_ALLOWED
#define WVJB_PLATFORM_OSX
#define WVJB_WEBVIEW_TYPE WebView
#define WVJB_WEBVIEW_DELEGATE_TYPE NSObject<WebViewJavascriptBridgeBaseDelegate>
#define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject<WebViewJavascriptBridgeBaseDelegate, WebPolicyDelegate>
#elif defined __IPHONE_OS_VERSION_MAX_ALLOWED
#import <UIKit/UIWebView.h>
#define WVJB_PLATFORM_IOS
#define WVJB_WEBVIEW_TYPE UIWebView
#define WVJB_WEBVIEW_DELEGATE_TYPE NSObject<UIWebViewDelegate>
#define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject<UIWebViewDelegate, WebViewJavascriptBridgeBaseDelegate>
#endif
@interface WebViewJavascriptBridge : WVJB_WEBVIEW_DELEGATE_INTERFACE
+ (instancetype)bridgeForWebView:(id)webView;
+ (instancetype)bridge:(id)webView;
+ (void)enableLogging;
+ (void)setLogMaxLength:(int)length;
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)removeHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)setWebViewDelegate:(id)webViewDelegate;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end

View File

@@ -0,0 +1,211 @@
//
// WebViewJavascriptBridge.m
// ExampleApp-iOS
//
// Created by Marcus Westin on 6/14/13.
// Copyright (c) 2013 Marcus Westin. All rights reserved.
//
#import "WebViewJavascriptBridge.h"
#if defined(supportsWKWebView)
#import "WKWebViewJavascriptBridge.h"
#endif
#if __has_feature(objc_arc_weak)
#define WVJB_WEAK __weak
#else
#define WVJB_WEAK __unsafe_unretained
#endif
@implementation WebViewJavascriptBridge {
WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView;
WVJB_WEAK id _webViewDelegate;
long _uniqueId;
WebViewJavascriptBridgeBase *_base;
}
/* API
*****/
+ (void)enableLogging {
[WebViewJavascriptBridgeBase enableLogging];
}
+ (void)setLogMaxLength:(int)length {
[WebViewJavascriptBridgeBase setLogMaxLength:length];
}
+ (instancetype)bridgeForWebView:(id)webView {
return [self bridge:webView];
}
+ (instancetype)bridge:(id)webView {
#if defined supportsWKWebView
if ([webView isKindOfClass:[WKWebView class]]) {
return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView];
}
#endif
if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]]) {
WebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _platformSpecificSetup:webView];
return bridge;
}
[NSException raise:@"BadWebViewType" format:@"Unknown web view type."];
return nil;
}
- (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate {
_webViewDelegate = webViewDelegate;
}
- (void)send:(id)data {
[self send:data responseCallback:nil];
}
- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:nil];
}
- (void)callHandler:(NSString *)handlerName {
[self callHandler:handlerName data:nil responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data {
[self callHandler:handlerName data:data responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
- (void)removeHandler:(NSString *)handlerName {
[_base.messageHandlers removeObjectForKey:handlerName];
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[_base disableJavscriptAlertBoxSafetyTimeout];
}
/* Platform agnostic internals
*****************************/
- (void)dealloc {
[self _platformSpecificDealloc];
_base = nil;
_webView = nil;
_webViewDelegate = nil;
}
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
}
#if defined WVJB_PLATFORM_OSX
/* Platform specific internals: OSX
**********************************/
- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
_webView = webView;
_webView.policyDelegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void) _platformSpecificDealloc {
_webView.policyDelegate = nil;
}
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
if (webView != _webView) { return; }
NSURL *url = [request URL];
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
[listener ignore];
} else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
} else {
[listener use];
}
}
#elif defined WVJB_PLATFORM_IOS
/* Platform specific internals: iOS
**********************************/
- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
_webView = webView;
_webView.delegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void) _platformSpecificDealloc {
_webView.delegate = nil;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongDelegate webViewDidFinishLoad:webView];
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[strongDelegate webView:webView didFailLoadWithError:error];
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (webView != _webView) { return YES; }
NSURL *url = [request URL];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[strongDelegate webViewDidStartLoad:webView];
}
}
#endif
@end

View File

@@ -0,0 +1,46 @@
//
// WebViewJavascriptBridgeBase.h
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#define kOldProtocolScheme @"wvjbscheme"
#define kNewProtocolScheme @"https"
#define kQueueHasMessage @"__wvjb_queue_message__"
#define kBridgeLoaded @"__bridge_loaded__"
typedef void (^WVJBResponseCallback)(id responseData);
typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);
typedef NSDictionary WVJBMessage;
@protocol WebViewJavascriptBridgeBaseDelegate <NSObject>
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand;
@end
@interface WebViewJavascriptBridgeBase : NSObject
@property (weak, nonatomic) id <WebViewJavascriptBridgeBaseDelegate> delegate;
@property (strong, nonatomic) NSMutableArray* startupMessageQueue;
@property (strong, nonatomic) NSMutableDictionary* responseCallbacks;
@property (strong, nonatomic) NSMutableDictionary* messageHandlers;
@property (strong, nonatomic) WVJBHandler messageHandler;
+ (void)enableLogging;
+ (void)setLogMaxLength:(int)length;
- (void)reset;
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName;
- (void)flushMessageQueue:(NSString *)messageQueueString;
- (void)injectJavascriptFile;
- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url;
- (BOOL)isQueueMessageURL:(NSURL*)urll;
- (BOOL)isBridgeLoadedURL:(NSURL*)urll;
- (void)logUnkownMessage:(NSURL*)url;
- (NSString *)webViewJavascriptCheckCommand;
- (NSString *)webViewJavascriptFetchQueyCommand;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end

View File

@@ -0,0 +1,221 @@
//
// WebViewJavascriptBridgeBase.m
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#import "WebViewJavascriptBridge_JS.h"
@implementation WebViewJavascriptBridgeBase {
__weak id _webViewDelegate;
long _uniqueId;
}
static bool logging = false;
static int logMaxLength = 500;
+ (void)enableLogging { logging = true; }
+ (void)setLogMaxLength:(int)length { logMaxLength = length;}
- (id)init {
if (self = [super init]) {
self.messageHandlers = [NSMutableDictionary dictionary];
self.startupMessageQueue = [NSMutableArray array];
self.responseCallbacks = [NSMutableDictionary dictionary];
_uniqueId = 0;
}
return self;
}
- (void)dealloc {
self.startupMessageQueue = nil;
self.responseCallbacks = nil;
self.messageHandlers = nil;
}
- (void)reset {
self.startupMessageQueue = [NSMutableArray array];
self.responseCallbacks = [NSMutableDictionary dictionary];
_uniqueId = 0;
}
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
NSMutableDictionary* message = [NSMutableDictionary dictionary];
if (data) {
message[@"data"] = data;
}
if (responseCallback) {
NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
self.responseCallbacks[callbackId] = [responseCallback copy];
message[@"callbackId"] = callbackId;
}
if (handlerName) {
message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];
}
- (void)flushMessageQueue:(NSString *)messageQueueString{
if (messageQueueString == nil || messageQueueString.length == 0) {
NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
return;
}
id messages = [self _deserializeMessageJSON:messageQueueString];
for (WVJBMessage* message in messages) {
if (![message isKindOfClass:[WVJBMessage class]]) {
NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
continue;
}
[self _log:@"RCVD" json:message];
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
} else {
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
if (!handler) {
NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
continue;
}
handler(message[@"data"], responseCallback);
}
}
}
- (void)injectJavascriptFile {
NSString *js = WebViewJavascriptBridge_js();
[self _evaluateJavascript:js];
if (self.startupMessageQueue) {
NSArray* queue = self.startupMessageQueue;
self.startupMessageQueue = nil;
for (id queuedMessage in queue) {
[self _dispatchMessage:queuedMessage];
}
}
}
- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url {
if (![self isSchemeMatch:url]) {
return NO;
}
return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url];
}
- (BOOL)isSchemeMatch:(NSURL*)url {
NSString* scheme = url.scheme.lowercaseString;
return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme];
}
- (BOOL)isQueueMessageURL:(NSURL*)url {
NSString* host = url.host.lowercaseString;
return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage];
}
- (BOOL)isBridgeLoadedURL:(NSURL*)url {
NSString* host = url.host.lowercaseString;
return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];
}
- (void)logUnkownMessage:(NSURL*)url {
NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@", [url absoluteString]);
}
- (NSString *)webViewJavascriptCheckCommand {
return @"typeof WebViewJavascriptBridge == \'object\';";
}
- (NSString *)webViewJavascriptFetchQueyCommand {
return @"WebViewJavascriptBridge._fetchQueue();";
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"];
}
// Private
// -------------------------------------------
- (void) _evaluateJavascript:(NSString *)javascriptCommand {
[self.delegate _evaluateJavascript:javascriptCommand];
}
- (void)_queueMessage:(WVJBMessage*)message {
if (self.startupMessageQueue) {
[self.startupMessageQueue addObject:message];
} else {
[self _dispatchMessage:message];
}
}
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString *messageJSON = [self _serializeMessage:message pretty:NO];
[self _log:@"SEND" json:messageJSON];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
if ([[NSThread currentThread] isMainThread]) {
[self _evaluateJavascript:javascriptCommand];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self _evaluateJavascript:javascriptCommand];
});
}
}
- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{
return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding];
}
- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON {
return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
}
- (void)_log:(NSString *)action json:(id)json {
if (!logging) { return; }
if (![json isKindOfClass:[NSString class]]) {
json = [self _serializeMessage:json pretty:YES];
}
if ([json length] > logMaxLength) {
NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]);
} else {
NSLog(@"WVJB %@: %@", action, json);
}
}
@end

View File

@@ -0,0 +1,3 @@
#import <Foundation/Foundation.h>
NSString * WebViewJavascriptBridge_js();

View File

@@ -0,0 +1,139 @@
// This file contains the source for the Javascript side of the
// WebViewJavascriptBridge. It is plaintext, but converted to an NSString
// via some preprocessor tricks.
//
// Previous implementations of WebViewJavascriptBridge loaded the javascript source
// from a resource. This worked fine for app developers, but library developers who
// included the bridge into their library, awkwardly had to ask consumers of their
// library to include the resource, violating their encapsulation. By including the
// Javascript as a string resource, the encapsulation of the library is maintained.
#import "WebViewJavascriptBridge_JS.h"
NSString * WebViewJavascriptBridge_js() {
#define __wvjb_js_func__(x) #x
// BEGIN preprocessorJSCode
static NSString * preprocessorJSCode = @__wvjb_js_func__(
;(function() {
if (window.WebViewJavascriptBridge) {
return;
}
if (!window.onerror) {
window.onerror = function(msg, url, line) {
console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
}
}
window.WebViewJavascriptBridge = {
registerHandler: registerHandler,
callHandler: callHandler,
disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
_fetchQueue: _fetchQueue,
_handleMessageFromObjC: _handleMessageFromObjC
};
var messagingIframe;
var sendMessageQueue = [];
var messageHandlers = {};
var CUSTOM_PROTOCOL_SCHEME = 'https';
var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
var responseCallbacks = {};
var uniqueId = 1;
var dispatchMessagesWithTimeoutSafety = true;
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
function callHandler(handlerName, data, responseCallback) {
if (arguments.length == 2 && typeof data == 'function') {
responseCallback = data;
data = null;
}
_doSend({ handlerName:handlerName, data:data }, responseCallback);
}
function disableJavscriptAlertBoxSafetyTimeout() {
dispatchMessagesWithTimeoutSafety = false;
}
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message['callbackId'] = callbackId;
}
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
function _dispatchMessageFromObjC(messageJSON) {
if (dispatchMessagesWithTimeoutSafety) {
setTimeout(_doDispatchMessageFromObjC);
} else {
_doDispatchMessageFromObjC();
}
function _doDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON);
var messageHandler;
var responseCallback;
if (message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};
}
var handler = messageHandlers[message.handlerName];
if (!handler) {
console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
} else {
handler(message.data, responseCallback);
}
}
}
}
function _handleMessageFromObjC(messageJSON) {
_dispatchMessageFromObjC(messageJSON);
}
messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);
registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
var callbacks = window.WVJBCallbacks;
delete window.WVJBCallbacks;
for (var i=0; i<callbacks.length; i++) {
callbacks[i](WebViewJavascriptBridge);
}
}
})();
); // END preprocessorJSCode
#undef __wvjb_js_func__
return preprocessorJSCode;
};