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

feat: add WebView "blockedURLs" property #11842

Merged
merged 3 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@

@Kroll.proxy(creatableInModule = UIModule.class,
propertyAccessors = {
TiC.PROPERTY_BLACKLISTED_URLS,
TiC.PROPERTY_BLACKLISTED_URLS, // DEPRECATED: Superseded by PROPERTY_BLOCKED_URLS.
TiC.PROPERTY_BLOCKED_URLS,
TiC.PROPERTY_DATA,
TiC.PROPERTY_ON_CREATE_WINDOW,
TiC.PROPERTY_SCALES_PAGE_TO_FIT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,35 @@ public boolean shouldOverrideUrlLoading(final WebView view, String url)
}
}
}
if (proxy.hasProperty(TiC.PROPERTY_BLACKLISTED_URLS)) {
String[] blacklistedSites =
TiConvert.toStringArray((Object[]) proxy.getProperty(TiC.PROPERTY_BLACKLISTED_URLS));
for (String site : blacklistedSites) {

// Do not load the URL if it's on the proxy's black-list.
// DEPRECATED: Superseded by PROPERTY_BLOCKED_URLS down below.
Object value = proxy.getProperty(TiC.PROPERTY_BLACKLISTED_URLS);
if ((value != null) && value.getClass().isArray()) {
String message
= "Property '" + TiC.PROPERTY_BLACKLISTED_URLS + "' is deprecated. Use '"
+ TiC.PROPERTY_BLOCKED_URLS + "' instead.";
Log.w(TAG, message);
for (String site : TiConvert.toStringArray((Object[]) value)) {
if (url.equalsIgnoreCase(site) || (url.indexOf(site) > -1)) {
KrollDict data = new KrollDict();
data.put("url", url);
data.put("message", "Webview did not load blacklisted url.");
proxy.fireEvent(TiC.PROPERTY_BLACKLIST_URL, data);
proxy.fireEvent(TiC.EVENT_BLACKLIST_URL, data);
return true;
}
}
}

// Do not load the URL if it's on the proxy's block-list.
value = proxy.getProperty(TiC.PROPERTY_BLOCKED_URLS);
if ((value != null) && value.getClass().isArray()) {
for (String site : TiConvert.toStringArray((Object[]) value)) {
if (url.equalsIgnoreCase(site) || (url.indexOf(site) > -1)) {
KrollDict data = new KrollDict();
data.put("url", url);
data.put("message", "Webview did not load blocked url.");
proxy.fireEvent(TiC.EVENT_BLOCKED_URL, data);
return true;
}
}
Expand Down
14 changes: 12 additions & 2 deletions android/titanium/src/java/org/appcelerator/titanium/TiC.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ public class TiC
*/
public static final String EVENT_BATTERY = "battery";

/**
* @module.api
*/
public static final String EVENT_BLACKLIST_URL = "blacklisturl";

/**
* @module.api
*/
public static final String EVENT_BLOCKED_URL = "blockedurl";

/**
* @module.api
*/
Expand Down Expand Up @@ -1110,12 +1120,12 @@ public class TiC
/**
* @module.api
*/
public static final String PROPERTY_BLACKLIST_URL = "blacklisturl";
public static final String PROPERTY_BLACKLISTED_URLS = "blacklistedURLs";

/**
* @module.api
*/
public static final String PROPERTY_BLACKLISTED_URLS = "blacklistedURLs";
public static final String PROPERTY_BLOCKED_URLS = "blockedURLs";

/**
* @module.api
Expand Down
30 changes: 30 additions & 0 deletions apidoc/Titanium/UI/WebView.yml
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,21 @@ events:
- name: url
summary: The URL of the web document that is stopped.
type: String
deprecated:
since: "9.2.0"
notes: Use the `blockedurl` event instead.

- name: blockedurl
summary: Fired when a URL has been blocked from loading.
description: |
This event is fired when the end-user attempts to navigate to a website matching a URL in
the <Titanium.UI.WebView.blockedURLs> property.
platforms: [android, iphone, ipad]
since: {android: "9.2.0", iphone: "9.2.0", ipad: "9.2.0"}
properties:
- name: url
summary: The URL of the web document that has been blocked from loading.
type: String

- name: message
summary: Fired when a script message is received from a webpage.
Expand Down Expand Up @@ -650,6 +665,21 @@ properties:
since: {android: "5.4.0", iphone: "6.1.0", ipad: "6.1.0"}
platforms: [android, iphone, ipad]
availability: creation
deprecated:
since: "9.2.0"
notes: Use the <Titanium.UI.WebView.blockedURLs> property instead.

- name: blockedURLs
summary: |
An array of url strings to be blocked.
description: |
An array of url strings to be blocked from loading. This will stop the webview from going to urls
listed in this array. Note that this only applies to the links tapped on by the end-user.
The first website that is loaded will not be stopped, even if it is listed in the blocklist.
type: Array<String>
since: {android: "9.2.0", iphone: "9.2.0", ipad: "9.2.0"}
platforms: [android, iphone, ipad]
availability: creation

- name: data
summary: Web content to load.
Expand Down
1 change: 1 addition & 0 deletions iphone/Classes/TiUIWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

BOOL _willHandleTouches;
NSArray<NSString *> *_blacklistedURLs;
NSArray<NSString *> *_blockedURLs;
NSURL *_currentURL;
UIActivityIndicatorView *_loadingIndicator;
BOOL _isViewDetached;
Expand Down
44 changes: 36 additions & 8 deletions iphone/Classes/TiUIWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -245,15 +245,26 @@ - (void)setData_:(id)value

- (void)setBlacklistedURLs_:(id)blacklistedURLs
{
ENSURE_TYPE(blacklistedURLs, NSArray);
DEPRECATED_REPLACED(@"UI.WebView.blacklistedURLs", @"9.2.0", @"UI.WebView.blockedURLs");

for (id blacklistedURL in blacklistedURLs) {
ENSURE_TYPE(blacklistedURL, NSString);
ENSURE_TYPE(blacklistedURLs, NSArray);
for (id nextURL in blacklistedURLs) {
ENSURE_TYPE(nextURL, NSString);
}

_blacklistedURLs = blacklistedURLs;
Comment on lines -250 to 255
Copy link
Contributor

Choose a reason for hiding this comment

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

Call [self setBlockedURLs_: blacklistedURLs] from here.

}

- (void)setBlockedURLs_:(id)blockedURLs
{
ENSURE_TYPE(blockedURLs, NSArray);
for (id nextURL in blockedURLs) {
ENSURE_TYPE(nextURL, NSString);
}

_blockedURLs = blockedURLs;
}

- (void)setHtml_:(id)args
{
ignoreNextRequest = YES;
Expand Down Expand Up @@ -1010,20 +1021,37 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WK
}
NSArray<NSString *> *allowedURLSchemes = [[self proxy] valueForKey:@"allowedURLSchemes"];

// Handle blacklisted URL's
// Do not load the URL if it's black-listed.
// DEPRECATED: Should use "blockedURLs" property with a "blockedurl" event listener instead.
NSString *urlCandidate = navigationAction.request.URL.absoluteString;
if (_blacklistedURLs != nil && _blacklistedURLs.count > 0) {
NSString *urlCandidate = navigationAction.request.URL.absoluteString;

for (NSString *blackListedURL in _blacklistedURLs) {
if ([urlCandidate rangeOfString:blackListedURL options:NSCaseInsensitiveSearch].location != NSNotFound) {
for (NSString *blockedURL in _blacklistedURLs) {
if ([urlCandidate rangeOfString:blockedURL options:NSCaseInsensitiveSearch].location != NSNotFound) {
if ([[self proxy] _hasListeners:@"blacklisturl"]) {
[[self proxy] fireEvent:@"blacklisturl"
withObject:@{
@"url" : urlCandidate,
@"message" : @"Webview did not load blacklisted url."
}];
}
decisionHandler(WKNavigationActionPolicyCancel);
[self _cleanupLoadingIndicator];
return;
}
}
}

// Do not load the URL if it's on the block-list.
if (_blockedURLs != nil && _blockedURLs.count > 0) {
for (NSString *blockedURL in _blockedURLs) {
if ([urlCandidate rangeOfString:blockedURL options:NSCaseInsensitiveSearch].location != NSNotFound) {
if ([[self proxy] _hasListeners:@"blockedurl"]) {
[[self proxy] fireEvent:@"blockedurl"
withObject:@{
@"url" : urlCandidate,
@"message" : @"Webview did not load blocked url."
}];
}
decisionHandler(WKNavigationActionPolicyCancel);
[self _cleanupLoadingIndicator];
return;
Comment on lines -1013 to 1057
Copy link
Contributor

Choose a reason for hiding this comment

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

I would be better to merge 'backlisteurl' event related code snippet inside 'blockedurl' . So above code snippet can be replaced by following-
`
// Do not load the URL if it's on the block-list.
if (_blockedURLs != nil && _blockedURLs.count > 0) {
NSString *urlCandidate = navigationAction.request.URL.absoluteString;
for (NSString *blockedURL in _blockedURLs) {
if ([urlCandidate rangeOfString:blockedURL options:NSCaseInsensitiveSearch].location != NSNotFound) {
if ([[self proxy] _hasListeners:@"blacklisturl"]) {
DEPRECATED_REPLACED(@"UI.WebView.blacklisturl", @"9.2.0", @"UI.WebView.blockedurl");

      [[self proxy] fireEvent:@"blacklisturl"
                   withObject:@{
                     @"url" : urlCandidate,
                     @"message" : @"Webview did not load blacklisted url."
                   }];
    }
    if ([[self proxy] _hasListeners:@"blockedurl"]) {
      [[self proxy] fireEvent:@"blockedurl"
                   withObject:@{
                     @"url" : urlCandidate,
                     @"message" : @"Webview did not load blocked url."
                   }];
    }
    decisionHandler(WKNavigationActionPolicyCancel);
    [self _cleanupLoadingIndicator];
    return;`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I separated the 2 on purpose so that we can easily delete the "blacklisted" code in the future.
(Otherwise I wouldn't normally duplicate code like this.)

So, the way it works now is a "blacklisturl" event will only be fired for matches in the blacklistedURLs property... and a "blockedurl" event will only be fired for matches in the blockedURLs property. I purposely treat them separately on both Android and iOS.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for explaining. Approved!

Expand Down
42 changes: 42 additions & 0 deletions tests/Resources/ti.ui.webview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ describe('Titanium.UI.WebView', function () {
win.open();
});

// DEPRECATED: Since Titanium 9.2.0
it.ios('blacklisturl', function (finish) {
win = Ti.UI.createWindow();
const webView = Ti.UI.createWebView({
Expand All @@ -501,6 +502,47 @@ describe('Titanium.UI.WebView', function () {
win.open();
});

it.ios('blockedurl', function (finish) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could this be ran on Android too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately no. On Android, we can only "natively" block the links the end-user tapped on. We can't block the URLs that are loaded programmatically. This is something we document.

We can natively detect the URL being navigated to after the HTTP request has been sent by the WebView on Android, but then it's already too late. We may not be able to abort it in time. Especially if a cached HTTP response is received.

win = Ti.UI.createWindow();
const webView = Ti.UI.createWebView({
url: 'https://google.com',
blockedURLs: [ 'https://google.com' ]
});

webView.addEventListener('blockedurl', function () {
finish();
});
win.add(webView);
win.open();
});

// DEPRECATED: Since Titanium 9.2.0
it('blacklistedURLs', (finish) => {
win = Ti.UI.createWindow();
const webView = Ti.UI.createWebView({
url: 'https://www.axway.com',
blacklistedURLs: [ 'www.apple.com', 'www.google.com' ]
});
webView.addEventListener('load', () => {
finish();
});
win.add(webView);
win.open();
});

it('blockedURLs', (finish) => {
win = Ti.UI.createWindow();
const webView = Ti.UI.createWebView({
url: 'https://www.axway.com',
blockedURLs: [ 'www.apple.com', 'www.google.com' ]
});
webView.addEventListener('load', () => {
finish();
});
win.add(webView);
win.open();
});

it.ios('basicAuthentication', function (finish) {
const url = 'https://httpbin.org/basic-auth/user/password';

Expand Down