diff --git a/README.md b/README.md index 615f69a..3ec2f3b 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,6 @@ Although `window.open` is in the global scope, InAppBrowser is not available unt console.log("window.open works well"); } -Report issues with this plugin on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22cordova-plugin-inappbrowser%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC) - - -## Reference ## Installation cordova plugin add cordova-plugin-inappbrowser @@ -115,8 +111,9 @@ instance, or the system browser. Android supports these additional options: - __hidden__: set to `yes` to create the browser and load the page, but not show it. The loadstop event fires when loading is complete. Omit or set to `no` (default) to have the browser open and load normally. - - __clearcache__: set to `yes` to have the browser's cookie cache cleared before the new window is opened. - - __clearsessioncache__: set to `yes` to have the session cookie cache cleared before the new window is opened. + - __beforeload__: set to `yes` to enable the `beforeload` event to modify which pages are actually loaded in the browser. + - __clearcache__: set to `yes` to have the browser's cookie cache cleared before the new window is opened + - __clearsessioncache__: set to `yes` to have the session cookie cache cleared before the new window is opened - __closebuttoncaption__: set to a string to use as the close button's caption instead of a X. Note that you need to localize this value yourself. - __closebuttoncolor__: set to a valid hex color string, for example: `#00ff00`, and it will change the close button color from default, regardless of being a text or default X. Only has effect if user has location set to `yes`. @@ -138,6 +135,7 @@ instance, or the system browser. - __usewkwebview__: set to `yes` to use WKWebView engine for the InappBrowser. Omit or set to `no` (default) to use UIWebView. Note: Using `usewkwebview=yes` requires that a WKWebView engine plugin be installed in the Cordova project (e.g. [cordova-plugin-wkwebview-engine](https://github.com/apache/cordova-plugin-wkwebview-engine) or [cordova-plugin-ionic-webview](https://github.com/ionic-team/cordova-plugin-ionic-webview)). - __hidden__: set to `yes` to create the browser and load the page, but not show it. The loadstop event fires when loading is complete. Omit or set to `no` (default) to have the browser open and load normally. + - __beforeload__: set to `yes` to enable the `beforeload` event to modify which pages are actually loaded in the browser. - __clearcache__: set to `yes` to have the browser's cookie cache cleared before the new window is opened - __clearsessioncache__: set to `yes` to have the session cookie cache cleared before the new window is opened. For WKWebView, requires iOS 11+ on target device. - __cleardata__: set to `yes` to have the browser's entire local storage cleared (cookies, HTML5 local storage, IndexedDB, etc.) before the new window is opened @@ -219,6 +217,7 @@ The object returned from a call to `cordova.InAppBrowser.open` when the target i - __loadstop__: event fires when the `InAppBrowser` finishes loading a URL. - __loaderror__: event fires when the `InAppBrowser` encounters an error when loading a URL. - __exit__: event fires when the `InAppBrowser` window is closed. + - __beforeload__: event fires when the `InAppBrowser` decides whether to load an URL or not (only with option `beforeload=yes`). - __callback__: the function that executes when the event fires. The function is passed an `InAppBrowserEvent` object as a parameter. @@ -232,7 +231,7 @@ function showHelp(url) { var target = "_blank"; - var options = "location=yes,hidden=yes"; + var options = "location=yes,hidden=yes,beforeload=yes"; inAppBrowserRef = cordova.InAppBrowser.open(url, target, options); @@ -242,6 +241,8 @@ function showHelp(url) { inAppBrowserRef.addEventListener('loaderror', loadErrorCallBack); + inAppBrowserRef.addEventListener('beforeload', beforeloadCallBack); + } function loadStartCallBack() { @@ -290,6 +291,20 @@ function executeScriptCallBack(params) { } +function beforeloadCallback(params, callback) { + + if (params.url.startsWith("http://www.example.com/")) { + + // Load this URL in the inAppBrowser. + callback(params.url); + } else { + + // The callback is not invoked, so the page will not be loaded. + $('#status-message').text("This browser only opens pages on http://www.example.com/"); + } + +} + ``` ### InAppBrowserEvent Properties diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index 92ca3c1..93d9460 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -110,6 +110,7 @@ public class InAppBrowser extends CordovaPlugin { private static final String HIDE_URL = "hideurlbar"; private static final String FOOTER = "footer"; private static final String FOOTER_COLOR = "footercolor"; + private static final String BEFORELOAD = "beforeload"; private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR); @@ -138,6 +139,7 @@ public class InAppBrowser extends CordovaPlugin { private boolean hideUrlBar = false; private boolean showFooter = false; private String footerColor = ""; + private boolean useBeforeload = false; private String[] allowedSchemes; /** @@ -246,6 +248,20 @@ public class InAppBrowser extends CordovaPlugin { else if (action.equals("close")) { closeDialog(); } + else if (action.equals("loadAfterBeforeload")) { + if (!useBeforeload) { + LOG.e(LOG_TAG, "unexpected loadAfterBeforeload called without feature beforeload=yes"); + } + final String url = args.getString(0); + this.cordova.getActivity().runOnUiThread(new Runnable() { + @SuppressLint("NewApi") + @Override + public void run() { + ((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false; + inAppWebView.loadUrl(url); + } + }); + } else if (action.equals("injectScriptCode")) { String jsWrapper = null; if (args.getBoolean(1)) { @@ -674,6 +690,10 @@ public class InAppBrowser extends CordovaPlugin { if (footerColorSet != null) { footerColor = footerColorSet; } + String beforeload = features.get(BEFORELOAD); + if (beforeload != null) { + useBeforeload = beforeload.equals("yes") ? true : false; + } } final CordovaWebView thatWebView = this.webView; @@ -924,7 +944,7 @@ public class InAppBrowser extends CordovaPlugin { } }); - WebViewClient client = new InAppBrowserClient(thatWebView, edittext); + WebViewClient client = new InAppBrowserClient(thatWebView, edittext, useBeforeload); inAppWebView.setWebViewClient(client); WebSettings settings = inAppWebView.getSettings(); settings.setJavaScriptEnabled(true); @@ -1085,6 +1105,8 @@ public class InAppBrowser extends CordovaPlugin { public class InAppBrowserClient extends WebViewClient { EditText edittext; CordovaWebView webView; + boolean useBeforeload; + boolean waitForBeforeload; /** * Constructor. @@ -1092,9 +1114,11 @@ public class InAppBrowser extends CordovaPlugin { * @param webView * @param mEditText */ - public InAppBrowserClient(CordovaWebView webView, EditText mEditText) { + public InAppBrowserClient(CordovaWebView webView, EditText mEditText, boolean useBeforeload) { this.webView = webView; this.edittext = mEditText; + this.useBeforeload = useBeforeload; + this.waitForBeforeload = useBeforeload; } /** @@ -1107,12 +1131,27 @@ public class InAppBrowser extends CordovaPlugin { */ @Override public boolean shouldOverrideUrlLoading(WebView webView, String url) { + boolean override = false; + + // On first URL change, initiate JS callback. Only after the beforeload event, continue. + if (this.waitForBeforeload) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", "beforeload"); + obj.put("url", url); + sendUpdate(obj, true); + return true; + } catch (JSONException ex) { + LOG.e(LOG_TAG, "URI passed in has caused a JSON error."); + } + } + if (url.startsWith(WebView.SCHEME_TEL)) { try { Intent intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(url)); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); } @@ -1121,7 +1160,7 @@ public class InAppBrowser extends CordovaPlugin { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString()); } @@ -1152,7 +1191,7 @@ public class InAppBrowser extends CordovaPlugin { intent.putExtra("address", address); intent.setType("vnd.android-dir/mms-sms"); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); } @@ -1173,7 +1212,7 @@ public class InAppBrowser extends CordovaPlugin { obj.put("type", "customscheme"); obj.put("url", url); sendUpdate(obj, true); - return true; + override = true; } catch (JSONException ex) { LOG.e(LOG_TAG, "Custom Scheme URI passed in has caused a JSON error."); } @@ -1182,7 +1221,10 @@ public class InAppBrowser extends CordovaPlugin { } } - return false; + if (this.useBeforeload) { + this.waitForBeforeload = true; + } + return override; } @@ -1304,4 +1346,4 @@ public class InAppBrowser extends CordovaPlugin { super.onReceivedHttpAuthRequest(view, handler, host, realm); } } -} \ No newline at end of file +} diff --git a/src/ios/CDVInAppBrowserOptions.h b/src/ios/CDVInAppBrowserOptions.h index 93e48d4..14b7a7a 100644 --- a/src/ios/CDVInAppBrowserOptions.h +++ b/src/ios/CDVInAppBrowserOptions.h @@ -45,6 +45,7 @@ @property (nonatomic, assign) BOOL suppressesincrementalrendering; @property (nonatomic, assign) BOOL hidden; @property (nonatomic, assign) BOOL disallowoverscroll; +@property (nonatomic, assign) BOOL beforeload; + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options; diff --git a/src/ios/CDVUIInAppBrowser.h b/src/ios/CDVUIInAppBrowser.h index 64afe3b..55c3662 100644 --- a/src/ios/CDVUIInAppBrowser.h +++ b/src/ios/CDVUIInAppBrowser.h @@ -33,6 +33,10 @@ @interface CDVUIInAppBrowser : CDVPlugin { UIWindow * tmpWindow; + + @private + BOOL _useBeforeload; + BOOL _waitForBeforeload; } @property (nonatomic, retain) CDVUIInAppBrowserViewController* inAppBrowserViewController; @@ -45,6 +49,7 @@ - (void)injectScriptCode:(CDVInvokedUrlCommand*)command; - (void)show:(CDVInvokedUrlCommand*)command; - (void)hide:(CDVInvokedUrlCommand*)command; +- (void)loadAfterBeforeload:(CDVInvokedUrlCommand*)command; @end diff --git a/src/ios/CDVUIInAppBrowser.m b/src/ios/CDVUIInAppBrowser.m index 2ae8611..ebd6607 100644 --- a/src/ios/CDVUIInAppBrowser.m +++ b/src/ios/CDVUIInAppBrowser.m @@ -53,6 +53,8 @@ static CDVUIInAppBrowser* instance = nil; instance = self; _previousStatusBarStyle = -1; _callbackIdPattern = nil; + _useBeforeload = NO; + _waitForBeforeload = NO; } - (id)settingForKey:(NSString*)key @@ -216,6 +218,10 @@ static CDVUIInAppBrowser* instance = nil; self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering; } + // use of beforeload event + _useBeforeload = browserOptions.beforeload; + _waitForBeforeload = browserOptions.beforeload; + [self.inAppBrowserViewController navigateTo:url]; if (!browserOptions.hidden) { [self show:nil]; @@ -312,6 +318,27 @@ static CDVUIInAppBrowser* instance = nil; } } +- (void)loadAfterBeforeload:(CDVInvokedUrlCommand*)command +{ + NSString* urlStr = [command argumentAtIndex:0]; + + if (!_useBeforeload) { + NSLog(@"unexpected loadAfterBeforeload called without feature beforeload=yes"); + } + if (self.inAppBrowserViewController == nil) { + NSLog(@"Tried to invoke loadAfterBeforeload on IAB after it was closed."); + return; + } + if (urlStr == nil) { + NSLog(@"loadAfterBeforeload called with nil argument, ignoring."); + return; + } + + NSURL* url = [NSURL URLWithString:urlStr]; + _waitForBeforeload = NO; + [self.inAppBrowserViewController navigateTo:url]; +} + // 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. // @@ -421,6 +448,7 @@ static CDVUIInAppBrowser* instance = nil; { NSURL* url = request.URL; BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; + BOOL shouldStart = YES; // 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. @@ -448,11 +476,22 @@ static CDVUIInAppBrowser* instance = nil; return NO; } } + + // When beforeload=yes, on first URL change, initiate JS callback. Only after the beforeload event, continue. + if (_waitForBeforeload && isTopLevelNavigation) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"beforeload", @"url":[url absoluteString]}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + return NO; + } + //if is an app store link, let the system handle it, otherwise it fails to load it - else if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) { + if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) { [theWebView stopLoading]; [self openInSystem:url]; - return NO; + shouldStart = NO; } else if ((self.callbackId != nil) && isTopLevelNavigation) { // Send a loadstart event for each top-level navigation (includes redirects). @@ -463,8 +502,11 @@ static CDVUIInAppBrowser* instance = nil; [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; } - return YES; -} + if (_useBeforeload && isTopLevelNavigation) { + _waitForBeforeload = YES; + } + + return shouldStart; - (void)webViewDidStartLoad:(UIWebView*)theWebView { diff --git a/www/inappbrowser.js b/www/inappbrowser.js index 3619f17..7764765 100644 --- a/www/inappbrowser.js +++ b/www/inappbrowser.js @@ -33,6 +33,7 @@ function InAppBrowser () { this.channels = { + 'beforeload': channel.create('beforeload'), 'loadstart': channel.create('loadstart'), 'loadstop': channel.create('loadstop'), 'loaderror': channel.create('loaderror'), @@ -44,9 +45,17 @@ InAppBrowser.prototype = { _eventHandler: function (event) { if (event && (event.type in this.channels)) { - this.channels[event.type].fire(event); + if (event.type === 'beforeload') { + this.channels[event.type].fire(event, this._loadAfterBeforeload); + } else { + this.channels[event.type].fire(event); + } } }, + _loadAfterBeforeload: function (strUrl) { + strUrl = urlutil.makeAbsolute(strUrl); + exec(null, null, 'InAppBrowser', 'loadAfterBeforeload', [strUrl]); + }, close: function (eventname) { exec(null, null, 'InAppBrowser', 'close', []); },