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

Allow plugins to hook into the WKURLSchemeHandler #1030

Merged
merged 6 commits into from
Jan 31, 2021

Conversation

NiklasMerz
Copy link
Member

@NiklasMerz NiklasMerz commented Nov 25, 2020

Platforms affected

iOS

Motivation and Context

This offers plugin authors the possibility to plug into the WKURLSchemeHandler. Some options where discussed on the list

I did this "hook" to implement a plugin which acts as a proxy to bypass CORS and cookie issues: https://github.com/GEDYSIntraWare/cordova-plugin-webview-proxy.

Supersedes #1004

Description

The implementation was based on the discussions on the list and similarly implemented like shouldOverrideLoadWithRequest. Thanks to @erisu for the hint in the right direction.

TODO

  • Testing with more than one plugin
  • Automated testing possible?
  • Documentation in cordova-docs?

Testing

Checklist

  • I've run the tests to see all new and existing tests pass
  • I added automated test coverage as appropriate for this change
  • Commit is prefixed with (platform) if this change only applies to one platform (e.g. (android))
  • If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct keyword to close issues using keywords)
  • I've updated the documentation if necessary

@NiklasMerz NiklasMerz changed the title Schemehandler hook Allow plugins to hook into the WKURLSchemeHandler Nov 25, 2020
@NiklasMerz NiklasMerz marked this pull request as ready for review November 25, 2020 15:22
@dpogue dpogue added this to the 6.1.2 milestone Nov 25, 2020
@Ichhakhemka

This comment has been minimized.

@jcesarmobile
Copy link
Member

Just noticed it was added to 6.1.2 milestone. I think this is a feature, so should be added as 6.2.0

@dpogue
Copy link
Member

dpogue commented Nov 27, 2020

Just noticed it was added to 6.1.2 milestone. I think this is a feature, so should be added as 6.2.0

👍
I've updated the 6.1.2 milestone to 6.next to better reflect "the next release in the 6.x version, before 7.0.0". The details of patch vs minor can be figured out when we're ready to tag (I agree this should probably be a minor bump).

CordovaLib/Classes/Public/CDVURLSchemeHandler.m Outdated Show resolved Hide resolved
BOOL anyPluginsResponded = NO;
BOOL handledRequest = NO;

for (NSString* pluginName in vc.pluginObjects) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This for loop pattern, which we also see in CDVWebViewEngine.m, occasionally causes crashes in our production apps because vc.pluginObjects can be mutated on a different thread while you're looping. Here's an example:

0   CoreFoundation                	0x1ac96a5ac __exceptionPreprocess + 220 (NSException.m:199)
1   libobjc.A.dylib               	0x1c0a5842c objc_exception_throw + 60 (objc-exception.mm:565)
2   CoreFoundation                	0x1ac969f04 __NSFastEnumerationMutationHandler + 124 (NSEnumerator.m:129)
3   Our App                	0x1022e13ec -[CDVWebViewEngine webView:decidePolicyForNavigationAction:decisionHandler:] + 336 (CDVWebViewEngine.m:543)

There are several right ways to deal with this:

  1. The best would be to use a protocol, like my similar request here: CDVWebViewEngine needs a way to set the websiteDataStore of its configuration #900
  2. You could alternatively use DispatchQueue to safely read and write pluginObjects on separate threads
  3. You could simply make a copy of pluginObjects and enumerate the copy instead

Since #3 is the easiest to write in a PR comment, that would look like the below, but I'd be happy to help you do #1, which would be a lot better.

    /*
     * Give plugins the chance to handle the url
     */
    __block BOOL anyPluginsResponded = NO;
    __block BOOL shouldAllowRequest = NO;
    
    NSDictionary *pluginObjects = [[vc pluginObjects] copy];
    SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
    [pluginObjects enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull plugin, BOOL * _Nonnull stop) {
        if ([plugin respondsToSelector:selector]) {
            anyPluginsResponded = YES;
            // https://issues.apache.org/jira/browse/CB-12497
            int navType = (int)navigationAction.navigationType;
            shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType));
            if (!shouldAllowRequest) {
                *stop = YES;
            }
        }
    }];

Copy link
Contributor

Choose a reason for hiding this comment

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

I updated #900 with an example of a protocol that you could use here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for your feedback on this. As I am not an ObjectiveC expert I am not sure how to deal with this. I would probably use "#3" for this PR and add a commen to #900 to properly implement the protocol for this and the other affected loop.

@adamdport

This comment has been minimized.

@NiklasMerz
Copy link
Member Author

@adamdport Thanks for you tests and comments. Most of your comments apply to the "proxy" I built in #1004. This PR is a generic way for plugins to intercept web requests. I built the first plugin to use this API from the proxy code of the previous PR here: https://github.com/GEDYSIntraWare/cordova-plugin-webview-proxy

So this PR has nothing to do with CORS, cookies etc. I just happen to use for these things by building a plugin that depends on this PR.

@adamdport
Copy link

@NiklasMerz my mistake. I'll open issues on that repo instead, thanks

@girish2711

This comment has been minimized.

@NiklasMerz
Copy link
Member Author

NiklasMerz commented Jan 18, 2021

@girish2711 Thanks for you feedback. Seems like you hit some edge cases with the cookie quirks in WebKit. We should move this discussion to the proxy plugin and not continue in this PR.

This PR just adds the ability for plugins to hook into the code in the platform. Again please don't discuss issues around the "proxy" here.

@codecov-io
Copy link

Codecov Report

Merging #1030 (6078b65) into master (7e3402c) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1030   +/-   ##
=======================================
  Coverage   74.82%   74.82%           
=======================================
  Files          13       13           
  Lines        1720     1720           
=======================================
  Hits         1287     1287           
  Misses        433      433           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7e3402c...b99d68d. Read the comment docs.

@NiklasMerz
Copy link
Member Author

If no one objects I will merge this in 24 hours.

@msmtamburro
Copy link
Contributor

Just a note: I was waiting for #1050 to be merged so that I could merge that in to #1051. #1051 has the example you could follow in this PR (but it might be hard to read until #1050 is merged).

@msmtamburro
Copy link
Contributor

Just a note: I was waiting for #1050 to be merged so that I could merge that in to #1051. #1051 has the example you could follow in this PR (but it might be hard to read until #1050 is merged).

This newly merged PR copied the crashing pattern that I've addressed in #1050 and #1051. You might want to open a new issue to follow the new pattern once those two PRs get merged.

@NiklasMerz
Copy link
Member Author

@msmtamburro Yes we should address that in the next version with #1050, #1051 and changes to this feature together. Feel free to open an issue.

@mattdsteele
Copy link

@NiklasMerz Thank you (and everyone else) for putting this together! I'm excited to put this to use.

It's not clear to me how this could be integrated into a Cordova app, for folks looking to solve the original issue of bypassing CORS errors in their app.

Is updating to cordova-ios@6.2.0 and loading https://github.com/GEDYSIntraWare/cordova-plugin-webview-proxy (following the "Testing" guide above) the near-term recommendation? Or is there another plugin folks should be tracking on?

@NiklasMerz
Copy link
Member Author

@mattdsteele As far as I know my plugin https://github.com/GEDYSIntraWare/cordova-plugin-webview-proxy is only one to use this feature so far. I built it to solve my CORS and cookie issues. Maybe others will use this hook to implementen different solutions.

Give the plugin a try and let me know in the plugins repo if it works for you or issues you find.

@tudordumitriu
Copy link

@NiklasMerz I have tried using the cordova ios 6.2.0 but unfortunately still getting the "error: Build input file cannot be found: '../mobile/platforms/ios/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewProcessPoolFactory.m' (in target 'CordovaLib' from project 'CordovaLib')" and if you check the code https://github.com/apache/cordova-ios/tree/master/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine the folder indeed doesn't seem to be containing CDVWebViewProcessPoolFactory.m
I did add the platform via cordova platform add ios@6.2.0

tudordumitriu referenced this pull request Feb 8, 2021
* set CDVWebViewProcessPoolFactory to public

* Move CDVWebViewProcessPoolFactory.m to Classes/Public
@NiklasMerz
Copy link
Member Author

@tudordumitriu This is not the right PR for this. Please open a new issue or contine in the commit.

@tudordumitriu
Copy link

@NiklasMerz
Sorry, will post it in the proper thread

@dpogue dpogue mentioned this pull request Aug 18, 2024
5 tasks
dpogue added a commit to dpogue/cordova-ios that referenced this pull request Aug 26, 2024
We don't rely on the protocol itself being implemented by the plugins
(we continue to check with `-respondsToSelector:`) but this allows us to
avoid `objc_msgSend` and provides a way to document some of this plugin
behaviour that is not otherwise explained.

This should also resolve the unsafe plugin iteration issue that was
mentioned in apacheGH-1272 and apacheGH-1030 by always iterating over an array of
plugin objects that is a copy (due to calling `-allValues`).
dpogue added a commit that referenced this pull request Aug 28, 2024
We don't rely on the protocol itself being implemented by the plugins
(we continue to check with `-respondsToSelector:`) but this allows us to
avoid `objc_msgSend` and provides a way to document some of this plugin
behaviour that is not otherwise explained.

This should also resolve the unsafe plugin iteration issue that was
mentioned in GH-1272 and GH-1030 by always iterating over an array of
plugin objects that is a copy (due to calling `-allValues`).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iOS 14 will enable Intelligent Tracking Prevention in WKWebView by default