Commit b2cd9bdf authored by Takashi Toyoshima's avatar Takashi Toyoshima Committed by Commit Bot

OOR-CORS: Update cors-and-webview-api.md for asset/res file URLs

This change removes the last TODO in the cors-and-webview-api.md.
file:///android_asset/ and file:///android_res/ are almost same
with other usual file:// URLs, but still can be accessible even
if AllowFileAccess is set to false.

Test cases mentioned in the doc are also added to ensure legacy
and current CORS behaviors.

TEST=./out/a/bin/run_webview_instrumentation_test_apk -A Feature=CORS

Bug: 1035366
Change-Id: I5bdb7c54e261136d099944ed3690e6892fe72c19
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2253262
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Reviewed-by: default avatarNate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781138}
parent 31da2799
......@@ -58,7 +58,18 @@ Pages loaded with any scheme other than `content://` can't load `content://`
page in iframes and they can not specify `content://` URLs for sub-resources.
### file:///android\_{asset,res}/ URLs
TODO
Android assets and resources are accessible using `file:///android_asset/` and
`file:///android_res/` URLs. WebView handles these special `file://` URLs as it
does other `file://` URLs. Only difference is these special paths are accessible
even if `setAllowFileAccess` is called with `false`. Even so, still CORS-enabled
requests are not permitted until these are explicitly permitted by
`setAllowFileAccessFromFileURLs`.
*** note
**Note:** `file:///android_asset,res}/` URLs are discouraged. Apps are
encouraged to use [WebViewAssetLoader](https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader)
instead, for better compatibility with the Same-Origin policy.
***
## WebView APIs
......@@ -82,6 +93,10 @@ does not allow to access `content://` and `file://` URLs.
The requests from service workers also don't care for this setting.
*** note
**Note:** `setAllowFileAccessFromFileURLs` is deprecated in API level 30.
***
### setAllowUniversalAccessFromFileURLs
When this API is called with `true`, URLs starting with file:// will have a
scheme based origin, and can access other scheme based URLs over XMLHttpRequest.
......@@ -95,6 +110,10 @@ does not allow to access `content://` and `file://` URLs.
The requests from service workers also don't care for this setting.
*** note
**Note:** `setAllowUniversalAccessFromFileURLs` is deprecated in API level 30.
***
### shouldInterceptRequest
Custom scheme should not be permitted for CORS-enabled requests usually.
However, when `shouldInterceptRequest` is used, the API allows developers to
......
......@@ -1559,6 +1559,9 @@ public class AwSettingsTest {
}
class AwSettingsCorsTestHelper {
public static final String ASSET_MAIN_URL = "file:///android_asset/cors.html";
public static final String RESOURCE_IMAGE_URL = "file:///android_res/raw/resource_icon.png";
private static final String TEST_HTML_FILE_PATH = "android_webview/test/data/cors.html";
private static final String TEST_IMAGE_FILE_PATH = "android_webview/test/data/chrome.png";
private static final String TEST_HTML_CONTENT_PATH = "cors.html";
......@@ -1613,6 +1616,14 @@ public class AwSettingsTest {
public void allowUniversalAccessFromFileURLs() {
mAwSettings.setAllowUniversalAccessFromFileURLs(true);
}
public void disallowFileAccess() {
mAwSettings.setAllowFileAccess(false);
}
public void disallowContentAccess() {
mAwSettings.setAllowContentAccess(false);
}
}
// The test verifies that JavaScript is disabled upon WebView
......@@ -2092,15 +2103,35 @@ public class AwSettingsTest {
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mContentMainUrl, corsTestHelper.mFileImageUrl));
// Case b') content:// to file:///android_res/ should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", corsTestHelper.mContentMainUrl,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
// Case c) file:// to content:// should fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mContentImageUrl));
// Case c') file:///android_asset/ to content:// should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d) file:// to file:// should fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mFileImageUrl));
// Case d') file:///android_asset/ to file:// should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
// Case d'') file:///android_asset/ to file:///android_res/ should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
}
// Check if setAllowFileAccessFromFileURLs(true) allows same-scheme CORS accesses.
......@@ -2121,15 +2152,35 @@ public class AwSettingsTest {
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mContentMainUrl, corsTestHelper.mFileImageUrl));
// Case b') content:// to file:///android_res/ should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", corsTestHelper.mContentMainUrl,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
// Case c) file:// to content:// should fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mContentImageUrl));
// Case c') file:///android_asset/ to content:// should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d) file:// to file:// should pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mFileImageUrl));
// Case d') file:///android_asset/ to file:// should also pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
// Case d'') file:///android_asset/ to file:///android_res/ should also pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
}
// Check if setAllowUniversalAccessFromFileURLs(true) allows any CORS access from file://.
......@@ -2150,22 +2201,42 @@ public class AwSettingsTest {
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mContentMainUrl, corsTestHelper.mFileImageUrl));
// Case b') content:// to file:///android_res/ should also fail.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", corsTestHelper.mContentMainUrl,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
// Case c) file:// to content:// should pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mContentImageUrl));
// Case c') file:///android_asset/ to content:// should also pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d) file:// to file:// should pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult(
"xhr", corsTestHelper.mFileMainUrl, corsTestHelper.mFileImageUrl));
// Case d') file:///android_asset/ to file:// should also pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
// Case d'') file:///android_asset/ to file:///android_res/ should also pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
}
// Check if the Fetch API always fails on file:// and content://.
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "CORS"})
public void testContentUrlMakesFetchRequests() throws Throwable {
public void testContentUrlMakesFetchRequestsWithoutFileAccess() throws Throwable {
final AwSettingsCorsTestHelper corsTestHelper = new AwSettingsCorsTestHelper();
// Run tests with the most relaxed settings.
corsTestHelper.allowFileAccessFromFileURLs();
......@@ -2192,6 +2263,97 @@ public class AwSettingsTest {
"fetch", corsTestHelper.mFileMainUrl, corsTestHelper.mFileImageUrl));
}
// Check if file:// and content:// can be accessible from
// file:///android_asset/ when the file and content access is disallowed.
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "CORS"})
public void testAndroidUrlMakesXhrRequestsWithoutFileAndContentAccesses() throws Throwable {
final AwSettingsCorsTestHelper corsTestHelper = new AwSettingsCorsTestHelper();
// file:///android_asset/ and file:///android_res can be accessible even
// if AllowFileAccess and AllowContentAccess are set to false.
corsTestHelper.disallowFileAccess();
corsTestHelper.disallowContentAccess();
corsTestHelper.allowFileAccessFromFileURLs();
// Case c') file:///android_asset/ to content:// should fail as
// content:// is still disallowed by AllowContentAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d') file:///android_asset/ to file:// should fail as file://
// is still disallowed by AllowFileAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
// Case d'') file:///android_asset/ to file:///android_res/ should pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
// AllowUniversalAccessFromFileURLs should not help.
corsTestHelper.allowUniversalAccessFromFileURLs();
// Case c') file:///android_asset/ to content:// should fail as
// content:// is still disallowed by AllowContentAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d') file:///android_asset/ to file:// should fail as file://
// is still disallowed by AllowFileAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
}
// Check if file:// and content:// can be accessible from
// file:///android_asset/ when the file access is disallowed.
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences", "CORS"})
public void testAndroidUrlMakesXhrRequests() throws Throwable {
final AwSettingsCorsTestHelper corsTestHelper = new AwSettingsCorsTestHelper();
// file:///android_asset/ and file:///android_res can be accessible even
// if AllowFileAccess is set to false.
corsTestHelper.disallowFileAccess();
corsTestHelper.allowFileAccessFromFileURLs();
// Case c') file:///android_asset/ to content:// should fail as
// content:// is still accessible but CORS is not permitted.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d') file:///android_asset/ to file:// should fail as file://
// is still disallowed by AllowFileAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
// Case d'') file:///android_asset/ to file:///android_res/ should pass.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
AwSettingsCorsTestHelper.RESOURCE_IMAGE_URL));
// AllowUniversalAccessFromFileURLs should not help.
corsTestHelper.allowUniversalAccessFromFileURLs();
// Case c') file:///android_asset/ to content:// pass as CORS accesses
// are permitted now.
Assert.assertEquals("load",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mContentImageUrl));
// Case d') file:///android_asset/ to file:// should fail as file://
// is still disallowed by AllowFileAccess.
Assert.assertEquals("error",
corsTestHelper.getTestResult("xhr", AwSettingsCorsTestHelper.ASSET_MAIN_URL,
corsTestHelper.mFileImageUrl));
}
@Test
@SmallTest
@Feature({"AndroidWebView", "Preferences"})
......
......@@ -112,6 +112,7 @@ android_assets("webview_instrumentation_apk_assets") {
"shell/assets/asset_icon.png",
"shell/assets/autofill.html",
"shell/assets/cookie_test.html",
"shell/assets/cors.html",
"shell/assets/full_screen_video.js",
"shell/assets/full_screen_video_inside_div_test.html",
"shell/assets/full_screen_video_test.html",
......
<!doctype html>
<html>
<head><title>running</title></head>
<body><script>
window.onload = function () {
const params = new URL(document.location).searchParams;
const api = params.get('api');
if (api == "xhr") {
const xhr = new XMLHttpRequest();
xhr.open('get', params.get('url'), true);
xhr.onload = e => document.title = 'load';
xhr.onerror = e => document.title = 'error';
xhr.onabort = e => document.title = 'abort';
xhr.send();
} else if (api == "fetch") {
fetch(params.get('url')).then(
e => document.title = 'load',
e => document.title = 'error');
} else {
document.title = "unknown api";
}
};
</script></body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment