Commit 2908f2ce authored by mnaganov@chromium.org's avatar mnaganov@chromium.org

[Android] Implement WebSettings.setAppCache{Enabled|Path}

AppCacheEnabled is mapped onto WebPreferences.application_cache_enabled, which goes directly into WebKit Settings.

AppCachePath is only used as a flag to enable AppCache. We can't make use of the full path given, because in Chromium the Application Cache directory lives inside the browser context (profile).

The tests added trigger a DCHECK in disk cache, unless the profile is empty when the test starts. This makes impossible to run them both now, so only one of them is enabled for now.

Android CTS tests WebSettings.testAppCache{Disabled|Enabled} are passing with this patch.

Review URL: https://chromiumcodereview.appspot.com/11411229

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@171074 0039d316-1c4b-4281-b951-d872f2087c98
parent 383ca80a
......@@ -114,9 +114,9 @@ gfx::ImageSkia* AwContentBrowserClient::GetDefaultFavicon() {
bool AwContentBrowserClient::AllowAppCache(const GURL& manifest_url,
const GURL& first_party,
content::ResourceContext* context) {
// TODO(boliu): Implement this to power WebSettings.SetAppCacheEnabled.
NOTIMPLEMENTED();
return false;
// WebView doesn't have a per-site policy for locally stored data,
// instead AppCache can be disabled for individual WebViews.
return true;
}
......
......@@ -290,6 +290,10 @@ public class AndroidWebViewTestBase
});
}
/**
* Clears the resource cache. Note that the cache is per-application, so this will clear the
* cache for all WebViews used.
*/
protected void clearCacheOnUiThread(
final AwContents awContents,
final boolean includeDiskFiles) throws Throwable {
......
......@@ -16,6 +16,7 @@ import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.test.util.CommonResources;
import org.chromium.android_webview.test.util.ImagePageGenerator;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.TestFileUtil;
import org.chromium.base.test.util.UrlUtils;
......@@ -32,7 +33,6 @@ import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.List;
/**
* A test suite for ContentSettings class. The key objective is to verify that each
* settings applies either to each individual view or to all views of the
......@@ -1807,7 +1807,7 @@ public class AwSettingsTest extends AndroidWebViewTestBase {
return false;
}
}
}, WAIT_TIMEOUT_SECONDS * 1000, CHECK_INTERVAL));
}, TEST_TIMEOUT, CHECK_INTERVAL));
} finally {
if (webServer != null) webServer.shutdown();
}
......@@ -2161,6 +2161,163 @@ public class AwSettingsTest extends AndroidWebViewTestBase {
}
}
static class ManifestTestHelper {
private final TestWebServer mWebServer;
private final String mHtmlPath;
private final String mHtmlUrl;
private final String mManifestPath;
ManifestTestHelper(
TestWebServer webServer, String htmlPageName, String manifestName) {
mWebServer = webServer;
mHtmlPath = "/" + htmlPageName;
mHtmlUrl = webServer.setResponse(
mHtmlPath, "<html manifest=\"" + manifestName + "\"></html>", null);
mManifestPath = "/" + manifestName;
webServer.setResponse(
mManifestPath,
"CACHE MANIFEST",
CommonResources.getContentTypeAndCacheHeaders("text/cache-manifest", false));
}
String getHtmlPath() {
return mHtmlPath;
}
String getHtmlUrl() {
return mHtmlUrl;
}
String getManifestPath() {
return mManifestPath;
}
int waitUntilHtmlIsRequested(final int initialRequestCount) throws InterruptedException {
return waitUntilResourceIsRequested(mHtmlPath, initialRequestCount);
}
int waitUntilManifestIsRequested(final int initialRequestCount)
throws InterruptedException {
return waitUntilResourceIsRequested(mManifestPath, initialRequestCount);
}
private int waitUntilResourceIsRequested(
final String path, final int initialRequestCount) throws InterruptedException {
assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
@Override
public boolean isSatisfied() {
return mWebServer.getRequestCount(path) > initialRequestCount;
}
}, TEST_TIMEOUT, CHECK_INTERVAL));
return mWebServer.getRequestCount(path);
}
}
@SmallTest
@Feature({"AndroidWebView", "Preferences", "AppCache"})
public void testAppCache() throws Throwable {
final TestAwContentsClient contentClient = new TestAwContentsClient();
final AwTestContainerView testContainer =
createAwTestContainerViewOnMainSync(false, contentClient);
final AwContents awContents = testContainer.getAwContents();
final ContentSettings settings = getContentSettingsOnUiThread(awContents);
// Not sure why, but I'm experiencing DCHECK failures at net/disk_cache/in_flight_io.cc:98
// w/o this line. crbug.com/163383
clearCacheOnUiThread(awContents, true);
settings.setJavaScriptEnabled(true);
// Note that the cache isn't actually enabled until the call to setAppCachePath.
settings.setAppCacheEnabled(true);
TestWebServer webServer = null;
try {
webServer = new TestWebServer(false);
ManifestTestHelper helper = new ManifestTestHelper(
webServer, "testAppCache.html", "appcache.manifest");
loadUrlSync(
awContents,
contentClient.getOnPageFinishedHelper(),
helper.getHtmlUrl());
helper.waitUntilHtmlIsRequested(0);
// Unfortunately, there is no other good way of verifying that AppCache is
// disabled, other than checking that it didn't try to fetch the manifest.
Thread.sleep(1000);
assertEquals(0, webServer.getRequestCount(helper.getManifestPath()));
settings.setAppCachePath("whatever"); // Enables AppCache.
loadUrlSync(
awContents,
contentClient.getOnPageFinishedHelper(),
helper.getHtmlUrl());
helper.waitUntilManifestIsRequested(0);
} finally {
if (webServer != null) webServer.shutdown();
}
}
/*
* @SmallTest
* @Feature({"AndroidWebView", "Preferences", "AppCache"})
* BUG=crbug.com/163383
* If you run this test with a non-empty profile, it will crash,
* unless you delete the test's data directory.
*/
@DisabledTest
public void testAppCacheNormal() throws Throwable {
// We don't use the test helper here, because making sure that AppCache
// is disabled takes a lot of time, so running through the usual drill
// will take about 20 seconds.
ViewPair views = createViews(NORMAL_VIEW, NORMAL_VIEW);
ContentSettings settings0 = getContentSettingsOnUiThread(views.getContents0());
settings0.setJavaScriptEnabled(true);
settings0.setAppCachePath("whatever");
settings0.setAppCacheEnabled(true);
ContentSettings settings1 = getContentSettingsOnUiThread(views.getContents1());
settings1.setJavaScriptEnabled(true);
// AppCachePath setting is global, no need to set it for the second view.
settings1.setAppCacheEnabled(true);
clearCacheOnUiThread(views.getContents0(), true);
TestWebServer webServer = null;
try {
webServer = new TestWebServer(false);
ManifestTestHelper helper0 = new ManifestTestHelper(
webServer, "testAppCache_0.html", "appcache.manifest_0");
loadUrlSync(
views.getContents0(),
views.getClient0().getOnPageFinishedHelper(),
helper0.getHtmlUrl());
int manifestRequests0 = helper0.waitUntilManifestIsRequested(0);
ManifestTestHelper helper1 = new ManifestTestHelper(
webServer, "testAppCache_1.html", "appcache.manifest_1");
loadUrlSync(
views.getContents1(),
views.getClient1().getOnPageFinishedHelper(),
helper1.getHtmlUrl());
helper1.waitUntilManifestIsRequested(0);
settings1.setAppCacheEnabled(false);
loadUrlSync(
views.getContents0(),
views.getClient0().getOnPageFinishedHelper(),
helper0.getHtmlUrl());
helper0.waitUntilManifestIsRequested(manifestRequests0);
final int prevManifestRequestCount =
webServer.getRequestCount(helper1.getManifestPath());
int htmlRequests1 = webServer.getRequestCount(helper1.getHtmlPath());
loadUrlSync(
views.getContents1(),
views.getClient1().getOnPageFinishedHelper(),
helper1.getHtmlUrl());
helper1.waitUntilHtmlIsRequested(htmlRequests1);
// Unfortunately, there is no other good way of verifying that AppCache is
// disabled, other than checking that it didn't try to fetch the manifest.
Thread.sleep(1000);
assertEquals(
prevManifestRequestCount, webServer.getRequestCount(helper1.getManifestPath()));
} finally {
if (webServer != null) webServer.shutdown();
}
}
class ViewPair {
private final AwContents contents0;
private final TestAwContentsClient client0;
......
......@@ -13,24 +13,23 @@ public class CommonResources {
// Content-type headers used for HTML code.
public static List<Pair<String, String>> getTextHtmlHeaders(boolean disableCache) {
List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
headers.add(Pair.create("Content-Type", "text/html"));
if (disableCache) headers.add(Pair.create("Cache-Control", "no-store"));
return headers;
return getContentTypeAndCacheHeaders("text/html", disableCache);
}
// Content-type headers used for javascript code.
public static List<Pair<String, String>> getTextJavascriptHeaders(boolean disableCache) {
List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
headers.add(Pair.create("Content-Type", "text/javascript"));
if (disableCache) headers.add(Pair.create("Cache-Control", "no-store"));
return headers;
return getContentTypeAndCacheHeaders("text/javascript", disableCache);
}
// Content-type headers used for png images.
public static List<Pair<String, String>> getImagePngHeaders(boolean disableCache) {
return getContentTypeAndCacheHeaders("image/png", disableCache);
}
public static List<Pair<String, String>> getContentTypeAndCacheHeaders(
String contentType, boolean disableCache) {
List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
headers.add(Pair.create("Content-Type", "image/png"));
headers.add(Pair.create("Content-Type", contentType));
if (disableCache) headers.add(Pair.create("Cache-Control", "no-store"));
return headers;
}
......
......@@ -73,7 +73,6 @@ M D DLS: Dead store to testUrl in org.chromium.android_webview.test.ClientOnPage
M D DLS: Dead store to time in org.chromium.net.test.util.TestWebServer.setDateHeaders(HttpResponse) At TestWebServer.java
M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testAutoBadPath() At ArchiveTest.java
M D DMI: Hard coded reference to an absolute pathname in org.chromium.android_webview.test.ArchiveTest.testExplicitBadPath() At ArchiveTest.java
M D ICAST: Result of integer multiplication cast to long in org.chromium.android_webview.test.AwSettingsTest.testBlockNetworkImagesBlocksNetworkImageAndReloadInPlace() At AwSettingsTest.java
M D ICAST: integral division result cast to double or float in org.chromium.content.browser.HandleView.setOrientation(int) At HandleView.java
M D REC: Exception is caught when Exception is not thrown in org.chromium.content.browser.test.util.UiUtils.findParentViewForIdAcrossActivities(int) At UiUtils.java
M D SF: Switch statement found in org.chromium.chrome.browser.ChromeBrowserProvider.insert(Uri, ContentValues) where one case falls through to the next case At ChromeBrowserProvider.java
......@@ -141,3 +140,4 @@ M V MS: org.chromium.content.browser.LoadUrlParams.UA_OVERRIDE_INHERIT should be
M V MS: org.chromium.content.browser.LoadUrlParams.UA_OVERRIDE_TRUE should be package protected In LoadUrlParams.java
M C RCN: Nullcheck of GestureDetector.mVelocityTracker at line 630 of value previously dereferenced in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) At GestureDetector.java
M D SF: Switch statement found in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) where default case is missing At GestureDetector.java
M D ST: Write to static field org.chromium.content.browser.ContentSettings.sAppCachePathIsSet from instance method org.chromium.content.browser.ContentSettings.setAppCachePath(String) At ContentSettings.java
......@@ -248,6 +248,9 @@ void ContentSettings::SyncFromNativeImpl() {
Java_ContentSettings_setPluginsDisabled(env, obj, !prefs.plugins_enabled);
CheckException(env);
// We don't need to sync AppCache settings to Java, because there are
// no getters for them in the API.
env->SetBooleanField(
obj,
field_ids_->dom_storage_enabled,
......@@ -351,6 +354,9 @@ void ContentSettings::SyncToNativeImpl() {
prefs.plugins_enabled = !Java_ContentSettings_getPluginsDisabled(env, obj);
prefs.application_cache_enabled =
Java_ContentSettings_getAppCacheEnabled(env, obj);
prefs.local_storage_enabled = env->GetBooleanField(
obj, field_ids_->dom_storage_enabled);
......
......@@ -78,6 +78,7 @@ public class ContentSettings {
private boolean mJavaScriptCanOpenWindowsAutomatically = false;
private boolean mSupportMultipleWindows = false;
private PluginState mPluginState = PluginState.OFF;
private boolean mAppCacheEnabled = false;
private boolean mDomStorageEnabled = false;
// Not accessed by the native side.
......@@ -86,6 +87,13 @@ public class ContentSettings {
private boolean mBuiltInZoomControls = false;
private boolean mDisplayZoomControls = true;
// Protects access to settings global fields.
private static final Object sGlobalContentSettingsLock = new Object();
// For compatibility with the legacy WebView, we can only enable AppCache when the path is
// provided. However, we don't use the path, so we just check if we have received it from the
// client.
private static boolean sAppCachePathIsSet = false;
// Class to handle messages to be processed on the UI thread.
private class EventHandler {
// Message id for syncing
......@@ -839,9 +847,9 @@ public class ContentSettings {
*/
@CalledByNative
private boolean getPluginsDisabled() {
synchronized (mContentSettingsLock) {
return mPluginState == PluginState.OFF;
}
// This should only be called from SyncToNative, which is called
// either from the constructor, or with mContentSettingsLock being held.
return mPluginState == PluginState.OFF;
}
/**
......@@ -851,9 +859,9 @@ public class ContentSettings {
*/
@CalledByNative
private void setPluginsDisabled(boolean disabled) {
synchronized (mContentSettingsLock) {
mPluginState = disabled ? PluginState.OFF : PluginState.ON;
}
// This should only be called from SyncFromToNative, which is called
// either from the constructor, or with mContentSettingsLock being held.
mPluginState = disabled ? PluginState.OFF : PluginState.ON;
}
/**
......@@ -922,6 +930,71 @@ public class ContentSettings {
}
}
/**
* Sets whether the Application Caches API should be enabled. The default
* is false. Note that in order for the Application Caches API to be
* enabled, a non-empty database path must also be supplied to
* {@link #setAppCachePath} (this is done for compatibility with the
* legacy implementation).
*
* @param flag true if the WebView should enable Application Caches
*/
public void setAppCacheEnabled(boolean flag) {
assert mCanModifySettings;
synchronized (mContentSettingsLock) {
if (mAppCacheEnabled != flag) {
mAppCacheEnabled = flag;
mEventHandler.syncSettingsLocked();
}
}
}
/**
* Sets the path to the Application Caches files. In order for the
* Application Caches API to be enabled, this method must be called with a
* non-empty path. This method should only be called once: repeated calls
* are ignored.
*
* @param path a non empty-string
*/
public void setAppCachePath(String path) {
assert mCanModifySettings;
boolean needToSync = false;
synchronized (sGlobalContentSettingsLock) {
// AppCachePath can only be set once.
if (!sAppCachePathIsSet && path != null && !path.isEmpty()) {
sAppCachePathIsSet = true;
needToSync = true;
}
}
// The obvious problem here is that other WebViews will not be updated,
// until they execute synchronization from Java to the native side.
// But this is the same behaviour as it was in the legacy WebView.
if (needToSync) {
synchronized (mContentSettingsLock) {
mEventHandler.syncSettingsLocked();
}
}
}
/**
* Gets whether Application Cache is enabled.
*
* @return true if Application Cache is enabled
* @hide
*/
@CalledByNative
private boolean getAppCacheEnabled() {
// This should only be called from SyncToNative, which is called
// either from the constructor, or with mContentSettingsLock being held.
if (!mAppCacheEnabled) {
return false;
}
synchronized (sGlobalContentSettingsLock) {
return sAppCachePathIsSet;
}
}
/**
* Sets whether the DOM storage API is enabled. The default value is false.
*
......@@ -1012,6 +1085,7 @@ public class ContentSettings {
settings.getJavaScriptCanOpenWindowsAutomatically());
setSupportMultipleWindows(settings.supportMultipleWindows());
setPluginState(settings.getPluginState());
setAppCacheEnabled(settings.mAppCacheEnabled);
setDomStorageEnabled(settings.getDomStorageEnabled());
setSupportZoom(settings.supportZoom());
setBuiltInZoomControls(settings.getBuiltInZoomControls());
......
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