Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import android.os.Handler;
     20 import android.os.Message;
     21 
     22 import java.util.Collection;
     23 import java.util.HashMap;
     24 import java.util.Map;
     25 import java.util.Set;
     26 
     27 /** @hide */
     28 public class WebStorageClassic extends WebStorage {
     29     // Global instance of a WebStorage
     30     private static WebStorageClassic sWebStorage;
     31 
     32     // Message ids
     33     static final int UPDATE = 0;
     34     static final int SET_QUOTA_ORIGIN = 1;
     35     static final int DELETE_ORIGIN = 2;
     36     static final int DELETE_ALL = 3;
     37     static final int GET_ORIGINS = 4;
     38     static final int GET_USAGE_ORIGIN = 5;
     39     static final int GET_QUOTA_ORIGIN = 6;
     40 
     41     // Message ids on the UI thread
     42     static final int RETURN_ORIGINS = 0;
     43     static final int RETURN_USAGE_ORIGIN = 1;
     44     static final int RETURN_QUOTA_ORIGIN = 2;
     45 
     46     private static final String ORIGINS = "origins";
     47     private static final String ORIGIN = "origin";
     48     private static final String CALLBACK = "callback";
     49     private static final String USAGE = "usage";
     50     private static final String QUOTA = "quota";
     51 
     52     private Map <String, Origin> mOrigins;
     53 
     54     private Handler mHandler = null;
     55     private Handler mUIHandler = null;
     56 
     57     /**
     58      * @hide
     59      * Message handler, UI side
     60      * @hide
     61      */
     62     public void createUIHandler() {
     63         if (mUIHandler == null) {
     64             mUIHandler = new Handler() {
     65                 @Override
     66                 public void handleMessage(Message msg) {
     67                     switch (msg.what) {
     68                         case RETURN_ORIGINS: {
     69                             Map values = (Map) msg.obj;
     70                             Map origins = (Map) values.get(ORIGINS);
     71                             ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK);
     72                             callback.onReceiveValue(origins);
     73                             } break;
     74 
     75                         case RETURN_USAGE_ORIGIN: {
     76                             Map values = (Map) msg.obj;
     77                             ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
     78                             callback.onReceiveValue((Long)values.get(USAGE));
     79                             } break;
     80 
     81                         case RETURN_QUOTA_ORIGIN: {
     82                             Map values = (Map) msg.obj;
     83                             ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
     84                             callback.onReceiveValue((Long)values.get(QUOTA));
     85                             } break;
     86                     }
     87                 }
     88             };
     89         }
     90     }
     91 
     92     /**
     93      * Message handler, WebCore side
     94      * @hide
     95      */
     96     public synchronized void createHandler() {
     97         if (mHandler == null) {
     98             mHandler = new Handler() {
     99                 @Override
    100                 public void handleMessage(Message msg) {
    101                     switch (msg.what) {
    102                         case SET_QUOTA_ORIGIN: {
    103                             Origin website = (Origin) msg.obj;
    104                             nativeSetQuotaForOrigin(website.getOrigin(),
    105                                                     website.getQuota());
    106                             } break;
    107 
    108                         case DELETE_ORIGIN: {
    109                             Origin website = (Origin) msg.obj;
    110                             nativeDeleteOrigin(website.getOrigin());
    111                             } break;
    112 
    113                         case DELETE_ALL:
    114                             nativeDeleteAllData();
    115                             break;
    116 
    117                         case GET_ORIGINS: {
    118                             syncValues();
    119                             ValueCallback callback = (ValueCallback) msg.obj;
    120                             Map origins = new HashMap(mOrigins);
    121                             Map values = new HashMap<String, Object>();
    122                             values.put(CALLBACK, callback);
    123                             values.put(ORIGINS, origins);
    124                             postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
    125                             } break;
    126 
    127                         case GET_USAGE_ORIGIN: {
    128                             syncValues();
    129                             Map values = (Map) msg.obj;
    130                             String origin = (String) values.get(ORIGIN);
    131                             ValueCallback callback = (ValueCallback) values.get(CALLBACK);
    132                             Origin website = mOrigins.get(origin);
    133                             Map retValues = new HashMap<String, Object>();
    134                             retValues.put(CALLBACK, callback);
    135                             if (website != null) {
    136                                 long usage = website.getUsage();
    137                                 retValues.put(USAGE, new Long(usage));
    138                             }
    139                             postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues));
    140                             } break;
    141 
    142                         case GET_QUOTA_ORIGIN: {
    143                             syncValues();
    144                             Map values = (Map) msg.obj;
    145                             String origin = (String) values.get(ORIGIN);
    146                             ValueCallback callback = (ValueCallback) values.get(CALLBACK);
    147                             Origin website = mOrigins.get(origin);
    148                             Map retValues = new HashMap<String, Object>();
    149                             retValues.put(CALLBACK, callback);
    150                             if (website != null) {
    151                                 long quota = website.getQuota();
    152                                 retValues.put(QUOTA, new Long(quota));
    153                             }
    154                             postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues));
    155                             } break;
    156 
    157                         case UPDATE:
    158                             syncValues();
    159                             break;
    160                     }
    161                 }
    162             };
    163         }
    164     }
    165 
    166     /*
    167      * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(),
    168      * we need to get the values from WebCore, but we cannot block while doing so
    169      * as we used to do, as this could result in a full deadlock (other WebCore
    170      * messages received while we are still blocked here, see http://b/2127737).
    171      *
    172      * We have to do everything asynchronously, by providing a callback function.
    173      * We post a message on the WebCore thread (mHandler) that will get the result
    174      * from WebCore, and we post it back on the UI thread (using mUIHandler).
    175      * We can then use the callback function to return the value.
    176      */
    177 
    178     @Override
    179     public void getOrigins(ValueCallback<Map> callback) {
    180         if (callback != null) {
    181             if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    182                 syncValues();
    183                 callback.onReceiveValue(mOrigins);
    184             } else {
    185                 postMessage(Message.obtain(null, GET_ORIGINS, callback));
    186             }
    187         }
    188     }
    189 
    190     /**
    191      * Returns a list of origins having a database
    192      * should only be called from WebViewCore.
    193      */
    194     Collection<Origin> getOriginsSync() {
    195         if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    196             update();
    197             return mOrigins.values();
    198         }
    199         return null;
    200     }
    201 
    202     @Override
    203     public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
    204         if (callback == null) {
    205             return;
    206         }
    207         if (origin == null) {
    208             callback.onReceiveValue(null);
    209             return;
    210         }
    211         if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    212             syncValues();
    213             Origin website = mOrigins.get(origin);
    214             callback.onReceiveValue(new Long(website.getUsage()));
    215         } else {
    216             HashMap values = new HashMap<String, Object>();
    217             values.put(ORIGIN, origin);
    218             values.put(CALLBACK, callback);
    219             postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values));
    220         }
    221     }
    222 
    223     @Override
    224     public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
    225         if (callback == null) {
    226             return;
    227         }
    228         if (origin == null) {
    229             callback.onReceiveValue(null);
    230             return;
    231         }
    232         if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    233             syncValues();
    234             Origin website = mOrigins.get(origin);
    235             callback.onReceiveValue(new Long(website.getUsage()));
    236         } else {
    237             HashMap values = new HashMap<String, Object>();
    238             values.put(ORIGIN, origin);
    239             values.put(CALLBACK, callback);
    240             postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values));
    241         }
    242     }
    243 
    244     @Override
    245     public void setQuotaForOrigin(String origin, long quota) {
    246         if (origin != null) {
    247             if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    248                 nativeSetQuotaForOrigin(origin, quota);
    249             } else {
    250                 postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
    251                     new Origin(origin, quota)));
    252             }
    253         }
    254     }
    255 
    256     @Override
    257     public void deleteOrigin(String origin) {
    258         if (origin != null) {
    259             if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    260                 nativeDeleteOrigin(origin);
    261             } else {
    262                 postMessage(Message.obtain(null, DELETE_ORIGIN,
    263                     new Origin(origin)));
    264             }
    265         }
    266     }
    267 
    268     @Override
    269     public void deleteAllData() {
    270         if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    271             nativeDeleteAllData();
    272         } else {
    273             postMessage(Message.obtain(null, DELETE_ALL));
    274         }
    275     }
    276 
    277     /**
    278      * Sets the maximum size of the ApplicationCache.
    279      * This should only ever be called on the WebKit thread.
    280      * Not part of the base-class API: this is only used by dump render tree.
    281      */
    282     public void setAppCacheMaximumSize(long size) {
    283         nativeSetAppCacheMaximumSize(size);
    284     }
    285 
    286     /**
    287      * Utility function to send a message to our handler
    288      */
    289     private synchronized void postMessage(Message msg) {
    290         if (mHandler != null) {
    291             mHandler.sendMessage(msg);
    292         }
    293     }
    294 
    295     /**
    296      * Utility function to send a message to the handler on the UI thread
    297      */
    298     private void postUIMessage(Message msg) {
    299         if (mUIHandler != null) {
    300             mUIHandler.sendMessage(msg);
    301         }
    302     }
    303 
    304     /**
    305      * Get the singleton instance of this class.
    306      * @return The singleton {@link WebStorage} instance.
    307      */
    308     public static WebStorageClassic getInstance() {
    309       if (sWebStorage == null) {
    310           sWebStorage = new WebStorageClassic();
    311       }
    312       return sWebStorage;
    313     }
    314 
    315     /**
    316      * @hide
    317      * Post a Sync request
    318      */
    319     public void update() {
    320         if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
    321             syncValues();
    322         } else {
    323             postMessage(Message.obtain(null, UPDATE));
    324         }
    325     }
    326 
    327     /**
    328      * Run on the WebCore thread
    329      * set the local values with the current ones
    330      */
    331     private void syncValues() {
    332         Set<String> tmp = nativeGetOrigins();
    333         mOrigins = new HashMap<String, Origin>();
    334         for (String origin : tmp) {
    335             Origin website = new Origin(origin,
    336                                  nativeGetQuotaForOrigin(origin),
    337                                  nativeGetUsageForOrigin(origin));
    338             mOrigins.put(origin, website);
    339         }
    340     }
    341 
    342     WebStorageClassic() {}
    343 
    344     // Native functions
    345     private static native Set nativeGetOrigins();
    346     private static native long nativeGetUsageForOrigin(String origin);
    347     private static native long nativeGetQuotaForOrigin(String origin);
    348     private static native void nativeSetQuotaForOrigin(String origin, long quota);
    349     private static native void nativeDeleteOrigin(String origin);
    350     private static native void nativeDeleteAllData();
    351     private static native void nativeSetAppCacheMaximumSize(long size);
    352 }
    353