Wire in the AppCache out-of-space callback
diff --git a/api/current.xml b/api/current.xml
index 4dabd6f..3c79b90 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -163330,6 +163330,8 @@
 </parameter>
 <parameter name="currentQuota" type="long">
 </parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
 <parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
 </parameter>
 </method>
@@ -164990,6 +164992,8 @@
 </parameter>
 <parameter name="currentQuota" type="long">
 </parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
 <parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
 </parameter>
 </method>
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 3b67fb6..a6d0347 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -99,8 +99,9 @@
     private static final int RECEIVED_CERTIFICATE      = 124;
     private static final int SWITCH_OUT_HISTORY        = 125;
     private static final int EXCEEDED_DATABASE_QUOTA   = 126;
-    private static final int JS_TIMEOUT                = 127;
-    private static final int ADD_MESSAGE_TO_CONSOLE    = 128;
+    private static final int REACHED_APPCACHE_MAXSIZE  = 127;
+    private static final int JS_TIMEOUT                = 128;
+    private static final int ADD_MESSAGE_TO_CONSOLE    = 129;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                    = 200;
@@ -410,11 +411,30 @@
                     String url = (String) map.get("url");
                     long currentQuota =
                             ((Long) map.get("currentQuota")).longValue();
+                    long totalUsedQuota =
+                            ((Long) map.get("totalUsedQuota")).longValue();
                     WebStorage.QuotaUpdater quotaUpdater =
                         (WebStorage.QuotaUpdater) map.get("quotaUpdater");
 
                     mWebChromeClient.onExceededDatabaseQuota(url,
-                            databaseIdentifier, currentQuota, quotaUpdater);
+                            databaseIdentifier, currentQuota, totalUsedQuota,
+                            quotaUpdater);
+                }
+                break;
+
+            case REACHED_APPCACHE_MAXSIZE:
+                if (mWebChromeClient != null) {
+                    HashMap<String, Object> map =
+                            (HashMap<String, Object>) msg.obj;
+                    long spaceNeeded =
+                            ((Long) map.get("spaceNeeded")).longValue();
+                    long totalUsedQuota =
+                        ((Long) map.get("totalUsedQuota")).longValue();
+                    WebStorage.QuotaUpdater quotaUpdater =
+                        (WebStorage.QuotaUpdater) map.get("quotaUpdater");
+
+                    mWebChromeClient.onReachedMaxAppCacheSize(spaceNeeded,
+                            totalUsedQuota, quotaUpdater);
                 }
                 break;
 
@@ -1120,13 +1140,14 @@
      * @param databaseIdentifier The identifier of the database that the
      *     transaction that caused the overflow was running on.
      * @param currentQuota The current quota the origin is allowed.
+     * @param totalUsedQuota is the sum of all origins' quota.
      * @param quotaUpdater An instance of a class encapsulating a callback
      *     to WebViewCore to run when the decision to allow or deny more
      *     quota has been made.
      */
     public void onExceededDatabaseQuota(
             String url, String databaseIdentifier, long currentQuota,
-            WebStorage.QuotaUpdater quotaUpdater) {
+            long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
         if (mWebChromeClient == null) {
             quotaUpdater.updateQuota(currentQuota);
             return;
@@ -1137,12 +1158,40 @@
         map.put("databaseIdentifier", databaseIdentifier);
         map.put("url", url);
         map.put("currentQuota", currentQuota);
+        map.put("totalUsedQuota", totalUsedQuota);
         map.put("quotaUpdater", quotaUpdater);
         exceededQuota.obj = map;
         sendMessage(exceededQuota);
     }
 
     /**
+     * Called by WebViewCore to inform the Java side that the appcache has
+     * exceeded its max size.
+     * @param spaceNeeded is the amount of disk space that would be needed
+     * in order for the last appcache operation to succeed.
+     * @param totalUsedQuota is the sum of all origins' quota.
+     * @param quotaUpdater An instance of a class encapsulating a callback
+     * to WebViewCore to run when the decision to allow or deny a bigger
+     * app cache size has been made.
+     * @hide pending API council approval.
+     */
+    public void onReachedMaxAppCacheSize(long spaceNeeded,
+            long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+        if (mWebChromeClient == null) {
+            quotaUpdater.updateQuota(0);
+            return;
+        }
+
+        Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
+        HashMap<String, Object> map = new HashMap();
+        map.put("spaceNeeded", spaceNeeded);
+        map.put("totalUsedQuota", totalUsedQuota);
+        map.put("quotaUpdater", quotaUpdater);
+        msg.obj = map;
+        sendMessage(msg);
+    }
+
+    /**
      * Called by WebViewCore when we have a message to be added to the JavaScript
      * error console. Sends a message to the Java side with the details.
      * @param message The message to add to the console.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index dd43b8b..fa78777 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -198,17 +198,34 @@
     * @param databaseIdentifier The identifier of the database that caused the
     *     quota overflow.
     * @param currentQuota The current quota for the origin.
+    * @param totalUsedQuota is the sum of all origins' quota.
     * @param quotaUpdater A callback to inform the WebCore thread that a new
     *     quota is available. This callback must always be executed at some
     *     point to ensure that the sleeping WebCore thread is woken up.
     */
     public void onExceededDatabaseQuota(String url, String databaseIdentifier,
-        long currentQuota, WebStorage.QuotaUpdater quotaUpdater) {
+        long currentQuota, long totalUsedQuota,
+        WebStorage.QuotaUpdater quotaUpdater) {
         // This default implementation passes the current quota back to WebCore.
         // WebCore will interpret this that new quota was declined.
         quotaUpdater.updateQuota(currentQuota);
     }
 
+   /**
+    * Tell the client that the Application Cache has exceeded its max size.
+    * @param spaceNeeded is the amount of disk space that would be needed
+    * in order for the last appcache operation to succeed.
+    * @param totalUsedQuota is the sum of all origins' quota.
+    * @param quotaUpdater A callback to inform the WebCore thread that a new
+    * app cache size is available. This callback must always be executed at
+    * some point to ensure that the sleeping WebCore thread is woken up.
+    * @hide pending API council approval.
+    */
+    public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+        quotaUpdater.updateQuota(0);
+    }
+
     /**
      * Tell the client that a JavaScript execution timeout has occured. And the
      * client may decide whether or not to interrupt the execution. If the
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 1a60dba..c3b359e 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -55,7 +55,8 @@
     // 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 mCacheUpdated = mLock.newCondition();
+    private static Condition mUpdateCondition = mLock.newCondition();
+    private static boolean mUpdateAvailable;
 
     // Message ids
     static final int UPDATE = 0;
@@ -133,8 +134,11 @@
         Set ret = null;
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             ret = mOrigins;
         } catch (InterruptedException e) {
             Log.e(TAG, "Exception while waiting on the updated origins", e);
@@ -155,8 +159,11 @@
         }
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             Long usage = mUsages.get(origin);
             if (usage != null) {
                 ret = usage.longValue();
@@ -180,8 +187,11 @@
         }
         mLock.lock();
         try {
+            mUpdateAvailable = false;
             update();
-            mCacheUpdated.await();
+            while (!mUpdateAvailable) {
+                mUpdateCondition.await();
+            }
             Long quota = mQuotas.get(origin);
             if (quota != null) {
                 ret = quota.longValue();
@@ -286,7 +296,8 @@
             mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
             mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
         }
-        mCacheUpdated.signal();
+        mUpdateAvailable = true;
+        mUpdateCondition.signal();
         mLock.unlock();
     }
 
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 36c5f0c8..85867b4 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -36,6 +36,7 @@
 import android.view.SurfaceView;
 
 import java.util.ArrayList;
+import java.util.Set;
 
 import junit.framework.Assert;
 
@@ -247,7 +248,7 @@
     }
 
     /**
-     * Notify the user that the origin has exceeded it's database quota.
+     * Notify the browser that the origin has exceeded it's database quota.
      * @param url The URL that caused the overflow.
      * @param databaseIdentifier The identifier of the database.
      * @param currentQuota The current quota for the origin.
@@ -260,14 +261,28 @@
         // awaken the sleeping webcore thread when a decision from the
         // client to allow or deny quota is available.
         mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
-                currentQuota, new WebStorage.QuotaUpdater() {
+                currentQuota, getUsedQuota(), new WebStorage.QuotaUpdater() {
                                   public void updateQuota(long quota) {
-                                      nativeSetDatabaseQuota(quota);
+                                      nativeSetNewStorageLimit(quota);
                                   }
                               });
     }
 
     /**
+     * Notify the browser that the appcache has exceeded its max size.
+     * @param spaceNeeded is the amount of disk space that would be needed
+     * in order for the last appcache operation to succeed.
+     */
+    protected void reachedMaxAppCacheSize(long spaceNeeded) {
+        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
+                new WebStorage.QuotaUpdater() {
+                    public void updateQuota(long quota) {
+                        nativeSetNewStorageLimit(quota);
+                    }
+                });
+    }
+
+    /**
      * Invoke a javascript confirm dialog.
      * @param message The message displayed in the dialog.
      * @return True if the user confirmed or false if the user cancelled.
@@ -440,11 +455,11 @@
 
     /*
      * Inform webcore that the user has decided whether to allow or deny new
-     * quota for the current origin and that the main thread should wake up
-     * now.
-     * @param quota The new quota.
+     * quota for the current origin or more space for the app cache, and that
+     * the main thread should wake up now.
+     * @param limit Is the new quota for an origin or new app cache max size.
      */
-    private native void nativeSetDatabaseQuota(long quota);
+    private native void nativeSetNewStorageLimit(long limit);
 
     private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
 
@@ -1395,6 +1410,21 @@
         }
     }
 
+    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
+    // callbacks. Computes the sum of database quota for all origins.
+    private long getUsedQuota() {
+        WebStorage webStorage = WebStorage.getInstance();
+        Set<String> origins = webStorage.getOrigins();
+        if (origins == null) {
+            return 0;
+        }
+        long usedQuota = 0;
+        for (String origin : origins) {
+            usedQuota += webStorage.getQuotaForOrigin(origin);
+        }
+        return usedQuota;
+    }
+
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 4483a8e..30e1d99 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -538,7 +538,7 @@
 
         @Override
         public void onExceededDatabaseQuota(String url_str,
-                String databaseIdentifier, long currentQuota,
+                String databaseIdentifier, long currentQuota, long totalUsedQuota,
                 WebStorage.QuotaUpdater callback) {
             if (mDumpDatabaseCallbacks) {
                 if (mDatabaseCallbackStrings == null) {