-
Notifications
You must be signed in to change notification settings - Fork 24.9k
Blockable WebView #10772
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
Blockable WebView #10772
Conversation
|
By analyzing the blame information on this pull request, we identified @jacobp100 and @spicyj to be potential reviewers. |
|
I’m reviewing a similar PR at #10654. We’ll need to decide what direction this wants to go in, but I would suggest this looks a lot more flexible, and could incorporate the other PR (I think without additional work). |
|
Yeah, this PR seems achieve the same thing as mine. I was inspired by the same issue (#6478). The reason I didn't go for a regex-based solution though is because I was under the impression that there was a divergence in regex support between Java and Objective-C. I haven't looked into that in detail, so maybe someone else can comment on that? It would hinder consistency between platform though, and make the behaviour unpredictable. Other than that it seems pretty similar, and I wouldn't mind closing my PR in favor of this if the regex bit is a non-issue. |
|
I'm no regex expert, but from what I understand all patterns that work for JS should work for the other 2 languages. It seems that JS have less available patterns, but the ones that it does have should work seemingly on Java and Obj-C. So if the regex that is written is "JS friendly" it should work for Java and Obj-C. I have this code, using this library in production at the moment and the checks that we used, which I believe are the major people uses, are checking for some URL, checking for the absence of a word in a URL, checking protocols, and they all proved the same for all platforms. |
|
Do you have any source that compares the regular expressions in JS, Android, and iOS? If you’re sure that any JS regular expression will be valid for Android and iOS, it might be worth it in dev mode to check that we can construct RegExps from the strings. |
|
Nothing that neatly done. This statement of mine comes from the time I needed to find regex patterns, specially for the lookahead ones, and saw some of them existing in iOS and Java, but not in JavaScript. |
|
I did notice a few minor inconsistencies ( Alternatively, could it be made simpler by not using RegExps and using simple wildcards instead (like those in Windows Explorer)? |
|
The wildcards wouldn't work for some more complex use cases, where blacklisting and whitelisting can me mixed. |
|
Could that be fixed by adding a |
|
I did that in the first version, but I found that it gets much more complicated then a generic regex. |
|
@jacobp100 should I fix the ci errors then so we can merge this, or we need further approval? |
|
I can’t merge it myself, but it can’t be merged with CI errors (if they’re caused by this). |
|
Does the blocking work on Android for the initially set source URL? |
|
The initial source is never blocked, not in Android nor in iOS, actually. The main goal is to block navigations made from the webview, not the react code. Since that is something we have control already I supposed we shouldn't block that. |
|
I'm not sure that's right @rseemann. For RN at least, it seems to be that |
|
That is correct. What I meant is that the way the code was implemented was done in a way that neither iOS nor Android blocks the initial loading. That was done due to the fact that we understand that the setting of the source can be controlled from the react code already, while this policies would be used to block the navigation from the webview. |
|
Hm, but testing with your example in |
|
Is that so? I'll take a look at it then. It shouldn't. |
|
@cbrevik what url are you testing? I tested Facebook's URL and what occur is that facebook navigates - via javascript - to another URL pretty much immediately after the page is loaded, so this is not the initial load. If a URL of a website that does not navigate forward is used, the page loads as expected. Do you have the same behaviour? |
|
@rseemann For some reason when I tested earlier today it consistently blocked the URL when I went into the WebView-example. But I cannot seem to reproduce that behavior now. I am probably mistaken, sorry for the false alarm! |
| if (url.startsWith("http://") || url.startsWith("https://") || | ||
| url.startsWith("file://")) { | ||
| return false; | ||
| boolean shouldBlock = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking over this again, I don't think this will solve the issue my PR is attempting to solve. The problem there (#10055) is with custom URL schemes. If you can only block http/https/file requests it will not be sufficient. Same thing with the iOS implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is true. We could use an even broader approach where the policies would include the schemes as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What’s the verdict on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say let's push this version as is and we can come up with a more general solution later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? It's just about not making the check specific to certain URL schemes? Since there's an open issue that can be fixed by this PR, I don't see the point of not doing it.
|
@jacobp100 I have no idea why the tests aren't passing. Can you give me a hand here? |
|
When did you last merge master in? |
|
You mean rebase master into my branch? for some days now. I guess I should do it. |
|
You could try pinging someone on the Facebook team to see if they know what’s up. Has it been failing in the same way for a few days now? |
jacobp100
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
@jacobp100 do you think we're moving forward with this? Are we missing something? |
|
I’m not able to merge pull requests. Try cc’ing one of the FB team. |
| ); | ||
| } | ||
|
|
||
| customLoginRender() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be called renderCustomLogin, or ideally should be a small component.
| */ | ||
| navigationBlockingPolicies: PropTypes.arrayOf(PropTypes.shape({ | ||
| currentURL: PropTypes.string, | ||
| url: PropTypes.string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add docs. What is currentURL. What is url?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems from the blocking logic that currentURL is the "source" URL, i.e. the URL we're going from. Whilst url is the "request" URL, i.e. the URL we're going to. Maybe the naming should reflect that a bit better.
Also both of them are matched as regex, so the docs should reflect that.
| } | ||
|
|
||
| onNavigationBlocked = (event) => { | ||
| var {onNavigationBlocked} = this.props; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use const
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it isn't used anywhere else in the class, are you sure?
| navigationBlockingPolicies: PropTypes.arrayOf(PropTypes.shape({ | ||
| currentURL: PropTypes.string, | ||
| navigationType: PropTypes.string, | ||
| url: PropTypes.string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there navigationType only on iOS? What are the possible values? Add docs please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like navigationType is only used in the iOS RCTWebView.h. That field isn't available in ReactWebViewManager on Android.
Though if we need navigationType as a policy at all (for blocking purposes) is unclear to me.
|
|
||
| BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme]; | ||
| BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; | ||
| BOOL isLoadingSourceURL = ([sourceURL isEqualToString:requestURL] && !_webView.isLoading); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if sourceURL is nil?
| }); | ||
|
|
||
| NSString *sourceURL = (NSString *)self.source[@"uri"]; | ||
| NSString *requestURL = (request.URL).absoluteString; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can request be nil?
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This large block of code that deal with blocking should be extracted to a private method for readability, not inlined into shouldStartLoadWithRequest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seemed to be the way it is done in this class, and similar ones. E.g.: take a look at the webViewDidFinishLoad implementation. I agree with you about readability, just want to make sure this is the way to go.
|
|
||
| // unverifiable policy rule | ||
| if (!eventValue) { | ||
| RCTLogWarn(@"Could not verify webview loading policy named %@. Failing policy with rules %@", key, policy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebView is spelled like this.
| BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; | ||
| BOOL isLoadingSourceURL = ([sourceURL isEqualToString:requestURL] && !_webView.isLoading); | ||
|
|
||
| // skip this for the JS Navigation handler, initial load, iFrames |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment should explain why skip blocking in these cases.
Comments start with a capital letter.
|
|
||
| NSMutableDictionary<NSString *, id> *event = [self baseEvent]; | ||
| [event addEntriesFromDictionary: @{ | ||
| NSMutableDictionary<NSString *, id> *_event = [self baseEvent]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change this to _event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because there is an event variable declared on line 215 and I didn't want to reuse it.
| FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); | ||
| } | ||
| return true; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, all the blocking handling code should be extracted to a private method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same explanation as before. If we think this is the way to go, sure, it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please extract to a protected method instead, since that is a bit more extensible.
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
| view.getContext().startActivity(intent); | ||
| } catch (ActivityNotFoundException e) { | ||
| FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sentences start with a capital letter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This came with the rebase, but sure, I changed it.
| navigationBlockingPolicies = null; | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove extra empty line
| } | ||
|
|
||
| navigationBlockingPolicies = new ReadableMap[policies.size()]; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove extra empty line
| for (int i = 0; i < policies.size(); i++) { | ||
| navigationBlockingPolicies[i] = policies.getMap(i); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation is 2 spaces in the Java codebase.
|
Sorry about the delay. Understand the intention now. The code needs quite a lot of changes. |
|
@rseemann Do you need any help with this PR? I've found more use cases for it on my end (in different projects), so I'd love to have this merged before the next January release. |
|
Any progress? Seems like this PR needs some changes before it can be reviewed again. |
|
I'll work on this very soon, but any help is appreciated :) |
|
Any news on this PR ? :) |
|
This PR appears to be abandoned. Closing for now. |
Motivation
There isn't yet a fully reliable way to block, and handle, navigation to a specific url while using the WebViews. The current iOS method is at best (as mentioned here #6478 by @astreet) an unpredictable way of handling this. Android itself has no way of doing it at all.
Test Plan
A usage example was built in the UIExplorer WebView section. The controlled webview renders the react-native GitHub and blocks any navigations that would take the user to a non github.com urls or /login within github.com. It handles both blockage differently, showing a different component when the login url is identified or taking the users to the browser on the non github case.