From 0d81d36d9187fb660d90215d76c170d1787be430 Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Mon, 9 Feb 2026 03:25:21 +0900 Subject: [PATCH 1/5] feat(ios): custom scheme --- src/ios/CDVWKInAppBrowser.m | 24 ++++++++++++++++++++++++ tests/tests.js | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 993181cdc..5cf72ef0b 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 objectForKey:@"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. + 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' + ); }; From fe85886724b8704f6912bd07bd395237f9eb1b29 Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Mon, 9 Feb 2026 04:07:18 +0900 Subject: [PATCH 2/5] doc: update readme --- README.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) 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. From ae1a256a8cf048151d52568a534474b97c6c4b18 Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Mon, 9 Feb 2026 14:16:11 +0900 Subject: [PATCH 3/5] fix(ios): fix code style --- src/ios/CDVWKInAppBrowser.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 5cf72ef0b..8915487d7 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -413,14 +413,14 @@ - (void)injectStyleFile:(CDVInvokedUrlCommand *)command [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; } -- (BOOL)isAllowedScheme:(NSString*)scheme +- (BOOL)isAllowedScheme:(NSString *)scheme { - NSString* allowedSchemesPreference = [self.commandDelegate.settings objectForKey:@"AllowedSchemes"]; + NSString *allowedSchemesPreference = [_settings cordovaSettingForKey:@"AllowedSchemes"]; if (allowedSchemesPreference == nil || [allowedSchemesPreference isEqualToString:@""]) { // Preference missing. return NO; } - for (NSString* allowedScheme in [allowedSchemesPreference componentsSeparatedByString:@","]) { + for (NSString *allowedScheme in [allowedSchemesPreference componentsSeparatedByString:@","]) { if ([allowedScheme isEqualToString:scheme]) { return YES; } @@ -481,9 +481,9 @@ - (void)webView:(WKWebView *)theWebView decidePolicyForNavigationAction:(WKNavig [theWebView stopLoading]; [self openInSystem:url]; shouldStart = NO; - } else if ((self.callbackId != nil) && ![[ url scheme] isEqualToString:@"http"] && ![[ url scheme] isEqualToString:@"https"] && [self isAllowedScheme:[url scheme]]) { + } else if ((self.callbackId != nil) && ![[url scheme] isEqualToString:@"http"] && ![[url scheme] isEqualToString:@"https"] && [self isAllowedScheme:[url scheme]]) { // Send a customscheme event. - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{@"type":@"customscheme", @"url":[url absoluteString]}]; [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; From 80b38ed608e0784bef6c9c39990de38cca42c553 Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Mon, 9 Feb 2026 23:32:54 +0900 Subject: [PATCH 4/5] chore: Add comment --- src/ios/CDVWKInAppBrowser.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 8915487d7..7d3ccbba2 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -482,7 +482,7 @@ - (void)webView:(WKWebView *)theWebView decidePolicyForNavigationAction:(WKNavig [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. + // 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]]; From 56a079c24be444d4988b5352addaa43ffbd7447a Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Tue, 10 Feb 2026 11:53:09 +0900 Subject: [PATCH 5/5] fix(ios): fix build error --- src/ios/CDVWKInAppBrowser.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index 7d3ccbba2..6fa25a845 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -415,7 +415,7 @@ - (void)injectStyleFile:(CDVInvokedUrlCommand *)command - (BOOL)isAllowedScheme:(NSString *)scheme { - NSString *allowedSchemesPreference = [_settings cordovaSettingForKey:@"AllowedSchemes"]; + NSString *allowedSchemesPreference = [self.commandDelegate.settings cordovaSettingForKey:@"AllowedSchemes"]; if (allowedSchemesPreference == nil || [allowedSchemesPreference isEqualToString:@""]) { // Preference missing. return NO;