blob: c079404cbf355b490259ab4daa62d4df6023c408 [file] [log] [blame]
Ben Murdoch7df19852009-04-22 13:07:58 +01001/*
2 * Copyright (C) 2009 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
17package android.webkit;
18
Nicolas Roard11e8fe52009-05-11 15:04:16 +010019import android.os.Handler;
20import android.os.Message;
21import android.util.Log;
22
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010023import java.util.Collection;
24import java.util.Map;
Nicolas Roard11e8fe52009-05-11 15:04:16 +010025import java.util.HashMap;
Andrei Popescuaf9c77e2009-07-21 17:02:35 +010026import java.util.HashSet;
27import java.util.Iterator;
28import java.util.Set;
Nicolas Roard11e8fe52009-05-11 15:04:16 +010029
Ben Murdoch7df19852009-04-22 13:07:58 +010030/**
31 * Functionality for manipulating the webstorage databases.
32 */
33public final class WebStorage {
34
35 /**
36 * Encapsulates a callback function to be executed when a new quota is made
37 * available. We primarily want this to allow us to call back the sleeping
38 * WebCore thread from outside the WebViewCore class (as the native call
39 * is private). It is imperative that this the setDatabaseQuota method is
40 * executed once a decision to either allow or deny new quota is made,
41 * otherwise the WebCore thread will remain asleep.
42 */
43 public interface QuotaUpdater {
44 public void updateQuota(long newQuota);
45 };
Nicolas Roard11e8fe52009-05-11 15:04:16 +010046
47 // Log tag
48 private static final String TAG = "webstorage";
49
50 // Global instance of a WebStorage
51 private static WebStorage sWebStorage;
52
Nicolas Roard11e8fe52009-05-11 15:04:16 +010053 // Message ids
54 static final int UPDATE = 0;
55 static final int SET_QUOTA_ORIGIN = 1;
56 static final int DELETE_ORIGIN = 2;
57 static final int DELETE_ALL = 3;
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010058 static final int GET_ORIGINS = 4;
59 static final int GET_USAGE_ORIGIN = 5;
60 static final int GET_QUOTA_ORIGIN = 6;
Nicolas Roard11e8fe52009-05-11 15:04:16 +010061
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010062 // Message ids on the UI thread
63 static final int RETURN_ORIGINS = 0;
64 static final int RETURN_USAGE_ORIGIN = 1;
65 static final int RETURN_QUOTA_ORIGIN = 2;
66
67 private static final String ORIGINS = "origins";
68 private static final String ORIGIN = "origin";
69 private static final String CALLBACK = "callback";
70 private static final String USAGE = "usage";
71 private static final String QUOTA = "quota";
72
73 private Map <String, Origin> mOrigins;
Nicolas Roard11e8fe52009-05-11 15:04:16 +010074
75 private Handler mHandler = null;
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010076 private Handler mUIHandler = null;
Nicolas Roard11e8fe52009-05-11 15:04:16 +010077
John Reck9b2a59b2011-01-24 17:31:39 -080078 /**
79 * Class containing the HTML5 database quota and usage for an origin.
80 */
John Reck87745ce2010-11-30 14:00:54 -080081 public static class Origin {
82 private String mOrigin = null;
83 private long mQuota = 0;
84 private long mUsage = 0;
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010085
John Reck87745ce2010-11-30 14:00:54 -080086 private Origin(String origin, long quota, long usage) {
Nicolas Roard6c24b4d2009-09-22 18:44:52 +010087 mOrigin = origin;
88 mQuota = quota;
89 mUsage = usage;
90 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +010091
John Reck87745ce2010-11-30 14:00:54 -080092 private Origin(String origin, long quota) {
Nicolas Roard11e8fe52009-05-11 15:04:16 +010093 mOrigin = origin;
94 mQuota = quota;
95 }
96
John Reck87745ce2010-11-30 14:00:54 -080097 private Origin(String origin) {
Nicolas Roard11e8fe52009-05-11 15:04:16 +010098 mOrigin = origin;
99 }
100
John Reck9b2a59b2011-01-24 17:31:39 -0800101 /**
102 * An origin string is created using WebCore::SecurityOrigin::toString().
103 * Note that WebCore::SecurityOrigin uses 0 (which is not printed) for
104 * the port if the port is the default for the protocol. Eg
105 * http://www.google.com and http://www.google.com:80 both record a port
106 * of 0 and hence toString() == 'http://www.google.com' for both.
107 * @return The origin string.
108 */
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100109 public String getOrigin() {
110 return mOrigin;
111 }
112
John Reck9b2a59b2011-01-24 17:31:39 -0800113 /**
114 * Returns the quota for this origin's HTML5 database.
115 * @return The quota in bytes.
116 */
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100117 public long getQuota() {
118 return mQuota;
119 }
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100120
John Reck9b2a59b2011-01-24 17:31:39 -0800121 /**
122 * Returns the usage for this origin's HTML5 database.
123 * @return The usage in bytes.
124 */
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100125 public long getUsage() {
126 return mUsage;
127 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100128 }
129
130 /**
131 * @hide
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100132 * Message handler, UI side
133 */
134 public void createUIHandler() {
135 if (mUIHandler == null) {
136 mUIHandler = new Handler() {
137 @Override
138 public void handleMessage(Message msg) {
139 switch (msg.what) {
140 case RETURN_ORIGINS: {
141 Map values = (Map) msg.obj;
142 Map origins = (Map) values.get(ORIGINS);
143 ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK);
144 callback.onReceiveValue(origins);
145 } break;
146
147 case RETURN_USAGE_ORIGIN: {
148 Map values = (Map) msg.obj;
149 ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
150 callback.onReceiveValue((Long)values.get(USAGE));
151 } break;
152
153 case RETURN_QUOTA_ORIGIN: {
154 Map values = (Map) msg.obj;
155 ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
156 callback.onReceiveValue((Long)values.get(QUOTA));
157 } break;
158 }
159 }
160 };
161 }
162 }
163
164 /**
165 * @hide
166 * Message handler, webcore side
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100167 */
Steve Blocke4b2d4d2010-02-19 12:21:35 +0000168 public synchronized void createHandler() {
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100169 if (mHandler == null) {
170 mHandler = new Handler() {
171 @Override
172 public void handleMessage(Message msg) {
173 switch (msg.what) {
174 case SET_QUOTA_ORIGIN: {
175 Origin website = (Origin) msg.obj;
176 nativeSetQuotaForOrigin(website.getOrigin(),
177 website.getQuota());
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100178 } break;
179
180 case DELETE_ORIGIN: {
181 Origin website = (Origin) msg.obj;
182 nativeDeleteOrigin(website.getOrigin());
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100183 } break;
184
185 case DELETE_ALL:
Andrei Popescuaf9c77e2009-07-21 17:02:35 +0100186 nativeDeleteAllData();
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100187 break;
188
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100189 case GET_ORIGINS: {
190 syncValues();
191 ValueCallback callback = (ValueCallback) msg.obj;
192 Map origins = new HashMap(mOrigins);
193 Map values = new HashMap<String, Object>();
194 values.put(CALLBACK, callback);
195 values.put(ORIGINS, origins);
196 postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
197 } break;
198
199 case GET_USAGE_ORIGIN: {
200 syncValues();
201 Map values = (Map) msg.obj;
202 String origin = (String) values.get(ORIGIN);
203 ValueCallback callback = (ValueCallback) values.get(CALLBACK);
204 Origin website = mOrigins.get(origin);
205 Map retValues = new HashMap<String, Object>();
206 retValues.put(CALLBACK, callback);
207 if (website != null) {
208 long usage = website.getUsage();
209 retValues.put(USAGE, new Long(usage));
210 }
211 postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues));
212 } break;
213
214 case GET_QUOTA_ORIGIN: {
215 syncValues();
216 Map values = (Map) msg.obj;
217 String origin = (String) values.get(ORIGIN);
218 ValueCallback callback = (ValueCallback) values.get(CALLBACK);
219 Origin website = mOrigins.get(origin);
220 Map retValues = new HashMap<String, Object>();
221 retValues.put(CALLBACK, callback);
222 if (website != null) {
223 long quota = website.getQuota();
224 retValues.put(QUOTA, new Long(quota));
225 }
226 postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues));
227 } break;
228
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100229 case UPDATE:
230 syncValues();
231 break;
232 }
233 }
234 };
235 }
236 }
237
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100238 /*
239 * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(),
240 * we need to get the values from webcore, but we cannot block while doing so
241 * as we used to do, as this could result in a full deadlock (other webcore
242 * messages received while we are still blocked here, see http://b/2127737).
243 *
244 * We have to do everything asynchronously, by providing a callback function.
245 * We post a message on the webcore thread (mHandler) that will get the result
246 * from webcore, and we post it back on the UI thread (using mUIHandler).
247 * We can then use the callback function to return the value.
248 */
249
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100250 /**
John Reck9b2a59b2011-01-24 17:31:39 -0800251 * Returns a list of origins having a database. The Map is of type
252 * Map<String, Origin>.
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100253 */
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100254 public void getOrigins(ValueCallback<Map> callback) {
255 if (callback != null) {
256 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
257 syncValues();
258 callback.onReceiveValue(mOrigins);
259 } else {
260 postMessage(Message.obtain(null, GET_ORIGINS, callback));
Andrei Popescu59e2ad92009-07-28 13:38:06 +0100261 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100262 }
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100263 }
264
265 /**
266 * Returns a list of origins having a database
267 * should only be called from WebViewCore.
268 */
269 Collection<Origin> getOriginsSync() {
270 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
271 update();
272 return mOrigins.values();
273 }
274 return null;
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100275 }
276
277 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100278 * Returns the use for a given origin
279 */
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100280 public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
281 if (callback == null) {
282 return;
283 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100284 if (origin == null) {
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100285 callback.onReceiveValue(null);
286 return;
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100287 }
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100288 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
289 syncValues();
290 Origin website = mOrigins.get(origin);
291 callback.onReceiveValue(new Long(website.getUsage()));
292 } else {
293 HashMap values = new HashMap<String, Object>();
294 values.put(ORIGIN, origin);
295 values.put(CALLBACK, callback);
296 postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values));
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100297 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100298 }
299
300 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100301 * Returns the quota for a given origin
302 */
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100303 public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
304 if (callback == null) {
305 return;
306 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100307 if (origin == null) {
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100308 callback.onReceiveValue(null);
309 return;
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100310 }
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100311 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
312 syncValues();
313 Origin website = mOrigins.get(origin);
314 callback.onReceiveValue(new Long(website.getUsage()));
315 } else {
316 HashMap values = new HashMap<String, Object>();
317 values.put(ORIGIN, origin);
318 values.put(CALLBACK, callback);
319 postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values));
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100320 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100321 }
322
323 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100324 * Set the quota for a given origin
325 */
326 public void setQuotaForOrigin(String origin, long quota) {
327 if (origin != null) {
Ben Murdoch5647bb32009-06-18 17:48:34 +0100328 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
329 nativeSetQuotaForOrigin(origin, quota);
Ben Murdoch5647bb32009-06-18 17:48:34 +0100330 } else {
331 postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
332 new Origin(origin, quota)));
333 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100334 }
335 }
336
337 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100338 * Delete a given origin
339 */
340 public void deleteOrigin(String origin) {
341 if (origin != null) {
Ben Murdoch5647bb32009-06-18 17:48:34 +0100342 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
343 nativeDeleteOrigin(origin);
Ben Murdoch5647bb32009-06-18 17:48:34 +0100344 } else {
345 postMessage(Message.obtain(null, DELETE_ORIGIN,
346 new Origin(origin)));
347 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100348 }
349 }
350
351 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100352 * Delete all databases
353 */
Andrei Popescuaf9c77e2009-07-21 17:02:35 +0100354 public void deleteAllData() {
Ben Murdoch5647bb32009-06-18 17:48:34 +0100355 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
Andrei Popescuaf9c77e2009-07-21 17:02:35 +0100356 nativeDeleteAllData();
Ben Murdoch5647bb32009-06-18 17:48:34 +0100357 } else {
358 postMessage(Message.obtain(null, DELETE_ALL));
359 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100360 }
361
362 /**
Steve Block427efcc2010-03-19 15:26:42 +0000363 * Sets the maximum size of the ApplicationCache.
364 * This should only ever be called on the WebKit thread.
Jonathan Dixon4cde13a2011-12-16 18:36:20 +0000365 * @hide
Steve Block427efcc2010-03-19 15:26:42 +0000366 */
367 public void setAppCacheMaximumSize(long size) {
368 nativeSetAppCacheMaximumSize(size);
369 }
370
371 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100372 * Utility function to send a message to our handler
373 */
Steve Blocke4b2d4d2010-02-19 12:21:35 +0000374 private synchronized void postMessage(Message msg) {
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100375 if (mHandler != null) {
376 mHandler.sendMessage(msg);
377 }
378 }
379
380 /**
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100381 * Utility function to send a message to the handler on the UI thread
382 */
383 private void postUIMessage(Message msg) {
384 if (mUIHandler != null) {
385 mUIHandler.sendMessage(msg);
386 }
387 }
388
389 /**
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100390 * Get the global instance of WebStorage.
391 * @return A single instance of WebStorage.
392 */
393 public static WebStorage getInstance() {
394 if (sWebStorage == null) {
395 sWebStorage = new WebStorage();
396 }
397 return sWebStorage;
398 }
399
400 /**
401 * @hide
402 * Post a Sync request
403 */
404 public void update() {
Ben Murdoch5647bb32009-06-18 17:48:34 +0100405 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
406 syncValues();
407 } else {
408 postMessage(Message.obtain(null, UPDATE));
409 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100410 }
411
412 /**
413 * Run on the webcore thread
Andrei Popescu9a56bb82009-07-22 19:21:48 +0100414 * set the local values with the current ones
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100415 */
416 private void syncValues() {
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100417 Set<String> tmp = nativeGetOrigins();
418 mOrigins = new HashMap<String, Origin>();
419 for (String origin : tmp) {
420 Origin website = new Origin(origin,
Andrei Popescu98409702010-01-08 16:41:23 +0000421 nativeGetQuotaForOrigin(origin),
422 nativeGetUsageForOrigin(origin));
Nicolas Roard6c24b4d2009-09-22 18:44:52 +0100423 mOrigins.put(origin, website);
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100424 }
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100425 }
426
427 // Native functions
Andrei Popescuaf9c77e2009-07-21 17:02:35 +0100428 private static native Set nativeGetOrigins();
Nicolas Roard11e8fe52009-05-11 15:04:16 +0100429 private static native long nativeGetUsageForOrigin(String origin);
430 private static native long nativeGetQuotaForOrigin(String origin);
431 private static native void nativeSetQuotaForOrigin(String origin, long quota);
432 private static native void nativeDeleteOrigin(String origin);
Andrei Popescuaf9c77e2009-07-21 17:02:35 +0100433 private static native void nativeDeleteAllData();
Steve Block427efcc2010-03-19 15:26:42 +0000434 private static native void nativeSetAppCacheMaximumSize(long size);
Ben Murdoch7df19852009-04-22 13:07:58 +0100435}