fixed merge conflict
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 548a6a2..5ad2e29 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -35,4 +35,17 @@
### 0.2.3 (Oct 9, 2013)
* [CB-4915] Incremented plugin version on dev branch.
-* [CB-4926] Fixes inappbrowser plugin loading for windows8
\ No newline at end of file
+* [CB-4926] Fixes inappbrowser plugin loading for windows8
+
+### 0.2.4 (Oct 28, 2013)
+* CB-5128: added repo + issue tag to plugin.xml for inappbrowser plugin
+* CB-4995 Fix crash when WebView is quickly opened then closed.
+* CB-4930 - iOS - InAppBrowser should take into account the status bar
+* [CB-5010] Incremented plugin version on dev branch.
+* [CB-5010] Updated version and RELEASENOTES.md for release 0.2.3
+* CB-4858 - Run IAB methods on the UI thread.
+* CB-4858 Convert relative URLs to absolute URLs in JS
+* CB-3747 Fix back button having different dismiss logic from the close button.
+* CB-5021 Expose closeDialog() as a public function and make it safe to call multiple times.
+* CB-5021 Make it safe to call close() multiple times
+>>>>>>> dev
diff --git a/plugin.xml b/plugin.xml
index 03cb490..94cfb82 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -2,12 +2,18 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="org.apache.cordova.inappbrowser"
- version="0.2.3">
+ version="0.2.4">
+
<name>InAppBrowser</name>
<description>Cordova InAppBrowser Plugin</description>
<license>Apache 2.0</license>
<keywords>cordova,in,app,browser,inappbrowser</keywords>
+ <repo>https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git</repo>
+ <issue>https://issues.apache.org/jira/browse/CB/component/12320641</issue>
+ <engines>
+ <engine name="cordova" version=">=3.1.0" /><!-- Needs cordova/urlutil -->
+ </engines>
<js-module src="www/InAppBrowser.js" name="InAppBrowser">
<clobbers target="window.open" />
diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java
index 3be0316..da6b241 100644
--- a/src/android/InAppBrowser.java
+++ b/src/android/InAppBrowser.java
@@ -18,20 +18,6 @@
*/
package org.apache.cordova.inappbrowser;
-import java.util.HashMap;
-import java.util.StringTokenizer;
-
-
-import org.apache.cordova.Config;
-import org.apache.cordova.CordovaWebView;
-import org.apache.cordova.CallbackContext;
-import org.apache.cordova.CordovaPlugin;
-import org.apache.cordova.LOG;
-import org.apache.cordova.PluginResult;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
@@ -52,11 +38,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.CookieManager;
-import android.webkit.WebChromeClient;
-import android.webkit.GeolocationPermissions.Callback;
-import android.webkit.JsPromptResult;
import android.webkit.WebSettings;
-import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
@@ -64,6 +46,19 @@
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.Config;
+import org.apache.cordova.CordovaArgs;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.LOG;
+import org.apache.cordova.PluginResult;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
@SuppressLint("SetJavaScriptEnabled")
public class InAppBrowser extends CordovaPlugin {
@@ -100,121 +95,135 @@
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
- public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
- try {
- if (action.equals("open")) {
- this.callbackContext = callbackContext;
- String url = args.getString(0);
- String target = args.optString(1);
- if (target == null || target.equals("") || target.equals(NULL)) {
- target = SELF;
- }
- HashMap<String, Boolean> features = parseFeature(args.optString(2));
-
- Log.d(LOG_TAG, "target = " + target);
-
- url = updateUrl(url);
- String result = "";
-
- // SELF
- if (SELF.equals(target)) {
- Log.d(LOG_TAG, "in self");
- // load in webview
- if (url.startsWith("file://") || url.startsWith("javascript:")
- || Config.isUrlWhiteListed(url)) {
- this.webView.loadUrl(url);
- }
- //Load the dialer
- else if (url.startsWith(WebView.SCHEME_TEL))
- {
- try {
- Intent intent = new Intent(Intent.ACTION_DIAL);
- intent.setData(Uri.parse(url));
- this.cordova.getActivity().startActivity(intent);
- } catch (android.content.ActivityNotFoundException e) {
- LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
+ public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
+ if (action.equals("open")) {
+ this.callbackContext = callbackContext;
+ final String url = args.getString(0);
+ String t = args.optString(1);
+ if (t == null || t.equals("") || t.equals(NULL)) {
+ t = SELF;
+ }
+ final String target = t;
+ final HashMap<String, Boolean> features = parseFeature(args.optString(2));
+
+ Log.d(LOG_TAG, "target = " + target);
+
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String result = "";
+ // SELF
+ if (SELF.equals(target)) {
+ Log.d(LOG_TAG, "in self");
+ // load in webview
+ if (url.startsWith("file://") || url.startsWith("javascript:")
+ || Config.isUrlWhiteListed(url)) {
+ webView.loadUrl(url);
+ }
+ //Load the dialer
+ else if (url.startsWith(WebView.SCHEME_TEL))
+ {
+ try {
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ intent.setData(Uri.parse(url));
+ cordova.getActivity().startActivity(intent);
+ } catch (android.content.ActivityNotFoundException e) {
+ LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
+ }
+ }
+ // load in InAppBrowser
+ else {
+ result = showWebPage(url, features);
}
}
- // load in InAppBrowser
+ // SYSTEM
+ else if (SYSTEM.equals(target)) {
+ Log.d(LOG_TAG, "in system");
+ result = openExternal(url);
+ }
+ // BLANK - or anything else
else {
- result = this.showWebPage(url, features);
+ Log.d(LOG_TAG, "in blank");
+ result = showWebPage(url, features);
}
+
+ PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
+ pluginResult.setKeepCallback(true);
+ callbackContext.sendPluginResult(pluginResult);
}
- // SYSTEM
- else if (SYSTEM.equals(target)) {
- Log.d(LOG_TAG, "in system");
- result = this.openExternal(url);
+ });
+ }
+ else if (action.equals("close")) {
+ closeDialog();
+ }
+ else if (action.equals("injectScriptCode")) {
+ String jsWrapper = null;
+ if (args.getBoolean(1)) {
+ jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
+ }
+ injectDeferredObject(args.getString(0), jsWrapper);
+ }
+ else if (action.equals("injectScriptFile")) {
+ String jsWrapper;
+ if (args.getBoolean(1)) {
+ jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
+ } else {
+ jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
+ }
+ injectDeferredObject(args.getString(0), jsWrapper);
+ }
+ else if (action.equals("injectStyleCode")) {
+ String jsWrapper;
+ if (args.getBoolean(1)) {
+ jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
+ } else {
+ jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
+ }
+ injectDeferredObject(args.getString(0), jsWrapper);
+ }
+ else if (action.equals("injectStyleFile")) {
+ String jsWrapper;
+ if (args.getBoolean(1)) {
+ jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
+ } else {
+ jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
+ }
+ injectDeferredObject(args.getString(0), jsWrapper);
+ }
+ else if (action.equals("show")) {
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ dialog.show();
}
- // BLANK - or anything else
- else {
- Log.d(LOG_TAG, "in blank");
- result = this.showWebPage(url, features);
- }
-
- PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
- pluginResult.setKeepCallback(true);
- this.callbackContext.sendPluginResult(pluginResult);
- }
- else if (action.equals("close")) {
- closeDialog();
- this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
- }
- else if (action.equals("injectScriptCode")) {
- String jsWrapper = null;
- if (args.getBoolean(1)) {
- jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
- }
- injectDeferredObject(args.getString(0), jsWrapper);
- }
- else if (action.equals("injectScriptFile")) {
- String jsWrapper;
- if (args.getBoolean(1)) {
- jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
- } else {
- jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
- }
- injectDeferredObject(args.getString(0), jsWrapper);
- }
- else if (action.equals("injectStyleCode")) {
- String jsWrapper;
- if (args.getBoolean(1)) {
- jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
- } else {
- jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
- }
- injectDeferredObject(args.getString(0), jsWrapper);
- }
- else if (action.equals("injectStyleFile")) {
- String jsWrapper;
- if (args.getBoolean(1)) {
- jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
- } else {
- jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
- }
- injectDeferredObject(args.getString(0), jsWrapper);
- }
- else if (action.equals("show")) {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- dialog.show();
- }
- };
- this.cordova.getActivity().runOnUiThread(runnable);
- PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
- pluginResult.setKeepCallback(true);
- this.callbackContext.sendPluginResult(pluginResult);
- }
- else {
- return false;
- }
- } catch (JSONException e) {
- this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
+ });
+ PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
+ pluginResult.setKeepCallback(true);
+ this.callbackContext.sendPluginResult(pluginResult);
+ }
+ else {
+ return false;
}
return true;
}
/**
+ * Called when the view navigates.
+ */
+ @Override
+ public void onReset() {
+ closeDialog();
+ }
+
+ /**
+ * Called by AccelBroker when listener is to be shut down.
+ * Stop listener.
+ */
+ public void onDestroy() {
+ closeDialog();
+ }
+
+ /**
* Inject an object (script or style) into the InAppBrowser WebView.
*
* This is a helper method for the inject{Script|Style}{Code|File} API calls, which
@@ -241,8 +250,14 @@
} else {
scriptToInject = source;
}
+ final String finalScriptToInject = scriptToInject;
// This action will have the side-effect of blurring the currently focused element
- this.inAppWebView.loadUrl("javascript:" + scriptToInject);
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ inAppWebView.loadUrl("javascript:" + finalScriptToInject);
+ }
+ });
}
/**
@@ -275,20 +290,6 @@
}
/**
- * Convert relative URL to full path
- *
- * @param url
- * @return
- */
- private String updateUrl(String url) {
- Uri newUrl = Uri.parse(url);
- if (newUrl.isRelative()) {
- url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
- }
- return url;
- }
-
- /**
* Display a new browser with the specified URL.
*
* @param url The url to load.
@@ -311,30 +312,30 @@
/**
* Closes the dialog
*/
- private void closeDialog() {
- try {
- final WebView childView = this.inAppWebView;
- Runnable runnable = new Runnable() {
-
- @Override
- public void run() {
- childView.loadUrl("about:blank");
+ public void closeDialog() {
+ final WebView childView = this.inAppWebView;
+ // The JS protects against multiple calls, so this should happen only when
+ // closeDialog() is called by other native code.
+ if (childView == null) {
+ return;
+ }
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ childView.loadUrl("about:blank");
+ if (dialog != null) {
+ dialog.dismiss();
}
-
- };
-
- this.cordova.getActivity().runOnUiThread(runnable);
+ }
+ });
+ try {
JSONObject obj = new JSONObject();
obj.put("type", EXIT_EVENT);
-
sendUpdate(obj, false);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
- if (dialog != null) {
- dialog.dismiss();
- }
}
/**
@@ -438,14 +439,7 @@
dialog.setCancelable(true);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
- try {
- JSONObject obj = new JSONObject();
- obj.put("type", EXIT_EVENT);
-
- sendUpdate(obj, false);
- } catch (JSONException e) {
- Log.d(LOG_TAG, "Should never happen");
- }
+ closeDialog();
}
});
@@ -620,10 +614,16 @@
*
* @param obj a JSONObject contain event payload information
* @param status the status code to return to the JavaScript environment
- */ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
- PluginResult result = new PluginResult(status, obj);
- result.setKeepCallback(keepCallback);
- this.callbackContext.sendPluginResult(result);
+ */
+ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
+ if (callbackContext != null) {
+ PluginResult result = new PluginResult(status, obj);
+ result.setKeepCallback(keepCallback);
+ callbackContext.sendPluginResult(result);
+ if (!keepCallback) {
+ callbackContext = null;
+ }
+ }
}
diff --git a/src/ios/CDVInAppBrowser.m b/src/ios/CDVInAppBrowser.m
index 4170dd2..4ef051f 100644
--- a/src/ios/CDVInAppBrowser.m
+++ b/src/ios/CDVInAppBrowser.m
@@ -32,6 +32,11 @@
#pragma mark CDVInAppBrowser
+@interface CDVInAppBrowser () {
+ UIStatusBarStyle _previousStatusBarStyle;
+}
+@end
+
@implementation CDVInAppBrowser
- (CDVInAppBrowser*)initWithWebView:(UIWebView*)theWebView
@@ -51,12 +56,8 @@
- (void)close:(CDVInvokedUrlCommand*)command
{
- if (self.inAppBrowserViewController != nil) {
- [self.inAppBrowserViewController close];
- self.inAppBrowserViewController = nil;
- }
-
- self.callbackId = nil;
+ // Things are cleaned up in browserExit.
+ [self.inAppBrowserViewController close];
}
- (BOOL) isSystemUrl:(NSURL*)url
@@ -115,6 +116,7 @@
}
}
+ _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
[self.inAppBrowserViewController showLocationBar:browserOptions.location];
@@ -155,8 +157,13 @@
}
if (! browserOptions.hidden) {
+
+ UINavigationController* nav = [[UINavigationController alloc]
+ initWithRootViewController:self.inAppBrowserViewController];
+ nav.navigationBarHidden = YES;
+
if (self.viewController.modalViewController != self.inAppBrowserViewController) {
- [self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
+ [self.viewController presentModalViewController:nav animated:YES];
}
}
[self.inAppBrowserViewController navigateTo:url];
@@ -166,7 +173,13 @@
{
if ([self.inAppBrowserViewController isViewLoaded] && self.inAppBrowserViewController.view.window)
return;
- [self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
+
+ _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
+
+ UINavigationController* nav = [[UINavigationController alloc]
+ initWithRootViewController:self.inAppBrowserViewController];
+ nav.navigationBarHidden = YES;
+ [self.viewController presentModalViewController:nav animated:YES];
}
- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
@@ -355,13 +368,18 @@
if (self.callbackId != nil) {
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"exit"}];
- [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
-
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ self.callbackId = nil;
}
+ // Set navigationDelegate to nil to ensure no callbacks are received from it.
+ self.inAppBrowserViewController.navigationDelegate = nil;
// Don't recycle the ViewController since it may be consuming a lot of memory.
// Also - this is required for the PDF/User-Agent bug work-around.
self.inAppBrowserViewController = nil;
+
+ if (IsAtLeastiOSVersion(@"7.0")) {
+ [[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle];
+ }
}
@end
@@ -632,6 +650,11 @@
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[super viewDidUnload];
}
+
+- (UIStatusBarStyle)preferredStatusBarStyle
+{
+ return UIStatusBarStyleDefault;
+}
- (void)close
{
@@ -674,6 +697,15 @@
{
[self.webView goForward];
}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ if (IsAtLeastiOSVersion(@"7.0")) {
+ [[UIApplication sharedApplication] setStatusBarStyle:[self preferredStatusBarStyle]];
+ }
+
+ [super viewWillAppear:animated];
+}
#pragma mark UIWebViewDelegate
diff --git a/www/InAppBrowser.js b/www/InAppBrowser.js
index 5da53fd..3fe9261 100644
--- a/www/InAppBrowser.js
+++ b/www/InAppBrowser.js
@@ -22,6 +22,7 @@
var exec = require('cordova/exec');
var channel = require('cordova/channel');
var modulemapper = require('cordova/modulemapper');
+var urlutil = require('cordova/urlutil');
function InAppBrowser() {
this.channels = {
@@ -30,6 +31,7 @@
'loaderror' : channel.create('loaderror'),
'exit' : channel.create('exit')
};
+ this._alive = true;
}
InAppBrowser.prototype = {
@@ -39,7 +41,10 @@
}
},
close: function (eventname) {
- exec(null, null, "InAppBrowser", "close", []);
+ if (this._alive) {
+ this._alive = false;
+ exec(null, null, "InAppBrowser", "close", []);
+ }
},
show: function (eventname) {
exec(null, null, "InAppBrowser", "show", []);
@@ -77,17 +82,18 @@
};
module.exports = function(strUrl, strWindowName, strWindowFeatures) {
- var iab = new InAppBrowser();
- var cb = function(eventname) {
- iab._eventHandler(eventname);
- };
-
// Don't catch calls that write to existing frames (e.g. named iframes).
if (window.frames && window.frames[strWindowName]) {
var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
return origOpenFunc.apply(window, arguments);
}
+ strUrl = urlutil.makeAbsolute(strUrl);
+ var iab = new InAppBrowser();
+ var cb = function(eventname) {
+ iab._eventHandler(eventname);
+ };
+
exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
return iab;
};