From 6c24b4d10223cb522e6bdbf0e334f61e672f4366 Mon Sep 17 00:00:00 2001 From: Nicolas Roard Date: Tue, 22 Sep 2009 18:44:52 +0100 Subject: Reimplement the settings to use async callbacks --- core/java/android/webkit/WebStorage.java | 263 +++++++++++++++++++++---------- 1 file changed, 182 insertions(+), 81 deletions(-) (limited to 'core/java/android/webkit/WebStorage.java') diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index ae560fb..0022248 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -20,9 +20,8 @@ import android.os.Handler; import android.os.Message; import android.util.Log; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.Collection; +import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -51,28 +50,41 @@ public final class WebStorage { // Global instance of a WebStorage private static WebStorage sWebStorage; - // We keep the origins, quotas and usages as member values - // that we protect via a lock and update in syncValues(). - // This is needed to transfer this data across threads. - private static Lock mLock = new ReentrantLock(); - private static Condition mUpdateCondition = mLock.newCondition(); - private static boolean mUpdateAvailable; - // Message ids static final int UPDATE = 0; static final int SET_QUOTA_ORIGIN = 1; static final int DELETE_ORIGIN = 2; static final int DELETE_ALL = 3; + static final int GET_ORIGINS = 4; + static final int GET_USAGE_ORIGIN = 5; + static final int GET_QUOTA_ORIGIN = 6; + + // Message ids on the UI thread + static final int RETURN_ORIGINS = 0; + static final int RETURN_USAGE_ORIGIN = 1; + static final int RETURN_QUOTA_ORIGIN = 2; + + private static final String ORIGINS = "origins"; + private static final String ORIGIN = "origin"; + private static final String CALLBACK = "callback"; + private static final String USAGE = "usage"; + private static final String QUOTA = "quota"; - private Set mOrigins; - private HashMap mQuotas = new HashMap(); - private HashMap mUsages = new HashMap(); + private Map mOrigins; private Handler mHandler = null; + private Handler mUIHandler = null; - private static class Origin { + static class Origin { String mOrigin = null; long mQuota = 0; + long mUsage = 0; + + public Origin(String origin, long quota, long usage) { + mOrigin = origin; + mQuota = quota; + mUsage = usage; + } public Origin(String origin, long quota) { mOrigin = origin; @@ -90,11 +102,49 @@ public final class WebStorage { public long getQuota() { return mQuota; } + + public long getUsage() { + return mUsage; + } + } + + /** + * @hide + * Message handler, UI side + */ + public void createUIHandler() { + if (mUIHandler == null) { + mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case RETURN_ORIGINS: { + Map values = (Map) msg.obj; + Map origins = (Map) values.get(ORIGINS); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + callback.onReceiveValue(origins); + } break; + + case RETURN_USAGE_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(USAGE)); + } break; + + case RETURN_QUOTA_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(QUOTA)); + } break; + } + } + }; + } } /** * @hide - * Message handler + * Message handler, webcore side */ public void createHandler() { if (mHandler == null) { @@ -117,6 +167,46 @@ public final class WebStorage { nativeDeleteAllData(); break; + case GET_ORIGINS: { + syncValues(); + ValueCallback callback = (ValueCallback) msg.obj; + Map origins = new HashMap(mOrigins); + Map values = new HashMap(); + values.put(CALLBACK, callback); + values.put(ORIGINS, origins); + postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); + } break; + + case GET_USAGE_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap(); + retValues.put(CALLBACK, callback); + if (website != null) { + long usage = website.getUsage(); + retValues.put(USAGE, new Long(usage)); + } + postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues)); + } break; + + case GET_QUOTA_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap(); + retValues.put(CALLBACK, callback); + if (website != null) { + long quota = website.getQuota(); + retValues.put(QUOTA, new Long(quota)); + } + postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues)); + } break; + case UPDATE: syncValues(); break; @@ -126,82 +216,91 @@ public final class WebStorage { } } + /* + * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(), + * we need to get the values from webcore, but we cannot block while doing so + * as we used to do, as this could result in a full deadlock (other webcore + * messages received while we are still blocked here, see http://b/2127737). + * + * We have to do everything asynchronously, by providing a callback function. + * We post a message on the webcore thread (mHandler) that will get the result + * from webcore, and we post it back on the UI thread (using mUIHandler). + * We can then use the callback function to return the value. + */ + /** * @hide * Returns a list of origins having a database */ - public Set getOrigins() { - Set ret = null; - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); + public void getOrigins(ValueCallback callback) { + if (callback != null) { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + callback.onReceiveValue(mOrigins); + } else { + postMessage(Message.obtain(null, GET_ORIGINS, callback)); } - ret = mOrigins; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); } - return ret; + } + + /** + * Returns a list of origins having a database + * should only be called from WebViewCore. + */ + Collection getOriginsSync() { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + update(); + return mOrigins.values(); + } + return null; } /** * @hide * Returns the use for a given origin */ - public long getUsageForOrigin(String origin) { - long ret = 0; + public void getUsageForOrigin(String origin, ValueCallback callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long usage = mUsages.get(origin); - if (usage != null) { - ret = usage.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values)); } - return ret; } /** * @hide * Returns the quota for a given origin */ - public long getQuotaForOrigin(String origin) { - long ret = 0; + public void getQuotaForOrigin(String origin, ValueCallback callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long quota = mQuotas.get(origin); - if (quota != null) { - ret = quota.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values)); } - return ret; } /** @@ -256,6 +355,15 @@ public final class WebStorage { } /** + * Utility function to send a message to the handler on the UI thread + */ + private void postUIMessage(Message msg) { + if (mUIHandler != null) { + mUIHandler.sendMessage(msg); + } + } + + /** * @hide * Get the global instance of WebStorage. * @return A single instance of WebStorage. @@ -284,21 +392,14 @@ public final class WebStorage { * set the local values with the current ones */ private void syncValues() { - mLock.lock(); - Set tmp = nativeGetOrigins(); - mOrigins = new HashSet(); - mQuotas.clear(); - mUsages.clear(); - Iterator iter = tmp.iterator(); - while (iter.hasNext()) { - String origin = iter.next(); - mOrigins.add(origin); - mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin))); - mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin))); + Set tmp = nativeGetOrigins(); + mOrigins = new HashMap(); + for (String origin : tmp) { + Origin website = new Origin(origin, + nativeGetUsageForOrigin(origin), + nativeGetQuotaForOrigin(origin)); + mOrigins.put(origin, website); } - mUpdateAvailable = true; - mUpdateCondition.signal(); - mLock.unlock(); } // Native functions -- cgit v1.1