diff --git a/README.md b/README.md index c2bc74ef8..fc164950d 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,7 @@ The object returned from a call to `cordova.InAppBrowser.open` when the target i - __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` set). - __message__: event fires when the `InAppBrowser` receives a message posted from the page loaded inside the `InAppBrowser` Webview. + - __customscheme__: event fires when a link is followed that matches `AllowedSchemes`. - __download__: _(Android Only)_ event fires when the `InAppBrowser` loads a URL that leads in downloading of a file. - __callback__: the function that executes when the event fires. The function is passed an `InAppBrowserEvent` object as a parameter. @@ -309,6 +310,36 @@ function messageCallBack(params){ } ``` + +### Customscheme event Example + +Sometimes you may want to respond to an event happening on the page loaded in the browser, +for example a button to open the barcode scanner, or closing the browser when a login flow +was finished. This can done by navigating to a URL with a custom scheme listed in the +`AllowedSchemes` preference in `config.xml`, triggering a `customscheme` event on the +browser. Multiple values are separated by comma's. + +- **type** _it contains the String value "customscheme" always_ +- **url** _The url with custom scheme that triggered the event_ + +In `config.xml`, include the following: +```xml + +``` + +```javascript +function onCustomScheme(e) { + if (e.url === 'app://hide') { + inAppBrowserRef.hide(); + } +} + +inAppBrowserRef = cordova.InAppBrowser.open('https://example.com', '_blank'); +inAppBrowserRef.addEventListener('customscheme', onCustomScheme); +``` + +When the opened page navigates to the link `app://hide`, the browser is hidden. + #### Download event Example Whenever the InAppBrowser receives or locates to a url which leads in downloading a file, the callback assigned to the "download" event is called. The parameter passed to this callback is an object with the the following properties @@ -340,7 +371,7 @@ function downloadListener(params){ ### InAppBrowserEvent Properties -- __type__: the eventname, either `loadstart`, `loadstop`, `loaderror`, `message` or `exit`. _(String)_ +- __type__: the eventname, either `loadstart`, `loadstop`, `loaderror`, `message`, `customscheme` or `exit`. _(String)_ - __url__: the URL that was loaded. _(String)_ - __code__: the error code, only in the case of `loaderror`. _(Number)_ - __message__: the error message, only in the case of `loaderror`. _(String)_ @@ -354,7 +385,7 @@ function downloadListener(params){ ### Browser Quirks -`loadstart`, `loaderror`, `message` events are not fired. +`loadstart`, `loaderror`, `message`, `customscheme` events are not fired. ### Quick Example @@ -376,6 +407,7 @@ function downloadListener(params){ - __loaderror__: event fires when the `InAppBrowser` encounters an error loading a URL. - __exit__: event fires when the `InAppBrowser` window is closed. - __message__: event fires when the `InAppBrowser` receives a message posted from the page loaded inside the `InAppBrowser` Webview. + - __customscheme__: event fires when a link is followed that matches `AllowedSchemes`. - __download__: _(Android only)_ event fires when the `InAppBrowser` loads a URL that leads in downloading of a file. - __callback__: the function to execute when the event fires. diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 993181cdc..6fa25a845 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -413,6 +413,21 @@ - (void)injectStyleFile:(CDVInvokedUrlCommand *)command [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; } +- (BOOL)isAllowedScheme:(NSString *)scheme +{ + NSString *allowedSchemesPreference = [self.commandDelegate.settings cordovaSettingForKey:@"AllowedSchemes"]; + if (allowedSchemesPreference == nil || [allowedSchemesPreference isEqualToString:@""]) { + // Preference missing. + return NO; + } + for (NSString *allowedScheme in [allowedSchemesPreference componentsSeparatedByString:@","]) { + if ([allowedScheme isEqualToString:scheme]) { + return YES; + } + } + return NO; +} + /** * The message handler 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 @@ -465,6 +480,15 @@ - (void)webView:(WKWebView *)theWebView decidePolicyForNavigationAction:(WKNavig if ([allowedSchemes containsObject:[url scheme]]) { [theWebView stopLoading]; [self openInSystem:url]; + shouldStart = NO; + } else if ((self.callbackId != nil) && ![[url scheme] isEqualToString:@"http"] && ![[url scheme] isEqualToString:@"https"] && [self isAllowedScheme:[url scheme]]) { + // Send a customscheme event for allowed schemes that are not http/https. + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"customscheme", @"url":[url absoluteString]}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + shouldStart = NO; } else if ((self.callbackId != nil) && isTopLevelNavigation) { // Send a loadstart event for each top-level navigation (includes redirects). diff --git a/tests/tests.js b/tests/tests.js index f315f0675..4e492a47e 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -377,6 +377,7 @@ exports.defineManualTests = function (contentEl, createActionButton) { 'Make sure http://www.apple.com is not in the white list.
' + 'In iOS, starred * tests will put the app in a state with no way to return.
' + '

User-Agent: ' + + 'Make sure your config.xml contains: <preference name="AllowedSchemes" value="custom" >/
' + ''; const local_tests = @@ -525,6 +526,11 @@ exports.defineManualTests = function (contentEl, createActionButton) { '

' + 'Expected result: consistently open browsers with with the appropriate option: hardwareback=defaults to yes then hardwareback=no then hardwareback=defaults to yes. By default hardwareback is yes so pressing back button should navigate backwards in history then close InAppBrowser'; + const customscheme_tests = + '

Customscheme

' + + '

' + + 'Expected result: open an alert dialog with the text "Results verified". Works only on Android and iOS.'; + contentEl.innerHTML = info_div + platform_info + @@ -539,7 +545,8 @@ exports.defineManualTests = function (contentEl, createActionButton) { clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + - hardwareback_tests; + hardwareback_tests + + customscheme_tests; document.getElementById('user-agent').textContent = navigator.userAgent; @@ -979,4 +986,28 @@ exports.defineManualTests = function (contentEl, createActionButton) { }, 'openHardwareBackDefaultAfterNo' ); + + // Customscheme + createActionButton( + 'customscheme', + function () { + const ref = cordova.InAppBrowser.open('about:blank', '_blank', 'hidden=yes' + (platformOpts ? ',' + platformOpts : '')); + ref.addEventListener('loadstop', function (_) { + ref.executeScript({ code: 'window.location.replace("custom://test");' }); + }); + ref.addEventListener('customscheme', function (e) { + if (e && e.url === 'custom://test') { + alert('Results verified'); + } else { + alert('Got: ' + e.url); + } + ref.close(); + }); + ref.addEventListener('loaderror', function (e) { + alert('Load error: ' + e.message + '\nYou may need to set the AllowedSchemes preference'); + ref.close(); + }); + }, + 'openCustomscheme' + ); };