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