You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2013/04/04 20:48:02 UTC
ios commit: [CB-2653] Delay executeScript/insertCSS callback until
resources have loaded; pass JS results to callback
Updated Branches:
refs/heads/master d2ffc92a5 -> c9f233250
[CB-2653] Delay executeScript/insertCSS callback until resources have loaded; pass JS results to callback
Project: http://git-wip-us.apache.org/repos/asf/cordova-ios/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/c9f23325
Tree: http://git-wip-us.apache.org/repos/asf/cordova-ios/tree/c9f23325
Diff: http://git-wip-us.apache.org/repos/asf/cordova-ios/diff/c9f23325
Branch: refs/heads/master
Commit: c9f233250d4b800f3412eeded811daaafb17b2cc
Parents: d2ffc92
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Mar 25 14:55:16 2013 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Thu Apr 4 14:45:56 2013 -0400
----------------------------------------------------------------------
CordovaLib/Classes/CDVInAppBrowser.h | 1 -
CordovaLib/Classes/CDVInAppBrowser.m | 120 +++++++++++++++++++++++++++--
2 files changed, 113 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/c9f23325/CordovaLib/Classes/CDVInAppBrowser.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVInAppBrowser.h b/CordovaLib/Classes/CDVInAppBrowser.h
index aa3e7a5..cb16569 100644
--- a/CordovaLib/Classes/CDVInAppBrowser.h
+++ b/CordovaLib/Classes/CDVInAppBrowser.h
@@ -41,7 +41,6 @@
- (void)open:(CDVInvokedUrlCommand*)command;
- (void)close:(CDVInvokedUrlCommand*)command;
- (void)injectScriptCode:(CDVInvokedUrlCommand*)command;
-- (void)injectCSS:(CDVInvokedUrlCommand*)command;
@end
http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/c9f23325/CordovaLib/Classes/CDVInAppBrowser.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVInAppBrowser.m b/CordovaLib/Classes/CDVInAppBrowser.m
index ef0acb3..1844a7f 100644
--- a/CordovaLib/Classes/CDVInAppBrowser.m
+++ b/CordovaLib/Classes/CDVInAppBrowser.m
@@ -20,6 +20,7 @@
#import "CDVInAppBrowser.h"
#import "CDVPluginResult.h"
#import "CDVUserAgentUtil.h"
+#import "CDVJSON.h"
#define kInAppBrowserTargetSelf @"_self"
#define kInAppBrowserTargetSystem @"_system"
@@ -159,24 +160,124 @@
}
}
+// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
+// provides a consistent method for injecting JavaScript code into the document.
+//
+// If a wrapper string is supplied, then the source string will be JSON-encoded (adding
+// quotes) and wrapped using string formatting. (The wrapper string should have a single
+// '%@' marker).
+//
+// If no wrapper is supplied, then the source string is executed directly.
+
+- (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
+{
+ if (jsWrapper != nil) {
+ NSString* sourceArrayString = [@[source] JSONString];
+ if (sourceArrayString) {
+ NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
+ NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
+ }
+ } else {
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
+ }
+}
+
- (void)injectScriptCode:(CDVInvokedUrlCommand*)command
{
- NSString* source = [command argumentAtIndex:0];
- CDVPluginResult* pluginResult;
+ NSString* jsWrapper = nil;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"document.getElementById('cdv-iab-bridge').src='gap-iab://%@/'+window.escape(JSON.stringify([eval(%%@)]));", command.callbackId];
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
- [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
+- (void)injectScriptFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
- pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { document.getElementById('cdv-iab-bridge').src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleCode:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { document.getElementById('cdv-iab-bridge').src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { document.getElementById('cdv-iab-bridge').src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
}
#pragma mark CDVInAppBrowserNavigationDelegate
+/**
+ * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
+ * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
+ * other code execution is possible.
+ *
+ * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form:
+ *
+ * gap-iab://<callbackId>/<arguments>
+ *
+ * where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789")
+ *
+ * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded
+ * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION
+ * is returned if the JSON is invalid.
+ */
- (void)browserLoadStart:(NSURL*)url
{
+ CDVPluginResult* pluginResult;
+
+ // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
+ // and the path, if present, should be a JSON-encoded value to pass to the callback.
+ if ([[url scheme] isEqualToString:@"gap-iab"]) {
+ NSString* scriptCallbackId = [url host];
+
+ if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
+ NSString* scriptResult = [url path];
+ NSError* __autoreleasing error = nil;
+
+ // The message should be a JSON-encoded array of the result of the script which executed.
+ if ((scriptResult != nil) && ([scriptResult length] > 1)) {
+ scriptResult = [scriptResult substringFromIndex:1];
+ NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
+ if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
+ }
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
+ return;
+ }
+ }
if (self.callbackId != nil) {
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
- messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
[pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
@@ -185,6 +286,8 @@
- (void)browserLoadStop:(NSURL*)url
{
+ // Create an iframe bridge in the new document to communicate with the CDVInAppBrowserViewController
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){var e=d.createElement('iframe');e.id='cdv-iab-bridge';e.style.display='none';d.body.appendChild(e);})(document)"];
if (self.callbackId != nil) {
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"loadstop", @"url":[url absoluteString]}];
@@ -452,6 +555,9 @@
url = _requestedURL;
}
[self.navigationDelegate browserLoadStart:url];
+ // Return NO if the URL uses the 'gap-iab' protocol, as this is used for the
+ // IAB bridge, rather than any actual web content.
+ return ![[url scheme] isEqualToString:@"gap-iab"];
}
return YES;
}