Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CB-14187: (ios) Change the InAppBrowser to allow custom schemes #274

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ cordova plugin add ../cordova-plugin-inappbrowser/tests
cordova plugin add ../cordova-plugin-test-framework
```
* edit ```config.xml``` and replace ```<content src="index.html" />``` with ```<content src="cdvtests/index.html" />```
* edit ```config.xml``` and add ```<preference name="AllowedSchemes" value="custom" />```
* run application
```
cordova run
Expand Down
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ 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` set).
- __beforeload__: event fires when the `InAppBrowser` decides whether to load an URL or not (only with option `beforeload=yes`).
- __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` (Android, iOS).
- __callback__: the function that executes when the event fires. The function is passed an `InAppBrowserEvent` object as a parameter.

## Example
Expand Down Expand Up @@ -340,6 +340,10 @@ function messageCallBack(params){
- Windows
- OSX

### iOS Quirks

`loadstop` is being fired after a `customscheme` event. The event URL is that of the currently loaded page, not the URL with the custom scheme.

### Browser Quirks

`loadstart`, `loaderror`, `message` events are not fired.
Expand Down Expand Up @@ -367,6 +371,7 @@ function messageCallBack(params){
- __loadstop__: event fires when the `InAppBrowser` finishes loading a URL.
- __loaderror__: event fires when the `InAppBrowser` encounters an error loading a URL.
- __exit__: event fires when the `InAppBrowser` window is closed.
- __customscheme__: event fires when a link is followed that matches `AllowedSchemes` (Android, iOS).
- __message__: event fires when the `InAppBrowser` receives a message posted from the page loaded inside the `InAppBrowser` Webview.

- __callback__: the function to execute when the event fires.
Expand Down Expand Up @@ -670,6 +675,35 @@ function executeScriptCallBack(params) {

```

### <a id="events_from_browser"></a>Events from the browser

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.

In `config.xml`, include the following:
```xml
<preference name="AllowedSchemes" value="app" />
```

```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.

Please note that this feature is only available on Android and iOS (pull requests
for other platforms are welcome).

## More Usage Info

### Local Urls ( source is in the app package )
Expand Down
25 changes: 25 additions & 0 deletions src/ios/CDVUIInAppBrowser.m
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,21 @@ - (BOOL)isValidCallbackId:(NSString *)callbackId
return NO;
}

- (BOOL)isAllowedScheme:(NSString*)scheme
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This need to be added to the WKWebView, too.

{
NSString* allowedSchemesPreference = [self settingForKey:@"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 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
Expand Down Expand Up @@ -537,6 +552,16 @@ - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}

//test for whitelisted custom scheme names like mycoolapp:// or twitteroauthresponse:// (Twitter Oauth Response)
else if (![[ url scheme] isEqualToString:@"http"] && ![[ url scheme] isEqualToString:@"https"] && [self isAllowedScheme:[url scheme]]) {
// Send a customscheme event.
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsDictionary:@{@"type":@"customscheme", @"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
if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) {
[theWebView stopLoading];
Expand Down
35 changes: 33 additions & 2 deletions tests/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
'<div id="info">' +
'Make sure http://cordova.apache.org and http://google.co.uk and https://www.google.co.uk are white listed. </br>' +
'Make sure http://www.apple.com is not in the white list.</br>' +
'Make sure your config.xml contains: &lt;preference name="AllowedSchemes" value="custom" &gt;/<br/>' +
'In iOS, starred <span style="vertical-align:super">*</span> tests will put the app in a state with no way to return. </br>' +
'<h4>User-Agent: <span id="user-agent"> </span></hr>' +
'</div>';
Expand Down Expand Up @@ -521,16 +522,20 @@ exports.defineManualTests = function (contentEl, createActionButton) {
'<p/> <div id="openHardwareBackDefaultAfterNo"></div>' +
'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';

var customscheme_tests = '<h1>customscheme</h1>' +
'<p/> <div id="openCustomscheme"></div>' +
'Expected result: open an alert dialog with the text "Results verified". Works only on Android and iOS.';

// CB-7490 We need to wrap this code due to Windows security restrictions
// see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details
if (window.MSApp && window.MSApp.execUnsafeLocalFunction) {
MSApp.execUnsafeLocalFunction(function () {
contentEl.innerHTML = info_div + platform_info + local_tests + white_listed_tests + non_white_listed_tests + page_with_redirects_tests + pdf_url_tests + invalid_url_tests +
css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests;
css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests + customscheme_tests;
});
} else {
contentEl.innerHTML = info_div + platform_info + local_tests + white_listed_tests + non_white_listed_tests + page_with_redirects_tests + pdf_url_tests + invalid_url_tests +
css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests;
css_js_injection_tests + open_hidden_tests + clearing_cache_tests + video_tag_tests + local_with_anchor_tag_tests + hardwareback_tests + customscheme_tests;
}

document.getElementById('user-agent').textContent = navigator.userAgent;
Expand Down Expand Up @@ -761,4 +766,30 @@ exports.defineManualTests = function (contentEl, createActionButton) {
});
});
}, 'openHardwareBackDefaultAfterNo');

// Customscheme
createActionButton('customscheme', function () {

var ref = cordova.InAppBrowser.open('about:blank', '_blank', 'hidden=yes' + (platformOpts ? ',' + platformOpts : ''));
var openedCustomscheme = false;
ref.addEventListener('loadstop', function (e) {
// Avoid showing the alert twice on iOS, since loadstop is also being called after the customscheme event.
if (!openedCustomscheme) {
openedCustomscheme = true;
ref.executeScript({ code: 'window.location.replace("custom://test");' });
}
});
ref.addEventListener('customscheme', function (e) {
if (e && e.url === 'custom://test') {
alert('Results verified'); // eslint-disable-line no-undef
} else {
alert('Got: ' + e.url); // eslint-disable-line no-undef
}
ref.close();
});
ref.addEventListener('loaderror', function (e) {
alert('Load error: ' + e.message + '\nYou may need to set the AllowedSchemes preference'); // eslint-disable-line no-undef
ref.close();
});
}, 'openCustomscheme');
};