Skip to content

fix(ios) issue #349 - navigate to cdvfile with wkwebview #358

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

Conversation

Lindsay-Needs-Sleep
Copy link

@Lindsay-Needs-Sleep Lindsay-Needs-Sleep commented Nov 7, 2019

Platforms affected

iOS

Motivation and Context

Fixes #349
This issue is only present when using the wkwebview engine, but since that is the future of iOS this should be addressed. The wkWebview does not allow unknown schemes (eg. cdvfile://).

Description

These changes intercept navigation requests and check if the url is a cdvfile:// url. If it is, it stops the request and creates a new request for the equivalent file:// url.

Alternative solution:
Use a similar workaround to what is proposed by @guylando in pull request #296. This will get us past the unknown scheme, but I'm not sure how to get it to return the correct file from there. (Other than loading a request for the equivalent file:// url).

Future (better) solution:
When the lowest cordova supported iOS version is 11.0 this work-around can potentially be removed. WKWebview for iOS 11+ supports "WKURLSchemeHandler" and "setURLSchemeHandler" which should allow actual support for the cdvfile scheme.

Advice requested:
This is my first foray into objc and the ios side of cordova-plugin-file.

  • I'm not sure if my method nativeUrlFromCdvfileUrl is the most efficient way to get the equivalent file:// url, please advise.
  • This addition basically allows you to do window.location.href = 'cdvfile://...'; but the window.location.href after the page load will actually be file://..., can anyone see any issues that this could cause? (Especially issues that could be worse than not being able to navigate to cdvfile:// url at all?)

Testing

Tested on ios 9.3.5 and 10.3.4 devices. Tested with and without wkwebview.
Ran npm test passed.
Ran the automatic tests. No change from before and after the change. (One test failed before and after the additions (on andriod and ios), reported in issue #357)

Checklist

  • I've run the tests to see all new and existing tests pass
  • I added automated (manual) 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

…bview.

WKWebView does not allow unknown url schemes so we have to intercept the request, stop it, and send out a new request for the equivalent file:// url.
Note: When the lowest cordova supported iOS version is 11.0 this work-around can potentially be removed.  WKWebview for iOS 11+ supports "WKURLSchemeHandler" and "setURLSchemeHandler" which should allow actual support for the cdvfile scheme.
@tomaz-dugii
Copy link

Hi, I am trying to use your fix for problems with cdvfile on ios. But I can't figure it out how this works.
I'm trying to load .png image from phone app storage (Documents folder) with native URL (.toInternalURL()) cdvfile://url and get this error:
[Error] Failed to load resource: unsupported URL (cdvfile://localhost/persistent/image.png, line 0)
When using .toURL() for file:// link I receive this error:
Failed to load resource: The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 1.)
url like this :
[Log] file:///var/mobile/Containers/Data/Application/7ECD0022-9EA9-4BA5-8D45-0ECCD417625C/Documents/image.png

@Lindsay-Needs-Sleep
Copy link
Author

Lindsay-Needs-Sleep commented Jun 12, 2020

@tomaz-dugii
Gee, it's been a long time since I have worked with this stuff, but let me see....


Just to be clear, these changes are only relevant if you are trying to NAVIGATE to a cdvfile:// url from an https:// page

If you are trying to display an image with a cdvfile:// url on an https:// page this may also help (but it's been long and I can't remember for sure).

(If your situation is one of the above cases, I might be able to help.)


If you are just having trouble figuring out what the correct url is for file you should look here:
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#file-system-layouts

For your image in the documents folder, it should be something like:
cordova.file.documentsDirectory + '/image.png';

(personally, I just use the cordova.file.dataDirectory if you don't need to share image.png with the users)

Otherwise I would recommend reading through the quirks section:
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#ios-quirks
And maybe here:
https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#configuring-the-plugin-optional
(if you haven't already)

@tomaz-dugii
Copy link

Hi, thanks for reply. I did a workaround for this issue.
So basically I used Swift, got the file, read file and convert to base64 string and send it to Cordova and just user in <img src="data:image/png;base64, 'file content'> in HTML and it worked.

@breautek
Copy link
Contributor

This addition basically allows you to do window.location.href = 'cdvfile://...'; but the window.location.href after the page load will actually be file://..., can anyone see any issues that this could cause? (Especially issues that could be worse than not being able to navigate to cdvfile:// url at all?)

I think this may break location.reload() or page refreshes maybe?

@Lindsay-Needs-Sleep
Copy link
Author

Since you would be on a file:// url, the location.reload() should just reload the file:// url which 99% should be fine. (But I am the OP, so of course I wouldn’t see any issues. :p)
(I have been using it in our app since Nov, and no problems.)

But, since cordova-ios has a minimum ios of 11 (I believe?), and with iOS 11+ there is WKURLSchemeHandler, which would probably be a better solution.

@breautek
Copy link
Contributor

To be clear, I didn't test (i dont even have mac hardware to test with). It was just speculation.

I just have someone on slack who is on cordova-ios@6, using the schemes and is stating he has an issue using cdvfile urls on img nodes. It produces the same behavior as this pr is trying to fix... so it sounds like the same issue.

@Lindsay-Needs-Sleep
Copy link
Author

Ah okay, interesting.
I had/have high hopes for WKURLSchemeHandler.
You could possibly tell that person to look at cordova-plugin-ionic-webview 4.x because I believe it uses the WKURLSchemeHandler.

I have only used the 2.x version of cordova-plugin-ionic-webview (which hosts a local server), so I am able go from https to the local server no problem.
But an interesting test would be if you can go from https to local content via cordova-plugin-ionic-webview 4.x.

@Lindsay-Needs-Sleep
Copy link
Author

Lindsay-Needs-Sleep commented Nov 5, 2020

Since iOS 11+ is the minimum now this isn't necessary.

If you enable the new scheme preference from cordova-ios@6.x.x you are able to go from https://... to app://localhost/.... (or whatever your scheme+hostname is.) Instead of going to cdvfile://.

<preference name="scheme" value="app" />
<preference name="hostname" value="localhost" />

Note:

  • This even works for navigating from https:// to a file in the persistent dataDirectory (aka. stuff not in www)

@LonestarX91
Copy link

LonestarX91 commented Dec 22, 2020

@Lindsay-Needs-Sleep desperately need your help please :/
cordova ios 6.1.1 / 10.0.0, ios 14.3
[blocked] The page at [mysite] was not allowed to run insecure content from dani://localhost/bundle/www/cordova.js. when trying to load my local cordova from the remote web page.
CORS =
Plugins:

cordova-plugin-dialogs 2.0.2 "Notification"
cordova-plugin-file 6.0.2 "File"
cordova-plugin-globalization 1.11.0 "Globalization"
cordova-plugin-iroot 2.0.2 "iRoot"
cordova-plugin-optionsmenu 1.0.23 "OptionsMenu"
cordova-plugin-whitelist 1.3.4 "Whitelist"
cordova-plugin-wkwebview-engine 1.2.1 "Cordova WKWebView Engine"
cordova-plugin-wkwebview-file-xhr 3.0.0 "Cordova WKWebView File XHR Plugin"

tried with both cdvfile:// scheme as well as the custom one you see above
WKURLSchemeHandler's startURLSchemeTask is not even called

Code for loading the js:

url = "https://localhost/bundle/www/cordova.js";
var el = document.createElement('script');
el.id = "123";
el.onerror = function () {
	if (el.onerror) show_error("Cordova js not found")
}
el.onload = function () {}
el.src = url;
document.body.appendChild(el);

(excuse the indentation)

Any thoughts, ideas, prayers ?

thanks

@Lindsay-Needs-Sleep
Copy link
Author

@LonestarX91
My first thought is that since you are on cordova-ios 6.1.1 you shouldn’t include cordova-plugin-wkwebview-engine anymore (since it is included by default in cordova-ios 6.x.x).

The next thing is you should make sure you have your scheme <preference’s set correctly. (See my comment in this thread just above your post).

If that doesn’t work, you can post your config.xml.

(I use cordova-plugin-hostedwebapp to inject the cordova libraries onto our webpage. I was including them by a script tag at one point long ago, can’t remember if I ever fully got it working or not. I think it should be possible....)

@LonestarX91
Copy link

LonestarX91 commented Dec 23, 2020

@Lindsay-Needs-Sleep thanks a lot for the quick input !
So I did the following: removed wkwebview engine plugin, set my pref scheme to

<preference name="scheme" value="com.mycomp.myapp" />
    <preference name="hostname" value="localhost" />

set the CSP to

<meta http-equiv="Content-Security-Policy" content="default-src * com.mycomp.myapp: gap://ready file: cdvfile: data:; script-src * 'self' com.mycomp.myapp: file: cdvfile: 'unsafe-inline' 'unsafe-eval'">

and i still get

[Warning] [blocked] The page at https://mywebsite.com/page.html?platform=ios was not allowed to run insecure content from com.mycomp.myapp://localhost/bundle/www/cordova.js.

See config.xml below

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.mycomp.myapp" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <feature name="CDVWebViewEngine">
        <param name="ios-package" value="CDVWebViewEngine" />
    </feature>
    <feature name="LaunchScreen">
        <param name="ios-package" value="CDVLaunchScreen" />
    </feature>
    <feature name="LocalStorage">
        <param name="ios-package" value="CDVLocalStorage" />
    </feature>
    <feature name="Console">
        <param name="ios-package" value="CDVLogger" />
        <param name="onload" value="true" />
    </feature>
    <feature name="HandleOpenUrl">
        <param name="ios-package" value="CDVHandleOpenURL" />
        <param name="onload" value="true" />
    </feature>
    <feature name="IntentAndNavigationFilter">
        <param name="ios-package" value="CDVIntentAndNavigationFilter" />
        <param name="onload" value="true" />
    </feature>
    <feature name="GestureHandler">
        <param name="ios-package" value="CDVGestureHandler" />
        <param name="onload" value="true" />
    </feature>
    <access origin="cdvfile:*" />
    <allow-navigation href="cdvfile:*" />
    <allow-intent href="cdvfile:*" />
    <feature name="Notification">
        <param name="ios-package" value="CDVNotification" />
    </feature>
    <feature name="File">
        <param name="ios-package" value="CDVFile" />
        <param name="onload" value="true" />
    </feature>
    <feature name="Globalization">
        <param name="ios-package" value="CDVGlobalization" />
    </feature>
    <feature name="IRoot">
        <param name="ios-package" value="IRoot" />
        <param name="onload" value="true" />
    </feature>
    <name>test</name>
    <description>
        test
    </description>
    <author email="dev@cordova.apache.org" href="http://cordova.io">
        test
    </author>
    <content src="https://myweb.com/page.html?platform=ios" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="com.mycomp.myapp://*/*" />

    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <allow-intent href="itms:*" />
    <allow-intent href="itms-apps:*" />
    <preference name="scheme" value="com.mycomp.myapp" />
    <preference name="hostname" value="localhost" />
    <preference name="AllowInlineMediaPlayback" value="false" />
    <preference name="BackupWebStorage" value="cloud" />
    <preference name="DisallowOverscroll" value="false" />
    <preference name="EnableViewportScale" value="false" />
    <preference name="KeyboardDisplayRequiresUserAction" value="true" />
    <preference name="MediaTypesRequiringUserActionForPlayback" value="none" />
    <preference name="SuppressesIncrementalRendering" value="false" />
    <preference name="SuppressesLongPressGesture" value="false" />
    <preference name="Suppresses3DTouchGesture" value="false" />
    <preference name="GapBetweenPages" value="0" />
    <preference name="PageLength" value="0" />
    <preference name="PaginationBreakingMode" value="page" />
    <preference name="PaginationMode" value="unpaginated" />
    <preference name="OverrideUserAgent" value="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1" />
    <preference name="ErrorUrl" value="error_page.html" />
</widget>

I guess I'm gonna look into cordova-plugin-hostedwebapp for the moment, if you have any ideas why it's not working for me, please let me know

Thanks

LE: big oof...

Plugin doesn't support this project's cordova-ios version. cordova-ios: 6.1.1, failed version requirement: <=4.1.9
Skipping 'cordova-plugin-hostedwebapp' for ios

@imselvakumar
Copy link

@LonestarX91 Have you tired using this ionic plugin "cordova-plugin-ionic-webview". I've modified the url to match plugin recommendation.

url = "ionic://localhost/cordova.js";
var el = document.createElement('script');
el.id = "123";
el.onerror = function () {
if (el.onerror) show_error("Cordova js not found")
}
el.onload = function () {}
el.src = url;
document.body.appendChild(el);

Can you give try once?

@LonestarX91
Copy link

@LonestarX91 Have you tired using this ionic plugin "cordova-plugin-ionic-webview". I've modified the url to match plugin recommendation.

url = "ionic://localhost/cordova.js";
var el = document.createElement('script');
el.id = "123";
el.onerror = function () {
if (el.onerror) show_error("Cordova js not found")
}
el.onload = function () {}
el.src = url;
document.body.appendChild(el);

Can you give try once?

changed everything in prefs (scheme), allow-intent, etc to ionic, changed CSP on the server to

<meta http-equiv="Content-Security-Policy" content="
                           default-src * ionic: cdvfile: cdvfile://* data: blob: gap://ready file://*;
                           style-src * 'unsafe-inline'; 
                           script-src * ionic: ionic://* file://* cdvfile: cdvfile://* cdvfile://localhost/* 'unsafe-inline' 'unsafe-eval';">

changed my loading code to

var url = "ionic://localhost/" + localFolder + "/www/cordova.js";
                    var el = document.createElement('script');
                    el.id = "cordova";
                    el.type = "text/javascript";
                    el.onerror = function () {
                        console.error('error loading cordova script')
                    }
                    el.onload = function () {
                    }
                    el.src = url;
                    document.body.appendChild(el);

aaaaand i get....
Refused to load ionic://localhost/bundle/www/cordova.js because it does not appear in the script-src directive of the Content Security Policy.

@LonestarX91
Copy link

it seems that no matter what I try (tried ios 5.1.1 with xhr (+ fix) plugin etc) CSP always bites me in the ass...

Refused to load cdvfile://localhost/bundle/www/cordova.js because it does not appear in the script-src directive of the Content Security Policy.

Any more ideas please ?
Thanks

@LonestarX91
Copy link

i'm beginning to think i am the only person in the world trying to load local cordova.js from remote, inside a wkwebview...

@breautek
Copy link
Contributor

breautek commented Jan 5, 2021

i'm beginning to think i am the only person in the world trying to load local cordova.js from remote, inside a wkwebview...

Exposing cordova.js to a remote source is a security vulnerability. It opens the door for someone to load that remote source and allows malicious actors to exploit your app by giving them the ability to remotely call native APIs on behalf of your app. Do not do this. This would also be indirectly against iOS app store terms of service because you're exposing native APIs to developers who may not have signed the Apple developer agreement.

I'm not sure what you're trying to accomplish but it sounds like you may be tackling your problem in the wrong way.

@Lindsay-Needs-Sleep
Copy link
Author

@LonestarX91
Sorry, I didn’t get any notifications.

It looks like you are doing everything right that I can think of.
I would suggest going with cordova-plugin-hostedwebapp.

The error you received when trying to use it is easy to fix (it is just modifying 1 line in plugin.xml) You can see how to do it from any of the many forks of that plugin. (in github, you can go: insights > network to see the most recently active forks).

Make sure you set up rules to only inject cordova onto your website to decrease the security vulnerability that breautek mentions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(ios) Can't navigate to cdvfile:// with wkwebview
5 participants